aboutsummaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/au1x/db1200.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c13
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c166
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c42
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c56
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c45
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c42
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c23
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c172
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c159
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h16
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c42
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c23
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c110
-rw-r--r--sound/soc/codecs/88pm860x-codec.c2
-rw-r--r--sound/soc/codecs/Kconfig20
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ad193x.c23
-rw-r--r--sound/soc/codecs/ad1980.c2
-rw-r--r--sound/soc/codecs/ad73311.c2
-rw-r--r--sound/soc/codecs/ak4535.c19
-rw-r--r--sound/soc/codecs/ak4641.c664
-rw-r--r--sound/soc/codecs/ak4641.h47
-rw-r--r--sound/soc/codecs/ak4671.c18
-rw-r--r--sound/soc/codecs/cx20442.c18
-rw-r--r--sound/soc/codecs/dmic.c26
-rw-r--r--sound/soc/codecs/jz4740.c18
-rw-r--r--sound/soc/codecs/max98088.c87
-rw-r--r--sound/soc/codecs/max98088.h13
-rw-r--r--sound/soc/codecs/max98095.c2396
-rw-r--r--sound/soc/codecs/max98095.h299
-rw-r--r--sound/soc/codecs/sn95031.c17
-rw-r--r--sound/soc/codecs/spdif_transciever.c8
-rw-r--r--sound/soc/codecs/ssm2602.c464
-rw-r--r--sound/soc/codecs/ssm2602.h6
-rw-r--r--sound/soc/codecs/tlv320aic23.c19
-rw-r--r--sound/soc/codecs/tlv320aic3x.c3
-rw-r--r--sound/soc/codecs/tlv320dac33.c17
-rw-r--r--sound/soc/codecs/tlv320dac33.h2
-rw-r--r--sound/soc/codecs/tpa6130a2.c4
-rw-r--r--sound/soc/codecs/tpa6130a2.h2
-rw-r--r--sound/soc/codecs/twl6040.c6
-rw-r--r--sound/soc/codecs/wm1250-ev1.c108
-rw-r--r--sound/soc/codecs/wm8711.c18
-rw-r--r--sound/soc/codecs/wm8728.c18
-rw-r--r--sound/soc/codecs/wm8731.c22
-rw-r--r--sound/soc/codecs/wm8903.c44
-rw-r--r--sound/soc/codecs/wm8915.c2931
-rw-r--r--sound/soc/codecs/wm8915.h3717
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c1051
-rw-r--r--sound/soc/codecs/wm8962.c63
-rw-r--r--sound/soc/codecs/wm8993.c3
-rw-r--r--sound/soc/codecs/wm8994.c395
-rw-r--r--sound/soc/codecs/wm8994.h97
-rw-r--r--sound/soc/codecs/wm8995.c4
-rw-r--r--sound/soc/codecs/wm9705.c18
-rw-r--r--sound/soc/codecs/wm9712.c18
-rw-r--r--sound/soc/codecs/wm9713.c19
-rw-r--r--sound/soc/codecs/wm_hubs.c24
-rw-r--r--sound/soc/davinci/davinci-mcasp.c2
-rw-r--r--sound/soc/imx/imx-ssi.c6
-rw-r--r--sound/soc/jz4740/qi_lb60.c46
-rw-r--r--sound/soc/mid-x86/sst_platform.c4
-rw-r--r--sound/soc/omap/omap-mcbsp.c6
-rw-r--r--sound/soc/omap/omap-mcbsp.h2
-rw-r--r--sound/soc/omap/omap-pcm.c7
-rw-r--r--sound/soc/omap/omap-pcm.h2
-rw-r--r--sound/soc/omap/rx51.c2
-rw-r--r--sound/soc/pxa/Kconfig9
-rw-r--r--sound/soc/pxa/Makefile2
-rw-r--r--sound/soc/pxa/corgi.c2
-rw-r--r--sound/soc/pxa/hx4700.c255
-rw-r--r--sound/soc/pxa/poodle.c2
-rw-r--r--sound/soc/pxa/spitz.c41
-rw-r--r--sound/soc/samsung/Kconfig15
-rw-r--r--sound/soc/samsung/Makefile4
-rw-r--r--sound/soc/samsung/goni_wm8994.c1
-rw-r--r--sound/soc/samsung/neo1973_wm8753.c1
-rw-r--r--sound/soc/samsung/smdk_wm8580pcm.c206
-rw-r--r--sound/soc/samsung/speyside.c332
-rw-r--r--sound/soc/sh/fsi.c188
-rw-r--r--sound/soc/soc-cache.c612
-rw-r--r--sound/soc/soc-core.c191
-rw-r--r--sound/soc/soc-dapm.c611
-rw-r--r--sound/soc/soc-jack.c2
-rw-r--r--sound/soc/soc-utils.c53
-rw-r--r--sound/soc/tegra/Kconfig38
-rw-r--r--sound/soc/tegra/Makefile14
-rw-r--r--sound/soc/tegra/harmony.c394
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c9
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h2
-rw-r--r--sound/soc/tegra/tegra_i2s.c2
-rw-r--r--sound/soc/tegra/tegra_wm8903.c475
-rw-r--r--sound/soc/tegra/trimslice.c228
95 files changed, 14905 insertions, 2536 deletions
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index af3c73053ee..28afbbf69ce 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -184,7 +184,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
.codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops,
};
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index cb99f04abe8..1d3e258c9ea 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -77,7 +77,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = {
.codec_dai_name = "wm8731-hifi",
.cpu_dai_name = "au1xpsc_i2s.1",
.platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.ops = &db1200_i2s_wm8731_ops,
};
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 5a2fd8abaef..98b44b316e7 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -243,6 +243,9 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
@@ -314,6 +317,9 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max
@@ -377,6 +383,9 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
struct snd_dma_buffer *buf;
int stream;
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
sizeof(struct ac97_frame) / 4;
#endif
@@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
#endif
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@@ -458,7 +465,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bf5xx_pcm_driver = {
.driver = {
- .name = "bf5xx-pcm-audio",
+ .name = "bfin-ac97-pcm-audio",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index ffbac26b9bc..6d216259088 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -41,48 +41,7 @@
* anomaly does not affect blackfin sound drivers.
*/
-static int *cmd_count;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-#define SPORT_REQ(x) \
- [x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
- P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
-static u16 sport_req[][7] = {
-#ifdef SPORT0_TCR1
- SPORT_REQ(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_REQ(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_REQ(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_REQ(3),
-#endif
-};
-
-#define SPORT_PARAMS(x) \
- [x] = { \
- .dma_rx_chan = CH_SPORT##x##_RX, \
- .dma_tx_chan = CH_SPORT##x##_TX, \
- .err_irq = IRQ_SPORT##x##_ERROR, \
- .regs = (struct sport_register *)SPORT##x##_TCR1, \
- }
-static struct sport_param sport_params[4] = {
-#ifdef SPORT0_TCR1
- SPORT_PARAMS(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_PARAMS(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_PARAMS(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_PARAMS(3),
-#endif
-};
+static struct sport_device *ac97_sport_handle;
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
size_t count, unsigned int chan_mask)
@@ -140,7 +99,8 @@ static unsigned int sport_tx_curr_frag(struct sport_device *sport)
static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
{
- struct sport_device *sport = sport_handle;
+ struct sport_device *sport = ac97_sport_handle;
+ int *cmd_count = sport->private_data;
int nextfrag = sport_tx_curr_frag(sport);
struct ac97_frame *nextwrite;
@@ -161,6 +121,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
+ struct sport_device *sport_handle = ac97_sport_handle;
struct ac97_frame out_frame[2], in_frame[2];
pr_debug("%s enter 0x%x\n", __func__, reg);
@@ -185,6 +146,8 @@ static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
+ struct sport_device *sport_handle = ac97_sport_handle;
+
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
if (sport_handle->tx_run) {
@@ -203,28 +166,19 @@ void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
- (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
-
-#define CONCAT(a, b, c) a ## b ## c
-#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
-
- u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
- u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
+ struct sport_device *sport_handle = ac97_sport_handle;
+ u16 gpio = P_IDENT(sport_handle->pin_req[3]);
pr_debug("%s enter\n", __func__);
- peripheral_free(per);
+ peripheral_free_list(sport_handle->pin_req);
gpio_request(gpio, "bf5xx-ac97");
gpio_direction_output(gpio, 1);
udelay(2);
gpio_set_value(gpio, 0);
udelay(1);
gpio_free(gpio);
- peripheral_request(per, "soc-audio");
-#else
- pr_info("%s: Not implemented\n", __func__);
-#endif
+ peripheral_request_list(sport_handle->pin_req, "soc-audio");
}
static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
@@ -306,18 +260,32 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#define bf5xx_ac97_resume NULL
#endif
-static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
+static struct snd_soc_dai_driver bfin_ac97_dai = {
+ .ac97_control = 1,
+ .suspend = bf5xx_ac97_suspend,
+ .resume = bf5xx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+ .channels_max = 6,
+#else
+ .channels_max = 2,
+#endif
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+
+static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
{
- int ret = 0;
- cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
- if (cmd_count == NULL)
- return -ENOMEM;
-
- if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- ret = -EFAULT;
- goto peripheral_err;
- }
+ struct sport_device *sport_handle;
+ int ret;
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
@@ -329,12 +297,14 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
}
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif
- sport_handle = sport_init(&sport_params[sport_num], 2, \
- sizeof(struct ac97_frame), NULL);
+
+ sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
+ PAGE_SIZE);
if (!sport_handle) {
ret = -ENODEV;
goto sport_err;
}
+
/*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
@@ -361,67 +331,37 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
goto sport_config_err;
}
+ ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ goto sport_config_err;
+ }
+
+ ac97_sport_handle = sport_handle;
+
return 0;
sport_config_err:
- kfree(sport_handle);
+ sport_done(sport_handle);
sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
gpio_err:
#endif
- peripheral_free_list(sport_req[sport_num]);
-peripheral_err:
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
return ret;
}
-static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
+static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
- peripheral_free_list(sport_req[sport_num]);
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
- return 0;
-}
-
-struct snd_soc_dai_driver bfin_ac97_dai = {
- .ac97_control = 1,
- .probe = bf5xx_ac97_probe,
- .remove = bf5xx_ac97_remove,
- .suspend = bf5xx_ac97_suspend,
- .resume = bf5xx_ac97_resume,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- .channels_max = 6,
-#else
- .channels_max = 2,
-#endif
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
-};
-EXPORT_SYMBOL_GPL(bfin_ac97_dai);
-
-static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
-{
- return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
-}
-static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_dai(&pdev->dev);
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index 83012da9dfc..ea4951cf552 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -29,22 +29,12 @@
#include <asm/portmux.h>
#include "../codecs/ad1836.h"
-#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836;
-static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -75,23 +65,33 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ad1836_ops = {
- .startup = bf5xx_ad1836_startup,
.hw_params = bf5xx_ad1836_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad1836_dai = {
- .name = "ad1836",
- .stream_name = "AD1836",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad1836-codec.0",
- .ops = &bf5xx_ad1836_ops,
+static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
+ {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai_name = "bfin-tdm.0",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad1836.0",
+ .ops = &bf5xx_ad1836_ops,
+ },
+ {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai_name = "bfin-tdm.1",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad1836.0",
+ .ops = &bf5xx_ad1836_ops,
+ },
};
static struct snd_soc_card bf5xx_ad1836 = {
- .name = "bf5xx_ad1836",
- .dai_link = &bf5xx_ad1836_dai,
+ .name = "bfin-ad1836",
+ .dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c
index d3ccb926b5e..d6651c033cb 100644
--- a/sound/soc/blackfin/bf5xx-ad193x.c
+++ b/sound/soc/blackfin/bf5xx-ad193x.c
@@ -38,30 +38,28 @@
#include <asm/portmux.h>
#include "../codecs/ad193x.h"
-#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad193x;
-static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int clk = 0;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
+
+ switch (params_rate(params)) {
+ case 48000:
+ clk = 12288000;
+ break;
+ }
+
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
@@ -74,6 +72,12 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
/* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
@@ -89,23 +93,33 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ad193x_ops = {
- .startup = bf5xx_ad193x_startup,
.hw_params = bf5xx_ad193x_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad193x_dai = {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad193x-codec.5",
- .ops = &bf5xx_ad193x_ops,
+static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
+ {
+ .name = "ad193x",
+ .stream_name = "AD193X",
+ .cpu_dai_name = "bfin-tdm.0",
+ .codec_dai_name ="ad193x-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad193x.5",
+ .ops = &bf5xx_ad193x_ops,
+ },
+ {
+ .name = "ad193x",
+ .stream_name = "AD193X",
+ .cpu_dai_name = "bfin-tdm.1",
+ .codec_dai_name ="ad193x-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad193x.5",
+ .ops = &bf5xx_ad193x_ops,
+ },
};
static struct snd_soc_card bf5xx_ad193x = {
- .name = "bf5xx_ad193x",
- .dai_link = &bf5xx_ad193x_dai,
+ .name = "bfin-ad193x",
+ .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index d57c9c9c988..06a84b211b5 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -47,39 +47,34 @@
#include <asm/portmux.h>
#include "../codecs/ad1980.h"
-#include "bf5xx-sport.h"
+
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
-static int bf5xx_board_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
-static struct snd_soc_ops bf5xx_board_ops = {
- .startup = bf5xx_board_startup,
-};
-
-static struct snd_soc_dai_link bf5xx_board_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad1980-codec",
- .ops = &bf5xx_board_ops,
+static struct snd_soc_dai_link bf5xx_board_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "bfin-ac97.0",
+ .codec_dai_name = "ad1980-hifi",
+ .platform_name = "bfin-ac97-pcm-audio",
+ .codec_name = "ad1980",
+ },
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "bfin-ac97.1",
+ .codec_dai_name = "ad1980-hifi",
+ .platform_name = "bfin-ac97-pcm-audio",
+ .codec_name = "ad1980",
+ },
};
static struct snd_soc_card bf5xx_board = {
- .name = "bf5xx-board",
- .dai_link = &bf5xx_board_dai,
+ .name = "bfin-ad1980",
+ .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index 732fb8bad07..732a247f252 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev)
return 0;
}
-static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -176,24 +166,34 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
static struct snd_soc_ops bf5xx_ad73311_ops = {
- .startup = bf5xx_ad73311_startup,
.hw_params = bf5xx_ad73311_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad73311_dai = {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bf5xx-i2s",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad73311-codec",
- .ops = &bf5xx_ad73311_ops,
+static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
+ {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "ad73311-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ad73311",
+ .ops = &bf5xx_ad73311_ops,
+ },
+ {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "ad73311-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ad73311",
+ .ops = &bf5xx_ad73311_ops,
+ },
};
static struct snd_soc_card bf5xx_ad73311 = {
- .name = "bf5xx_ad73311",
+ .name = "bfin-ad73311",
.probe = bf5xx_probe,
- .dai_link = &bf5xx_ad73311_dai,
+ .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 890a0dccf90..b5101efd1c8 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -148,10 +148,15 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret;
pr_debug("%s enter\n", __func__);
+
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime, \
@@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
- if (sport_handle != NULL)
+ if (sport_handle != NULL) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
runtime->private_data = sport_handle;
- else {
+ } else {
pr_err("sport_handle is NULL\n");
return -1;
}
@@ -214,11 +224,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
buf->area, buf->bytes);
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
return 0;
}
@@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@@ -292,7 +295,7 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
- .name = "bfin-pcm-audio",
+ .name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index d453b1e9d60..00cc3e00b2f 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -51,59 +51,24 @@ struct bf5xx_i2s_port {
int configured;
};
-static struct bf5xx_i2s_port bf5xx_i2s;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
/* interface format:support I2S,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- bf5xx_i2s.tcr1 |= TFSR | TCKFE;
- bf5xx_i2s.rcr1 |= RFSR | RCKFE;
- bf5xx_i2s.tcr2 |= TSFSE;
- bf5xx_i2s.rcr2 |= RSFSE;
+ bf5xx_i2s->tcr1 |= TFSR | TCKFE;
+ bf5xx_i2s->rcr1 |= RFSR | RCKFE;
+ bf5xx_i2s->tcr2 |= TSFSE;
+ bf5xx_i2s->rcr2 |= RSFSE;
break;
case SND_SOC_DAIFMT_DSP_A:
- bf5xx_i2s.tcr1 |= TFSR;
- bf5xx_i2s.rcr1 |= RFSR;
+ bf5xx_i2s->tcr1 |= TFSR;
+ bf5xx_i2s->rcr1 |= RFSR;
break;
case SND_SOC_DAIFMT_LEFT_J:
ret = -EINVAL;
@@ -135,29 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
- bf5xx_i2s.tcr2 &= ~0x1f;
- bf5xx_i2s.rcr2 &= ~0x1f;
+ bf5xx_i2s->tcr2 &= ~0x1f;
+ bf5xx_i2s->rcr2 &= ~0x1f;
switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ bf5xx_i2s->tcr2 |= 7;
+ bf5xx_i2s->rcr2 |= 7;
+ sport_handle->wdsize = 1;
case SNDRV_PCM_FORMAT_S16_LE:
- bf5xx_i2s.tcr2 |= 15;
- bf5xx_i2s.rcr2 |= 15;
+ bf5xx_i2s->tcr2 |= 15;
+ bf5xx_i2s->rcr2 |= 15;
sport_handle->wdsize = 2;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- bf5xx_i2s.tcr2 |= 23;
- bf5xx_i2s.rcr2 |= 23;
+ bf5xx_i2s->tcr2 |= 23;
+ bf5xx_i2s->rcr2 |= 23;
sport_handle->wdsize = 3;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_i2s.tcr2 |= 31;
- bf5xx_i2s.rcr2 |= 31;
+ bf5xx_i2s->tcr2 |= 31;
+ bf5xx_i2s->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
}
- if (!bf5xx_i2s.configured) {
+ if (!bf5xx_i2s->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
@@ -166,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
*
* CPU DAI:slave mode.
*/
- bf5xx_i2s.configured = 1;
- ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
- bf5xx_i2s.rcr2, 0, 0);
+ bf5xx_i2s->configured = 1;
+ ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
+ bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
- bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
+ bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@@ -188,41 +159,19 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+
pr_debug("%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
- bf5xx_i2s.configured = 0;
-}
-
-static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
-
- /* request DMA for SPORT */
- sport_handle = sport_init(&sport_params[sport_num], 4, \
- 2 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- peripheral_free_list(&sport_req[sport_num][0]);
- return 0;
+ bf5xx_i2s->configured = 0;
}
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
@@ -235,19 +184,21 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
pr_debug("%s : sport %d\n", __func__, dai->id);
- ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
- bf5xx_i2s.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
+ bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
- bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
+ bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@@ -266,8 +217,11 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
-#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S32_LE)
+#define BF5XX_I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.shutdown = bf5xx_i2s_shutdown,
@@ -276,8 +230,6 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
- .probe = bf5xx_i2s_probe,
- .remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
@@ -293,23 +245,45 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.ops = &bf5xx_i2s_dai_ops,
};
-static int bfin_i2s_drv_probe(struct platform_device *pdev)
+static int __devinit bf5xx_i2s_probe(struct platform_device *pdev)
{
- return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ struct sport_device *sport_handle;
+ int ret;
+
+ /* configure SPORT for I2S */
+ sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
+ sizeof(struct bf5xx_i2s_port));
+ if (!sport_handle)
+ return -ENODEV;
+
+ /* register with the ASoC layers */
+ ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ sport_done(sport_handle);
+ return ret;
+ }
+
+ return 0;
}
-static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
+static int __devexit bf5xx_i2s_remove(struct platform_device *pdev)
{
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
+ pr_debug("%s enter\n", __func__);
+
snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
+
return 0;
}
static struct platform_driver bfin_i2s_driver = {
- .probe = bfin_i2s_drv_probe,
- .remove = __devexit_p(bfin_i2s_drv_remove),
-
+ .probe = bf5xx_i2s_probe,
+ .remove = __devexit_p(bf5xx_i2s_remove),
.driver = {
- .name = "bf5xx-i2s",
+ .name = "bfin-i2s",
.owner = THIS_MODULE,
},
};
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
index 99051ff0954..a2d40349fcc 100644
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -42,8 +42,6 @@
/* delay between frame sync pulse and first data bit in multichannel mode */
#define FRAME_DELAY (1<<12)
-struct sport_device *sport_handle;
-EXPORT_SYMBOL(sport_handle);
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
@@ -798,86 +796,164 @@ int sport_set_err_callback(struct sport_device *sport,
}
EXPORT_SYMBOL(sport_set_err_callback);
-struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
- unsigned dummy_count, void *private_data)
+static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
{
- int ret;
+ /* Extract settings from platform data */
+ struct device *dev = &pdev->dev;
+ struct bfin_snd_platform_data *pdata = dev->platform_data;
+ struct resource *res;
+
+ param->num = pdev->id;
+
+ if (!pdata) {
+ dev_err(dev, "no platform_data\n");
+ return -ENODEV;
+ }
+ param->pin_req = pdata->pin_req;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no MEM resource\n");
+ return -ENODEV;
+ }
+ param->regs = (struct sport_register *)res->start;
+
+ /* first RX, then TX */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(dev, "no rx DMA resource\n");
+ return -ENODEV;
+ }
+ param->dma_rx_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(dev, "no tx DMA resource\n");
+ return -ENODEV;
+ }
+ param->dma_tx_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no irq resource\n");
+ return -ENODEV;
+ }
+ param->err_irq = res->start;
+
+ return 0;
+}
+
+struct sport_device *sport_init(struct platform_device *pdev,
+ unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
+{
+ struct device *dev = &pdev->dev;
+ struct sport_param param;
struct sport_device *sport;
- pr_debug("%s enter\n", __func__);
- BUG_ON(param == NULL);
- BUG_ON(wdsize == 0 || dummy_count == 0);
- sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
- if (!sport) {
- pr_err("Failed to allocate for sport device\n");
+ int ret;
+
+ dev_dbg(dev, "%s enter\n", __func__);
+
+ param.wdsize = wdsize;
+ param.dummy_count = dummy_count;
+ BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
+
+ ret = sport_config_pdev(pdev, &param);
+ if (ret)
+ return NULL;
+
+ if (peripheral_request_list(param.pin_req, "soc-audio")) {
+ dev_err(dev, "requesting Peripherals failed\n");
return NULL;
}
- memset(sport, 0, sizeof(struct sport_device));
- sport->dma_rx_chan = param->dma_rx_chan;
- sport->dma_tx_chan = param->dma_tx_chan;
- sport->err_irq = param->err_irq;
- sport->regs = param->regs;
- sport->private_data = private_data;
+ sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+ if (!sport) {
+ dev_err(dev, "failed to allocate for sport device\n");
+ goto __init_err0;
+ }
+
+ sport->num = param.num;
+ sport->dma_rx_chan = param.dma_rx_chan;
+ sport->dma_tx_chan = param.dma_tx_chan;
+ sport->err_irq = param.err_irq;
+ sport->regs = param.regs;
+ sport->pin_req = param.pin_req;
if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
- pr_err("Failed to request RX dma %d\n", \
- sport->dma_rx_chan);
+ dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
goto __init_err1;
}
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
- pr_err("Failed to request RX irq %d\n", \
- sport->dma_rx_chan);
+ dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
goto __init_err2;
}
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
- pr_err("Failed to request TX dma %d\n", \
- sport->dma_tx_chan);
+ dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
goto __init_err2;
}
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
- pr_err("Failed to request TX irq %d\n", \
- sport->dma_tx_chan);
+ dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
goto __init_err3;
}
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
sport) < 0) {
- pr_err("Failed to request err irq:%d\n", \
- sport->err_irq);
+ dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
goto __init_err3;
}
- pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
+ dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
sport->dma_rx_chan, sport->dma_tx_chan,
sport->err_irq, sport->regs);
- sport->wdsize = wdsize;
- sport->dummy_count = dummy_count;
+ sport->wdsize = param.wdsize;
+ sport->dummy_count = param.dummy_count;
+
+ sport->private_data = kzalloc(priv_size, GFP_KERNEL);
+ if (!sport->private_data) {
+ dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
+ goto __init_err4;
+ }
if (L1_DATA_A_LENGTH)
- sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
+ sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
else
- sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
+ sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) {
- pr_err("Failed to allocate dummy buffer\n");
- goto __error;
+ dev_err(dev, "failed to allocate dummy buffer\n");
+ goto __error1;
}
ret = sport_config_rx_dummy(sport);
if (ret) {
- pr_err("Failed to config rx dummy ring\n");
- goto __error;
+ dev_err(dev, "failed to config rx dummy ring\n");
+ goto __error2;
}
ret = sport_config_tx_dummy(sport);
if (ret) {
- pr_err("Failed to config tx dummy ring\n");
- goto __error;
+ dev_err(dev, "failed to config tx dummy ring\n");
+ goto __error3;
}
+ platform_set_drvdata(pdev, sport);
+
return sport;
-__error:
+__error3:
+ if (L1_DATA_A_LENGTH)
+ l1_data_sram_free(sport->dummy_rx_desc);
+ else
+ dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+ sport->dummy_rx_desc, 0);
+__error2:
+ if (L1_DATA_A_LENGTH)
+ l1_data_sram_free(sport->dummy_buf);
+ else
+ kfree(sport->dummy_buf);
+__error1:
+ kfree(sport->private_data);
+__init_err4:
free_irq(sport->err_irq, sport);
__init_err3:
free_dma(sport->dma_tx_chan);
@@ -885,6 +961,8 @@ __init_err2:
free_dma(sport->dma_rx_chan);
__init_err1:
kfree(sport);
+__init_err0:
+ peripheral_free_list(param.pin_req);
return NULL;
}
EXPORT_SYMBOL(sport_init);
@@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport)
free_dma(sport->dma_tx_chan);
free_irq(sport->err_irq, sport);
+ kfree(sport->private_data);
+ peripheral_free_list(sport->pin_req);
kfree(sport);
- sport = NULL;
}
EXPORT_SYMBOL(sport_done);
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
index a86e8cc0b2d..5ab60bd613e 100644
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -1,5 +1,5 @@
/*
- * File: bf5xx_ac97_sport.h
+ * File: bf5xx_sport.h
* Based on:
* Author: Roy Huang <roy.huang@analog.com>
*
@@ -33,15 +33,18 @@
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/platform_device.h>
#include <asm/dma.h>
#include <asm/bfin_sport.h>
#define DESC_ELEMENT_COUNT 9
struct sport_device {
+ int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
+ const unsigned short *pin_req;
struct sport_register *regs;
unsigned char *rx_buf;
@@ -103,17 +106,20 @@ struct sport_device {
void *private_data;
};
-extern struct sport_device *sport_handle;
-
struct sport_param {
+ int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
+ const unsigned short *pin_req;
struct sport_register *regs;
+ unsigned int wdsize;
+ unsigned int dummy_count;
+ void *private_data;
};
-struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
- unsigned dummy_count, void *private_data);
+struct sport_device *sport_init(struct platform_device *pdev,
+ unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
void sport_done(struct sport_device *sport);
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index ad28663f5bb..767e772a815 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -44,16 +44,6 @@
static struct snd_soc_card bf5xx_ssm2602;
-static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -109,23 +99,33 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ssm2602_ops = {
- .startup = bf5xx_ssm2602_startup,
.hw_params = bf5xx_ssm2602_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bf5xx-i2s",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bf5xx-pcm-audio",
- .codec_name = "ssm2602-codec.0-001b",
- .ops = &bf5xx_ssm2602_ops,
+static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
+ {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "ssm2602-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ssm2602.0-001b",
+ .ops = &bf5xx_ssm2602_ops,
+ },
+ {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "ssm2602-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ssm2602.0-001b",
+ .ops = &bf5xx_ssm2602_ops,
+ },
};
static struct snd_soc_card bf5xx_ssm2602 = {
- .name = "bf5xx_ssm2602",
- .dai_link = &bf5xx_ssm2602_dai,
+ .name = "bfin-ssm2602",
+ .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index 74cf759b78a..07cfc7a9e49 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -154,7 +154,12 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
@@ -164,9 +169,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
- if (sport_handle != NULL)
+ if (sport_handle != NULL) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
runtime->private_data = sport_handle;
- else {
+ } else {
pr_err("sport_handle is NULL\n");
ret = -ENODEV;
}
@@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
}
buf->bytes = size;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
return 0;
}
@@ -274,8 +279,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@@ -326,7 +329,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_tdm_driver = {
.driver = {
- .name = "bf5xx-tdm-pcm-audio",
+ .name = "bfin-tdm-pcm-audio",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index 5515ac9e05c..a822d1ee138 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -46,43 +46,6 @@
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
-static struct bf5xx_tdm_port bf5xx_tdm;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
@@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int ret = 0;
- bf5xx_tdm.tcr2 &= ~0x1f;
- bf5xx_tdm.rcr2 &= ~0x1f;
+ bf5xx_tdm->tcr2 &= ~0x1f;
+ bf5xx_tdm->rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_tdm.tcr2 |= 31;
- bf5xx_tdm.rcr2 |= 31;
+ bf5xx_tdm->tcr2 |= 31;
+ bf5xx_tdm->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
@@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
break;
}
- if (!bf5xx_tdm.configured) {
+ if (!bf5xx_tdm->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
@@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
*
* CPU DAI:slave mode.
*/
- ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
- bf5xx_tdm.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
+ bf5xx_tdm->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
- bf5xx_tdm.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
+ bf5xx_tdm->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- bf5xx_tdm.configured = 1;
+ bf5xx_tdm->configured = 1;
}
return 0;
@@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
+
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
- bf5xx_tdm.configured = 0;
+ bf5xx_tdm->configured = 0;
}
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int i;
unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0;
@@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
- bf5xx_tdm.tx_map[i] = slot;
+ bf5xx_tdm->tx_map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
@@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
- bf5xx_tdm.rx_map[i] = slot;
+ bf5xx_tdm->rx_map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
@@ -212,12 +182,14 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- if (!dai->active)
- return 0;
- if (dai->capture_active)
- sport_rx_stop(sport);
if (dai->playback_active)
sport_tx_stop(sport);
+ if (dai->capture_active)
+ sport_rx_stop(sport);
+
+ /* isolate sync/clock pins from codec while sports resume */
+ peripheral_free_list(sport->pin_req);
+
return 0;
}
@@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
int ret;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- if (!dai->active)
- return 0;
-
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
@@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
ret = -EBUSY;
}
+ peripheral_request_list(sport->pin_req, "soc-audio");
+
return 0;
}
@@ -280,20 +251,14 @@ static struct snd_soc_dai_driver bf5xx_tdm_dai = {
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{
- int ret = 0;
-
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
+ struct sport_device *sport_handle;
+ int ret;
- /* request DMA for SPORT */
- sport_handle = sport_init(&sport_params[sport_num], 4, \
- 8 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
+ /* configure SPORT for TDM */
+ sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
+ sizeof(struct bf5xx_tdm_port));
+ if (!sport_handle)
return -ENODEV;
- }
/* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
@@ -323,18 +288,19 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
goto sport_config_err;
}
- sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:
- peripheral_free_list(&sport_req[sport_num][0]);
+ sport_done(sport_handle);
return ret;
}
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{
- peripheral_free_list(&sport_req[sport_num][0]);
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
return 0;
}
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 06b6981b8d6..19241576b6b 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -120,7 +120,7 @@
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrols = NULL, \
+ .shift = 0, .invert = 0, .kcontrol_news = NULL, \
.num_kcontrols = 0, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 6943e24a74a..98175a096df 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -16,10 +16,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+ select SND_SOC_AD73311
select SND_SOC_ADS117X
- select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
+ select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_ALC5623 if I2C
@@ -33,13 +34,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_JZ4740_CODEC if SOC_JZ4740
select SND_SOC_LM4857 if I2C
select SND_SOC_MAX98088 if I2C
+ select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
- select SND_SOC_SSM2602 if I2C
+ select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
@@ -52,6 +54,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE
+ select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
@@ -72,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
+ select SND_SOC_WM8915 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
@@ -136,6 +140,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
+config SND_SOC_AK4641
+ tristate
+
config SND_SOC_AK4642
tristate
@@ -187,6 +194,9 @@ config SND_SOC_DMIC
config SND_SOC_MAX98088
tristate
+config SND_SOC_MAX98095
+ tristate
+
config SND_SOC_MAX9850
tristate
@@ -241,6 +251,9 @@ config SND_SOC_UDA1380
config SND_SOC_WL1273
tristate
+config SND_SOC_WM1250_EV1
+ tristate
+
config SND_SOC_WM8350
tristate
@@ -298,6 +311,9 @@ config SND_SOC_WM8903
config SND_SOC_WM8904
tristate
+config SND_SOC_WM8915
+ tristate
+
config SND_SOC_WM8940
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 379bc55f072..fd8558406ef 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -7,6 +7,7 @@ snd-soc-ad73311-objs := ad73311.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
@@ -19,6 +20,7 @@ snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o
+snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-sgtl5000-objs := sgtl5000.o
@@ -37,6 +39,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
@@ -56,6 +59,7 @@ snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
+snd-soc-wm8915-objs := wm8915.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
@@ -69,7 +73,7 @@ snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
-snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
+snd-soc-wm8994-objs := wm8994.o wm8994-tables.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
@@ -94,6 +98,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
@@ -108,6 +113,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
@@ -125,6 +131,7 @@ obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
@@ -144,6 +151,7 @@ obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
+obj-$(CONFIG_SND_SOC_WM8915) += snd-soc-wm8915.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index da46479bfcf..2374ca5ffe6 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -23,8 +23,7 @@
/* codec private data */
struct ad193x_priv {
- enum snd_soc_control_type bus_type;
- void *control_data;
+ enum snd_soc_control_type control_type;
int sysclk;
};
@@ -354,14 +353,12 @@ static int ad193x_probe(struct snd_soc_codec *codec)
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
- codec->control_data = ad193x->control_data;
- if (ad193x->bus_type == SND_SOC_I2C)
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
+ if (ad193x->control_type == SND_SOC_I2C)
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
else
- ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type);
+ ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
if (ret < 0) {
- dev_err(codec->dev, "failed to set cache I/O: %d\n",
- ret);
+ dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
return ret;
}
@@ -408,8 +405,7 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, ad193x);
- ad193x->control_data = spi;
- ad193x->bus_type = SND_SOC_SPI;
+ ad193x->control_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
@@ -427,7 +423,7 @@ static int __devexit ad193x_spi_remove(struct spi_device *spi)
static struct spi_driver ad193x_spi_driver = {
.driver = {
- .name = "ad193x-codec",
+ .name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
@@ -454,8 +450,7 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, ad193x);
- ad193x->control_data = client;
- ad193x->bus_type = SND_SOC_I2C;
+ ad193x->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
@@ -473,7 +468,7 @@ static int __devexit ad193x_i2c_remove(struct i2c_client *client)
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
- .name = "ad193x-codec",
+ .name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = __devexit_p(ad193x_i2c_remove),
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 34cb51ef215..923b364a3e4 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -266,7 +266,7 @@ static int __devexit ad1980_remove(struct platform_device *pdev)
static struct platform_driver ad1980_codec_driver = {
.driver = {
- .name = "ad1980-codec",
+ .name = "ad1980",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index de799cd1ba7..8d793e993e9 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -55,7 +55,7 @@ static int __devexit ad73311_remove(struct platform_device *pdev)
static struct platform_driver ad73311_codec_driver = {
.driver = {
- .name = "ad73311-codec",
+ .name = "ad73311",
.owner = THIS_MODULE,
},
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 8b38739c88f..e1a214ee757 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -230,7 +230,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
@@ -287,17 +287,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Input Mixer", "Aux Capture Switch", "Aux In"},
};
-static int ak4535_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ak4535_dapm_widgets,
- ARRAY_SIZE(ak4535_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@@ -457,8 +446,6 @@ static int ak4535_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
- ak4535_add_widgets(codec);
-
return 0;
}
@@ -480,6 +467,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
.reg_cache_size = ARRAY_SIZE(ak4535_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4535_reg,
+ .dapm_widgets = ak4535_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
+ .dapm_routes = ak4535_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
new file mode 100644
index 00000000000..ed96f247c2d
--- /dev/null
+++ b/sound/soc/codecs/ak4641.c
@@ -0,0 +1,664 @@
+/*
+ * ak4641.c -- AK4641 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
+ * Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
+ *
+ * Based on ak4535.c by Richard Purdie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/ak4641.h>
+
+#include "ak4641.h"
+
+/* codec private data */
+struct ak4641_priv {
+ struct snd_soc_codec *codec;
+ unsigned int sysclk;
+ int deemph;
+ int playback_fs;
+};
+
+/*
+ * ak4641 register cache
+ */
+static const u8 ak4641_reg[AK4641_CACHEREGNUM] = {
+ 0x00, 0x80, 0x00, 0x80,
+ 0x02, 0x00, 0x11, 0x05,
+ 0x00, 0x00, 0x36, 0x10,
+ 0x00, 0x00, 0x57, 0x00,
+ 0x88, 0x88, 0x08, 0x08
+};
+
+static const int deemph_settings[] = {44100, 0, 48000, 32000};
+
+static int ak4641_set_deemph(struct snd_soc_codec *codec)
+{
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int i, best = 0;
+
+ for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
+ /* if deemphasis is on, select the nearest available rate */
+ if (ak4641->deemph && deemph_settings[i] != 0 &&
+ abs(deemph_settings[i] - ak4641->playback_fs) <
+ abs(deemph_settings[best] - ak4641->playback_fs))
+ best = i;
+
+ if (!ak4641->deemph && deemph_settings[i] == 0)
+ best = i;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x3, best);
+}
+
+static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.enumerated.item[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ ak4641->deemph = deemph;
+
+ return ak4641_set_deemph(codec);
+}
+
+static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4641->deemph;
+ return 0;
+};
+
+static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4641_mic_select[] = {"Internal", "External"};
+static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
+
+
+static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
+
+
+static const struct soc_enum ak4641_mono_out_enum =
+ SOC_ENUM_SINGLE(AK4641_SIG1, 6, 2, ak4641_mono_out);
+static const struct soc_enum ak4641_hp_out_enum =
+ SOC_ENUM_SINGLE(AK4641_MODE2, 2, 2, ak4641_hp_out);
+static const struct soc_enum ak4641_mic_select_enum =
+ SOC_ENUM_SINGLE(AK4641_MIC, 1, 2, ak4641_mic_select);
+static const struct soc_enum ak4641_mic_or_dac_enum =
+ SOC_ENUM_SINGLE(AK4641_BTIF, 4, 2, ak4641_mic_or_dac);
+
+static const struct snd_kcontrol_new ak4641_snd_controls[] = {
+ SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
+ SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
+ mono_gain_tlv),
+ SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
+ SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ ak4641_get_deemph, ak4641_put_deemph),
+
+ SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
+
+ SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
+
+ SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
+
+ SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
+ SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
+ SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
+
+ SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
+ AK4641_RATT, 0, 255, 1, master_tlv),
+
+ SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
+
+ SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
+ SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
+};
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
+ mic_mono_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
+ mic_stereo_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
+};
+
+/* Mic mux */
+static const struct snd_kcontrol_new ak4641_mic_mux_control =
+ SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4641_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4641_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
+
+/* ak4641 dapm widgets */
+static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4641_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4641_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mixer_controls[0],
+ ARRAY_SIZE(ak4641_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_mic_mux_control),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mux_control),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono2_control),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
+ SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
+ SND_SOC_DAPM_ADC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route ak4641_audio_map[] = {
+ /* Stereo Mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* Mono 1 Mixer */
+ {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Mic Mux", "Internal", "Mic Int Bias"},
+ {"Mic Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Mic Mux"},
+
+ /* Input Mux */
+ {"Input Mux", "Microphone", "Mic"},
+ {"Input Mux", "Voice DAC", "Voice DAC"},
+
+ /* Line Out */
+ {"LOUT", NULL, "Line Out"},
+ {"ROUT", NULL, "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* Mono 1 Out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* Mono 2 Out */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Mono Out 2"},
+ {"Mono Out 2", NULL, "Stereo Mixer"},
+
+ {"Voice ADC", NULL, "Mono 2 Enable"},
+
+ /* Aux In */
+ {"AUX In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "AUX In"},
+};
+
+static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ak4641->sysclk = freq;
+ return 0;
+}
+
+static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int rate = params_rate(params), fs = 256;
+ u8 mode2;
+
+ if (rate)
+ fs = ak4641->sysclk / rate;
+ else
+ return -EINVAL;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 = (0x2 << 5);
+ break;
+ case 512:
+ mode2 = (0x1 << 5);
+ break;
+ case 256:
+ mode2 = (0x0 << 5);
+ break;
+ default:
+ dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
+
+ /* Update de-emphasis filter for the new rate */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ak4641->playback_fs = rate;
+ ak4641_set_deemph(codec);
+ };
+
+ return 0;
+}
+
+static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 btif;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ btif = (0x3 << 5);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ btif = (0x2 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
+ btif = (0x0 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
+ btif = (0x1 << 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+}
+
+static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x02;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, AK4641_MODE1, mode1);
+}
+
+static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+}
+
+static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* unmute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* mute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 1);
+ mdelay(1);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 1);
+ mdelay(1);
+
+ ret = snd_soc_cache_sync(codec);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
+ snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
+#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000)
+#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
+ .hw_params = ak4641_i2s_hw_params,
+ .set_fmt = ak4641_i2s_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
+ .hw_params = NULL, /* rates are controlled by BT chip */
+ .set_fmt = ak4641_pcm_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+struct snd_soc_dai_driver ak4641_dai[] = {
+{
+ .name = "ak4641-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_i2s_dai_ops,
+ .symmetric_rates = 1,
+},
+{
+ .name = "ak4641-voice",
+ .id = 1,
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_pcm_dai_ops,
+ .symmetric_rates = 1,
+},
+};
+
+static int ak4641_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak4641_resume(struct snd_soc_codec *codec)
+{
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+static int ak4641_probe(struct snd_soc_codec *codec)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ int ret;
+
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "ak4641 power");
+ if (ret)
+ goto err_out;
+ }
+ if (gpio_is_valid(pdata->gpio_npdn)) {
+ ret = gpio_request_one(pdata->gpio_npdn,
+ GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+ if (ret)
+ goto err_gpio;
+
+ udelay(1); /* > 150 ns */
+ gpio_set_value(pdata->gpio_npdn, 1);
+ }
+ }
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err_register;
+ }
+
+ /* power on device */
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+
+err_register:
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+err_gpio:
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+err_out:
+ return ret;
+}
+
+static int ak4641_remove(struct snd_soc_codec *codec)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_power);
+ }
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+ return 0;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
+ .probe = ak4641_probe,
+ .remove = ak4641_remove,
+ .suspend = ak4641_suspend,
+ .resume = ak4641_resume,
+ .controls = ak4641_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4641_snd_controls),
+ .dapm_widgets = ak4641_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
+ .dapm_routes = ak4641_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ .set_bias_level = ak4641_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(ak4641_reg),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = ak4641_reg,
+ .reg_cache_step = 1,
+};
+
+
+static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak4641_priv *ak4641;
+ int ret;
+
+ ak4641 = kzalloc(sizeof(struct ak4641_priv), GFP_KERNEL);
+ if (!ak4641)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ak4641);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
+ ak4641_dai, ARRAY_SIZE(ak4641_dai));
+ if (ret < 0)
+ kfree(ak4641);
+
+ return ret;
+}
+
+static int __devexit ak4641_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ kfree(i2c_get_clientdata(i2c));
+ return 0;
+}
+
+static const struct i2c_device_id ak4641_i2c_id[] = {
+ { "ak4641", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
+
+static struct i2c_driver ak4641_i2c_driver = {
+ .driver = {
+ .name = "ak4641",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4641_i2c_probe,
+ .remove = __devexit_p(ak4641_i2c_remove),
+ .id_table = ak4641_i2c_id,
+};
+
+static int __init ak4641_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&ak4641_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register AK4641 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(ak4641_modinit);
+
+static void __exit ak4641_exit(void)
+{
+ i2c_del_driver(&ak4641_i2c_driver);
+}
+module_exit(ak4641_exit);
+
+MODULE_DESCRIPTION("SoC AK4641 driver");
+MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4641.h b/sound/soc/codecs/ak4641.h
new file mode 100644
index 00000000000..4a263248efe
--- /dev/null
+++ b/sound/soc/codecs/ak4641.h
@@ -0,0 +1,47 @@
+/*
+ * ak4641.h -- AK4641 SoC Audio driver
+ *
+ * Copyright 2008 Harald Welte <laforge@gnufiish.org>
+ *
+ * Based on ak4535.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AK4641_H
+#define _AK4641_H
+
+/* AK4641 register space */
+
+#define AK4641_PM1 0x00
+#define AK4641_PM2 0x01
+#define AK4641_SIG1 0x02
+#define AK4641_SIG2 0x03
+#define AK4641_MODE1 0x04
+#define AK4641_MODE2 0x05
+#define AK4641_DAC 0x06
+#define AK4641_MIC 0x07
+#define AK4641_TIMER 0x08
+#define AK4641_ALC1 0x09
+#define AK4641_ALC2 0x0a
+#define AK4641_PGA 0x0b
+#define AK4641_LATT 0x0c
+#define AK4641_RATT 0x0d
+#define AK4641_VOL 0x0e
+#define AK4641_STATUS 0x0f
+#define AK4641_EQLO 0x10
+#define AK4641_EQMID 0x11
+#define AK4641_EQHI 0x12
+#define AK4641_BTIF 0x13
+
+#define AK4641_CACHEREGNUM 0x14
+
+
+
+#define AK4641_DAI_HIFI 0
+#define AK4641_DAI_VOICE 1
+
+
+#endif
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 2ec75abfa3e..88b29f8c748 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -352,7 +352,7 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route ak4671_intercon[] = {
{"DAC Left", "NULL", "PMPLL"},
{"DAC Right", "NULL", "PMPLL"},
{"ADC Left", "NULL", "PMPLL"},
@@ -433,17 +433,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
};
-static int ak4671_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ak4671_dapm_widgets,
- ARRAY_SIZE(ak4671_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -650,7 +639,6 @@ static int ak4671_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4671_snd_controls,
ARRAY_SIZE(ak4671_snd_controls));
- ak4671_add_widgets(codec);
ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -670,6 +658,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
.reg_cache_size = AK4671_CACHEREGNUM,
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4671_reg,
+ .dapm_widgets = ak4671_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
+ .dapm_routes = ak4671_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
};
static int __devinit ak4671_i2c_probe(struct i2c_client *client,
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 0bb424af956..d68ea532cc7 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -86,18 +86,6 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"},
};
-static int cx20442_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, cx20442_dapm_widgets,
- ARRAY_SIZE(cx20442_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, cx20442_audio_map,
- ARRAY_SIZE(cx20442_audio_map));
-
- return 0;
-}
-
static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -344,8 +332,6 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, cx20442);
- cx20442_add_widgets(codec);
-
cx20442->control_data = NULL;
codec->hw_write = NULL;
codec->card->pop_time = 0;
@@ -377,6 +363,10 @@ static struct snd_soc_codec_driver cx20442_codec_dev = {
.reg_word_size = sizeof(u8),
.read = cx20442_read_reg_cache,
.write = cx20442_write,
+ .dapm_widgets = cx20442_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
+ .dapm_routes = cx20442_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
};
static int cx20442_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 57e9dac88d3..f9a87737ec1 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -39,7 +39,31 @@ static struct snd_soc_dai_driver dmic_dai = {
},
};
-static struct snd_soc_codec_driver soc_dmic = {};
+static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("DMic"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"DMIC AIF", NULL, "DMic"},
+};
+
+static int dmic_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
+ ARRAY_SIZE(dmic_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_dmic = {
+ .probe = dmic_probe,
+};
static int __devinit dmic_dev_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index f5ccdbf7ebc..e373f8f0690 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -294,20 +294,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
- snd_soc_add_controls(codec, jz4740_codec_controls,
- ARRAY_SIZE(jz4740_codec_controls));
-
- snd_soc_dapm_new_controls(dapm, jz4740_codec_dapm_widgets,
- ARRAY_SIZE(jz4740_codec_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes,
- ARRAY_SIZE(jz4740_codec_dapm_routes));
-
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
@@ -348,6 +337,13 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
.reg_cache_default = jz4740_codec_regs,
.reg_word_size = sizeof(u32),
.reg_cache_size = 2,
+
+ .controls = jz4740_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4740_codec_controls),
+ .dapm_widgets = jz4740_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
+ .dapm_routes = jz4740_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
};
static int __devinit jz4740_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index bd0517cb798..4173b67c94d 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -656,8 +656,6 @@ static const struct soc_enum max98088_exmode_enum =
ARRAY_SIZE(max98088_exmode_texts),
max98088_exmode_texts,
max98088_exmode_values);
-static const struct snd_kcontrol_new max98088_exmode_controls =
- SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
static const char *max98088_ex_thresh[] = { /* volts PP */
"0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
@@ -783,6 +781,7 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+ SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
@@ -808,10 +807,10 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
/* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
@@ -836,10 +835,10 @@ static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
/* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
@@ -864,10 +863,10 @@ static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
/* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
@@ -1094,9 +1093,6 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
- SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
- &max98088_exmode_controls),
-
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKL"),
@@ -1112,7 +1108,7 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("INB2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */
{"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
@@ -1226,22 +1222,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"MIC2 Input", NULL, "MIC2"},
};
-static int max98088_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, max98088_dapm_widgets,
- ARRAY_SIZE(max98088_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- snd_soc_add_controls(codec, max98088_snd_controls,
- ARRAY_SIZE(max98088_snd_controls));
-
- snd_soc_dapm_new_widgets(dapm);
- return 0;
-}
-
/* codec mclk clock divider coefficients */
static const struct {
u32 rate;
@@ -1586,6 +1566,36 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
+static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
static void max98088_sync_cache(struct snd_soc_codec *codec)
{
u16 *reg_cache = codec->reg_cache;
@@ -1647,12 +1657,14 @@ static struct snd_soc_dai_ops max98088_dai1_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
+ .digital_mute = max98088_dai1_digital_mute,
};
static struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
+ .digital_mute = max98088_dai2_digital_mute,
};
static struct snd_soc_dai_driver max98088_dai[] = {
@@ -2010,7 +2022,8 @@ static int max98088_probe(struct snd_soc_codec *codec)
max98088_handle_pdata(codec);
- max98088_add_widgets(codec);
+ snd_soc_add_controls(codec, max98088_snd_controls,
+ ARRAY_SIZE(max98088_snd_controls));
err_access:
return ret;
@@ -2036,6 +2049,10 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
.reg_word_size = sizeof(u8),
.reg_cache_default = max98088_reg,
.volatile_register = max98088_volatile_register,
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
};
static int max98088_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index 56554c797fe..be89a4f4aab 100644
--- a/sound/soc/codecs/max98088.h
+++ b/sound/soc/codecs/max98088.h
@@ -133,6 +133,19 @@
#define M98088_REC_LINEMODE (1<<7)
#define M98088_REC_LINEMODE_MASK (1<<7)
+/* M98088_REG_2D_MIX_SPK_CNTL */
+ #define M98088_MIX_SPKR_GAIN_MASK (3<<2)
+ #define M98088_MIX_SPKR_GAIN_SHIFT 2
+ #define M98088_MIX_SPKL_GAIN_MASK (3<<0)
+ #define M98088_MIX_SPKL_GAIN_SHIFT 0
+
+/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
+ #define M98088_DAI_MUTE (1<<7)
+ #define M98088_DAI_MUTE_MASK (1<<7)
+ #define M98088_DAI_VOICE_GAIN_MASK (3<<4)
+ #define M98088_DAI_ATTENUATION_MASK (0xF<<0)
+ #define M98088_DAI_ATTENUATION_SHIFT 0
+
/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
#define M98088_MICPRE_MASK (3<<5)
#define M98088_MICPRE_SHIFT 5
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
new file mode 100644
index 00000000000..e1d282d477d
--- /dev/null
+++ b/sound/soc/codecs/max98095.c
@@ -0,0 +1,2396 @@
+/*
+ * max98095.c -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98095.h>
+#include "max98095.h"
+
+enum max98095_type {
+ MAX98095,
+};
+
+struct max98095_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+ int bq_sel;
+};
+
+struct max98095_priv {
+ enum max98095_type devtype;
+ void *control_data;
+ struct max98095_pdata *pdata;
+ unsigned int sysclk;
+ struct max98095_cdata dai[3];
+ const char **eq_texts;
+ const char **bq_texts;
+ struct soc_enum eq_enum;
+ struct soc_enum bq_enum;
+ int eq_textcnt;
+ int bq_textcnt;
+ u8 lin_state;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+};
+
+static const u8 max98095_reg_def[M98095_REG_CNT] = {
+ 0x00, /* 00 */
+ 0x00, /* 01 */
+ 0x00, /* 02 */
+ 0x00, /* 03 */
+ 0x00, /* 04 */
+ 0x00, /* 05 */
+ 0x00, /* 06 */
+ 0x00, /* 07 */
+ 0x00, /* 08 */
+ 0x00, /* 09 */
+ 0x00, /* 0A */
+ 0x00, /* 0B */
+ 0x00, /* 0C */
+ 0x00, /* 0D */
+ 0x00, /* 0E */
+ 0x00, /* 0F */
+ 0x00, /* 10 */
+ 0x00, /* 11 */
+ 0x00, /* 12 */
+ 0x00, /* 13 */
+ 0x00, /* 14 */
+ 0x00, /* 15 */
+ 0x00, /* 16 */
+ 0x00, /* 17 */
+ 0x00, /* 18 */
+ 0x00, /* 19 */
+ 0x00, /* 1A */
+ 0x00, /* 1B */
+ 0x00, /* 1C */
+ 0x00, /* 1D */
+ 0x00, /* 1E */
+ 0x00, /* 1F */
+ 0x00, /* 20 */
+ 0x00, /* 21 */
+ 0x00, /* 22 */
+ 0x00, /* 23 */
+ 0x00, /* 24 */
+ 0x00, /* 25 */
+ 0x00, /* 26 */
+ 0x00, /* 27 */
+ 0x00, /* 28 */
+ 0x00, /* 29 */
+ 0x00, /* 2A */
+ 0x00, /* 2B */
+ 0x00, /* 2C */
+ 0x00, /* 2D */
+ 0x00, /* 2E */
+ 0x00, /* 2F */
+ 0x00, /* 30 */
+ 0x00, /* 31 */
+ 0x00, /* 32 */
+ 0x00, /* 33 */
+ 0x00, /* 34 */
+ 0x00, /* 35 */
+ 0x00, /* 36 */
+ 0x00, /* 37 */
+ 0x00, /* 38 */
+ 0x00, /* 39 */
+ 0x00, /* 3A */
+ 0x00, /* 3B */
+ 0x00, /* 3C */
+ 0x00, /* 3D */
+ 0x00, /* 3E */
+ 0x00, /* 3F */
+ 0x00, /* 40 */
+ 0x00, /* 41 */
+ 0x00, /* 42 */
+ 0x00, /* 43 */
+ 0x00, /* 44 */
+ 0x00, /* 45 */
+ 0x00, /* 46 */
+ 0x00, /* 47 */
+ 0x00, /* 48 */
+ 0x00, /* 49 */
+ 0x00, /* 4A */
+ 0x00, /* 4B */
+ 0x00, /* 4C */
+ 0x00, /* 4D */
+ 0x00, /* 4E */
+ 0x00, /* 4F */
+ 0x00, /* 50 */
+ 0x00, /* 51 */
+ 0x00, /* 52 */
+ 0x00, /* 53 */
+ 0x00, /* 54 */
+ 0x00, /* 55 */
+ 0x00, /* 56 */
+ 0x00, /* 57 */
+ 0x00, /* 58 */
+ 0x00, /* 59 */
+ 0x00, /* 5A */
+ 0x00, /* 5B */
+ 0x00, /* 5C */
+ 0x00, /* 5D */
+ 0x00, /* 5E */
+ 0x00, /* 5F */
+ 0x00, /* 60 */
+ 0x00, /* 61 */
+ 0x00, /* 62 */
+ 0x00, /* 63 */
+ 0x00, /* 64 */
+ 0x00, /* 65 */
+ 0x00, /* 66 */
+ 0x00, /* 67 */
+ 0x00, /* 68 */
+ 0x00, /* 69 */
+ 0x00, /* 6A */
+ 0x00, /* 6B */
+ 0x00, /* 6C */
+ 0x00, /* 6D */
+ 0x00, /* 6E */
+ 0x00, /* 6F */
+ 0x00, /* 70 */
+ 0x00, /* 71 */
+ 0x00, /* 72 */
+ 0x00, /* 73 */
+ 0x00, /* 74 */
+ 0x00, /* 75 */
+ 0x00, /* 76 */
+ 0x00, /* 77 */
+ 0x00, /* 78 */
+ 0x00, /* 79 */
+ 0x00, /* 7A */
+ 0x00, /* 7B */
+ 0x00, /* 7C */
+ 0x00, /* 7D */
+ 0x00, /* 7E */
+ 0x00, /* 7F */
+ 0x00, /* 80 */
+ 0x00, /* 81 */
+ 0x00, /* 82 */
+ 0x00, /* 83 */
+ 0x00, /* 84 */
+ 0x00, /* 85 */
+ 0x00, /* 86 */
+ 0x00, /* 87 */
+ 0x00, /* 88 */
+ 0x00, /* 89 */
+ 0x00, /* 8A */
+ 0x00, /* 8B */
+ 0x00, /* 8C */
+ 0x00, /* 8D */
+ 0x00, /* 8E */
+ 0x00, /* 8F */
+ 0x00, /* 90 */
+ 0x00, /* 91 */
+ 0x30, /* 92 */
+ 0xF0, /* 93 */
+ 0x00, /* 94 */
+ 0x00, /* 95 */
+ 0x3F, /* 96 */
+ 0x00, /* 97 */
+ 0x00, /* 98 */
+ 0x00, /* 99 */
+ 0x00, /* 9A */
+ 0x00, /* 9B */
+ 0x00, /* 9C */
+ 0x00, /* 9D */
+ 0x00, /* 9E */
+ 0x00, /* 9F */
+ 0x00, /* A0 */
+ 0x00, /* A1 */
+ 0x00, /* A2 */
+ 0x00, /* A3 */
+ 0x00, /* A4 */
+ 0x00, /* A5 */
+ 0x00, /* A6 */
+ 0x00, /* A7 */
+ 0x00, /* A8 */
+ 0x00, /* A9 */
+ 0x00, /* AA */
+ 0x00, /* AB */
+ 0x00, /* AC */
+ 0x00, /* AD */
+ 0x00, /* AE */
+ 0x00, /* AF */
+ 0x00, /* B0 */
+ 0x00, /* B1 */
+ 0x00, /* B2 */
+ 0x00, /* B3 */
+ 0x00, /* B4 */
+ 0x00, /* B5 */
+ 0x00, /* B6 */
+ 0x00, /* B7 */
+ 0x00, /* B8 */
+ 0x00, /* B9 */
+ 0x00, /* BA */
+ 0x00, /* BB */
+ 0x00, /* BC */
+ 0x00, /* BD */
+ 0x00, /* BE */
+ 0x00, /* BF */
+ 0x00, /* C0 */
+ 0x00, /* C1 */
+ 0x00, /* C2 */
+ 0x00, /* C3 */
+ 0x00, /* C4 */
+ 0x00, /* C5 */
+ 0x00, /* C6 */
+ 0x00, /* C7 */
+ 0x00, /* C8 */
+ 0x00, /* C9 */
+ 0x00, /* CA */
+ 0x00, /* CB */
+ 0x00, /* CC */
+ 0x00, /* CD */
+ 0x00, /* CE */
+ 0x00, /* CF */
+ 0x00, /* D0 */
+ 0x00, /* D1 */
+ 0x00, /* D2 */
+ 0x00, /* D3 */
+ 0x00, /* D4 */
+ 0x00, /* D5 */
+ 0x00, /* D6 */
+ 0x00, /* D7 */
+ 0x00, /* D8 */
+ 0x00, /* D9 */
+ 0x00, /* DA */
+ 0x00, /* DB */
+ 0x00, /* DC */
+ 0x00, /* DD */
+ 0x00, /* DE */
+ 0x00, /* DF */
+ 0x00, /* E0 */
+ 0x00, /* E1 */
+ 0x00, /* E2 */
+ 0x00, /* E3 */
+ 0x00, /* E4 */
+ 0x00, /* E5 */
+ 0x00, /* E6 */
+ 0x00, /* E7 */
+ 0x00, /* E8 */
+ 0x00, /* E9 */
+ 0x00, /* EA */
+ 0x00, /* EB */
+ 0x00, /* EC */
+ 0x00, /* ED */
+ 0x00, /* EE */
+ 0x00, /* EF */
+ 0x00, /* F0 */
+ 0x00, /* F1 */
+ 0x00, /* F2 */
+ 0x00, /* F3 */
+ 0x00, /* F4 */
+ 0x00, /* F5 */
+ 0x00, /* F6 */
+ 0x00, /* F7 */
+ 0x00, /* F8 */
+ 0x00, /* F9 */
+ 0x00, /* FA */
+ 0x00, /* FB */
+ 0x00, /* FC */
+ 0x00, /* FD */
+ 0x00, /* FE */
+ 0x00, /* FF */
+};
+
+static struct {
+ int readable;
+ int writable;
+} max98095_access[M98095_REG_CNT] = {
+ { 0x00, 0x00 }, /* 00 */
+ { 0xFF, 0x00 }, /* 01 */
+ { 0xFF, 0x00 }, /* 02 */
+ { 0xFF, 0x00 }, /* 03 */
+ { 0xFF, 0x00 }, /* 04 */
+ { 0xFF, 0x00 }, /* 05 */
+ { 0xFF, 0x00 }, /* 06 */
+ { 0xFF, 0x00 }, /* 07 */
+ { 0xFF, 0x00 }, /* 08 */
+ { 0xFF, 0x00 }, /* 09 */
+ { 0xFF, 0x00 }, /* 0A */
+ { 0xFF, 0x00 }, /* 0B */
+ { 0xFF, 0x00 }, /* 0C */
+ { 0xFF, 0x00 }, /* 0D */
+ { 0xFF, 0x00 }, /* 0E */
+ { 0xFF, 0x9F }, /* 0F */
+ { 0xFF, 0xFF }, /* 10 */
+ { 0xFF, 0xFF }, /* 11 */
+ { 0xFF, 0xFF }, /* 12 */
+ { 0xFF, 0xFF }, /* 13 */
+ { 0xFF, 0xFF }, /* 14 */
+ { 0xFF, 0xFF }, /* 15 */
+ { 0xFF, 0xFF }, /* 16 */
+ { 0xFF, 0xFF }, /* 17 */
+ { 0xFF, 0xFF }, /* 18 */
+ { 0xFF, 0xFF }, /* 19 */
+ { 0xFF, 0xFF }, /* 1A */
+ { 0xFF, 0xFF }, /* 1B */
+ { 0xFF, 0xFF }, /* 1C */
+ { 0xFF, 0xFF }, /* 1D */
+ { 0xFF, 0x77 }, /* 1E */
+ { 0xFF, 0x77 }, /* 1F */
+ { 0xFF, 0x77 }, /* 20 */
+ { 0xFF, 0x77 }, /* 21 */
+ { 0xFF, 0x77 }, /* 22 */
+ { 0xFF, 0x77 }, /* 23 */
+ { 0xFF, 0xFF }, /* 24 */
+ { 0xFF, 0x7F }, /* 25 */
+ { 0xFF, 0x31 }, /* 26 */
+ { 0xFF, 0xFF }, /* 27 */
+ { 0xFF, 0xFF }, /* 28 */
+ { 0xFF, 0xFF }, /* 29 */
+ { 0xFF, 0xF7 }, /* 2A */
+ { 0xFF, 0x2F }, /* 2B */
+ { 0xFF, 0xEF }, /* 2C */
+ { 0xFF, 0xFF }, /* 2D */
+ { 0xFF, 0xFF }, /* 2E */
+ { 0xFF, 0xFF }, /* 2F */
+ { 0xFF, 0xFF }, /* 30 */
+ { 0xFF, 0xFF }, /* 31 */
+ { 0xFF, 0xFF }, /* 32 */
+ { 0xFF, 0xFF }, /* 33 */
+ { 0xFF, 0xF7 }, /* 34 */
+ { 0xFF, 0x2F }, /* 35 */
+ { 0xFF, 0xCF }, /* 36 */
+ { 0xFF, 0xFF }, /* 37 */
+ { 0xFF, 0xFF }, /* 38 */
+ { 0xFF, 0xFF }, /* 39 */
+ { 0xFF, 0xFF }, /* 3A */
+ { 0xFF, 0xFF }, /* 3B */
+ { 0xFF, 0xFF }, /* 3C */
+ { 0xFF, 0xFF }, /* 3D */
+ { 0xFF, 0xF7 }, /* 3E */
+ { 0xFF, 0x2F }, /* 3F */
+ { 0xFF, 0xCF }, /* 40 */
+ { 0xFF, 0xFF }, /* 41 */
+ { 0xFF, 0x77 }, /* 42 */
+ { 0xFF, 0xFF }, /* 43 */
+ { 0xFF, 0xFF }, /* 44 */
+ { 0xFF, 0xFF }, /* 45 */
+ { 0xFF, 0xFF }, /* 46 */
+ { 0xFF, 0xFF }, /* 47 */
+ { 0xFF, 0xFF }, /* 48 */
+ { 0xFF, 0x0F }, /* 49 */
+ { 0xFF, 0xFF }, /* 4A */
+ { 0xFF, 0xFF }, /* 4B */
+ { 0xFF, 0x3F }, /* 4C */
+ { 0xFF, 0x3F }, /* 4D */
+ { 0xFF, 0x3F }, /* 4E */
+ { 0xFF, 0xFF }, /* 4F */
+ { 0xFF, 0x7F }, /* 50 */
+ { 0xFF, 0x7F }, /* 51 */
+ { 0xFF, 0x0F }, /* 52 */
+ { 0xFF, 0x3F }, /* 53 */
+ { 0xFF, 0x3F }, /* 54 */
+ { 0xFF, 0x3F }, /* 55 */
+ { 0xFF, 0xFF }, /* 56 */
+ { 0xFF, 0xFF }, /* 57 */
+ { 0xFF, 0xBF }, /* 58 */
+ { 0xFF, 0x1F }, /* 59 */
+ { 0xFF, 0xBF }, /* 5A */
+ { 0xFF, 0x1F }, /* 5B */
+ { 0xFF, 0xBF }, /* 5C */
+ { 0xFF, 0x3F }, /* 5D */
+ { 0xFF, 0x3F }, /* 5E */
+ { 0xFF, 0x7F }, /* 5F */
+ { 0xFF, 0x7F }, /* 60 */
+ { 0xFF, 0x47 }, /* 61 */
+ { 0xFF, 0x9F }, /* 62 */
+ { 0xFF, 0x9F }, /* 63 */
+ { 0xFF, 0x9F }, /* 64 */
+ { 0xFF, 0x9F }, /* 65 */
+ { 0xFF, 0x9F }, /* 66 */
+ { 0xFF, 0xBF }, /* 67 */
+ { 0xFF, 0xBF }, /* 68 */
+ { 0xFF, 0xFF }, /* 69 */
+ { 0xFF, 0xFF }, /* 6A */
+ { 0xFF, 0x7F }, /* 6B */
+ { 0xFF, 0xF7 }, /* 6C */
+ { 0xFF, 0xFF }, /* 6D */
+ { 0xFF, 0xFF }, /* 6E */
+ { 0xFF, 0x1F }, /* 6F */
+ { 0xFF, 0xF7 }, /* 70 */
+ { 0xFF, 0xFF }, /* 71 */
+ { 0xFF, 0xFF }, /* 72 */
+ { 0xFF, 0x1F }, /* 73 */
+ { 0xFF, 0xF7 }, /* 74 */
+ { 0xFF, 0xFF }, /* 75 */
+ { 0xFF, 0xFF }, /* 76 */
+ { 0xFF, 0x1F }, /* 77 */
+ { 0xFF, 0xF7 }, /* 78 */
+ { 0xFF, 0xFF }, /* 79 */
+ { 0xFF, 0xFF }, /* 7A */
+ { 0xFF, 0x1F }, /* 7B */
+ { 0xFF, 0xF7 }, /* 7C */
+ { 0xFF, 0xFF }, /* 7D */
+ { 0xFF, 0xFF }, /* 7E */
+ { 0xFF, 0x1F }, /* 7F */
+ { 0xFF, 0xF7 }, /* 80 */
+ { 0xFF, 0xFF }, /* 81 */
+ { 0xFF, 0xFF }, /* 82 */
+ { 0xFF, 0x1F }, /* 83 */
+ { 0xFF, 0x7F }, /* 84 */
+ { 0xFF, 0x0F }, /* 85 */
+ { 0xFF, 0xD8 }, /* 86 */
+ { 0xFF, 0xFF }, /* 87 */
+ { 0xFF, 0xEF }, /* 88 */
+ { 0xFF, 0xFE }, /* 89 */
+ { 0xFF, 0xFE }, /* 8A */
+ { 0xFF, 0xFF }, /* 8B */
+ { 0xFF, 0xFF }, /* 8C */
+ { 0xFF, 0x3F }, /* 8D */
+ { 0xFF, 0xFF }, /* 8E */
+ { 0xFF, 0x3F }, /* 8F */
+ { 0xFF, 0x8F }, /* 90 */
+ { 0xFF, 0xFF }, /* 91 */
+ { 0xFF, 0x3F }, /* 92 */
+ { 0xFF, 0xFF }, /* 93 */
+ { 0xFF, 0xFF }, /* 94 */
+ { 0xFF, 0x0F }, /* 95 */
+ { 0xFF, 0x3F }, /* 96 */
+ { 0xFF, 0x8C }, /* 97 */
+ { 0x00, 0x00 }, /* 98 */
+ { 0x00, 0x00 }, /* 99 */
+ { 0x00, 0x00 }, /* 9A */
+ { 0x00, 0x00 }, /* 9B */
+ { 0x00, 0x00 }, /* 9C */
+ { 0x00, 0x00 }, /* 9D */
+ { 0x00, 0x00 }, /* 9E */
+ { 0x00, 0x00 }, /* 9F */
+ { 0x00, 0x00 }, /* A0 */
+ { 0x00, 0x00 }, /* A1 */
+ { 0x00, 0x00 }, /* A2 */
+ { 0x00, 0x00 }, /* A3 */
+ { 0x00, 0x00 }, /* A4 */
+ { 0x00, 0x00 }, /* A5 */
+ { 0x00, 0x00 }, /* A6 */
+ { 0x00, 0x00 }, /* A7 */
+ { 0x00, 0x00 }, /* A8 */
+ { 0x00, 0x00 }, /* A9 */
+ { 0x00, 0x00 }, /* AA */
+ { 0x00, 0x00 }, /* AB */
+ { 0x00, 0x00 }, /* AC */
+ { 0x00, 0x00 }, /* AD */
+ { 0x00, 0x00 }, /* AE */
+ { 0x00, 0x00 }, /* AF */
+ { 0x00, 0x00 }, /* B0 */
+ { 0x00, 0x00 }, /* B1 */
+ { 0x00, 0x00 }, /* B2 */
+ { 0x00, 0x00 }, /* B3 */
+ { 0x00, 0x00 }, /* B4 */
+ { 0x00, 0x00 }, /* B5 */
+ { 0x00, 0x00 }, /* B6 */
+ { 0x00, 0x00 }, /* B7 */
+ { 0x00, 0x00 }, /* B8 */
+ { 0x00, 0x00 }, /* B9 */
+ { 0x00, 0x00 }, /* BA */
+ { 0x00, 0x00 }, /* BB */
+ { 0x00, 0x00 }, /* BC */
+ { 0x00, 0x00 }, /* BD */
+ { 0x00, 0x00 }, /* BE */
+ { 0x00, 0x00 }, /* BF */
+ { 0x00, 0x00 }, /* C0 */
+ { 0x00, 0x00 }, /* C1 */
+ { 0x00, 0x00 }, /* C2 */
+ { 0x00, 0x00 }, /* C3 */
+ { 0x00, 0x00 }, /* C4 */
+ { 0x00, 0x00 }, /* C5 */
+ { 0x00, 0x00 }, /* C6 */
+ { 0x00, 0x00 }, /* C7 */
+ { 0x00, 0x00 }, /* C8 */
+ { 0x00, 0x00 }, /* C9 */
+ { 0x00, 0x00 }, /* CA */
+ { 0x00, 0x00 }, /* CB */
+ { 0x00, 0x00 }, /* CC */
+ { 0x00, 0x00 }, /* CD */
+ { 0x00, 0x00 }, /* CE */
+ { 0x00, 0x00 }, /* CF */
+ { 0x00, 0x00 }, /* D0 */
+ { 0x00, 0x00 }, /* D1 */
+ { 0x00, 0x00 }, /* D2 */
+ { 0x00, 0x00 }, /* D3 */
+ { 0x00, 0x00 }, /* D4 */
+ { 0x00, 0x00 }, /* D5 */
+ { 0x00, 0x00 }, /* D6 */
+ { 0x00, 0x00 }, /* D7 */
+ { 0x00, 0x00 }, /* D8 */
+ { 0x00, 0x00 }, /* D9 */
+ { 0x00, 0x00 }, /* DA */
+ { 0x00, 0x00 }, /* DB */
+ { 0x00, 0x00 }, /* DC */
+ { 0x00, 0x00 }, /* DD */
+ { 0x00, 0x00 }, /* DE */
+ { 0x00, 0x00 }, /* DF */
+ { 0x00, 0x00 }, /* E0 */
+ { 0x00, 0x00 }, /* E1 */
+ { 0x00, 0x00 }, /* E2 */
+ { 0x00, 0x00 }, /* E3 */
+ { 0x00, 0x00 }, /* E4 */
+ { 0x00, 0x00 }, /* E5 */
+ { 0x00, 0x00 }, /* E6 */
+ { 0x00, 0x00 }, /* E7 */
+ { 0x00, 0x00 }, /* E8 */
+ { 0x00, 0x00 }, /* E9 */
+ { 0x00, 0x00 }, /* EA */
+ { 0x00, 0x00 }, /* EB */
+ { 0x00, 0x00 }, /* EC */
+ { 0x00, 0x00 }, /* ED */
+ { 0x00, 0x00 }, /* EE */
+ { 0x00, 0x00 }, /* EF */
+ { 0x00, 0x00 }, /* F0 */
+ { 0x00, 0x00 }, /* F1 */
+ { 0x00, 0x00 }, /* F2 */
+ { 0x00, 0x00 }, /* F3 */
+ { 0x00, 0x00 }, /* F4 */
+ { 0x00, 0x00 }, /* F5 */
+ { 0x00, 0x00 }, /* F6 */
+ { 0x00, 0x00 }, /* F7 */
+ { 0x00, 0x00 }, /* F8 */
+ { 0x00, 0x00 }, /* F9 */
+ { 0x00, 0x00 }, /* FA */
+ { 0x00, 0x00 }, /* FB */
+ { 0x00, 0x00 }, /* FC */
+ { 0x00, 0x00 }, /* FD */
+ { 0x00, 0x00 }, /* FE */
+ { 0xFF, 0x00 }, /* FF */
+};
+
+static int max98095_readable(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg >= M98095_REG_CNT)
+ return 0;
+ return max98095_access[reg].readable != 0;
+}
+
+static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg > M98095_REG_MAX_CACHED)
+ return 1;
+
+ switch (reg) {
+ case M98095_000_HOST_DATA:
+ case M98095_001_HOST_INT_STS:
+ case M98095_002_HOST_RSP_STS:
+ case M98095_003_HOST_CMD_STS:
+ case M98095_004_CODEC_STS:
+ case M98095_005_DAI1_ALC_STS:
+ case M98095_006_DAI2_ALC_STS:
+ case M98095_007_JACK_AUTO_STS:
+ case M98095_008_JACK_MANUAL_STS:
+ case M98095_009_JACK_VBAT_STS:
+ case M98095_00A_ACC_ADC_STS:
+ case M98095_00B_MIC_NG_AGC_STS:
+ case M98095_00C_SPK_L_VOLT_STS:
+ case M98095_00D_SPK_R_VOLT_STS:
+ case M98095_00E_TEMP_SENSOR_STS:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Filter coefficients are in a separate register segment
+ * and they share the address space of the normal registers.
+ * The coefficient registers do not need or share the cache.
+ */
+static int max98095_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ data[0] = reg;
+ data[1] = value;
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 4);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ max98095_hw_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
+ max98095_hw_write(codec, eq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+/*
+ * Load biquad filter coefficient configurations registers
+ */
+static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int bq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 1);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ bq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ max98095_hw_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
+ max98095_hw_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+static const char * const max98095_fltr_mode[] = { "Voice", "Music" };
+static const struct soc_enum max98095_dai1_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 7, 2, max98095_fltr_mode),
+};
+static const struct soc_enum max98095_dai2_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 7, 2, max98095_fltr_mode),
+};
+
+static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static const struct soc_enum max98095_extmic_enum =
+ SOC_ENUM_SINGLE(M98095_087_CFG_MIC, 0, 3, max98095_extmic_text);
+
+static const struct snd_kcontrol_new max98095_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum);
+
+static const char * const max98095_linein_text[] = { "INA", "INB" };
+
+static const struct soc_enum max98095_linein_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 6, 2, max98095_linein_text);
+
+static const struct snd_kcontrol_new max98095_linein_mux =
+ SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum);
+
+static const char * const max98095_line_mode_text[] = {
+ "Stereo", "Differential"};
+
+static const struct soc_enum max98095_linein_mode_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 7, 2, max98095_line_mode_text);
+
+static const struct soc_enum max98095_lineout_mode_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 4, 2, max98095_line_mode_text);
+
+static const char * const max98095_dai_fltr[] = {
+ "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k",
+ "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"};
+static const struct soc_enum max98095_dai1_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 0, 6, max98095_dai_fltr),
+};
+static const struct soc_enum max98095_dai2_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 0, 6, max98095_dai_fltr),
+};
+static const struct soc_enum max98095_dai3_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_042_DAI3_FILTERS, 0, 6, max98095_dai_fltr),
+};
+
+static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic1pre = sel;
+ snd_soc_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98095->mic1pre;
+ return 0;
+}
+
+static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic2pre = sel;
+ snd_soc_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98095->mic2pre;
+ return 0;
+}
+
+static const unsigned int max98095_micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0);
+
+static const unsigned int max98095_hp_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
+};
+
+static const unsigned int max98095_spk_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0),
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_rcv_lout_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_lin_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
+};
+
+static const struct snd_kcontrol_new max98095_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv),
+
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv),
+
+ SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV,
+ 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 7, 1, 1),
+
+ SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 7, 1, 1),
+
+ SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1),
+
+ SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 7, 1, 1),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98095_05F_LVL_MIC1, 5, 2, 0,
+ max98095_mic1pre_get, max98095_mic1pre_set,
+ max98095_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98095_060_LVL_MIC2, 5, 2, 0,
+ max98095_mic2pre_get, max98095_mic2pre_set,
+ max98095_micboost_tlv),
+
+ SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1,
+ max98095_lin_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1,
+ max98095_adc_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1,
+ max98095_adc_tlv),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0,
+ max98095_adcboost_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0,
+ max98095_adcboost_tlv),
+
+ SOC_SINGLE("EQ1 Switch", M98095_088_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98095_088_CFG_LEVEL, 1, 1, 0),
+
+ SOC_SINGLE("Biquad1 Switch", M98095_088_CFG_LEVEL, 2, 1, 0),
+ SOC_SINGLE("Biquad2 Switch", M98095_088_CFG_LEVEL, 3, 1, 0),
+
+ SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum),
+ SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum),
+ SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum),
+ SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum),
+
+ SOC_ENUM("Linein Mode", max98095_linein_mode_enum),
+ SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0),
+};
+
+/* Receiver earpiece mixer switch */
+static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0),
+};
+
+/* Left lineout mixer switch */
+static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0),
+};
+
+/* Right lineout mixer switch */
+static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0),
+};
+
+static int max98095_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->reg == M98095_05F_LVL_MIC1) {
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic2pre)<<M98095_MICPRE_SHIFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are stereo inputs with the left and right
+ * channels sharing a common PGA power control signal.
+ */
+static int max98095_line_pga(struct snd_soc_dapm_widget *w,
+ int event, u8 channel)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ u8 *state;
+
+ BUG_ON(!((channel == 1) || (channel == 2)));
+
+ state = &max98095->lin_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), (1 << w->shift));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ *state &= ~channel;
+ if (*state == 0) {
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 1);
+}
+
+static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 2);
+}
+
+/*
+ * The stereo line out mixer outputs to two stereo line outs.
+ * The 2nd pair has a separate set of enables.
+ */
+static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), (1 << (w->shift+2)));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACM2", "Aux Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+ SND_SOC_DAPM_DAC("DACM3", "Voice Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT,
+ 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT,
+ 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98095_extmic_mux),
+
+ SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0,
+ &max98095_linein_mux),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_mono_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98095_mono_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCV"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route max98095_audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Receiver Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Receiver Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Receiver Mixer", "IN1 Switch", "IN1 Input"},
+ {"Receiver Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left Lineout output mixer */
+ {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right lineout output mixer */
+ {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ {"HP Left Out", NULL, "Left Headphone Mixer"},
+ {"HP Right Out", NULL, "Right Headphone Mixer"},
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Mono Out", NULL, "Receiver Mixer"},
+ {"LINE Left Out", NULL, "Left Lineout Mixer"},
+ {"LINE Right Out", NULL, "Right Lineout Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCV", NULL, "RCV Mono Out"},
+ {"OUT1", NULL, "LINE Left Out"},
+ {"OUT2", NULL, "LINE Right Out"},
+ {"OUT3", NULL, "LINE Left Out"},
+ {"OUT4", NULL, "LINE Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+
+ {"IN1 Input", NULL, "INA1"},
+ {"IN2 Input", NULL, "INA2"},
+
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98095_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_add_controls(codec, max98095_snd_controls,
+ ARRAY_SIZE(max98095_snd_controls));
+
+ return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x01},
+ {11025, 0x02},
+ {16000, 0x03},
+ {22050, 0x04},
+ {24000, 0x05},
+ {32000, 0x06},
+ {44100, 0x07},
+ {48000, 0x08},
+ {88200, 0x09},
+ {96000, 0x0A},
+};
+
+static int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98095_dai1_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai2_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai3_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 max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[2];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98095->sysclk)
+ return 0;
+
+ max98095->sysclk = freq; /* remember current sysclk */
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 40000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ } else if ((freq >= 40000000) && (freq < 60000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98095->sysclk = freq;
+ return 0;
+}
+
+static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[1];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_035_DAI2_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[2];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_03F_DAI3_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = snd_soc_cache_sync(codec);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ M98095_MBEN, M98095_MBEN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ M98095_MBEN, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98095_dai1_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai1_set_fmt,
+ .hw_params = max98095_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98095_dai2_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai2_set_fmt,
+ .hw_params = max98095_dai2_hw_params,
+};
+
+static struct snd_soc_dai_ops max98095_dai3_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai3_set_fmt,
+ .hw_params = max98095_dai3_hw_params,
+};
+
+static struct snd_soc_dai_driver max98095_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai2_ops,
+},
+{
+ .name = "Voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai3_ops,
+}
+
+};
+
+static int max98095_get_eq_channel(const char *name)
+{
+ if (strcmp(name, "EQ1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "EQ2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+ struct max98095_eq_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ BUG_ON(channel > 1);
+
+ if (!pdata || !max98095->eq_textcnt)
+ return 0;
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->eq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98095->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ coef_set = &pdata->eq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&codec->mutex);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_eq_band(codec, channel, 0, coef_set->band1);
+ m98095_eq_band(codec, channel, 1, coef_set->band2);
+ m98095_eq_band(codec, channel, 2, coef_set->band3);
+ m98095_eq_band(codec, channel, 3, coef_set->band4);
+ m98095_eq_band(codec, channel, 4, coef_set->band5);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&codec->mutex);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ };
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->eq_textcnt = 0;
+ max98095->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->eq_texts,
+ sizeof(char *) * (max98095->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->eq_textcnt] = cfg[i].name;
+ max98095->eq_textcnt++;
+ max98095->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->eq_enum.texts = max98095->eq_texts;
+ max98095->eq_enum.max = max98095->eq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static int max98095_get_bq_channel(const char *name)
+{
+ if (strcmp(name, "Biquad1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "Biquad2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_bq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+ struct max98095_biquad_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ BUG_ON(channel > 1);
+
+ if (!pdata || !max98095->bq_textcnt)
+ return 0;
+
+ if (sel >= pdata->bq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->bq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->bq_cfgcnt; i++) {
+ if (strcmp(pdata->bq_cfg[i].name, max98095->bq_texts[sel]) == 0 &&
+ abs(pdata->bq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->bq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->bq_cfg[best].name,
+ pdata->bq_cfg[best].rate, fs);
+
+ coef_set = &pdata->bq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&codec->mutex);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_biquad_band(codec, channel, 0, coef_set->band1);
+ m98095_biquad_band(codec, channel, 1, coef_set->band2);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&codec->mutex);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98095_get_bq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->bq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_biquad_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("Biquad1 Mode",
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ SOC_ENUM_EXT("Biquad2 Mode",
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ };
+
+ cfg = pdata->bq_cfg;
+ cfgcnt = pdata->bq_cfgcnt;
+
+ /* Setup an array of texts for the biquad enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->bq_textcnt = 0;
+ max98095->bq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->bq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->bq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->bq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->bq_texts,
+ sizeof(char *) * (max98095->bq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->bq_textcnt] = cfg[i].name;
+ max98095->bq_textcnt++;
+ max98095->bq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->bq_enum.texts = max98095->bq_texts;
+ max98095->bq_enum.max = max98095->bq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add Biquad control: %d\n", ret);
+}
+
+static void max98095_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(codec->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98095_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98095_DIGMIC_R;
+
+ snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98095_handle_eq_pdata(codec);
+
+ /* Configure bi-quad filters */
+ if (pdata->bq_cfgcnt)
+ max98095_handle_bq_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98095_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98095_resume(struct snd_soc_codec *codec)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define max98095_suspend NULL
+#define max98095_resume NULL
+#endif
+
+static int max98095_reset(struct snd_soc_codec *codec)
+{
+ int i, ret;
+
+ /* Gracefully reset the DSP core and the codec hardware
+ * in a proper sequence */
+ ret = snd_soc_write(codec, M98095_00F_HOST_CFG, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset to hardware default for registers, as there is not
+ * a soft reset hardware control register */
+ for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) {
+ ret = snd_soc_write(codec, i, max98095_reg_def[i]);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int max98095_probe(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ int ret = 0;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* reset the codec, the DSP core, and disable all interrupts */
+ max98095_reset(codec);
+
+ /* initialize private data */
+
+ max98095->sysclk = (unsigned)-1;
+ max98095->eq_textcnt = 0;
+ max98095->bq_textcnt = 0;
+
+ cdata = &max98095->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[2];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ max98095->lin_state = 0;
+ max98095->mic1pre = 0;
+ max98095->mic2pre = 0;
+
+ ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_access;
+ }
+ dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+ snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+
+ /* initialize registers cache to hardware default */
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
+
+ snd_soc_write(codec, M98095_049_MIX_DAC_M,
+ M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
+
+ snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
+ snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
+ snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
+
+ snd_soc_write(codec, M98095_02C_DAI1_IOCFG,
+ M98095_S1NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ M98095_S2NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ M98095_S3NORMAL|M98095_SDATA);
+
+ max98095_handle_pdata(codec);
+
+ /* take the codec out of the shut down */
+ snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ M98095_SHDNRUN);
+
+ max98095_add_widgets(codec);
+
+err_access:
+ return ret;
+}
+
+static int max98095_remove(struct snd_soc_codec *codec)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98095 = {
+ .probe = max98095_probe,
+ .remove = max98095_remove,
+ .suspend = max98095_suspend,
+ .resume = max98095_resume,
+ .set_bias_level = max98095_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(max98095_reg_def),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = max98095_reg_def,
+ .readable_register = max98095_readable,
+ .volatile_register = max98095_volatile,
+ .dapm_widgets = max98095_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
+ .dapm_routes = max98095_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+};
+
+static int max98095_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98095_priv *max98095;
+ int ret;
+
+ max98095 = kzalloc(sizeof(struct max98095_priv), GFP_KERNEL);
+ if (max98095 == NULL)
+ return -ENOMEM;
+
+ max98095->devtype = id->driver_data;
+ i2c_set_clientdata(i2c, max98095);
+ max98095->control_data = i2c;
+ max98095->pdata = i2c->dev.platform_data;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_max98095, &max98095_dai[0], 3);
+ if (ret < 0)
+ kfree(max98095);
+ return ret;
+}
+
+static int __devexit max98095_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static const struct i2c_device_id max98095_i2c_id[] = {
+ { "max98095", MAX98095 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static struct i2c_driver max98095_i2c_driver = {
+ .driver = {
+ .name = "max98095",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98095_i2c_probe,
+ .remove = __devexit_p(max98095_i2c_remove),
+ .id_table = max98095_i2c_id,
+};
+
+static int __init max98095_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&max98095_i2c_driver);
+ if (ret)
+ pr_err("Failed to register max98095 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(max98095_init);
+
+static void __exit max98095_exit(void)
+{
+ i2c_del_driver(&max98095_i2c_driver);
+}
+module_exit(max98095_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98095 driver");
+MODULE_AUTHOR("Peter Hsiang");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
new file mode 100644
index 00000000000..891584a0eb0
--- /dev/null
+++ b/sound/soc/codecs/max98095.h
@@ -0,0 +1,299 @@
+/*
+ * max98095.h -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MAX98095_H
+#define _MAX98095_H
+
+/*
+ * MAX98095 Registers Definition
+ */
+
+#define M98095_000_HOST_DATA 0x00
+#define M98095_001_HOST_INT_STS 0x01
+#define M98095_002_HOST_RSP_STS 0x02
+#define M98095_003_HOST_CMD_STS 0x03
+#define M98095_004_CODEC_STS 0x04
+#define M98095_005_DAI1_ALC_STS 0x05
+#define M98095_006_DAI2_ALC_STS 0x06
+#define M98095_007_JACK_AUTO_STS 0x07
+#define M98095_008_JACK_MANUAL_STS 0x08
+#define M98095_009_JACK_VBAT_STS 0x09
+#define M98095_00A_ACC_ADC_STS 0x0A
+#define M98095_00B_MIC_NG_AGC_STS 0x0B
+#define M98095_00C_SPK_L_VOLT_STS 0x0C
+#define M98095_00D_SPK_R_VOLT_STS 0x0D
+#define M98095_00E_TEMP_SENSOR_STS 0x0E
+#define M98095_00F_HOST_CFG 0x0F
+#define M98095_010_HOST_INT_CFG 0x10
+#define M98095_011_HOST_INT_EN 0x11
+#define M98095_012_CODEC_INT_EN 0x12
+#define M98095_013_JACK_INT_EN 0x13
+#define M98095_014_JACK_INT_EN 0x14
+#define M98095_015_DEC 0x15
+#define M98095_016_RESERVED 0x16
+#define M98095_017_RESERVED 0x17
+#define M98095_018_KEYCODE3 0x18
+#define M98095_019_KEYCODE2 0x19
+#define M98095_01A_KEYCODE1 0x1A
+#define M98095_01B_KEYCODE0 0x1B
+#define M98095_01C_OEMCODE1 0x1C
+#define M98095_01D_OEMCODE0 0x1D
+#define M98095_01E_XCFG1 0x1E
+#define M98095_01F_XCFG2 0x1F
+#define M98095_020_XCFG3 0x20
+#define M98095_021_XCFG4 0x21
+#define M98095_022_XCFG5 0x22
+#define M98095_023_XCFG6 0x23
+#define M98095_024_XGPIO 0x24
+#define M98095_025_XCLKCFG 0x25
+#define M98095_026_SYS_CLK 0x26
+#define M98095_027_DAI1_CLKMODE 0x27
+#define M98095_028_DAI1_CLKCFG_HI 0x28
+#define M98095_029_DAI1_CLKCFG_LO 0x29
+#define M98095_02A_DAI1_FORMAT 0x2A
+#define M98095_02B_DAI1_CLOCK 0x2B
+#define M98095_02C_DAI1_IOCFG 0x2C
+#define M98095_02D_DAI1_TDM 0x2D
+#define M98095_02E_DAI1_FILTERS 0x2E
+#define M98095_02F_DAI1_LVL1 0x2F
+#define M98095_030_DAI1_LVL2 0x30
+#define M98095_031_DAI2_CLKMODE 0x31
+#define M98095_032_DAI2_CLKCFG_HI 0x32
+#define M98095_033_DAI2_CLKCFG_LO 0x33
+#define M98095_034_DAI2_FORMAT 0x34
+#define M98095_035_DAI2_CLOCK 0x35
+#define M98095_036_DAI2_IOCFG 0x36
+#define M98095_037_DAI2_TDM 0x37
+#define M98095_038_DAI2_FILTERS 0x38
+#define M98095_039_DAI2_LVL1 0x39
+#define M98095_03A_DAI2_LVL2 0x3A
+#define M98095_03B_DAI3_CLKMODE 0x3B
+#define M98095_03C_DAI3_CLKCFG_HI 0x3C
+#define M98095_03D_DAI3_CLKCFG_LO 0x3D
+#define M98095_03E_DAI3_FORMAT 0x3E
+#define M98095_03F_DAI3_CLOCK 0x3F
+#define M98095_040_DAI3_IOCFG 0x40
+#define M98095_041_DAI3_TDM 0x41
+#define M98095_042_DAI3_FILTERS 0x42
+#define M98095_043_DAI3_LVL1 0x43
+#define M98095_044_DAI3_LVL2 0x44
+#define M98095_045_CFG_DSP 0x45
+#define M98095_046_DAC_CTRL1 0x46
+#define M98095_047_DAC_CTRL2 0x47
+#define M98095_048_MIX_DAC_LR 0x48
+#define M98095_049_MIX_DAC_M 0x49
+#define M98095_04A_MIX_ADC_LEFT 0x4A
+#define M98095_04B_MIX_ADC_RIGHT 0x4B
+#define M98095_04C_MIX_HP_LEFT 0x4C
+#define M98095_04D_MIX_HP_RIGHT 0x4D
+#define M98095_04E_CFG_HP 0x4E
+#define M98095_04F_MIX_RCV 0x4F
+#define M98095_050_MIX_SPK_LEFT 0x50
+#define M98095_051_MIX_SPK_RIGHT 0x51
+#define M98095_052_MIX_SPK_CFG 0x52
+#define M98095_053_MIX_LINEOUT1 0x53
+#define M98095_054_MIX_LINEOUT2 0x54
+#define M98095_055_MIX_LINEOUT_CFG 0x55
+#define M98095_056_LVL_SIDETONE_DAI12 0x56
+#define M98095_057_LVL_SIDETONE_DAI3 0x57
+#define M98095_058_LVL_DAI1_PLAY 0x58
+#define M98095_059_LVL_DAI1_EQ 0x59
+#define M98095_05A_LVL_DAI2_PLAY 0x5A
+#define M98095_05B_LVL_DAI2_EQ 0x5B
+#define M98095_05C_LVL_DAI3_PLAY 0x5C
+#define M98095_05D_LVL_ADC_L 0x5D
+#define M98095_05E_LVL_ADC_R 0x5E
+#define M98095_05F_LVL_MIC1 0x5F
+#define M98095_060_LVL_MIC2 0x60
+#define M98095_061_LVL_LINEIN 0x61
+#define M98095_062_LVL_LINEOUT1 0x62
+#define M98095_063_LVL_LINEOUT2 0x63
+#define M98095_064_LVL_HP_L 0x64
+#define M98095_065_LVL_HP_R 0x65
+#define M98095_066_LVL_RCV 0x66
+#define M98095_067_LVL_SPK_L 0x67
+#define M98095_068_LVL_SPK_R 0x68
+#define M98095_069_MICAGC_CFG 0x69
+#define M98095_06A_MICAGC_THRESH 0x6A
+#define M98095_06B_SPK_NOISEGATE 0x6B
+#define M98095_06C_DAI1_ALC1_TIME 0x6C
+#define M98095_06D_DAI1_ALC1_COMP 0x6D
+#define M98095_06E_DAI1_ALC1_EXPN 0x6E
+#define M98095_06F_DAI1_ALC1_GAIN 0x6F
+#define M98095_070_DAI1_ALC2_TIME 0x70
+#define M98095_071_DAI1_ALC2_COMP 0x71
+#define M98095_072_DAI1_ALC2_EXPN 0x72
+#define M98095_073_DAI1_ALC2_GAIN 0x73
+#define M98095_074_DAI1_ALC3_TIME 0x74
+#define M98095_075_DAI1_ALC3_COMP 0x75
+#define M98095_076_DAI1_ALC3_EXPN 0x76
+#define M98095_077_DAI1_ALC3_GAIN 0x77
+#define M98095_078_DAI2_ALC1_TIME 0x78
+#define M98095_079_DAI2_ALC1_COMP 0x79
+#define M98095_07A_DAI2_ALC1_EXPN 0x7A
+#define M98095_07B_DAI2_ALC1_GAIN 0x7B
+#define M98095_07C_DAI2_ALC2_TIME 0x7C
+#define M98095_07D_DAI2_ALC2_COMP 0x7D
+#define M98095_07E_DAI2_ALC2_EXPN 0x7E
+#define M98095_07F_DAI2_ALC2_GAIN 0x7F
+#define M98095_080_DAI2_ALC3_TIME 0x80
+#define M98095_081_DAI2_ALC3_COMP 0x81
+#define M98095_082_DAI2_ALC3_EXPN 0x82
+#define M98095_083_DAI2_ALC3_GAIN 0x83
+#define M98095_084_HP_NOISE_GATE 0x84
+#define M98095_085_AUX_ADC 0x85
+#define M98095_086_CFG_LINE 0x86
+#define M98095_087_CFG_MIC 0x87
+#define M98095_088_CFG_LEVEL 0x88
+#define M98095_089_JACK_DET_AUTO 0x89
+#define M98095_08A_JACK_DET_MANUAL 0x8A
+#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
+#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
+#define M98095_08D_JACK_KEY_THRESH 0x8D
+#define M98095_08E_JACK_DC_SLEW 0x8E
+#define M98095_08F_JACK_TEST_CFG 0x8F
+#define M98095_090_PWR_EN_IN 0x90
+#define M98095_091_PWR_EN_OUT 0x91
+#define M98095_092_PWR_EN_OUT 0x92
+#define M98095_093_BIAS_CTRL 0x93
+#define M98095_094_PWR_DAC_21 0x94
+#define M98095_095_PWR_DAC_03 0x95
+#define M98095_096_PWR_DAC_CK 0x96
+#define M98095_097_PWR_SYS 0x97
+
+#define M98095_0FF_REV_ID 0xFF
+
+#define M98095_REG_CNT (0xFF+1)
+#define M98095_REG_MAX_CACHED 0X97
+
+/* MAX98095 Registers Bit Fields */
+
+/* M98095_00F_HOST_CFG */
+ #define M98095_SEG (1<<0)
+ #define M98095_XTEN (1<<1)
+ #define M98095_MDLLEN (1<<2)
+
+/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
+ #define M98095_CLKMODE_MASK 0xFF
+
+/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
+ #define M98095_DAI_MAS (1<<7)
+ #define M98095_DAI_WCI (1<<6)
+ #define M98095_DAI_BCI (1<<5)
+ #define M98095_DAI_DLY (1<<4)
+ #define M98095_DAI_TDM (1<<2)
+ #define M98095_DAI_FSW (1<<1)
+ #define M98095_DAI_WS (1<<0)
+
+/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
+ #define M98095_DAI_BSEL64 (1<<0)
+ #define M98095_DAI_DOSR_DIV2 (0<<5)
+ #define M98095_DAI_DOSR_DIV4 (1<<5)
+
+/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
+ #define M98095_S1NORMAL (1<<6)
+ #define M98095_S2NORMAL (2<<6)
+ #define M98095_S3NORMAL (3<<6)
+ #define M98095_SDATA (3<<0)
+
+/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
+ #define M98095_DAI_DHF (1<<3)
+
+/* M98095_045_DSP_CFG */
+ #define M98095_DSPNORMAL (5<<4)
+
+/* M98095_048_MIX_DAC_LR */
+ #define M98095_DAI1L_TO_DACR (1<<7)
+ #define M98095_DAI1R_TO_DACR (1<<6)
+ #define M98095_DAI2M_TO_DACR (1<<5)
+ #define M98095_DAI1L_TO_DACL (1<<3)
+ #define M98095_DAI1R_TO_DACL (1<<2)
+ #define M98095_DAI2M_TO_DACL (1<<1)
+ #define M98095_DAI3M_TO_DACL (1<<0)
+
+/* M98095_049_MIX_DAC_M */
+ #define M98095_DAI1L_TO_DACM (1<<3)
+ #define M98095_DAI1R_TO_DACM (1<<2)
+ #define M98095_DAI2M_TO_DACM (1<<1)
+ #define M98095_DAI3M_TO_DACM (1<<0)
+
+/* M98095_04E_MIX_HP_CFG */
+ #define M98095_HPNORMAL (3<<4)
+
+/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
+ #define M98095_MICPRE_MASK (3<<5)
+ #define M98095_MICPRE_SHIFT 5
+
+/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
+ #define M98095_HP_MUTE (1<<7)
+
+/* M98095_066_LVL_RCV */
+ #define M98095_REC_MUTE (1<<7)
+
+/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
+ #define M98095_SP_MUTE (1<<7)
+
+/* M98095_087_CFG_MIC */
+ #define M98095_MICSEL_MASK (3<<0)
+ #define M98095_DIGMIC_L (1<<2)
+ #define M98095_DIGMIC_R (1<<3)
+ #define M98095_DIGMIC2L (1<<4)
+ #define M98095_DIGMIC2R (1<<5)
+
+/* M98095_088_CFG_LEVEL */
+ #define M98095_VSEN (1<<6)
+ #define M98095_ZDEN (1<<5)
+ #define M98095_BQ2EN (1<<3)
+ #define M98095_BQ1EN (1<<2)
+ #define M98095_EQ2EN (1<<1)
+ #define M98095_EQ1EN (1<<0)
+
+/* M98095_090_PWR_EN_IN */
+ #define M98095_INEN (1<<7)
+ #define M98095_MB2EN (1<<3)
+ #define M98095_MB1EN (1<<2)
+ #define M98095_MBEN (3<<2)
+ #define M98095_ADREN (1<<1)
+ #define M98095_ADLEN (1<<0)
+
+/* M98095_091_PWR_EN_OUT */
+ #define M98095_HPLEN (1<<7)
+ #define M98095_HPREN (1<<6)
+ #define M98095_SPLEN (1<<5)
+ #define M98095_SPREN (1<<4)
+ #define M98095_RECEN (1<<3)
+ #define M98095_DALEN (1<<1)
+ #define M98095_DAREN (1<<0)
+
+/* M98095_092_PWR_EN_OUT */
+ #define M98095_SPK_FIXEDSPECTRUM (0<<4)
+ #define M98095_SPK_SPREADSPECTRUM (1<<4)
+
+/* M98095_097_PWR_SYS */
+ #define M98095_SHDNRUN (1<<7)
+ #define M98095_PERFMODE (1<<3)
+ #define M98095_HPPLYBACK (1<<2)
+ #define M98095_PWRSV8K (1<<1)
+ #define M98095_PWRSV (1<<0)
+
+#define M98095_COEFS_PER_BAND 5
+
+#define M98095_BYTE1(w) ((w >> 8) & 0xff)
+#define M98095_BYTE0(w) (w & 0xff)
+
+/* Equalizer filter coefficients */
+#define M98095_110_DAI1_EQ_BASE 0x10
+#define M98095_142_DAI2_EQ_BASE 0x42
+
+/* Biquad filter coefficients */
+#define M98095_174_DAI1_BQ_BASE 0x74
+#define M98095_17E_DAI2_BQ_BASE 0x7E
+
+#endif
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index 4d9fb279e14..84ffdebb8a8 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -827,8 +827,6 @@ EXPORT_SYMBOL_GPL(sn95031_jack_detection);
/* codec registration */
static int sn95031_codec_probe(struct snd_soc_codec *codec)
{
- int ret;
-
pr_debug("codec_probe called\n");
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
@@ -879,16 +877,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, sn95031_snd_controls,
ARRAY_SIZE(sn95031_snd_controls));
- ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
- ARRAY_SIZE(sn95031_dapm_widgets));
- if (ret)
- pr_err("soc_dapm_new_control failed %d", ret);
- ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
- ARRAY_SIZE(sn95031_audio_map));
- if (ret)
- pr_err("soc_dapm_add_routes failed %d", ret);
-
- return ret;
+ return 0;
}
static int sn95031_codec_remove(struct snd_soc_codec *codec)
@@ -905,6 +894,10 @@ struct snd_soc_codec_driver sn95031_codec = {
.read = sn95031_read,
.write = sn95031_write,
.set_bias_level = sn95031_set_vaud_bias,
+ .dapm_widgets = sn95031_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets),
+ .dapm_routes = sn95031_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map),
};
static int __devinit sn95031_device_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
index 4c32b54913a..6a1a7e705cd 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -21,7 +21,7 @@
#include <sound/pcm.h>
#include <sound/initval.h>
-MODULE_LICENSE("GPL");
+#define DRV_NAME "spdif-dit"
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
@@ -56,7 +56,7 @@ static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
.remove = spdif_dit_remove,
.driver = {
- .name = "spdif-dit",
+ .name = DRV_NAME,
.owner = THIS_MODULE,
},
};
@@ -74,3 +74,7 @@ static void __exit dit_exit(void)
module_init(dit_modinit);
module_exit(dit_exit);
+MODULE_AUTHOR("Steve Chen <schen@mvista.com>");
+MODULE_DESCRIPTION("SPDIF dummy codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index b04d28039c1..84f4ad56855 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -32,6 +32,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -39,18 +40,25 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include "ssm2602.h"
#define SSM2602_VERSION "0.1"
+enum ssm2602_type {
+ SSM2602,
+ SSM2604,
+};
+
/* codec private data */
struct ssm2602_priv {
unsigned int sysclk;
enum snd_soc_control_type control_type;
- void *control_data;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
+
+ enum ssm2602_type type;
};
/*
@@ -60,60 +68,12 @@ struct ssm2602_priv {
* There is no point in caching the reset register
*/
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0017, 0x0017, 0x0079, 0x0079,
- 0x0000, 0x0000, 0x0000, 0x000a,
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
0x0000, 0x0000
};
-/*
- * read ssm2602 register cache
- */
-static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == SSM2602_RESET)
- return 0;
- if (reg >= SSM2602_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write ssm2602 register cache
- */
-static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= SSM2602_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the ssm2602 register space
- */
-static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 ssm2602 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- ssm2602_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0)
+#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
/*Appending several "None"s just for OSS mixer use*/
static const char *ssm2602_input_select[] = {
@@ -128,174 +88,187 @@ static const struct soc_enum ssm2602_enum[] = {
SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
};
-static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+static const unsigned int ssm260x_outmix_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
+};
-SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
- 0, 127, 0),
-SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
- 7, 1, 0),
+static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
-SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
+static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
+ ssm260x_inpga_tlv),
SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
-SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
-SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
-
-SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
-
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
-SOC_ENUM("Capture Source", ssm2602_enum[0]),
-
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
};
+static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 0, 127, 0, ssm260x_outmix_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 7, 1, 0),
+SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
+ ssm260x_sidetone_tlv),
+
+SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
+SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
+};
+
/* Output Mixer */
-static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
+static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
-SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
-static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
-SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
- &ssm2602_output_mixer_controls[0],
- ARRAY_SIZE(ssm2602_output_mixer_controls)),
+static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
+
SND_SOC_DAPM_OUTPUT("LOUT"),
-SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
-SND_SOC_DAPM_OUTPUT("RHPOUT"),
-SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls)),
+
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
-SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
-SND_SOC_DAPM_INPUT("RLINEIN"),
-SND_SOC_DAPM_INPUT("LLINEIN"),
};
-static const struct snd_soc_dapm_route audio_conn[] = {
- /* output mixer */
+static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
+};
+
+static const struct snd_soc_dapm_route ssm260x_routes[] = {
+ {"DAC", NULL, "Digital Core Power"},
+ {"ADC", NULL, "Digital Core Power"},
+
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ {"ROUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+};
+
+static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
- /* outputs */
{"RHPOUT", NULL, "Output Mixer"},
- {"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
- {"LOUT", NULL, "Output Mixer"},
- /* input mux */
{"Input Mux", "Line", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"},
- /* inputs */
- {"Line Input", NULL, "LLINEIN"},
- {"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
};
-static int ssm2602_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
- ARRAY_SIZE(ssm2602_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn));
-
- return 0;
-}
+static const struct snd_soc_dapm_route ssm2604_routes[] = {
+ {"ADC", NULL, "Line Input"},
+};
-struct _coeff_div {
+struct ssm2602_coeff {
u32 mclk;
u32 rate;
- u16 fs;
- u8 sr:4;
- u8 bosr:1;
- u8 usb:1;
+ u8 srate;
};
-/* codec mclk clock divider coefficients */
-static const struct _coeff_div coeff_div[] = {
+#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
+
+/* codec mclk clock coefficients */
+static const struct ssm2602_coeff ssm2602_coeff_table[] = {
/* 48k */
- {12288000, 48000, 256, 0x0, 0x0, 0x0},
- {18432000, 48000, 384, 0x0, 0x1, 0x0},
- {12000000, 48000, 250, 0x0, 0x0, 0x1},
+ {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
+ {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
+ {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
/* 32k */
- {12288000, 32000, 384, 0x6, 0x0, 0x0},
- {18432000, 32000, 576, 0x6, 0x1, 0x0},
- {12000000, 32000, 375, 0x6, 0x0, 0x1},
+ {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
+ {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
+ {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
/* 8k */
- {12288000, 8000, 1536, 0x3, 0x0, 0x0},
- {18432000, 8000, 2304, 0x3, 0x1, 0x0},
- {11289600, 8000, 1408, 0xb, 0x0, 0x0},
- {16934400, 8000, 2112, 0xb, 0x1, 0x0},
- {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+ {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
+ {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
+ {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
+ {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
+ {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
/* 96k */
- {12288000, 96000, 128, 0x7, 0x0, 0x0},
- {18432000, 96000, 192, 0x7, 0x1, 0x0},
- {12000000, 96000, 125, 0x7, 0x0, 0x1},
+ {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
+ {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
+ {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
/* 44.1k */
- {11289600, 44100, 256, 0x8, 0x0, 0x0},
- {16934400, 44100, 384, 0x8, 0x1, 0x0},
- {12000000, 44100, 272, 0x8, 0x1, 0x1},
+ {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
+ {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
+ {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
/* 88.2k */
- {11289600, 88200, 128, 0xf, 0x0, 0x0},
- {16934400, 88200, 192, 0xf, 0x1, 0x0},
- {12000000, 88200, 136, 0xf, 0x1, 0x1},
+ {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
+ {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
+ {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
};
-static inline int get_coeff(int mclk, int rate)
+static inline int ssm2602_get_coeff(int mclk, int rate)
{
int i;
- for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
- if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
- return i;
+ for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
+ if (ssm2602_coeff_table[i].rate == rate &&
+ ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
}
- return i;
+ return -EINVAL;
}
static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = codec->control_data;
- u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
- int i = get_coeff(ssm2602->sysclk, params_rate(params));
+ u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
+ int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
if (substream == ssm2602->slave_substream) {
- dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+ dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
return 0;
}
- /*no match is found*/
- if (i == ARRAY_SIZE(coeff_div))
- return -EINVAL;
+ if (srate < 0)
+ return srate;
- srate = (coeff_div[i].sr << 2) |
- (coeff_div[i].bosr << 1) | coeff_div[i].usb;
-
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
- ssm2602_write(codec, SSM2602_SRATE, srate);
+ snd_soc_write(codec, SSM2602_SRATE, srate);
/* bit size */
switch (params_format(params)) {
@@ -311,8 +284,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
iface |= 0x000c;
break;
}
- ssm2602_write(codec, SSM2602_IFACE, iface);
- ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+ snd_soc_write(codec, SSM2602_IFACE, iface);
return 0;
}
@@ -354,17 +326,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- /* set active */
- ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
-
- return 0;
-}
-
static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -372,25 +333,22 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- /* deactivate */
- if (!codec->active)
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
-
if (ssm2602->master_substream == substream)
ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL;
}
+
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
+ u16 mute_reg = snd_soc_read(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
if (mute)
- ssm2602_write(codec, SSM2602_APDIGI,
+ snd_soc_write(codec, SSM2602_APDIGI,
mute_reg | APDIGI_ENABLE_DAC_MUTE);
else
- ssm2602_write(codec, SSM2602_APDIGI, mute_reg);
+ snd_soc_write(codec, SSM2602_APDIGI, mute_reg);
return 0;
}
@@ -466,30 +424,29 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- ssm2602_write(codec, SSM2602_IFACE, iface);
+ snd_soc_write(codec, SSM2602_IFACE, iface);
return 0;
}
static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f;
+ u16 reg = snd_soc_read(codec, SSM2602_PWR) & 0xff7f;
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
- ssm2602_write(codec, SSM2602_PWR, reg);
+ snd_soc_write(codec, SSM2602_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
+ snd_soc_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
- ssm2602_write(codec, SSM2602_PWR, 0xffff);
+ snd_soc_write(codec, SSM2602_PWR, 0xffff);
break;
}
@@ -506,7 +463,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
- .prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute,
@@ -539,50 +495,87 @@ static int ssm2602_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int ssm2602_resume(struct snd_soc_codec *codec)
{
- int i;
- u8 data[2];
- u16 *cache = codec->reg_cache;
-
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) {
- data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
- data[1] = cache[i] & 0x00ff;
- codec->hw_write(codec->control_data, data, 2);
- }
+ snd_soc_cache_sync(codec);
+
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
return 0;
}
static int ssm2602_probe(struct snd_soc_codec *codec)
{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret, reg;
+
+ reg = snd_soc_read(codec, SSM2602_LOUT1V);
+ snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
+ reg = snd_soc_read(codec, SSM2602_ROUT1V);
+ snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+
+ ret = snd_soc_add_controls(codec, ssm2602_snd_controls,
+ ARRAY_SIZE(ssm2602_snd_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
+ ARRAY_SIZE(ssm2602_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
+ ARRAY_SIZE(ssm2602_routes));
+}
+
+static int ssm2604_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
+ ARRAY_SIZE(ssm2604_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
+ ARRAY_SIZE(ssm2604_routes));
+}
+
+static int ssm260x_probe(struct snd_soc_codec *codec)
+{
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- int ret = 0, reg;
+ int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
- codec->control_data = ssm2602->control_data;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
- ssm2602_reset(codec);
+ ret = ssm2602_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
- /*power on device*/
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
/* set the update bits */
- reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL);
- ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL);
- ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V);
- ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
- ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+ reg = snd_soc_read(codec, SSM2602_LINVOL);
+ snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
+ reg = snd_soc_read(codec, SSM2602_RINVOL);
+ snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
/*select Line in as default input*/
- ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
+ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST);
- ssm2602_write(codec, SSM2602_PWR, 0);
- snd_soc_add_controls(codec, ssm2602_snd_controls,
- ARRAY_SIZE(ssm2602_snd_controls));
- ssm2602_add_widgets(codec);
+ switch (ssm2602->type) {
+ case SSM2602:
+ ret = ssm2602_probe(codec);
+ break;
+ case SSM2604:
+ ret = ssm2604_probe(codec);
+ break;
+ }
return ret;
}
@@ -595,18 +588,61 @@ static int ssm2602_remove(struct snd_soc_codec *codec)
}
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
- .probe = ssm2602_probe,
+ .probe = ssm260x_probe,
.remove = ssm2602_remove,
.suspend = ssm2602_suspend,
.resume = ssm2602_resume,
- .read = ssm2602_read_reg_cache,
- .write = ssm2602_write,
.set_bias_level = ssm2602_set_bias_level,
.reg_cache_size = ARRAY_SIZE(ssm2602_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = ssm2602_reg,
+
+ .controls = ssm260x_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
+ .dapm_widgets = ssm260x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
+ .dapm_routes = ssm260x_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
};
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit ssm2602_spi_probe(struct spi_device *spi)
+{
+ struct ssm2602_priv *ssm2602;
+ int ret;
+
+ ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
+ if (ssm2602 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, ssm2602);
+ ssm2602->control_type = SND_SOC_SPI;
+ ssm2602->type = SSM2602;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
+ if (ret < 0)
+ kfree(ssm2602);
+ return ret;
+}
+
+static int __devexit ssm2602_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ kfree(spi_get_drvdata(spi));
+ return 0;
+}
+
+static struct spi_driver ssm2602_spi_driver = {
+ .driver = {
+ .name = "ssm2602",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2602_spi_probe,
+ .remove = __devexit_p(ssm2602_spi_remove),
+};
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* ssm2602 2 wire address is determined by GPIO5
@@ -625,8 +661,8 @@ static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, ssm2602);
- ssm2602->control_data = i2c;
ssm2602->control_type = SND_SOC_I2C;
+ ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
@@ -643,7 +679,9 @@ static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id ssm2602_i2c_id[] = {
- { "ssm2602", 0 },
+ { "ssm2602", SSM2602 },
+ { "ssm2603", SSM2602 },
+ { "ssm2604", SSM2604 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
@@ -651,7 +689,7 @@ MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
/* corgi i2c codec control layer */
static struct i2c_driver ssm2602_i2c_driver = {
.driver = {
- .name = "ssm2602-codec",
+ .name = "ssm2602",
.owner = THIS_MODULE,
},
.probe = ssm2602_i2c_probe,
@@ -664,25 +702,35 @@ static struct i2c_driver ssm2602_i2c_driver = {
static int __init ssm2602_modinit(void)
{
int ret = 0;
+
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&ssm2602_spi_driver);
+ if (ret)
+ return ret;
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&ssm2602_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n",
- ret);
- }
+ if (ret)
+ return ret;
#endif
+
return ret;
}
module_init(ssm2602_modinit);
static void __exit ssm2602_exit(void)
{
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&ssm2602_spi_driver);
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&ssm2602_i2c_driver);
#endif
}
module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC ssm2602 driver");
+MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
MODULE_AUTHOR("Cliff Cai");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
index 42a47d0f8e2..b98c6916803 100644
--- a/sound/soc/codecs/ssm2602.h
+++ b/sound/soc/codecs/ssm2602.h
@@ -117,11 +117,5 @@
#define SSM2602_CACHEREGNUM 10
#define SSM2602_SYSCLK 0
-#define SSM2602_DAI 0
-
-struct ssm2602_setup_data {
- int i2c_bus;
- unsigned short i2c_address;
-};
#endif
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 54a30ef0ec8..33bb52f3f68 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MICIN"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
/* Output Mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "Playback Switch", "DAC"},
@@ -388,18 +388,6 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
return 0;
}
-static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
- ARRAY_SIZE(tlv320aic23_dapm_widgets));
- /* set up audio path interconnects */
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -676,7 +664,6 @@ static int tlv320aic23_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls));
- tlv320aic23_add_widgets(codec);
return 0;
}
@@ -698,6 +685,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
.read = tlv320aic23_read_reg_cache,
.write = tlv320aic23_write,
.set_bias_level = tlv320aic23_set_bias_level,
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 6c43c13f043..c3d96fc8c26 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -157,7 +157,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 082e9d51963..faa5e9fb147 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1,7 +1,7 @@
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
@@ -587,6 +587,9 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Right DAC Power",
DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Codec Power",
+ DAC33_PWR_CTRL, 4, 0, NULL, 0),
+
SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event),
SND_SOC_DAPM_POST("Post Playback", dac33_playback_event),
};
@@ -619,6 +622,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
/* output */
{"LEFT_LO", NULL, "Output Left Amplifier"},
{"RIGHT_LO", NULL, "Output Right Amplifier"},
+
+ {"LEFT_LO", NULL, "Codec Power"},
+ {"RIGHT_LO", NULL, "Codec Power"},
};
static int dac33_add_widgets(struct snd_soc_codec *codec)
@@ -636,13 +642,10 @@ static int dac33_add_widgets(struct snd_soc_codec *codec)
static int dac33_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- if (!dac33->substream)
- dac33_soft_power(codec, 1);
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -943,8 +946,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
/* Write registers 0x08 and 0x09 (MSB, LSB) */
dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
- /* calib time: 128 is a nice number ;) */
- dac33_write(codec, DAC33_CALIB_TIME, 128);
+ /* OSC calibration time */
+ dac33_write(codec, DAC33_CALIB_TIME, 96);
/* adjustment treshold & step */
dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
@@ -1655,5 +1658,5 @@ module_exit(dac33_module_exit);
MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
index 7c318b5da43..ed69670747b 100644
--- a/sound/soc/codecs/tlv320dac33.h
+++ b/sound/soc/codecs/tlv320dac33.h
@@ -1,7 +1,7 @@
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 1f1ac8110be..239e0c46106 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -3,7 +3,7 @@
*
* Copyright (C) Nokia Corporation
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -495,7 +495,7 @@ static void __exit tpa6130a2_exit(void)
i2c_del_driver(&tpa6130a2_i2c_driver);
}
-MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
index 5df49c8756b..417444020ba 100644
--- a/sound/soc/codecs/tpa6130a2.h
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -3,7 +3,7 @@
*
* Copyright (C) Nokia Corporation
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 255901c4460..4c336636d4f 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -960,9 +960,9 @@ static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
/*
* AFMGAIN volume control:
- * from 18 to 24 dB in 6 dB steps
+ * from -18 to 24 dB in 6 dB steps
*/
-static DECLARE_TLV_DB_SCALE(afm_amp_tlv, 1800, 600, 0);
+static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0);
/*
* HSGAIN volume control:
@@ -1049,7 +1049,7 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
/* AFM gains */
SOC_DOUBLE_TLV("Aux FM Volume",
- TWL6040_REG_LINEGAIN, 0, 4, 0xF, 0, afm_amp_tlv),
+ TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
/* Playback gains */
SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume",
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
new file mode 100644
index 00000000000..14d0716bf00
--- /dev/null
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -0,0 +1,108 @@
+/*
+ * Driver for the 1250-EV1 audio I/O module
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = {
+SND_SOC_DAPM_ADC("ADC", "wm1250-ev1 Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_INPUT("WM1250 Input"),
+SND_SOC_DAPM_INPUT("WM1250 Output"),
+};
+
+static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
+ { "ADC", NULL, "WM1250 Input" },
+ { "WM1250 Output", NULL, "DAC" },
+};
+
+static struct snd_soc_dai_driver wm1250_ev1_dai = {
+ .name = "wm1250-ev1",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
+ .dapm_widgets = wm1250_ev1_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets),
+ .dapm_routes = wm1250_ev1_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes),
+};
+
+static int __devinit wm1250_ev1_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
+ &wm1250_ev1_dai, 1);
+}
+
+static int __devexit wm1250_ev1_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
+ { "wm1250-ev1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
+
+static struct i2c_driver wm1250_ev1_i2c_driver = {
+ .driver = {
+ .name = "wm1250-ev1",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm1250_ev1_probe,
+ .remove = __devexit_p(wm1250_ev1_remove),
+ .id_table = wm1250_ev1_i2c_id,
+};
+
+static int __init wm1250_ev1_modinit(void)
+{
+ int ret = 0;
+
+ ret = i2c_add_driver(&wm1250_ev1_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(wm1250_ev1_modinit);
+
+static void __exit wm1250_ev1_exit(void)
+{
+ i2c_del_driver(&wm1250_ev1_i2c_driver);
+}
+module_exit(wm1250_ev1_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 97c30382d3f..a537e4af6ae 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -77,7 +77,7 @@ SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8711_intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -89,17 +89,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"LOUT", NULL, "Output Mixer"},
};
-static int wm8711_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8711_dapm_widgets,
- ARRAY_SIZE(wm8711_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
struct _coeff_div {
u32 mclk;
u32 rate;
@@ -398,7 +387,6 @@ static int wm8711_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8711_snd_controls,
ARRAY_SIZE(wm8711_snd_controls));
- wm8711_add_widgets(codec);
return ret;
@@ -420,6 +408,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
.reg_cache_size = ARRAY_SIZE(wm8711_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8711_reg,
+ .dapm_widgets = wm8711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
+ .dapm_routes = wm8711_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 736b0352d0a..86d4718d3a7 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -65,22 +65,11 @@ SND_SOC_DAPM_OUTPUT("VOUTL"),
SND_SOC_DAPM_OUTPUT("VOUTR"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8728_intercon[] = {
{"VOUTL", NULL, "DAC"},
{"VOUTR", NULL, "DAC"},
};
-static int wm8728_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8728_dapm_widgets,
- ARRAY_SIZE(wm8728_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int wm8728_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
@@ -255,7 +244,6 @@ static int wm8728_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls));
- wm8728_add_widgets(codec);
return ret;
}
@@ -275,6 +263,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
.reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8728_reg_defaults,
+ .dapm_widgets = wm8728_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
+ .dapm_routes = wm8728_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
};
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 0a67c31b266..6dec7cee2cb 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -201,7 +201,7 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
return wm8731->sysclk_type == WM8731_SYSCLK_MCLK;
}
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8731_intercon[] = {
{"DAC", NULL, "OSC", wm8731_check_osc},
{"ADC", NULL, "OSC", wm8731_check_osc},
@@ -227,17 +227,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Mic Bias", NULL, "MICIN"},
};
-static int wm8731_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets,
- ARRAY_SIZE(wm8731_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
struct _coeff_div {
u32 mclk;
u32 rate;
@@ -599,7 +588,6 @@ static int wm8731_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls));
- wm8731_add_widgets(codec);
/* Regulators will have been enabled by bias management */
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
@@ -636,6 +624,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
.reg_cache_size = ARRAY_SIZE(wm8731_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8731_reg,
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = wm8731_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
};
#if defined(CONFIG_SPI_MASTER)
@@ -667,7 +659,7 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
static struct spi_driver wm8731_spi_driver = {
.driver = {
- .name = "wm8731-codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
@@ -711,7 +703,7 @@ MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
- .name = "wm8731-codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_i2c_probe,
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 824d1c8c8a3..43e3d760766 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -382,7 +382,8 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
u16 reg;
@@ -634,6 +635,13 @@ static const struct soc_enum lsidetone_enum =
static const struct soc_enum rsidetone_enum =
SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
+static const char *adcinput_text[] = {
+ "ADC", "DMIC"
+};
+
+static const struct soc_enum adcinput_enum =
+ SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text);
+
static const char *aif_text[] = {
"Left", "Right"
};
@@ -767,6 +775,9 @@ static const struct snd_kcontrol_new lsidetone_mux =
static const struct snd_kcontrol_new rsidetone_mux =
SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
+static const struct snd_kcontrol_new adcinput_mux =
+ SOC_DAPM_ENUM("ADC Input", adcinput_enum);
+
static const struct snd_kcontrol_new lcapture_mux =
SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
@@ -817,6 +828,7 @@ SND_SOC_DAPM_INPUT("IN2L"),
SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
@@ -842,6 +854,9 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
+SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+
SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
@@ -930,7 +945,7 @@ SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8903_intercon[] = {
{ "CLK_DSP", NULL, "CLK_SYS" },
{ "Mic Bias", NULL, "CLK_SYS" },
@@ -979,6 +994,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Left Input PGA", NULL, "Left Input Mode Mux" },
{ "Right Input PGA", NULL, "Right Input Mode Mux" },
+ { "Left ADC Input", "ADC", "Left Input PGA" },
+ { "Left ADC Input", "DMIC", "DMICDAT" },
+ { "Right ADC Input", "ADC", "Right Input PGA" },
+ { "Right ADC Input", "DMIC", "DMICDAT" },
+
{ "Left Capture Mux", "Left", "ADCL" },
{ "Left Capture Mux", "Right", "ADCR" },
@@ -988,9 +1008,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIFTXL", NULL, "Left Capture Mux" },
{ "AIFTXR", NULL, "Right Capture Mux" },
- { "ADCL", NULL, "Left Input PGA" },
+ { "ADCL", NULL, "Left ADC Input" },
{ "ADCL", NULL, "CLK_DSP" },
- { "ADCR", NULL, "Right Input PGA" },
+ { "ADCR", NULL, "Right ADC Input" },
{ "ADCR", NULL, "CLK_DSP" },
{ "Left Playback Mux", "Left", "AIFRXL" },
@@ -1087,17 +1107,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "Right Line Output PGA", NULL, "Charge Pump" },
};
-static int wm8903_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8903_dapm_widgets,
- ARRAY_SIZE(wm8903_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int wm8903_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -2028,7 +2037,6 @@ static int wm8903_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
- wm8903_add_widgets(codec);
wm8903_init_gpio(codec);
@@ -2054,6 +2062,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
.reg_cache_default = wm8903_reg_defaults,
.volatile_register = wm8903_volatile_register,
.seq_notifier = wm8903_seq_notifier,
+ .dapm_widgets = wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
+ .dapm_routes = wm8903_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c
new file mode 100644
index 00000000000..ccc9bd83279
--- /dev/null
+++ b/sound/soc/codecs/wm8915.c
@@ -0,0 +1,2931 @@
+/*
+ * wm8915.c - WM8915 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <sound/wm8915.h>
+#include "wm8915.h"
+
+#define WM8915_AIFS 2
+
+#define HPOUT1L 1
+#define HPOUT1R 2
+#define HPOUT2L 4
+#define HPOUT2R 8
+
+#define WM8915_NUM_SUPPLIES 6
+static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "MICVDD",
+};
+
+struct wm8915_priv {
+ struct snd_soc_codec *codec;
+
+ int ldo1ena;
+
+ int sysclk;
+
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ struct completion fll_lock;
+
+ u16 dcs_pending;
+ struct completion dcs_done;
+
+ u16 hpout_ena;
+ u16 hpout_pending;
+
+ struct regulator_bulk_data supplies[WM8915_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8915_NUM_SUPPLIES];
+
+ struct wm8915_pdata pdata;
+
+ int rx_rate[WM8915_AIFS];
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[2];
+ struct soc_enum retune_mobile_enum;
+
+ struct snd_soc_jack *jack;
+ bool detecting;
+ bool jack_mic;
+ wm8915_polarity_fn polarity_cb;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+/* We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8915_REGULATOR_EVENT(n) \
+static int wm8915_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8915_priv *wm8915 = container_of(nb, struct wm8915_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ wm8915->codec->cache_sync = 1; \
+ } \
+ return 0; \
+}
+
+WM8915_REGULATOR_EVENT(0)
+WM8915_REGULATOR_EVENT(1)
+WM8915_REGULATOR_EVENT(2)
+WM8915_REGULATOR_EVENT(3)
+WM8915_REGULATOR_EVENT(4)
+WM8915_REGULATOR_EVENT(5)
+
+static const u16 wm8915_reg[WM8915_MAX_REGISTER] = {
+ [WM8915_SOFTWARE_RESET] = 0x8915,
+ [WM8915_POWER_MANAGEMENT_7] = 0x10,
+ [WM8915_DAC1_HPOUT1_VOLUME] = 0x88,
+ [WM8915_DAC2_HPOUT2_VOLUME] = 0x88,
+ [WM8915_DAC1_LEFT_VOLUME] = 0x2c0,
+ [WM8915_DAC1_RIGHT_VOLUME] = 0x2c0,
+ [WM8915_DAC2_LEFT_VOLUME] = 0x2c0,
+ [WM8915_DAC2_RIGHT_VOLUME] = 0x2c0,
+ [WM8915_OUTPUT1_LEFT_VOLUME] = 0x80,
+ [WM8915_OUTPUT1_RIGHT_VOLUME] = 0x80,
+ [WM8915_OUTPUT2_LEFT_VOLUME] = 0x80,
+ [WM8915_OUTPUT2_RIGHT_VOLUME] = 0x80,
+ [WM8915_MICBIAS_1] = 0x39,
+ [WM8915_MICBIAS_2] = 0x39,
+ [WM8915_LDO_1] = 0x3,
+ [WM8915_LDO_2] = 0x13,
+ [WM8915_ACCESSORY_DETECT_MODE_1] = 0x4,
+ [WM8915_HEADPHONE_DETECT_1] = 0x20,
+ [WM8915_MIC_DETECT_1] = 0x7600,
+ [WM8915_MIC_DETECT_2] = 0xbf,
+ [WM8915_CHARGE_PUMP_1] = 0x1f25,
+ [WM8915_CHARGE_PUMP_2] = 0xab19,
+ [WM8915_DC_SERVO_5] = 0x2a2a,
+ [WM8915_CONTROL_INTERFACE_1] = 0x8004,
+ [WM8915_CLOCKING_1] = 0x10,
+ [WM8915_AIF_RATE] = 0x83,
+ [WM8915_FLL_CONTROL_4] = 0x5dc0,
+ [WM8915_FLL_CONTROL_5] = 0xc84,
+ [WM8915_FLL_EFS_2] = 0x2,
+ [WM8915_AIF1_TX_LRCLK_1] = 0x80,
+ [WM8915_AIF1_TX_LRCLK_2] = 0x8,
+ [WM8915_AIF1_RX_LRCLK_1] = 0x80,
+ [WM8915_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8915_AIF1RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8915_AIF1TX_TEST] = 0x7,
+ [WM8915_AIF2_TX_LRCLK_1] = 0x80,
+ [WM8915_AIF2_TX_LRCLK_2] = 0x8,
+ [WM8915_AIF2_RX_LRCLK_1] = 0x80,
+ [WM8915_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8915_AIF2RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8915_AIF2TX_TEST] = 0x1,
+ [WM8915_DSP1_TX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP1_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP1_RX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP1_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP1_TX_FILTERS] = 0x2000,
+ [WM8915_DSP1_RX_FILTERS_1] = 0x200,
+ [WM8915_DSP1_RX_FILTERS_2] = 0x10,
+ [WM8915_DSP1_DRC_1] = 0x98,
+ [WM8915_DSP1_DRC_2] = 0x845,
+ [WM8915_DSP1_RX_EQ_GAINS_1] = 0x6318,
+ [WM8915_DSP1_RX_EQ_GAINS_2] = 0x6300,
+ [WM8915_DSP1_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8915_DSP1_RX_EQ_BAND_1_B] = 0x400,
+ [WM8915_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8915_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8915_DSP1_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8915_DSP1_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8915_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8915_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8915_DSP1_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8915_DSP1_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8915_DSP1_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8915_DSP1_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8915_DSP1_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8915_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8915_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8915_DSP1_RX_EQ_BAND_5_A] = 0x564,
+ [WM8915_DSP1_RX_EQ_BAND_5_B] = 0x559,
+ [WM8915_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8915_DSP2_TX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP2_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP2_RX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP2_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP2_TX_FILTERS] = 0x2000,
+ [WM8915_DSP2_RX_FILTERS_1] = 0x200,
+ [WM8915_DSP2_RX_FILTERS_2] = 0x10,
+ [WM8915_DSP2_DRC_1] = 0x98,
+ [WM8915_DSP2_DRC_2] = 0x845,
+ [WM8915_DSP2_RX_EQ_GAINS_1] = 0x6318,
+ [WM8915_DSP2_RX_EQ_GAINS_2] = 0x6300,
+ [WM8915_DSP2_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8915_DSP2_RX_EQ_BAND_1_B] = 0x400,
+ [WM8915_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8915_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8915_DSP2_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8915_DSP2_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8915_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8915_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8915_DSP2_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8915_DSP2_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8915_DSP2_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8915_DSP2_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8915_DSP2_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8915_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8915_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8915_DSP2_RX_EQ_BAND_5_A] = 0x564,
+ [WM8915_DSP2_RX_EQ_BAND_5_B] = 0x559,
+ [WM8915_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8915_OVERSAMPLING] = 0xd,
+ [WM8915_SIDETONE] = 0x1040,
+ [WM8915_GPIO_1] = 0xa101,
+ [WM8915_GPIO_2] = 0xa101,
+ [WM8915_GPIO_3] = 0xa101,
+ [WM8915_GPIO_4] = 0xa101,
+ [WM8915_GPIO_5] = 0xa101,
+ [WM8915_PULL_CONTROL_2] = 0x140,
+ [WM8915_INTERRUPT_STATUS_1_MASK] = 0x1f,
+ [WM8915_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
+ [WM8915_RIGHT_PDM_SPEAKER] = 0x1,
+ [WM8915_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
+ [WM8915_PDM_SPEAKER_VOLUME] = 0x66,
+ [WM8915_WRITE_SEQUENCER_0] = 0x1,
+ [WM8915_WRITE_SEQUENCER_1] = 0x1,
+ [WM8915_WRITE_SEQUENCER_3] = 0x6,
+ [WM8915_WRITE_SEQUENCER_4] = 0x40,
+ [WM8915_WRITE_SEQUENCER_5] = 0x1,
+ [WM8915_WRITE_SEQUENCER_6] = 0xf,
+ [WM8915_WRITE_SEQUENCER_7] = 0x6,
+ [WM8915_WRITE_SEQUENCER_8] = 0x1,
+ [WM8915_WRITE_SEQUENCER_9] = 0x3,
+ [WM8915_WRITE_SEQUENCER_10] = 0x104,
+ [WM8915_WRITE_SEQUENCER_12] = 0x60,
+ [WM8915_WRITE_SEQUENCER_13] = 0x11,
+ [WM8915_WRITE_SEQUENCER_14] = 0x401,
+ [WM8915_WRITE_SEQUENCER_16] = 0x50,
+ [WM8915_WRITE_SEQUENCER_17] = 0x3,
+ [WM8915_WRITE_SEQUENCER_18] = 0x100,
+ [WM8915_WRITE_SEQUENCER_20] = 0x51,
+ [WM8915_WRITE_SEQUENCER_21] = 0x3,
+ [WM8915_WRITE_SEQUENCER_22] = 0x104,
+ [WM8915_WRITE_SEQUENCER_23] = 0xa,
+ [WM8915_WRITE_SEQUENCER_24] = 0x60,
+ [WM8915_WRITE_SEQUENCER_25] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_26] = 0x502,
+ [WM8915_WRITE_SEQUENCER_27] = 0x100,
+ [WM8915_WRITE_SEQUENCER_28] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_32] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_36] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_40] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_44] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_48] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_52] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_56] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_60] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_64] = 0x1,
+ [WM8915_WRITE_SEQUENCER_65] = 0x1,
+ [WM8915_WRITE_SEQUENCER_67] = 0x6,
+ [WM8915_WRITE_SEQUENCER_68] = 0x40,
+ [WM8915_WRITE_SEQUENCER_69] = 0x1,
+ [WM8915_WRITE_SEQUENCER_70] = 0xf,
+ [WM8915_WRITE_SEQUENCER_71] = 0x6,
+ [WM8915_WRITE_SEQUENCER_72] = 0x1,
+ [WM8915_WRITE_SEQUENCER_73] = 0x3,
+ [WM8915_WRITE_SEQUENCER_74] = 0x104,
+ [WM8915_WRITE_SEQUENCER_76] = 0x60,
+ [WM8915_WRITE_SEQUENCER_77] = 0x11,
+ [WM8915_WRITE_SEQUENCER_78] = 0x401,
+ [WM8915_WRITE_SEQUENCER_80] = 0x50,
+ [WM8915_WRITE_SEQUENCER_81] = 0x3,
+ [WM8915_WRITE_SEQUENCER_82] = 0x100,
+ [WM8915_WRITE_SEQUENCER_84] = 0x60,
+ [WM8915_WRITE_SEQUENCER_85] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_86] = 0x502,
+ [WM8915_WRITE_SEQUENCER_87] = 0x100,
+ [WM8915_WRITE_SEQUENCER_88] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_92] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_96] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_100] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_104] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_108] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_112] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_116] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_120] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_124] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_128] = 0x1,
+ [WM8915_WRITE_SEQUENCER_129] = 0x1,
+ [WM8915_WRITE_SEQUENCER_131] = 0x6,
+ [WM8915_WRITE_SEQUENCER_132] = 0x40,
+ [WM8915_WRITE_SEQUENCER_133] = 0x1,
+ [WM8915_WRITE_SEQUENCER_134] = 0xf,
+ [WM8915_WRITE_SEQUENCER_135] = 0x6,
+ [WM8915_WRITE_SEQUENCER_136] = 0x1,
+ [WM8915_WRITE_SEQUENCER_137] = 0x3,
+ [WM8915_WRITE_SEQUENCER_138] = 0x106,
+ [WM8915_WRITE_SEQUENCER_140] = 0x61,
+ [WM8915_WRITE_SEQUENCER_141] = 0x11,
+ [WM8915_WRITE_SEQUENCER_142] = 0x401,
+ [WM8915_WRITE_SEQUENCER_144] = 0x50,
+ [WM8915_WRITE_SEQUENCER_145] = 0x3,
+ [WM8915_WRITE_SEQUENCER_146] = 0x102,
+ [WM8915_WRITE_SEQUENCER_148] = 0x51,
+ [WM8915_WRITE_SEQUENCER_149] = 0x3,
+ [WM8915_WRITE_SEQUENCER_150] = 0x106,
+ [WM8915_WRITE_SEQUENCER_151] = 0xa,
+ [WM8915_WRITE_SEQUENCER_152] = 0x61,
+ [WM8915_WRITE_SEQUENCER_153] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_154] = 0x502,
+ [WM8915_WRITE_SEQUENCER_155] = 0x100,
+ [WM8915_WRITE_SEQUENCER_156] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_160] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_164] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_168] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_172] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_176] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_180] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_184] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_188] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_192] = 0x1,
+ [WM8915_WRITE_SEQUENCER_193] = 0x1,
+ [WM8915_WRITE_SEQUENCER_195] = 0x6,
+ [WM8915_WRITE_SEQUENCER_196] = 0x40,
+ [WM8915_WRITE_SEQUENCER_197] = 0x1,
+ [WM8915_WRITE_SEQUENCER_198] = 0xf,
+ [WM8915_WRITE_SEQUENCER_199] = 0x6,
+ [WM8915_WRITE_SEQUENCER_200] = 0x1,
+ [WM8915_WRITE_SEQUENCER_201] = 0x3,
+ [WM8915_WRITE_SEQUENCER_202] = 0x106,
+ [WM8915_WRITE_SEQUENCER_204] = 0x61,
+ [WM8915_WRITE_SEQUENCER_205] = 0x11,
+ [WM8915_WRITE_SEQUENCER_206] = 0x401,
+ [WM8915_WRITE_SEQUENCER_208] = 0x50,
+ [WM8915_WRITE_SEQUENCER_209] = 0x3,
+ [WM8915_WRITE_SEQUENCER_210] = 0x102,
+ [WM8915_WRITE_SEQUENCER_212] = 0x61,
+ [WM8915_WRITE_SEQUENCER_213] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_214] = 0x502,
+ [WM8915_WRITE_SEQUENCER_215] = 0x100,
+ [WM8915_WRITE_SEQUENCER_216] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_220] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_224] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_228] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_232] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_236] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_240] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_244] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_248] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_252] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_256] = 0x60,
+ [WM8915_WRITE_SEQUENCER_258] = 0x601,
+ [WM8915_WRITE_SEQUENCER_260] = 0x50,
+ [WM8915_WRITE_SEQUENCER_262] = 0x100,
+ [WM8915_WRITE_SEQUENCER_264] = 0x1,
+ [WM8915_WRITE_SEQUENCER_266] = 0x104,
+ [WM8915_WRITE_SEQUENCER_267] = 0x100,
+ [WM8915_WRITE_SEQUENCER_268] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_272] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_276] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_280] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_284] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_288] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_292] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_296] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_300] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_304] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_308] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_312] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_316] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_320] = 0x61,
+ [WM8915_WRITE_SEQUENCER_322] = 0x601,
+ [WM8915_WRITE_SEQUENCER_324] = 0x50,
+ [WM8915_WRITE_SEQUENCER_326] = 0x102,
+ [WM8915_WRITE_SEQUENCER_328] = 0x1,
+ [WM8915_WRITE_SEQUENCER_330] = 0x106,
+ [WM8915_WRITE_SEQUENCER_331] = 0x100,
+ [WM8915_WRITE_SEQUENCER_332] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_336] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_340] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_344] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_348] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_352] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_356] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_360] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_364] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_368] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_372] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_376] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_380] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_384] = 0x60,
+ [WM8915_WRITE_SEQUENCER_386] = 0x601,
+ [WM8915_WRITE_SEQUENCER_388] = 0x61,
+ [WM8915_WRITE_SEQUENCER_390] = 0x601,
+ [WM8915_WRITE_SEQUENCER_392] = 0x50,
+ [WM8915_WRITE_SEQUENCER_394] = 0x300,
+ [WM8915_WRITE_SEQUENCER_396] = 0x1,
+ [WM8915_WRITE_SEQUENCER_398] = 0x304,
+ [WM8915_WRITE_SEQUENCER_400] = 0x40,
+ [WM8915_WRITE_SEQUENCER_402] = 0xf,
+ [WM8915_WRITE_SEQUENCER_404] = 0x1,
+ [WM8915_WRITE_SEQUENCER_407] = 0x100,
+};
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *sidetone_hpf_text[] = {
+ "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
+};
+
+static const struct soc_enum sidetone_hpf =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 7, 6, sidetone_hpf_text);
+
+static const char *hpf_mode_text[] = {
+ "HiFi", "Custom", "Voice"
+};
+
+static const struct soc_enum dsp1tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const struct soc_enum dsp2tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const char *hpf_cutoff_text[] = {
+ "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum dsp1tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static const struct soc_enum dsp2tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static void wm8915_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+ int base, best, best_val, save, i, cfg, iface;
+
+ if (!wm8915->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ base = WM8915_DSP1_RX_EQ_GAINS_1;
+ if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
+ WM8915_DSP1RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ case 1:
+ base = WM8915_DSP1_RX_EQ_GAINS_2;
+ if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
+ WM8915_DSP2RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8915->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8915->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8915->rx_rate[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8915->rx_rate[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8915->rx_rate[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8915_DSP1RX_EQ_ENA;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8915_DSP1RX_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8915_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "DSP1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "DSP2 EQ Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int wm8915_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+ int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8915->retune_mobile_cfg[block] = value;
+
+ wm8915_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8915_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
+
+ ucontrol->value.enumerated.item[0] = wm8915->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8915_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),
+
+SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8915_DAC1_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8915_DAC2_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_SINGLE("Sidetone LPF Switch", WM8915_SIDETONE, 12, 1, 0),
+SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8915_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8915_DSP1_TX_LEFT_VOLUME,
+ WM8915_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8915_DSP2_TX_LEFT_VOLUME,
+ WM8915_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8915_DSP1_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP1 Capture HPF Switch", WM8915_DSP1_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
+SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),
+
+SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8915_DSP2_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP2 Capture HPF Switch", WM8915_DSP2_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
+SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),
+
+SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8915_DSP1_RX_LEFT_VOLUME,
+ WM8915_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP1 Playback Switch", WM8915_DSP1_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8915_DSP2_RX_LEFT_VOLUME,
+ WM8915_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP2 Playback Switch", WM8915_DSP2_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE("Speaker High Performance Switch", WM8915_OVERSAMPLING, 3, 1, 0),
+SOC_SINGLE("DMIC High Performance Switch", WM8915_OVERSAMPLING, 2, 1, 0),
+SOC_SINGLE("ADC High Performance Switch", WM8915_OVERSAMPLING, 1, 1, 0),
+SOC_SINGLE("DAC High Performance Switch", WM8915_OVERSAMPLING, 0, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8915_DAC_SOFTMUTE, 1, 1, 0),
+SOC_SINGLE("DAC Slow Soft Mute Switch", WM8915_DAC_SOFTMUTE, 0, 1, 0),
+
+SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8915_DAC1_HPOUT1_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8915_DAC2_HPOUT2_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+
+SOC_DOUBLE_R_TLV("Output 1 Volume", WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 1 ZC Switch", WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Output 2 Volume", WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 2 ZC Switch", WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_TLV("Speaker Volume", WM8915_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
+ spk_tlv),
+SOC_DOUBLE_R("Speaker Switch", WM8915_LEFT_PDM_SPEAKER,
+ WM8915_RIGHT_PDM_SPEAKER, 3, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8915_LEFT_PDM_SPEAKER,
+ WM8915_RIGHT_PDM_SPEAKER, 2, 1, 0),
+
+SOC_SINGLE("DSP1 EQ Switch", WM8915_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("DSP2 EQ Switch", WM8915_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8915_eq_controls[] = {
+SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(5);
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmv_short_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
+
+ /* Record which outputs we enabled */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ wm8915->hpout_pending &= ~w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8915->hpout_pending |= w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ unsigned long timeout = 200;
+
+ snd_soc_write(codec, WM8915_DC_SERVO_2, mask);
+
+ /* Use the interrupt if possible */
+ do {
+ if (i2c->irq) {
+ timeout = wait_for_completion_timeout(&wm8915->dcs_done,
+ msecs_to_jiffies(200));
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out\n");
+
+ } else {
+ msleep(1);
+ if (--i) {
+ timeout = 0;
+ break;
+ }
+ }
+
+ ret = snd_soc_read(codec, WM8915_DC_SERVO_2);
+ dev_dbg(codec->dev, "DC servo state: %x\n", ret);
+ } while (ret & mask);
+
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out for %x\n", mask);
+ else
+ dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
+}
+
+static void wm8915_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = container_of(dapm,
+ struct snd_soc_codec, dapm);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ u16 val, mask;
+
+ /* Complete any pending DC servo starts */
+ if (wm8915->dcs_pending) {
+ dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ wm8915->dcs_pending);
+
+ /* Trigger a startup sequence */
+ wait_for_dc_servo(codec, wm8915->dcs_pending
+ << WM8915_DCS_TRIG_STARTUP_0_SHIFT);
+
+ wm8915->dcs_pending = 0;
+ }
+
+ if (wm8915->hpout_pending != wm8915->hpout_ena) {
+ dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
+ wm8915->hpout_ena, wm8915->hpout_pending);
+
+ val = 0;
+ mask = 0;
+ if (wm8915->hpout_pending & HPOUT1L) {
+ val |= WM8915_HPOUT1L_RMV_SHORT;
+ mask |= WM8915_HPOUT1L_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT1L_RMV_SHORT |
+ WM8915_HPOUT1L_OUTP |
+ WM8915_HPOUT1L_DLY;
+ }
+
+ if (wm8915->hpout_pending & HPOUT1R) {
+ val |= WM8915_HPOUT1R_RMV_SHORT;
+ mask |= WM8915_HPOUT1R_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT1R_RMV_SHORT |
+ WM8915_HPOUT1R_OUTP |
+ WM8915_HPOUT1R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_1, mask, val);
+
+ val = 0;
+ mask = 0;
+ if (wm8915->hpout_pending & HPOUT2L) {
+ val |= WM8915_HPOUT2L_RMV_SHORT;
+ mask |= WM8915_HPOUT2L_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT2L_RMV_SHORT |
+ WM8915_HPOUT2L_OUTP |
+ WM8915_HPOUT2L_DLY;
+ }
+
+ if (wm8915->hpout_pending & HPOUT2R) {
+ val |= WM8915_HPOUT2R_RMV_SHORT;
+ mask |= WM8915_HPOUT2R_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT2R_RMV_SHORT |
+ WM8915_HPOUT2R_OUTP |
+ WM8915_HPOUT2R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_2, mask, val);
+
+ wm8915->hpout_ena = wm8915->hpout_pending;
+ }
+}
+
+static int dcs_start(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wm8915->dcs_pending |= 1 << w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *sidetone_text[] = {
+ "IN1", "IN2",
+};
+
+static const struct soc_enum left_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 0, 2, sidetone_text);
+
+static const struct snd_kcontrol_new left_sidetone =
+ SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
+
+static const struct soc_enum right_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new right_sidetone =
+ SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
+
+static const char *spk_text[] = {
+ "DAC1L", "DAC1R", "DAC2L", "DAC2R"
+};
+
+static const struct soc_enum spkl_enum =
+ SOC_ENUM_SINGLE(WM8915_LEFT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkl_mux =
+ SOC_DAPM_ENUM("SPKL", spkl_enum);
+
+static const struct soc_enum spkr_enum =
+ SOC_ENUM_SINGLE(WM8915_RIGHT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkr_mux =
+ SOC_DAPM_ENUM("SPKR", spkr_enum);
+
+static const char *dsp1rx_text[] = {
+ "AIF1", "AIF2"
+};
+
+static const struct soc_enum dsp1rx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);
+
+static const struct snd_kcontrol_new dsp1rx =
+ SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
+
+static const char *dsp2rx_text[] = {
+ "AIF2", "AIF1"
+};
+
+static const struct soc_enum dsp2rx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);
+
+static const struct snd_kcontrol_new dsp2rx =
+ SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
+
+static const char *aif2tx_text[] = {
+ "DSP2", "DSP1", "AIF1"
+};
+
+static const struct soc_enum aif2tx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);
+
+static const struct snd_kcontrol_new aif2tx =
+ SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
+
+static const char *inmux_text[] = {
+ "ADC", "DMIC1", "DMIC2"
+};
+
+static const struct soc_enum in1_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 0, 3, inmux_text);
+
+static const struct snd_kcontrol_new in1_mux =
+ SOC_DAPM_ENUM("IN1 Mux", in1_enum);
+
+static const struct soc_enum in2_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 4, 3, inmux_text);
+
+static const struct snd_kcontrol_new in2_mux =
+ SOC_DAPM_ENUM("IN2 Mux", in2_enum);
+
+static const struct snd_kcontrol_new dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+
+static const struct snd_soc_dapm_widget wm8915_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8915_AIF_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8915_CLOCKING_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8915_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8915_CHARGE_PUMP_1, 15, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("LDO2", WM8915_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("MICB2", WM8915_POWER_MANAGEMENT_1, 9, 0),
+SND_SOC_DAPM_MICBIAS("MICB1", WM8915_POWER_MANAGEMENT_1, 8, 0),
+
+SND_SOC_DAPM_PGA("IN1L PGA", WM8915_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R PGA", WM8915_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+
+SND_SOC_DAPM_PGA("IN1L", WM8915_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R", WM8915_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2L", WM8915_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2R", WM8915_POWER_MANAGEMENT_7, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DMIC2", WM8915_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DMIC1", WM8915_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8915_POWER_MANAGEMENT_3, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8915_POWER_MANAGEMENT_3, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8915_POWER_MANAGEMENT_3, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8915_POWER_MANAGEMENT_3, 2, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8915_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8915_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),
+
+SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 11, 0),
+SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 10, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 9, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 8, 0),
+
+SND_SOC_DAPM_MIXER("DSP2TXL", WM8915_POWER_MANAGEMENT_5, 11, 0,
+ dsp2txl, ARRAY_SIZE(dsp2txl)),
+SND_SOC_DAPM_MIXER("DSP2TXR", WM8915_POWER_MANAGEMENT_5, 10, 0,
+ dsp2txr, ARRAY_SIZE(dsp2txr)),
+SND_SOC_DAPM_MIXER("DSP1TXL", WM8915_POWER_MANAGEMENT_5, 9, 0,
+ dsp1txl, ARRAY_SIZE(dsp1txl)),
+SND_SOC_DAPM_MIXER("DSP1TXR", WM8915_POWER_MANAGEMENT_5, 8, 0,
+ dsp1txr, ARRAY_SIZE(dsp1txr)),
+
+SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ dac2l_mix, ARRAY_SIZE(dac2l_mix)),
+SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ dac2r_mix, ARRAY_SIZE(dac2r_mix)),
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8915_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8915_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8915_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8915_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
+ WM8915_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
+ WM8915_POWER_MANAGEMENT_4, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
+ WM8915_POWER_MANAGEMENT_6, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
+ WM8915_POWER_MANAGEMENT_6, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
+ WM8915_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
+ WM8915_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
+ WM8915_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
+ WM8915_POWER_MANAGEMENT_4, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
+ WM8915_POWER_MANAGEMENT_4, 1, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
+ WM8915_POWER_MANAGEMENT_4, 0, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
+ WM8915_POWER_MANAGEMENT_6, 5, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
+ WM8915_POWER_MANAGEMENT_6, 4, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
+ WM8915_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
+ WM8915_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
+ WM8915_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
+ WM8915_POWER_MANAGEMENT_6, 0, 0),
+
+/* We route as stereo pairs so define some dummy widgets to squash
+ * things down for now. RXA = 0,1, RXB = 2,3 and so on */
+SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
+SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
+SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),
+
+SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
+SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
+SND_SOC_DAPM_PGA("SPKL PGA", WM8915_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKR PGA", WM8915_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8915_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8915_ANALOGUE_HP_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8915_DC_SERVO_1, 2, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8915_ANALOGUE_HP_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8915_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8915_ANALOGUE_HP_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8915_DC_SERVO_1, 3, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8915_ANALOGUE_HP_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8915_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8915_ANALOGUE_HP_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8915_DC_SERVO_1, 0, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8915_ANALOGUE_HP_1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8915_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8915_ANALOGUE_HP_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8915_DC_SERVO_1, 1, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8915_ANALOGUE_HP_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT"),
+};
+
+static const struct snd_soc_dapm_route wm8915_dapm_routes[] = {
+ { "AIFCLK", NULL, "SYSCLK" },
+ { "SYSDSPCLK", NULL, "SYSCLK" },
+ { "Charge Pump", NULL, "SYSCLK" },
+
+ { "MICB1", NULL, "LDO2" },
+ { "MICB2", NULL, "LDO2" },
+
+ { "IN1L PGA", NULL, "IN2LN" },
+ { "IN1L PGA", NULL, "IN2LP" },
+ { "IN1L PGA", NULL, "IN1LN" },
+ { "IN1L PGA", NULL, "IN1LP" },
+
+ { "IN1R PGA", NULL, "IN2RN" },
+ { "IN1R PGA", NULL, "IN2RP" },
+ { "IN1R PGA", NULL, "IN1RN" },
+ { "IN1R PGA", NULL, "IN1RP" },
+
+ { "ADCL", NULL, "IN1L PGA" },
+
+ { "ADCR", NULL, "IN1R PGA" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+
+ { "DMIC2L", NULL, "DMIC2" },
+ { "DMIC2R", NULL, "DMIC2" },
+ { "DMIC1L", NULL, "DMIC1" },
+ { "DMIC1R", NULL, "DMIC1" },
+
+ { "IN1L Mux", "ADC", "ADCL" },
+ { "IN1L Mux", "DMIC1", "DMIC1L" },
+ { "IN1L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN1R Mux", "ADC", "ADCR" },
+ { "IN1R Mux", "DMIC1", "DMIC1R" },
+ { "IN1R Mux", "DMIC2", "DMIC2R" },
+
+ { "IN2L Mux", "ADC", "ADCL" },
+ { "IN2L Mux", "DMIC1", "DMIC1L" },
+ { "IN2L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN2R Mux", "ADC", "ADCR" },
+ { "IN2R Mux", "DMIC1", "DMIC1R" },
+ { "IN2R Mux", "DMIC2", "DMIC2R" },
+
+ { "Left Sidetone", "IN1", "IN1L Mux" },
+ { "Left Sidetone", "IN2", "IN2L Mux" },
+
+ { "Right Sidetone", "IN1", "IN1R Mux" },
+ { "Right Sidetone", "IN2", "IN2R Mux" },
+
+ { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
+ { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
+
+ { "DSP2TXL", "IN1 Switch", "IN2L Mux" },
+ { "DSP2TXR", "IN1 Switch", "IN2R Mux" },
+
+ { "AIF1TX0", NULL, "DSP1TXL" },
+ { "AIF1TX1", NULL, "DSP1TXR" },
+ { "AIF1TX2", NULL, "DSP2TXL" },
+ { "AIF1TX3", NULL, "DSP2TXR" },
+ { "AIF1TX4", NULL, "AIF2RX0" },
+ { "AIF1TX5", NULL, "AIF2RX1" },
+
+ { "AIF1RX0", NULL, "AIFCLK" },
+ { "AIF1RX1", NULL, "AIFCLK" },
+ { "AIF1RX2", NULL, "AIFCLK" },
+ { "AIF1RX3", NULL, "AIFCLK" },
+ { "AIF1RX4", NULL, "AIFCLK" },
+ { "AIF1RX5", NULL, "AIFCLK" },
+
+ { "AIF2RX0", NULL, "AIFCLK" },
+ { "AIF2RX1", NULL, "AIFCLK" },
+
+ { "DSP1RXL", NULL, "SYSDSPCLK" },
+ { "DSP1RXR", NULL, "SYSDSPCLK" },
+ { "DSP2RXL", NULL, "SYSDSPCLK" },
+ { "DSP2RXR", NULL, "SYSDSPCLK" },
+ { "DSP1TXL", NULL, "SYSDSPCLK" },
+ { "DSP1TXR", NULL, "SYSDSPCLK" },
+ { "DSP2TXL", NULL, "SYSDSPCLK" },
+ { "DSP2TXR", NULL, "SYSDSPCLK" },
+
+ { "AIF1RXA", NULL, "AIF1RX0" },
+ { "AIF1RXA", NULL, "AIF1RX1" },
+ { "AIF1RXB", NULL, "AIF1RX2" },
+ { "AIF1RXB", NULL, "AIF1RX3" },
+ { "AIF1RXC", NULL, "AIF1RX4" },
+ { "AIF1RXC", NULL, "AIF1RX5" },
+
+ { "AIF2RX", NULL, "AIF2RX0" },
+ { "AIF2RX", NULL, "AIF2RX1" },
+
+ { "AIF2TX", "DSP2", "DSP2TX" },
+ { "AIF2TX", "DSP1", "DSP1RX" },
+ { "AIF2TX", "AIF1", "AIF1RXC" },
+
+ { "DSP1RXL", NULL, "DSP1RX" },
+ { "DSP1RXR", NULL, "DSP1RX" },
+ { "DSP2RXL", NULL, "DSP2RX" },
+ { "DSP2RXR", NULL, "DSP2RX" },
+
+ { "DSP2TX", NULL, "DSP2TXL" },
+ { "DSP2TX", NULL, "DSP2TXR" },
+
+ { "DSP1RX", "AIF1", "AIF1RXA" },
+ { "DSP1RX", "AIF2", "AIF2RX" },
+
+ { "DSP2RX", "AIF1", "AIF1RXB" },
+ { "DSP2RX", "AIF2", "AIF2RX" },
+
+ { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "DAC2L Mixer" },
+ { "DAC2R", NULL, "DAC2R Mixer" },
+
+ { "HPOUT2L PGA", NULL, "Charge Pump" },
+ { "HPOUT2L PGA", NULL, "DAC2L" },
+ { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
+ { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
+ { "HPOUT2L_OUTP", NULL, "HPOUT2L_DCS" },
+ { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
+
+ { "HPOUT2R PGA", NULL, "Charge Pump" },
+ { "HPOUT2R PGA", NULL, "DAC2R" },
+ { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
+ { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
+ { "HPOUT2R_OUTP", NULL, "HPOUT2R_DCS" },
+ { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
+
+ { "HPOUT1L PGA", NULL, "Charge Pump" },
+ { "HPOUT1L PGA", NULL, "DAC1L" },
+ { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
+ { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
+ { "HPOUT1L_OUTP", NULL, "HPOUT1L_DCS" },
+ { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
+
+ { "HPOUT1R PGA", NULL, "Charge Pump" },
+ { "HPOUT1R PGA", NULL, "DAC1R" },
+ { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
+ { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
+ { "HPOUT1R_OUTP", NULL, "HPOUT1R_DCS" },
+ { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_OUTP" },
+
+ { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
+ { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
+ { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
+ { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },
+
+ { "SPKL", "DAC1L", "DAC1L" },
+ { "SPKL", "DAC1R", "DAC1R" },
+ { "SPKL", "DAC2L", "DAC2L" },
+ { "SPKL", "DAC2R", "DAC2R" },
+
+ { "SPKR", "DAC1L", "DAC1L" },
+ { "SPKR", "DAC1R", "DAC1R" },
+ { "SPKR", "DAC2L", "DAC2L" },
+ { "SPKR", "DAC2R", "DAC2R" },
+
+ { "SPKL PGA", NULL, "SPKL" },
+ { "SPKR PGA", NULL, "SPKR" },
+
+ { "SPKDAT", NULL, "SPKL PGA" },
+ { "SPKDAT", NULL, "SPKR PGA" },
+};
+
+static int wm8915_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Due to the sparseness of the register map the compiler
+ * output from an explicit switch statement ends up being much
+ * more efficient than a table.
+ */
+ switch (reg) {
+ case WM8915_SOFTWARE_RESET:
+ case WM8915_POWER_MANAGEMENT_1:
+ case WM8915_POWER_MANAGEMENT_2:
+ case WM8915_POWER_MANAGEMENT_3:
+ case WM8915_POWER_MANAGEMENT_4:
+ case WM8915_POWER_MANAGEMENT_5:
+ case WM8915_POWER_MANAGEMENT_6:
+ case WM8915_POWER_MANAGEMENT_7:
+ case WM8915_POWER_MANAGEMENT_8:
+ case WM8915_LEFT_LINE_INPUT_VOLUME:
+ case WM8915_RIGHT_LINE_INPUT_VOLUME:
+ case WM8915_LINE_INPUT_CONTROL:
+ case WM8915_DAC1_HPOUT1_VOLUME:
+ case WM8915_DAC2_HPOUT2_VOLUME:
+ case WM8915_DAC1_LEFT_VOLUME:
+ case WM8915_DAC1_RIGHT_VOLUME:
+ case WM8915_DAC2_LEFT_VOLUME:
+ case WM8915_DAC2_RIGHT_VOLUME:
+ case WM8915_OUTPUT1_LEFT_VOLUME:
+ case WM8915_OUTPUT1_RIGHT_VOLUME:
+ case WM8915_OUTPUT2_LEFT_VOLUME:
+ case WM8915_OUTPUT2_RIGHT_VOLUME:
+ case WM8915_MICBIAS_1:
+ case WM8915_MICBIAS_2:
+ case WM8915_LDO_1:
+ case WM8915_LDO_2:
+ case WM8915_ACCESSORY_DETECT_MODE_1:
+ case WM8915_ACCESSORY_DETECT_MODE_2:
+ case WM8915_HEADPHONE_DETECT_1:
+ case WM8915_HEADPHONE_DETECT_2:
+ case WM8915_MIC_DETECT_1:
+ case WM8915_MIC_DETECT_2:
+ case WM8915_MIC_DETECT_3:
+ case WM8915_CHARGE_PUMP_1:
+ case WM8915_CHARGE_PUMP_2:
+ case WM8915_DC_SERVO_1:
+ case WM8915_DC_SERVO_2:
+ case WM8915_DC_SERVO_3:
+ case WM8915_DC_SERVO_5:
+ case WM8915_DC_SERVO_6:
+ case WM8915_DC_SERVO_7:
+ case WM8915_DC_SERVO_READBACK_0:
+ case WM8915_ANALOGUE_HP_1:
+ case WM8915_ANALOGUE_HP_2:
+ case WM8915_CHIP_REVISION:
+ case WM8915_CONTROL_INTERFACE_1:
+ case WM8915_WRITE_SEQUENCER_CTRL_1:
+ case WM8915_WRITE_SEQUENCER_CTRL_2:
+ case WM8915_AIF_CLOCKING_1:
+ case WM8915_AIF_CLOCKING_2:
+ case WM8915_CLOCKING_1:
+ case WM8915_CLOCKING_2:
+ case WM8915_AIF_RATE:
+ case WM8915_FLL_CONTROL_1:
+ case WM8915_FLL_CONTROL_2:
+ case WM8915_FLL_CONTROL_3:
+ case WM8915_FLL_CONTROL_4:
+ case WM8915_FLL_CONTROL_5:
+ case WM8915_FLL_CONTROL_6:
+ case WM8915_FLL_EFS_1:
+ case WM8915_FLL_EFS_2:
+ case WM8915_AIF1_CONTROL:
+ case WM8915_AIF1_BCLK:
+ case WM8915_AIF1_TX_LRCLK_1:
+ case WM8915_AIF1_TX_LRCLK_2:
+ case WM8915_AIF1_RX_LRCLK_1:
+ case WM8915_AIF1_RX_LRCLK_2:
+ case WM8915_AIF1TX_DATA_CONFIGURATION_1:
+ case WM8915_AIF1TX_DATA_CONFIGURATION_2:
+ case WM8915_AIF1RX_DATA_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_2_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_3_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_4_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_5_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_2_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_3_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_4_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_5_CONFIGURATION:
+ case WM8915_AIF1RX_MONO_CONFIGURATION:
+ case WM8915_AIF1TX_TEST:
+ case WM8915_AIF2_CONTROL:
+ case WM8915_AIF2_BCLK:
+ case WM8915_AIF2_TX_LRCLK_1:
+ case WM8915_AIF2_TX_LRCLK_2:
+ case WM8915_AIF2_RX_LRCLK_1:
+ case WM8915_AIF2_RX_LRCLK_2:
+ case WM8915_AIF2TX_DATA_CONFIGURATION_1:
+ case WM8915_AIF2TX_DATA_CONFIGURATION_2:
+ case WM8915_AIF2RX_DATA_CONFIGURATION:
+ case WM8915_AIF2TX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF2TX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF2RX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF2RX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF2RX_MONO_CONFIGURATION:
+ case WM8915_AIF2TX_TEST:
+ case WM8915_DSP1_TX_LEFT_VOLUME:
+ case WM8915_DSP1_TX_RIGHT_VOLUME:
+ case WM8915_DSP1_RX_LEFT_VOLUME:
+ case WM8915_DSP1_RX_RIGHT_VOLUME:
+ case WM8915_DSP1_TX_FILTERS:
+ case WM8915_DSP1_RX_FILTERS_1:
+ case WM8915_DSP1_RX_FILTERS_2:
+ case WM8915_DSP1_DRC_1:
+ case WM8915_DSP1_DRC_2:
+ case WM8915_DSP1_DRC_3:
+ case WM8915_DSP1_DRC_4:
+ case WM8915_DSP1_DRC_5:
+ case WM8915_DSP1_RX_EQ_GAINS_1:
+ case WM8915_DSP1_RX_EQ_GAINS_2:
+ case WM8915_DSP1_RX_EQ_BAND_1_A:
+ case WM8915_DSP1_RX_EQ_BAND_1_B:
+ case WM8915_DSP1_RX_EQ_BAND_1_PG:
+ case WM8915_DSP1_RX_EQ_BAND_2_A:
+ case WM8915_DSP1_RX_EQ_BAND_2_B:
+ case WM8915_DSP1_RX_EQ_BAND_2_C:
+ case WM8915_DSP1_RX_EQ_BAND_2_PG:
+ case WM8915_DSP1_RX_EQ_BAND_3_A:
+ case WM8915_DSP1_RX_EQ_BAND_3_B:
+ case WM8915_DSP1_RX_EQ_BAND_3_C:
+ case WM8915_DSP1_RX_EQ_BAND_3_PG:
+ case WM8915_DSP1_RX_EQ_BAND_4_A:
+ case WM8915_DSP1_RX_EQ_BAND_4_B:
+ case WM8915_DSP1_RX_EQ_BAND_4_C:
+ case WM8915_DSP1_RX_EQ_BAND_4_PG:
+ case WM8915_DSP1_RX_EQ_BAND_5_A:
+ case WM8915_DSP1_RX_EQ_BAND_5_B:
+ case WM8915_DSP1_RX_EQ_BAND_5_PG:
+ case WM8915_DSP2_TX_LEFT_VOLUME:
+ case WM8915_DSP2_TX_RIGHT_VOLUME:
+ case WM8915_DSP2_RX_LEFT_VOLUME:
+ case WM8915_DSP2_RX_RIGHT_VOLUME:
+ case WM8915_DSP2_TX_FILTERS:
+ case WM8915_DSP2_RX_FILTERS_1:
+ case WM8915_DSP2_RX_FILTERS_2:
+ case WM8915_DSP2_DRC_1:
+ case WM8915_DSP2_DRC_2:
+ case WM8915_DSP2_DRC_3:
+ case WM8915_DSP2_DRC_4:
+ case WM8915_DSP2_DRC_5:
+ case WM8915_DSP2_RX_EQ_GAINS_1:
+ case WM8915_DSP2_RX_EQ_GAINS_2:
+ case WM8915_DSP2_RX_EQ_BAND_1_A:
+ case WM8915_DSP2_RX_EQ_BAND_1_B:
+ case WM8915_DSP2_RX_EQ_BAND_1_PG:
+ case WM8915_DSP2_RX_EQ_BAND_2_A:
+ case WM8915_DSP2_RX_EQ_BAND_2_B:
+ case WM8915_DSP2_RX_EQ_BAND_2_C:
+ case WM8915_DSP2_RX_EQ_BAND_2_PG:
+ case WM8915_DSP2_RX_EQ_BAND_3_A:
+ case WM8915_DSP2_RX_EQ_BAND_3_B:
+ case WM8915_DSP2_RX_EQ_BAND_3_C:
+ case WM8915_DSP2_RX_EQ_BAND_3_PG:
+ case WM8915_DSP2_RX_EQ_BAND_4_A:
+ case WM8915_DSP2_RX_EQ_BAND_4_B:
+ case WM8915_DSP2_RX_EQ_BAND_4_C:
+ case WM8915_DSP2_RX_EQ_BAND_4_PG:
+ case WM8915_DSP2_RX_EQ_BAND_5_A:
+ case WM8915_DSP2_RX_EQ_BAND_5_B:
+ case WM8915_DSP2_RX_EQ_BAND_5_PG:
+ case WM8915_DAC1_MIXER_VOLUMES:
+ case WM8915_DAC1_LEFT_MIXER_ROUTING:
+ case WM8915_DAC1_RIGHT_MIXER_ROUTING:
+ case WM8915_DAC2_MIXER_VOLUMES:
+ case WM8915_DAC2_LEFT_MIXER_ROUTING:
+ case WM8915_DAC2_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP1_TX_LEFT_MIXER_ROUTING:
+ case WM8915_DSP1_TX_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP2_TX_LEFT_MIXER_ROUTING:
+ case WM8915_DSP2_TX_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP_TX_MIXER_SELECT:
+ case WM8915_DAC_SOFTMUTE:
+ case WM8915_OVERSAMPLING:
+ case WM8915_SIDETONE:
+ case WM8915_GPIO_1:
+ case WM8915_GPIO_2:
+ case WM8915_GPIO_3:
+ case WM8915_GPIO_4:
+ case WM8915_GPIO_5:
+ case WM8915_PULL_CONTROL_1:
+ case WM8915_PULL_CONTROL_2:
+ case WM8915_INTERRUPT_STATUS_1:
+ case WM8915_INTERRUPT_STATUS_2:
+ case WM8915_INTERRUPT_RAW_STATUS_2:
+ case WM8915_INTERRUPT_STATUS_1_MASK:
+ case WM8915_INTERRUPT_STATUS_2_MASK:
+ case WM8915_INTERRUPT_CONTROL:
+ case WM8915_LEFT_PDM_SPEAKER:
+ case WM8915_RIGHT_PDM_SPEAKER:
+ case WM8915_PDM_SPEAKER_MUTE_SEQUENCE:
+ case WM8915_PDM_SPEAKER_VOLUME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8915_volatile_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WM8915_SOFTWARE_RESET:
+ case WM8915_CHIP_REVISION:
+ case WM8915_LDO_1:
+ case WM8915_LDO_2:
+ case WM8915_INTERRUPT_STATUS_1:
+ case WM8915_INTERRUPT_STATUS_2:
+ case WM8915_INTERRUPT_RAW_STATUS_2:
+ case WM8915_DC_SERVO_READBACK_0:
+ case WM8915_DC_SERVO_2:
+ case WM8915_DC_SERVO_6:
+ case WM8915_DC_SERVO_7:
+ case WM8915_FLL_CONTROL_6:
+ case WM8915_MIC_DETECT_3:
+ case WM8915_HEADPHONE_DETECT_1:
+ case WM8915_HEADPHONE_DETECT_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8915_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
+}
+
+static int wm8915_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
+ WM8915_BG_ENA, WM8915_BG_ENA);
+ msleep(2);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena,
+ 1);
+ msleep(5);
+ }
+
+ codec->cache_only = false;
+ snd_soc_cache_sync(codec);
+ }
+
+ snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
+ WM8915_BG_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ codec->cache_only = true;
+ if (wm8915->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int aifctrl = 0;
+ int bclk = 0;
+ int lrclk_tx = 0;
+ int lrclk_rx = 0;
+ int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;
+
+ switch (dai->id) {
+ case 0:
+ aifctrl_reg = WM8915_AIF1_CONTROL;
+ bclk_reg = WM8915_AIF1_BCLK;
+ lrclk_tx_reg = WM8915_AIF1_TX_LRCLK_2;
+ lrclk_rx_reg = WM8915_AIF1_RX_LRCLK_2;
+ break;
+ case 1:
+ aifctrl_reg = WM8915_AIF2_CONTROL;
+ bclk_reg = WM8915_AIF2_BCLK;
+ lrclk_tx_reg = WM8915_AIF2_TX_LRCLK_2;
+ lrclk_rx_reg = WM8915_AIF2_RX_LRCLK_2;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM8915_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM8915_AIF1_BCLK_INV;
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM8915_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= WM8915_AIF1_BCLK_MSTR;
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl |= 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl |= 2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl |= 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, aifctrl_reg, WM8915_AIF1_FMT_MASK, aifctrl);
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8915_AIF1_BCLK_INV | WM8915_AIF1_BCLK_MSTR,
+ bclk);
+ snd_soc_update_bits(codec, lrclk_tx_reg,
+ WM8915_AIF1TX_LRCLK_INV |
+ WM8915_AIF1TX_LRCLK_MSTR,
+ lrclk_tx);
+ snd_soc_update_bits(codec, lrclk_rx_reg,
+ WM8915_AIF1RX_LRCLK_INV |
+ WM8915_AIF1RX_LRCLK_MSTR,
+ lrclk_rx);
+
+ return 0;
+}
+
+static const int bclk_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static const int dsp_divs[] = {
+ 48000, 32000, 16000, 8000
+};
+
+static int wm8915_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 wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int bits, i, bclk_rate, best, cur_val;
+ int aifdata = 0;
+ int bclk = 0;
+ int lrclk = 0;
+ int dsp = 0;
+ int aifdata_reg, bclk_reg, lrclk_reg, dsp_shift;
+
+ if (!wm8915->sysclk) {
+ dev_err(codec->dev, "SYSCLK not configured\n");
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case 0:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8915_GPIO_1)) & WM8915_GP1_FN_MASK) {
+ aifdata_reg = WM8915_AIF1RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8915_AIF1_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8915_AIF1_TX_LRCLK_1;
+ }
+ bclk_reg = WM8915_AIF1_BCLK;
+ dsp_shift = 0;
+ break;
+ case 1:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8915_GPIO_2)) & WM8915_GP2_FN_MASK) {
+ aifdata_reg = WM8915_AIF2RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8915_AIF2_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8915_AIF2_TX_LRCLK_1;
+ }
+ bclk_reg = WM8915_AIF2_BCLK;
+ dsp_shift = WM8915_DSP2_DIV_SHIFT;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
+ return bclk_rate;
+ }
+
+ /* Needs looking at for TDM */
+ bits = snd_pcm_format_width(params_format(params));
+ if (bits < 0)
+ return bits;
+ aifdata |= (bits << WM8915_AIF1TX_WL_SHIFT) | bits;
+
+ for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
+ if (dsp_divs[i] == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(dsp_divs)) {
+ dev_err(codec->dev, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ dsp |= i << dsp_shift;
+
+ /* Pick a divisor for BCLK as close as we can get to ideal */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8915->sysclk / bclk_divs[best];
+ dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+ bclk |= best;
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aifdata_reg,
+ WM8915_AIF1TX_WL_MASK |
+ WM8915_AIF1TX_SLOT_LEN_MASK,
+ aifdata);
+ snd_soc_update_bits(codec, bclk_reg, WM8915_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2,
+ WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp);
+
+ wm8915->rx_rate[dai->id] = params_rate(params);
+
+ return 0;
+}
+
+static int wm8915_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int lfclk = 0;
+ int ratediv = 0;
+ int src;
+ int old;
+
+ /* Disable SYSCLK while we reconfigure */
+ old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_ENA, 0);
+
+ switch (clk_id) {
+ case WM8915_SYSCLK_MCLK1:
+ wm8915->sysclk = freq;
+ src = 0;
+ break;
+ case WM8915_SYSCLK_MCLK2:
+ wm8915->sysclk = freq;
+ src = 1;
+ break;
+ case WM8915_SYSCLK_FLL:
+ wm8915->sysclk = freq;
+ src = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (wm8915->sysclk) {
+ case 6144000:
+ snd_soc_update_bits(codec, WM8915_AIF_RATE,
+ WM8915_SYSCLK_RATE, 0);
+ break;
+ case 24576000:
+ ratediv = WM8915_SYSCLK_DIV;
+ case 12288000:
+ snd_soc_update_bits(codec, WM8915_AIF_RATE,
+ WM8915_SYSCLK_RATE, WM8915_SYSCLK_RATE);
+ break;
+ case 32000:
+ case 32768:
+ lfclk = WM8915_LFCLK_ENA;
+ break;
+ default:
+ dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
+ wm8915->sysclk);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
+ src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
+ snd_soc_update_bits(codec, WM8915_CLOCKING_1, WM8915_LFCLK_ENA, lfclk);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_ENA, old);
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 fll_loop_gain;
+ u16 fll_ref_freq;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ if (Fref >= 3000000)
+ fll_div->fll_loop_gain = 5;
+ else
+ fll_div->fll_loop_gain = 0;
+
+ if (Fref >= 48000)
+ fll_div->fll_ref_freq = 0;
+ else
+ fll_div->fll_ref_freq = 1;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div fll_div;
+ unsigned long timeout;
+ int ret, reg;
+
+ /* Any change? */
+ if (source == wm8915->fll_src && Fref == wm8915->fll_fref &&
+ Fout == wm8915->fll_fout)
+ return 0;
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8915->fll_fref = 0;
+ wm8915->fll_fout = 0;
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
+ WM8915_FLL_ENA, 0);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ switch (source) {
+ case WM8915_FLL_MCLK1:
+ reg = 0;
+ break;
+ case WM8915_FLL_MCLK2:
+ reg = 1;
+ case WM8915_FLL_DACLRCLK1:
+ reg = 2;
+ break;
+ case WM8915_FLL_BCLK1:
+ reg = 3;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ return -EINVAL;
+ }
+
+ reg |= fll_div.fll_refclk_div << WM8915_FLL_REFCLK_DIV_SHIFT;
+ reg |= fll_div.fll_ref_freq << WM8915_FLL_REF_FREQ_SHIFT;
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_5,
+ WM8915_FLL_REFCLK_DIV_MASK | WM8915_FLL_REF_FREQ |
+ WM8915_FLL_REFCLK_SRC_MASK, reg);
+
+ reg = 0;
+ if (fll_div.theta || fll_div.lambda)
+ reg |= WM8915_FLL_EFS_ENA | (3 << WM8915_FLL_LFSR_SEL_SHIFT);
+ else
+ reg |= 1 << WM8915_FLL_LFSR_SEL_SHIFT;
+ snd_soc_write(codec, WM8915_FLL_EFS_2, reg);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_2,
+ WM8915_FLL_OUTDIV_MASK |
+ WM8915_FLL_FRATIO_MASK,
+ (fll_div.fll_outdiv << WM8915_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio));
+
+ snd_soc_write(codec, WM8915_FLL_CONTROL_3, fll_div.theta);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_4,
+ WM8915_FLL_N_MASK | WM8915_FLL_LOOP_GAIN_MASK,
+ (fll_div.n << WM8915_FLL_N_SHIFT) |
+ fll_div.fll_loop_gain);
+
+ snd_soc_write(codec, WM8915_FLL_EFS_1, fll_div.lambda);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
+ WM8915_FLL_ENA, WM8915_FLL_ENA);
+
+ /* The FLL supports live reconfiguration - kick that in case we were
+ * already enabled.
+ */
+ snd_soc_write(codec, WM8915_FLL_CONTROL_6, WM8915_FLL_SWITCH_CLK);
+
+ /* Wait for the FLL to lock, using the interrupt if possible */
+ if (Fref > 1000000)
+ timeout = usecs_to_jiffies(300);
+ else
+ timeout = msecs_to_jiffies(2);
+
+ wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ wm8915->fll_fref = Fref;
+ wm8915->fll_fout = Fout;
+ wm8915->fll_src = source;
+
+ return 0;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8915_priv *gpio_to_wm8915(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8915_priv, gpio_chip);
+}
+
+static void wm8915_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+
+ snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_LVL, !!value << WM8915_GP1_LVL_SHIFT);
+}
+
+static int wm8915_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+ int val;
+
+ val = (1 << WM8915_GP1_FN_SHIFT) | (!!value << WM8915_GP1_LVL_SHIFT);
+
+ return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_FN_MASK | WM8915_GP1_DIR |
+ WM8915_GP1_LVL, val);
+}
+
+static int wm8915_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+ int ret;
+
+ ret = snd_soc_read(codec, WM8915_GPIO_1 + offset);
+ if (ret < 0)
+ return ret;
+
+ return (ret & WM8915_GP1_LVL) != 0;
+}
+
+static int wm8915_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+
+ return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_FN_MASK | WM8915_GP1_DIR,
+ (1 << WM8915_GP1_FN_SHIFT) |
+ (1 << WM8915_GP1_DIR_SHIFT));
+}
+
+static struct gpio_chip wm8915_template_chip = {
+ .label = "wm8915",
+ .owner = THIS_MODULE,
+ .direction_output = wm8915_gpio_direction_out,
+ .set = wm8915_gpio_set,
+ .direction_input = wm8915_gpio_direction_in,
+ .get = wm8915_gpio_get,
+ .can_sleep = 1,
+};
+
+static void wm8915_init_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8915->gpio_chip = wm8915_template_chip;
+ wm8915->gpio_chip.ngpio = 5;
+ wm8915->gpio_chip.dev = codec->dev;
+
+ if (wm8915->pdata.gpio_base)
+ wm8915->gpio_chip.base = wm8915->pdata.gpio_base;
+ else
+ wm8915->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8915->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8915_free_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = gpiochip_remove(&wm8915->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8915_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8915_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+/**
+ * wm8915_detect - Enable default WM8915 jack detection
+ *
+ * The WM8915 has advanced accessory detection support for headsets.
+ * This function provides a default implementation which integrates
+ * the majority of this functionality with minimal user configuration.
+ *
+ * This will detect headset, headphone and short circuit button and
+ * will also detect inverted microphone ground connections and update
+ * the polarity of the connections.
+ */
+int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8915_polarity_fn polarity_cb)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+
+ wm8915->jack = jack;
+ wm8915->detecting = true;
+ wm8915->polarity_cb = polarity_cb;
+
+ if (wm8915->polarity_cb)
+ wm8915->polarity_cb(codec, 0);
+
+ /* Clear discarge to avoid noise during detection */
+ snd_soc_update_bits(codec, WM8915_MICBIAS_1,
+ WM8915_MICB1_DISCH, 0);
+ snd_soc_update_bits(codec, WM8915_MICBIAS_2,
+ WM8915_MICB2_DISCH, 0);
+
+ /* LDO2 powers the microphones, SYSCLK clocks detection */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
+
+ /* We start off just enabling microphone detection - even a
+ * plain headphone will trigger detection.
+ */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_ENA, WM8915_MICD_ENA);
+
+ /* Slowest detection rate, gives debounce for initial detection */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ WM8915_MICD_RATE_MASK);
+
+ /* Enable interrupts and we're off */
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_STATUS_2_MASK,
+ WM8915_IM_MICD_EINT, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8915_detect);
+
+static void wm8915_micd(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int val, reg;
+
+ val = snd_soc_read(codec, WM8915_MIC_DETECT_3);
+
+ dev_dbg(codec->dev, "Microphone event: %x\n", val);
+
+ if (!(val & WM8915_MICD_VALID)) {
+ dev_warn(codec->dev, "Microphone detection state invalid\n");
+ return;
+ }
+
+ /* No accessory, reset everything and report removal */
+ if (!(val & WM8915_MICD_STS)) {
+ dev_dbg(codec->dev, "Jack removal detected\n");
+ wm8915->jack_mic = false;
+ wm8915->detecting = true;
+ snd_soc_jack_report(wm8915->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ WM8915_MICD_RATE_MASK);
+ return;
+ }
+
+ /* If the measurement is very high we've got a microphone but
+ * do a little debounce to account for mechanical issues.
+ */
+ if (val & 0x400) {
+ dev_dbg(codec->dev, "Microphone detected\n");
+ snd_soc_jack_report(wm8915->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ wm8915->jack_mic = true;
+ wm8915->detecting = false;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones.
+ */
+ if (wm8915->detecting && (val & 0x3f0)) {
+ reg = snd_soc_read(codec, WM8915_ACCESSORY_DETECT_MODE_2);
+ reg ^= WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
+ WM8915_MICD_BIAS_SRC;
+ snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
+ WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
+ WM8915_MICD_BIAS_SRC, reg);
+
+ if (wm8915->polarity_cb)
+ wm8915->polarity_cb(codec,
+ (reg & WM8915_MICD_SRC) != 0);
+
+ dev_dbg(codec->dev, "Set microphone polarity to %d\n",
+ (reg & WM8915_MICD_SRC) != 0);
+
+ return;
+ }
+
+ /* Don't distinguish between buttons, just report any low
+ * impedence as BTN_0.
+ */
+ if (val & 0x3fc) {
+ if (wm8915->jack_mic) {
+ dev_dbg(codec->dev, "Mic button detected\n");
+ snd_soc_jack_report(wm8915->jack,
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ } else {
+ dev_dbg(codec->dev, "Headphone detected\n");
+ snd_soc_jack_report(wm8915->jack,
+ SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0);
+ wm8915->detecting = false;
+ }
+ }
+
+ /* Increase poll rate to give better responsiveness for buttons */
+ if (!wm8915->detecting)
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ 5 << WM8915_MICD_RATE_SHIFT);
+}
+
+static irqreturn_t wm8915_irq(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int irq_val;
+
+ irq_val = snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2);
+ if (irq_val < 0) {
+ dev_err(codec->dev, "Failed to read IRQ status: %d\n",
+ irq_val);
+ return IRQ_NONE;
+ }
+ irq_val &= ~snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2_MASK);
+
+ if (irq_val & (WM8915_DCS_DONE_01_EINT | WM8915_DCS_DONE_23_EINT)) {
+ dev_dbg(codec->dev, "DC servo IRQ\n");
+ complete(&wm8915->dcs_done);
+ }
+
+ if (irq_val & WM8915_FIFOS_ERR_EINT)
+ dev_err(codec->dev, "Digital core FIFO error\n");
+
+ if (irq_val & WM8915_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8915->fll_lock);
+ }
+
+ if (irq_val & WM8915_MICD_EINT)
+ wm8915_micd(codec);
+
+ if (irq_val) {
+ snd_soc_write(codec, WM8915_INTERRUPT_STATUS_2, irq_val);
+
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("DSP1 EQ Mode",
+ wm8915->retune_mobile_enum,
+ wm8915_get_retune_mobile_enum,
+ wm8915_put_retune_mobile_enum),
+ SOC_ENUM_EXT("DSP2 EQ Mode",
+ wm8915->retune_mobile_enum,
+ wm8915_get_retune_mobile_enum,
+ wm8915_put_retune_mobile_enum),
+ };
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8915->num_retune_mobile_texts = 0;
+ wm8915->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8915->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8915->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8915->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8915->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8915->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8915->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8915->num_retune_mobile_texts++;
+ wm8915->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8915->num_retune_mobile_texts);
+
+ wm8915->retune_mobile_enum.max = wm8915->num_retune_mobile_texts;
+ wm8915->retune_mobile_enum.texts = wm8915->retune_mobile_texts;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static int wm8915_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i, irq_flags;
+
+ wm8915->codec = codec;
+
+ init_completion(&wm8915->dcs_done);
+ init_completion(&wm8915->fll_lock);
+
+ dapm->idle_bias_off = true;
+ dapm->bias_level = SND_SOC_BIAS_OFF;
+
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
+ wm8915->supplies[i].supply = wm8915_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ wm8915->disable_nb[0].notifier_call = wm8915_regulator_event_0;
+ wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1;
+ wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2;
+ wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3;
+ wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4;
+ wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) {
+ ret = regulator_register_notifier(wm8915->supplies[i].consumer,
+ &wm8915->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 1);
+ msleep(5);
+ }
+
+ ret = snd_soc_read(codec, WM8915_SOFTWARE_RESET);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read ID register: %d\n", ret);
+ goto err_enable;
+ }
+ if (ret != 0x8915) {
+ dev_err(codec->dev, "Device is not a WM8915, ID %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = snd_soc_read(codec, WM8915_CHIP_REVISION);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ dev_info(codec->dev, "revision %c\n",
+ (ret & WM8915_CHIP_REV_MASK) + 'A');
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+ } else {
+ ret = wm8915_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+ }
+
+ codec->cache_only = true;
+
+ /* Apply platform data settings */
+ snd_soc_update_bits(codec, WM8915_LINE_INPUT_CONTROL,
+ WM8915_INL_MODE_MASK | WM8915_INR_MODE_MASK,
+ wm8915->pdata.inl_mode << WM8915_INL_MODE_SHIFT |
+ wm8915->pdata.inr_mode);
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->pdata.gpio_default); i++) {
+ if (!wm8915->pdata.gpio_default[i])
+ continue;
+
+ snd_soc_write(codec, WM8915_GPIO_1 + i,
+ wm8915->pdata.gpio_default[i] & 0xffff);
+ }
+
+ if (wm8915->pdata.spkmute_seq)
+ snd_soc_update_bits(codec, WM8915_PDM_SPEAKER_MUTE_SEQUENCE,
+ WM8915_SPK_MUTE_ENDIAN |
+ WM8915_SPK_MUTE_SEQ1_MASK,
+ wm8915->pdata.spkmute_seq);
+
+ snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
+ WM8915_MICD_BIAS_SRC | WM8915_HPOUT1FB_SRC |
+ WM8915_MICD_SRC, wm8915->pdata.micdet_def);
+
+ /* Latch volume update bits */
+ snd_soc_update_bits(codec, WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_IN1_VU, WM8915_IN1_VU);
+ snd_soc_update_bits(codec, WM8915_RIGHT_LINE_INPUT_VOLUME,
+ WM8915_IN1_VU, WM8915_IN1_VU);
+
+ snd_soc_update_bits(codec, WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_DAC1_RIGHT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+ snd_soc_update_bits(codec, WM8915_DAC2_RIGHT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT1_RIGHT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT2_RIGHT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8915_DSP1_TX_LEFT_VOLUME,
+ WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP1_TX_RIGHT_VOLUME,
+ WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_TX_LEFT_VOLUME,
+ WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_TX_RIGHT_VOLUME,
+ WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
+
+ snd_soc_update_bits(codec, WM8915_DSP1_RX_LEFT_VOLUME,
+ WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP1_RX_RIGHT_VOLUME,
+ WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_RX_LEFT_VOLUME,
+ WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_RX_RIGHT_VOLUME,
+ WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
+
+ /* No support currently for the underclocked TDM modes and
+ * pick a default TDM layout with each channel pair working with
+ * slots 0 and 1. */
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF1RX_CHAN0_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF1RX_CHAN1_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_2_CONFIGURATION,
+ WM8915_AIF1RX_CHAN2_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN2_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_3_CONFIGURATION,
+ WM8915_AIF1RX_CHAN3_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_4_CONFIGURATION,
+ WM8915_AIF1RX_CHAN4_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_5_CONFIGURATION,
+ WM8915_AIF1RX_CHAN5_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF2RX_CHAN0_SLOTS_MASK |
+ WM8915_AIF2RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF2RX_CHAN1_SLOTS_MASK |
+ WM8915_AIF2RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF1TX_CHAN0_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF1TX_CHAN1_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_2_CONFIGURATION,
+ WM8915_AIF1TX_CHAN2_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_3_CONFIGURATION,
+ WM8915_AIF1TX_CHAN3_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_4_CONFIGURATION,
+ WM8915_AIF1TX_CHAN4_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_5_CONFIGURATION,
+ WM8915_AIF1TX_CHAN5_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF2TX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF2TX_CHAN0_SLOTS_MASK |
+ WM8915_AIF2TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF2TX_CHAN1_SLOTS_MASK |
+ WM8915_AIF2TX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+
+ if (wm8915->pdata.num_retune_mobile_cfgs)
+ wm8915_retune_mobile_pdata(codec);
+ else
+ snd_soc_add_controls(codec, wm8915_eq_controls,
+ ARRAY_SIZE(wm8915_eq_controls));
+
+ /* If the TX LRCLK pins are not in LRCLK mode configure the
+ * AIFs to source their clocks from the RX LRCLKs.
+ */
+ if ((snd_soc_read(codec, WM8915_GPIO_1)))
+ snd_soc_update_bits(codec, WM8915_AIF1_TX_LRCLK_2,
+ WM8915_AIF1TX_LRCLK_MODE,
+ WM8915_AIF1TX_LRCLK_MODE);
+
+ if ((snd_soc_read(codec, WM8915_GPIO_2)))
+ snd_soc_update_bits(codec, WM8915_AIF2_TX_LRCLK_2,
+ WM8915_AIF2TX_LRCLK_MODE,
+ WM8915_AIF2TX_LRCLK_MODE);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+
+ wm8915_init_gpio(codec);
+
+ if (i2c->irq) {
+ if (wm8915->pdata.irq_flags)
+ irq_flags = wm8915->pdata.irq_flags;
+ else
+ irq_flags = IRQF_TRIGGER_LOW;
+
+ irq_flags |= IRQF_ONESHOT;
+
+ ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
+ irq_flags, "wm8915", codec);
+ if (ret == 0) {
+ /* Unmask the interrupt */
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
+ WM8915_IM_IRQ, 0);
+
+ /* Enable error reporting and DC servo status */
+ snd_soc_update_bits(codec,
+ WM8915_INTERRUPT_STATUS_2_MASK,
+ WM8915_IM_DCS_DONE_23_EINT |
+ WM8915_IM_DCS_DONE_01_EINT |
+ WM8915_IM_FLL_LOCK_EINT |
+ WM8915_IM_FIFOS_ERR_EINT,
+ 0);
+ } else {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n",
+ ret);
+ }
+ }
+
+ return 0;
+
+err_enable:
+ if (wm8915->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+err:
+ return ret;
+}
+
+static int wm8915_remove(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int i;
+
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
+ WM8915_IM_IRQ, WM8915_IM_IRQ);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, codec);
+
+ wm8915_free_gpio(codec);
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
+ regulator_unregister_notifier(wm8915->supplies[i].consumer,
+ &wm8915->disable_nb[i]);
+ regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8915 = {
+ .probe = wm8915_probe,
+ .remove = wm8915_remove,
+ .set_bias_level = wm8915_set_bias_level,
+ .seq_notifier = wm8915_seq_notifier,
+ .reg_cache_size = WM8915_MAX_REGISTER + 1,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = wm8915_reg,
+ .volatile_register = wm8915_volatile_register,
+ .readable_register = wm8915_readable_register,
+ .compress_type = SND_SOC_RBTREE_COMPRESSION,
+ .controls = wm8915_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8915_snd_controls),
+ .dapm_widgets = wm8915_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8915_dapm_widgets),
+ .dapm_routes = wm8915_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8915_dapm_routes),
+ .set_pll = wm8915_set_fll,
+};
+
+#define WM8915_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define WM8915_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8915_dai_ops = {
+ .set_fmt = wm8915_set_fmt,
+ .hw_params = wm8915_hw_params,
+ .set_sysclk = wm8915_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8915_dai[] = {
+ {
+ .name = "wm8915-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .ops = &wm8915_dai_ops,
+ },
+ {
+ .name = "wm8915-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .ops = &wm8915_dai_ops,
+ },
+};
+
+static __devinit int wm8915_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8915_priv *wm8915;
+ int ret;
+
+ wm8915 = kzalloc(sizeof(struct wm8915_priv), GFP_KERNEL);
+ if (wm8915 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8915);
+
+ if (dev_get_platdata(&i2c->dev))
+ memcpy(&wm8915->pdata, dev_get_platdata(&i2c->dev),
+ sizeof(wm8915->pdata));
+
+ if (wm8915->pdata.ldo_ena > 0) {
+ ret = gpio_request_one(wm8915->pdata.ldo_ena,
+ GPIOF_OUT_INIT_LOW, "WM8915 ENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
+ wm8915->pdata.ldo_ena, ret);
+ goto err;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8915, wm8915_dai,
+ ARRAY_SIZE(wm8915_dai));
+ if (ret < 0)
+ goto err_gpio;
+
+ return ret;
+
+err_gpio:
+ if (wm8915->pdata.ldo_ena > 0)
+ gpio_free(wm8915->pdata.ldo_ena);
+err:
+ kfree(wm8915);
+
+ return ret;
+}
+
+static __devexit int wm8915_i2c_remove(struct i2c_client *client)
+{
+ struct wm8915_priv *wm8915 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ if (wm8915->pdata.ldo_ena > 0)
+ gpio_free(wm8915->pdata.ldo_ena);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id wm8915_i2c_id[] = {
+ { "wm8915", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8915_i2c_id);
+
+static struct i2c_driver wm8915_i2c_driver = {
+ .driver = {
+ .name = "wm8915",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8915_i2c_probe,
+ .remove = __devexit_p(wm8915_i2c_remove),
+ .id_table = wm8915_i2c_id,
+};
+
+static int __init wm8915_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm8915_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8915 I2C driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(wm8915_modinit);
+
+static void __exit wm8915_exit(void)
+{
+ i2c_del_driver(&wm8915_i2c_driver);
+}
+module_exit(wm8915_exit);
+
+MODULE_DESCRIPTION("ASoC WM8915 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8915.h b/sound/soc/codecs/wm8915.h
new file mode 100644
index 00000000000..200ffd7bf95
--- /dev/null
+++ b/sound/soc/codecs/wm8915.h
@@ -0,0 +1,3717 @@
+/*
+ * wm8915.h - WM8915 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM8915_H
+#define _WM8915_H
+
+#define WM8915_SYSCLK_MCLK1 1
+#define WM8915_SYSCLK_MCLK2 2
+#define WM8915_SYSCLK_FLL 3
+
+#define WM8915_FLL_MCLK1 1
+#define WM8915_FLL_MCLK2 2
+#define WM8915_FLL_DACLRCLK1 3
+#define WM8915_FLL_BCLK1 4
+
+typedef void (*wm8915_polarity_fn)(struct snd_soc_codec *codec, int polarity);
+
+int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8915_polarity_fn polarity_cb);
+
+/*
+ * Register values.
+ */
+#define WM8915_SOFTWARE_RESET 0x00
+#define WM8915_POWER_MANAGEMENT_1 0x01
+#define WM8915_POWER_MANAGEMENT_2 0x02
+#define WM8915_POWER_MANAGEMENT_3 0x03
+#define WM8915_POWER_MANAGEMENT_4 0x04
+#define WM8915_POWER_MANAGEMENT_5 0x05
+#define WM8915_POWER_MANAGEMENT_6 0x06
+#define WM8915_POWER_MANAGEMENT_7 0x07
+#define WM8915_POWER_MANAGEMENT_8 0x08
+#define WM8915_LEFT_LINE_INPUT_VOLUME 0x10
+#define WM8915_RIGHT_LINE_INPUT_VOLUME 0x11
+#define WM8915_LINE_INPUT_CONTROL 0x12
+#define WM8915_DAC1_HPOUT1_VOLUME 0x15
+#define WM8915_DAC2_HPOUT2_VOLUME 0x16
+#define WM8915_DAC1_LEFT_VOLUME 0x18
+#define WM8915_DAC1_RIGHT_VOLUME 0x19
+#define WM8915_DAC2_LEFT_VOLUME 0x1A
+#define WM8915_DAC2_RIGHT_VOLUME 0x1B
+#define WM8915_OUTPUT1_LEFT_VOLUME 0x1C
+#define WM8915_OUTPUT1_RIGHT_VOLUME 0x1D
+#define WM8915_OUTPUT2_LEFT_VOLUME 0x1E
+#define WM8915_OUTPUT2_RIGHT_VOLUME 0x1F
+#define WM8915_MICBIAS_1 0x20
+#define WM8915_MICBIAS_2 0x21
+#define WM8915_LDO_1 0x28
+#define WM8915_LDO_2 0x29
+#define WM8915_ACCESSORY_DETECT_MODE_1 0x30
+#define WM8915_ACCESSORY_DETECT_MODE_2 0x31
+#define WM8915_HEADPHONE_DETECT_1 0x34
+#define WM8915_HEADPHONE_DETECT_2 0x35
+#define WM8915_MIC_DETECT_1 0x38
+#define WM8915_MIC_DETECT_2 0x39
+#define WM8915_MIC_DETECT_3 0x3A
+#define WM8915_CHARGE_PUMP_1 0x40
+#define WM8915_CHARGE_PUMP_2 0x41
+#define WM8915_DC_SERVO_1 0x50
+#define WM8915_DC_SERVO_2 0x51
+#define WM8915_DC_SERVO_3 0x52
+#define WM8915_DC_SERVO_5 0x54
+#define WM8915_DC_SERVO_6 0x55
+#define WM8915_DC_SERVO_7 0x56
+#define WM8915_DC_SERVO_READBACK_0 0x57
+#define WM8915_ANALOGUE_HP_1 0x60
+#define WM8915_ANALOGUE_HP_2 0x61
+#define WM8915_CHIP_REVISION 0x100
+#define WM8915_CONTROL_INTERFACE_1 0x101
+#define WM8915_WRITE_SEQUENCER_CTRL_1 0x110
+#define WM8915_WRITE_SEQUENCER_CTRL_2 0x111
+#define WM8915_AIF_CLOCKING_1 0x200
+#define WM8915_AIF_CLOCKING_2 0x201
+#define WM8915_CLOCKING_1 0x208
+#define WM8915_CLOCKING_2 0x209
+#define WM8915_AIF_RATE 0x210
+#define WM8915_FLL_CONTROL_1 0x220
+#define WM8915_FLL_CONTROL_2 0x221
+#define WM8915_FLL_CONTROL_3 0x222
+#define WM8915_FLL_CONTROL_4 0x223
+#define WM8915_FLL_CONTROL_5 0x224
+#define WM8915_FLL_CONTROL_6 0x225
+#define WM8915_FLL_EFS_1 0x226
+#define WM8915_FLL_EFS_2 0x227
+#define WM8915_AIF1_CONTROL 0x300
+#define WM8915_AIF1_BCLK 0x301
+#define WM8915_AIF1_TX_LRCLK_1 0x302
+#define WM8915_AIF1_TX_LRCLK_2 0x303
+#define WM8915_AIF1_RX_LRCLK_1 0x304
+#define WM8915_AIF1_RX_LRCLK_2 0x305
+#define WM8915_AIF1TX_DATA_CONFIGURATION_1 0x306
+#define WM8915_AIF1TX_DATA_CONFIGURATION_2 0x307
+#define WM8915_AIF1RX_DATA_CONFIGURATION 0x308
+#define WM8915_AIF1TX_CHANNEL_0_CONFIGURATION 0x309
+#define WM8915_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A
+#define WM8915_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B
+#define WM8915_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C
+#define WM8915_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D
+#define WM8915_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E
+#define WM8915_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F
+#define WM8915_AIF1RX_CHANNEL_1_CONFIGURATION 0x310
+#define WM8915_AIF1RX_CHANNEL_2_CONFIGURATION 0x311
+#define WM8915_AIF1RX_CHANNEL_3_CONFIGURATION 0x312
+#define WM8915_AIF1RX_CHANNEL_4_CONFIGURATION 0x313
+#define WM8915_AIF1RX_CHANNEL_5_CONFIGURATION 0x314
+#define WM8915_AIF1RX_MONO_CONFIGURATION 0x315
+#define WM8915_AIF1TX_TEST 0x31A
+#define WM8915_AIF2_CONTROL 0x320
+#define WM8915_AIF2_BCLK 0x321
+#define WM8915_AIF2_TX_LRCLK_1 0x322
+#define WM8915_AIF2_TX_LRCLK_2 0x323
+#define WM8915_AIF2_RX_LRCLK_1 0x324
+#define WM8915_AIF2_RX_LRCLK_2 0x325
+#define WM8915_AIF2TX_DATA_CONFIGURATION_1 0x326
+#define WM8915_AIF2TX_DATA_CONFIGURATION_2 0x327
+#define WM8915_AIF2RX_DATA_CONFIGURATION 0x328
+#define WM8915_AIF2TX_CHANNEL_0_CONFIGURATION 0x329
+#define WM8915_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A
+#define WM8915_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B
+#define WM8915_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C
+#define WM8915_AIF2RX_MONO_CONFIGURATION 0x32D
+#define WM8915_AIF2TX_TEST 0x32F
+#define WM8915_DSP1_TX_LEFT_VOLUME 0x400
+#define WM8915_DSP1_TX_RIGHT_VOLUME 0x401
+#define WM8915_DSP1_RX_LEFT_VOLUME 0x402
+#define WM8915_DSP1_RX_RIGHT_VOLUME 0x403
+#define WM8915_DSP1_TX_FILTERS 0x410
+#define WM8915_DSP1_RX_FILTERS_1 0x420
+#define WM8915_DSP1_RX_FILTERS_2 0x421
+#define WM8915_DSP1_DRC_1 0x440
+#define WM8915_DSP1_DRC_2 0x441
+#define WM8915_DSP1_DRC_3 0x442
+#define WM8915_DSP1_DRC_4 0x443
+#define WM8915_DSP1_DRC_5 0x444
+#define WM8915_DSP1_RX_EQ_GAINS_1 0x480
+#define WM8915_DSP1_RX_EQ_GAINS_2 0x481
+#define WM8915_DSP1_RX_EQ_BAND_1_A 0x482
+#define WM8915_DSP1_RX_EQ_BAND_1_B 0x483
+#define WM8915_DSP1_RX_EQ_BAND_1_PG 0x484
+#define WM8915_DSP1_RX_EQ_BAND_2_A 0x485
+#define WM8915_DSP1_RX_EQ_BAND_2_B 0x486
+#define WM8915_DSP1_RX_EQ_BAND_2_C 0x487
+#define WM8915_DSP1_RX_EQ_BAND_2_PG 0x488
+#define WM8915_DSP1_RX_EQ_BAND_3_A 0x489
+#define WM8915_DSP1_RX_EQ_BAND_3_B 0x48A
+#define WM8915_DSP1_RX_EQ_BAND_3_C 0x48B
+#define WM8915_DSP1_RX_EQ_BAND_3_PG 0x48C
+#define WM8915_DSP1_RX_EQ_BAND_4_A 0x48D
+#define WM8915_DSP1_RX_EQ_BAND_4_B 0x48E
+#define WM8915_DSP1_RX_EQ_BAND_4_C 0x48F
+#define WM8915_DSP1_RX_EQ_BAND_4_PG 0x490
+#define WM8915_DSP1_RX_EQ_BAND_5_A 0x491
+#define WM8915_DSP1_RX_EQ_BAND_5_B 0x492
+#define WM8915_DSP1_RX_EQ_BAND_5_PG 0x493
+#define WM8915_DSP2_TX_LEFT_VOLUME 0x500
+#define WM8915_DSP2_TX_RIGHT_VOLUME 0x501
+#define WM8915_DSP2_RX_LEFT_VOLUME 0x502
+#define WM8915_DSP2_RX_RIGHT_VOLUME 0x503
+#define WM8915_DSP2_TX_FILTERS 0x510
+#define WM8915_DSP2_RX_FILTERS_1 0x520
+#define WM8915_DSP2_RX_FILTERS_2 0x521
+#define WM8915_DSP2_DRC_1 0x540
+#define WM8915_DSP2_DRC_2 0x541
+#define WM8915_DSP2_DRC_3 0x542
+#define WM8915_DSP2_DRC_4 0x543
+#define WM8915_DSP2_DRC_5 0x544
+#define WM8915_DSP2_RX_EQ_GAINS_1 0x580
+#define WM8915_DSP2_RX_EQ_GAINS_2 0x581
+#define WM8915_DSP2_RX_EQ_BAND_1_A 0x582
+#define WM8915_DSP2_RX_EQ_BAND_1_B 0x583
+#define WM8915_DSP2_RX_EQ_BAND_1_PG 0x584
+#define WM8915_DSP2_RX_EQ_BAND_2_A 0x585
+#define WM8915_DSP2_RX_EQ_BAND_2_B 0x586
+#define WM8915_DSP2_RX_EQ_BAND_2_C 0x587
+#define WM8915_DSP2_RX_EQ_BAND_2_PG 0x588
+#define WM8915_DSP2_RX_EQ_BAND_3_A 0x589
+#define WM8915_DSP2_RX_EQ_BAND_3_B 0x58A
+#define WM8915_DSP2_RX_EQ_BAND_3_C 0x58B
+#define WM8915_DSP2_RX_EQ_BAND_3_PG 0x58C
+#define WM8915_DSP2_RX_EQ_BAND_4_A 0x58D
+#define WM8915_DSP2_RX_EQ_BAND_4_B 0x58E
+#define WM8915_DSP2_RX_EQ_BAND_4_C 0x58F
+#define WM8915_DSP2_RX_EQ_BAND_4_PG 0x590
+#define WM8915_DSP2_RX_EQ_BAND_5_A 0x591
+#define WM8915_DSP2_RX_EQ_BAND_5_B 0x592
+#define WM8915_DSP2_RX_EQ_BAND_5_PG 0x593
+#define WM8915_DAC1_MIXER_VOLUMES 0x600
+#define WM8915_DAC1_LEFT_MIXER_ROUTING 0x601
+#define WM8915_DAC1_RIGHT_MIXER_ROUTING 0x602
+#define WM8915_DAC2_MIXER_VOLUMES 0x603
+#define WM8915_DAC2_LEFT_MIXER_ROUTING 0x604
+#define WM8915_DAC2_RIGHT_MIXER_ROUTING 0x605
+#define WM8915_DSP1_TX_LEFT_MIXER_ROUTING 0x606
+#define WM8915_DSP1_TX_RIGHT_MIXER_ROUTING 0x607
+#define WM8915_DSP2_TX_LEFT_MIXER_ROUTING 0x608
+#define WM8915_DSP2_TX_RIGHT_MIXER_ROUTING 0x609
+#define WM8915_DSP_TX_MIXER_SELECT 0x60A
+#define WM8915_DAC_SOFTMUTE 0x610
+#define WM8915_OVERSAMPLING 0x620
+#define WM8915_SIDETONE 0x621
+#define WM8915_GPIO_1 0x700
+#define WM8915_GPIO_2 0x701
+#define WM8915_GPIO_3 0x702
+#define WM8915_GPIO_4 0x703
+#define WM8915_GPIO_5 0x704
+#define WM8915_PULL_CONTROL_1 0x720
+#define WM8915_PULL_CONTROL_2 0x721
+#define WM8915_INTERRUPT_STATUS_1 0x730
+#define WM8915_INTERRUPT_STATUS_2 0x731
+#define WM8915_INTERRUPT_RAW_STATUS_2 0x732
+#define WM8915_INTERRUPT_STATUS_1_MASK 0x738
+#define WM8915_INTERRUPT_STATUS_2_MASK 0x739
+#define WM8915_INTERRUPT_CONTROL 0x740
+#define WM8915_LEFT_PDM_SPEAKER 0x800
+#define WM8915_RIGHT_PDM_SPEAKER 0x801
+#define WM8915_PDM_SPEAKER_MUTE_SEQUENCE 0x802
+#define WM8915_PDM_SPEAKER_VOLUME 0x803
+#define WM8915_WRITE_SEQUENCER_0 0x3000
+#define WM8915_WRITE_SEQUENCER_1 0x3001
+#define WM8915_WRITE_SEQUENCER_2 0x3002
+#define WM8915_WRITE_SEQUENCER_3 0x3003
+#define WM8915_WRITE_SEQUENCER_4 0x3004
+#define WM8915_WRITE_SEQUENCER_5 0x3005
+#define WM8915_WRITE_SEQUENCER_6 0x3006
+#define WM8915_WRITE_SEQUENCER_7 0x3007
+#define WM8915_WRITE_SEQUENCER_8 0x3008
+#define WM8915_WRITE_SEQUENCER_9 0x3009
+#define WM8915_WRITE_SEQUENCER_10 0x300A
+#define WM8915_WRITE_SEQUENCER_11 0x300B
+#define WM8915_WRITE_SEQUENCER_12 0x300C
+#define WM8915_WRITE_SEQUENCER_13 0x300D
+#define WM8915_WRITE_SEQUENCER_14 0x300E
+#define WM8915_WRITE_SEQUENCER_15 0x300F
+#define WM8915_WRITE_SEQUENCER_16 0x3010
+#define WM8915_WRITE_SEQUENCER_17 0x3011
+#define WM8915_WRITE_SEQUENCER_18 0x3012
+#define WM8915_WRITE_SEQUENCER_19 0x3013
+#define WM8915_WRITE_SEQUENCER_20 0x3014
+#define WM8915_WRITE_SEQUENCER_21 0x3015
+#define WM8915_WRITE_SEQUENCER_22 0x3016
+#define WM8915_WRITE_SEQUENCER_23 0x3017
+#define WM8915_WRITE_SEQUENCER_24 0x3018
+#define WM8915_WRITE_SEQUENCER_25 0x3019
+#define WM8915_WRITE_SEQUENCER_26 0x301A
+#define WM8915_WRITE_SEQUENCER_27 0x301B
+#define WM8915_WRITE_SEQUENCER_28 0x301C
+#define WM8915_WRITE_SEQUENCER_29 0x301D
+#define WM8915_WRITE_SEQUENCER_30 0x301E
+#define WM8915_WRITE_SEQUENCER_31 0x301F
+#define WM8915_WRITE_SEQUENCER_32 0x3020
+#define WM8915_WRITE_SEQUENCER_33 0x3021
+#define WM8915_WRITE_SEQUENCER_34 0x3022
+#define WM8915_WRITE_SEQUENCER_35 0x3023
+#define WM8915_WRITE_SEQUENCER_36 0x3024
+#define WM8915_WRITE_SEQUENCER_37 0x3025
+#define WM8915_WRITE_SEQUENCER_38 0x3026
+#define WM8915_WRITE_SEQUENCER_39 0x3027
+#define WM8915_WRITE_SEQUENCER_40 0x3028
+#define WM8915_WRITE_SEQUENCER_41 0x3029
+#define WM8915_WRITE_SEQUENCER_42 0x302A
+#define WM8915_WRITE_SEQUENCER_43 0x302B
+#define WM8915_WRITE_SEQUENCER_44 0x302C
+#define WM8915_WRITE_SEQUENCER_45 0x302D
+#define WM8915_WRITE_SEQUENCER_46 0x302E
+#define WM8915_WRITE_SEQUENCER_47 0x302F
+#define WM8915_WRITE_SEQUENCER_48 0x3030
+#define WM8915_WRITE_SEQUENCER_49 0x3031
+#define WM8915_WRITE_SEQUENCER_50 0x3032
+#define WM8915_WRITE_SEQUENCER_51 0x3033
+#define WM8915_WRITE_SEQUENCER_52 0x3034
+#define WM8915_WRITE_SEQUENCER_53 0x3035
+#define WM8915_WRITE_SEQUENCER_54 0x3036
+#define WM8915_WRITE_SEQUENCER_55 0x3037
+#define WM8915_WRITE_SEQUENCER_56 0x3038
+#define WM8915_WRITE_SEQUENCER_57 0x3039
+#define WM8915_WRITE_SEQUENCER_58 0x303A
+#define WM8915_WRITE_SEQUENCER_59 0x303B
+#define WM8915_WRITE_SEQUENCER_60 0x303C
+#define WM8915_WRITE_SEQUENCER_61 0x303D
+#define WM8915_WRITE_SEQUENCER_62 0x303E
+#define WM8915_WRITE_SEQUENCER_63 0x303F
+#define WM8915_WRITE_SEQUENCER_64 0x3040
+#define WM8915_WRITE_SEQUENCER_65 0x3041
+#define WM8915_WRITE_SEQUENCER_66 0x3042
+#define WM8915_WRITE_SEQUENCER_67 0x3043
+#define WM8915_WRITE_SEQUENCER_68 0x3044
+#define WM8915_WRITE_SEQUENCER_69 0x3045
+#define WM8915_WRITE_SEQUENCER_70 0x3046
+#define WM8915_WRITE_SEQUENCER_71 0x3047
+#define WM8915_WRITE_SEQUENCER_72 0x3048
+#define WM8915_WRITE_SEQUENCER_73 0x3049
+#define WM8915_WRITE_SEQUENCER_74 0x304A
+#define WM8915_WRITE_SEQUENCER_75 0x304B
+#define WM8915_WRITE_SEQUENCER_76 0x304C
+#define WM8915_WRITE_SEQUENCER_77 0x304D
+#define WM8915_WRITE_SEQUENCER_78 0x304E
+#define WM8915_WRITE_SEQUENCER_79 0x304F
+#define WM8915_WRITE_SEQUENCER_80 0x3050
+#define WM8915_WRITE_SEQUENCER_81 0x3051
+#define WM8915_WRITE_SEQUENCER_82 0x3052
+#define WM8915_WRITE_SEQUENCER_83 0x3053
+#define WM8915_WRITE_SEQUENCER_84 0x3054
+#define WM8915_WRITE_SEQUENCER_85 0x3055
+#define WM8915_WRITE_SEQUENCER_86 0x3056
+#define WM8915_WRITE_SEQUENCER_87 0x3057
+#define WM8915_WRITE_SEQUENCER_88 0x3058
+#define WM8915_WRITE_SEQUENCER_89 0x3059
+#define WM8915_WRITE_SEQUENCER_90 0x305A
+#define WM8915_WRITE_SEQUENCER_91 0x305B
+#define WM8915_WRITE_SEQUENCER_92 0x305C
+#define WM8915_WRITE_SEQUENCER_93 0x305D
+#define WM8915_WRITE_SEQUENCER_94 0x305E
+#define WM8915_WRITE_SEQUENCER_95 0x305F
+#define WM8915_WRITE_SEQUENCER_96 0x3060
+#define WM8915_WRITE_SEQUENCER_97 0x3061
+#define WM8915_WRITE_SEQUENCER_98 0x3062
+#define WM8915_WRITE_SEQUENCER_99 0x3063
+#define WM8915_WRITE_SEQUENCER_100 0x3064
+#define WM8915_WRITE_SEQUENCER_101 0x3065
+#define WM8915_WRITE_SEQUENCER_102 0x3066
+#define WM8915_WRITE_SEQUENCER_103 0x3067
+#define WM8915_WRITE_SEQUENCER_104 0x3068
+#define WM8915_WRITE_SEQUENCER_105 0x3069
+#define WM8915_WRITE_SEQUENCER_106 0x306A
+#define WM8915_WRITE_SEQUENCER_107 0x306B
+#define WM8915_WRITE_SEQUENCER_108 0x306C
+#define WM8915_WRITE_SEQUENCER_109 0x306D
+#define WM8915_WRITE_SEQUENCER_110 0x306E
+#define WM8915_WRITE_SEQUENCER_111 0x306F
+#define WM8915_WRITE_SEQUENCER_112 0x3070
+#define WM8915_WRITE_SEQUENCER_113 0x3071
+#define WM8915_WRITE_SEQUENCER_114 0x3072
+#define WM8915_WRITE_SEQUENCER_115 0x3073
+#define WM8915_WRITE_SEQUENCER_116 0x3074
+#define WM8915_WRITE_SEQUENCER_117 0x3075
+#define WM8915_WRITE_SEQUENCER_118 0x3076
+#define WM8915_WRITE_SEQUENCER_119 0x3077
+#define WM8915_WRITE_SEQUENCER_120 0x3078
+#define WM8915_WRITE_SEQUENCER_121 0x3079
+#define WM8915_WRITE_SEQUENCER_122 0x307A
+#define WM8915_WRITE_SEQUENCER_123 0x307B
+#define WM8915_WRITE_SEQUENCER_124 0x307C
+#define WM8915_WRITE_SEQUENCER_125 0x307D
+#define WM8915_WRITE_SEQUENCER_126 0x307E
+#define WM8915_WRITE_SEQUENCER_127 0x307F
+#define WM8915_WRITE_SEQUENCER_128 0x3080
+#define WM8915_WRITE_SEQUENCER_129 0x3081
+#define WM8915_WRITE_SEQUENCER_130 0x3082
+#define WM8915_WRITE_SEQUENCER_131 0x3083
+#define WM8915_WRITE_SEQUENCER_132 0x3084
+#define WM8915_WRITE_SEQUENCER_133 0x3085
+#define WM8915_WRITE_SEQUENCER_134 0x3086
+#define WM8915_WRITE_SEQUENCER_135 0x3087
+#define WM8915_WRITE_SEQUENCER_136 0x3088
+#define WM8915_WRITE_SEQUENCER_137 0x3089
+#define WM8915_WRITE_SEQUENCER_138 0x308A
+#define WM8915_WRITE_SEQUENCER_139 0x308B
+#define WM8915_WRITE_SEQUENCER_140 0x308C
+#define WM8915_WRITE_SEQUENCER_141 0x308D
+#define WM8915_WRITE_SEQUENCER_142 0x308E
+#define WM8915_WRITE_SEQUENCER_143 0x308F
+#define WM8915_WRITE_SEQUENCER_144 0x3090
+#define WM8915_WRITE_SEQUENCER_145 0x3091
+#define WM8915_WRITE_SEQUENCER_146 0x3092
+#define WM8915_WRITE_SEQUENCER_147 0x3093
+#define WM8915_WRITE_SEQUENCER_148 0x3094
+#define WM8915_WRITE_SEQUENCER_149 0x3095
+#define WM8915_WRITE_SEQUENCER_150 0x3096
+#define WM8915_WRITE_SEQUENCER_151 0x3097
+#define WM8915_WRITE_SEQUENCER_152 0x3098
+#define WM8915_WRITE_SEQUENCER_153 0x3099
+#define WM8915_WRITE_SEQUENCER_154 0x309A
+#define WM8915_WRITE_SEQUENCER_155 0x309B
+#define WM8915_WRITE_SEQUENCER_156 0x309C
+#define WM8915_WRITE_SEQUENCER_157 0x309D
+#define WM8915_WRITE_SEQUENCER_158 0x309E
+#define WM8915_WRITE_SEQUENCER_159 0x309F
+#define WM8915_WRITE_SEQUENCER_160 0x30A0
+#define WM8915_WRITE_SEQUENCER_161 0x30A1
+#define WM8915_WRITE_SEQUENCER_162 0x30A2
+#define WM8915_WRITE_SEQUENCER_163 0x30A3
+#define WM8915_WRITE_SEQUENCER_164 0x30A4
+#define WM8915_WRITE_SEQUENCER_165 0x30A5
+#define WM8915_WRITE_SEQUENCER_166 0x30A6
+#define WM8915_WRITE_SEQUENCER_167 0x30A7
+#define WM8915_WRITE_SEQUENCER_168 0x30A8
+#define WM8915_WRITE_SEQUENCER_169 0x30A9
+#define WM8915_WRITE_SEQUENCER_170 0x30AA
+#define WM8915_WRITE_SEQUENCER_171 0x30AB
+#define WM8915_WRITE_SEQUENCER_172 0x30AC
+#define WM8915_WRITE_SEQUENCER_173 0x30AD
+#define WM8915_WRITE_SEQUENCER_174 0x30AE
+#define WM8915_WRITE_SEQUENCER_175 0x30AF
+#define WM8915_WRITE_SEQUENCER_176 0x30B0
+#define WM8915_WRITE_SEQUENCER_177 0x30B1
+#define WM8915_WRITE_SEQUENCER_178 0x30B2
+#define WM8915_WRITE_SEQUENCER_179 0x30B3
+#define WM8915_WRITE_SEQUENCER_180 0x30B4
+#define WM8915_WRITE_SEQUENCER_181 0x30B5
+#define WM8915_WRITE_SEQUENCER_182 0x30B6
+#define WM8915_WRITE_SEQUENCER_183 0x30B7
+#define WM8915_WRITE_SEQUENCER_184 0x30B8
+#define WM8915_WRITE_SEQUENCER_185 0x30B9
+#define WM8915_WRITE_SEQUENCER_186 0x30BA
+#define WM8915_WRITE_SEQUENCER_187 0x30BB
+#define WM8915_WRITE_SEQUENCER_188 0x30BC
+#define WM8915_WRITE_SEQUENCER_189 0x30BD
+#define WM8915_WRITE_SEQUENCER_190 0x30BE
+#define WM8915_WRITE_SEQUENCER_191 0x30BF
+#define WM8915_WRITE_SEQUENCER_192 0x30C0
+#define WM8915_WRITE_SEQUENCER_193 0x30C1
+#define WM8915_WRITE_SEQUENCER_194 0x30C2
+#define WM8915_WRITE_SEQUENCER_195 0x30C3
+#define WM8915_WRITE_SEQUENCER_196 0x30C4
+#define WM8915_WRITE_SEQUENCER_197 0x30C5
+#define WM8915_WRITE_SEQUENCER_198 0x30C6
+#define WM8915_WRITE_SEQUENCER_199 0x30C7
+#define WM8915_WRITE_SEQUENCER_200 0x30C8
+#define WM8915_WRITE_SEQUENCER_201 0x30C9
+#define WM8915_WRITE_SEQUENCER_202 0x30CA
+#define WM8915_WRITE_SEQUENCER_203 0x30CB
+#define WM8915_WRITE_SEQUENCER_204 0x30CC
+#define WM8915_WRITE_SEQUENCER_205 0x30CD
+#define WM8915_WRITE_SEQUENCER_206 0x30CE
+#define WM8915_WRITE_SEQUENCER_207 0x30CF
+#define WM8915_WRITE_SEQUENCER_208 0x30D0
+#define WM8915_WRITE_SEQUENCER_209 0x30D1
+#define WM8915_WRITE_SEQUENCER_210 0x30D2
+#define WM8915_WRITE_SEQUENCER_211 0x30D3
+#define WM8915_WRITE_SEQUENCER_212 0x30D4
+#define WM8915_WRITE_SEQUENCER_213 0x30D5
+#define WM8915_WRITE_SEQUENCER_214 0x30D6
+#define WM8915_WRITE_SEQUENCER_215 0x30D7
+#define WM8915_WRITE_SEQUENCER_216 0x30D8
+#define WM8915_WRITE_SEQUENCER_217 0x30D9
+#define WM8915_WRITE_SEQUENCER_218 0x30DA
+#define WM8915_WRITE_SEQUENCER_219 0x30DB
+#define WM8915_WRITE_SEQUENCER_220 0x30DC
+#define WM8915_WRITE_SEQUENCER_221 0x30DD
+#define WM8915_WRITE_SEQUENCER_222 0x30DE
+#define WM8915_WRITE_SEQUENCER_223 0x30DF
+#define WM8915_WRITE_SEQUENCER_224 0x30E0
+#define WM8915_WRITE_SEQUENCER_225 0x30E1
+#define WM8915_WRITE_SEQUENCER_226 0x30E2
+#define WM8915_WRITE_SEQUENCER_227 0x30E3
+#define WM8915_WRITE_SEQUENCER_228 0x30E4
+#define WM8915_WRITE_SEQUENCER_229 0x30E5
+#define WM8915_WRITE_SEQUENCER_230 0x30E6
+#define WM8915_WRITE_SEQUENCER_231 0x30E7
+#define WM8915_WRITE_SEQUENCER_232 0x30E8
+#define WM8915_WRITE_SEQUENCER_233 0x30E9
+#define WM8915_WRITE_SEQUENCER_234 0x30EA
+#define WM8915_WRITE_SEQUENCER_235 0x30EB
+#define WM8915_WRITE_SEQUENCER_236 0x30EC
+#define WM8915_WRITE_SEQUENCER_237 0x30ED
+#define WM8915_WRITE_SEQUENCER_238 0x30EE
+#define WM8915_WRITE_SEQUENCER_239 0x30EF
+#define WM8915_WRITE_SEQUENCER_240 0x30F0
+#define WM8915_WRITE_SEQUENCER_241 0x30F1
+#define WM8915_WRITE_SEQUENCER_242 0x30F2
+#define WM8915_WRITE_SEQUENCER_243 0x30F3
+#define WM8915_WRITE_SEQUENCER_244 0x30F4
+#define WM8915_WRITE_SEQUENCER_245 0x30F5
+#define WM8915_WRITE_SEQUENCER_246 0x30F6
+#define WM8915_WRITE_SEQUENCER_247 0x30F7
+#define WM8915_WRITE_SEQUENCER_248 0x30F8
+#define WM8915_WRITE_SEQUENCER_249 0x30F9
+#define WM8915_WRITE_SEQUENCER_250 0x30FA
+#define WM8915_WRITE_SEQUENCER_251 0x30FB
+#define WM8915_WRITE_SEQUENCER_252 0x30FC
+#define WM8915_WRITE_SEQUENCER_253 0x30FD
+#define WM8915_WRITE_SEQUENCER_254 0x30FE
+#define WM8915_WRITE_SEQUENCER_255 0x30FF
+#define WM8915_WRITE_SEQUENCER_256 0x3100
+#define WM8915_WRITE_SEQUENCER_257 0x3101
+#define WM8915_WRITE_SEQUENCER_258 0x3102
+#define WM8915_WRITE_SEQUENCER_259 0x3103
+#define WM8915_WRITE_SEQUENCER_260 0x3104
+#define WM8915_WRITE_SEQUENCER_261 0x3105
+#define WM8915_WRITE_SEQUENCER_262 0x3106
+#define WM8915_WRITE_SEQUENCER_263 0x3107
+#define WM8915_WRITE_SEQUENCER_264 0x3108
+#define WM8915_WRITE_SEQUENCER_265 0x3109
+#define WM8915_WRITE_SEQUENCER_266 0x310A
+#define WM8915_WRITE_SEQUENCER_267 0x310B
+#define WM8915_WRITE_SEQUENCER_268 0x310C
+#define WM8915_WRITE_SEQUENCER_269 0x310D
+#define WM8915_WRITE_SEQUENCER_270 0x310E
+#define WM8915_WRITE_SEQUENCER_271 0x310F
+#define WM8915_WRITE_SEQUENCER_272 0x3110
+#define WM8915_WRITE_SEQUENCER_273 0x3111
+#define WM8915_WRITE_SEQUENCER_274 0x3112
+#define WM8915_WRITE_SEQUENCER_275 0x3113
+#define WM8915_WRITE_SEQUENCER_276 0x3114
+#define WM8915_WRITE_SEQUENCER_277 0x3115
+#define WM8915_WRITE_SEQUENCER_278 0x3116
+#define WM8915_WRITE_SEQUENCER_279 0x3117
+#define WM8915_WRITE_SEQUENCER_280 0x3118
+#define WM8915_WRITE_SEQUENCER_281 0x3119
+#define WM8915_WRITE_SEQUENCER_282 0x311A
+#define WM8915_WRITE_SEQUENCER_283 0x311B
+#define WM8915_WRITE_SEQUENCER_284 0x311C
+#define WM8915_WRITE_SEQUENCER_285 0x311D
+#define WM8915_WRITE_SEQUENCER_286 0x311E
+#define WM8915_WRITE_SEQUENCER_287 0x311F
+#define WM8915_WRITE_SEQUENCER_288 0x3120
+#define WM8915_WRITE_SEQUENCER_289 0x3121
+#define WM8915_WRITE_SEQUENCER_290 0x3122
+#define WM8915_WRITE_SEQUENCER_291 0x3123
+#define WM8915_WRITE_SEQUENCER_292 0x3124
+#define WM8915_WRITE_SEQUENCER_293 0x3125
+#define WM8915_WRITE_SEQUENCER_294 0x3126
+#define WM8915_WRITE_SEQUENCER_295 0x3127
+#define WM8915_WRITE_SEQUENCER_296 0x3128
+#define WM8915_WRITE_SEQUENCER_297 0x3129
+#define WM8915_WRITE_SEQUENCER_298 0x312A
+#define WM8915_WRITE_SEQUENCER_299 0x312B
+#define WM8915_WRITE_SEQUENCER_300 0x312C
+#define WM8915_WRITE_SEQUENCER_301 0x312D
+#define WM8915_WRITE_SEQUENCER_302 0x312E
+#define WM8915_WRITE_SEQUENCER_303 0x312F
+#define WM8915_WRITE_SEQUENCER_304 0x3130
+#define WM8915_WRITE_SEQUENCER_305 0x3131
+#define WM8915_WRITE_SEQUENCER_306 0x3132
+#define WM8915_WRITE_SEQUENCER_307 0x3133
+#define WM8915_WRITE_SEQUENCER_308 0x3134
+#define WM8915_WRITE_SEQUENCER_309 0x3135
+#define WM8915_WRITE_SEQUENCER_310 0x3136
+#define WM8915_WRITE_SEQUENCER_311 0x3137
+#define WM8915_WRITE_SEQUENCER_312 0x3138
+#define WM8915_WRITE_SEQUENCER_313 0x3139
+#define WM8915_WRITE_SEQUENCER_314 0x313A
+#define WM8915_WRITE_SEQUENCER_315 0x313B
+#define WM8915_WRITE_SEQUENCER_316 0x313C
+#define WM8915_WRITE_SEQUENCER_317 0x313D
+#define WM8915_WRITE_SEQUENCER_318 0x313E
+#define WM8915_WRITE_SEQUENCER_319 0x313F
+#define WM8915_WRITE_SEQUENCER_320 0x3140
+#define WM8915_WRITE_SEQUENCER_321 0x3141
+#define WM8915_WRITE_SEQUENCER_322 0x3142
+#define WM8915_WRITE_SEQUENCER_323 0x3143
+#define WM8915_WRITE_SEQUENCER_324 0x3144
+#define WM8915_WRITE_SEQUENCER_325 0x3145
+#define WM8915_WRITE_SEQUENCER_326 0x3146
+#define WM8915_WRITE_SEQUENCER_327 0x3147
+#define WM8915_WRITE_SEQUENCER_328 0x3148
+#define WM8915_WRITE_SEQUENCER_329 0x3149
+#define WM8915_WRITE_SEQUENCER_330 0x314A
+#define WM8915_WRITE_SEQUENCER_331 0x314B
+#define WM8915_WRITE_SEQUENCER_332 0x314C
+#define WM8915_WRITE_SEQUENCER_333 0x314D
+#define WM8915_WRITE_SEQUENCER_334 0x314E
+#define WM8915_WRITE_SEQUENCER_335 0x314F
+#define WM8915_WRITE_SEQUENCER_336 0x3150
+#define WM8915_WRITE_SEQUENCER_337 0x3151
+#define WM8915_WRITE_SEQUENCER_338 0x3152
+#define WM8915_WRITE_SEQUENCER_339 0x3153
+#define WM8915_WRITE_SEQUENCER_340 0x3154
+#define WM8915_WRITE_SEQUENCER_341 0x3155
+#define WM8915_WRITE_SEQUENCER_342 0x3156
+#define WM8915_WRITE_SEQUENCER_343 0x3157
+#define WM8915_WRITE_SEQUENCER_344 0x3158
+#define WM8915_WRITE_SEQUENCER_345 0x3159
+#define WM8915_WRITE_SEQUENCER_346 0x315A
+#define WM8915_WRITE_SEQUENCER_347 0x315B
+#define WM8915_WRITE_SEQUENCER_348 0x315C
+#define WM8915_WRITE_SEQUENCER_349 0x315D
+#define WM8915_WRITE_SEQUENCER_350 0x315E
+#define WM8915_WRITE_SEQUENCER_351 0x315F
+#define WM8915_WRITE_SEQUENCER_352 0x3160
+#define WM8915_WRITE_SEQUENCER_353 0x3161
+#define WM8915_WRITE_SEQUENCER_354 0x3162
+#define WM8915_WRITE_SEQUENCER_355 0x3163
+#define WM8915_WRITE_SEQUENCER_356 0x3164
+#define WM8915_WRITE_SEQUENCER_357 0x3165
+#define WM8915_WRITE_SEQUENCER_358 0x3166
+#define WM8915_WRITE_SEQUENCER_359 0x3167
+#define WM8915_WRITE_SEQUENCER_360 0x3168
+#define WM8915_WRITE_SEQUENCER_361 0x3169
+#define WM8915_WRITE_SEQUENCER_362 0x316A
+#define WM8915_WRITE_SEQUENCER_363 0x316B
+#define WM8915_WRITE_SEQUENCER_364 0x316C
+#define WM8915_WRITE_SEQUENCER_365 0x316D
+#define WM8915_WRITE_SEQUENCER_366 0x316E
+#define WM8915_WRITE_SEQUENCER_367 0x316F
+#define WM8915_WRITE_SEQUENCER_368 0x3170
+#define WM8915_WRITE_SEQUENCER_369 0x3171
+#define WM8915_WRITE_SEQUENCER_370 0x3172
+#define WM8915_WRITE_SEQUENCER_371 0x3173
+#define WM8915_WRITE_SEQUENCER_372 0x3174
+#define WM8915_WRITE_SEQUENCER_373 0x3175
+#define WM8915_WRITE_SEQUENCER_374 0x3176
+#define WM8915_WRITE_SEQUENCER_375 0x3177
+#define WM8915_WRITE_SEQUENCER_376 0x3178
+#define WM8915_WRITE_SEQUENCER_377 0x3179
+#define WM8915_WRITE_SEQUENCER_378 0x317A
+#define WM8915_WRITE_SEQUENCER_379 0x317B
+#define WM8915_WRITE_SEQUENCER_380 0x317C
+#define WM8915_WRITE_SEQUENCER_381 0x317D
+#define WM8915_WRITE_SEQUENCER_382 0x317E
+#define WM8915_WRITE_SEQUENCER_383 0x317F
+#define WM8915_WRITE_SEQUENCER_384 0x3180
+#define WM8915_WRITE_SEQUENCER_385 0x3181
+#define WM8915_WRITE_SEQUENCER_386 0x3182
+#define WM8915_WRITE_SEQUENCER_387 0x3183
+#define WM8915_WRITE_SEQUENCER_388 0x3184
+#define WM8915_WRITE_SEQUENCER_389 0x3185
+#define WM8915_WRITE_SEQUENCER_390 0x3186
+#define WM8915_WRITE_SEQUENCER_391 0x3187
+#define WM8915_WRITE_SEQUENCER_392 0x3188
+#define WM8915_WRITE_SEQUENCER_393 0x3189
+#define WM8915_WRITE_SEQUENCER_394 0x318A
+#define WM8915_WRITE_SEQUENCER_395 0x318B
+#define WM8915_WRITE_SEQUENCER_396 0x318C
+#define WM8915_WRITE_SEQUENCER_397 0x318D
+#define WM8915_WRITE_SEQUENCER_398 0x318E
+#define WM8915_WRITE_SEQUENCER_399 0x318F
+#define WM8915_WRITE_SEQUENCER_400 0x3190
+#define WM8915_WRITE_SEQUENCER_401 0x3191
+#define WM8915_WRITE_SEQUENCER_402 0x3192
+#define WM8915_WRITE_SEQUENCER_403 0x3193
+#define WM8915_WRITE_SEQUENCER_404 0x3194
+#define WM8915_WRITE_SEQUENCER_405 0x3195
+#define WM8915_WRITE_SEQUENCER_406 0x3196
+#define WM8915_WRITE_SEQUENCER_407 0x3197
+#define WM8915_WRITE_SEQUENCER_408 0x3198
+#define WM8915_WRITE_SEQUENCER_409 0x3199
+#define WM8915_WRITE_SEQUENCER_410 0x319A
+#define WM8915_WRITE_SEQUENCER_411 0x319B
+#define WM8915_WRITE_SEQUENCER_412 0x319C
+#define WM8915_WRITE_SEQUENCER_413 0x319D
+#define WM8915_WRITE_SEQUENCER_414 0x319E
+#define WM8915_WRITE_SEQUENCER_415 0x319F
+#define WM8915_WRITE_SEQUENCER_416 0x31A0
+#define WM8915_WRITE_SEQUENCER_417 0x31A1
+#define WM8915_WRITE_SEQUENCER_418 0x31A2
+#define WM8915_WRITE_SEQUENCER_419 0x31A3
+#define WM8915_WRITE_SEQUENCER_420 0x31A4
+#define WM8915_WRITE_SEQUENCER_421 0x31A5
+#define WM8915_WRITE_SEQUENCER_422 0x31A6
+#define WM8915_WRITE_SEQUENCER_423 0x31A7
+#define WM8915_WRITE_SEQUENCER_424 0x31A8
+#define WM8915_WRITE_SEQUENCER_425 0x31A9
+#define WM8915_WRITE_SEQUENCER_426 0x31AA
+#define WM8915_WRITE_SEQUENCER_427 0x31AB
+#define WM8915_WRITE_SEQUENCER_428 0x31AC
+#define WM8915_WRITE_SEQUENCER_429 0x31AD
+#define WM8915_WRITE_SEQUENCER_430 0x31AE
+#define WM8915_WRITE_SEQUENCER_431 0x31AF
+#define WM8915_WRITE_SEQUENCER_432 0x31B0
+#define WM8915_WRITE_SEQUENCER_433 0x31B1
+#define WM8915_WRITE_SEQUENCER_434 0x31B2
+#define WM8915_WRITE_SEQUENCER_435 0x31B3
+#define WM8915_WRITE_SEQUENCER_436 0x31B4
+#define WM8915_WRITE_SEQUENCER_437 0x31B5
+#define WM8915_WRITE_SEQUENCER_438 0x31B6
+#define WM8915_WRITE_SEQUENCER_439 0x31B7
+#define WM8915_WRITE_SEQUENCER_440 0x31B8
+#define WM8915_WRITE_SEQUENCER_441 0x31B9
+#define WM8915_WRITE_SEQUENCER_442 0x31BA
+#define WM8915_WRITE_SEQUENCER_443 0x31BB
+#define WM8915_WRITE_SEQUENCER_444 0x31BC
+#define WM8915_WRITE_SEQUENCER_445 0x31BD
+#define WM8915_WRITE_SEQUENCER_446 0x31BE
+#define WM8915_WRITE_SEQUENCER_447 0x31BF
+#define WM8915_WRITE_SEQUENCER_448 0x31C0
+#define WM8915_WRITE_SEQUENCER_449 0x31C1
+#define WM8915_WRITE_SEQUENCER_450 0x31C2
+#define WM8915_WRITE_SEQUENCER_451 0x31C3
+#define WM8915_WRITE_SEQUENCER_452 0x31C4
+#define WM8915_WRITE_SEQUENCER_453 0x31C5
+#define WM8915_WRITE_SEQUENCER_454 0x31C6
+#define WM8915_WRITE_SEQUENCER_455 0x31C7
+#define WM8915_WRITE_SEQUENCER_456 0x31C8
+#define WM8915_WRITE_SEQUENCER_457 0x31C9
+#define WM8915_WRITE_SEQUENCER_458 0x31CA
+#define WM8915_WRITE_SEQUENCER_459 0x31CB
+#define WM8915_WRITE_SEQUENCER_460 0x31CC
+#define WM8915_WRITE_SEQUENCER_461 0x31CD
+#define WM8915_WRITE_SEQUENCER_462 0x31CE
+#define WM8915_WRITE_SEQUENCER_463 0x31CF
+#define WM8915_WRITE_SEQUENCER_464 0x31D0
+#define WM8915_WRITE_SEQUENCER_465 0x31D1
+#define WM8915_WRITE_SEQUENCER_466 0x31D2
+#define WM8915_WRITE_SEQUENCER_467 0x31D3
+#define WM8915_WRITE_SEQUENCER_468 0x31D4
+#define WM8915_WRITE_SEQUENCER_469 0x31D5
+#define WM8915_WRITE_SEQUENCER_470 0x31D6
+#define WM8915_WRITE_SEQUENCER_471 0x31D7
+#define WM8915_WRITE_SEQUENCER_472 0x31D8
+#define WM8915_WRITE_SEQUENCER_473 0x31D9
+#define WM8915_WRITE_SEQUENCER_474 0x31DA
+#define WM8915_WRITE_SEQUENCER_475 0x31DB
+#define WM8915_WRITE_SEQUENCER_476 0x31DC
+#define WM8915_WRITE_SEQUENCER_477 0x31DD
+#define WM8915_WRITE_SEQUENCER_478 0x31DE
+#define WM8915_WRITE_SEQUENCER_479 0x31DF
+#define WM8915_WRITE_SEQUENCER_480 0x31E0
+#define WM8915_WRITE_SEQUENCER_481 0x31E1
+#define WM8915_WRITE_SEQUENCER_482 0x31E2
+#define WM8915_WRITE_SEQUENCER_483 0x31E3
+#define WM8915_WRITE_SEQUENCER_484 0x31E4
+#define WM8915_WRITE_SEQUENCER_485 0x31E5
+#define WM8915_WRITE_SEQUENCER_486 0x31E6
+#define WM8915_WRITE_SEQUENCER_487 0x31E7
+#define WM8915_WRITE_SEQUENCER_488 0x31E8
+#define WM8915_WRITE_SEQUENCER_489 0x31E9
+#define WM8915_WRITE_SEQUENCER_490 0x31EA
+#define WM8915_WRITE_SEQUENCER_491 0x31EB
+#define WM8915_WRITE_SEQUENCER_492 0x31EC
+#define WM8915_WRITE_SEQUENCER_493 0x31ED
+#define WM8915_WRITE_SEQUENCER_494 0x31EE
+#define WM8915_WRITE_SEQUENCER_495 0x31EF
+#define WM8915_WRITE_SEQUENCER_496 0x31F0
+#define WM8915_WRITE_SEQUENCER_497 0x31F1
+#define WM8915_WRITE_SEQUENCER_498 0x31F2
+#define WM8915_WRITE_SEQUENCER_499 0x31F3
+#define WM8915_WRITE_SEQUENCER_500 0x31F4
+#define WM8915_WRITE_SEQUENCER_501 0x31F5
+#define WM8915_WRITE_SEQUENCER_502 0x31F6
+#define WM8915_WRITE_SEQUENCER_503 0x31F7
+#define WM8915_WRITE_SEQUENCER_504 0x31F8
+#define WM8915_WRITE_SEQUENCER_505 0x31F9
+#define WM8915_WRITE_SEQUENCER_506 0x31FA
+#define WM8915_WRITE_SEQUENCER_507 0x31FB
+#define WM8915_WRITE_SEQUENCER_508 0x31FC
+#define WM8915_WRITE_SEQUENCER_509 0x31FD
+#define WM8915_WRITE_SEQUENCER_510 0x31FE
+#define WM8915_WRITE_SEQUENCER_511 0x31FF
+
+#define WM8915_REGISTER_COUNT 706
+#define WM8915_MAX_REGISTER 0x31FF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8915_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8915_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8915_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8915_MICB2_ENA 0x0200 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8915_MICB1_ENA 0x0100 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8915_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
+#define WM8915_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8915_BG_ENA 0x0001 /* BG_ENA */
+#define WM8915_BG_ENA_MASK 0x0001 /* BG_ENA */
+#define WM8915_BG_ENA_SHIFT 0 /* BG_ENA */
+#define WM8915_BG_ENA_WIDTH 1 /* BG_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8915_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8915_INL_ENA 0x0020 /* INL_ENA */
+#define WM8915_INL_ENA_MASK 0x0020 /* INL_ENA */
+#define WM8915_INL_ENA_SHIFT 5 /* INL_ENA */
+#define WM8915_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8915_INR_ENA 0x0010 /* INR_ENA */
+#define WM8915_INR_ENA_MASK 0x0010 /* INR_ENA */
+#define WM8915_INR_ENA_SHIFT 4 /* INR_ENA */
+#define WM8915_INR_ENA_WIDTH 1 /* INR_ENA */
+#define WM8915_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8915_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */
+#define WM8915_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */
+#define WM8915_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
+#define WM8915_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
+#define WM8915_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
+#define WM8915_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
+#define WM8915_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8915_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (4)
+ */
+#define WM8915_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */
+
+/*
+ * R5 (0x05) - Power Management (5)
+ */
+#define WM8915_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */
+#define WM8915_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */
+#define WM8915_DAC2L_ENA 0x0008 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
+#define WM8915_DAC2R_ENA 0x0004 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
+#define WM8915_DAC1L_ENA 0x0002 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
+#define WM8915_DAC1R_ENA 0x0001 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
+
+/*
+ * R6 (0x06) - Power Management (6)
+ */
+#define WM8915_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */
+
+/*
+ * R7 (0x07) - Power Management (7)
+ */
+#define WM8915_DMIC2_FN 0x0200 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_SHIFT 9 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_WIDTH 1 /* DMIC2_FN */
+#define WM8915_DMIC1_FN 0x0100 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_SHIFT 8 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_WIDTH 1 /* DMIC1_FN */
+#define WM8915_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8915_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8915_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */
+
+/*
+ * R8 (0x08) - Power Management (8)
+ */
+#define WM8915_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */
+#define WM8915_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */
+#define WM8915_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */
+#define WM8915_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */
+#define WM8915_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */
+
+/*
+ * R16 (0x10) - Left Line Input Volume
+ */
+#define WM8915_IN1_VU 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8915_IN1L_ZC 0x0020 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8915_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8915_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8915_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R17 (0x11) - Right Line Input Volume
+ */
+#define WM8915_IN1_VU 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8915_IN1R_ZC 0x0020 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8915_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8915_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8915_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R18 (0x12) - Line Input Control
+ */
+#define WM8915_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */
+#define WM8915_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */
+#define WM8915_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */
+#define WM8915_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */
+#define WM8915_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */
+#define WM8915_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */
+
+/*
+ * R21 (0x15) - DAC1 HPOUT1 Volume
+ */
+#define WM8915_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8915_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8915_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */
+
+/*
+ * R22 (0x16) - DAC2 HPOUT2 Volume
+ */
+#define WM8915_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8915_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8915_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */
+
+/*
+ * R24 (0x18) - DAC1 Left Volume
+ */
+#define WM8915_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
+#define WM8915_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
+#define WM8915_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
+
+/*
+ * R25 (0x19) - DAC1 Right Volume
+ */
+#define WM8915_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
+#define WM8915_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
+#define WM8915_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
+
+/*
+ * R26 (0x1A) - DAC2 Left Volume
+ */
+#define WM8915_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
+#define WM8915_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
+#define WM8915_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
+
+/*
+ * R27 (0x1B) - DAC2 Right Volume
+ */
+#define WM8915_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
+#define WM8915_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
+#define WM8915_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
+
+/*
+ * R28 (0x1C) - Output1 Left Volume
+ */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */
+#define WM8915_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */
+#define WM8915_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */
+
+/*
+ * R29 (0x1D) - Output1 Right Volume
+ */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */
+#define WM8915_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */
+#define WM8915_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */
+
+/*
+ * R30 (0x1E) - Output2 Left Volume
+ */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */
+#define WM8915_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */
+#define WM8915_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */
+
+/*
+ * R31 (0x1F) - Output2 Right Volume
+ */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */
+#define WM8915_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */
+#define WM8915_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */
+
+/*
+ * R32 (0x20) - MICBIAS (1)
+ */
+#define WM8915_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM8915_MICB1_MODE 0x0010 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM8915_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_DISCH 0x0001 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+
+/*
+ * R33 (0x21) - MICBIAS (2)
+ */
+#define WM8915_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM8915_MICB2_MODE 0x0010 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM8915_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_DISCH 0x0001 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+
+/*
+ * R40 (0x28) - LDO 1
+ */
+#define WM8915_LDO1_MODE 0x0020 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
+#define WM8915_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_DISCH 0x0001 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
+
+/*
+ * R41 (0x29) - LDO 2
+ */
+#define WM8915_LDO2_MODE 0x0020 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
+#define WM8915_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_DISCH 0x0001 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
+
+/*
+ * R48 (0x30) - Accessory Detect Mode 1
+ */
+#define WM8915_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
+#define WM8915_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
+#define WM8915_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
+
+/*
+ * R49 (0x31) - Accessory Detect Mode 2
+ */
+#define WM8915_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */
+#define WM8915_MICD_SRC 0x0002 /* MICD_SRC */
+#define WM8915_MICD_SRC_MASK 0x0002 /* MICD_SRC */
+#define WM8915_MICD_SRC_SHIFT 1 /* MICD_SRC */
+#define WM8915_MICD_SRC_WIDTH 1 /* MICD_SRC */
+#define WM8915_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */
+
+/*
+ * R52 (0x34) - Headphone Detect 1
+ */
+#define WM8915_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
+#define WM8915_HP_POLL 0x0001 /* HP_POLL */
+#define WM8915_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM8915_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM8915_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R53 (0x35) - Headphone Detect 2
+ */
+#define WM8915_HP_DONE 0x0080 /* HP_DONE */
+#define WM8915_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM8915_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM8915_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM8915_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM8915_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM8915_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R56 (0x38) - Mic Detect 1
+ */
+#define WM8915_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_DBTIME 0x0002 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
+#define WM8915_MICD_ENA 0x0001 /* MICD_ENA */
+#define WM8915_MICD_ENA_MASK 0x0001 /* MICD_ENA */
+#define WM8915_MICD_ENA_SHIFT 0 /* MICD_ENA */
+#define WM8915_MICD_ENA_WIDTH 1 /* MICD_ENA */
+
+/*
+ * R57 (0x39) - Mic Detect 2
+ */
+#define WM8915_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */
+#define WM8915_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */
+#define WM8915_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R58 (0x3A) - Mic Detect 3
+ */
+#define WM8915_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
+#define WM8915_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
+#define WM8915_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */
+#define WM8915_MICD_VALID 0x0002 /* MICD_VALID */
+#define WM8915_MICD_VALID_MASK 0x0002 /* MICD_VALID */
+#define WM8915_MICD_VALID_SHIFT 1 /* MICD_VALID */
+#define WM8915_MICD_VALID_WIDTH 1 /* MICD_VALID */
+#define WM8915_MICD_STS 0x0001 /* MICD_STS */
+#define WM8915_MICD_STS_MASK 0x0001 /* MICD_STS */
+#define WM8915_MICD_STS_SHIFT 0 /* MICD_STS */
+#define WM8915_MICD_STS_WIDTH 1 /* MICD_STS */
+
+/*
+ * R64 (0x40) - Charge Pump (1)
+ */
+#define WM8915_CP_ENA 0x8000 /* CP_ENA */
+#define WM8915_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8915_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8915_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R65 (0x41) - Charge Pump (2)
+ */
+#define WM8915_CP_DISCH 0x8000 /* CP_DISCH */
+#define WM8915_CP_DISCH_MASK 0x8000 /* CP_DISCH */
+#define WM8915_CP_DISCH_SHIFT 15 /* CP_DISCH */
+#define WM8915_CP_DISCH_WIDTH 1 /* CP_DISCH */
+
+/*
+ * R80 (0x50) - DC Servo (1)
+ */
+#define WM8915_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R81 (0x51) - DC Servo (2)
+ */
+#define WM8915_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R82 (0x52) - DC Servo (3)
+ */
+#define WM8915_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8915_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8915_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R84 (0x54) - DC Servo (5)
+ */
+#define WM8915_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8915_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8915_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R85 (0x55) - DC Servo (6)
+ */
+#define WM8915_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R86 (0x56) - DC Servo (7)
+ */
+#define WM8915_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R87 (0x57) - DC Servo Readback 0
+ */
+#define WM8915_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8915_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8915_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R96 (0x60) - Analogue HP (1)
+ */
+#define WM8915_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R97 (0x61) - Analogue HP (2)
+ */
+#define WM8915_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
+
+/*
+ * R256 (0x100) - Chip Revision
+ */
+#define WM8915_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8915_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8915_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R257 (0x101) - Control Interface (1)
+ */
+#define WM8915_AUTO_INC 0x0004 /* AUTO_INC */
+#define WM8915_AUTO_INC_MASK 0x0004 /* AUTO_INC */
+#define WM8915_AUTO_INC_SHIFT 2 /* AUTO_INC */
+#define WM8915_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R272 (0x110) - Write Sequencer Ctrl (1)
+ */
+#define WM8915_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8915_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8915_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8915_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8915_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8915_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8915_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8915_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8915_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R273 (0x111) - Write Sequencer Ctrl (2)
+ */
+#define WM8915_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+#define WM8915_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8915_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8915_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
+
+/*
+ * R512 (0x200) - AIF Clocking (1)
+ */
+#define WM8915_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_INV 0x0004 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */
+#define WM8915_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+
+/*
+ * R513 (0x201) - AIF Clocking (2)
+ */
+#define WM8915_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */
+#define WM8915_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */
+#define WM8915_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */
+
+/*
+ * R520 (0x208) - Clocking (1)
+ */
+#define WM8915_LFCLK_ENA 0x0020 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
+#define WM8915_TOCLK_ENA 0x0010 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8915_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
+
+/*
+ * R521 (0x209) - Clocking (2)
+ */
+#define WM8915_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
+#define WM8915_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
+#define WM8915_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
+#define WM8915_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
+#define WM8915_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
+#define WM8915_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
+#define WM8915_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
+#define WM8915_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
+#define WM8915_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
+
+/*
+ * R528 (0x210) - AIF Rate
+ */
+#define WM8915_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */
+
+/*
+ * R544 (0x220) - FLL Control (1)
+ */
+#define WM8915_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8915_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8915_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8915_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8915_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R545 (0x221) - FLL Control (2)
+ */
+#define WM8915_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8915_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8915_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R546 (0x222) - FLL Control (3)
+ */
+#define WM8915_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM8915_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM8915_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R547 (0x223) - FLL Control (4)
+ */
+#define WM8915_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8915_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8915_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8915_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */
+#define WM8915_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */
+#define WM8915_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */
+
+/*
+ * R548 (0x224) - FLL Control (5)
+ */
+#define WM8915_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8915_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */
+#define WM8915_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8915_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8915_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */
+
+/*
+ * R549 (0x225) - FLL Control (6)
+ */
+#define WM8915_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */
+
+/*
+ * R550 (0x226) - FLL EFS 1
+ */
+#define WM8915_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM8915_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM8915_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R551 (0x227) - FLL EFS 2
+ */
+#define WM8915_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
+
+/*
+ * R768 (0x300) - AIF1 Control
+ */
+#define WM8915_AIF1_TRI 0x0004 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_SHIFT 2 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM8915_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */
+#define WM8915_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */
+#define WM8915_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */
+
+/*
+ * R769 (0x301) - AIF1 BCLK
+ */
+#define WM8915_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
+#define WM8915_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
+#define WM8915_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
+
+/*
+ * R770 (0x302) - AIF1 TX LRCLK(1)
+ */
+#define WM8915_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */
+#define WM8915_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */
+#define WM8915_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */
+
+/*
+ * R771 (0x303) - AIF1 TX LRCLK(2)
+ */
+#define WM8915_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R772 (0x304) - AIF1 RX LRCLK(1)
+ */
+#define WM8915_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */
+#define WM8915_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */
+#define WM8915_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */
+
+/*
+ * R773 (0x305) - AIF1 RX LRCLK(2)
+ */
+#define WM8915_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R774 (0x306) - AIF1TX Data Configuration (1)
+ */
+#define WM8915_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R775 (0x307) - AIF1TX Data Configuration (2)
+ */
+#define WM8915_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+
+/*
+ * R776 (0x308) - AIF1RX Data Configuration
+ */
+#define WM8915_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R777 (0x309) - AIF1TX Channel 0 Configuration
+ */
+#define WM8915_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R778 (0x30A) - AIF1TX Channel 1 Configuration
+ */
+#define WM8915_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R779 (0x30B) - AIF1TX Channel 2 Configuration
+ */
+#define WM8915_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R780 (0x30C) - AIF1TX Channel 3 Configuration
+ */
+#define WM8915_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R781 (0x30D) - AIF1TX Channel 4 Configuration
+ */
+#define WM8915_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R782 (0x30E) - AIF1TX Channel 5 Configuration
+ */
+#define WM8915_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R783 (0x30F) - AIF1RX Channel 0 Configuration
+ */
+#define WM8915_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R784 (0x310) - AIF1RX Channel 1 Configuration
+ */
+#define WM8915_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R785 (0x311) - AIF1RX Channel 2 Configuration
+ */
+#define WM8915_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R786 (0x312) - AIF1RX Channel 3 Configuration
+ */
+#define WM8915_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R787 (0x313) - AIF1RX Channel 4 Configuration
+ */
+#define WM8915_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R788 (0x314) - AIF1RX Channel 5 Configuration
+ */
+#define WM8915_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R789 (0x315) - AIF1RX Mono Configuration
+ */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */
+
+/*
+ * R794 (0x31A) - AIF1TX Test
+ */
+#define WM8915_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */
+
+/*
+ * R800 (0x320) - AIF2 Control
+ */
+#define WM8915_AIF2_TRI 0x0004 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_SHIFT 2 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM8915_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */
+#define WM8915_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */
+#define WM8915_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */
+
+/*
+ * R801 (0x321) - AIF2 BCLK
+ */
+#define WM8915_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */
+#define WM8915_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */
+#define WM8915_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */
+
+/*
+ * R802 (0x322) - AIF2 TX LRCLK(1)
+ */
+#define WM8915_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */
+#define WM8915_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */
+#define WM8915_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */
+
+/*
+ * R803 (0x323) - AIF2 TX LRCLK(2)
+ */
+#define WM8915_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R804 (0x324) - AIF2 RX LRCLK(1)
+ */
+#define WM8915_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */
+#define WM8915_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */
+#define WM8915_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */
+
+/*
+ * R805 (0x325) - AIF2 RX LRCLK(2)
+ */
+#define WM8915_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R806 (0x326) - AIF2TX Data Configuration (1)
+ */
+#define WM8915_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R807 (0x327) - AIF2TX Data Configuration (2)
+ */
+#define WM8915_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
+
+/*
+ * R808 (0x328) - AIF2RX Data Configuration
+ */
+#define WM8915_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R809 (0x329) - AIF2TX Channel 0 Configuration
+ */
+#define WM8915_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R810 (0x32A) - AIF2TX Channel 1 Configuration
+ */
+#define WM8915_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R811 (0x32B) - AIF2RX Channel 0 Configuration
+ */
+#define WM8915_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R812 (0x32C) - AIF2RX Channel 1 Configuration
+ */
+#define WM8915_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R813 (0x32D) - AIF2RX Mono Configuration
+ */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */
+
+/*
+ * R815 (0x32F) - AIF2TX Test
+ */
+#define WM8915_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */
+
+/*
+ * R1024 (0x400) - DSP1 TX Left Volume
+ */
+#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8915_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */
+#define WM8915_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */
+#define WM8915_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - DSP1 TX Right Volume
+ */
+#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8915_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */
+#define WM8915_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */
+#define WM8915_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */
+
+/*
+ * R1026 (0x402) - DSP1 RX Left Volume
+ */
+#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8915_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */
+#define WM8915_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */
+#define WM8915_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */
+
+/*
+ * R1027 (0x403) - DSP1 RX Right Volume
+ */
+#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8915_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */
+#define WM8915_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */
+#define WM8915_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */
+
+/*
+ * R1040 (0x410) - DSP1 TX Filters
+ */
+#define WM8915_DSP1TX_NF 0x2000 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */
+#define WM8915_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */
+#define WM8915_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8915_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8915_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */
+
+/*
+ * R1056 (0x420) - DSP1 RX Filters (1)
+ */
+#define WM8915_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */
+
+/*
+ * R1057 (0x421) - DSP1 RX Filters (2)
+ */
+#define WM8915_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */
+
+/*
+ * R1088 (0x440) - DSP1 DRC (1)
+ */
+#define WM8915_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */
+
+/*
+ * R1089 (0x441) - DSP1 DRC (2)
+ */
+#define WM8915_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1090 (0x442) - DSP1 DRC (3)
+ */
+#define WM8915_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8915_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8915_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */
+
+/*
+ * R1091 (0x443) - DSP1 DRC (4)
+ */
+#define WM8915_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1092 (0x444) - DSP1 DRC (5)
+ */
+#define WM8915_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1152 (0x480) - DSP1 RX EQ Gains (1)
+ */
+#define WM8915_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */
+
+/*
+ * R1153 (0x481) - DSP1 RX EQ Gains (2)
+ */
+#define WM8915_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1154 (0x482) - DSP1 RX EQ Band 1 A
+ */
+#define WM8915_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1155 (0x483) - DSP1 RX EQ Band 1 B
+ */
+#define WM8915_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1156 (0x484) - DSP1 RX EQ Band 1 PG
+ */
+#define WM8915_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1157 (0x485) - DSP1 RX EQ Band 2 A
+ */
+#define WM8915_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1158 (0x486) - DSP1 RX EQ Band 2 B
+ */
+#define WM8915_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1159 (0x487) - DSP1 RX EQ Band 2 C
+ */
+#define WM8915_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1160 (0x488) - DSP1 RX EQ Band 2 PG
+ */
+#define WM8915_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1161 (0x489) - DSP1 RX EQ Band 3 A
+ */
+#define WM8915_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1162 (0x48A) - DSP1 RX EQ Band 3 B
+ */
+#define WM8915_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1163 (0x48B) - DSP1 RX EQ Band 3 C
+ */
+#define WM8915_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1164 (0x48C) - DSP1 RX EQ Band 3 PG
+ */
+#define WM8915_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1165 (0x48D) - DSP1 RX EQ Band 4 A
+ */
+#define WM8915_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1166 (0x48E) - DSP1 RX EQ Band 4 B
+ */
+#define WM8915_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1167 (0x48F) - DSP1 RX EQ Band 4 C
+ */
+#define WM8915_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1168 (0x490) - DSP1 RX EQ Band 4 PG
+ */
+#define WM8915_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1169 (0x491) - DSP1 RX EQ Band 5 A
+ */
+#define WM8915_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1170 (0x492) - DSP1 RX EQ Band 5 B
+ */
+#define WM8915_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1171 (0x493) - DSP1 RX EQ Band 5 PG
+ */
+#define WM8915_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1280 (0x500) - DSP2 TX Left Volume
+ */
+#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8915_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */
+#define WM8915_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */
+#define WM8915_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */
+
+/*
+ * R1281 (0x501) - DSP2 TX Right Volume
+ */
+#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8915_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */
+#define WM8915_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */
+#define WM8915_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */
+
+/*
+ * R1282 (0x502) - DSP2 RX Left Volume
+ */
+#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8915_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */
+#define WM8915_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */
+#define WM8915_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */
+
+/*
+ * R1283 (0x503) - DSP2 RX Right Volume
+ */
+#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8915_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */
+#define WM8915_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */
+#define WM8915_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */
+
+/*
+ * R1296 (0x510) - DSP2 TX Filters
+ */
+#define WM8915_DSP2TX_NF 0x2000 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */
+#define WM8915_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */
+#define WM8915_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8915_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8915_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */
+
+/*
+ * R1312 (0x520) - DSP2 RX Filters (1)
+ */
+#define WM8915_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */
+
+/*
+ * R1313 (0x521) - DSP2 RX Filters (2)
+ */
+#define WM8915_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */
+
+/*
+ * R1344 (0x540) - DSP2 DRC (1)
+ */
+#define WM8915_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */
+
+/*
+ * R1345 (0x541) - DSP2 DRC (2)
+ */
+#define WM8915_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1346 (0x542) - DSP2 DRC (3)
+ */
+#define WM8915_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8915_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8915_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */
+
+/*
+ * R1347 (0x543) - DSP2 DRC (4)
+ */
+#define WM8915_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1348 (0x544) - DSP2 DRC (5)
+ */
+#define WM8915_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1408 (0x580) - DSP2 RX EQ Gains (1)
+ */
+#define WM8915_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */
+
+/*
+ * R1409 (0x581) - DSP2 RX EQ Gains (2)
+ */
+#define WM8915_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1410 (0x582) - DSP2 RX EQ Band 1 A
+ */
+#define WM8915_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1411 (0x583) - DSP2 RX EQ Band 1 B
+ */
+#define WM8915_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1412 (0x584) - DSP2 RX EQ Band 1 PG
+ */
+#define WM8915_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1413 (0x585) - DSP2 RX EQ Band 2 A
+ */
+#define WM8915_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1414 (0x586) - DSP2 RX EQ Band 2 B
+ */
+#define WM8915_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1415 (0x587) - DSP2 RX EQ Band 2 C
+ */
+#define WM8915_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1416 (0x588) - DSP2 RX EQ Band 2 PG
+ */
+#define WM8915_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1417 (0x589) - DSP2 RX EQ Band 3 A
+ */
+#define WM8915_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1418 (0x58A) - DSP2 RX EQ Band 3 B
+ */
+#define WM8915_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1419 (0x58B) - DSP2 RX EQ Band 3 C
+ */
+#define WM8915_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1420 (0x58C) - DSP2 RX EQ Band 3 PG
+ */
+#define WM8915_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1421 (0x58D) - DSP2 RX EQ Band 4 A
+ */
+#define WM8915_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1422 (0x58E) - DSP2 RX EQ Band 4 B
+ */
+#define WM8915_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1423 (0x58F) - DSP2 RX EQ Band 4 C
+ */
+#define WM8915_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1424 (0x590) - DSP2 RX EQ Band 4 PG
+ */
+#define WM8915_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1425 (0x591) - DSP2 RX EQ Band 5 A
+ */
+#define WM8915_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1426 (0x592) - DSP2 RX EQ Band 5 B
+ */
+#define WM8915_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1427 (0x593) - DSP2 RX EQ Band 5 PG
+ */
+#define WM8915_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1536 (0x600) - DAC1 Mixer Volumes
+ */
+#define WM8915_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
+#define WM8915_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
+#define WM8915_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
+
+/*
+ * R1537 (0x601) - DAC1 Left Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */
+
+/*
+ * R1538 (0x602) - DAC1 Right Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */
+
+/*
+ * R1539 (0x603) - DAC2 Mixer Volumes
+ */
+#define WM8915_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
+#define WM8915_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
+#define WM8915_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
+
+/*
+ * R1540 (0x604) - DAC2 Left Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */
+
+/*
+ * R1541 (0x605) - DAC2 Right Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */
+
+/*
+ * R1542 (0x606) - DSP1 TX Left Mixer Routing
+ */
+#define WM8915_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */
+
+/*
+ * R1543 (0x607) - DSP1 TX Right Mixer Routing
+ */
+#define WM8915_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */
+
+/*
+ * R1544 (0x608) - DSP2 TX Left Mixer Routing
+ */
+#define WM8915_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */
+
+/*
+ * R1545 (0x609) - DSP2 TX Right Mixer Routing
+ */
+#define WM8915_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */
+
+/*
+ * R1546 (0x60A) - DSP TX Mixer Select
+ */
+#define WM8915_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */
+
+/*
+ * R1552 (0x610) - DAC Softmute
+ */
+#define WM8915_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+
+/*
+ * R1568 (0x620) - Oversampling
+ */
+#define WM8915_SPK_OSR128 0x0008 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */
+#define WM8915_DMIC_OSR64 0x0004 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */
+#define WM8915_ADC_OSR128 0x0002 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8915_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R1569 (0x621) - Sidetone
+ */
+#define WM8915_ST_LPF 0x1000 /* ST_LPF */
+#define WM8915_ST_LPF_MASK 0x1000 /* ST_LPF */
+#define WM8915_ST_LPF_SHIFT 12 /* ST_LPF */
+#define WM8915_ST_LPF_WIDTH 1 /* ST_LPF */
+#define WM8915_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF 0x0040 /* ST_HPF */
+#define WM8915_ST_HPF_MASK 0x0040 /* ST_HPF */
+#define WM8915_ST_HPF_SHIFT 6 /* ST_HPF */
+#define WM8915_ST_HPF_WIDTH 1 /* ST_HPF */
+#define WM8915_STR_SEL 0x0002 /* STR_SEL */
+#define WM8915_STR_SEL_MASK 0x0002 /* STR_SEL */
+#define WM8915_STR_SEL_SHIFT 1 /* STR_SEL */
+#define WM8915_STR_SEL_WIDTH 1 /* STR_SEL */
+#define WM8915_STL_SEL 0x0001 /* STL_SEL */
+#define WM8915_STL_SEL_MASK 0x0001 /* STL_SEL */
+#define WM8915_STL_SEL_SHIFT 0 /* STL_SEL */
+#define WM8915_STL_SEL_WIDTH 1 /* STL_SEL */
+
+/*
+ * R1792 (0x700) - GPIO 1
+ */
+#define WM8915_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM8915_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM8915_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM8915_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8915_GP1_PU 0x4000 /* GP1_PU */
+#define WM8915_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM8915_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM8915_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8915_GP1_PD 0x2000 /* GP1_PD */
+#define WM8915_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM8915_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM8915_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8915_GP1_POL 0x0400 /* GP1_POL */
+#define WM8915_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM8915_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM8915_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM8915_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8915_GP1_DB 0x0100 /* GP1_DB */
+#define WM8915_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM8915_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM8915_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM8915_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM8915_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM8915_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM8915_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8915_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */
+#define WM8915_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */
+#define WM8915_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */
+
+/*
+ * R1793 (0x701) - GPIO 2
+ */
+#define WM8915_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM8915_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM8915_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM8915_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8915_GP2_PU 0x4000 /* GP2_PU */
+#define WM8915_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM8915_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM8915_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8915_GP2_PD 0x2000 /* GP2_PD */
+#define WM8915_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM8915_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM8915_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8915_GP2_POL 0x0400 /* GP2_POL */
+#define WM8915_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8915_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8915_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8915_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8915_GP2_DB 0x0100 /* GP2_DB */
+#define WM8915_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM8915_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM8915_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM8915_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8915_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8915_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8915_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8915_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */
+#define WM8915_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */
+#define WM8915_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */
+
+/*
+ * R1794 (0x702) - GPIO 3
+ */
+#define WM8915_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM8915_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM8915_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM8915_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8915_GP3_PU 0x4000 /* GP3_PU */
+#define WM8915_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM8915_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM8915_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8915_GP3_PD 0x2000 /* GP3_PD */
+#define WM8915_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM8915_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM8915_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8915_GP3_POL 0x0400 /* GP3_POL */
+#define WM8915_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8915_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8915_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8915_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8915_GP3_DB 0x0100 /* GP3_DB */
+#define WM8915_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM8915_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM8915_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM8915_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8915_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8915_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8915_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8915_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */
+#define WM8915_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */
+#define WM8915_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */
+
+/*
+ * R1795 (0x703) - GPIO 4
+ */
+#define WM8915_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM8915_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM8915_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM8915_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8915_GP4_PU 0x4000 /* GP4_PU */
+#define WM8915_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM8915_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM8915_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8915_GP4_PD 0x2000 /* GP4_PD */
+#define WM8915_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM8915_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM8915_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8915_GP4_POL 0x0400 /* GP4_POL */
+#define WM8915_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM8915_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM8915_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM8915_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8915_GP4_DB 0x0100 /* GP4_DB */
+#define WM8915_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM8915_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM8915_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM8915_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM8915_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM8915_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM8915_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8915_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */
+#define WM8915_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */
+#define WM8915_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */
+
+/*
+ * R1796 (0x704) - GPIO 5
+ */
+#define WM8915_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8915_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8915_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8915_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8915_GP5_PU 0x4000 /* GP5_PU */
+#define WM8915_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8915_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8915_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8915_GP5_PD 0x2000 /* GP5_PD */
+#define WM8915_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8915_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8915_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8915_GP5_POL 0x0400 /* GP5_POL */
+#define WM8915_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8915_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8915_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8915_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8915_GP5_DB 0x0100 /* GP5_DB */
+#define WM8915_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8915_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8915_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8915_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8915_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8915_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8915_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8915_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */
+#define WM8915_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */
+#define WM8915_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */
+
+/*
+ * R1824 (0x720) - Pull Control (1)
+ */
+#define WM8915_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM8915_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM8915_MCLK2_PU 0x0200 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
+#define WM8915_MCLK2_PD 0x0100 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM8915_MCLK1_PU 0x0080 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
+#define WM8915_MCLK1_PD 0x0040 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM8915_DACDAT1_PU 0x0020 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PD 0x0010 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM8915_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM8915_BCLK1_PU 0x0002 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM8915_BCLK1_PD 0x0001 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+
+/*
+ * R1825 (0x721) - Pull Control (2)
+ */
+#define WM8915_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM8915_ADDR_PD 0x0040 /* ADDR_PD */
+#define WM8915_ADDR_PD_MASK 0x0040 /* ADDR_PD */
+#define WM8915_ADDR_PD_SHIFT 6 /* ADDR_PD */
+#define WM8915_ADDR_PD_WIDTH 1 /* ADDR_PD */
+#define WM8915_DACDAT2_PU 0x0020 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PD 0x0010 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */
+#define WM8915_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */
+#define WM8915_BCLK2_PU 0x0002 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_SHIFT 1 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_WIDTH 1 /* BCLK2_PU */
+#define WM8915_BCLK2_PD 0x0001 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_SHIFT 0 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_WIDTH 1 /* BCLK2_PD */
+
+/*
+ * R1840 (0x730) - Interrupt Status 1
+ */
+#define WM8915_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8915_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8915_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8915_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8915_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8915_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8915_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8915_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8915_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8915_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8915_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8915_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8915_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8915_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8915_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8915_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8915_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8915_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8915_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8915_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R1841 (0x731) - Interrupt Status 2
+ */
+#define WM8915_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
+#define WM8915_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8915_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8915_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
+#define WM8915_MICD_EINT 0x0001 /* MICD_EINT */
+#define WM8915_MICD_EINT_MASK 0x0001 /* MICD_EINT */
+#define WM8915_MICD_EINT_SHIFT 0 /* MICD_EINT */
+#define WM8915_MICD_EINT_WIDTH 1 /* MICD_EINT */
+
+/*
+ * R1842 (0x732) - Interrupt Raw Status 2
+ */
+#define WM8915_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
+#define WM8915_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
+#define WM8915_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
+
+/*
+ * R1848 (0x738) - Interrupt Status 1 Mask
+ */
+#define WM8915_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8915_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8915_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8915_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8915_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R1849 (0x739) - Interrupt Status 2 Mask
+ */
+#define WM8915_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
+#define WM8915_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+
+/*
+ * R1856 (0x740) - Interrupt Control
+ */
+#define WM8915_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM8915_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM8915_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM8915_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2048 (0x800) - Left PDM Speaker
+ */
+#define WM8915_SPKL_ENA 0x0010 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_SHIFT 4 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8915_SPKL_MUTE 0x0008 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */
+#define WM8915_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */
+#define WM8915_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */
+
+/*
+ * R2049 (0x801) - Right PDM Speaker
+ */
+#define WM8915_SPKR_ENA 0x0010 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_SHIFT 4 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8915_SPKR_MUTE 0x0008 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */
+#define WM8915_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */
+#define WM8915_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */
+
+/*
+ * R2050 (0x802) - PDM Speaker Mute Sequence
+ */
+#define WM8915_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8915_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8915_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R2051 (0x803) - PDM Speaker Volume
+ */
+#define WM8915_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */
+#define WM8915_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */
+#define WM8915_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */
+
+#endif
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
new file mode 100644
index 00000000000..0293763debe
--- /dev/null
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -0,0 +1,1051 @@
+/*
+ * wm8958-dsp2.c -- WM8958 DSP2 support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+
+#define WM_FW_BLOCK_INFO 0xff
+#define WM_FW_BLOCK_PM 0x00
+#define WM_FW_BLOCK_X 0x01
+#define WM_FW_BLOCK_Y 0x02
+#define WM_FW_BLOCK_Z 0x03
+#define WM_FW_BLOCK_I 0x06
+#define WM_FW_BLOCK_A 0x08
+#define WM_FW_BLOCK_C 0x0c
+
+static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
+ const struct firmware *fw, bool check)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ u64 data64;
+ u32 data32;
+ const u8 *data;
+ char *str;
+ size_t block_len, len;
+ int ret = 0;
+
+ /* Suppress unneeded downloads */
+ if (wm8994->cur_fw == fw)
+ return 0;
+
+ if (fw->size < 32) {
+ dev_err(codec->dev, "%s: firmware too short\n", name);
+ goto err;
+ }
+
+ if (memcmp(fw->data, "WMFW", 4) != 0) {
+ dev_err(codec->dev, "%s: firmware has bad file magic %08x\n",
+ name, data32);
+ goto err;
+ }
+
+ memcpy(&data32, fw->data + 4, sizeof(data32));
+ len = be32_to_cpu(data32);
+
+ memcpy(&data32, fw->data + 8, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+ if ((data32 >> 24) & 0xff) {
+ dev_err(codec->dev, "%s: unsupported firmware version %d\n",
+ name, (data32 >> 24) & 0xff);
+ goto err;
+ }
+ if ((data32 & 0xffff) != 8958) {
+ dev_err(codec->dev, "%s: unsupported target device %d\n",
+ name, data32 & 0xffff);
+ goto err;
+ }
+ if (((data32 >> 16) & 0xff) != 0xc) {
+ dev_err(codec->dev, "%s: unsupported target core %d\n",
+ name, (data32 >> 16) & 0xff);
+ goto err;
+ }
+
+ if (check) {
+ memcpy(&data64, fw->data + 24, sizeof(u64));
+ dev_info(codec->dev, "%s timestamp %llx\n",
+ name, be64_to_cpu(data64));
+ } else {
+ snd_soc_write(codec, 0x102, 0x2);
+ snd_soc_write(codec, 0x900, 0x2);
+ }
+
+ data = fw->data + len;
+ len = fw->size - len;
+ while (len) {
+ if (len < 12) {
+ dev_err(codec->dev, "%s short data block of %zd\n",
+ name, len);
+ goto err;
+ }
+
+ memcpy(&data32, data + 4, sizeof(data32));
+ block_len = be32_to_cpu(data32);
+ if (block_len + 8 > len) {
+ dev_err(codec->dev, "%zd byte block longer than file\n",
+ block_len);
+ goto err;
+ }
+ if (block_len == 0) {
+ dev_err(codec->dev, "Zero length block\n");
+ goto err;
+ }
+
+ memcpy(&data32, data, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+
+ switch ((data32 >> 24) & 0xff) {
+ case WM_FW_BLOCK_INFO:
+ /* Informational text */
+ if (!check)
+ break;
+
+ str = kzalloc(block_len + 1, GFP_KERNEL);
+ if (str) {
+ memcpy(str, data + 8, block_len);
+ dev_info(codec->dev, "%s: %s\n", name, str);
+ kfree(str);
+ } else {
+ dev_err(codec->dev, "Out of memory\n");
+ }
+ break;
+ case WM_FW_BLOCK_PM:
+ case WM_FW_BLOCK_X:
+ case WM_FW_BLOCK_Y:
+ case WM_FW_BLOCK_Z:
+ case WM_FW_BLOCK_I:
+ case WM_FW_BLOCK_A:
+ case WM_FW_BLOCK_C:
+ dev_dbg(codec->dev, "%s: %zd bytes of %x@%x\n", name,
+ block_len, (data32 >> 24) & 0xff,
+ data32 & 0xffffff);
+
+ if (check)
+ break;
+
+ data32 &= 0xffffff;
+
+ wm8994_bulk_write(codec->control_data,
+ data32 & 0xffffff,
+ block_len / 2,
+ (void *)(data + 8));
+
+ break;
+ default:
+ dev_warn(codec->dev, "%s: unknown block type %d\n",
+ name, (data32 >> 24) & 0xff);
+ break;
+ }
+
+ /* Round up to the next 32 bit word */
+ block_len += block_len % 4;
+
+ data += block_len + 8;
+ len -= block_len + 8;
+ }
+
+ if (!check) {
+ dev_dbg(codec->dev, "%s: download done\n", name);
+ wm8994->cur_fw = fw;
+ } else {
+ dev_info(codec->dev, "%s: got firmware\n", name);
+ }
+
+ goto ok;
+
+err:
+ ret = -EINVAL;
+ok:
+ if (!check) {
+ snd_soc_write(codec, 0x900, 0x0);
+ snd_soc_write(codec, 0x102, 0x0);
+ }
+
+ return ret;
+}
+
+static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i;
+
+ /* If the DSP is already running then noop */
+ if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
+ return;
+
+ /* If we have MBC firmware download it */
+ if (wm8994->mbc)
+ wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied MBC settings use them */
+ if (pdata && pdata->num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &pdata->mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
+ snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
+ cfg->coeff_regs[i]);
+
+ for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
+ snd_soc_write(codec,
+ i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
+ cfg->cutoff_regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* And we're off! */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA |
+ WM8958_MBC_SEL_MASK,
+ path << WM8958_MBC_SEL_SHIFT |
+ WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i, ena;
+
+ if (wm8994->mbc_vss)
+ wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (pdata && pdata->num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &pdata->mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
+ snd_soc_write(codec, i + 0x2800,
+ cfg->combined_regs[i]);
+ }
+
+ if (pdata && pdata->num_vss_cfgs) {
+ struct wm8958_vss_cfg *cfg
+ = &pdata->vss_cfgs[wm8994->vss_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2600, cfg->regs[i]);
+ }
+
+ if (pdata && pdata->num_vss_hpf_cfgs) {
+ struct wm8958_vss_hpf_cfg *cfg
+ = &pdata->vss_hpf_cfgs[wm8994->vss_hpf_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2400, cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Enable the algorithms we've selected */
+ ena = 0;
+ if (wm8994->mbc_ena[path])
+ ena |= 0x8;
+ if (wm8994->hpf2_ena[path])
+ ena |= 0x4;
+ if (wm8994->hpf1_ena[path])
+ ena |= 0x2;
+ if (wm8994->vss_ena[path])
+ ena |= 0x1;
+
+ snd_soc_write(codec, 0x2201, ena);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i;
+
+ wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (pdata && pdata->num_enh_eq_cfgs) {
+ struct wm8958_enh_eq_cfg *cfg
+ = &pdata->enh_eq_cfgs[wm8994->enh_eq_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2200,
+ cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
+ int ena, reg, aif;
+
+ switch (path) {
+ case 0:
+ pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
+ aif = 0;
+ break;
+ case 1:
+ pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
+ aif = 0;
+ break;
+ case 2:
+ pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
+ aif = 1;
+ break;
+ default:
+ BUG();
+ return;
+ }
+
+ /* Do we have both an active AIF and an active algorithm? */
+ ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
+ wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||
+ wm8994->enh_eq_ena[path];
+ if (!pwr_reg)
+ ena = 0;
+
+ reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
+
+ dev_dbg(codec->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
+ path, wm8994->dsp_active, start, pwr_reg, reg);
+
+ if (start && ena) {
+ /* If the DSP is already running then noop */
+ if (reg & WM8958_DSP2_ENA)
+ return;
+
+ /* If either AIFnCLK is not yet enabled postpone */
+ if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+ & WM8994_AIF1CLK_ENA_MASK) &&
+ !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+ & WM8994_AIF2CLK_ENA_MASK))
+ return;
+
+ /* Switch the clock over to the appropriate AIF */
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
+ aif << WM8958_DSP2CLK_SRC_SHIFT |
+ WM8958_DSP2CLK_ENA);
+
+ if (wm8994->enh_eq_ena[path])
+ wm8958_dsp_start_enh_eq(codec, path);
+ else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
+ wm8994->hpf2_ena[path])
+ wm8958_dsp_start_vss(codec, path);
+ else if (wm8994->mbc_ena[path])
+ wm8958_dsp_start_mbc(codec, path);
+
+ wm8994->dsp_active = path;
+
+ dev_dbg(codec->dev, "DSP running in path %d\n", path);
+ }
+
+ if (!start && wm8994->dsp_active == path) {
+ /* If the DSP is already stopped then noop */
+ if (!(reg & WM8958_DSP2_ENA))
+ return;
+
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA, 0);
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_STOP);
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, 0);
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_ENA, 0);
+
+ wm8994->dsp_active = -1;
+
+ dev_dbg(codec->dev, "DSP stopped\n");
+ }
+}
+
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMU:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Check if DSP2 is in use on another AIF */
+static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
+ if (i == aif)
+ continue;
+ if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
+ wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_mbc_cfgs)
+ return -EINVAL;
+
+ wm8994->mbc_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
+
+ return 0;
+}
+
+static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
+
+ return 0;
+}
+
+static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (wm8958_dsp2_busy(wm8994, mbc)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", mbc);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[mbc])
+ return -EBUSY;
+
+ wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]);
+
+ return 0;
+}
+
+#define WM8958_MBC_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_mbc_info, \
+ .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
+ .private_value = xval }
+
+static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_vss_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
+
+ return 0;
+}
+
+static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_vss_hpf_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_hpf_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
+
+ return 0;
+}
+
+static int wm8958_vss_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
+
+ return 0;
+}
+
+static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, vss)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", vss);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[vss])
+ return -EBUSY;
+
+ wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]);
+
+ return 0;
+}
+
+
+#define WM8958_VSS_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_vss_info, \
+ .get = wm8958_vss_get, .put = wm8958_vss_put, \
+ .private_value = xval }
+
+static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3)
+ ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
+ else
+ ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
+
+ return 0;
+}
+
+static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3) {
+ if (wm8994->hpf1_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ } else {
+ if (wm8994->hpf2_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ }
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[hpf % 3])
+ return -EBUSY;
+
+ if (hpf < 3)
+ wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
+ else
+ wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_HPF_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_hpf_info, \
+ .get = wm8958_hpf_get, .put = wm8958_hpf_put, \
+ .private_value = xval }
+
+static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_enh_eq_cfgs)
+ return -EINVAL;
+
+ wm8994->enh_eq_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
+
+ return 0;
+}
+
+static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
+
+ return 0;
+}
+
+static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->enh_eq)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, eq)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", eq);
+ return -EBUSY;
+ }
+
+ if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||
+ wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])
+ return -EBUSY;
+
+ wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_ENH_EQ_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_enh_eq_info, \
+ .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \
+ .private_value = xval }
+
+static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
+WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
+WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
+WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
+};
+
+static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
+WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
+WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
+WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
+WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
+WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
+};
+
+static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
+WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
+WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
+WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
+};
+
+static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
+ mutex_lock(&codec->mutex);
+ wm8994->enh_eq = fw;
+ mutex_unlock(&codec->mutex);
+ }
+}
+
+static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
+ mutex_lock(&codec->mutex);
+ wm8994->mbc_vss = fw;
+ mutex_unlock(&codec->mutex);
+ }
+
+ /* We can't have more than one request outstanding at once so
+ * we daisy chain.
+ */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_enh_eq.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_enh_eq_loaded);
+}
+
+static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8958_dsp2_fw(codec, "MBC", fw, true) != 0)
+ return;
+
+ mutex_lock(&codec->mutex);
+ wm8994->mbc = fw;
+ mutex_unlock(&codec->mutex);
+
+ /* We can't have more than one request outstanding at once so
+ * we daisy chain.
+ */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc_vss.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_vss_loaded);
+}
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int ret, i;
+
+ wm8994->dsp_active = -1;
+
+ snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
+ ARRAY_SIZE(wm8958_mbc_snd_controls));
+ snd_soc_add_controls(codec, wm8958_vss_snd_controls,
+ ARRAY_SIZE(wm8958_vss_snd_controls));
+ snd_soc_add_controls(codec, wm8958_enh_eq_snd_controls,
+ ARRAY_SIZE(wm8958_enh_eq_snd_controls));
+
+
+ /* We don't *require* firmware and don't want to delay boot */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_loaded);
+
+ if (!pdata)
+ return;
+
+ if (pdata->num_mbc_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
+ wm8958_get_mbc_enum, wm8958_put_mbc_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->mbc_texts = kmalloc(sizeof(char *)
+ * pdata->num_mbc_cfgs, GFP_KERNEL);
+ if (!wm8994->mbc_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d MBC config texts\n",
+ pdata->num_mbc_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_mbc_cfgs; i++)
+ wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
+
+ wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
+ wm8994->mbc_enum.texts = wm8994->mbc_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add MBC mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
+ wm8958_get_vss_enum, wm8958_put_vss_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d VSS config texts\n",
+ pdata->num_vss_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_vss_cfgs; i++)
+ wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
+
+ wm8994->vss_enum.max = pdata->num_vss_cfgs;
+ wm8994->vss_enum.texts = wm8994->vss_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add VSS mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_hpf_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
+ wm8958_get_vss_hpf_enum,
+ wm8958_put_vss_hpf_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_hpf_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_hpf_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d VSS HPF config texts\n",
+ pdata->num_vss_hpf_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
+ wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
+
+ wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
+ wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add VSS HPFmode controls: %d\n",
+ ret);
+ }
+
+ if (pdata->num_enh_eq_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
+ wm8958_get_enh_eq_enum,
+ wm8958_put_enh_eq_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->enh_eq_texts = kmalloc(sizeof(char *)
+ * pdata->num_enh_eq_cfgs, GFP_KERNEL);
+ if (!wm8994->enh_eq_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d enhanced EQ config texts\n",
+ pdata->num_enh_eq_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
+ wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
+
+ wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs;
+ wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add enhanced EQ controls: %d\n",
+ ret);
+ }
+}
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 500011eb8b2..f90ae427242 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -58,6 +58,7 @@ struct wm8962_priv {
int bclk; /* Desired BCLK */
int lrclk;
+ struct completion fll_lock;
int fll_src;
int fll_fref;
int fll_fout;
@@ -2038,6 +2039,13 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
return 0;
}
+static const char *cap_hpf_mode_text[] = {
+ "Hi-fi", "Application"
+};
+
+static const struct soc_enum cap_hpf_mode =
+ SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
+
static const struct snd_kcontrol_new wm8962_snd_controls[] = {
SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
@@ -2063,6 +2071,9 @@ SOC_DOUBLE_R("Capture Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1),
SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1),
+SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
+SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
+SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
@@ -2467,6 +2478,7 @@ SND_SOC_DAPM_INPUT("IN3R"),
SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_INPUT("Beep"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0),
@@ -2486,6 +2498,8 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0,
SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
mixinr, ARRAY_SIZE(mixinr)),
+SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+
SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
@@ -2563,13 +2577,17 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "MICBIAS", NULL, "SYSCLK" },
+ { "DMIC", NULL, "DMICDAT" },
+
{ "ADCL", NULL, "SYSCLK" },
{ "ADCL", NULL, "TOCLK" },
{ "ADCL", NULL, "MIXINL" },
+ { "ADCL", NULL, "DMIC" },
{ "ADCR", NULL, "SYSCLK" },
{ "ADCR", NULL, "TOCLK" },
{ "ADCR", NULL, "MIXINR" },
+ { "ADCR", NULL, "DMIC" },
{ "STL", "Left", "ADCL" },
{ "STL", "Right", "ADCR" },
@@ -2990,7 +3008,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
case WM8962_SYSCLK_FLL:
wm8962->sysclk = WM8962_SYSCLK_FLL;
src = 1 << WM8962_SYSCLK_SRC_SHIFT;
- WARN_ON(freq != wm8962->fll_fout);
break;
default:
return -EINVAL;
@@ -3172,12 +3189,12 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct snd_soc_codec *codec = dai->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct _fll_div fll_div;
+ unsigned long timeout;
int ret;
int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA;
@@ -3244,6 +3261,11 @@ static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+ /* This should be a massive overestimate */
+ timeout = msecs_to_jiffies(1);
+
+ wait_for_completion_timeout(&wm8962->fll_lock, timeout);
+
wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout;
wm8962->fll_src = source;
@@ -3274,7 +3296,6 @@ static struct snd_soc_dai_ops wm8962_dai_ops = {
.hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk,
.set_fmt = wm8962_set_dai_fmt,
- .set_pll = wm8962_set_fll,
.digital_mute = wm8962_mute,
};
@@ -3340,6 +3361,11 @@ static irqreturn_t wm8962_irq(int irq, void *data)
active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
active &= ~mask;
+ if (active & WM8962_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8962->fll_lock);
+ }
+
if (active & WM8962_FIFOS_ERR_EINT)
dev_err(codec->dev, "FIFO error\n");
@@ -3709,9 +3735,11 @@ static int wm8962_probe(struct snd_soc_codec *codec)
dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
+ bool dmicclk, dmicdat;
wm8962->codec = codec;
INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
+ init_completion(&wm8962->fll_lock);
codec->cache_sync = 1;
codec->dapm.idle_bias_off = 1;
@@ -3845,6 +3873,29 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_add_widgets(codec);
+ /* Save boards having to disable DMIC when not in use */
+ dmicclk = false;
+ dmicdat = false;
+ for (i = 0; i < WM8962_MAX_GPIO; i++) {
+ switch (snd_soc_read(codec, WM8962_GPIO_BASE + i)
+ & WM8962_GP2_FN_MASK) {
+ case WM8962_GPIO_FN_DMICCLK:
+ dmicclk = true;
+ break;
+ case WM8962_GPIO_FN_DMICDAT:
+ dmicdat = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!dmicclk || !dmicdat) {
+ dev_dbg(codec->dev, "DMIC not in use, disabling\n");
+ snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+ }
+ if (dmicclk != dmicdat)
+ dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
+
wm8962_init_beep(codec);
wm8962_init_gpio(codec);
@@ -3868,9 +3919,10 @@ static int wm8962_probe(struct snd_soc_codec *codec)
i2c->irq, ret);
/* Non-fatal */
} else {
- /* Enable error reporting IRQs by default */
+ /* Enable some IRQs by default */
snd_soc_update_bits(codec,
WM8962_INTERRUPT_STATUS_2_MASK,
+ WM8962_FLL_LOCK_EINT |
WM8962_TEMP_SHUT_EINT |
WM8962_FIFOS_ERR_EINT, 0);
}
@@ -3918,6 +3970,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
.reg_cache_default = wm8962_reg,
.volatile_register = wm8962_volatile_register,
.readable_register = wm8962_readable_register,
+ .set_pll = wm8962_set_fll,
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 056aef90434..9e5ff789b80 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -718,7 +718,8 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
static int class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret;
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 84e1bd1d282..970a95c5360 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -38,12 +38,6 @@
#include "wm8994.h"
#include "wm_hubs.h"
-struct fll_config {
- int src;
- int in;
- int out;
-};
-
#define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3
@@ -59,63 +53,11 @@ static int wm8994_retune_mobile_base[] = {
WM8994_AIF2_EQ_GAINS_1,
};
-struct wm8994_micdet {
- struct snd_soc_jack *jack;
- int det;
- int shrt;
-};
-
-/* codec private data */
-struct wm8994_priv {
- struct wm_hubs_data hubs;
- enum snd_soc_control_type control_type;
- void *control_data;
- struct snd_soc_codec *codec;
- int sysclk[2];
- int sysclk_rate[2];
- int mclk[2];
- int aifclk[2];
- struct fll_config fll[2], fll_suspend[2];
-
- int dac_rates[2];
- int lrclk_shared[2];
-
- int mbc_ena[3];
-
- /* Platform dependent DRC configuration */
- const char **drc_texts;
- int drc_cfg[WM8994_NUM_DRC];
- struct soc_enum drc_enum;
-
- /* Platform dependent ReTune mobile configuration */
- int num_retune_mobile_texts;
- const char **retune_mobile_texts;
- int retune_mobile_cfg[WM8994_NUM_EQ];
- struct soc_enum retune_mobile_enum;
-
- /* Platform dependent MBC configuration */
- int mbc_cfg;
- const char **mbc_texts;
- struct soc_enum mbc_enum;
-
- struct wm8994_micdet micdet[2];
-
- wm8958_micdet_cb jack_cb;
- void *jack_cb_data;
- int micdet_irq;
-
- int revision;
- struct wm8994_pdata *pdata;
-
- unsigned int aif1clk_enable:1;
- unsigned int aif2clk_enable:1;
-
- unsigned int aif1clk_disable:1;
- unsigned int aif2clk_disable:1;
-};
-
static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->control_data;
+
switch (reg) {
case WM8994_GPIO_1:
case WM8994_GPIO_2:
@@ -132,6 +74,15 @@ static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
case WM8994_INTERRUPT_STATUS_2:
case WM8994_INTERRUPT_RAW_STATUS_2:
return 1;
+
+ case WM8958_DSP2_PROGRAM:
+ case WM8958_DSP2_CONFIG:
+ case WM8958_DSP2_EXECCONTROL:
+ if (control->type == WM8958)
+ return 1;
+ else
+ return 0;
+
default:
break;
}
@@ -574,215 +525,6 @@ static const struct soc_enum dac_osr =
static const struct soc_enum adc_osr =
SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text);
-static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
-{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994_pdata *pdata = wm8994->pdata;
- int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
- int ena, reg, aif, i;
-
- switch (mbc) {
- case 0:
- pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
- aif = 0;
- break;
- case 1:
- pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
- aif = 0;
- break;
- case 2:
- pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
- aif = 1;
- break;
- default:
- BUG();
- return;
- }
-
- /* We can only enable the MBC if the AIF is enabled and we
- * want it to be enabled. */
- ena = pwr_reg && wm8994->mbc_ena[mbc];
-
- reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
-
- dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
- mbc, start, pwr_reg, reg);
-
- if (start && ena) {
- /* If the DSP is already running then noop */
- if (reg & WM8958_DSP2_ENA)
- return;
-
- /* Switch the clock over to the appropriate AIF */
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
- WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
- aif << WM8958_DSP2CLK_SRC_SHIFT |
- WM8958_DSP2CLK_ENA);
-
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
- WM8958_DSP2_ENA, WM8958_DSP2_ENA);
-
- /* If we've got user supplied MBC settings use them */
- if (pdata && pdata->num_mbc_cfgs) {
- struct wm8958_mbc_cfg *cfg
- = &pdata->mbc_cfgs[wm8994->mbc_cfg];
-
- for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
- snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
- cfg->coeff_regs[i]);
-
- for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
- snd_soc_write(codec,
- i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
- cfg->cutoff_regs[i]);
- }
-
- /* Run the DSP */
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
- WM8958_DSP2_RUNR);
-
- /* And we're off! */
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
- WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
- mbc << WM8958_MBC_SEL_SHIFT |
- WM8958_MBC_ENA);
- } else {
- /* If the DSP is already stopped then noop */
- if (!(reg & WM8958_DSP2_ENA))
- return;
-
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
- WM8958_MBC_ENA, 0);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
- WM8958_DSP2_ENA, 0);
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
- WM8958_DSP2CLK_ENA, 0);
- }
-}
-
-static int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- int mbc;
-
- switch (w->shift) {
- case 13:
- case 12:
- mbc = 2;
- break;
- case 11:
- case 10:
- mbc = 1;
- break;
- case 9:
- case 8:
- mbc = 0;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- wm8958_mbc_apply(codec, mbc, 1);
- break;
- case SND_SOC_DAPM_POST_PMD:
- wm8958_mbc_apply(codec, mbc, 0);
- break;
- }
-
- return 0;
-}
-
-static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994_pdata *pdata = wm8994->pdata;
- int value = ucontrol->value.integer.value[0];
- int reg;
-
- /* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
- if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
- return -EBUSY;
-
- if (value >= pdata->num_mbc_cfgs)
- return -EINVAL;
-
- wm8994->mbc_cfg = value;
-
- return 0;
-}
-
-static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
-
- return 0;
-}
-
-static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-
-static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int mbc = kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
-
- return 0;
-}
-
-static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int mbc = kcontrol->private_value;
- int i;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- if (ucontrol->value.integer.value[0] > 1)
- return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
- if (mbc != i && wm8994->mbc_ena[i]) {
- dev_dbg(codec->dev, "MBC %d active already\n", mbc);
- return -EBUSY;
- }
- }
-
- wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
-
- wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
-
- return 0;
-}
-
-#define WM8958_MBC_SWITCH(xname, xval) {\
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .info = wm8958_mbc_info, \
- .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
- .private_value = xval }
-
static const struct snd_kcontrol_new wm8994_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
@@ -924,9 +666,6 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
static const struct snd_kcontrol_new wm8958_snd_controls[] = {
SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
-WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
-WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
-WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
};
static int clk_sys_event(struct snd_soc_dapm_widget *w,
@@ -1032,6 +771,9 @@ static int late_enable_ev(struct snd_soc_dapm_widget *w,
break;
}
+ /* We may also have postponed startup of DSP, handle that. */
+ wm8958_aif_ev(w, kcontrol, event);
+
return 0;
}
@@ -1135,7 +877,8 @@ static const char *hp_mux_text[] = {
static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec;
int ret;
@@ -1262,7 +1005,8 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec;
int ret;
@@ -2180,6 +1924,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK, 0);
+ wm8994->cur_fw = NULL;
+
pm_runtime_put(codec->dev);
}
break;
@@ -2672,11 +2418,22 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = codec->control_data;
int i, ret;
+ switch (control->type) {
+ case WM8994:
+ snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
+ break;
+ case WM8958:
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, 0);
+ break;
+ }
+
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
- sizeof(struct fll_config));
+ sizeof(struct wm8994_fll_config));
ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0);
if (ret < 0)
dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
@@ -2691,6 +2448,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int wm8994_resume(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = codec->control_data;
int i, ret;
unsigned int val, mask;
@@ -2729,6 +2487,19 @@ static int wm8994_resume(struct snd_soc_codec *codec)
i + 1, ret);
}
+ switch (control->type) {
+ case WM8994:
+ if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
+ snd_soc_update_bits(codec, WM8994_MICBIAS,
+ WM8994_MICD_ENA, WM8994_MICD_ENA);
+ break;
+ case WM8958:
+ if (wm8994->jack_cb)
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, WM8958_MICD_ENA);
+ break;
+ }
+
return 0;
}
#else
@@ -2862,34 +2633,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs);
- if (pdata->num_mbc_cfgs) {
- struct snd_kcontrol_new control[] = {
- SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
- wm8958_get_mbc_enum, wm8958_put_mbc_enum),
- };
-
- /* We need an array of texts for the enum API */
- wm8994->mbc_texts = kmalloc(sizeof(char *)
- * pdata->num_mbc_cfgs, GFP_KERNEL);
- if (!wm8994->mbc_texts) {
- dev_err(wm8994->codec->dev,
- "Failed to allocate %d MBC config texts\n",
- pdata->num_mbc_cfgs);
- return;
- }
-
- for (i = 0; i < pdata->num_mbc_cfgs; i++)
- wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
-
- wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
- wm8994->mbc_enum.texts = wm8994->mbc_texts;
-
- ret = snd_soc_add_controls(wm8994->codec, control, 1);
- if (ret != 0)
- dev_err(wm8994->codec->dev,
- "Failed to add MBC mode controls: %d\n", ret);
- }
-
if (pdata->num_retune_mobile_cfgs)
wm8994_handle_retune_mobile_pdata(wm8994);
else
@@ -3343,14 +3086,23 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
case WM8958:
snd_soc_add_controls(codec, wm8958_snd_controls,
ARRAY_SIZE(wm8958_snd_controls));
- snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
- ARRAY_SIZE(wm8994_lateclk_widgets));
- snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
- ARRAY_SIZE(wm8994_adc_widgets));
- snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
- ARRAY_SIZE(wm8994_dac_widgets));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
+ if (wm8994->revision < 1) {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
+ ARRAY_SIZE(wm8994_lateclk_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
+ ARRAY_SIZE(wm8994_adc_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
+ ARRAY_SIZE(wm8994_dac_revd_widgets));
+ } else {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+ ARRAY_SIZE(wm8994_lateclk_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+ ARRAY_SIZE(wm8994_adc_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+ ARRAY_SIZE(wm8994_dac_widgets));
+ }
break;
}
@@ -3374,10 +3126,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
break;
case WM8958:
- snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
- ARRAY_SIZE(wm8994_lateclk_intercon));
- snd_soc_dapm_add_routes(dapm, wm8958_intercon,
- ARRAY_SIZE(wm8958_intercon));
+ if (wm8994->revision < 1) {
+ snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
+ ARRAY_SIZE(wm8994_revd_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
+ ARRAY_SIZE(wm8994_lateclk_revd_intercon));
+ } else {
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+ ARRAY_SIZE(wm8994_lateclk_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8958_intercon,
+ ARRAY_SIZE(wm8958_intercon));
+ }
+
+ wm8958_dsp2_init(codec);
break;
}
@@ -3420,6 +3181,12 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
free_irq(wm8994->micdet_irq, wm8994);
break;
}
+ if (wm8994->mbc)
+ release_firmware(wm8994->mbc);
+ if (wm8994->mbc_vss)
+ release_firmware(wm8994->mbc_vss);
+ if (wm8994->enh_eq)
+ release_firmware(wm8994->enh_eq);
kfree(wm8994->retune_mobile_texts);
kfree(wm8994->drc_texts);
kfree(wm8994);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 999b8851226..0a1db04b73b 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -10,6 +10,9 @@
#define _WM8994_H
#include <sound/soc.h>
+#include <linux/firmware.h>
+
+#include "wm_hubs.h"
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
@@ -45,4 +48,98 @@ struct wm8994_access_mask {
extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec);
+
+struct wm8994_micdet {
+ struct snd_soc_jack *jack;
+ int det;
+ int shrt;
+};
+
+/* codec private data */
+struct wm8994_fll_config {
+ int src;
+ int in;
+ int out;
+};
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ 3
+
+struct wm8994_priv {
+ struct wm_hubs_data hubs;
+ enum snd_soc_control_type control_type;
+ void *control_data;
+ struct snd_soc_codec *codec;
+ int sysclk[2];
+ int sysclk_rate[2];
+ int mclk[2];
+ int aifclk[2];
+ struct wm8994_fll_config fll[2], fll_suspend[2];
+
+ int dac_rates[2];
+ int lrclk_shared[2];
+
+ int mbc_ena[3];
+ int hpf1_ena[3];
+ int hpf2_ena[3];
+ int vss_ena[3];
+ int enh_eq_ena[3];
+
+ /* Platform dependant DRC configuration */
+ const char **drc_texts;
+ int drc_cfg[WM8994_NUM_DRC];
+ struct soc_enum drc_enum;
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[WM8994_NUM_EQ];
+ struct soc_enum retune_mobile_enum;
+
+ /* Platform dependant MBC configuration */
+ int mbc_cfg;
+ const char **mbc_texts;
+ struct soc_enum mbc_enum;
+
+ /* Platform dependant VSS configuration */
+ int vss_cfg;
+ const char **vss_texts;
+ struct soc_enum vss_enum;
+
+ /* Platform dependant VSS HPF configuration */
+ int vss_hpf_cfg;
+ const char **vss_hpf_texts;
+ struct soc_enum vss_hpf_enum;
+
+ /* Platform dependant enhanced EQ configuration */
+ int enh_eq_cfg;
+ const char **enh_eq_texts;
+ struct soc_enum enh_eq_enum;
+
+ struct wm8994_micdet micdet[2];
+
+ wm8958_micdet_cb jack_cb;
+ void *jack_cb_data;
+ int micdet_irq;
+
+ int revision;
+ struct wm8994_pdata *pdata;
+
+ unsigned int aif1clk_enable:1;
+ unsigned int aif2clk_enable:1;
+
+ unsigned int aif1clk_disable:1;
+ unsigned int aif2clk_disable:1;
+
+ int dsp_active;
+ const struct firmware *cur_fw;
+ const struct firmware *mbc;
+ const struct firmware *mbc_vss;
+ const struct firmware *enh_eq;
+};
+
#endif
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 67eaaecbb42..5ad873fda81 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -305,11 +305,11 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec;
int ret;
- w = snd_kcontrol_chip(kcontrol);
codec = w->codec;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
wm8995_update_class_w(codec);
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 47b357adabd..646b58dda84 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -142,7 +142,7 @@ static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = {
* constantly enabled, we use the mutes on those inputs to simulate such
* controls.
*/
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9705_audio_map[] = {
/* HP mixer */
{"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
{"HP Mixer", "CD Playback Switch", "CD PGA"},
@@ -200,17 +200,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Right ADC", NULL, "ADC PGA"},
};
-static int wm9705_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9705_dapm_widgets,
- ARRAY_SIZE(wm9705_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
/* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{
@@ -364,7 +353,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9705_snd_ac97_controls,
ARRAY_SIZE(wm9705_snd_ac97_controls));
- wm9705_add_widgets(codec);
return 0;
@@ -390,6 +378,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9705_reg,
+ .dapm_widgets = wm9705_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
+ .dapm_routes = wm9705_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
};
static __devinit int wm9705_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index bf5d4ef1a2a..90117f8156e 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -332,7 +332,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9712_audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"},
@@ -429,17 +429,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"ROUT2", NULL, "Speaker PGA"},
};
-static int wm9712_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9712_dapm_widgets,
- ARRAY_SIZE(wm9712_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -651,7 +640,6 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls));
- wm9712_add_widgets(codec);
return 0;
@@ -678,6 +666,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9712_reg,
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
};
static __devinit int wm9712_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 38ed9855871..7167cb6787d 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -487,7 +487,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
SND_SOC_DAPM_VMID("VMID"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9713_audio_map[] = {
/* left HP mixer */
{"Left HP Mixer", "Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -644,18 +644,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Capture Mono Mux", "Right", "Right Capture Source"},
};
-static int wm9713_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9713_dapm_widgets,
- ARRAY_SIZE(wm9713_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -1231,7 +1219,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
- wm9713_add_widgets(codec);
return 0;
@@ -1262,6 +1249,10 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
+ .dapm_widgets = wm9713_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
+ .dapm_routes = wm9713_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
};
static __devinit int wm9713_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 4005e9af5d6..e55b298c14a 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -787,17 +787,17 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
- { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+ { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
};
static const struct snd_soc_dapm_route lineout1_se_routes[] = {
- { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+ { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
@@ -806,17 +806,17 @@ static const struct snd_soc_dapm_route lineout1_se_routes[] = {
static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
{ "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
- { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+ { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
};
static const struct snd_soc_dapm_route lineout2_se_routes[] = {
- { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+ { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
@@ -836,17 +836,21 @@ int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
WM8993_IN2_VU, WM8993_IN2_VU);
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
- WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
- WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 4ddc6d3b667..8566238db2a 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -909,6 +909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
+ dma_data->sram_size = pdata->sram_size_playback;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
mem->start);
@@ -925,6 +926,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
+ dma_data->sram_size = pdata->sram_size_capture;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
mem->start);
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
index ac2ded96925..5b13feca753 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/imx/imx-ssi.c
@@ -667,12 +667,6 @@ static int imx_ssi_probe(struct platform_device *pdev)
if (res)
ssi->dma_params_rx.dma = res->start;
- if ((cpu_is_mx27() || cpu_is_mx21()) &&
- !(ssi->flags & IMX_SSI_USE_AC97) &&
- (ssi->flags & IMX_SSI_DMA)) {
- ssi->flags |= IMX_SSI_DMA;
- }
-
platform_set_drvdata(pdev, ssi);
ret = snd_soc_register_dai(&pdev->dev, dai);
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
index 49723e3e7e3..c5fc339f68f 100644
--- a/sound/soc/jz4740/qi_lb60.c
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -27,11 +27,7 @@
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *ctrl, int event)
{
- int on = 0;
- if (event & SND_SOC_DAPM_POST_PMU)
- on = 1;
- else if (event & SND_SOC_DAPM_PRE_PMD)
- on = 0;
+ int on = !SND_SOC_DAPM_EVENT_OFF(event);
gpio_set_value(QI_LB60_SND_GPIO, on);
gpio_set_value(QI_LB60_AMP_GPIO, on);
@@ -70,12 +66,6 @@ static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- snd_soc_dapm_new_controls(dapm, qi_lb60_widgets,
- ARRAY_SIZE(qi_lb60_widgets));
- snd_soc_dapm_add_routes(dapm, qi_lb60_routes,
- ARRAY_SIZE(qi_lb60_routes));
- snd_soc_dapm_sync(dapm);
-
return 0;
}
@@ -93,10 +83,20 @@ static struct snd_soc_card qi_lb60 = {
.name = "QI LB60",
.dai_link = &qi_lb60_dai,
.num_links = 1,
+
+ .dapm_widgets = qi_lb60_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
+ .dapm_routes = qi_lb60_routes,
+ .num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
};
static struct platform_device *qi_lb60_snd_device;
+static const struct gpio qi_lb60_gpios[] = {
+ { QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
+ { QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
+};
+
static int __init qi_lb60_init(void)
{
int ret;
@@ -106,23 +106,12 @@ static int __init qi_lb60_init(void)
if (!qi_lb60_snd_device)
return -ENOMEM;
- ret = gpio_request(QI_LB60_SND_GPIO, "SND");
+ ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
if (ret) {
- pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
- QI_LB60_SND_GPIO, ret);
+ pr_err("qi_lb60 snd: Failed to request gpios: %d\n", ret);
goto err_device_put;
}
- ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
- if (ret) {
- pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
- QI_LB60_AMP_GPIO, ret);
- goto err_gpio_free_snd;
- }
-
- gpio_direction_output(QI_LB60_SND_GPIO, 0);
- gpio_direction_output(QI_LB60_AMP_GPIO, 0);
-
platform_set_drvdata(qi_lb60_snd_device, &qi_lb60);
ret = platform_device_add(qi_lb60_snd_device);
@@ -135,10 +124,8 @@ static int __init qi_lb60_init(void)
err_unset_pdata:
platform_set_drvdata(qi_lb60_snd_device, NULL);
-/*err_gpio_free_amp:*/
- gpio_free(QI_LB60_AMP_GPIO);
-err_gpio_free_snd:
- gpio_free(QI_LB60_SND_GPIO);
+/*err_gpio_free_array:*/
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
err_device_put:
platform_device_put(qi_lb60_snd_device);
@@ -148,9 +135,8 @@ module_init(qi_lb60_init);
static void __exit qi_lb60_exit(void)
{
- gpio_free(QI_LB60_AMP_GPIO);
- gpio_free(QI_LB60_SND_GPIO);
platform_device_unregister(qi_lb60_snd_device);
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
}
module_exit(qi_lb60_exit);
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c
index 6b1f9d3bf34..5a946b4115a 100644
--- a/sound/soc/mid-x86/sst_platform.c
+++ b/sound/soc/mid-x86/sst_platform.c
@@ -249,10 +249,13 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
return -ENOMEM;
}
stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+ stream->sstdrv_ops->module_name = SST_CARD_NAMES;
/* registering with SST driver to get access to SST APIs to use */
ret_val = register_sst_card(stream->sstdrv_ops);
if (ret_val) {
pr_err("sst: sst card registration failed\n");
+ kfree(stream->sstdrv_ops);
+ kfree(stream);
return ret_val;
}
runtime->private_data = stream;
@@ -270,6 +273,7 @@ static int sst_platform_close(struct snd_pcm_substream *substream)
str_id = stream->stream_info.str_id;
if (str_id)
ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
+ unregister_sst_card(stream->sstdrv_ops);
kfree(stream->sstdrv_ops);
kfree(stream);
return ret_val;
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 2175f09e57b..07b77235724 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -146,7 +146,7 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
* 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
* 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
*/
- if (cpu_is_omap343x() || cpu_is_omap44xx()) {
+ if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
/*
* Rule for the buffer size. We should not allow
* smaller buffer than the FIFO size to avoid underruns
@@ -258,7 +258,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- if (cpu_is_omap343x()) {
+ if (cpu_is_omap34xx()) {
dma_data->set_threshold = omap_mcbsp_set_threshold;
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (omap_mcbsp_get_dma_op_mode(bus_id) ==
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 37dc7211ed3..9a7dedd6f5a 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 8caeb8d305c..e6a6b991d05 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -37,7 +37,8 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
@@ -195,7 +196,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
if ((cpu_is_omap1510()))
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
- else
+ else if (!substream->runtime->no_period_wakeup)
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
if (!(cpu_class_is_omap1())) {
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
index fea0515331f..a0ed1dbb52d 100644
--- a/sound/soc/omap/omap-pcm.h
+++ b/sound/soc/omap/omap-pcm.h
@@ -4,7 +4,7 @@
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index d0986220eff..0aae998b654 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 - 2009 Nokia Corporation
*
- * Contact: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
* Eduardo Valentin <eduardo.valentin@nokia.com>
* Jarkko Nikula <jhnikula@gmail.com>
*
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 580f4857130..33ebc46b45b 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -155,6 +155,15 @@ config SND_SOC_RAUMFELD
help
Say Y if you want to add support for SoC audio on Raumfeld devices
+config SND_PXA2XX_SOC_HX4700
+ tristate "SoC Audio support for HP iPAQ hx4700"
+ depends on SND_PXA2XX_SOC && MACH_H4700
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_AK4641
+ help
+ Say Y if you want to add support for SoC audio on the
+ HP iPAQ hx4700.
+
config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician"
depends on SND_PXA2XX_SOC && MACH_MAGICIAN
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 07660165ec8..af357623be9 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -22,6 +22,7 @@ snd-soc-palm27x-objs := palm27x.o
snd-soc-saarb-objs := saarb.o
snd-soc-tavorevb3-objs := tavorevb3.o
snd-soc-zylonite-objs := zylonite.o
+snd-soc-hx4700-objs := hx4700.o
snd-soc-magician-objs := magician.o
snd-soc-mioa701-objs := mioa701_wm9713.o
snd-soc-z2-objs := z2.o
@@ -37,6 +38,7 @@ obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 9027da466ca..28757fb9df3 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = {
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.init = corgi_wm8731_init,
.ops = &corgi_ops,
};
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
new file mode 100644
index 00000000000..65c124831a0
--- /dev/null
+++ b/sound/soc/pxa/hx4700.c
@@ -0,0 +1,255 @@
+/*
+ * SoC audio for HP iPAQ hx4700
+ *
+ * Copyright (c) 2009 Philipp Zabel
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hx4700.h>
+#include <asm/mach-types.h>
+#include "pxa2xx-i2s.h"
+
+#include "../codecs/ak4641.h"
+
+static struct snd_soc_jack hs_jack;
+
+/* Headphones jack detection DAPM pin */
+static struct snd_soc_jack_pin hs_jack_pin[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Speaker",
+ /* disable speaker when hp jack is inserted */
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1,
+ },
+};
+
+/* Headphones jack detection GPIO */
+static struct snd_soc_jack_gpio hs_jack_gpio = {
+ .gpio = GPIO75_HX4700_EARPHONE_nDET,
+ .invert = true,
+ .name = "hp-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+};
+
+/*
+ * iPAQ hx4700 uses I2S for capture and playback.
+ */
+static int hx4700_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as output */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ /* inform codec driver about clock freq *
+ * (PXA I2S always uses divider 256) */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops hx4700_ops = {
+ .hw_params = hx4700_hw_params,
+};
+
+static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* hx4700 machine dapm widgets */
+static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
+ SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
+ SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
+};
+
+/* hx4700 machine audio_map */
+static const struct snd_soc_dapm_route hx4700_audio_map[] = {
+
+ /* Headphone connected to LOUT, ROUT */
+ {"Headphone Jack", NULL, "LOUT"},
+ {"Headphone Jack", NULL, "ROUT"},
+
+ /* Speaker connected to MOUT2 */
+ {"Speaker", NULL, "MOUT2"},
+
+ /* Microphone connected to MICIN */
+ {"MICIN", NULL, "Built-in Microphone"},
+ {"AIN", NULL, "MICOUT"},
+};
+
+/*
+ * Logic for a ak4641 as connected on a HP iPAQ hx4700
+ */
+static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int err;
+
+ /* NC codec pins */
+ /* FIXME: is anything connected here? */
+ snd_soc_dapm_nc_pin(dapm, "MOUT1");
+ snd_soc_dapm_nc_pin(dapm, "MICEXT");
+ snd_soc_dapm_nc_pin(dapm, "AUX");
+
+ /* Jack detection API stuff */
+ err = snd_soc_jack_new(codec, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack);
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin),
+ hs_jack_pin);
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
+
+ return err;
+}
+
+/* hx4700 digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link hx4700_dai = {
+ .name = "ak4641",
+ .stream_name = "AK4641",
+ .cpu_dai_name = "pxa2xx-i2s",
+ .codec_dai_name = "ak4641-hifi",
+ .platform_name = "pxa-pcm-audio",
+ .codec_name = "ak4641.0-0012",
+ .init = hx4700_ak4641_init,
+ .ops = &hx4700_ops,
+};
+
+/* hx4700 audio machine driver */
+static struct snd_soc_card snd_soc_card_hx4700 = {
+ .name = "iPAQ hx4700",
+ .dai_link = &hx4700_dai,
+ .num_links = 1,
+ .dapm_widgets = hx4700_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
+ .dapm_routes = hx4700_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
+};
+
+static struct gpio hx4700_audio_gpios[] = {
+ { GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
+ { GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
+};
+
+static int __devinit hx4700_audio_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!machine_is_h4700())
+ return -ENODEV;
+
+ ret = gpio_request_array(hx4700_audio_gpios,
+ ARRAY_SIZE(hx4700_audio_gpios));
+ if (ret)
+ return ret;
+
+ snd_soc_card_hx4700.dev = &pdev->dev;
+ ret = snd_soc_register_card(&snd_soc_card_hx4700);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __devexit hx4700_audio_remove(struct platform_device *pdev)
+{
+ snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
+ snd_soc_unregister_card(&snd_soc_card_hx4700);
+
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
+
+ gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver hx4700_audio_driver = {
+ .driver = {
+ .name = "hx4700-audio",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = hx4700_audio_probe,
+ .remove = __devexit_p(hx4700_audio_remove),
+};
+
+static int __init hx4700_modinit(void)
+{
+ return platform_driver_register(&hx4700_audio_driver);
+}
+module_init(hx4700_modinit);
+
+static void __exit hx4700_modexit(void)
+{
+ platform_driver_unregister(&hx4700_audio_driver);
+}
+
+module_exit(hx4700_modexit);
+
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hx4700-audio");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index a7d4999f9b2..da3ae4316cf 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = {
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.init = poodle_wm8731_init,
.ops = &poodle_ops,
};
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 8e157135063..b253d864868 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -42,6 +42,7 @@
static int spitz_jack_func;
static int spitz_spk_func;
+static int spitz_mic_gpio;
static void spitz_ext_control(struct snd_soc_codec *codec)
{
@@ -217,14 +218,7 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol,
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (machine_is_borzoi() || machine_is_spitz())
- gpio_set_value(SPITZ_GPIO_MIC_BIAS,
- SND_SOC_DAPM_EVENT_ON(event));
-
- if (machine_is_akita())
- gpio_set_value(AKITA_GPIO_MIC_BIAS,
- SND_SOC_DAPM_EVENT_ON(event));
-
+ gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -339,22 +333,45 @@ static int __init spitz_init(void)
if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
return -ENODEV;
+ if (machine_is_borzoi() || machine_is_spitz())
+ spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
+ else
+ spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+
+ ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
+ if (ret)
+ goto err1;
+
+ ret = gpio_direction_output(spitz_mic_gpio, 0);
+ if (ret)
+ goto err2;
+
spitz_snd_device = platform_device_alloc("soc-audio", -1);
- if (!spitz_snd_device)
- return -ENOMEM;
+ if (!spitz_snd_device) {
+ ret = -ENOMEM;
+ goto err2;
+ }
platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
- ret = platform_device_add(spitz_snd_device);
+ ret = platform_device_add(spitz_snd_device);
if (ret)
- platform_device_put(spitz_snd_device);
+ goto err3;
+
+ return 0;
+err3:
+ platform_device_put(spitz_snd_device);
+err2:
+ gpio_free(spitz_mic_gpio);
+err1:
return ret;
}
static void __exit spitz_exit(void)
{
platform_device_unregister(spitz_snd_device);
+ gpio_free(spitz_mic_gpio);
}
module_init(spitz_init);
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index a3fdfb63146..459566bfcd3 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -162,3 +162,18 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF
select SND_SAMSUNG_SPDIF
help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
+
+config SND_SOC_SMDK_WM8580_PCM
+ tristate "SoC PCM Audio support for WM8580 on SMDK"
+ depends on SND_SOC_SAMSUNG && (MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110)
+ select SND_SOC_WM8580
+ select SND_SAMSUNG_PCM
+ help
+ Say Y if you want to add support for SoC audio on the SMDK.
+
+config SND_SOC_SPEYSIDE
+ tristate "Audio support for Wolfson Speyside"
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ select SND_SAMSUNG_I2S
+ select SND_SOC_WM8915
+ select SND_SOC_WM9081
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 294dec05c26..683843a2744 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -34,6 +34,8 @@ snd-soc-smdk-wm9713-objs := smdk_wm9713.o
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
snd-soc-goni-wm8994-objs := goni_wm8994.o
snd-soc-smdk-spdif-objs := smdk_spdif.o
+snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
+snd-soc-speyside-objs := speyside.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -51,3 +53,5 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
+obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
+obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c
index 0e80daee8b6..eb6d72ed55a 100644
--- a/sound/soc/samsung/goni_wm8994.c
+++ b/sound/soc/samsung/goni_wm8994.c
@@ -246,7 +246,6 @@ static struct snd_soc_dai_link goni_dai[] = {
.stream_name = "Voice",
.cpu_dai_name = "goni-voice-dai",
.codec_dai_name = "wm8994-aif2",
- .platform_name = "samsung-audio",
.codec_name = "wm8994-codec.0-001a",
.ops = &goni_voice_ops,
},
diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c
index 45223097563..16152ed0864 100644
--- a/sound/soc/samsung/neo1973_wm8753.c
+++ b/sound/soc/samsung/neo1973_wm8753.c
@@ -432,7 +432,6 @@ static struct snd_soc_dai_link neo1973_dai[] = {
{ /* Voice via BT */
.name = "Bluetooth",
.stream_name = "Voice",
- .platform_name = "samsung-audio",
.cpu_dai_name = "dfbmcs320-pcm",
.codec_dai_name = "wm8753-voice",
.codec_name = "wm8753-codec.0-001a",
diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c
new file mode 100644
index 00000000000..0d12092df16
--- /dev/null
+++ b/sound/soc/samsung/smdk_wm8580pcm.c
@@ -0,0 +1,206 @@
+/*
+ * sound/soc/samsung/smdk_wm8580pcm.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co. Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm8580.h"
+#include "dma.h"
+#include "pcm.h"
+
+/*
+ * Board Settings:
+ * o '1' means 'ON'
+ * o '0' means 'OFF'
+ * o 'X' means 'Don't care'
+ *
+ * SMDK6410, SMDK6440, SMDK6450 Base B/D: CFG1-0000, CFG2-1111
+ * SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000
+ */
+
+#define SMDK_WM8580_EXT_OSC 12000000
+#define SMDK_WM8580_EXT_MCLK 4096000
+#define SMDK_WM8580_EXT_VOICE 2048000
+
+static unsigned long mclk_freq;
+static unsigned long xtal_freq;
+
+/*
+ * If MCLK clock directly gets from XTAL, we don't have to use PLL
+ * to make MCLK, but if XTAL clock source connects with other codec
+ * pin (like XTI), we should have to set codec's PLL to make MCLK.
+ * Because Samsung SoC does not support pcmcdclk output like I2S.
+ */
+
+static int smdk_wm8580_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int rfs, ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ break;
+ default:
+ printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n",
+ __func__, __LINE__, params_rate(params));
+ return -EINVAL;
+ }
+
+ rfs = mclk_freq / params_rate(params) / 2;
+
+ /* Set the codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B
+ | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* Set the cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B
+ | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ if (mclk_freq == xtal_freq) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_MCLK);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_PLLA);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
+ xtal_freq, mclk_freq);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set PCM source clock on CPU */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Set SCLK_DIV for making bclk */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops smdk_wm8580_pcm_ops = {
+ .hw_params = smdk_wm8580_pcm_hw_params,
+};
+
+static struct snd_soc_dai_link smdk_dai[] = {
+ {
+ .name = "WM8580 PAIF PCM RX",
+ .stream_name = "Playback",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8580-hifi-playback",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8580-codec.0-001b",
+ .ops = &smdk_wm8580_pcm_ops,
+ }, {
+ .name = "WM8580 PAIF PCM TX",
+ .stream_name = "Capture",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8580-hifi-capture",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8580-codec.0-001b",
+ .ops = &smdk_wm8580_pcm_ops,
+ },
+};
+
+static struct snd_soc_card smdk_pcm = {
+ .name = "SMDK-PCM",
+ .dai_link = smdk_dai,
+ .num_links = 2,
+};
+
+/*
+ * After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1)
+ * is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4),
+ * 2.0484Mhz, directly with MCLK both Codec and SoC.
+ */
+static int __devinit snd_smdk_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ xtal_freq = SMDK_WM8580_EXT_OSC;
+ mclk_freq = SMDK_WM8580_EXT_MCLK;
+
+ if (machine_is_smdkc110() || machine_is_smdkv210())
+ xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE;
+
+ smdk_pcm.dev = &pdev->dev;
+ ret = snd_soc_register_card(&smdk_pcm);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit snd_smdk_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&smdk_pcm);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver snd_smdk_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "samsung-smdk-pcm",
+ },
+ .probe = snd_smdk_probe,
+ .remove = __devexit_p(snd_smdk_remove),
+};
+
+static int __init smdk_audio_init(void)
+{
+ return platform_driver_register(&snd_smdk_driver);
+}
+
+module_init(smdk_audio_init);
+
+static void __exit smdk_audio_exit(void)
+{
+ platform_driver_unregister(&snd_smdk_driver);
+}
+
+module_exit(smdk_audio_exit);
+
+MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
new file mode 100644
index 00000000000..360a333cb7c
--- /dev/null
+++ b/sound/soc/samsung/speyside.c
@@ -0,0 +1,332 @@
+/*
+ * Speyside audio support
+ *
+ * Copyright 2011 Wolfson Microelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+
+#include "../codecs/wm8915.h"
+#include "../codecs/wm9081.h"
+
+#define WM8915_HPSEL_GPIO 214
+
+static int speyside_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
+ 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL\n");
+ return ret;
+ }
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int speyside_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
+ 32768, 256 * 48000);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
+ 256 * 48000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops speyside_ops = {
+ .hw_params = speyside_hw_params,
+};
+
+static struct snd_soc_jack speyside_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin speyside_headset_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* Default the headphone selection to active high */
+static int speyside_jack_polarity;
+
+static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
+ return 1;
+ if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
+ return 1;
+
+ return 0;
+}
+
+static void speyside_set_polarity(struct snd_soc_codec *codec,
+ int polarity)
+{
+ speyside_jack_polarity = !polarity;
+ gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+
+ /* Re-run DAPM to make sure we're using the correct mic bias */
+ snd_soc_dapm_sync(&codec->dapm);
+}
+
+static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
+ if (ret != 0)
+ pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
+ gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+
+ ret = snd_soc_jack_new(codec, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &speyside_headset);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(&speyside_headset,
+ ARRAY_SIZE(speyside_headset_pins),
+ speyside_headset_pins);
+ if (ret)
+ return ret;
+
+ wm8915_detect(codec, &speyside_headset, speyside_set_polarity);
+
+ return 0;
+}
+
+static int speyside_late_probe(struct snd_soc_card *card)
+{
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
+
+ return 0;
+}
+
+static struct snd_soc_dai_link speyside_dai[] = {
+ {
+ .name = "CPU",
+ .stream_name = "CPU",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "wm8915-aif1",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8915.1-001a",
+ .init = speyside_wm8915_init,
+ .ops = &speyside_ops,
+ },
+ {
+ .name = "Baseband",
+ .stream_name = "Baseband",
+ .cpu_dai_name = "wm8915-aif2",
+ .codec_dai_name = "wm1250-ev1",
+ .codec_name = "wm1250-ev1.1-0027",
+ .ops = &speyside_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
+{
+ snd_soc_dapm_nc_pin(dapm, "LINEOUT");
+
+ /* At any time the WM9081 is active it will have this clock */
+ return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
+ 48000 * 256, 0);
+}
+
+static struct snd_soc_aux_dev speyside_aux_dev[] = {
+ {
+ .name = "wm9081",
+ .codec_name = "wm9081.1-006c",
+ .init = speyside_wm9081_init,
+ },
+};
+
+static struct snd_soc_codec_conf speyside_codec_conf[] = {
+ {
+ .dev_name = "wm9081.1-006c",
+ .name_prefix = "Sub",
+ },
+};
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Main Speaker"),
+ SOC_DAPM_PIN_SWITCH("Main DMIC"),
+ SOC_DAPM_PIN_SWITCH("Main AMIC"),
+ SOC_DAPM_PIN_SWITCH("WM1250 Input"),
+ SOC_DAPM_PIN_SWITCH("WM1250 Output"),
+};
+
+static struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+ SND_SOC_DAPM_SPK("Main Speaker", NULL),
+
+ SND_SOC_DAPM_MIC("Main AMIC", NULL),
+ SND_SOC_DAPM_MIC("Main DMIC", NULL),
+};
+
+static struct snd_soc_dapm_route audio_paths[] = {
+ { "IN1RN", NULL, "MICB1" },
+ { "IN1RP", NULL, "MICB1" },
+ { "IN1RN", NULL, "MICB2" },
+ { "IN1RP", NULL, "MICB2" },
+ { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
+ { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
+
+ { "IN1LP", NULL, "MICB2" },
+ { "IN1RN", NULL, "MICB1" },
+ { "MICB2", NULL, "Main AMIC" },
+
+ { "DMIC1DAT", NULL, "MICB1" },
+ { "DMIC2DAT", NULL, "MICB1" },
+ { "MICB1", NULL, "Main DMIC" },
+
+ { "Headphone", NULL, "HPOUT1L" },
+ { "Headphone", NULL, "HPOUT1R" },
+
+ { "Sub IN1", NULL, "HPOUT2L" },
+ { "Sub IN2", NULL, "HPOUT2R" },
+
+ { "Main Speaker", NULL, "Sub SPKN" },
+ { "Main Speaker", NULL, "Sub SPKP" },
+ { "Main Speaker", NULL, "SPKDAT" },
+};
+
+static struct snd_soc_card speyside = {
+ .name = "Speyside",
+ .dai_link = speyside_dai,
+ .num_links = ARRAY_SIZE(speyside_dai),
+ .aux_dev = speyside_aux_dev,
+ .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
+ .codec_conf = speyside_codec_conf,
+ .num_configs = ARRAY_SIZE(speyside_codec_conf),
+
+ .set_bias_level = speyside_set_bias_level,
+
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+
+ .late_probe = speyside_late_probe,
+};
+
+static __devinit int speyside_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &speyside;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit speyside_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver speyside_driver = {
+ .driver = {
+ .name = "speyside",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = speyside_probe,
+ .remove = __devexit_p(speyside_remove),
+};
+
+static int __init speyside_audio_init(void)
+{
+ return platform_driver_register(&speyside_driver);
+}
+module_init(speyside_audio_init);
+
+static void __exit speyside_audio_exit(void)
+{
+ platform_driver_unregister(&speyside_driver);
+}
+module_exit(speyside_audio_exit);
+
+MODULE_DESCRIPTION("Speyside audio support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:speyside");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 23c0e83d4c1..4a9da6b5f4e 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -86,8 +86,8 @@
#define SE (1 << 0) /* Fix the master clock */
/* CLK_RST */
-#define B_CLK 0x00000010
-#define A_CLK 0x00000001
+#define CRB (1 << 4)
+#define CRA (1 << 0)
/* IO SHIFT / MACRO */
#define BI_SHIFT 12
@@ -146,11 +146,20 @@ struct fsi_priv {
void __iomem *base;
struct fsi_master *master;
- int chan_num;
struct fsi_stream playback;
struct fsi_stream capture;
+ int chan_num:16;
+ int clk_master:1;
+
long rate;
+
+ /* for suspend/resume */
+ u32 saved_do_fmt;
+ u32 saved_di_fmt;
+ u32 saved_ckg1;
+ u32 saved_ckg2;
+ u32 saved_out_sel;
};
struct fsi_core {
@@ -171,6 +180,14 @@ struct fsi_master {
struct fsi_core *core;
struct sh_fsi_platform_info *info;
spinlock_t lock;
+
+ /* for suspend/resume */
+ u32 saved_a_mclk;
+ u32 saved_b_mclk;
+ u32 saved_iemsk;
+ u32 saved_imsk;
+ u32 saved_clk_rst;
+ u32 saved_soft_rst;
};
/*
@@ -244,6 +261,11 @@ static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
return fsi->master;
}
+static int fsi_is_clk_master(struct fsi_priv *fsi)
+{
+ return fsi->clk_master;
+}
+
static int fsi_is_port_a(struct fsi_priv *fsi)
{
return fsi->master->base == fsi->base;
@@ -535,20 +557,45 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
}
/*
- * ctrl function
+ * clock function
*/
+#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1)
+#define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0)
+static void __fsi_module_clk_ctrl(struct fsi_master *master,
+ struct device *dev,
+ int enable)
+{
+ pm_runtime_get_sync(dev);
+
+ if (enable) {
+ /* enable only SR */
+ fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
+ fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
+ } else {
+ /* clear all registers */
+ fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
+ }
-static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
+ pm_runtime_put_sync(dev);
+}
+
+#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1)
+#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0)
+static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
{
- u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
struct fsi_master *master = fsi_get_master(fsi);
+ u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR;
+ u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
+ int is_master = fsi_is_clk_master(fsi);
- if (enable)
- fsi_master_mask_set(master, CLK_RST, val, val);
- else
- fsi_master_mask_set(master, CLK_RST, val, 0);
+ fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
+ if (is_master)
+ fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
+/*
+ * ctrl function
+ */
static void fsi_fifo_init(struct fsi_priv *fsi,
int is_play,
struct snd_soc_dai *dai)
@@ -601,18 +648,6 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
}
}
-static void fsi_soft_all_reset(struct fsi_master *master)
-{
- /* port AB reset */
- fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
- mdelay(10);
-
- /* soft reset */
- fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
- fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
- mdelay(10);
-}
-
static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
{
struct snd_pcm_runtime *runtime;
@@ -793,14 +828,13 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = fsi_is_play(substream);
struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate;
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
fsi_irq_disable(fsi, is_play);
- fsi_clk_ctrl(fsi, 0);
- set_rate = fsi_get_info_set_rate(master);
- if (set_rate && fsi->rate)
+ if (fsi_is_clk_master(fsi))
set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
+
fsi->rate = 0;
pm_runtime_put_sync(dai->dev);
@@ -821,8 +855,10 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
frames_to_bytes(runtime, runtime->period_size));
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
fsi_irq_enable(fsi, is_play);
+ fsi_port_start(fsi);
break;
case SNDRV_PCM_TRIGGER_STOP:
+ fsi_port_stop(fsi);
fsi_irq_disable(fsi, is_play);
fsi_stream_pop(fsi, is_play);
break;
@@ -876,6 +912,8 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
+ struct fsi_master *master = fsi_get_master(fsi);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
u32 flags = fsi_get_info_flags(fsi);
u32 data = 0;
int ret;
@@ -886,6 +924,7 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
data = DIMD | DOMD;
+ fsi->clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
@@ -893,6 +932,13 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
ret = -EINVAL;
goto set_fmt_exit;
}
+
+ if (fsi_is_clk_master(fsi) && !set_rate) {
+ dev_err(dai->dev, "platform doesn't have set_rate\n");
+ ret = -EINVAL;
+ goto set_fmt_exit;
+ }
+
fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* set format */
@@ -919,13 +965,12 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate;
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
int fsi_ver = master->core->ver;
long rate = params_rate(params);
int ret;
- set_rate = fsi_get_info_set_rate(master);
- if (!set_rate)
+ if (!fsi_is_clk_master(fsi))
return 0;
ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
@@ -987,7 +1032,6 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10);
- fsi_clk_ctrl(fsi, 1);
ret = 0;
}
@@ -1202,9 +1246,7 @@ static int fsi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
- pm_runtime_get_sync(&pdev->dev);
- fsi_soft_all_reset(master);
- pm_runtime_put_sync(&pdev->dev);
+ fsi_module_init(master, &pdev->dev);
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
id_entry->name, master);
@@ -1248,6 +1290,8 @@ static int fsi_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev);
+ fsi_module_kill(master, &pdev->dev);
+
free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev);
@@ -1260,6 +1304,82 @@ static int fsi_remove(struct platform_device *pdev)
return 0;
}
+static void __fsi_suspend(struct fsi_priv *fsi,
+ struct device *dev,
+ set_rate_func set_rate)
+{
+ fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT);
+ fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT);
+ fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1);
+ fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2);
+ fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL);
+
+ if (fsi_is_clk_master(fsi))
+ set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
+}
+
+static void __fsi_resume(struct fsi_priv *fsi,
+ struct device *dev,
+ set_rate_func set_rate)
+{
+ fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt);
+ fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt);
+ fsi_reg_write(fsi, CKG1, fsi->saved_ckg1);
+ fsi_reg_write(fsi, CKG2, fsi->saved_ckg2);
+ fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel);
+
+ if (fsi_is_clk_master(fsi))
+ set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1);
+}
+
+static int fsi_suspend(struct device *dev)
+{
+ struct fsi_master *master = dev_get_drvdata(dev);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+
+ pm_runtime_get_sync(dev);
+
+ __fsi_suspend(&master->fsia, dev, set_rate);
+ __fsi_suspend(&master->fsib, dev, set_rate);
+
+ master->saved_a_mclk = fsi_core_read(master, a_mclk);
+ master->saved_b_mclk = fsi_core_read(master, b_mclk);
+ master->saved_iemsk = fsi_core_read(master, iemsk);
+ master->saved_imsk = fsi_core_read(master, imsk);
+ master->saved_clk_rst = fsi_master_read(master, CLK_RST);
+ master->saved_soft_rst = fsi_master_read(master, SOFT_RST);
+
+ fsi_module_kill(master, dev);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int fsi_resume(struct device *dev)
+{
+ struct fsi_master *master = dev_get_drvdata(dev);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+
+ pm_runtime_get_sync(dev);
+
+ fsi_module_init(master, dev);
+
+ fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst);
+ fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst);
+ fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk);
+ fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk);
+ fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk);
+ fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk);
+
+ __fsi_resume(&master->fsia, dev, set_rate);
+ __fsi_resume(&master->fsib, dev, set_rate);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
static int fsi_runtime_nop(struct device *dev)
{
/* Runtime PM callback shared between ->runtime_suspend()
@@ -1273,6 +1393,8 @@ static int fsi_runtime_nop(struct device *dev)
}
static struct dev_pm_ops fsi_pm_ops = {
+ .suspend = fsi_suspend,
+ .resume = fsi_resume,
.runtime_suspend = fsi_runtime_nop,
.runtime_resume = fsi_runtime_nop,
};
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 5d76da43b14..06b7b81a160 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -20,40 +20,28 @@
#include <trace/events/asoc.h>
-static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
- unsigned int reg)
+#ifdef CONFIG_SPI_MASTER
+static int do_spi_write(void *control, const char *data, int len)
{
+ struct spi_device *spi = control;
int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
- ret = snd_soc_cache_read(codec, reg, &val);
+ ret = spi_write(spi, data, len);
if (ret < 0)
- return -1;
- return val;
+ return ret;
+
+ return len;
}
+#endif
-static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value, const void *data, int len)
{
- u8 data[2];
int ret;
- data[0] = (reg << 4) | ((value >> 8) & 0x000f);
- data[1] = value & 0x00ff;
-
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
@@ -64,8 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret == 2)
+ ret = codec->hw_write(codec->control_data, data, len);
+ if (ret == len)
return 0;
if (ret < 0)
return ret;
@@ -73,50 +61,19 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_4_12_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[1];
- msg[1] = data[0];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-#else
-#define snd_soc_4_12_spi_write NULL
-#endif
-
-static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
+ if (codec->cache_only)
+ return -1;
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
+ BUG_ON(!codec->hw_read);
+ return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
@@ -125,259 +82,117 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
return val;
}
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
+ unsigned int reg)
{
- u8 data[2];
- int ret;
-
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret == 2)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
+ return do_hw_read(codec, reg);
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_7_9_spi_write(void *control_data, const char *data,
- int len)
+static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
+ u16 data;
- if (len <= 0)
- return 0;
+ data = cpu_to_be16((reg << 12) | (value & 0xffffff));
- msg[0] = data[0];
- msg[1] = data[1];
+ return do_hw_write(codec, reg, value, &data, 2);
+}
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
+static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return do_hw_read(codec, reg);
+}
- t.tx_buf = &msg[0];
- t.len = len;
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
- return len;
+ return do_hw_write(codec, reg, value, data, 2);
}
-#else
-#define snd_soc_7_9_spi_write NULL
-#endif
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
- int ret;
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
+ return do_hw_write(codec, reg, value, data, 2);
}
static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- reg &= 0xff;
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_8_8_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_read(codec, reg);
}
-#else
-#define snd_soc_8_8_spi_write NULL
-#endif
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
- int ret;
data[0] = reg;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- if (codec->hw_write(codec->control_data, data, 3) == 3)
- return 0;
- else
- return -EIO;
+ return do_hw_write(codec, reg, value, data, 3);
}
static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_8_16_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[3];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_read(codec, reg);
}
-#else
-#define snd_soc_8_16_spi_write NULL
-#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
- unsigned int r)
+static unsigned int do_i2c_read(struct snd_soc_codec *codec,
+ void *reg, int reglen,
+ void *data, int datalen)
{
struct i2c_msg xfer[2];
- u8 reg = r;
- u8 data;
int ret;
struct i2c_client *client = codec->control_data;
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
+ xfer[0].len = reglen;
+ xfer[0].buf = reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
- xfer[1].len = 1;
- xfer[1].buf = &data;
+ xfer[1].len = datalen;
+ xfer[1].buf = data;
ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ if (ret == 2)
return 0;
- }
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+#endif
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
+ unsigned int r)
+{
+ u8 reg = r;
+ u8 data;
+ int ret;
+
+ ret = do_i2c_read(codec, &reg, 1, &data, 1);
+ if (ret < 0)
+ return 0;
return data;
}
#else
@@ -388,30 +203,13 @@ static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u8 reg = r;
u16 data;
int ret;
- struct i2c_client *client = codec->control_data;
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, &reg, 1, &data, 2);
+ if (ret < 0)
return 0;
- }
-
return (data >> 8) | ((data & 0xff) << 8);
}
#else
@@ -422,30 +220,13 @@ static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u16 reg = r;
u8 data;
int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (u8 *)&reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 1;
- xfer[1].buf = &data;
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, &reg, 2, &data, 1);
+ if (ret < 0)
return 0;
- }
-
return data;
}
#else
@@ -453,120 +234,34 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
#endif
static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
- unsigned int reg)
+ unsigned int reg)
{
- int ret;
- unsigned int val;
-
- reg &= 0xff;
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
+ return do_hw_read(codec, reg);
}
static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+ unsigned int value)
{
u8 data[3];
- int ret;
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = value;
- reg &= 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- ret = codec->hw_write(codec->control_data, data, 3);
- if (ret == 3)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_8_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[3];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_write(codec, reg, value, data, 3);
}
-#else
-#define snd_soc_16_8_spi_write NULL
-#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u16 reg = cpu_to_be16(r);
u16 data;
int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (u8 *)&reg;
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, &reg, 2, &data, 2);
+ if (ret < 0)
return 0;
- }
-
return be16_to_cpu(data);
}
#else
@@ -576,52 +271,59 @@ static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
-
- return val;
+ return do_hw_read(codec, reg);
}
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[4];
- int ret;
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
+ return do_hw_write(codec, reg, value, data, 4);
+}
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
+/* Primitive bulk write support for soc-cache. The data pointed to by
+ * `data' needs to already be in the form the hardware expects
+ * including any leading register specific data. Any data written
+ * through this function will not go through the cache as it only
+ * handles writing to volatile or out of bounds registers.
+ */
+static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
+ const void *data, size_t len)
+{
+ int ret;
+
+ /* To ensure that we don't get out of sync with the cache, check
+ * whether the base register is volatile or if we've directly asked
+ * to bypass the cache. Out of bounds registers are considered
+ * volatile.
+ */
+ if (!codec->cache_bypass
+ && !snd_soc_codec_volatile_register(codec, reg)
+ && reg < codec->driver->reg_cache_size)
+ return -EINVAL;
+
+ switch (codec->control_type) {
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ case SND_SOC_I2C:
+ ret = i2c_master_send(codec->control_data, data, len);
+ break;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ case SND_SOC_SPI:
+ ret = spi_write(codec->control_data, data, len);
+ break;
+#endif
+ default:
+ BUG();
}
- ret = codec->hw_write(codec->control_data, data, 4);
- if (ret == 4)
+ if (ret == len)
return 0;
if (ret < 0)
return ret;
@@ -629,79 +331,40 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_16_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[4];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
- msg[3] = data[3];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-#else
-#define snd_soc_16_16_spi_write NULL
-#endif
-
static struct {
int addr_bits;
int data_bits;
int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
- int (*spi_write)(void *, const char *, int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
{
.addr_bits = 4, .data_bits = 12,
.write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
- .spi_write = snd_soc_4_12_spi_write,
},
{
.addr_bits = 7, .data_bits = 9,
.write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
- .spi_write = snd_soc_7_9_spi_write,
},
{
.addr_bits = 8, .data_bits = 8,
.write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
.i2c_read = snd_soc_8_8_read_i2c,
- .spi_write = snd_soc_8_8_spi_write,
},
{
.addr_bits = 8, .data_bits = 16,
.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
.i2c_read = snd_soc_8_16_read_i2c,
- .spi_write = snd_soc_8_16_spi_write,
},
{
.addr_bits = 16, .data_bits = 8,
.write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
.i2c_read = snd_soc_16_8_read_i2c,
- .spi_write = snd_soc_16_8_spi_write,
},
{
.addr_bits = 16, .data_bits = 16,
.write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
.i2c_read = snd_soc_16_16_read_i2c,
- .spi_write = snd_soc_16_16_spi_write,
},
};
@@ -709,7 +372,6 @@ static struct {
* snd_soc_codec_set_cache_io: Set up standard I/O functions.
*
* @codec: CODEC to configure.
- * @type: Type of cache.
* @addr_bits: Number of bits of register address data.
* @data_bits: Number of bits of data per register.
* @control: Control bus used.
@@ -744,6 +406,7 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
codec->write = io_types[i].write;
codec->read = io_types[i].read;
+ codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
switch (control) {
case SND_SOC_CUSTOM:
@@ -762,8 +425,9 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
break;
case SND_SOC_SPI:
- if (io_types[i].spi_write)
- codec->hw_write = io_types[i].spi_write;
+#ifdef CONFIG_SPI_MASTER
+ codec->hw_write = do_spi_write;
+#endif
codec->control_data = container_of(codec->dev,
struct spi_device,
@@ -889,6 +553,8 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
if (rbnode->value == rbnode->defval)
continue;
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, rbnode->reg));
ret = snd_soc_cache_read(codec, rbnode->reg, &val);
if (ret)
return ret;
@@ -1149,6 +815,8 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
lzo_blocks = codec->reg_cache;
for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
@@ -1407,6 +1075,8 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
codec_drv = codec->driver;
for (i = 0; i < codec_drv->reg_cache_size; ++i) {
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
@@ -1523,7 +1193,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
codec->cache_ops->name, codec->name);
return codec->cache_ops->init(codec);
}
- return -EINVAL;
+ return -ENOSYS;
}
/*
@@ -1538,7 +1208,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
codec->cache_ops->name, codec->name);
return codec->cache_ops->exit(codec);
}
- return -EINVAL;
+ return -ENOSYS;
}
/**
@@ -1562,7 +1232,7 @@ int snd_soc_cache_read(struct snd_soc_codec *codec,
}
mutex_unlock(&codec->cache_rw_mutex);
- return -EINVAL;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_read);
@@ -1587,7 +1257,7 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
}
mutex_unlock(&codec->cache_rw_mutex);
- return -EINVAL;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_write);
@@ -1610,7 +1280,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec)
}
if (!codec->cache_ops || !codec->cache_ops->sync)
- return -EINVAL;
+ return -ENOSYS;
if (codec->cache_ops->name)
name = codec->cache_ops->name;
@@ -1677,3 +1347,17 @@ int snd_soc_default_readable_register(struct snd_soc_codec *codec,
return codec->driver->reg_access_default[index].read;
}
EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
+
+int snd_soc_default_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ int index;
+
+ if (reg >= codec->driver->reg_cache_size)
+ return 1;
+ index = snd_soc_get_reg_access_index(codec, reg);
+ if (index < 0)
+ return 0;
+ return codec->driver->reg_access_default[index].write;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_writable_register);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index dd55d106946..bb7cd581294 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -242,7 +242,7 @@ static ssize_t codec_reg_write_file(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[32];
- int buf_size;
+ size_t buf_size;
char *start = buf;
unsigned long reg, value;
int step = 1;
@@ -302,13 +302,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
printk(KERN_WARNING
"ASoC: Failed to create codec register debugfs file\n");
- codec->dapm.debugfs_dapm = debugfs_create_dir("dapm",
- codec->debugfs_codec_root);
- if (!codec->dapm.debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(&codec->dapm);
+ snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
}
static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
@@ -555,7 +549,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
}
}
- if (platform->driver->ops->open) {
+ if (platform->driver->ops && platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
@@ -685,7 +679,7 @@ machine_err:
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
- if (platform->driver->ops->close)
+ if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
@@ -767,7 +761,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
- if (platform->driver->ops->close)
+ if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
cpu_dai->runtime = NULL;
@@ -810,7 +804,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
}
- if (platform->driver->ops->prepare) {
+ if (platform->driver->ops && platform->driver->ops->prepare) {
ret = platform->driver->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: platform prepare error\n");
@@ -899,7 +893,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
}
- if (platform->driver->ops->hw_params) {
+ if (platform->driver->ops && platform->driver->ops->hw_params) {
ret = platform->driver->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: platform %s hw params failed\n",
@@ -952,7 +946,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
rtd->dai_link->ops->hw_free(substream);
/* free any DMA resources */
- if (platform->driver->ops->hw_free)
+ if (platform->driver->ops && platform->driver->ops->hw_free)
platform->driver->ops->hw_free(substream);
/* now free hw params for the DAIs */
@@ -980,7 +974,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
- if (platform->driver->ops->trigger) {
+ if (platform->driver->ops && platform->driver->ops->trigger) {
ret = platform->driver->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
@@ -1009,7 +1003,7 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
- if (platform->driver->ops->pointer)
+ if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
if (cpu_dai->driver->ops->delay)
@@ -1299,6 +1293,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai, *cpu_dai;
+ const char *platform_name;
if (rtd->complete)
return 1;
@@ -1351,13 +1346,18 @@ find_codec:
dai_link->codec_name);
find_platform:
- /* do we already have the CODEC DAI for this link ? */
- if (rtd->platform) {
+ /* do we need a platform? */
+ if (rtd->platform)
goto out;
- }
- /* no, then find CPU DAI from registered DAIs*/
+
+ /* if there's no platform we match on the empty platform */
+ platform_name = dai_link->platform_name;
+ if (!platform_name)
+ platform_name = "snd-soc-dummy";
+
+ /* no, then find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
- if (!strcmp(platform->name, dai_link->platform_name)) {
+ if (!strcmp(platform->name, platform_name)) {
rtd->platform = platform;
goto out;
}
@@ -1453,6 +1453,16 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
}
+static void soc_remove_dai_links(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++)
+ soc_remove_dai_link(card, i);
+
+ card->num_rtd = 0;
+}
+
static void soc_set_name_prefix(struct snd_soc_card *card,
struct snd_soc_codec *codec)
{
@@ -1483,6 +1493,12 @@ static int soc_probe_codec(struct snd_soc_card *card,
if (!try_module_get(codec->dev->driver->owner))
return -ENODEV;
+ soc_init_codec_debugfs(codec);
+
+ if (driver->dapm_widgets)
+ snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
+ driver->num_dapm_widgets);
+
if (driver->probe) {
ret = driver->probe(codec);
if (ret < 0) {
@@ -1493,15 +1509,13 @@ static int soc_probe_codec(struct snd_soc_card *card,
}
}
- if (driver->dapm_widgets)
- snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
- driver->num_dapm_widgets);
+ if (driver->controls)
+ snd_soc_add_controls(codec, driver->controls,
+ driver->num_controls);
if (driver->dapm_routes)
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
driver->num_dapm_routes);
- soc_init_codec_debugfs(codec);
-
/* mark codec as probed and add to card codec list */
codec->probed = 1;
list_add(&codec->card_list, &card->codec_dev_list);
@@ -1510,6 +1524,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
return 0;
err_probe:
+ soc_cleanup_codec_debugfs(codec);
module_put(codec->dev->driver->owner);
return ret;
@@ -1860,11 +1875,19 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);
+#ifdef CONFIG_DEBUG_FS
+ snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
+#endif
+
#ifdef CONFIG_PM_SLEEP
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
+ if (card->dapm_widgets)
+ snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
+ card->num_dapm_widgets);
+
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
@@ -1890,27 +1913,24 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
}
}
- if (card->dapm_widgets)
- snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
- card->num_dapm_widgets);
+ /* We should have a non-codec control add function but we don't */
+ if (card->controls)
+ snd_soc_add_controls(list_first_entry(&card->codec_dev_list,
+ struct snd_soc_codec,
+ card_list),
+ card->controls,
+ card->num_controls);
+
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
-#ifdef CONFIG_DEBUG_FS
- card->dapm.debugfs_dapm = debugfs_create_dir("dapm",
- card->debugfs_card_root);
- if (!card->dapm.debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create card DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(&card->dapm);
-#endif
-
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
- "%s", card->name);
- snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->name);
+ snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
+ "%s", card->long_name ? card->long_name : card->name);
+ snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
+ "%s", card->driver_name ? card->driver_name : card->name);
if (card->late_probe) {
ret = card->late_probe(card);
@@ -1949,8 +1969,7 @@ probe_aux_dev_err:
soc_remove_aux_dev(card, i);
probe_dai_err:
- for (i = 0; i < card->num_links; i++)
- soc_remove_dai_link(card, i);
+ soc_remove_dai_links(card);
card_probe_error:
if (card->remove)
@@ -2012,8 +2031,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
soc_remove_aux_dev(card, i);
/* remove and free each DAI */
- for (i = 0; i < card->num_rtd; i++)
- soc_remove_dai_link(card, i);
+ soc_remove_dai_links(card);
soc_cleanup_card_debugfs(card);
@@ -2021,6 +2039,8 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
if (card->remove)
card->remove(card);
+ snd_soc_dapm_free(&card->dapm);
+
kfree(card->rtd);
snd_card_free(card->snd_card);
return 0;
@@ -2105,13 +2125,15 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->pcm = pcm;
pcm->private_data = rtd;
- soc_pcm_ops.mmap = platform->driver->ops->mmap;
- soc_pcm_ops.pointer = platform->driver->ops->pointer;
- soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
- soc_pcm_ops.copy = platform->driver->ops->copy;
- soc_pcm_ops.silence = platform->driver->ops->silence;
- soc_pcm_ops.ack = platform->driver->ops->ack;
- soc_pcm_ops.page = platform->driver->ops->page;
+ if (platform->driver->ops) {
+ soc_pcm_ops.mmap = platform->driver->ops->mmap;
+ soc_pcm_ops.pointer = platform->driver->ops->pointer;
+ soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
+ soc_pcm_ops.copy = platform->driver->ops->copy;
+ soc_pcm_ops.silence = platform->driver->ops->silence;
+ soc_pcm_ops.ack = platform->driver->ops->ack;
+ soc_pcm_ops.page = platform->driver->ops->page;
+ }
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
@@ -2119,10 +2141,13 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
- ret = platform->driver->pcm_new(rtd->card->snd_card, codec_dai, pcm);
- if (ret < 0) {
- printk(KERN_ERR "asoc: platform pcm constructor failed\n");
- return ret;
+ if (platform->driver->pcm_new) {
+ ret = platform->driver->pcm_new(rtd->card->snd_card,
+ codec_dai, pcm);
+ if (ret < 0) {
+ pr_err("asoc: platform pcm constructor failed\n");
+ return ret;
+ }
}
pcm->private_free = platform->driver->pcm_free;
@@ -2150,6 +2175,42 @@ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
/**
+ * snd_soc_codec_readable_register: Report if a register is readable.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indicating if a CODEC register is readable.
+ */
+int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ if (codec->readable_register)
+ return codec->readable_register(codec, reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register);
+
+/**
+ * snd_soc_codec_writable_register: Report if a register is writable.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indicating if a CODEC register is writable.
+ */
+int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ if (codec->writable_register)
+ return codec->writable_register(codec, reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
+
+/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
* @ops: AC97 bus operations
@@ -2231,6 +2292,13 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_write);
+unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
+ unsigned int reg, const void *data, size_t len)
+{
+ return codec->bulk_write_raw(codec, reg, data, len);
+}
+EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw);
+
/**
* snd_soc_update_bits - update codec register bits
* @codec: audio codec
@@ -3414,7 +3482,7 @@ int snd_soc_register_dai(struct device *dev,
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
if (dai == NULL)
- return -ENOMEM;
+ return -ENOMEM;
/* create DAI component name */
dai->name = fmt_single_name(dev, &dai->id);
@@ -3553,7 +3621,7 @@ int snd_soc_register_platform(struct device *dev,
platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
if (platform == NULL)
- return -ENOMEM;
+ return -ENOMEM;
/* create platform component name */
platform->name = fmt_single_name(dev, &platform->id);
@@ -3671,6 +3739,7 @@ int snd_soc_register_codec(struct device *dev,
codec->read = codec_drv->read;
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
+ codec->writable_register = codec_drv->writable_register;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
@@ -3705,6 +3774,8 @@ int snd_soc_register_codec(struct device *dev,
codec->volatile_register = snd_soc_default_volatile_register;
if (!codec->readable_register)
codec->readable_register = snd_soc_default_readable_register;
+ if (!codec->writable_register)
+ codec->writable_register = snd_soc_default_writable_register;
}
for (i = 0; i < num_dai; i++) {
@@ -3793,12 +3864,16 @@ static int __init snd_soc_init(void)
pr_warn("ASoC: Failed to create platform list debugfs file\n");
#endif
+ snd_soc_util_init();
+
return platform_driver_register(&soc_driver);
}
module_init(snd_soc_init);
static void __exit snd_soc_exit(void)
{
+ snd_soc_util_exit();
+
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(snd_soc_debugfs_root);
#endif
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 81c4052c127..456617e6378 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -187,7 +187,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_mixer_named_ctl: {
int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
- w->kcontrols[i].private_value;
+ w->kcontrol_news[i].private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
@@ -204,7 +204,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
}
break;
case snd_soc_dapm_mux: {
- struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ struct soc_enum *e = (struct soc_enum *)
+ w->kcontrol_news[i].private_value;
int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@@ -220,7 +221,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
}
break;
case snd_soc_dapm_virt_mux: {
- struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ struct soc_enum *e = (struct soc_enum *)
+ w->kcontrol_news[i].private_value;
p->connect = 0;
/* since a virtual mux has no backing registers to
@@ -235,7 +237,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
break;
case snd_soc_dapm_value_mux: {
struct soc_enum *e = (struct soc_enum *)
- w->kcontrols[i].private_value;
+ w->kcontrol_news[i].private_value;
int val, item;
val = snd_soc_read(w->codec, e->reg);
@@ -310,11 +312,11 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
/* search for mixer kcontrol */
for (i = 0; i < dest->num_kcontrols; i++) {
- if (!strcmp(control_name, dest->kcontrols[i].name)) {
+ if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &dest->sources);
list_add(&path->list_source, &src->sinks);
- path->name = dest->kcontrols[i].name;
+ path->name = dest->kcontrol_news[i].name;
dapm_set_path_status(dest, path, i);
return 0;
}
@@ -322,43 +324,26 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
return -ENODEV;
}
-/* update dapm codec register bits */
-static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
+static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
+ const struct snd_kcontrol_new *kcontrol_new,
+ struct snd_kcontrol **kcontrol)
{
- int change, power;
- unsigned int old, new;
- struct snd_soc_codec *codec = widget->codec;
- struct snd_soc_dapm_context *dapm = widget->dapm;
- struct snd_soc_card *card = dapm->card;
-
- /* check for valid widgets */
- if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
- widget->id == snd_soc_dapm_output ||
- widget->id == snd_soc_dapm_hp ||
- widget->id == snd_soc_dapm_mic ||
- widget->id == snd_soc_dapm_line ||
- widget->id == snd_soc_dapm_spk)
- return 0;
-
- power = widget->power;
- if (widget->invert)
- power = (power ? 0:1);
+ struct snd_soc_dapm_widget *w;
+ int i;
- old = snd_soc_read(codec, widget->reg);
- new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
+ *kcontrol = NULL;
- change = old != new;
- if (change) {
- pop_dbg(dapm->dev, card->pop_time,
- "pop test %s : %s in %d ms\n",
- widget->name, widget->power ? "on" : "off",
- card->pop_time);
- pop_wait(card->pop_time);
- snd_soc_write(codec, widget->reg, new);
+ list_for_each_entry(w, &dapm->card->widgets, list) {
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (&w->kcontrol_news[i] == kcontrol_new) {
+ if (w->kcontrols)
+ *kcontrol = w->kcontrols[i];
+ return 1;
+ }
+ }
}
- dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg,
- old, new, change);
- return change;
+
+ return 0;
}
/* create new dapm mixer control */
@@ -370,6 +355,8 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path;
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
+ struct snd_soc_dapm_widget_list *wlist;
+ size_t wlistsize;
if (dapm->codec)
prefix = dapm->codec->name_prefix;
@@ -388,23 +375,37 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */
- if (path->name != (char*)w->kcontrols[i].name)
+ if (path->name != (char *)w->kcontrol_news[i].name)
continue;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ sizeof(struct snd_soc_dapm_widget *),
+ wlist = kzalloc(wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev,
+ "asoc: can't allocate widget list for %s\n",
+ w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = 1;
+ wlist->widgets[0] = w;
+
/* add dapm control with long name.
* for dapm_mixer this is the concatenation of the
* mixer and kcontrol name.
* for dapm_mixer_named_ctl this is simply the
* kcontrol name.
*/
- name_len = strlen(w->kcontrols[i].name) + 1;
+ name_len = strlen(w->kcontrol_news[i].name) + 1;
if (w->id != snd_soc_dapm_mixer_named_ctl)
name_len += 1 + strlen(w->name);
path->long_name = kmalloc(name_len, GFP_KERNEL);
- if (path->long_name == NULL)
+ if (path->long_name == NULL) {
+ kfree(wlist);
return -ENOMEM;
+ }
switch (w->id) {
default:
@@ -416,27 +417,30 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
*/
snprintf(path->long_name, name_len, "%s %s",
w->name + prefix_len,
- w->kcontrols[i].name);
+ w->kcontrol_news[i].name);
break;
case snd_soc_dapm_mixer_named_ctl:
snprintf(path->long_name, name_len, "%s",
- w->kcontrols[i].name);
+ w->kcontrol_news[i].name);
break;
}
path->long_name[name_len - 1] = '\0';
- path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
- path->long_name, prefix);
+ path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
+ wlist, path->long_name,
+ prefix);
ret = snd_ctl_add(card, path->kcontrol);
if (ret < 0) {
dev_err(dapm->dev,
"asoc: failed to add dapm kcontrol %s: %d\n",
path->long_name, ret);
+ kfree(wlist);
kfree(path->long_name);
path->long_name = NULL;
return ret;
}
+ w->kcontrols[i] = path->kcontrol;
}
}
return ret;
@@ -451,42 +455,80 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
size_t prefix_len;
- int ret = 0;
-
- if (!w->num_kcontrols) {
- dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name);
+ int ret;
+ struct snd_soc_dapm_widget_list *wlist;
+ int shared, wlistentries;
+ size_t wlistsize;
+ char *name;
+
+ if (w->num_kcontrols != 1) {
+ dev_err(dapm->dev,
+ "asoc: mux %s has incorrect number of controls\n",
+ w->name);
return -EINVAL;
}
- if (dapm->codec)
- prefix = dapm->codec->name_prefix;
- else
- prefix = NULL;
+ shared = dapm_is_shared_kcontrol(dapm, &w->kcontrol_news[0],
+ &kcontrol);
+ if (kcontrol) {
+ wlist = kcontrol->private_data;
+ wlistentries = wlist->num_widgets + 1;
+ } else {
+ wlist = NULL;
+ wlistentries = 1;
+ }
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ wlistentries * sizeof(struct snd_soc_dapm_widget *),
+ wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev,
+ "asoc: can't allocate widget list for %s\n", w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = wlistentries;
+ wlist->widgets[wlistentries - 1] = w;
- if (prefix)
- prefix_len = strlen(prefix) + 1;
- else
- prefix_len = 0;
+ if (!kcontrol) {
+ if (dapm->codec)
+ prefix = dapm->codec->name_prefix;
+ else
+ prefix = NULL;
+
+ if (shared) {
+ name = w->kcontrol_news[0].name;
+ prefix_len = 0;
+ } else {
+ name = w->name;
+ if (prefix)
+ prefix_len = strlen(prefix) + 1;
+ else
+ prefix_len = 0;
+ }
- /* The control will get a prefix from the control creation
- * process but we're also using the same prefix for widgets so
- * cut the prefix off the front of the widget name.
- */
- kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len,
- prefix);
- ret = snd_ctl_add(card, kcontrol);
+ /*
+ * The control will get a prefix from the control creation
+ * process but we're also using the same prefix for widgets so
+ * cut the prefix off the front of the widget name.
+ */
+ kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
+ name + prefix_len, prefix);
+ ret = snd_ctl_add(card, kcontrol);
+ if (ret < 0) {
+ dev_err(dapm->dev,
+ "asoc: failed to add kcontrol %s\n", w->name);
+ kfree(wlist);
+ return ret;
+ }
+ }
- if (ret < 0)
- goto err;
+ kcontrol->private_data = wlist;
+
+ w->kcontrols[0] = kcontrol;
list_for_each_entry(path, &w->sources, list_sink)
path->kcontrol = kcontrol;
- return ret;
-
-err:
- dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
- return ret;
+ return 0;
}
/* create new dapm volume control */
@@ -644,57 +686,6 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(dapm_reg_event);
-/* Standard power change method, used to apply power changes to most
- * widgets.
- */
-static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
-{
- int ret;
-
- /* call any power change event handlers */
- if (w->event)
- dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n",
- w->power ? "on" : "off",
- w->name, w->event_flags);
-
- /* power up pre event */
- if (w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down pre event */
- if (!w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
- if (ret < 0)
- return ret;
- }
-
- dapm_update_bits(w);
-
- /* power up post event */
- if (w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down post event */
- if (!w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
/* Generic check to see if a widget should be powered.
*/
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
@@ -981,16 +972,6 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
NULL, SND_SOC_DAPM_POST_PMD);
break;
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
- case snd_soc_dapm_line:
- case snd_soc_dapm_spk:
- /* No register support currently */
- ret = dapm_generic_apply_power(w);
- break;
-
default:
/* Queue it up for application */
cur_sort = sort[w->id];
@@ -1201,6 +1182,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
}
}
+ /* Force all contexts in the card to the same bias state */
+ power = 0;
+ list_for_each_entry(d, &card->dapm_list, list)
+ if (d->dev_power)
+ power = 1;
+ list_for_each_entry(d, &card->dapm_list, list)
+ d->dev_power = power;
+
+
/* Run all the bias changes in parallel */
list_for_each_entry(d, &dapm->card->dapm_list, list)
async_schedule_domain(dapm_pre_sequence_async, d,
@@ -1304,31 +1294,104 @@ static const struct file_operations dapm_widget_power_fops = {
.llseek = default_llseek,
};
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+static int dapm_bias_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_dapm_context *dapm = file->private_data;
+ char *level;
+
+ switch (dapm->bias_level) {
+ case SND_SOC_BIAS_ON:
+ level = "On\n";
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ level = "Prepare\n";
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ level = "Standby\n";
+ break;
+ case SND_SOC_BIAS_OFF:
+ level = "Off\n";
+ break;
+ default:
+ BUG();
+ level = "Unknown\n";
+ break;
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, level,
+ strlen(level));
+}
+
+static const struct file_operations dapm_bias_fops = {
+ .open = dapm_bias_open_file,
+ .read = dapm_bias_read_file,
+ .llseek = default_llseek,
+};
+
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent)
{
- struct snd_soc_dapm_widget *w;
struct dentry *d;
- if (!dapm->debugfs_dapm)
+ dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
+
+ if (!dapm->debugfs_dapm) {
+ printk(KERN_WARNING
+ "Failed to create DAPM debugfs directory\n");
return;
+ }
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (!w->name || w->dapm != dapm)
- continue;
+ d = debugfs_create_file("bias_level", 0444,
+ dapm->debugfs_dapm, dapm,
+ &dapm_bias_fops);
+ if (!d)
+ dev_warn(dapm->dev,
+ "ASoC: Failed to create bias level debugfs file\n");
+}
- d = debugfs_create_file(w->name, 0444,
- dapm->debugfs_dapm, w,
- &dapm_widget_power_fops);
- if (!d)
- dev_warn(w->dapm->dev,
- "ASoC: Failed to create %s debugfs file\n",
- w->name);
- }
+static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct dentry *d;
+
+ if (!dapm->debugfs_dapm || !w->name)
+ return;
+
+ d = debugfs_create_file(w->name, 0444,
+ dapm->debugfs_dapm, w,
+ &dapm_widget_power_fops);
+ if (!d)
+ dev_warn(w->dapm->dev,
+ "ASoC: Failed to create %s debugfs file\n",
+ w->name);
+}
+
+static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
+{
+ debugfs_remove_recursive(dapm->debugfs_dapm);
}
+
#else
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent)
+{
+}
+
+static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+}
+
+static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
}
+
#endif
/* test and update the power status of a mux widget */
@@ -1496,32 +1559,49 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
kfree(p->long_name);
kfree(p);
}
+ kfree(w->kcontrols);
kfree(w->name);
kfree(w);
}
}
-static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
- const char *pin, int status)
+static struct snd_soc_dapm_widget *dapm_find_widget(
+ struct snd_soc_dapm_context *dapm, const char *pin,
+ bool search_other_contexts)
{
struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *fallback = NULL;
list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
if (!strcmp(w->name, pin)) {
- dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n",
- pin, status);
- w->connected = status;
- /* Allow disabling of forced pins */
- if (status == 0)
- w->force = 0;
- return 0;
+ if (w->dapm == dapm)
+ return w;
+ else
+ fallback = w;
}
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ if (search_other_contexts)
+ return fallback;
+
+ return NULL;
+}
+
+static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
+ const char *pin, int status)
+{
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
+ }
+
+ w->connected = status;
+ if (status == 0)
+ w->force = 0;
+
+ return 0;
}
/**
@@ -1627,7 +1707,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
}
/* connect dynamic paths */
- switch(wsink->id) {
+ switch (wsink->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
case snd_soc_dapm_pga:
@@ -1650,7 +1730,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
- &wsink->kcontrols[0]);
+ &wsink->kcontrol_news[0]);
if (ret != 0)
goto err;
break;
@@ -1730,6 +1810,14 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
if (w->new)
continue;
+ if (w->num_kcontrols) {
+ w->kcontrols = kzalloc(w->num_kcontrols *
+ sizeof(struct snd_kcontrol *),
+ GFP_KERNEL);
+ if (!w->kcontrols)
+ return -ENOMEM;
+ }
+
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
@@ -1785,6 +1873,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
}
w->new = 1;
+
+ dapm_debugfs_add_widget(w);
}
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
@@ -1804,7 +1894,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -1843,7 +1934,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
@@ -1854,6 +1947,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
+ int wi;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1862,31 +1956,36 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
mask = mask << shift;
val = val << shift;
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ if (val)
+ /* new connection */
+ connect = invert ? 0 : 1;
+ else
+ /* old connection must be powered down */
+ connect = invert ? 1 : 0;
+
+ mutex_lock(&codec->mutex);
change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) {
- if (val)
- /* new connection */
- connect = invert ? 0:1;
- else
- /* old connection must be powered down */
- connect = invert ? 1:0;
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mixer_update_power(widget, kcontrol, connect);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mixer_update_power(widget, kcontrol, connect);
+
+ widget->dapm->update = NULL;
+ }
}
- mutex_unlock(&widget->codec->mutex);
+ mutex_unlock(&codec->mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -1903,7 +2002,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, bitmask;
@@ -1931,11 +2031,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask, bitmask;
struct snd_soc_dapm_update update;
+ int wi;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
@@ -1951,22 +2054,29 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mask |= (bitmask - 1) << e->shift_r;
}
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ mutex_lock(&codec->mutex);
+
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
- mutex_unlock(&widget->codec->mutex);
+ widget->dapm->update = NULL;
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -1981,7 +2091,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
ucontrol->value.enumerated.item[0] = widget->value;
@@ -1999,22 +2110,33 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
int ret = 0;
+ int wi;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
- mutex_lock(&widget->codec->mutex);
+ mutex_lock(&codec->mutex);
change = widget->value != ucontrol->value.enumerated.item[0];
- widget->value = ucontrol->value.enumerated.item[0];
- dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- mutex_unlock(&widget->codec->mutex);
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ dapm_mux_update_power(widget, kcontrol, change,
+ widget->value, e);
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -2035,7 +2157,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val, mux;
@@ -2075,11 +2198,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
+ int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -2093,22 +2219,29 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mask |= e->mask << e->shift_r;
}
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ mutex_lock(&codec->mutex);
+
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ widget->dapm->update = NULL;
+ }
+ }
- mutex_unlock(&widget->codec->mutex);
+ mutex_unlock(&codec->mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@@ -2346,22 +2479,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin)) {
- dev_dbg(w->dapm->dev,
- "dapm: force enable pin %s\n", pin);
- w->connected = 1;
- w->force = 1;
- return 0;
- }
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
+ w->connected = 1;
+ w->force = 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
@@ -2413,14 +2542,10 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin))
- return w->connected;
- }
+ if (w)
+ return w->connected;
return 0;
}
@@ -2440,19 +2565,16 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin)) {
- w->ignore_suspend = 1;
- return 0;
- }
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ w->ignore_suspend = 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
@@ -2465,6 +2587,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
{
snd_soc_dapm_sys_remove(dapm->dev);
+ dapm_debugfs_cleanup(dapm);
dapm_free_widgets(dapm);
list_del(&dapm->list);
}
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index fc017c0a7b5..7c17b98d584 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -325,7 +325,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
gpio_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
- jack->codec->dev->driver->name,
+ gpios[i].name,
&gpios[i]);
if (ret)
goto err;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 3f45e6a439b..ec921ec99c2 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -13,6 +13,7 @@
* option) any later version.
*/
+#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -55,3 +56,55 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
+
+static struct snd_soc_platform_driver dummy_platform;
+
+static __devinit int snd_soc_dummy_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &dummy_platform);
+}
+
+static __devexit int snd_soc_dummy_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver soc_dummy_driver = {
+ .driver = {
+ .name = "snd-soc-dummy",
+ .owner = THIS_MODULE,
+ },
+ .probe = snd_soc_dummy_probe,
+ .remove = __devexit_p(snd_soc_dummy_remove),
+};
+
+static struct platform_device *soc_dummy_dev;
+
+int __init snd_soc_util_init(void)
+{
+ int ret;
+
+ soc_dummy_dev = platform_device_alloc("snd-soc-dummy", -1);
+ if (!soc_dummy_dev)
+ return -ENOMEM;
+
+ ret = platform_device_add(soc_dummy_dev);
+ if (ret != 0) {
+ platform_device_put(soc_dummy_dev);
+ return ret;
+ }
+
+ ret = platform_driver_register(&soc_dummy_driver);
+ if (ret != 0)
+ platform_device_unregister(soc_dummy_dev);
+
+ return ret;
+}
+
+void __exit snd_soc_util_exit(void)
+{
+ platform_device_unregister(soc_dummy_dev);
+ platform_driver_unregister(&soc_dummy_driver);
+}
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 66b504f06c2..035d39a4beb 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,26 +1,40 @@
-config SND_TEGRA_SOC
+config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
- default m
help
Say Y or M here if you want support for SoC audio on Tegra.
-config SND_TEGRA_SOC_I2S
+config SND_SOC_TEGRA_I2S
tristate
- depends on SND_TEGRA_SOC
- default m
+ depends on SND_SOC_TEGRA
help
Say Y or M if you want to add support for codecs attached to the
Tegra I2S interface. You will also need to select the individual
machine drivers to support below.
-config SND_TEGRA_SOC_HARMONY
- tristate "SoC Audio support for Tegra Harmony reference board"
- depends on SND_TEGRA_SOC && MACH_HARMONY && I2C
- default m
- select SND_TEGRA_SOC_I2S
+config MACH_HAS_SND_SOC_TEGRA_WM8903
+ bool
+ help
+ Machines that use the SND_SOC_TEGRA_WM8903 driver should select
+ this config option, in order to allow the user to enable
+ SND_SOC_TEGRA_WM8903.
+
+config SND_SOC_TEGRA_WM8903
+ tristate "SoC Audio support for Tegra boards using a WM8903 codec"
+ depends on SND_SOC_TEGRA && I2C
+ depends on MACH_HAS_SND_SOC_TEGRA_WM8903
+ select SND_SOC_TEGRA_I2S
select SND_SOC_WM8903
help
- Say Y or M here if you want to add support for SoC audio on the
- Tegra Harmony reference board.
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the WM8093 codec. Currently, the supported boards are
+ Harmony, Ventana, Seaboard, Kaen, and Aebl.
+config SND_SOC_TEGRA_TRIMSLICE
+ tristate "SoC Audio support for TrimSlice board"
+ depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
+ select SND_SOC_TEGRA_I2S
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y or M here if you want to add support for SoC audio on the
+ TrimSlice platform.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index fd183d3ab4f..fa6574d92a3 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -4,12 +4,14 @@ snd-soc-tegra-pcm-objs := tegra_pcm.o
snd-soc-tegra-i2s-objs := tegra_i2s.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-utils.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o
-obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
+obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
# Tegra machine Support
-snd-soc-tegra-harmony-objs := harmony.o
+snd-soc-tegra-wm8903-objs := tegra_wm8903.o
+snd-soc-tegra-trimslice-objs := trimslice.o
-obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o
+obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
+obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c
deleted file mode 100644
index 556a5713392..00000000000
--- a/sound/soc/tegra/harmony.c
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * harmony.c - Harmony machine ASoC driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010-2011 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
- *
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <asm/mach-types.h>
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/gpio.h>
-
-#include <mach/harmony_audio.h>
-
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "../codecs/wm8903.h"
-
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
-#include "tegra_asoc_utils.h"
-
-#define DRV_NAME "tegra-snd-harmony"
-
-#define GPIO_SPKR_EN BIT(0)
-#define GPIO_INT_MIC_EN BIT(1)
-#define GPIO_EXT_MIC_EN BIT(2)
-
-struct tegra_harmony {
- struct tegra_asoc_utils_data util_data;
- struct harmony_audio_platform_data *pdata;
- int gpio_requested;
-};
-
-static int harmony_asoc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- int srate, mclk, mclk_change;
- int err;
-
- srate = params_rate(params);
- switch (srate) {
- case 64000:
- case 88200:
- case 96000:
- mclk = 128 * srate;
- break;
- default:
- mclk = 256 * srate;
- break;
- }
- /* FIXME: Codec only requires >= 3MHz if OSR==0 */
- while (mclk < 6000000)
- mclk *= 2;
-
- err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk,
- &mclk_change);
- if (err < 0) {
- dev_err(card->dev, "Can't configure clocks\n");
- return err;
- }
-
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (err < 0) {
- dev_err(card->dev, "codec_dai fmt not set\n");
- return err;
- }
-
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (err < 0) {
- dev_err(card->dev, "cpu_dai fmt not set\n");
- return err;
- }
-
- if (mclk_change) {
- err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
- SND_SOC_CLOCK_IN);
- if (err < 0) {
- dev_err(card->dev, "codec_dai clock not set\n");
- return err;
- }
- }
-
- return 0;
-}
-
-static struct snd_soc_ops harmony_asoc_ops = {
- .hw_params = harmony_asoc_hw_params,
-};
-
-static struct snd_soc_jack harmony_hp_jack;
-
-static struct snd_soc_jack_pin harmony_hp_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = {
- {
- .name = "headphone detect",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 150,
- .invert = 1,
- }
-};
-
-static struct snd_soc_jack harmony_mic_jack;
-
-static struct snd_soc_jack_pin harmony_mic_jack_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-static int harmony_event_int_spk(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
-
- gpio_set_value_cansleep(pdata->gpio_spkr_en,
- SND_SOC_DAPM_EVENT_ON(event));
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route harmony_audio_map[] = {
- {"Headphone Jack", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Int Spk", NULL, "ROP"},
- {"Int Spk", NULL, "RON"},
- {"Int Spk", NULL, "LOP"},
- {"Int Spk", NULL, "LON"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN1L", NULL, "Mic Bias"},
-};
-
-static const struct snd_kcontrol_new harmony_controls[] = {
- SOC_DAPM_PIN_SWITCH("Int Spk"),
-};
-
-static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
- int ret;
-
- ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
- if (ret) {
- dev_err(card->dev, "cannot get spkr_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_SPKR_EN;
-
- gpio_direction_output(pdata->gpio_spkr_en, 0);
-
- ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
- if (ret) {
- dev_err(card->dev, "cannot get int_mic_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_INT_MIC_EN;
-
- /* Disable int mic; enable signal is active-high */
- gpio_direction_output(pdata->gpio_int_mic_en, 0);
-
- ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
- if (ret) {
- dev_err(card->dev, "cannot get ext_mic_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_EXT_MIC_EN;
-
- /* Enable ext mic; enable signal is active-low */
- gpio_direction_output(pdata->gpio_ext_mic_en, 0);
-
- ret = snd_soc_add_controls(codec, harmony_controls,
- ARRAY_SIZE(harmony_controls));
- if (ret < 0)
- return ret;
-
- snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets,
- ARRAY_SIZE(harmony_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, harmony_audio_map,
- ARRAY_SIZE(harmony_audio_map));
-
- harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det;
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &harmony_hp_jack);
- snd_soc_jack_add_pins(&harmony_hp_jack,
- ARRAY_SIZE(harmony_hp_jack_pins),
- harmony_hp_jack_pins);
- snd_soc_jack_add_gpios(&harmony_hp_jack,
- ARRAY_SIZE(harmony_hp_jack_gpios),
- harmony_hp_jack_gpios);
-
- snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
- &harmony_mic_jack);
- snd_soc_jack_add_pins(&harmony_mic_jack,
- ARRAY_SIZE(harmony_mic_jack_pins),
- harmony_mic_jack_pins);
- wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0);
-
- snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
-
- snd_soc_dapm_nc_pin(dapm, "IN3L");
- snd_soc_dapm_nc_pin(dapm, "IN3R");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
-
- snd_soc_dapm_sync(dapm);
-
- return 0;
-}
-
-static struct snd_soc_dai_link harmony_wm8903_dai = {
- .name = "WM8903",
- .stream_name = "WM8903 PCM",
- .codec_name = "wm8903.0-001a",
- .platform_name = "tegra-pcm-audio",
- .cpu_dai_name = "tegra-i2s.0",
- .codec_dai_name = "wm8903-hifi",
- .init = harmony_asoc_init,
- .ops = &harmony_asoc_ops,
-};
-
-static struct snd_soc_card snd_soc_harmony = {
- .name = "tegra-harmony",
- .dai_link = &harmony_wm8903_dai,
- .num_links = 1,
-};
-
-static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_harmony;
- struct tegra_harmony *harmony;
- struct harmony_audio_platform_data *pdata;
- int ret;
-
- if (!machine_is_harmony()) {
- dev_err(&pdev->dev, "Not running on Tegra Harmony!\n");
- return -ENODEV;
- }
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data supplied\n");
- return -EINVAL;
- }
-
- harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL);
- if (!harmony) {
- dev_err(&pdev->dev, "Can't allocate tegra_harmony\n");
- return -ENOMEM;
- }
-
- harmony->pdata = pdata;
-
- ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev);
- if (ret)
- goto err_free_harmony;
-
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
- snd_soc_card_set_drvdata(card, harmony);
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- goto err_clear_drvdata;
- }
-
- return 0;
-
-err_clear_drvdata:
- snd_soc_card_set_drvdata(card, NULL);
- platform_set_drvdata(pdev, NULL);
- card->dev = NULL;
- tegra_asoc_utils_fini(&harmony->util_data);
-err_free_harmony:
- kfree(harmony);
- return ret;
-}
-
-static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
-
- snd_soc_unregister_card(card);
-
- snd_soc_card_set_drvdata(card, NULL);
- platform_set_drvdata(pdev, NULL);
- card->dev = NULL;
-
- tegra_asoc_utils_fini(&harmony->util_data);
-
- if (harmony->gpio_requested & GPIO_EXT_MIC_EN)
- gpio_free(pdata->gpio_ext_mic_en);
- if (harmony->gpio_requested & GPIO_INT_MIC_EN)
- gpio_free(pdata->gpio_int_mic_en);
- if (harmony->gpio_requested & GPIO_SPKR_EN)
- gpio_free(pdata->gpio_spkr_en);
-
- kfree(harmony);
-
- return 0;
-}
-
-static struct platform_driver tegra_snd_harmony_driver = {
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &snd_soc_pm_ops,
- },
- .probe = tegra_snd_harmony_probe,
- .remove = __devexit_p(tegra_snd_harmony_remove),
-};
-
-static int __init snd_tegra_harmony_init(void)
-{
- return platform_driver_register(&tegra_snd_harmony_driver);
-}
-module_init(snd_tegra_harmony_init);
-
-static void __exit snd_tegra_harmony_exit(void)
-{
- platform_driver_unregister(&tegra_snd_harmony_driver);
-}
-module_exit(snd_tegra_harmony_exit);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Harmony machine ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index 52f0a3f9ce4..dfa85cbb05c 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -28,9 +28,10 @@
#include "tegra_asoc_utils.h"
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
- int mclk, int *mclk_change)
+ int mclk)
{
int new_baseclock;
+ bool clk_change;
int err;
switch (srate) {
@@ -52,10 +53,10 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
return -EINVAL;
}
- *mclk_change = ((new_baseclock != data->set_baseclock) ||
+ clk_change = ((new_baseclock != data->set_baseclock) ||
(mclk != data->set_mclk));
- if (!*mclk_change)
- return 0;
+ if (!clk_change)
+ return 0;
data->set_baseclock = 0;
data->set_mclk = 0;
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index bbba7afdfc2..4818195da25 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -36,7 +36,7 @@ struct tegra_asoc_utils_data {
};
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
- int mclk, int *mclk_change);
+ int mclk);
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
struct device *dev);
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
index 4f5e2c90b02..6b817e20548 100644
--- a/sound/soc/tegra/tegra_i2s.c
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -114,7 +114,7 @@ static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
debugfs_remove(i2s->debug);
}
#else
-static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
+static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
{
}
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
new file mode 100644
index 00000000000..0d6738a8b29
--- /dev/null
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -0,0 +1,475 @@
+/*
+ * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <mach/tegra_wm8903_pdata.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8903.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-wm8903"
+
+#define GPIO_SPKR_EN BIT(0)
+#define GPIO_HP_MUTE BIT(1)
+#define GPIO_INT_MIC_EN BIT(2)
+#define GPIO_EXT_MIC_EN BIT(3)
+
+struct tegra_wm8903 {
+ struct tegra_asoc_utils_data util_data;
+ struct tegra_wm8903_platform_data *pdata;
+ int gpio_requested;
+};
+
+static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 64000:
+ case 88200:
+ case 96000:
+ mclk = 128 * srate;
+ break;
+ default:
+ mclk = 256 * srate;
+ break;
+ }
+ /* FIXME: Codec only requires >= 3MHz if OSR==0 */
+ while (mclk < 6000000)
+ mclk *= 2;
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_wm8903_ops = {
+ .hw_params = tegra_wm8903_hw_params,
+};
+
+static struct snd_soc_jack tegra_wm8903_hp_jack;
+
+static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+ .invert = 1,
+};
+
+static struct snd_soc_jack tegra_wm8903_mic_jack;
+
+static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_SPKR_EN))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_spkr_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_HP_MUTE))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_hp_mute,
+ !SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
+ SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route harmony_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1L", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route seaboard_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1R", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route kaen_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN2R", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route aebl_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "LINEOUTR"},
+ {"Int Spk", NULL, "LINEOUTL"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1R", NULL, "Mic Bias"},
+};
+
+static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Int Spk"),
+};
+
+static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ int ret;
+
+ if (gpio_is_valid(pdata->gpio_spkr_en)) {
+ ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get spkr_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_SPKR_EN;
+
+ gpio_direction_output(pdata->gpio_spkr_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_mute)) {
+ ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
+ if (ret) {
+ dev_err(card->dev, "cannot get hp_mute gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_HP_MUTE;
+
+ gpio_direction_output(pdata->gpio_hp_mute, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_int_mic_en)) {
+ ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get int_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_INT_MIC_EN;
+
+ /* Disable int mic; enable signal is active-high */
+ gpio_direction_output(pdata->gpio_int_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
+ ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get ext_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_EXT_MIC_EN;
+
+ /* Enable ext mic; enable signal is active-low */
+ gpio_direction_output(pdata->gpio_ext_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_det)) {
+ tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
+ snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
+ &tegra_wm8903_hp_jack);
+ snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
+ ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
+ tegra_wm8903_hp_jack_pins);
+ snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
+ 1,
+ &tegra_wm8903_hp_jack_gpio);
+ }
+
+ snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
+ &tegra_wm8903_mic_jack);
+ snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
+ ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
+ tegra_wm8903_mic_jack_pins);
+ wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
+ 0);
+
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+ /* FIXME: Calculate automatically based on DAPM routes? */
+ if (!machine_is_harmony() && !machine_is_ventana())
+ snd_soc_dapm_nc_pin(dapm, "IN1L");
+ if (!machine_is_seaboard() && !machine_is_aebl())
+ snd_soc_dapm_nc_pin(dapm, "IN1R");
+ snd_soc_dapm_nc_pin(dapm, "IN2L");
+ if (!machine_is_kaen())
+ snd_soc_dapm_nc_pin(dapm, "IN2R");
+ snd_soc_dapm_nc_pin(dapm, "IN3L");
+ snd_soc_dapm_nc_pin(dapm, "IN3R");
+
+ if (machine_is_aebl()) {
+ snd_soc_dapm_nc_pin(dapm, "LON");
+ snd_soc_dapm_nc_pin(dapm, "RON");
+ snd_soc_dapm_nc_pin(dapm, "ROP");
+ snd_soc_dapm_nc_pin(dapm, "LOP");
+ } else {
+ snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
+ snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_wm8903_dai = {
+ .name = "WM8903",
+ .stream_name = "WM8903 PCM",
+ .codec_name = "wm8903.0-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "wm8903-hifi",
+ .init = tegra_wm8903_init,
+ .ops = &tegra_wm8903_ops,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm8903 = {
+ .name = "tegra-wm8903",
+ .dai_link = &tegra_wm8903_dai,
+ .num_links = 1,
+
+ .controls = tegra_wm8903_controls,
+ .num_controls = ARRAY_SIZE(tegra_wm8903_controls),
+ .dapm_widgets = tegra_wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
+};
+
+static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_tegra_wm8903;
+ struct tegra_wm8903 *machine;
+ struct tegra_wm8903_platform_data *pdata;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
+ return -ENOMEM;
+ }
+
+ machine->pdata = pdata;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_machine;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ if (machine_is_harmony() || machine_is_ventana()) {
+ card->dapm_routes = harmony_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
+ } else if (machine_is_seaboard()) {
+ card->dapm_routes = seaboard_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
+ } else if (machine_is_kaen()) {
+ card->dapm_routes = kaen_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
+ } else {
+ card->dapm_routes = aebl_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err_free_machine:
+ kfree(machine);
+ return ret;
+}
+
+static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ if (machine->gpio_requested & GPIO_EXT_MIC_EN)
+ gpio_free(pdata->gpio_ext_mic_en);
+ if (machine->gpio_requested & GPIO_INT_MIC_EN)
+ gpio_free(pdata->gpio_int_mic_en);
+ if (machine->gpio_requested & GPIO_HP_MUTE)
+ gpio_free(pdata->gpio_hp_mute);
+ if (machine->gpio_requested & GPIO_SPKR_EN)
+ gpio_free(pdata->gpio_spkr_en);
+
+ kfree(machine);
+
+ return 0;
+}
+
+static struct platform_driver tegra_wm8903_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = tegra_wm8903_driver_probe,
+ .remove = __devexit_p(tegra_wm8903_driver_remove),
+};
+
+static int __init tegra_wm8903_modinit(void)
+{
+ return platform_driver_register(&tegra_wm8903_driver);
+}
+module_init(tegra_wm8903_modinit);
+
+static void __exit tegra_wm8903_modexit(void)
+{
+ platform_driver_unregister(&tegra_wm8903_driver);
+}
+module_exit(tegra_wm8903_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
new file mode 100644
index 00000000000..8fc07e9adf2
--- /dev/null
+++ b/sound/soc/tegra/trimslice.c
@@ -0,0 +1,228 @@
+/*
+ * trimslice.c - TrimSlice machine ASoC driver
+ *
+ * Copyright (C) 2011 - CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on code copyright/by:
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/tlv320aic23.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-trimslice"
+
+struct tegra_trimslice {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ mclk = 128 * srate;
+
+ err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops trimslice_asoc_ops = {
+ .hw_params = trimslice_asoc_hw_params,
+};
+
+static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route trimslice_audio_map[] = {
+ {"Line Out", NULL, "LOUT"},
+ {"Line Out", NULL, "ROUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+};
+
+static int trimslice_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_nc_pin(dapm, "LHPOUT");
+ snd_soc_dapm_nc_pin(dapm, "RHPOUT");
+ snd_soc_dapm_nc_pin(dapm, "MICIN");
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .codec_name = "tlv320aic23-codec.2-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "tlv320aic23-hifi",
+ .init = trimslice_asoc_init,
+ .ops = &trimslice_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_trimslice = {
+ .name = "tegra-trimslice",
+ .dai_link = &trimslice_tlv320aic23_dai,
+ .num_links = 1,
+
+ .dapm_widgets = trimslice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
+ .dapm_routes = trimslice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
+};
+
+static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_trimslice;
+ struct tegra_trimslice *trimslice;
+ int ret;
+
+ trimslice = kzalloc(sizeof(struct tegra_trimslice), GFP_KERNEL);
+ if (!trimslice) {
+ dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
+ return -ENOMEM;
+ }
+
+ ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_trimslice;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, trimslice);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&trimslice->util_data);
+err_free_trimslice:
+ kfree(trimslice);
+ return ret;
+}
+
+static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&trimslice->util_data);
+
+ kfree(trimslice);
+
+ return 0;
+}
+
+static struct platform_driver tegra_snd_trimslice_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_snd_trimslice_probe,
+ .remove = __devexit_p(tegra_snd_trimslice_remove),
+};
+
+static int __init snd_tegra_trimslice_init(void)
+{
+ return platform_driver_register(&tegra_snd_trimslice_driver);
+}
+module_init(snd_tegra_trimslice_init);
+
+static void __exit snd_tegra_trimslice_exit(void)
+{
+ platform_driver_unregister(&tegra_snd_trimslice_driver);
+}
+module_exit(snd_tegra_trimslice_exit);
+
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Trimslice machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);