diff options
Diffstat (limited to 'sound/soc')
258 files changed, 34600 insertions, 8286 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 3e598e756e54..8224db5f0434 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -20,6 +20,21 @@ menuconfig SND_SOC if SND_SOC +config SND_SOC_CACHE_LZO + bool "Support LZO compression for register caches" + select LZO_COMPRESS + select LZO_DECOMPRESS + ---help--- + Select this to enable LZO compression for register caches. + This will allow machine or CODEC drivers to compress register + caches in memory, reducing the memory consumption at the + expense of performance. If this is not present and is used + the system will fall back to uncompressed caches. + + Usually it is safe to disable this option, where cache + compression in used the rbtree option will typically perform + better. + config SND_SOC_AC97_BUS bool @@ -35,10 +50,12 @@ source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" +source "sound/soc/mid-x86/Kconfig" source "sound/soc/pxa/Kconfig" -source "sound/soc/s3c24xx/Kconfig" +source "sound/soc/samsung/Kconfig" source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" +source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" # Supported codecs diff --git a/sound/soc/Makefile b/sound/soc/Makefile index eb183443eee4..1ed61c5df2c5 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -10,11 +10,13 @@ obj-$(CONFIG_SND_SOC) += ep93xx/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += imx/ obj-$(CONFIG_SND_SOC) += jz4740/ +obj-$(CONFIG_SND_SOC) += mid-x86/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ -obj-$(CONFIG_SND_SOC) += s3c24xx/ +obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ +obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 5d230cee3fa7..7fbfa051f6e1 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -672,7 +672,7 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) /* re-enable interrupts */ ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr); - /* Re-enable recieve and transmit as appropriate */ + /* Re-enable receive and transmit as appropriate */ cr = 0; cr |= (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0; diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c index 5f4e59f4461c..1aac2f4dbcf6 100644 --- a/sound/soc/atmel/playpaq_wm8510.c +++ b/sound/soc/atmel/playpaq_wm8510.c @@ -33,7 +33,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <mach/at32ap700x.h> #include <mach/portmux.h> @@ -318,27 +317,28 @@ static const struct snd_soc_dapm_route intercon[] = { static int playpaq_wm8510_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int i; /* * Add DAPM widgets */ for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++) - snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]); + snd_soc_dapm_new_control(dapm, &playpaq_dapm_widgets[i]); /* * Setup audio path interconnects */ - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); /* always connected pins */ - snd_soc_dapm_enable_pin(codec, "Int Mic"); - snd_soc_dapm_enable_pin(codec, "Ext Spk"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_enable_pin(dapm, "Int Mic"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_sync(dapm); diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index e521ada80542..af3c73053ee4 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -44,7 +44,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -140,6 +139,7 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; printk(KERN_DEBUG @@ -154,25 +154,25 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) } /* Add specific widgets */ - snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets, + snd_soc_dapm_new_controls(dapm, at91sam9g20ek_dapm_widgets, ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); /* Set up specific audio path interconnects */ - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); /* not connected */ - snd_soc_dapm_nc_pin(codec, "RLINEIN"); - snd_soc_dapm_nc_pin(codec, "LLINEIN"); + snd_soc_dapm_nc_pin(dapm, "RLINEIN"); + snd_soc_dapm_nc_pin(dapm, "LLINEIN"); #ifdef ENABLE_MIC_INPUT - snd_soc_dapm_enable_pin(codec, "Int Mic"); + snd_soc_dapm_enable_pin(dapm, "Int Mic"); #else - snd_soc_dapm_nc_pin(codec, "Int Mic"); + snd_soc_dapm_nc_pin(dapm, "Int Mic"); #endif /* always connected */ - snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c index 86e0f8586dc3..5e4d499d8434 100644 --- a/sound/soc/atmel/snd-soc-afeb9260.c +++ b/sound/soc/atmel/snd-soc-afeb9260.c @@ -30,7 +30,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -105,19 +104,20 @@ static const struct snd_soc_dapm_route audio_map[] = { static int afeb9260_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; /* Add afeb9260 specific widgets */ - snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets, ARRAY_SIZE(tlv320aic23_dapm_widgets)); /* Set up afeb9260 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Line In"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Line In"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -129,7 +129,7 @@ static struct snd_soc_dai_link afeb9260_dai = { .cpu_dai_name = "atmel-ssc-dai.0", .codec_dai_name = "tlv320aic23-hifi", .platform_name = "atmel_pcm-audio", - .codec_name = "tlv320aic23-codec.0-0x1a", + .codec_name = "tlv320aic23-codec.0-001a", .init = afeb9260_tlv320aic23_init, .ops = &afeb9260_ops, }; diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index b62fcd33e586..cb99f04abe88 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -13,7 +13,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1xxx_psc.h> #include <asm/mach-au1x00/au1xxx_dbdma.h> diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 3abeeddc67d3..ae403597fd31 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -1,6 +1,7 @@ config SND_BF5XX_I2S tristate "SoC I2S Audio for the ADI BF5xx chip" depends on BLACKFIN + select SND_BF5XX_SOC_SPORT help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in I2S @@ -35,6 +36,7 @@ config SND_BFIN_AD73311_SE config SND_BF5XX_TDM tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip" depends on (BLACKFIN && SND_SOC) + select SND_BF5XX_SOC_SPORT help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in TDM @@ -61,6 +63,10 @@ config SND_BF5XX_SOC_AD193X config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN + select AC97_BUS + select SND_SOC_AC97_BUS + select SND_BF5XX_SOC_SPORT + select SND_BF5XX_SOC_AC97 help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in slot 16 @@ -122,17 +128,12 @@ config SND_BF5XX_SOC_SPORT config SND_BF5XX_SOC_I2S tristate - select SND_BF5XX_SOC_SPORT config SND_BF5XX_SOC_TDM tristate - select SND_BF5XX_SOC_SPORT config SND_BF5XX_SOC_AC97 tristate - select AC97_BUS - select SND_SOC_AC97_BUS - select SND_BF5XX_SOC_SPORT config SND_BF5XX_SPORT_NUM int "Set a SPORT for Sound chip" diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index c5f856ec27ca..ffbac26b9bce 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -260,9 +260,9 @@ static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) pr_debug("%s : sport %d\n", __func__, dai->id); if (!dai->active) return 0; - if (dai->capture.active) + if (dai->capture_active) sport_rx_stop(sport); - if (dai->playback.active) + if (dai->playback_active) sport_tx_stop(sport); return 0; } diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index 2394bff2b655..83012da9dfc2 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -20,7 +20,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/pcm_params.h> #include <asm/blackfin.h> diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c index e4a625317a1a..d3ccb926b5e4 100644 --- a/sound/soc/blackfin/bf5xx-ad193x.c +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -29,7 +29,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/pcm_params.h> #include <asm/blackfin.h> diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c index 900ced54ac79..732fb8bad076 100644 --- a/sound/soc/blackfin/bf5xx-ad73311.c +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -35,7 +35,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/pcm_params.h> #include <asm/blackfin.h> diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index 36f2769eb912..ad28663f5bbd 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -33,7 +33,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/pcm_params.h> #include <asm/dma.h> @@ -120,7 +119,7 @@ static struct snd_soc_dai_link bf5xx_ssm2602_dai = { .cpu_dai_name = "bf5xx-i2s", .codec_dai_name = "ssm2602-hifi", .platform_name = "bf5xx-pcm-audio", - .codec_name = "ssm2602-codec.0-0x1b", + .codec_name = "ssm2602-codec.0-001b", .ops = &bf5xx_ssm2602_ops, }; diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 125123929f16..5515ac9e05c7 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -210,7 +210,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, #ifdef CONFIG_PM static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) { - struct sport_device *sport = dai->private_data; + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); if (!dai->active) return 0; @@ -235,13 +235,13 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai) ret = -EBUSY; } - ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0); + ret = sport_config_rx(sport, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; } - ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0); + ret = sport_config_tx(sport, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; @@ -303,14 +303,14 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev) goto sport_config_err; } - ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0); + ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; goto sport_config_err; } - ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0); + ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 01d19e9f53f9..06b6981b8d6d 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -19,10 +19,10 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/initval.h> #include <sound/jack.h> +#include <trace/events/asoc.h> #include "88pm860x-codec.h" @@ -146,7 +146,6 @@ struct pm860x_priv { int irq[4]; unsigned char name[4][MAX_NAME_LEN]; - unsigned char reg_cache[REG_CACHE_SIZE]; }; /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ @@ -1172,7 +1171,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Enable Audio PLL & Audio section */ data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; @@ -1185,7 +1184,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, pm860x_set_bits(codec->control_data, REG_MISC2, data, 0); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1263,6 +1262,12 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data) mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt | pm860x->det.hp_det; +#ifndef CONFIG_SND_SOC_88PM860X_MODULE + if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 | + SHORT_LO1 | SHORT_LO2)) + trace_snd_soc_jack_irq(dev_name(pm860x->codec->dev)); +#endif + if ((pm860x->det.hp_det & SND_JACK_HEADPHONE) && (status & HEADSET_STATUS)) report |= SND_JACK_HEADPHONE; @@ -1346,6 +1351,7 @@ EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); static int pm860x_probe(struct snd_soc_codec *codec) { struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int i, ret; pm860x->codec = codec; @@ -1358,7 +1364,7 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x->name[i], pm860x); if (ret < 0) { dev_err(codec->dev, "Failed to request IRQ!\n"); - goto out_irq; + goto out; } } @@ -1369,22 +1375,20 @@ static int pm860x_probe(struct snd_soc_codec *codec) if (ret < 0) { dev_err(codec->dev, "Failed to fill register cache: %d\n", ret); - goto out_codec; + goto out; } snd_soc_add_controls(codec, pm860x_snd_controls, ARRAY_SIZE(pm860x_snd_controls)); - snd_soc_dapm_new_controls(codec, pm860x_dapm_widgets, + snd_soc_dapm_new_controls(dapm, pm860x_dapm_widgets, ARRAY_SIZE(pm860x_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; -out_codec: - i = 3; -out_irq: - for (; i >= 0; i--) +out: + while (--i >= 0) free_irq(pm860x->irq[i], pm860x); - return -EINVAL; + return ret; } static int pm860x_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3b5690d28b8b..6943e24a74a1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -22,20 +22,28 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK4535 if I2C select SND_SOC_AK4642 if I2C select SND_SOC_AK4671 if I2C + select SND_SOC_ALC5623 if I2C select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C + select SND_SOC_DFBMCS320 select SND_SOC_JZ4740_CODEC if SOC_JZ4740 + select SND_SOC_LM4857 if I2C select SND_SOC_MAX98088 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_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER + select SND_SOC_TVL320AIC32X4 if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -43,7 +51,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TWL6040 if TWL4030_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C - select SND_SOC_WL1273 if WL1273_CORE + select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM2000 if I2C select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8400 if MFD_WM8400 @@ -54,9 +62,11 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8727 select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8737 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8770 if SPI_MASTER select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C @@ -73,8 +83,10 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8990 if I2C + select SND_SOC_WM8991 if I2C select SND_SOC_WM8993 if I2C select SND_SOC_WM8994 if MFD_WM8994 + select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS @@ -130,6 +142,9 @@ config SND_SOC_AK4642 config SND_SOC_AK4671 tristate +config SND_SOC_ALC5623 + tristate + config SND_SOC_CQ0093VC tristate @@ -148,6 +163,9 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_CS4271 + tristate + config SND_SOC_CX20442 tristate @@ -160,12 +178,28 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_DFBMCS320 + tristate + +config SND_SOC_DMIC + tristate + config SND_SOC_MAX98088 tristate +config SND_SOC_MAX9850 + tristate + config SND_SOC_PCM3008 tristate +#Freescale sgtl5000 codec +config SND_SOC_SGTL5000 + tristate + +config SND_SOC_SN95031 + tristate + config SND_SOC_SPDIF tristate @@ -182,6 +216,9 @@ config SND_SOC_TLV320AIC26 tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE depends on SPI +config SND_SOC_TVL320AIC32X4 + tristate + config SND_SOC_TLV320AIC3X tristate @@ -231,6 +268,9 @@ config SND_SOC_WM8728 config SND_SOC_WM8731 tristate +config SND_SOC_WM8737 + tristate + config SND_SOC_WM8741 tristate @@ -240,6 +280,9 @@ config SND_SOC_WM8750 config SND_SOC_WM8753 tristate +config SND_SOC_WM8770 + tristate + config SND_SOC_WM8776 tristate @@ -288,12 +331,18 @@ config SND_SOC_WM8988 config SND_SOC_WM8990 tristate +config SND_SOC_WM8991 + tristate + config SND_SOC_WM8993 tristate config SND_SOC_WM8994 tristate +config SND_SOC_WM8995 + tristate + config SND_SOC_WM9081 tristate @@ -307,6 +356,9 @@ config SND_SOC_WM9713 tristate # Amp +config SND_SOC_LM4857 + tristate + config SND_SOC_MAX9877 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f67a2d6f7a46..379bc55f0723 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -12,17 +12,25 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs4270-objs := cs4270.o +snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o +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-max9850-objs := max9850.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-sgtl5000-objs := sgtl5000.o +snd-soc-alc5623-objs := alc5623.o +snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-objs := spdif_transciever.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -38,9 +46,11 @@ snd-soc-wm8711-objs := wm8711.o snd-soc-wm8727-objs := wm8727.o snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o +snd-soc-wm8737-objs := wm8737.o snd-soc-wm8741-objs := wm8741.o snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o +snd-soc-wm8770-objs := wm8770.o snd-soc-wm8776-objs := wm8776.o snd-soc-wm8804-objs := wm8804.o snd-soc-wm8900-objs := wm8900.o @@ -57,8 +67,10 @@ snd-soc-wm8978-objs := wm8978.o snd-soc-wm8985-objs := wm8985.o 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 +snd-soc-wm8994-objs := wm8994.o wm8994-tables.o +snd-soc-wm8995-objs := wm8995.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o @@ -67,6 +79,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-jz4740-codec-objs := jz4740.o # Amp +snd-soc-lm4857-objs := lm4857.o snd-soc-max9877-objs := max9877.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-wm2000-objs := wm2000.o @@ -83,21 +96,29 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.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 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o +obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o +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_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o +obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o +obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o @@ -113,9 +134,11 @@ obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o +obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o +obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o @@ -132,8 +155,10 @@ obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o +obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o +obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o @@ -141,6 +166,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o # Amp +obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index d272534c8f84..ab63d52e36e1 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -27,7 +27,6 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> -#include <sound/soc-dapm.h> #include <linux/spi/spi.h> #include "ad1836.h" @@ -220,6 +219,7 @@ static struct snd_soc_dai_driver ad1836_dai = { static int ad1836_probe(struct snd_soc_codec *codec) { struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret = 0; codec->control_data = ad1836->control_data; @@ -227,7 +227,6 @@ static int ad1836_probe(struct snd_soc_codec *codec) if (ret < 0) { dev_err(codec->dev, "failed to set cache I/O: %d\n", ret); - kfree(ad1836); return ret; } @@ -252,9 +251,9 @@ static int ad1836_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, ad1836_snd_controls, ARRAY_SIZE(ad1836_snd_controls)); - snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets, + snd_soc_dapm_new_controls(dapm, ad1836_dapm_widgets, ARRAY_SIZE(ad1836_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); return ret; } diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index fa2834c91b9f..da46479bfcfa 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -19,12 +19,10 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> -#include <sound/soc-dapm.h> #include "ad193x.h" /* codec private data */ struct ad193x_priv { - u8 reg_cache[AD193X_NUM_REGS]; enum snd_soc_control_type bus_type; void *control_data; int sysclk; @@ -353,6 +351,7 @@ static struct snd_soc_dai_driver ad193x_dai = { static int ad193x_probe(struct snd_soc_codec *codec) { struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; codec->control_data = ad193x->control_data; @@ -363,7 +362,6 @@ static int ad193x_probe(struct snd_soc_codec *codec) if (ret < 0) { dev_err(codec->dev, "failed to set cache I/O: %d\n", ret); - kfree(ad193x); return ret; } @@ -385,9 +383,9 @@ static int ad193x_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, ad193x_snd_controls, ARRAY_SIZE(ad193x_snd_controls)); - snd_soc_dapm_new_controls(codec, ad193x_dapm_widgets, + snd_soc_dapm_new_controls(dapm, ad193x_dapm_widgets, ARRAY_SIZE(ad193x_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); return ret; } diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 410ccd5d41cd..34cb51ef2156 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -29,7 +29,6 @@ #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "ad1980.h" diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index c27f8f59dc66..cbf0b6d400b8 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -294,7 +294,6 @@ static struct spi_driver ak4104_spi_driver = { static int __init ak4104_init(void) { - pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n"); return spi_register_driver(&ak4104_spi_driver); } module_init(ak4104_init); diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index cd88c8f32a38..8b38739c88f8 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "ak4535.h" @@ -290,10 +289,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int ak4535_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets, - ARRAY_SIZE(ak4535_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + 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; } @@ -366,9 +366,9 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai, static int ak4535_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; + u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC); if (!mute) - ak4535_write(codec, AK4535_DAC, mute_reg); + ak4535_write(codec, AK4535_DAC, mute_reg & ~0x20); else ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); return 0; @@ -381,11 +381,11 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; - ak4535_write(codec, AK4535_DAC, mute_reg); + mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC); + ak4535_write(codec, AK4535_DAC, mute_reg & ~0x20); break; case SND_SOC_BIAS_PREPARE: - mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; + mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC); ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); break; case SND_SOC_BIAS_STANDBY: @@ -399,7 +399,7 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec, ak4535_write(codec, AK4535_PM1, i & (~0x80)); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 90c90b7f4a2e..4be0570e3f1f 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,7 +26,7 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -116,6 +116,12 @@ #define BCKO_MASK (1 << 3) #define BCKO_64 BCKO_MASK +#define DIF_MASK (3 << 0) +#define DSP (0 << 0) +#define RIGHT_J (1 << 0) +#define LEFT_J (2 << 0) +#define I2S (3 << 0) + /* MD_CTL2 */ #define FS0 (1 << 0) #define FS1 (1 << 1) @@ -354,6 +360,24 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) snd_soc_update_bits(codec, PW_MGMT2, MS, data); snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko); + /* format type */ + data = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + data = LEFT_J; + break; + case SND_SOC_DAIFMT_I2S: + data = I2S; + break; + /* FIXME + * Please add RIGHT_J / DSP support here + */ + default: + return -EINVAL; + break; + } + snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data); + return 0; } diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 24f5f49bb9d2..2ec75abfa3e9 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -17,7 +17,6 @@ #include <linux/delay.h> #include <linux/slab.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -28,7 +27,6 @@ struct ak4671_priv { enum snd_soc_control_type control_type; void *control_data; - u8 reg_cache[AK4671_CACHEREGNUM]; }; /* ak4671 register cache & default register settings */ @@ -437,10 +435,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int ak4671_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets, - ARRAY_SIZE(ak4671_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + 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; } @@ -602,7 +601,7 @@ static int ak4671_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c new file mode 100644 index 000000000000..eecffb548947 --- /dev/null +++ b/sound/soc/codecs/alc5623.c @@ -0,0 +1,1117 @@ +/* + * alc5623.c -- alc562[123] ALSA Soc Audio driver + * + * Copyright 2008 Realtek Microelectronics + * Author: flove <flove@realtek.com> Ethan <eku@marvell.com> + * + * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org> + * + * + * Based on WM8753.c + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/alc5623.h> + +#include "alc5623.h" + +static int caps_charge = 2000; +module_param(caps_charge, int, 0); +MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)"); + +/* codec private data */ +struct alc5623_priv { + enum snd_soc_control_type control_type; + void *control_data; + struct mutex mutex; + u8 id; + unsigned int sysclk; + u16 reg_cache[ALC5623_VENDOR_ID2+2]; + unsigned int add_ctrl; + unsigned int jack_det_ctrl; +}; + +static void alc5623_fill_cache(struct snd_soc_codec *codec) +{ + int i, step = codec->driver->reg_cache_step; + u16 *cache = codec->reg_cache; + + /* not really efficient ... */ + for (i = 0 ; i < codec->driver->reg_cache_size ; i += step) + cache[i] = codec->hw_read(codec, i); +} + +static inline int alc5623_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, ALC5623_RESET, 0); +} + +static int amp_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* to power-on/off class-d amp generators/speaker */ + /* need to write to 'index-46h' register : */ + /* so write index num (here 0x46) to reg 0x6a */ + /* and then 0xffff/0 to reg 0x6c */ + snd_soc_write(w->codec, ALC5623_HID_CTRL_INDEX, 0x46); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0xFFFF); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0); + break; + } + + return 0; +} + +/* + * ALC5623 Controls + */ + +static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0); +static const unsigned int boost_tlv[] = { + TLV_DB_RANGE_HEAD(3), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; +static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new rt5621_vol_snd_controls[] = { + SOC_DOUBLE_TLV("Speaker Playback Volume", + ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Speaker Playback Switch", + ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Headphone Playback Volume", + ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Headphone Playback Switch", + ALC5623_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new rt5622_vol_snd_controls[] = { + SOC_DOUBLE_TLV("Speaker Playback Volume", + ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Speaker Playback Switch", + ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Line Playback Volume", + ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Line Playback Switch", + ALC5623_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_vol_snd_controls[] = { + SOC_DOUBLE_TLV("Line Playback Volume", + ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Line Playback Switch", + ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Headphone Playback Volume", + ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Headphone Playback Switch", + ALC5623_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_snd_controls[] = { + SOC_DOUBLE_TLV("Auxout Playback Volume", + ALC5623_MONO_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Auxout Playback Switch", + ALC5623_MONO_AUX_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("PCM Playback Volume", + ALC5623_STEREO_DAC_VOL, 8, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("AuxI Capture Volume", + ALC5623_AUXIN_VOL, 8, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("LineIn Capture Volume", + ALC5623_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv), + SOC_SINGLE_TLV("Mic1 Capture Volume", + ALC5623_MIC_VOL, 8, 31, 1, vol_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", + ALC5623_MIC_VOL, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("Rec Capture Volume", + ALC5623_ADC_REC_GAIN, 7, 0, 31, 0, adc_rec_tlv), + SOC_SINGLE_TLV("Mic 1 Boost Volume", + ALC5623_MIC_CTRL, 10, 2, 0, boost_tlv), + SOC_SINGLE_TLV("Mic 2 Boost Volume", + ALC5623_MIC_CTRL, 8, 2, 0, boost_tlv), + SOC_SINGLE_TLV("Digital Boost Volume", + ALC5623_ADD_CTRL_REG, 4, 3, 0, dig_tlv), +}; + +/* + * DAPM Controls + */ +static const struct snd_kcontrol_new alc5623_hp_mixer_controls[] = { +SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5623_LINE_IN_VOL, 15, 1, 1), +SOC_DAPM_SINGLE("AUXI2HP Playback Switch", ALC5623_AUXIN_VOL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 7, 1, 1), +SOC_DAPM_SINGLE("DAC2HP Playback Switch", ALC5623_STEREO_DAC_VOL, 15, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_hpl_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5623_ADC_REC_GAIN, 15, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_hpr_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5623_ADC_REC_GAIN, 14, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5623_ADC_REC_GAIN, 13, 1, 1), +SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5623_ADC_REC_GAIN, 12, 1, 1), +SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5623_LINE_IN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("AUXI2MONO Playback Switch", ALC5623_AUXIN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC12MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC22MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 5, 1, 1), +SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5623_STEREO_DAC_VOL, 13, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5623_LINE_IN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("AUXI2SPK Playback Switch", ALC5623_AUXIN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("MIC12SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("MIC22SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 6, 1, 1), +SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5623_STEREO_DAC_VOL, 14, 1, 1), +}; + +/* Left Record Mixer */ +static const struct snd_kcontrol_new alc5623_captureL_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 14, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 13, 1, 1), +SOC_DAPM_SINGLE("LineInL Capture Switch", ALC5623_ADC_REC_MIXER, 12, 1, 1), +SOC_DAPM_SINGLE("Left AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 11, 1, 1), +SOC_DAPM_SINGLE("HPMixerL Capture Switch", ALC5623_ADC_REC_MIXER, 10, 1, 1), +SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 9, 1, 1), +SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 8, 1, 1), +}; + +/* Right Record Mixer */ +static const struct snd_kcontrol_new alc5623_captureR_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 6, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 5, 1, 1), +SOC_DAPM_SINGLE("LineInR Capture Switch", ALC5623_ADC_REC_MIXER, 4, 1, 1), +SOC_DAPM_SINGLE("Right AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 3, 1, 1), +SOC_DAPM_SINGLE("HPMixerR Capture Switch", ALC5623_ADC_REC_MIXER, 2, 1, 1), +SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 1, 1, 1), +SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 0, 1, 1), +}; + +static const char *alc5623_spk_n_sour_sel[] = { + "RN/-R", "RP/+R", "LN/-R", "Vmid" }; +static const char *alc5623_hpl_out_input_sel[] = { + "Vmid", "HP Left Mix"}; +static const char *alc5623_hpr_out_input_sel[] = { + "Vmid", "HP Right Mix"}; +static const char *alc5623_spkout_input_sel[] = { + "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; +static const char *alc5623_aux_out_input_sel[] = { + "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; + +/* auxout output mux */ +static const struct soc_enum alc5623_aux_out_input_enum = +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 6, 4, alc5623_aux_out_input_sel); +static const struct snd_kcontrol_new alc5623_auxout_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum); + +/* speaker output mux */ +static const struct soc_enum alc5623_spkout_input_enum = +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 10, 4, alc5623_spkout_input_sel); +static const struct snd_kcontrol_new alc5623_spkout_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum); + +/* headphone left output mux */ +static const struct soc_enum alc5623_hpl_out_input_enum = +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel); +static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum); + +/* headphone right output mux */ +static const struct soc_enum alc5623_hpr_out_input_enum = +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 8, 2, alc5623_hpr_out_input_sel); +static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum); + +/* speaker output N select */ +static const struct soc_enum alc5623_spk_n_sour_enum = +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel); +static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum); + +static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = { +/* Muxes */ +SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0, + &alc5623_auxout_mux_controls), +SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0, + &alc5623_spkout_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, + &alc5623_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, + &alc5623_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0, + &alc5623_spkoutn_mux_controls), + +/* output mixers */ +SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0, + &alc5623_hp_mixer_controls[0], + ARRAY_SIZE(alc5623_hp_mixer_controls)), +SND_SOC_DAPM_MIXER("HPR Mix", ALC5623_PWR_MANAG_ADD2, 4, 0, + &alc5623_hpr_mixer_controls[0], + ARRAY_SIZE(alc5623_hpr_mixer_controls)), +SND_SOC_DAPM_MIXER("HPL Mix", ALC5623_PWR_MANAG_ADD2, 5, 0, + &alc5623_hpl_mixer_controls[0], + ARRAY_SIZE(alc5623_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Mono Mix", ALC5623_PWR_MANAG_ADD2, 2, 0, + &alc5623_mono_mixer_controls[0], + ARRAY_SIZE(alc5623_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mix", ALC5623_PWR_MANAG_ADD2, 3, 0, + &alc5623_speaker_mixer_controls[0], + ARRAY_SIZE(alc5623_speaker_mixer_controls)), + +/* input mixers */ +SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5623_PWR_MANAG_ADD2, 1, 0, + &alc5623_captureL_mixer_controls[0], + ARRAY_SIZE(alc5623_captureL_mixer_controls)), +SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5623_PWR_MANAG_ADD2, 0, 0, + &alc5623_captureR_mixer_controls[0], + ARRAY_SIZE(alc5623_captureR_mixer_controls)), + +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", + ALC5623_PWR_MANAG_ADD2, 9, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", + ALC5623_PWR_MANAG_ADD2, 8, 0), +SND_SOC_DAPM_MIXER("I2S Mix", ALC5623_PWR_MANAG_ADD1, 15, 0, NULL, 0), +SND_SOC_DAPM_MIXER("AuxI Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", + ALC5623_PWR_MANAG_ADD2, 7, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", + ALC5623_PWR_MANAG_ADD2, 6, 0), +SND_SOC_DAPM_PGA("Left Headphone", ALC5623_PWR_MANAG_ADD3, 10, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", ALC5623_PWR_MANAG_ADD3, 9, 0, NULL, 0), +SND_SOC_DAPM_PGA("SpeakerOut", ALC5623_PWR_MANAG_ADD3, 12, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left AuxOut", ALC5623_PWR_MANAG_ADD3, 14, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right AuxOut", ALC5623_PWR_MANAG_ADD3, 13, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left LineIn", ALC5623_PWR_MANAG_ADD3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right LineIn", ALC5623_PWR_MANAG_ADD3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left AuxI", ALC5623_PWR_MANAG_ADD3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right AuxI", ALC5623_PWR_MANAG_ADD3, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC1 PGA", ALC5623_PWR_MANAG_ADD3, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC2 PGA", ALC5623_PWR_MANAG_ADD3, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5623_PWR_MANAG_ADD3, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5623_PWR_MANAG_ADD3, 0, 0, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias1", ALC5623_PWR_MANAG_ADD1, 11, 0), + +SND_SOC_DAPM_OUTPUT("AUXOUTL"), +SND_SOC_DAPM_OUTPUT("AUXOUTR"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_INPUT("LINEINL"), +SND_SOC_DAPM_INPUT("LINEINR"), +SND_SOC_DAPM_INPUT("AUXINL"), +SND_SOC_DAPM_INPUT("AUXINR"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_VMID("Vmid"), +}; + +static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"}; +static const struct soc_enum alc5623_amp_enum = + SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 13, 2, alc5623_amp_names); +static const struct snd_kcontrol_new alc5623_amp_mux_controls = + SOC_DAPM_ENUM("Route", alc5623_amp_enum); + +static const struct snd_soc_dapm_widget alc5623_dapm_amp_widgets[] = { +SND_SOC_DAPM_PGA_E("D Amp", ALC5623_PWR_MANAG_ADD2, 14, 0, NULL, 0, + amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA("AB Amp", ALC5623_PWR_MANAG_ADD2, 15, 0, NULL, 0), +SND_SOC_DAPM_MUX("AB-D Amp Mux", SND_SOC_NOPM, 0, 0, + &alc5623_amp_mux_controls), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* virtual mixer - mixes left & right channels */ + {"I2S Mix", NULL, "Left DAC"}, + {"I2S Mix", NULL, "Right DAC"}, + {"Line Mix", NULL, "Right LineIn"}, + {"Line Mix", NULL, "Left LineIn"}, + {"AuxI Mix", NULL, "Left AuxI"}, + {"AuxI Mix", NULL, "Right AuxI"}, + {"AUXOUTL", NULL, "Left AuxOut"}, + {"AUXOUTR", NULL, "Right AuxOut"}, + + /* HP mixer */ + {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"}, + {"HPL Mix", NULL, "HP Mix"}, + {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"}, + {"HPR Mix", NULL, "HP Mix"}, + {"HP Mix", "LI2HP Playback Switch", "Line Mix"}, + {"HP Mix", "AUXI2HP Playback Switch", "AuxI Mix"}, + {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"}, + {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"}, + {"HP Mix", "DAC2HP Playback Switch", "I2S Mix"}, + + /* speaker mixer */ + {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"}, + {"Speaker Mix", "AUXI2SPK Playback Switch", "AuxI Mix"}, + {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"}, + {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"}, + {"Speaker Mix", "DAC2SPK Playback Switch", "I2S Mix"}, + + /* mono mixer */ + {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"}, + {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"}, + {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"}, + {"Mono Mix", "AUXI2MONO Playback Switch", "AuxI Mix"}, + {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"}, + {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"}, + {"Mono Mix", "DAC2MONO Playback Switch", "I2S Mix"}, + + /* Left record mixer */ + {"Left Capture Mix", "LineInL Capture Switch", "LINEINL"}, + {"Left Capture Mix", "Left AuxI Capture Switch", "AUXINL"}, + {"Left Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"}, + {"Left Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"}, + {"Left Capture Mix", "HPMixerL Capture Switch", "HPL Mix"}, + {"Left Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"}, + {"Left Capture Mix", "MonoMixer Capture Switch", "Mono Mix"}, + + /*Right record mixer */ + {"Right Capture Mix", "LineInR Capture Switch", "LINEINR"}, + {"Right Capture Mix", "Right AuxI Capture Switch", "AUXINR"}, + {"Right Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"}, + {"Right Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"}, + {"Right Capture Mix", "HPMixerR Capture Switch", "HPR Mix"}, + {"Right Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"}, + {"Right Capture Mix", "MonoMixer Capture Switch", "Mono Mix"}, + + /* headphone left mux */ + {"Left Headphone Mux", "HP Left Mix", "HPL Mix"}, + {"Left Headphone Mux", "Vmid", "Vmid"}, + + /* headphone right mux */ + {"Right Headphone Mux", "HP Right Mix", "HPR Mix"}, + {"Right Headphone Mux", "Vmid", "Vmid"}, + + /* speaker out mux */ + {"SpeakerOut Mux", "Vmid", "Vmid"}, + {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"}, + {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"}, + {"SpeakerOut Mux", "Mono Mix", "Mono Mix"}, + + /* Mono/Aux Out mux */ + {"AuxOut Mux", "Vmid", "Vmid"}, + {"AuxOut Mux", "HPOut Mix", "HPOut Mix"}, + {"AuxOut Mux", "Speaker Mix", "Speaker Mix"}, + {"AuxOut Mux", "Mono Mix", "Mono Mix"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Mux"}, + {"Left AuxOut", NULL, "AuxOut Mux"}, + {"Right AuxOut", NULL, "AuxOut Mux"}, + + /* input pga */ + {"Left LineIn", NULL, "LINEINL"}, + {"Right LineIn", NULL, "LINEINR"}, + {"Left AuxI", NULL, "AUXINL"}, + {"Right AuxI", NULL, "AUXINR"}, + {"MIC1 Pre Amp", NULL, "MIC1"}, + {"MIC2 Pre Amp", NULL, "MIC2"}, + {"MIC1 PGA", NULL, "MIC1 Pre Amp"}, + {"MIC2 PGA", NULL, "MIC2 Pre Amp"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Mix"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Mix"}, + + {"SpeakerOut N Mux", "RN/-R", "SpeakerOut"}, + {"SpeakerOut N Mux", "RP/+R", "SpeakerOut"}, + {"SpeakerOut N Mux", "LN/-R", "SpeakerOut"}, + {"SpeakerOut N Mux", "Vmid", "Vmid"}, + + {"SPKOUT", NULL, "SpeakerOut"}, + {"SPKOUTN", NULL, "SpeakerOut N Mux"}, +}; + +static const struct snd_soc_dapm_route intercon_spk[] = { + {"SpeakerOut", NULL, "SpeakerOut Mux"}, +}; + +static const struct snd_soc_dapm_route intercon_amp_spk[] = { + {"AB Amp", NULL, "SpeakerOut Mux"}, + {"D Amp", NULL, "SpeakerOut Mux"}, + {"AB-D Amp Mux", "AB Amp", "AB Amp"}, + {"AB-D Amp Mux", "D Amp", "D Amp"}, + {"SpeakerOut", NULL, "AB-D Amp Mux"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +/* Note : pll code from original alc5623 driver. Not sure of how good it is */ +/* useful only for master mode */ +static const struct _pll_div codec_master_pll_div[] = { + + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + { 12000000, 8192000, 0x456b}, + { 13000000, 8192000, 0x495f}, + { 13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + { 12000000, 11289600, 0x3e2f}, + { 13000000, 11289600, 0x4d5b}, + { 13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + { 12000000, 16384000, 0x452b}, + { 13000000, 16384000, 0x542f}, + { 13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + { 12000000, 16934400, 0x4d2c}, + { 13000000, 16934400, 0x742f}, + { 13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + { 12000000, 22579200, 0x7e2f}, + { 13000000, 22579200, 0x742f}, + { 13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + { 12000000, 24576000, 0x2915}, + { 13000000, 24576000, 0x772e}, + { 13100000, 24576000, 0x0d20}, +}; + +static const struct _pll_div codec_slave_pll_div[] = { + + { 1024000, 16384000, 0x3ea0}, + { 1411200, 22579200, 0x3ea0}, + { 1536000, 24576000, 0x3ea0}, + { 2048000, 16384000, 0x1ea0}, + { 2822400, 22579200, 0x1ea0}, + { 3072000, 24576000, 0x1ea0}, + +}; + +static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + int i; + struct snd_soc_codec *codec = codec_dai->codec; + int gbl_clk = 0, pll_div = 0; + u16 reg; + + if (pll_id < ALC5623_PLL_FR_MCLK || pll_id > ALC5623_PLL_FR_BCK) + return -ENODEV; + + /* Disable PLL power */ + snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2, + ALC5623_PWR_ADD2_PLL, + 0); + + /* pll is not used in slave mode */ + reg = snd_soc_read(codec, ALC5623_DAI_CONTROL); + if (reg & ALC5623_DAI_SDP_SLAVE_MODE) + return 0; + + if (!freq_in || !freq_out) + return 0; + + switch (pll_id) { + case ALC5623_PLL_FR_MCLK: + for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) { + if (codec_master_pll_div[i].pll_in == freq_in + && codec_master_pll_div[i].pll_out == freq_out) { + /* PLL source from MCLK */ + pll_div = codec_master_pll_div[i].regvalue; + break; + } + } + break; + case ALC5623_PLL_FR_BCK: + for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) { + if (codec_slave_pll_div[i].pll_in == freq_in + && codec_slave_pll_div[i].pll_out == freq_out) { + /* PLL source from Bitclk */ + gbl_clk = ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK; + pll_div = codec_slave_pll_div[i].regvalue; + break; + } + } + break; + default: + return -EINVAL; + } + + if (!pll_div) + return -EINVAL; + + snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk); + snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div); + snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2, + ALC5623_PWR_ADD2_PLL, + ALC5623_PWR_ADD2_PLL); + gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL; + snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk); + + return 0; +} + +struct _coeff_div { + u16 fs; + u16 regvalue; +}; + +/* codec hifi mclk (after PLL) clock divider coefficients */ +/* values inspired from column BCLK=32Fs of Appendix A table */ +static const struct _coeff_div coeff_div[] = { + {256*8, 0x3a69}, + {384*8, 0x3c6b}, + {256*4, 0x2a69}, + {384*4, 0x2c6b}, + {256*2, 0x1a69}, + {384*2, 0x1c6b}, + {256*1, 0x0a69}, + {384*1, 0x0c6b}, +}; + +static int get_coeff(struct snd_soc_codec *codec, int rate) +{ + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].fs * rate == alc5623->sysclk) + return i; + } + return -EINVAL; +} + +/* + * Clock after PLL and dividers + */ +static int alc5623_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 alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case 8192000: + case 11289600: + case 12288000: + case 16384000: + case 16934400: + case 18432000: + case 22579200: + case 24576000: + alc5623->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = ALC5623_DAI_SDP_MASTER_MODE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = ALC5623_DAI_SDP_SLAVE_MODE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= ALC5623_DAI_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= ALC5623_DAI_I2S_DF_RIGHT; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= ALC5623_DAI_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= ALC5623_DAI_I2S_DF_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= ALC5623_DAI_I2S_DF_PCM | ALC5623_DAI_I2S_PCM_MODE; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + default: + return -EINVAL; + } + + return snd_soc_write(codec, ALC5623_DAI_CONTROL, iface); +} + +static int alc5623_pcm_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 alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + int coeff, rate; + u16 iface; + + iface = snd_soc_read(codec, ALC5623_DAI_CONTROL); + iface &= ~ALC5623_DAI_I2S_DL_MASK; + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iface |= ALC5623_DAI_I2S_DL_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= ALC5623_DAI_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= ALC5623_DAI_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= ALC5623_DAI_I2S_DL_32; + break; + default: + return -EINVAL; + } + + /* set iface & srate */ + snd_soc_write(codec, ALC5623_DAI_CONTROL, iface); + rate = params_rate(params); + coeff = get_coeff(codec, rate); + if (coeff < 0) + return -EINVAL; + + coeff = coeff_div[coeff].regvalue; + dev_dbg(codec->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n", + __func__, alc5623->sysclk, rate, coeff); + snd_soc_write(codec, ALC5623_STEREO_AD_DA_CLK_CTRL, coeff); + + return 0; +} + +static int alc5623_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 hp_mute = ALC5623_MISC_M_DAC_L_INPUT | ALC5623_MISC_M_DAC_R_INPUT; + u16 mute_reg = snd_soc_read(codec, ALC5623_MISC_CTRL) & ~hp_mute; + + if (mute) + mute_reg |= hp_mute; + + return snd_soc_write(codec, ALC5623_MISC_CTRL, mute_reg); +} + +#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \ + | ALC5623_PWR_ADD2_DAC_REF_CIR) + +#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \ + | ALC5623_PWR_ADD3_MIC1_BOOST_AD) + +#define ALC5623_ADD1_POWER_EN \ + (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \ + | ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \ + | ALC5623_PWR_ADD1_HP_OUT_ENH_AMP) + +#define ALC5623_ADD1_POWER_EN_5622 \ + (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \ + | ALC5623_PWR_ADD1_HP_OUT_AMP) + +static void enable_power_depop(struct snd_soc_codec *codec) +{ + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD1, + ALC5623_PWR_ADD1_SOFTGEN_EN, + ALC5623_PWR_ADD1_SOFTGEN_EN); + + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN); + + snd_soc_update_bits(codec, ALC5623_MISC_CTRL, + ALC5623_MISC_HP_DEPOP_MODE2_EN, + ALC5623_MISC_HP_DEPOP_MODE2_EN); + + msleep(500); + + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN); + + /* avoid writing '1' into 5622 reserved bits */ + if (alc5623->id == 0x22) + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, + ALC5623_ADD1_POWER_EN_5622); + else + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, + ALC5623_ADD1_POWER_EN); + + /* disable HP Depop2 */ + snd_soc_update_bits(codec, ALC5623_MISC_CTRL, + ALC5623_MISC_HP_DEPOP_MODE2_EN, + 0); + +} + +static int alc5623_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + enable_power_depop(codec); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, + ALC5623_PWR_ADD2_VREF); + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, + ALC5623_PWR_ADD3_MAIN_BIAS); + break; + case SND_SOC_BIAS_OFF: + /* everything off, dac mute, inactive */ + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, 0); + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, 0); + snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define ALC5623_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \ + | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops alc5623_dai_ops = { + .hw_params = alc5623_pcm_hw_params, + .digital_mute = alc5623_mute, + .set_fmt = alc5623_set_dai_fmt, + .set_sysclk = alc5623_set_dai_sysclk, + .set_pll = alc5623_set_dai_pll, +}; + +static struct snd_soc_dai_driver alc5623_dai = { + .name = "alc5623-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ALC5623_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ALC5623_FORMATS,}, + + .ops = &alc5623_dai_ops, +}; + +static int alc5623_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +{ + alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int alc5623_resume(struct snd_soc_codec *codec) +{ + int i, step = codec->driver->reg_cache_step; + u16 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware */ + for (i = 2 ; i < codec->driver->reg_cache_size ; i += step) + snd_soc_write(codec, i, cache[i]); + + alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* charge alc5623 caps */ + if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { + alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + codec->dapm.bias_level = SND_SOC_BIAS_ON; + alc5623_set_bias_level(codec, codec->dapm.bias_level); + } + + return 0; +} + +static int alc5623_probe(struct snd_soc_codec *codec) +{ + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_codec_set_cache_io(codec, 8, 16, alc5623->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + alc5623_reset(codec); + alc5623_fill_cache(codec); + + /* power on device */ + alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + if (alc5623->add_ctrl) { + snd_soc_write(codec, ALC5623_ADD_CTRL_REG, + alc5623->add_ctrl); + } + + if (alc5623->jack_det_ctrl) { + snd_soc_write(codec, ALC5623_JACK_DET_CTRL, + alc5623->jack_det_ctrl); + } + + switch (alc5623->id) { + case 0x21: + snd_soc_add_controls(codec, rt5621_vol_snd_controls, + ARRAY_SIZE(rt5621_vol_snd_controls)); + break; + case 0x22: + snd_soc_add_controls(codec, rt5622_vol_snd_controls, + ARRAY_SIZE(rt5622_vol_snd_controls)); + break; + case 0x23: + snd_soc_add_controls(codec, alc5623_vol_snd_controls, + ARRAY_SIZE(alc5623_vol_snd_controls)); + break; + default: + return -EINVAL; + } + + snd_soc_add_controls(codec, alc5623_snd_controls, + ARRAY_SIZE(alc5623_snd_controls)); + + snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets, + ARRAY_SIZE(alc5623_dapm_widgets)); + + /* set up audio path interconnects */ + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + switch (alc5623->id) { + case 0x21: + case 0x22: + snd_soc_dapm_new_controls(dapm, alc5623_dapm_amp_widgets, + ARRAY_SIZE(alc5623_dapm_amp_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_amp_spk, + ARRAY_SIZE(intercon_amp_spk)); + break; + case 0x23: + snd_soc_dapm_add_routes(dapm, intercon_spk, + ARRAY_SIZE(intercon_spk)); + break; + default: + return -EINVAL; + } + + return ret; +} + +/* power down chip */ +static int alc5623_remove(struct snd_soc_codec *codec) +{ + alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_device_alc5623 = { + .probe = alc5623_probe, + .remove = alc5623_remove, + .suspend = alc5623_suspend, + .resume = alc5623_resume, + .set_bias_level = alc5623_set_bias_level, + .reg_cache_size = ALC5623_VENDOR_ID2+2, + .reg_word_size = sizeof(u16), + .reg_cache_step = 2, +}; + +/* + * ALC5623 2 wire address is determined by A1 pin + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static int alc5623_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct alc5623_platform_data *pdata; + struct alc5623_priv *alc5623; + int ret, vid1, vid2; + + vid1 = i2c_smbus_read_word_data(client, ALC5623_VENDOR_ID1); + if (vid1 < 0) { + dev_err(&client->dev, "failed to read I2C\n"); + return -EIO; + } + vid1 = ((vid1 & 0xff) << 8) | (vid1 >> 8); + + vid2 = i2c_smbus_read_byte_data(client, ALC5623_VENDOR_ID2); + if (vid2 < 0) { + dev_err(&client->dev, "failed to read I2C\n"); + return -EIO; + } + + if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { + dev_err(&client->dev, "unknown or wrong codec\n"); + dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n", + 0x10ec, id->driver_data, + vid1, vid2); + return -ENODEV; + } + + dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2); + + alc5623 = kzalloc(sizeof(struct alc5623_priv), GFP_KERNEL); + if (alc5623 == NULL) + return -ENOMEM; + + pdata = client->dev.platform_data; + if (pdata) { + alc5623->add_ctrl = pdata->add_ctrl; + alc5623->jack_det_ctrl = pdata->jack_det_ctrl; + } + + alc5623->id = vid2; + switch (alc5623->id) { + case 0x21: + alc5623_dai.name = "alc5621-hifi"; + break; + case 0x22: + alc5623_dai.name = "alc5622-hifi"; + break; + case 0x23: + alc5623_dai.name = "alc5623-hifi"; + break; + default: + kfree(alc5623); + return -EINVAL; + } + + i2c_set_clientdata(client, alc5623); + alc5623->control_data = client; + alc5623->control_type = SND_SOC_I2C; + mutex_init(&alc5623->mutex); + + ret = snd_soc_register_codec(&client->dev, + &soc_codec_device_alc5623, &alc5623_dai, 1); + if (ret != 0) { + dev_err(&client->dev, "Failed to register codec: %d\n", ret); + kfree(alc5623); + } + + return ret; +} + +static int alc5623_i2c_remove(struct i2c_client *client) +{ + struct alc5623_priv *alc5623 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + kfree(alc5623); + return 0; +} + +static const struct i2c_device_id alc5623_i2c_table[] = { + {"alc5621", 0x21}, + {"alc5622", 0x22}, + {"alc5623", 0x23}, + {} +}; +MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); + +/* i2c codec control layer */ +static struct i2c_driver alc5623_i2c_driver = { + .driver = { + .name = "alc562x-codec", + .owner = THIS_MODULE, + }, + .probe = alc5623_i2c_probe, + .remove = __devexit_p(alc5623_i2c_remove), + .id_table = alc5623_i2c_table, +}; + +static int __init alc5623_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&alc5623_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "%s: can't add i2c driver", __func__); + return ret; + } + + return ret; +} +module_init(alc5623_modinit); + +static void __exit alc5623_modexit(void) +{ + i2c_del_driver(&alc5623_i2c_driver); +} +module_exit(alc5623_modexit); + +MODULE_DESCRIPTION("ASoC alc5621/2/3 driver"); +MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/alc5623.h b/sound/soc/codecs/alc5623.h new file mode 100644 index 000000000000..f3d68260d425 --- /dev/null +++ b/sound/soc/codecs/alc5623.h @@ -0,0 +1,161 @@ +/* + * alc5623.h -- alc562[123] ALSA Soc Audio driver + * + * Copyright 2008 Realtek Microelectronics + * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org> + * + * Author: flove <flove@realtek.com> + * Arnaud Patard <arnaud.patard@rtp-net.org> + * + * 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 _ALC5623_H +#define _ALC5623_H + +#define ALC5623_RESET 0x00 +/* 5621 5622 5623 */ +/* speaker output vol 2 2 */ +/* line output vol 4 2 */ +/* HP output vol 4 0 4 */ +#define ALC5623_SPK_OUT_VOL 0x02 +#define ALC5623_HP_OUT_VOL 0x04 +#define ALC5623_MONO_AUX_OUT_VOL 0x06 +#define ALC5623_AUXIN_VOL 0x08 +#define ALC5623_LINE_IN_VOL 0x0A +#define ALC5623_STEREO_DAC_VOL 0x0C +#define ALC5623_MIC_VOL 0x0E +#define ALC5623_MIC_ROUTING_CTRL 0x10 +#define ALC5623_ADC_REC_GAIN 0x12 +#define ALC5623_ADC_REC_MIXER 0x14 +#define ALC5623_SOFT_VOL_CTRL_TIME 0x16 +/* ALC5623_OUTPUT_MIXER_CTRL : */ +/* same remark as for reg 2 line vs speaker */ +#define ALC5623_OUTPUT_MIXER_CTRL 0x1C +#define ALC5623_MIC_CTRL 0x22 + +#define ALC5623_DAI_CONTROL 0x34 +#define ALC5623_DAI_SDP_MASTER_MODE (0 << 15) +#define ALC5623_DAI_SDP_SLAVE_MODE (1 << 15) +#define ALC5623_DAI_I2S_PCM_MODE (1 << 14) +#define ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7) +#define ALC5623_DAI_ADC_DATA_L_R_SWAP (1 << 5) +#define ALC5623_DAI_DAC_DATA_L_R_SWAP (1 << 4) +#define ALC5623_DAI_I2S_DL_MASK (3 << 2) +#define ALC5623_DAI_I2S_DL_32 (3 << 2) +#define ALC5623_DAI_I2S_DL_24 (2 << 2) +#define ALC5623_DAI_I2S_DL_20 (1 << 2) +#define ALC5623_DAI_I2S_DL_16 (0 << 2) +#define ALC5623_DAI_I2S_DF_PCM (3 << 0) +#define ALC5623_DAI_I2S_DF_LEFT (2 << 0) +#define ALC5623_DAI_I2S_DF_RIGHT (1 << 0) +#define ALC5623_DAI_I2S_DF_I2S (0 << 0) + +#define ALC5623_STEREO_AD_DA_CLK_CTRL 0x36 +#define ALC5623_COMPANDING_CTRL 0x38 + +#define ALC5623_PWR_MANAG_ADD1 0x3A +#define ALC5623_PWR_ADD1_MAIN_I2S_EN (1 << 15) +#define ALC5623_PWR_ADD1_ZC_DET_PD_EN (1 << 14) +#define ALC5623_PWR_ADD1_MIC1_BIAS_EN (1 << 11) +#define ALC5623_PWR_ADD1_SHORT_CURR_DET_EN (1 << 10) +#define ALC5623_PWR_ADD1_SOFTGEN_EN (1 << 8) /* rsvd on 5622 */ +#define ALC5623_PWR_ADD1_DEPOP_BUF_HP (1 << 6) /* rsvd on 5622 */ +#define ALC5623_PWR_ADD1_HP_OUT_AMP (1 << 5) +#define ALC5623_PWR_ADD1_HP_OUT_ENH_AMP (1 << 4) /* rsvd on 5622 */ +#define ALC5623_PWR_ADD1_DEPOP_BUF_AUX (1 << 2) +#define ALC5623_PWR_ADD1_AUX_OUT_AMP (1 << 1) +#define ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP (1 << 0) /* rsvd on 5622 */ + +#define ALC5623_PWR_MANAG_ADD2 0x3C +#define ALC5623_PWR_ADD2_LINEOUT (1 << 15) /* rt5623 */ +#define ALC5623_PWR_ADD2_CLASS_AB (1 << 15) /* rt5621 */ +#define ALC5623_PWR_ADD2_CLASS_D (1 << 14) /* rt5621 */ +#define ALC5623_PWR_ADD2_VREF (1 << 13) +#define ALC5623_PWR_ADD2_PLL (1 << 12) +#define ALC5623_PWR_ADD2_DAC_REF_CIR (1 << 10) +#define ALC5623_PWR_ADD2_L_DAC_CLK (1 << 9) +#define ALC5623_PWR_ADD2_R_DAC_CLK (1 << 8) +#define ALC5623_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7) +#define ALC5623_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6) +#define ALC5623_PWR_ADD2_L_HP_MIXER (1 << 5) +#define ALC5623_PWR_ADD2_R_HP_MIXER (1 << 4) +#define ALC5623_PWR_ADD2_SPK_MIXER (1 << 3) +#define ALC5623_PWR_ADD2_MONO_MIXER (1 << 2) +#define ALC5623_PWR_ADD2_L_ADC_REC_MIXER (1 << 1) +#define ALC5623_PWR_ADD2_R_ADC_REC_MIXER (1 << 0) + +#define ALC5623_PWR_MANAG_ADD3 0x3E +#define ALC5623_PWR_ADD3_MAIN_BIAS (1 << 15) +#define ALC5623_PWR_ADD3_AUXOUT_L_VOL_AMP (1 << 14) +#define ALC5623_PWR_ADD3_AUXOUT_R_VOL_AMP (1 << 13) +#define ALC5623_PWR_ADD3_SPK_OUT (1 << 12) +#define ALC5623_PWR_ADD3_HP_L_OUT_VOL (1 << 10) +#define ALC5623_PWR_ADD3_HP_R_OUT_VOL (1 << 9) +#define ALC5623_PWR_ADD3_LINEIN_L_VOL (1 << 7) +#define ALC5623_PWR_ADD3_LINEIN_R_VOL (1 << 6) +#define ALC5623_PWR_ADD3_AUXIN_L_VOL (1 << 5) +#define ALC5623_PWR_ADD3_AUXIN_R_VOL (1 << 4) +#define ALC5623_PWR_ADD3_MIC1_FUN_CTRL (1 << 3) +#define ALC5623_PWR_ADD3_MIC2_FUN_CTRL (1 << 2) +#define ALC5623_PWR_ADD3_MIC1_BOOST_AD (1 << 1) +#define ALC5623_PWR_ADD3_MIC2_BOOST_AD (1 << 0) + +#define ALC5623_ADD_CTRL_REG 0x40 + +#define ALC5623_GLOBAL_CLK_CTRL_REG 0x42 +#define ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL (1 << 15) +#define ALC5623_GBL_CLK_SYS_SOUR_SEL_MCLK (0 << 15) +#define ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK (1 << 14) +#define ALC5623_GBL_CLK_PLL_SOUR_SEL_MCLK (0 << 14) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV8 (3 << 1) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV4 (2 << 1) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV2 (1 << 1) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV1 (0 << 1) +#define ALC5623_GBL_CLK_PLL_PRE_DIV2 (1 << 0) +#define ALC5623_GBL_CLK_PLL_PRE_DIV1 (0 << 0) + +#define ALC5623_PLL_CTRL 0x44 +#define ALC5623_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8) +#define ALC5623_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4) +#define ALC5623_PLL_CTRL_M_VAL(m) ((m)&0xf) + +#define ALC5623_GPIO_OUTPUT_PIN_CTRL 0x4A +#define ALC5623_GPIO_PIN_CONFIG 0x4C +#define ALC5623_GPIO_PIN_POLARITY 0x4E +#define ALC5623_GPIO_PIN_STICKY 0x50 +#define ALC5623_GPIO_PIN_WAKEUP 0x52 +#define ALC5623_GPIO_PIN_STATUS 0x54 +#define ALC5623_GPIO_PIN_SHARING 0x56 +#define ALC5623_OVER_CURR_STATUS 0x58 +#define ALC5623_JACK_DET_CTRL 0x5A + +#define ALC5623_MISC_CTRL 0x5E +#define ALC5623_MISC_DISABLE_FAST_VREG (1 << 15) +#define ALC5623_MISC_SPK_CLASS_AB_OC_PD (1 << 13) /* 5621 */ +#define ALC5623_MISC_SPK_CLASS_AB_OC_DET (1 << 12) /* 5621 */ +#define ALC5623_MISC_HP_DEPOP_MODE3_EN (1 << 10) +#define ALC5623_MISC_HP_DEPOP_MODE2_EN (1 << 9) +#define ALC5623_MISC_HP_DEPOP_MODE1_EN (1 << 8) +#define ALC5623_MISC_AUXOUT_DEPOP_MODE3_EN (1 << 6) +#define ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN (1 << 5) +#define ALC5623_MISC_AUXOUT_DEPOP_MODE1_EN (1 << 4) +#define ALC5623_MISC_M_DAC_L_INPUT (1 << 3) +#define ALC5623_MISC_M_DAC_R_INPUT (1 << 2) +#define ALC5623_MISC_IRQOUT_INV_CTRL (1 << 0) + +#define ALC5623_PSEDUEO_SPATIAL_CTRL 0x60 +#define ALC5623_EQ_CTRL 0x62 +#define ALC5623_EQ_MODE_ENABLE 0x66 +#define ALC5623_AVC_CTRL 0x68 +#define ALC5623_HID_CTRL_INDEX 0x6A +#define ALC5623_HID_CTRL_DATA 0x6C +#define ALC5623_VENDOR_ID1 0x7C +#define ALC5623_VENDOR_ID2 0x7E + +#define ALC5623_PLL_FR_MCLK 0 +#define ALC5623_PLL_FR_BCK 1 +#endif diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 823643932dde..b8066ef10bb0 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -36,8 +36,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dai.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <mach/dm365.h> @@ -116,7 +114,7 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec, DAVINCI_VC_REG12_POWER_ALL_OFF); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -155,7 +153,8 @@ static int cq93vc_resume(struct snd_soc_codec *codec) static int cq93vc_probe(struct snd_soc_codec *codec) { - struct davinci_vc *davinci_vc = codec->dev->platform_data; + struct davinci_vc *davinci_vc = + mfd_get_data(to_platform_device(codec->dev)); davinci_vc->cq93vc.codec = codec; codec->control_data = davinci_vc; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 6d4bdc609ac8..0206a17d7283 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -106,6 +106,21 @@ #define CS4270_MUTE_DAC_A 0x01 #define CS4270_MUTE_DAC_B 0x02 +/* Power-on default values for the registers + * + * This array contains the power-on default values of the registers, with the + * exception of the "CHIPID" register (01h). The lower four bits of that + * register contain the hardware revision, so it is treated as volatile. + * + * Also note that on the CS4270, the first readable register is 1, but ASoC + * assumes the first register is 0. Therfore, the array must have an entry for + * register 0, but we use cs4270_reg_is_readable() to tell ASoC that it can't + * be read. + */ +static const u8 cs4270_default_reg_cache[CS4270_LASTREG + 1] = { + 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x20, 0x00, 0x00 +}; + static const char *supply_names[] = { "va", "vd", "vlc" }; @@ -114,7 +129,6 @@ static const char *supply_names[] = { struct cs4270_private { enum snd_soc_control_type control_type; void *control_data; - u8 reg_cache[CS4270_NUMREGS]; unsigned int mclk; /* Input frequency of the MCLK pin */ unsigned int mode; /* The mode (I2S or left-justified) */ unsigned int slave_mode; @@ -179,6 +193,20 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { /* The number of MCLK/LRCK ratios supported by the CS4270 */ #define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) +static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg) +{ + return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); +} + +static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg) +{ + /* Unreadable registers are considered volatile */ + if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) + return 1; + + return reg == CS4270_CHIPID; +} + /** * cs4270_set_dai_sysclk - determine the CS4270 samples rates. * @codec_dai: the codec DAI @@ -264,97 +292,6 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, } /** - * cs4270_fill_cache - pre-fill the CS4270 register cache. - * @codec: the codec for this CS4270 - * - * This function fills in the CS4270 register cache by reading the register - * values from the hardware. - * - * This CS4270 registers are cached to avoid excessive I2C I/O operations. - * After the initial read to pre-fill the cache, the CS4270 never updates - * the register values, so we won't have a cache coherency problem. - * - * We use the auto-increment feature of the CS4270 to read all registers in - * one shot. - */ -static int cs4270_fill_cache(struct snd_soc_codec *codec) -{ - u8 *cache = codec->reg_cache; - struct i2c_client *i2c_client = codec->control_data; - s32 length; - - length = i2c_smbus_read_i2c_block_data(i2c_client, - CS4270_FIRSTREG | CS4270_I2C_INCR, CS4270_NUMREGS, cache); - - if (length != CS4270_NUMREGS) { - dev_err(codec->dev, "i2c read failure, addr=0x%x\n", - i2c_client->addr); - return -EIO; - } - - return 0; -} - -/** - * cs4270_read_reg_cache - read from the CS4270 register cache. - * @codec: the codec for this CS4270 - * @reg: the register to read - * - * This function returns the value for a given register. It reads only from - * the register cache, not the hardware itself. - * - * This CS4270 registers are cached to avoid excessive I2C I/O operations. - * After the initial read to pre-fill the cache, the CS4270 never updates - * the register values, so we won't have a cache coherency problem. - */ -static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *cache = codec->reg_cache; - - if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) - return -EIO; - - return cache[reg - CS4270_FIRSTREG]; -} - -/** - * cs4270_i2c_write - write to a CS4270 register via the I2C bus. - * @codec: the codec for this CS4270 - * @reg: the register to write - * @value: the value to write to the register - * - * This function writes the given value to the given CS4270 register, and - * also updates the register cache. - * - * Note that we don't use the hw_write function pointer of snd_soc_codec. - * That's because it's too clunky: the hw_write_t prototype does not match - * i2c_smbus_write_byte_data(), and it's just another layer of overhead. - */ -static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 *cache = codec->reg_cache; - - if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) - return -EIO; - - /* Only perform an I2C operation if the new value is different */ - if (cache[reg - CS4270_FIRSTREG] != value) { - struct i2c_client *client = codec->control_data; - if (i2c_smbus_write_byte_data(client, reg, value)) { - dev_err(codec->dev, "i2c write failed\n"); - return -EIO; - } - - /* We've written to the hardware, so update the cache */ - cache[reg - CS4270_FIRSTREG] = value; - } - - return 0; -} - -/** * cs4270_hw_params - program the CS4270 with the given hardware parameters. * @substream: the audio stream * @params: the hardware parameters to set @@ -551,15 +488,16 @@ static struct snd_soc_dai_driver cs4270_dai = { static int cs4270_probe(struct snd_soc_codec *codec) { struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); - int i, ret, reg; + int i, ret; codec->control_data = cs4270->control_data; - /* The I2C interface is set up, so pre-fill our register cache */ - - ret = cs4270_fill_cache(codec); + /* Tell ASoC what kind of I/O to use to read the registers. ASoC will + * then do the I2C transactions itself. + */ + ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs4270->control_type); if (ret < 0) { - dev_err(codec->dev, "failed to fill register cache\n"); + dev_err(codec->dev, "failed to set cache I/O (ret=%i)\n", ret); return ret; } @@ -568,10 +506,7 @@ static int cs4270_probe(struct snd_soc_codec *codec) * this feature disabled by default. An application (e.g. alsactl) can * re-enabled it by using the controls. */ - - reg = cs4270_read_reg_cache(codec, CS4270_MUTE); - reg &= ~CS4270_MUTE_AUTO; - ret = cs4270_i2c_write(codec, CS4270_MUTE, reg); + ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0); if (ret < 0) { dev_err(codec->dev, "i2c write failed\n"); return ret; @@ -582,10 +517,8 @@ static int cs4270_probe(struct snd_soc_codec *codec) * playback has started. An application (e.g. alsactl) can * re-enabled it by using the controls. */ - - reg = cs4270_read_reg_cache(codec, CS4270_TRANS); - reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO); - ret = cs4270_i2c_write(codec, CS4270_TRANS, reg); + ret = snd_soc_update_bits(codec, CS4270_TRANS, + CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0); if (ret < 0) { dev_err(codec->dev, "i2c write failed\n"); return ret; @@ -708,15 +641,16 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec) * Assign this variable to the codec_dev field of the machine driver's * snd_soc_device structure. */ -static struct snd_soc_codec_driver soc_codec_device_cs4270 = { - .probe = cs4270_probe, - .remove = cs4270_remove, - .suspend = cs4270_soc_suspend, - .resume = cs4270_soc_resume, - .read = cs4270_read_reg_cache, - .write = cs4270_i2c_write, - .reg_cache_size = CS4270_NUMREGS, - .reg_word_size = sizeof(u8), +static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { + .probe = cs4270_probe, + .remove = cs4270_remove, + .suspend = cs4270_soc_suspend, + .resume = cs4270_soc_resume, + .volatile_register = cs4270_reg_is_volatile, + .readable_register = cs4270_reg_is_readable, + .reg_cache_size = CS4270_LASTREG + 1, + .reg_word_size = sizeof(u8), + .reg_cache_default = cs4270_default_reg_cache, }; /** @@ -785,7 +719,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client) /* * cs4270_id - I2C device IDs supported by this driver */ -static struct i2c_device_id cs4270_id[] = { +static const struct i2c_device_id cs4270_id[] = { {"cs4270", 0}, {} }; @@ -809,8 +743,6 @@ static struct i2c_driver cs4270_i2c_driver = { static int __init cs4270_init(void) { - pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n"); - return i2c_add_driver(&cs4270_i2c_driver); } module_init(cs4270_init); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c new file mode 100644 index 000000000000..083aab96ca80 --- /dev/null +++ b/sound/soc/codecs/cs4271.c @@ -0,0 +1,667 @@ +/* + * CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * 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. + * + * 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. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <sound/cs4271.h> + +#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000 + +/* + * CS4271 registers + * High byte represents SPI chip address (0x10) + write command (0) + * Low byte - codec register address + */ +#define CS4271_MODE1 0x2001 /* Mode Control 1 */ +#define CS4271_DACCTL 0x2002 /* DAC Control */ +#define CS4271_DACVOL 0x2003 /* DAC Volume & Mixing Control */ +#define CS4271_VOLA 0x2004 /* DAC Channel A Volume Control */ +#define CS4271_VOLB 0x2005 /* DAC Channel B Volume Control */ +#define CS4271_ADCCTL 0x2006 /* ADC Control */ +#define CS4271_MODE2 0x2007 /* Mode Control 2 */ +#define CS4271_CHIPID 0x2008 /* Chip ID */ + +#define CS4271_FIRSTREG CS4271_MODE1 +#define CS4271_LASTREG CS4271_MODE2 +#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) + +/* Bit masks for the CS4271 registers */ +#define CS4271_MODE1_MODE_MASK 0xC0 +#define CS4271_MODE1_MODE_1X 0x00 +#define CS4271_MODE1_MODE_2X 0x80 +#define CS4271_MODE1_MODE_4X 0xC0 + +#define CS4271_MODE1_DIV_MASK 0x30 +#define CS4271_MODE1_DIV_1 0x00 +#define CS4271_MODE1_DIV_15 0x10 +#define CS4271_MODE1_DIV_2 0x20 +#define CS4271_MODE1_DIV_3 0x30 + +#define CS4271_MODE1_MASTER 0x08 + +#define CS4271_MODE1_DAC_DIF_MASK 0x07 +#define CS4271_MODE1_DAC_DIF_LJ 0x00 +#define CS4271_MODE1_DAC_DIF_I2S 0x01 +#define CS4271_MODE1_DAC_DIF_RJ16 0x02 +#define CS4271_MODE1_DAC_DIF_RJ24 0x03 +#define CS4271_MODE1_DAC_DIF_RJ20 0x04 +#define CS4271_MODE1_DAC_DIF_RJ18 0x05 + +#define CS4271_DACCTL_AMUTE 0x80 +#define CS4271_DACCTL_IF_SLOW 0x40 + +#define CS4271_DACCTL_DEM_MASK 0x30 +#define CS4271_DACCTL_DEM_DIS 0x00 +#define CS4271_DACCTL_DEM_441 0x10 +#define CS4271_DACCTL_DEM_48 0x20 +#define CS4271_DACCTL_DEM_32 0x30 + +#define CS4271_DACCTL_SVRU 0x08 +#define CS4271_DACCTL_SRD 0x04 +#define CS4271_DACCTL_INVA 0x02 +#define CS4271_DACCTL_INVB 0x01 + +#define CS4271_DACVOL_BEQUA 0x40 +#define CS4271_DACVOL_SOFT 0x20 +#define CS4271_DACVOL_ZEROC 0x10 + +#define CS4271_DACVOL_ATAPI_MASK 0x0F +#define CS4271_DACVOL_ATAPI_M_M 0x00 +#define CS4271_DACVOL_ATAPI_M_BR 0x01 +#define CS4271_DACVOL_ATAPI_M_BL 0x02 +#define CS4271_DACVOL_ATAPI_M_BLR2 0x03 +#define CS4271_DACVOL_ATAPI_AR_M 0x04 +#define CS4271_DACVOL_ATAPI_AR_BR 0x05 +#define CS4271_DACVOL_ATAPI_AR_BL 0x06 +#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 +#define CS4271_DACVOL_ATAPI_AL_M 0x08 +#define CS4271_DACVOL_ATAPI_AL_BR 0x09 +#define CS4271_DACVOL_ATAPI_AL_BL 0x0A +#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B +#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C +#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D +#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E +#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F + +#define CS4271_VOLA_MUTE 0x80 +#define CS4271_VOLA_VOL_MASK 0x7F +#define CS4271_VOLB_MUTE 0x80 +#define CS4271_VOLB_VOL_MASK 0x7F + +#define CS4271_ADCCTL_DITHER16 0x20 + +#define CS4271_ADCCTL_ADC_DIF_MASK 0x10 +#define CS4271_ADCCTL_ADC_DIF_LJ 0x00 +#define CS4271_ADCCTL_ADC_DIF_I2S 0x10 + +#define CS4271_ADCCTL_MUTEA 0x08 +#define CS4271_ADCCTL_MUTEB 0x04 +#define CS4271_ADCCTL_HPFDA 0x02 +#define CS4271_ADCCTL_HPFDB 0x01 + +#define CS4271_MODE2_LOOP 0x10 +#define CS4271_MODE2_MUTECAEQUB 0x08 +#define CS4271_MODE2_FREEZE 0x04 +#define CS4271_MODE2_CPEN 0x02 +#define CS4271_MODE2_PDN 0x01 + +#define CS4271_CHIPID_PART_MASK 0xF0 +#define CS4271_CHIPID_REV_MASK 0x0F + +/* + * Default CS4271 power-up configuration + * Array contains non-existing in hw register at address 0 + * Array do not include Chip ID, as codec driver does not use + * registers read operations at all + */ +static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = { + 0, + 0, + CS4271_DACCTL_AMUTE, + CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, + 0, + 0, + 0, + 0, +}; + +struct cs4271_private { + /* SND_SOC_I2C or SND_SOC_SPI */ + enum snd_soc_control_type bus_type; + void *control_data; + unsigned int mclk; + bool master; + bool deemph; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + /* GPIO that disable serial bus, if any */ + int gpio_disable; +}; + +/* + * @freq is the desired MCLK rate + * MCLK rate should (c) be the sample rate, multiplied by one of the + * ratios listed in cs4271_mclk_fs_ratios table + */ +static int cs4271_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 cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->mclk = freq; + return 0; +} + +static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + int ret; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4271->master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4271->master = 1; + val |= CS4271_MODE1_MASTER; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val |= CS4271_MODE1_DAC_DIF_LJ; + ret = snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); + if (ret < 0) + return ret; + break; + case SND_SOC_DAIFMT_I2S: + val |= CS4271_MODE1_DAC_DIF_I2S; + ret = snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); + if (ret < 0) + return ret; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_deemph[] = {0, 44100, 48000, 32000}; + +static int cs4271_set_deemph(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i, ret; + int val = CS4271_DACCTL_DEM_DIS; + + if (cs4271->deemph) { + /* Find closest de-emphasis freq */ + val = 1; + for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) + if (abs(cs4271_deemph[i] - cs4271->rate) < + abs(cs4271_deemph[val] - cs4271->rate)) + val = i; + val <<= 4; + } + + ret = snd_soc_update_bits(codec, CS4271_DACCTL, + CS4271_DACCTL_DEM_MASK, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = cs4271->deemph; + return 0; +} + +static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->deemph = ucontrol->value.enumerated.item[0]; + return cs4271_set_deemph(codec); +} + +struct cs4271_clk_cfg { + bool master; /* codec mode */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + unsigned short ratio; /* MCLK / sample rate */ + u8 ratio_mask; /* ratio bit mask for Master mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3}, + {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2}, +}; + +#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + +static int cs4271_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 cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i, ret; + unsigned int ratio, val; + + cs4271->rate = params_rate(params); + + /* Configure DAC */ + if (cs4271->rate < 50000) + val = CS4271_MODE1_MODE_1X; + else if (cs4271->rate < 100000) + val = CS4271_MODE1_MODE_2X; + else + val = CS4271_MODE1_MODE_4X; + + ratio = cs4271->mclk / cs4271->rate; + for (i = 0; i < CS4171_NR_RATIOS; i++) + if ((cs4271_clk_tab[i].master == cs4271->master) && + (cs4271_clk_tab[i].speed_mode == val) && + (cs4271_clk_tab[i].ratio == ratio)) + break; + + if (i == CS4171_NR_RATIOS) { + dev_err(codec->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + val |= cs4271_clk_tab[i].ratio_mask; + + ret = snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); + if (ret < 0) + return ret; + + return cs4271_set_deemph(codec); +} + +static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int ret; + int val_a = 0; + int val_b = 0; + + if (mute) { + val_a = CS4271_VOLA_MUTE; + val_b = CS4271_VOLB_MUTE; + } + + ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a); + if (ret < 0) + return ret; + ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b); + if (ret < 0) + return ret; + + return 0; +} + +/* CS4271 controls */ +static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); + +static const struct snd_kcontrol_new cs4271_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB, + 0, 0x7F, 1, cs4271_dac_tlv), + SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + cs4271_get_deemph, cs4271_put_deemph), + SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0), + SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0), + SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0), + SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0), + SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1), + SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0), + SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1), + SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB, + 7, 1, 1), +}; + +static struct snd_soc_dai_ops cs4271_dai_ops = { + .hw_params = cs4271_hw_params, + .set_sysclk = cs4271_set_dai_sysclk, + .set_fmt = cs4271_set_dai_fmt, + .digital_mute = cs4271_digital_mute, +}; + +static struct snd_soc_dai_driver cs4271_dai = { + .name = "cs4271-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = CS4271_PCM_RATES, + .formats = CS4271_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS4271_PCM_RATES, + .formats = CS4271_PCM_FORMATS, + }, + .ops = &cs4271_dai_ops, + .symmetric_rates = 1, +}; + +#ifdef CONFIG_PM +static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +{ + int ret; + /* Set power-down bit */ + ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_soc_resume(struct snd_soc_codec *codec) +{ + int ret; + /* Restore codec state */ + ret = snd_soc_cache_sync(codec); + if (ret < 0) + return ret; + /* then disable the power-down bit */ + ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + return 0; +} +#else +#define cs4271_soc_suspend NULL +#define cs4271_soc_resume NULL +#endif /* CONFIG_PM */ + +static int cs4271_probe(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; + int ret; + int gpio_nreset = -EINVAL; + + codec->control_data = cs4271->control_data; + + if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + + if (gpio_nreset >= 0) + if (gpio_request(gpio_nreset, "CS4271 Reset")) + gpio_nreset = -EINVAL; + if (gpio_nreset >= 0) { + /* Reset codec */ + gpio_direction_output(gpio_nreset, 0); + udelay(1); + gpio_set_value(gpio_nreset, 1); + /* Give the codec time to wake up */ + udelay(1); + } + + cs4271->gpio_nreset = gpio_nreset; + + /* + * In case of I2C, chip address specified in board data. + * So cache IO operations use 8 bit codec register address. + * In case of SPI, chip address and register address + * passed together as 16 bit value. + * Anyway, register address is masked with 0xFF inside + * soc-cache code. + */ + if (cs4271->bus_type == SND_SOC_SPI) + ret = snd_soc_codec_set_cache_io(codec, 16, 8, + cs4271->bus_type); + else + ret = snd_soc_codec_set_cache_io(codec, 8, 8, + cs4271->bus_type); + if (ret) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN); + if (ret < 0) + return ret; + ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + /* Power-up sequence requires 85 uS */ + udelay(85); + + return snd_soc_add_controls(codec, cs4271_snd_controls, + ARRAY_SIZE(cs4271_snd_controls)); +} + +static int cs4271_remove(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int gpio_nreset; + + gpio_nreset = cs4271->gpio_nreset; + + if (gpio_is_valid(gpio_nreset)) { + /* Set codec to the reset state */ + gpio_set_value(gpio_nreset, 0); + gpio_free(gpio_nreset); + } + + return 0; +}; + +static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { + .probe = cs4271_probe, + .remove = cs4271_remove, + .suspend = cs4271_soc_suspend, + .resume = cs4271_soc_resume, + .reg_cache_default = cs4271_dflt_reg, + .reg_cache_size = ARRAY_SIZE(cs4271_dflt_reg), + .reg_word_size = sizeof(cs4271_dflt_reg[0]), + .compress_type = SND_SOC_FLAT_COMPRESSION, +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit cs4271_spi_probe(struct spi_device *spi) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + spi_set_drvdata(spi, cs4271); + cs4271->control_data = spi; + cs4271->bus_type = SND_SOC_SPI; + + return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .probe = cs4271_spi_probe, + .remove = __devexit_p(cs4271_spi_remove), +}; +#endif /* defined(CONFIG_SPI_MASTER) */ + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static const struct i2c_device_id cs4271_i2c_id[] = { + {"cs4271", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static int __devinit cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + i2c_set_clientdata(client, cs4271); + cs4271->control_data = client; + cs4271->bus_type = SND_SOC_I2C; + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .id_table = cs4271_i2c_id, + .probe = cs4271_i2c_probe, + .remove = __devexit_p(cs4271_i2c_remove), +}; +#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */ + +/* + * We only register our serial bus driver here without + * assignment to particular chip. So if any of the below + * fails, there is some problem with I2C or SPI subsystem. + * In most cases this module will be compiled with support + * of only one serial bus. + */ +static int __init cs4271_modinit(void) +{ + int ret; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&cs4271_i2c_driver); + if (ret) { + pr_err("Failed to register CS4271 I2C driver: %d\n", ret); + return ret; + } +#endif + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4271_spi_driver); + if (ret) { + pr_err("Failed to register CS4271 SPI driver: %d\n", ret); + return ret; + } +#endif + + return 0; +} +module_init(cs4271_modinit); + +static void __exit cs4271_modexit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4271_spi_driver); +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&cs4271_i2c_driver); +#endif +} +module_exit(cs4271_modexit); + +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index cb086eaf4e07..8fb7070108dd 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -26,7 +26,6 @@ #include <linux/slab.h> #include <sound/core.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/initval.h> #include <sound/pcm_params.h> @@ -47,7 +46,6 @@ struct cs42l51_private { unsigned int mclk; unsigned int audio_mode; /* The mode (I2S or left-justified) */ enum master_slave_mode func; - u8 reg_cache[CS42L51_NUMREGS]; }; #define CS42L51_FORMATS ( \ @@ -519,6 +517,7 @@ static struct snd_soc_dai_driver cs42l51_dai = { static int cs42l51_probe(struct snd_soc_codec *codec) { struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret, reg; codec->control_data = cs42l51->control_data; @@ -550,9 +549,9 @@ static int cs42l51_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, cs42l51_snd_controls, ARRAY_SIZE(cs42l51_snd_controls)); - snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets, + snd_soc_dapm_new_controls(dapm, cs42l51_dapm_widgets, ARRAY_SIZE(cs42l51_dapm_widgets)); - snd_soc_dapm_add_routes(codec, cs42l51_routes, + snd_soc_dapm_add_routes(dapm, cs42l51_routes, ARRAY_SIZE(cs42l51_routes)); return 0; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index e8d27c8f9ba3..0bb424af956f 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -18,7 +18,7 @@ #include <sound/core.h> #include <sound/initval.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include "cx20442.h" @@ -26,7 +26,6 @@ struct cx20442_priv { enum snd_soc_control_type control_type; void *control_data; - u8 reg_cache[1]; }; #define CX20442_PM 0x0 @@ -89,10 +88,11 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = { static int cx20442_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets, - ARRAY_SIZE(cx20442_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, cx20442_audio_map, + 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; @@ -263,7 +263,7 @@ static void v253_close(struct tty_struct *tty) /* Prevent the codec driver from further accessing the modem */ codec->hw_write = NULL; cx20442->control_data = NULL; - codec->pop_time = 0; + codec->card->pop_time = 0; } /* Line discipline .hangup() */ @@ -291,7 +291,7 @@ static void v253_receive(struct tty_struct *tty, /* Set up codec driver access to modem controls */ cx20442->control_data = tty; codec->hw_write = (hw_write_t)tty->ops->write; - codec->pop_time = 1; + codec->card->pop_time = 1; } } @@ -348,7 +348,7 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec) cx20442->control_data = NULL; codec->hw_write = NULL; - codec->pop_time = 0; + codec->card->pop_time = 0; return 0; } @@ -367,9 +367,12 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec) return 0; } +static const u8 cx20442_reg; + static struct snd_soc_codec_driver cx20442_codec_dev = { .probe = cx20442_codec_probe, .remove = cx20442_codec_remove, + .reg_cache_default = &cx20442_reg, .reg_cache_size = 1, .reg_word_size = sizeof(u8), .read = cx20442_read_reg_cache, diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 58bb9b994811..92fd9d7a9221 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -21,7 +21,7 @@ #include <linux/slab.h> #include <sound/pcm.h> #include <sound/pcm_params.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c new file mode 100644 index 000000000000..704bbde65737 --- /dev/null +++ b/sound/soc/codecs/dfbmcs320.c @@ -0,0 +1,72 @@ +/* + * Driver for the DFBM-CS320 bluetooth module + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> + * + * 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/platform_device.h> + +#include <sound/soc.h> + +static struct snd_soc_dai_driver dfbmcs320_dai = { + .name = "dfbmcs320-pcm", + .playback = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .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_dfbmcs320; + +static int __devinit dfbmcs320_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320, + &dfbmcs320_dai, 1); +} + +static int __devexit dfbmcs320_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver dfmcs320_driver = { + .driver = { + .name = "dfbmcs320", + .owner = THIS_MODULE, + }, + .probe = dfbmcs320_probe, + .remove = __devexit_p(dfbmcs320_remove), +}; + +static int __init dfbmcs320_init(void) +{ + return platform_driver_register(&dfmcs320_driver); +} +module_init(dfbmcs320_init); + +static void __exit dfbmcs320_exit(void) +{ + platform_driver_unregister(&dfmcs320_driver); +} +module_exit(dfbmcs320_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c new file mode 100644 index 000000000000..57e9dac88d38 --- /dev/null +++ b/sound/soc/codecs/dmic.c @@ -0,0 +1,81 @@ +/* + * dmic.c -- SoC audio for Generic Digital MICs + * + * Author: Liam Girdwood <lrg@slimlogic.co.uk> + * + * 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 <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +static struct snd_soc_dai_driver dmic_dai = { + .name = "dmic-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S32_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static struct snd_soc_codec_driver soc_dmic = {}; + +static int __devinit dmic_dev_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, + &soc_dmic, &dmic_dai, 1); +} + +static int __devexit dmic_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +MODULE_ALIAS("platform:dmic-codec"); + +static struct platform_driver dmic_driver = { + .driver = { + .name = "dmic-codec", + .owner = THIS_MODULE, + }, + .probe = dmic_dev_probe, + .remove = __devexit_p(dmic_dev_remove), +}; + +static int __init dmic_init(void) +{ + return platform_driver_register(&dmic_driver); +} +module_init(dmic_init); + +static void __exit dmic_exit(void) +{ + platform_driver_unregister(&dmic_driver); +} +module_exit(dmic_exit); + +MODULE_DESCRIPTION("Generic DMIC driver"); +MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 16253ec9b022..f5ccdbf7ebc6 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -22,7 +22,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/initval.h> -#include <sound/soc-dapm.h> #include <sound/soc.h> #define JZ4740_REG_CODEC_1 0x0 @@ -266,7 +265,7 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* The only way to clear the suspend flag is to reset the codec */ - if (codec->bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) jz4740_codec_wakeup(codec); mask = JZ4740_CODEC_1_VREF_DISABLE | @@ -288,27 +287,27 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } 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(codec, jz4740_codec_dapm_widgets, + snd_soc_dapm_new_controls(dapm, jz4740_codec_dapm_widgets, ARRAY_SIZE(jz4740_codec_dapm_widgets)); - snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes, + snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes, ARRAY_SIZE(jz4740_codec_dapm_routes)); - snd_soc_dapm_new_widgets(codec); - jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c new file mode 100644 index 000000000000..2c2a681da0d7 --- /dev/null +++ b/sound/soc/codecs/lm4857.c @@ -0,0 +1,276 @@ +/* + * LM4857 AMP driver + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> + * + * 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 <linux/slab.h> + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +struct lm4857 { + struct i2c_client *i2c; + uint8_t mode; +}; + +static const uint8_t lm4857_default_regs[] = { + 0x00, 0x00, 0x00, 0x00, +}; + +/* The register offsets in the cache array */ +#define LM4857_MVOL 0 +#define LM4857_LVOL 1 +#define LM4857_RVOL 2 +#define LM4857_CTRL 3 + +/* the shifts required to set these bits */ +#define LM4857_3D 5 +#define LM4857_WAKEUP 5 +#define LM4857_EPGAIN 4 + +static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + uint8_t data; + int ret; + + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return ret; + + data = (reg << 6) | value; + ret = i2c_master_send(codec->control_data, &data, 1); + if (ret != 1) { + dev_err(codec->dev, "Failed to write register: %d\n", ret); + return ret; + } + + return 0; +} + +static unsigned int lm4857_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret) + return -1; + + return val; +} + +static int lm4857_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = lm4857->mode; + + return 0; +} + +static int lm4857_set_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + uint8_t value = ucontrol->value.integer.value[0]; + + lm4857->mode = value; + + if (codec->dapm.bias_level == SND_SOC_BIAS_ON) + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6); + + return 1; +} + +static int lm4857_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6); + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0); + break; + default: + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static const char *lm4857_mode[] = { + "Earpiece", + "Loudspeaker", + "Loudspeaker + Headphone", + "Headphone", +}; + +static const struct soc_enum lm4857_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode); + +static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_OUTPUT("LS"), + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("EP"), +}; + +static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); +static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); + +static const struct snd_kcontrol_new lm4857_controls[] = { + SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0, + mono_tlv), + SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0), + SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0), + SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL, + LM4857_WAKEUP, 1, 0), + SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, + LM4857_EPGAIN, 1, 0), + + SOC_ENUM_EXT("Mode", lm4857_mode_enum, + lm4857_get_mode, lm4857_set_mode), +}; + +/* There is a demux between the input signal and the output signals. + * Currently there is no easy way to model it in ASoC and since it does not make + * much of a difference in practice simply connect the input direclty to the + * outputs. */ +static const struct snd_soc_dapm_route lm4857_routes[] = { + {"LS", NULL, "IN"}, + {"HP", NULL, "IN"}, + {"EP", NULL, "IN"}, +}; + +static int lm4857_probe(struct snd_soc_codec *codec) +{ + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + codec->control_data = lm4857->i2c; + + ret = snd_soc_add_controls(codec, lm4857_controls, + ARRAY_SIZE(lm4857_controls)); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets, + ARRAY_SIZE(lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, lm4857_routes, + ARRAY_SIZE(lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_new_widgets(dapm); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { + .write = lm4857_write, + .read = lm4857_read, + .probe = lm4857_probe, + .reg_cache_size = ARRAY_SIZE(lm4857_default_regs), + .reg_word_size = sizeof(uint8_t), + .reg_cache_default = lm4857_default_regs, + .set_bias_level = lm4857_set_bias_level, +}; + +static int __devinit lm4857_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lm4857 *lm4857; + int ret; + + lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL); + if (!lm4857) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm4857); + + lm4857->i2c = i2c; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); + + if (ret) { + kfree(lm4857); + return ret; + } + + return 0; +} + +static int __devexit lm4857_i2c_remove(struct i2c_client *i2c) +{ + struct lm4857 *lm4857 = i2c_get_clientdata(i2c); + + snd_soc_unregister_codec(&i2c->dev); + kfree(lm4857); + + return 0; +} + +static const struct i2c_device_id lm4857_i2c_id[] = { + { "lm4857", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); + +static struct i2c_driver lm4857_i2c_driver = { + .driver = { + .name = "lm4857", + .owner = THIS_MODULE, + }, + .probe = lm4857_i2c_probe, + .remove = __devexit_p(lm4857_i2c_remove), + .id_table = lm4857_i2c_id, +}; + +static int __init lm4857_init(void) +{ + return i2c_add_driver(&lm4857_i2c_driver); +} +module_init(lm4857_init); + +static void __exit lm4857_exit(void) +{ + i2c_del_driver(&lm4857_i2c_driver); +} +module_exit(lm4857_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("LM4857 amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index d63e28773eb1..bd0517cb7980 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -20,7 +20,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <linux/slab.h> @@ -40,7 +39,6 @@ struct max98088_cdata { }; struct max98088_priv { - u8 reg_cache[M98088_REG_CNT]; enum max98088_type devtype; void *control_data; struct max98088_pdata *pdata; @@ -610,7 +608,7 @@ static struct { { 0xFF, 0x00, 1 }, /* FF */ }; -static int max98088_volatile_register(unsigned int reg) +static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return max98088_access[reg].vol; } @@ -1230,15 +1228,17 @@ static const struct snd_soc_dapm_route audio_map[] = { static int max98088_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, max98088_dapm_widgets, + 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(codec, audio_map, ARRAY_SIZE(audio_map)); + 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(codec); + snd_soc_dapm_new_widgets(dapm); return 0; } @@ -1588,7 +1588,7 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai, static void max98088_sync_cache(struct snd_soc_codec *codec) { - struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); + u16 *reg_cache = codec->reg_cache; int i; if (!codec->cache_sync) @@ -1599,14 +1599,14 @@ static void max98088_sync_cache(struct snd_soc_codec *codec) /* write back cached values if they're writeable and * different from the hardware default. */ - for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) { + for (i = 1; i < codec->driver->reg_cache_size; i++) { if (!max98088_access[i].writable) continue; - if (max98088->reg_cache[i] == max98088_reg[i]) + if (reg_cache[i] == max98088_reg[i]) continue; - snd_soc_write(codec, i, max98088->reg_cache[i]); + snd_soc_write(codec, i, reg_cache[i]); } codec->cache_sync = 0; @@ -1623,7 +1623,7 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) max98088_sync_cache(codec); snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, @@ -1636,7 +1636,7 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec, codec->cache_sync = 1; break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1951,7 +1951,6 @@ static int max98088_probe(struct snd_soc_codec *codec) int ret = 0; codec->cache_sync = 1; - memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg)); ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); if (ret != 0) { @@ -1959,7 +1958,7 @@ static int max98088_probe(struct snd_soc_codec *codec) return ret; } - /* initalize private data */ + /* initialize private data */ max98088->sysclk = (unsigned)-1; max98088->eq_textcnt = 0; diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c new file mode 100644 index 000000000000..208d2ee61855 --- /dev/null +++ b/sound/soc/codecs/max9850.c @@ -0,0 +1,389 @@ +/* + * max9850.c -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * + * Author: Christian Glindkamp <christian.glindkamp@taskit.de> + * + * Initial development of this code was funded by + * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.de/ + * + * 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/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "max9850.h" + +struct max9850_priv { + unsigned int sysclk; +}; + +/* max9850 register cache */ +static const u8 max9850_reg[MAX9850_CACHEREGNUM] = { + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* these registers are not used at the moment but provided for the sake of + * completeness */ +static int max9850_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case MAX9850_STATUSA: + case MAX9850_STATUSB: + return 1; + default: + return 0; + } +} + +static const unsigned int max9850_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), + 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0), + 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0), + 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0), +}; + +static const struct snd_kcontrol_new max9850_controls[] = { +SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv), +SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1), +SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new max9850_mixer_controls[] = { + SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0), +SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0, + &max9850_mixer_controls[0], + ARRAY_SIZE(max9850_mixer_controls)), +SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0), +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_INPUT("INL"), +SND_SOC_DAPM_INPUT("INR"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* output mixer */ + {"Output Mixer", NULL, "DAC"}, + {"Output Mixer", "Line In Switch", "Line Input"}, + + /* outputs */ + {"Headphone Output", NULL, "Output Mixer"}, + {"HPL", NULL, "Headphone Output"}, + {"HPR", NULL, "Headphone Output"}, + {"OUTL", NULL, "Output Mixer"}, + {"OUTR", NULL, "Output Mixer"}, + + /* inputs */ + {"Line Input", NULL, "INL"}, + {"Line Input", NULL, "INR"}, + + /* supplies */ + {"Output Mixer", NULL, "Charge Pump 1"}, + {"Output Mixer", NULL, "Charge Pump 2"}, + {"Output Mixer", NULL, "SHDN"}, + {"DAC", NULL, "MCLK"}, +}; + +static int max9850_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 max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); + u64 lrclk_div; + u8 sf, da; + + if (!max9850->sysclk) + return -EINVAL; + + /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */ + sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1; + lrclk_div = (1 << 22); + lrclk_div *= params_rate(params); + lrclk_div *= sf; + do_div(lrclk_div, max9850->sysclk); + + snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f); + snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + da = 0; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + da = 0x2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + da = 0x3; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da); + + return 0; +} + +static int max9850_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 max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); + + /* calculate mclk -> iclk divider */ + if (freq <= 13000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x0); + else if (freq <= 26000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x4); + else if (freq <= 40000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x8); + else + return -EINVAL; + + max9850->sysclk = freq; + return 0; +} + +static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 da = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da |= MAX9850_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + da |= MAX9850_DLY; + break; + case SND_SOC_DAIFMT_RIGHT_J: + da |= MAX9850_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + da |= MAX9850_BCINV | MAX9850_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + da |= MAX9850_BCINV; + break; + case SND_SOC_DAIFMT_NB_IF: + da |= MAX9850_INV; + break; + default: + return -EINVAL; + } + + /* set da */ + snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da); + + return 0; +} + +static int max9850_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) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops max9850_dai_ops = { + .hw_params = max9850_hw_params, + .set_sysclk = max9850_set_dai_sysclk, + .set_fmt = max9850_set_dai_fmt, +}; + +static struct snd_soc_dai_driver max9850_dai = { + .name = "max9850-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9850_RATES, + .formats = MAX9850_FORMATS + }, + .ops = &max9850_dai_ops, +}; + +#ifdef CONFIG_PM +static int max9850_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + max9850_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max9850_resume(struct snd_soc_codec *codec) +{ + max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define max9850_suspend NULL +#define max9850_resume NULL +#endif + +static int max9850_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + 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; + } + + /* enable zero-detect */ + snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1); + /* enable slew-rate control */ + snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40); + /* set slew-rate 125ms */ + snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0); + + snd_soc_dapm_new_controls(dapm, max9850_dapm_widgets, + ARRAY_SIZE(max9850_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + snd_soc_add_controls(codec, max9850_controls, + ARRAY_SIZE(max9850_controls)); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max9850 = { + .probe = max9850_probe, + .suspend = max9850_suspend, + .resume = max9850_resume, + .set_bias_level = max9850_set_bias_level, + .reg_cache_size = ARRAY_SIZE(max9850_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = max9850_reg, + .volatile_register = max9850_volatile_register, +}; + +static int __devinit max9850_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max9850_priv *max9850; + int ret; + + max9850 = kzalloc(sizeof(struct max9850_priv), GFP_KERNEL); + if (max9850 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max9850); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max9850, &max9850_dai, 1); + if (ret < 0) + kfree(max9850); + return ret; +} + +static __devexit int max9850_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 max9850_i2c_id[] = { + { "max9850", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9850_i2c_id); + +static struct i2c_driver max9850_i2c_driver = { + .driver = { + .name = "max9850", + .owner = THIS_MODULE, + }, + .probe = max9850_i2c_probe, + .remove = __devexit_p(max9850_i2c_remove), + .id_table = max9850_i2c_id, +}; + +static int __init max9850_init(void) +{ + return i2c_add_driver(&max9850_i2c_driver); +} +module_init(max9850_init); + +static void __exit max9850_exit(void) +{ + i2c_del_driver(&max9850_i2c_driver); +} +module_exit(max9850_exit); + +MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>"); +MODULE_DESCRIPTION("ASoC MAX9850 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h new file mode 100644 index 000000000000..72b1ddb04b0d --- /dev/null +++ b/sound/soc/codecs/max9850.h @@ -0,0 +1,38 @@ +/* + * max9850.h -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * Author: Christian Glindkamp <christian.glindkamp@taskit.de> + * + * 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 _MAX9850_H +#define _MAX9850_H + +#define MAX9850_STATUSA 0x00 +#define MAX9850_STATUSB 0x01 +#define MAX9850_VOLUME 0x02 +#define MAX9850_GENERAL_PURPOSE 0x03 +#define MAX9850_INTERRUPT 0x04 +#define MAX9850_ENABLE 0x05 +#define MAX9850_CLOCK 0x06 +#define MAX9850_CHARGE_PUMP 0x07 +#define MAX9850_LRCLK_MSB 0x08 +#define MAX9850_LRCLK_LSB 0x09 +#define MAX9850_DIGITAL_AUDIO 0x0a + +#define MAX9850_CACHEREGNUM 11 + +/* MAX9850_DIGITAL_AUDIO */ +#define MAX9850_MASTER (1<<7) +#define MAX9850_INV (1<<6) +#define MAX9850_BCINV (1<<5) +#define MAX9850_DLY (1<<3) +#define MAX9850_RTJ (1<<2) + +#endif diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c new file mode 100644 index 000000000000..ff29380c9ed3 --- /dev/null +++ b/sound/soc/codecs/sgtl5000.c @@ -0,0 +1,1527 @@ +/* + * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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/slab.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/tlv.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "sgtl5000.h" + +#define SGTL5000_DAP_REG_OFFSET 0x0100 +#define SGTL5000_MAX_REG_OFFSET 0x013A + +/* default value of sgtl5000 registers except DAP */ +static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] = { + 0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */ + 0x0000, /* 0x0002, CHIP_DIG_POWER. */ + 0x0008, /* 0x0004, CHIP_CKL_CTRL */ + 0x0010, /* 0x0006, CHIP_I2S_CTRL */ + 0x0000, /* 0x0008, reserved */ + 0x0008, /* 0x000A, CHIP_SSS_CTRL */ + 0x0000, /* 0x000C, reserved */ + 0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */ + 0x3c3c, /* 0x0010, CHIP_DAC_VOL */ + 0x0000, /* 0x0012, reserved */ + 0x015f, /* 0x0014, CHIP_PAD_STRENGTH */ + 0x0000, /* 0x0016, reserved */ + 0x0000, /* 0x0018, reserved */ + 0x0000, /* 0x001A, reserved */ + 0x0000, /* 0x001E, reserved */ + 0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */ + 0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */ + 0x0111, /* 0x0024, CHIP_ANN_CTRL */ + 0x0000, /* 0x0026, CHIP_LINREG_CTRL */ + 0x0000, /* 0x0028, CHIP_REF_CTRL */ + 0x0000, /* 0x002A, CHIP_MIC_CTRL */ + 0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */ + 0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */ + 0x7060, /* 0x0030, CHIP_ANA_POWER */ + 0x5000, /* 0x0032, CHIP_PLL_CTRL */ + 0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */ + 0x0000, /* 0x0036, CHIP_ANA_STATUS */ + 0x0000, /* 0x0038, reserved */ + 0x0000, /* 0x003A, CHIP_ANA_TEST2 */ + 0x0000, /* 0x003C, CHIP_SHORT_CTRL */ + 0x0000, /* reserved */ +}; + +/* default value of dap registers */ +static const u16 sgtl5000_dap_regs[] = { + 0x0000, /* 0x0100, DAP_CONTROL */ + 0x0000, /* 0x0102, DAP_PEQ */ + 0x0040, /* 0x0104, DAP_BASS_ENHANCE */ + 0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */ + 0x0000, /* 0x0108, DAP_AUDIO_EQ */ + 0x0040, /* 0x010A, DAP_SGTL_SURROUND */ + 0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */ + 0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */ + 0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */ + 0x0000, /* 0x0112, reserved */ + 0x0000, /* 0x0114, reserved */ + 0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */ + 0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */ + 0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */ + 0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */ + 0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */ + 0x8000, /* 0x0120, DAP_MAIN_CHAN */ + 0x0000, /* 0x0122, DAP_MIX_CHAN */ + 0x0510, /* 0x0124, DAP_AVC_CTRL */ + 0x1473, /* 0x0126, DAP_AVC_THRESHOLD */ + 0x0028, /* 0x0128, DAP_AVC_ATTACK */ + 0x0050, /* 0x012A, DAP_AVC_DECAY */ + 0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */ + 0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */ + 0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */ + 0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */ + 0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */ + 0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */ + 0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */ + 0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */ +}; + +/* regulator supplies for sgtl5000, VDDD is an optional external supply */ +enum sgtl5000_regulator_supplies { + VDDA, + VDDIO, + VDDD, + SGTL5000_SUPPLY_NUM +}; + +/* vddd is optional supply */ +static const char *supply_names[SGTL5000_SUPPLY_NUM] = { + "VDDA", + "VDDIO", + "VDDD" +}; + +#define LDO_CONSUMER_NAME "VDDD_LDO" +#define LDO_VOLTAGE 1200000 + +static struct regulator_consumer_supply ldo_consumer[] = { + REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL), +}; + +static struct regulator_init_data ldo_init_data = { + .constraints = { + .min_uV = 850000, + .max_uV = 1600000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &ldo_consumer[0], +}; + +/* + * sgtl5000 internal ldo regulator, + * enabled when VDDD not provided + */ +struct ldo_regulator { + struct regulator_desc desc; + struct regulator_dev *dev; + int voltage; + void *codec_data; + bool enabled; +}; + +/* sgtl5000 private structure in codec */ +struct sgtl5000_priv { + int sysclk; /* sysclk rate */ + int master; /* i2s master or not */ + int fmt; /* i2s data format */ + struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; + struct ldo_regulator *ldo; +}; + +/* + * mic_bias power on/off share the same register bits with + * output impedance of mic bias, when power on mic bias, we + * need reclaim it to impedance value. + * 0x0 = Powered off + * 0x1 = 2Kohm + * 0x2 = 4Kohm + * 0x3 = 8Kohm + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias resistor to 4Kohm */ + snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_4k, SGTL5000_BIAS_R_4k); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* + * SGTL5000_BIAS_R_8k as mask to clean the two bits + * of mic bias and output impedance + */ + snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_8k, 0); + break; + } + return 0; +} + +/* + * using codec assist to small pop, hp_powerup or lineout_powerup + * should stay setting until vag_powerup is fully ramped down, + * vag fully ramped down require 400ms. + */ +static int small_pop_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + msleep(400); + break; + default: + break; + } + + return 0; +} + +/* input sources for ADC */ +static const char *adc_mux_text[] = { + "MIC_IN", "LINE_IN" +}; + +static const struct soc_enum adc_enum = +SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text); + +static const struct snd_kcontrol_new adc_mux = +SOC_DAPM_ENUM("Capture Mux", adc_enum); + +/* input sources for DAC */ +static const char *dac_mux_text[] = { + "DAC", "LINE_IN" +}; + +static const struct soc_enum dac_enum = +SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text); + +static const struct snd_kcontrol_new dac_mux = +SOC_DAPM_ENUM("Headphone Mux", dac_enum); + +static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("LINE_IN"), + SND_SOC_DAPM_INPUT("MIC_IN"), + + SND_SOC_DAPM_OUTPUT("HP_OUT"), + SND_SOC_DAPM_OUTPUT("LINE_OUT"), + + SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0, + small_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0, + small_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux), + SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux), + + /* aif for i2s input */ + SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", + 0, SGTL5000_CHIP_DIG_POWER, + 0, 0), + + /* aif for i2s output */ + SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture", + 0, SGTL5000_CHIP_DIG_POWER, + 1, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0), + + SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0), +}; + +/* routes for sgtl5000 */ +static const struct snd_soc_dapm_route audio_map[] = { + {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */ + {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */ + + {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ + {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ + + {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */ + {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ + {"LO", NULL, "DAC"}, /* dac --> line_out */ + + {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */ + {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */ + + {"LINE_OUT", NULL, "LO"}, + {"HP_OUT", NULL, "HP"}, +}; + +/* custom function to fetch info of PCM playback volume */ +static int dac_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xfc - 0x3c; + return 0; +} + +/* + * custom function to get of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * register value map to userspace value + * + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + * ------------------------------ + * userspace value 0xc0 0 + */ +static int dac_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg; + int l; + int r; + + reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL); + + /* get left channel volume */ + l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT; + + /* get right channel volume */ + r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT; + + /* make sure value fall in (0x3c,0xfc) */ + l = clamp(l, 0x3c, 0xfc); + r = clamp(r, 0x3c, 0xfc); + + /* invert it and map to userspace value */ + l = 0xfc - l; + r = 0xfc - r; + + ucontrol->value.integer.value[0] = l; + ucontrol->value.integer.value[1] = r; + + return 0; +} + +/* + * custom function to put of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * userspace value map to register value + * + * userspace value 0xc0 0 + * ------------------------------ + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + */ +static int dac_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg; + int l; + int r; + + l = ucontrol->value.integer.value[0]; + r = ucontrol->value.integer.value[1]; + + /* make sure userspace volume fall in (0, 0xfc-0x3c) */ + l = clamp(l, 0, 0xfc - 0x3c); + r = clamp(r, 0, 0xfc - 0x3c); + + /* invert it, get the value can be set to register */ + l = 0xfc - l; + r = 0xfc - r; + + /* shift to get the register value */ + reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT | + r << SGTL5000_DAC_VOL_RIGHT_SHIFT; + + snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0); + +/* tlv for mic gain, 0db 20db 30db 40db */ +static const unsigned int mic_gain_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0), +}; + +/* tlv for hp volume, -51.5db to 12.0db, step .5db */ +static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); + +static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { + /* SOC_DOUBLE_S8_TLV with invert */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = dac_info_volsw, + .get = dac_get_volsw, + .put = dac_put_volsw, + }, + + SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0), + SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)", + SGTL5000_CHIP_ANA_ADC_CTRL, + 8, 2, 0, capture_6db_attenuate), + SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), + + SOC_DOUBLE_TLV("Headphone Playback Volume", + SGTL5000_CHIP_ANA_HP_CTRL, + 0, 8, + 0x7f, 1, + headphone_volume), + SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL, + 5, 1, 0), + + SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL, + 0, 4, 0, mic_gain_tlv), +}; + +/* mute the codec used by alsa core */ +static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT; + + snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL, + adcdac_ctrl, mute ? adcdac_ctrl : 0); + + return 0; +} + +/* set codec format */ +static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + u16 i2sctl = 0; + + sgtl5000->master = 0; + /* + * i2s clock and frame master setting. + * ONLY support: + * - clock and frame slave, + * - clock and frame master + */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + i2sctl |= SGTL5000_I2S_MASTER; + sgtl5000->master = 1; + break; + default: + return -EINVAL; + } + + /* setting i2s data format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2sctl |= SGTL5000_I2S_MODE_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: + i2sctl |= SGTL5000_I2S_MODE_PCM; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + case SND_SOC_DAIFMT_I2S: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2sctl |= SGTL5000_I2S_MODE_RJ; + i2sctl |= SGTL5000_I2S_LRPOL; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + default: + return -EINVAL; + } + + sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + i2sctl |= SGTL5000_I2S_SCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl); + + return 0; +} + +/* set codec sysclk */ +static int sgtl5000_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case SGTL5000_SYSCLK: + sgtl5000->sysclk = freq; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * set clock according to i2s frame clock, + * sgtl5000 provide 2 clock sources. + * 1. sys_mclk. sample freq can only configure to + * 1/256, 1/384, 1/512 of sys_mclk. + * 2. pll. can derive any audio clocks. + * + * clock setting rules: + * 1. in slave mode, only sys_mclk can use. + * 2. as constraint by sys_mclk, sample freq should + * set to 32k, 44.1k and above. + * 3. using sys_mclk prefer to pll to save power. + */ +static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int clk_ctl = 0; + int sys_fs; /* sample freq */ + + /* + * sample freq should be divided by frame clock, + * if frame clock lower than 44.1khz, sample feq should set to + * 32khz or 44.1khz. + */ + switch (frame_rate) { + case 8000: + case 16000: + sys_fs = 32000; + break; + case 11025: + case 22050: + sys_fs = 44100; + break; + default: + sys_fs = frame_rate; + break; + } + + /* set divided factor of frame clock */ + switch (sys_fs / frame_rate) { + case 4: + clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT; + break; + case 2: + clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT; + break; + case 1: + clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + /* set the sys_fs according to frame rate */ + switch (sys_fs) { + case 32000: + clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT; + break; + case 44100: + clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT; + break; + case 48000: + clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT; + break; + case 96000: + clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT; + break; + default: + dev_err(codec->dev, "frame rate %d not supported\n", + frame_rate); + return -EINVAL; + } + + /* + * calculate the divider of mclk/sample_freq, + * factor of freq =96k can only be 256, since mclk in range (12m,27m) + */ + switch (sgtl5000->sysclk / sys_fs) { + case 256: + clk_ctl |= SGTL5000_MCLK_FREQ_256FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 384: + clk_ctl |= SGTL5000_MCLK_FREQ_384FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 512: + clk_ctl |= SGTL5000_MCLK_FREQ_512FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + default: + /* if mclk not satisify the divider, use pll */ + if (sgtl5000->master) { + clk_ctl |= SGTL5000_MCLK_FREQ_PLL << + SGTL5000_MCLK_FREQ_SHIFT; + } else { + dev_err(codec->dev, + "PLL not supported in slave mode\n"); + return -EINVAL; + } + } + + /* if using pll, please check manual 6.4.2 for detail */ + if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) { + u64 out, t; + int div2; + int pll_ctl; + unsigned int in, int_div, frac_div; + + if (sgtl5000->sysclk > 17000000) { + div2 = 1; + in = sgtl5000->sysclk / 2; + } else { + div2 = 0; + in = sgtl5000->sysclk; + } + if (sys_fs == 44100) + out = 180633600; + else + out = 196608000; + t = do_div(out, in); + int_div = out; + t *= 2048; + do_div(t, in); + frac_div = t; + pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT | + frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT; + + snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl); + if (div2) + snd_soc_update_bits(codec, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + SGTL5000_INPUT_FREQ_DIV2); + else + snd_soc_update_bits(codec, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + 0); + + /* power up pll */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + } else { + /* power down pll */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + 0); + } + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + * input: params_rate, params_fmt + */ +static int sgtl5000_pcm_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 sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int channels = params_channels(params); + int i2s_ctl = 0; + int stereo; + int ret; + + /* sysclk should already set */ + if (!sgtl5000->sysclk) { + dev_err(codec->dev, "%s: set sysclk first!\n", __func__); + return -EFAULT; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stereo = SGTL5000_DAC_STEREO; + else + stereo = SGTL5000_ADC_STEREO; + + /* set mono to save power */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo, + channels == 1 ? 0 : stereo); + + /* set codec clock base on lrclk */ + ret = sgtl5000_set_clock(codec, params_rate(params)); + if (ret) + return ret; + + /* set i2s data format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S32_LE: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl, i2s_ctl); + + return 0; +} + +#ifdef CONFIG_REGULATOR +static int ldo_regulator_is_enabled(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + + return ldo->enabled; +} + +static int ldo_regulator_enable(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + int reg; + + if (ldo_regulator_is_enabled(dev)) + return 0; + + /* set regulator value firstly */ + reg = (1600 - ldo->voltage / 1000) / 50; + reg = clamp(reg, 0x0, 0xf); + + /* amend the voltage value, unit: uV */ + ldo->voltage = (1600 - reg * 50) * 1000; + + /* set voltage to register */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, reg); + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + SGTL5000_LINEREG_D_POWERUP); + + /* when internal ldo enabled, simple digital power can be disabled */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP, + 0); + + ldo->enabled = 1; + return 0; +} + +static int ldo_regulator_disable(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + 0); + + /* clear voltage info */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, 0); + + ldo->enabled = 0; + + return 0; +} + +static int ldo_regulator_get_voltage(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + + return ldo->voltage; +} + +static struct regulator_ops ldo_regulator_ops = { + .is_enabled = ldo_regulator_is_enabled, + .enable = ldo_regulator_enable, + .disable = ldo_regulator_disable, + .get_voltage = ldo_regulator_get_voltage, +}; + +static int ldo_regulator_register(struct snd_soc_codec *codec, + struct regulator_init_data *init_data, + int voltage) +{ + struct ldo_regulator *ldo; + + ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL); + + if (!ldo) { + dev_err(codec->dev, "failed to allocate ldo_regulator\n"); + return -ENOMEM; + } + + ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL); + if (!ldo->desc.name) { + kfree(ldo); + dev_err(codec->dev, "failed to allocate decs name memory\n"); + return -ENOMEM; + } + + ldo->desc.type = REGULATOR_VOLTAGE; + ldo->desc.owner = THIS_MODULE; + ldo->desc.ops = &ldo_regulator_ops; + ldo->desc.n_voltages = 1; + + ldo->codec_data = codec; + ldo->voltage = voltage; + + ldo->dev = regulator_register(&ldo->desc, codec->dev, + init_data, ldo); + if (IS_ERR(ldo->dev)) { + int ret = PTR_ERR(ldo->dev); + + dev_err(codec->dev, "failed to register regulator\n"); + kfree(ldo->desc.name); + kfree(ldo); + + return ret; + } + + return 0; +} + +static int ldo_regulator_remove(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct ldo_regulator *ldo = sgtl5000->ldo; + + if (!ldo) + return 0; + + regulator_unregister(ldo->dev); + kfree(ldo->desc.name); + kfree(ldo); + + return 0; +} +#else +static int ldo_regulator_register(struct snd_soc_codec *codec, + struct regulator_init_data *init_data, + int voltage) +{ + return -EINVAL; +} + +static int ldo_regulator_remove(struct snd_soc_codec *codec) +{ + return 0; +} +#endif + +/* + * set dac bias + * common state changes: + * startup: + * off --> standby --> prepare --> on + * standby --> prepare --> on + * + * stop: + * on --> prepare --> standby + */ +static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable( + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + return ret; + udelay(10); + } + + break; + case SND_SOC_BIAS_OFF: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#define SGTL5000_FORMATS (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 sgtl5000_ops = { + .hw_params = sgtl5000_pcm_hw_params, + .digital_mute = sgtl5000_digital_mute, + .set_fmt = sgtl5000_set_dai_fmt, + .set_sysclk = sgtl5000_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver sgtl5000_dai = { + .name = "sgtl5000", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + /* + * only support 8~48K + 96K, + * TODO modify hw_param to support more + */ + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .ops = &sgtl5000_ops, + .symmetric_rates = 1, +}; + +static int sgtl5000_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case SGTL5000_CHIP_ID: + case SGTL5000_CHIP_ADCDAC_CTRL: + case SGTL5000_CHIP_ANA_STATUS: + return 1; + } + + return 0; +} + +#ifdef CONFIG_SUSPEND +static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +/* + * restore all sgtl5000 registers, + * since a big hole between dap and regular registers, + * we will restore them respectively. + */ +static int sgtl5000_restore_regs(struct snd_soc_codec *codec) +{ + u16 *cache = codec->reg_cache; + int i; + int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1; + + /* restore regular registers */ + for (i = 0; i < regular_regs; i++) { + int reg = i << 1; + + /* this regs depends on the others */ + if (reg == SGTL5000_CHIP_ANA_POWER || + reg == SGTL5000_CHIP_CLK_CTRL || + reg == SGTL5000_CHIP_LINREG_CTRL || + reg == SGTL5000_CHIP_LINE_OUT_CTRL || + reg == SGTL5000_CHIP_CLK_CTRL) + continue; + + snd_soc_write(codec, reg, cache[i]); + } + + /* restore dap registers */ + for (i = SGTL5000_DAP_REG_OFFSET >> 1; + i < SGTL5000_MAX_REG_OFFSET >> 1; i++) { + int reg = i << 1; + + snd_soc_write(codec, reg, cache[i]); + } + + /* + * restore power and other regs according + * to set_power() and set_clock() + */ + snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, + cache[SGTL5000_CHIP_LINREG_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, + cache[SGTL5000_CHIP_ANA_POWER >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, + cache[SGTL5000_CHIP_CLK_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, + cache[SGTL5000_CHIP_REF_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]); + return 0; +} + +static int sgtl5000_resume(struct snd_soc_codec *codec) +{ + /* Bring the codec back up to standby to enable regulators */ + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Restore registers by cached in memory */ + sgtl5000_restore_regs(codec); + return 0; +} +#else +#define sgtl5000_suspend NULL +#define sgtl5000_resume NULL +#endif /* CONFIG_SUSPEND */ + +/* + * sgtl5000 has 3 internal power supplies: + * 1. VAG, normally set to vdda/2 + * 2. chargepump, set to different value + * according to voltage of vdda and vddio + * 3. line out VAG, normally set to vddio/2 + * + * and should be set according to: + * 1. vddd provided by external or not + * 2. vdda and vddio voltage value. > 3.1v or not + * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd. + */ +static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) +{ + int vddd; + int vdda; + int vddio; + u16 ana_pwr; + u16 lreg_ctrl; + int vag; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); + vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer); + vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer); + + vdda = vdda / 1000; + vddio = vddio / 1000; + vddd = vddd / 1000; + + if (vdda <= 0 || vddio <= 0 || vddd < 0) { + dev_err(codec->dev, "regulator voltage not set correctly\n"); + + return -EINVAL; + } + + /* according to datasheet, maximum voltage of supplies */ + if (vdda > 3600 || vddio > 3600 || vddd > 1980) { + dev_err(codec->dev, + "exceed max voltage vdda %dmv vddio %dma vddd %dma\n", + vdda, vddio, vddd); + + return -EINVAL; + } + + /* reset value */ + ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER); + ana_pwr |= SGTL5000_DAC_STEREO | + SGTL5000_ADC_STEREO | + SGTL5000_REFTOP_POWERUP; + lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL); + + if (vddio < 3100 && vdda < 3100) { + /* enable internal oscillator used for charge pump */ + snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INT_OSC_EN, + SGTL5000_INT_OSC_EN); + /* Enable VDDC charge pump */ + ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; + } else if (vddio >= 3100 && vdda >= 3100) { + /* + * if vddio and vddd > 3.1v, + * charge pump should be clean before set ana_pwr + */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VDDC_CHRGPMP_POWERUP, 0); + + /* VDDC use VDDIO rail */ + lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; + lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << + SGTL5000_VDDC_MAN_ASSN_SHIFT; + } + + snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr); + + /* set voltage to register */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, 0x8); + + /* + * if vddd linear reg has been enabled, + * simple digital supply should be clear to get + * proper VDDD voltage. + */ + if (ana_pwr & SGTL5000_LINEREG_D_POWERUP) + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP, + 0); + else + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP | + SGTL5000_STARTUP_POWERUP, + 0); + + /* + * set ADC/DAC VAG to vdda / 2, + * should stay in range (0.8v, 1.575v) + */ + vag = vdda / 2; + if (vag <= SGTL5000_ANA_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP * + (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT)) + vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT; + else + vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP; + + snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + vag << SGTL5000_ANA_GND_SHIFT, + vag << SGTL5000_ANA_GND_SHIFT); + + /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ + vag = vddio / 2; + if (vag <= SGTL5000_LINE_OUT_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_LINE_OUT_GND_BASE + + SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX) + vag = SGTL5000_LINE_OUT_GND_MAX; + else + vag = (vag - SGTL5000_LINE_OUT_GND_BASE) / + SGTL5000_LINE_OUT_GND_STP; + + snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT, + vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT); + + return 0; +} + +static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) +{ + u16 reg; + int ret; + int rev; + int i; + int external_vddd = 0; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++) + sgtl5000->supplies[i].supply = supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (!ret) + external_vddd = 1; + else { + /* set internal ldo to 1.2v */ + int voltage = LDO_VOLTAGE; + + ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + if (ret) { + dev_err(codec->dev, + "Failed to register vddd internal supplies: %d\n", + ret); + return ret; + } + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, + "Failed to request supplies: %d\n", ret); + + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_regulator_free; + + /* wait for all power rails bring up */ + udelay(10); + + /* read chip information */ + reg = snd_soc_read(codec, SGTL5000_CHIP_ID); + if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != + SGTL5000_PARTID_PART_ID) { + dev_err(codec->dev, + "Device with ID register %x is not a sgtl5000\n", reg); + ret = -ENODEV; + goto err_regulator_disable; + } + + rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; + dev_info(codec->dev, "sgtl5000 revision %d\n", rev); + + /* + * workaround for revision 0x11 and later, + * roll back to use internal LDO + */ + if (external_vddd && rev >= 0x11) { + int voltage = LDO_VOLTAGE; + /* disable all regulator first */ + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + /* free VDDD regulator */ + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + if (ret) + return ret; + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, + "Failed to request supplies: %d\n", ret); + + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_regulator_free; + + /* wait for all power rails bring up */ + udelay(10); + } + + return 0; + +err_regulator_disable: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); +err_regulator_free: + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (external_vddd) + ldo_regulator_remove(codec); + return ret; + +} + +static int sgtl5000_probe(struct snd_soc_codec *codec) +{ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + /* setup i2c data ops */ + 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); + return ret; + } + + ret = sgtl5000_enable_regulators(codec); + if (ret) + return ret; + + /* power up sgtl5000 */ + ret = sgtl5000_set_power_regs(codec); + if (ret) + goto err; + + /* enable small pop, introduce 400ms delay in turning off */ + snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + SGTL5000_SMALL_POP, + SGTL5000_SMALL_POP); + + /* disable short cut detector */ + snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0); + + /* + * set i2s as default input of sound switch + * TODO: add sound switch to control and dapm widge. + */ + snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL, + SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT); + snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER, + SGTL5000_ADC_EN | SGTL5000_DAC_EN); + + /* enable dac volume ramp by default */ + snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL, + SGTL5000_DAC_VOL_RAMP_EN | + SGTL5000_DAC_MUTE_RIGHT | + SGTL5000_DAC_MUTE_LEFT); + + snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL, + SGTL5000_HP_ZCD_EN | + SGTL5000_ADC_ZCD_EN); + + snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0); + + /* + * disable DAP + * TODO: + * Enable DAP in kcontrol and dapm. + */ + snd_soc_write(codec, SGTL5000_DAP_CTRL, 0); + + /* leading to standby state */ + ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (ret) + goto err; + + snd_soc_add_controls(codec, sgtl5000_snd_controls, + ARRAY_SIZE(sgtl5000_snd_controls)); + + snd_soc_dapm_new_controls(&codec->dapm, sgtl5000_dapm_widgets, + ARRAY_SIZE(sgtl5000_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, audio_map, + ARRAY_SIZE(audio_map)); + + snd_soc_dapm_new_widgets(&codec->dapm); + + return 0; + +err: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + ldo_regulator_remove(codec); + + return ret; +} + +static int sgtl5000_remove(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + ldo_regulator_remove(codec); + + return 0; +} + +static struct snd_soc_codec_driver sgtl5000_driver = { + .probe = sgtl5000_probe, + .remove = sgtl5000_remove, + .suspend = sgtl5000_suspend, + .resume = sgtl5000_resume, + .set_bias_level = sgtl5000_set_bias_level, + .reg_cache_size = ARRAY_SIZE(sgtl5000_regs), + .reg_word_size = sizeof(u16), + .reg_cache_step = 2, + .reg_cache_default = sgtl5000_regs, + .volatile_register = sgtl5000_volatile_register, +}; + +static __devinit int sgtl5000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sgtl5000_priv *sgtl5000; + int ret; + + sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL); + if (!sgtl5000) + return -ENOMEM; + + /* + * copy DAP default values to default value array. + * sgtl5000 register space has a big hole, merge it + * at init phase makes life easy. + * FIXME: should we drop 'const' of sgtl5000_regs? + */ + memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)), + sgtl5000_dap_regs, + SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET); + + i2c_set_clientdata(client, sgtl5000); + + ret = snd_soc_register_codec(&client->dev, + &sgtl5000_driver, &sgtl5000_dai, 1); + if (ret) { + dev_err(&client->dev, "Failed to register codec: %d\n", ret); + kfree(sgtl5000); + return ret; + } + + return 0; +} + +static __devexit int sgtl5000_i2c_remove(struct i2c_client *client) +{ + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + + kfree(sgtl5000); + return 0; +} + +static const struct i2c_device_id sgtl5000_id[] = { + {"sgtl5000", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, sgtl5000_id); + +static struct i2c_driver sgtl5000_i2c_driver = { + .driver = { + .name = "sgtl5000", + .owner = THIS_MODULE, + }, + .probe = sgtl5000_i2c_probe, + .remove = __devexit_p(sgtl5000_i2c_remove), + .id_table = sgtl5000_id, +}; + +static int __init sgtl5000_modinit(void) +{ + return i2c_add_driver(&sgtl5000_i2c_driver); +} +module_init(sgtl5000_modinit); + +static void __exit sgtl5000_exit(void) +{ + i2c_del_driver(&sgtl5000_i2c_driver); +} +module_exit(sgtl5000_exit); + +MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Zeng Zhaoming <zhaoming.zeng@freescale.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h new file mode 100644 index 000000000000..eec3ab368f39 --- /dev/null +++ b/sound/soc/codecs/sgtl5000.h @@ -0,0 +1,400 @@ +/* + * sgtl5000.h - SGTL5000 audio codec interface + * + * Copyright 2010-2011 Freescale Semiconductor, 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. + */ + +#ifndef _SGTL5000_H +#define _SGTL5000_H + +/* + * Register values. + */ +#define SGTL5000_CHIP_ID 0x0000 +#define SGTL5000_CHIP_DIG_POWER 0x0002 +#define SGTL5000_CHIP_CLK_CTRL 0x0004 +#define SGTL5000_CHIP_I2S_CTRL 0x0006 +#define SGTL5000_CHIP_SSS_CTRL 0x000a +#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e +#define SGTL5000_CHIP_DAC_VOL 0x0010 +#define SGTL5000_CHIP_PAD_STRENGTH 0x0014 +#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020 +#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022 +#define SGTL5000_CHIP_ANA_CTRL 0x0024 +#define SGTL5000_CHIP_LINREG_CTRL 0x0026 +#define SGTL5000_CHIP_REF_CTRL 0x0028 +#define SGTL5000_CHIP_MIC_CTRL 0x002a +#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c +#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e +#define SGTL5000_CHIP_ANA_POWER 0x0030 +#define SGTL5000_CHIP_PLL_CTRL 0x0032 +#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034 +#define SGTL5000_CHIP_ANA_STATUS 0x0036 +#define SGTL5000_CHIP_SHORT_CTRL 0x003c +#define SGTL5000_CHIP_ANA_TEST2 0x003a +#define SGTL5000_DAP_CTRL 0x0100 +#define SGTL5000_DAP_PEQ 0x0102 +#define SGTL5000_DAP_BASS_ENHANCE 0x0104 +#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106 +#define SGTL5000_DAP_AUDIO_EQ 0x0108 +#define SGTL5000_DAP_SURROUND 0x010a +#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c +#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e +#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110 +#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116 +#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118 +#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a +#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c +#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e +#define SGTL5000_DAP_MAIN_CHAN 0x0120 +#define SGTL5000_DAP_MIX_CHAN 0x0122 +#define SGTL5000_DAP_AVC_CTRL 0x0124 +#define SGTL5000_DAP_AVC_THRESHOLD 0x0126 +#define SGTL5000_DAP_AVC_ATTACK 0x0128 +#define SGTL5000_DAP_AVC_DECAY 0x012a +#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c +#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e +#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130 +#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132 +#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134 +#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136 +#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138 +#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a + +/* + * Field Definitions. + */ + +/* + * SGTL5000_CHIP_ID + */ +#define SGTL5000_PARTID_MASK 0xff00 +#define SGTL5000_PARTID_SHIFT 8 +#define SGTL5000_PARTID_WIDTH 8 +#define SGTL5000_PARTID_PART_ID 0xa0 +#define SGTL5000_REVID_MASK 0x00ff +#define SGTL5000_REVID_SHIFT 0 +#define SGTL5000_REVID_WIDTH 8 + +/* + * SGTL5000_CHIP_DIG_POWER + */ +#define SGTL5000_ADC_EN 0x0040 +#define SGTL5000_DAC_EN 0x0020 +#define SGTL5000_DAP_POWERUP 0x0010 +#define SGTL5000_I2S_OUT_POWERUP 0x0002 +#define SGTL5000_I2S_IN_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_CLK_CTRL + */ +#define SGTL5000_RATE_MODE_MASK 0x0030 +#define SGTL5000_RATE_MODE_SHIFT 4 +#define SGTL5000_RATE_MODE_WIDTH 2 +#define SGTL5000_RATE_MODE_DIV_1 0 +#define SGTL5000_RATE_MODE_DIV_2 1 +#define SGTL5000_RATE_MODE_DIV_4 2 +#define SGTL5000_RATE_MODE_DIV_6 3 +#define SGTL5000_SYS_FS_MASK 0x000c +#define SGTL5000_SYS_FS_SHIFT 2 +#define SGTL5000_SYS_FS_WIDTH 2 +#define SGTL5000_SYS_FS_32k 0x0 +#define SGTL5000_SYS_FS_44_1k 0x1 +#define SGTL5000_SYS_FS_48k 0x2 +#define SGTL5000_SYS_FS_96k 0x3 +#define SGTL5000_MCLK_FREQ_MASK 0x0003 +#define SGTL5000_MCLK_FREQ_SHIFT 0 +#define SGTL5000_MCLK_FREQ_WIDTH 2 +#define SGTL5000_MCLK_FREQ_256FS 0x0 +#define SGTL5000_MCLK_FREQ_384FS 0x1 +#define SGTL5000_MCLK_FREQ_512FS 0x2 +#define SGTL5000_MCLK_FREQ_PLL 0x3 + +/* + * SGTL5000_CHIP_I2S_CTRL + */ +#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100 +#define SGTL5000_I2S_SCLKFREQ_SHIFT 8 +#define SGTL5000_I2S_SCLKFREQ_WIDTH 1 +#define SGTL5000_I2S_SCLKFREQ_64FS 0x0 +#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */ +#define SGTL5000_I2S_MASTER 0x0080 +#define SGTL5000_I2S_SCLK_INV 0x0040 +#define SGTL5000_I2S_DLEN_MASK 0x0030 +#define SGTL5000_I2S_DLEN_SHIFT 4 +#define SGTL5000_I2S_DLEN_WIDTH 2 +#define SGTL5000_I2S_DLEN_32 0x0 +#define SGTL5000_I2S_DLEN_24 0x1 +#define SGTL5000_I2S_DLEN_20 0x2 +#define SGTL5000_I2S_DLEN_16 0x3 +#define SGTL5000_I2S_MODE_MASK 0x000c +#define SGTL5000_I2S_MODE_SHIFT 2 +#define SGTL5000_I2S_MODE_WIDTH 2 +#define SGTL5000_I2S_MODE_I2S_LJ 0x0 +#define SGTL5000_I2S_MODE_RJ 0x1 +#define SGTL5000_I2S_MODE_PCM 0x2 +#define SGTL5000_I2S_LRALIGN 0x0002 +#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */ + +/* + * SGTL5000_CHIP_SSS_CTRL + */ +#define SGTL5000_DAP_MIX_LRSWAP 0x4000 +#define SGTL5000_DAP_LRSWAP 0x2000 +#define SGTL5000_DAC_LRSWAP 0x1000 +#define SGTL5000_I2S_OUT_LRSWAP 0x0400 +#define SGTL5000_DAP_MIX_SEL_MASK 0x0300 +#define SGTL5000_DAP_MIX_SEL_SHIFT 8 +#define SGTL5000_DAP_MIX_SEL_WIDTH 2 +#define SGTL5000_DAP_MIX_SEL_ADC 0x0 +#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1 +#define SGTL5000_DAP_SEL_MASK 0x00c0 +#define SGTL5000_DAP_SEL_SHIFT 6 +#define SGTL5000_DAP_SEL_WIDTH 2 +#define SGTL5000_DAP_SEL_ADC 0x0 +#define SGTL5000_DAP_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_MASK 0x0030 +#define SGTL5000_DAC_SEL_SHIFT 4 +#define SGTL5000_DAC_SEL_WIDTH 2 +#define SGTL5000_DAC_SEL_ADC 0x0 +#define SGTL5000_DAC_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_DAP 0x3 +#define SGTL5000_I2S_OUT_SEL_MASK 0x0003 +#define SGTL5000_I2S_OUT_SEL_SHIFT 0 +#define SGTL5000_I2S_OUT_SEL_WIDTH 2 +#define SGTL5000_I2S_OUT_SEL_ADC 0x0 +#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1 +#define SGTL5000_I2S_OUT_SEL_DAP 0x3 + +/* + * SGTL5000_CHIP_ADCDAC_CTRL + */ +#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000 +#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000 +#define SGTL5000_DAC_VOL_RAMP_EN 0x0200 +#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100 +#define SGTL5000_DAC_MUTE_RIGHT 0x0008 +#define SGTL5000_DAC_MUTE_LEFT 0x0004 +#define SGTL5000_ADC_HPF_FREEZE 0x0002 +#define SGTL5000_ADC_HPF_BYPASS 0x0001 + +/* + * SGTL5000_CHIP_DAC_VOL + */ +#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00 +#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8 +#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8 +#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff +#define SGTL5000_DAC_VOL_LEFT_SHIFT 0 +#define SGTL5000_DAC_VOL_LEFT_WIDTH 8 + +/* + * SGTL5000_CHIP_PAD_STRENGTH + */ +#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300 +#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8 +#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0 +#define SGTL5000_PAD_I2S_SCLK_SHIFT 6 +#define SGTL5000_PAD_I2S_SCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030 +#define SGTL5000_PAD_I2S_DOUT_SHIFT 4 +#define SGTL5000_PAD_I2S_DOUT_WIDTH 2 +#define SGTL5000_PAD_I2C_SDA_MASK 0x000c +#define SGTL5000_PAD_I2C_SDA_SHIFT 2 +#define SGTL5000_PAD_I2C_SDA_WIDTH 2 +#define SGTL5000_PAD_I2C_SCL_MASK 0x0003 +#define SGTL5000_PAD_I2C_SCL_SHIFT 0 +#define SGTL5000_PAD_I2C_SCL_WIDTH 2 + +/* + * SGTL5000_CHIP_ANA_ADC_CTRL + */ +#define SGTL5000_ADC_VOL_M6DB 0x0100 +#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0 +#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4 +#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4 +#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f +#define SGTL5000_ADC_VOL_LEFT_SHIFT 0 +#define SGTL5000_ADC_VOL_LEFT_WIDTH 4 + +/* + * SGTL5000_CHIP_ANA_HP_CTRL + */ +#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00 +#define SGTL5000_HP_VOL_RIGHT_SHIFT 8 +#define SGTL5000_HP_VOL_RIGHT_WIDTH 7 +#define SGTL5000_HP_VOL_LEFT_MASK 0x007f +#define SGTL5000_HP_VOL_LEFT_SHIFT 0 +#define SGTL5000_HP_VOL_LEFT_WIDTH 7 + +/* + * SGTL5000_CHIP_ANA_CTRL + */ +#define SGTL5000_LINE_OUT_MUTE 0x0100 +#define SGTL5000_HP_SEL_MASK 0x0040 +#define SGTL5000_HP_SEL_SHIFT 6 +#define SGTL5000_HP_SEL_WIDTH 1 +#define SGTL5000_HP_SEL_DAC 0x0 +#define SGTL5000_HP_SEL_LINE_IN 0x1 +#define SGTL5000_HP_ZCD_EN 0x0020 +#define SGTL5000_HP_MUTE 0x0010 +#define SGTL5000_ADC_SEL_MASK 0x0004 +#define SGTL5000_ADC_SEL_SHIFT 2 +#define SGTL5000_ADC_SEL_WIDTH 1 +#define SGTL5000_ADC_SEL_MIC 0x0 +#define SGTL5000_ADC_SEL_LINE_IN 0x1 +#define SGTL5000_ADC_ZCD_EN 0x0002 +#define SGTL5000_ADC_MUTE 0x0001 + +/* + * SGTL5000_CHIP_LINREG_CTRL + */ +#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040 +#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6 +#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1 +#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0 +#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1 +#define SGTL5000_VDDC_ASSN_OVRD 0x0020 +#define SGTL5000_LINREG_VDDD_MASK 0x000f +#define SGTL5000_LINREG_VDDD_SHIFT 0 +#define SGTL5000_LINREG_VDDD_WIDTH 4 + +/* + * SGTL5000_CHIP_REF_CTRL + */ +#define SGTL5000_ANA_GND_MASK 0x01f0 +#define SGTL5000_ANA_GND_SHIFT 4 +#define SGTL5000_ANA_GND_WIDTH 5 +#define SGTL5000_ANA_GND_BASE 800 /* mv */ +#define SGTL5000_ANA_GND_STP 25 /*mv */ +#define SGTL5000_BIAS_CTRL_MASK 0x000e +#define SGTL5000_BIAS_CTRL_SHIFT 1 +#define SGTL5000_BIAS_CTRL_WIDTH 3 +#define SGTL5000_SMALL_POP 0x0001 + +/* + * SGTL5000_CHIP_MIC_CTRL + */ +#define SGTL5000_BIAS_R_MASK 0x0200 +#define SGTL5000_BIAS_R_SHIFT 8 +#define SGTL5000_BIAS_R_WIDTH 2 +#define SGTL5000_BIAS_R_off 0x0 +#define SGTL5000_BIAS_R_2K 0x1 +#define SGTL5000_BIAS_R_4k 0x2 +#define SGTL5000_BIAS_R_8k 0x3 +#define SGTL5000_BIAS_VOLT_MASK 0x0070 +#define SGTL5000_BIAS_VOLT_SHIFT 4 +#define SGTL5000_BIAS_VOLT_WIDTH 3 +#define SGTL5000_MIC_GAIN_MASK 0x0003 +#define SGTL5000_MIC_GAIN_SHIFT 0 +#define SGTL5000_MIC_GAIN_WIDTH 2 + +/* + * SGTL5000_CHIP_LINE_OUT_CTRL + */ +#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00 +#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8 +#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4 +#define SGTL5000_LINE_OUT_CURRENT_180u 0x0 +#define SGTL5000_LINE_OUT_CURRENT_270u 0x1 +#define SGTL5000_LINE_OUT_CURRENT_360u 0x3 +#define SGTL5000_LINE_OUT_CURRENT_450u 0x7 +#define SGTL5000_LINE_OUT_CURRENT_540u 0xf +#define SGTL5000_LINE_OUT_GND_MASK 0x003f +#define SGTL5000_LINE_OUT_GND_SHIFT 0 +#define SGTL5000_LINE_OUT_GND_WIDTH 6 +#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */ +#define SGTL5000_LINE_OUT_GND_STP 25 +#define SGTL5000_LINE_OUT_GND_MAX 0x23 + +/* + * SGTL5000_CHIP_LINE_OUT_VOL + */ +#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00 +#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8 +#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5 +#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f +#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0 +#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5 + +/* + * SGTL5000_CHIP_ANA_POWER + */ +#define SGTL5000_DAC_STEREO 0x4000 +#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000 +#define SGTL5000_STARTUP_POWERUP 0x1000 +#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800 +#define SGTL5000_PLL_POWERUP 0x0400 +#define SGTL5000_LINEREG_D_POWERUP 0x0200 +#define SGTL5000_VCOAMP_POWERUP 0x0100 +#define SGTL5000_VAG_POWERUP 0x0080 +#define SGTL5000_ADC_STEREO 0x0040 +#define SGTL5000_REFTOP_POWERUP 0x0020 +#define SGTL5000_HP_POWERUP 0x0010 +#define SGTL5000_DAC_POWERUP 0x0008 +#define SGTL5000_CAPLESS_HP_POWERUP 0x0004 +#define SGTL5000_ADC_POWERUP 0x0002 +#define SGTL5000_LINE_OUT_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_PLL_CTRL + */ +#define SGTL5000_PLL_INT_DIV_MASK 0xf800 +#define SGTL5000_PLL_INT_DIV_SHIFT 11 +#define SGTL5000_PLL_INT_DIV_WIDTH 5 +#define SGTL5000_PLL_FRAC_DIV_MASK 0x0700 +#define SGTL5000_PLL_FRAC_DIV_SHIFT 0 +#define SGTL5000_PLL_FRAC_DIV_WIDTH 11 + +/* + * SGTL5000_CHIP_CLK_TOP_CTRL + */ +#define SGTL5000_INT_OSC_EN 0x0800 +#define SGTL5000_INPUT_FREQ_DIV2 0x0008 + +/* + * SGTL5000_CHIP_ANA_STATUS + */ +#define SGTL5000_HP_LRSHORT 0x0200 +#define SGTL5000_CAPLESS_SHORT 0x0100 +#define SGTL5000_PLL_LOCKED 0x0010 + +/* + * SGTL5000_CHIP_SHORT_CTRL + */ +#define SGTL5000_LVLADJR_MASK 0x7000 +#define SGTL5000_LVLADJR_SHIFT 12 +#define SGTL5000_LVLADJR_WIDTH 3 +#define SGTL5000_LVLADJL_MASK 0x0700 +#define SGTL5000_LVLADJL_SHIFT 8 +#define SGTL5000_LVLADJL_WIDTH 3 +#define SGTL5000_LVLADJC_MASK 0x0070 +#define SGTL5000_LVLADJC_SHIFT 4 +#define SGTL5000_LVLADJC_WIDTH 3 +#define SGTL5000_LR_SHORT_MOD_MASK 0x000c +#define SGTL5000_LR_SHORT_MOD_SHIFT 2 +#define SGTL5000_LR_SHORT_MOD_WIDTH 2 +#define SGTL5000_CM_SHORT_MOD_MASK 0x0003 +#define SGTL5000_CM_SHORT_MOD_SHIFT 0 +#define SGTL5000_CM_SHORT_MOD_WIDTH 2 + +/* + *SGTL5000_CHIP_ANA_TEST2 + */ +#define SGTL5000_MONO_DAC 0x1000 + +/* + * SGTL5000_DAP_CTRL + */ +#define SGTL5000_DAP_MIX_EN 0x0010 +#define SGTL5000_DAP_EN 0x0001 + +#define SGTL5000_SYSCLK 0x00 +#define SGTL5000_LRCLK 0x01 + +#endif diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c new file mode 100644 index 000000000000..4d9fb279e146 --- /dev/null +++ b/sound/soc/codecs/sn95031.c @@ -0,0 +1,951 @@ +/* + * sn95031.c - TI sn95031 Codec driver + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <asm/intel_scu_ipc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/jack.h> +#include "sn95031.h" + +#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100) +#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* adc helper functions */ + +/* enables mic bias voltage */ +static void sn95031_enable_mic_bias(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0)); + snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2)); +} + +/* Enable/Disable the ADC depending on the argument */ +static void configure_adc(struct snd_soc_codec *sn95031_codec, int val) +{ + int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); + + if (val) { + /* Enable and start the ADC */ + value |= (SN95031_ADC_ENBL | SN95031_ADC_START); + value &= (~SN95031_ADC_NO_LOOP); + } else { + /* Just stop the ADC */ + value &= (~SN95031_ADC_START); + } + snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value); +} + +/* + * finds an empty channel for conversion + * If the ADC is not enabled then start using 0th channel + * itself. Otherwise find an empty channel by looking for a + * channel in which the stopbit is set to 1. returns the index + * of the first free channel if succeeds or an error code. + * + * Context: can sleep + * + */ +static int find_free_channel(struct snd_soc_codec *sn95031_codec) +{ + int ret = 0, i, value; + + /* check whether ADC is enabled */ + value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); + + if ((value & SN95031_ADC_ENBL) == 0) + return 0; + + /* ADC is already enabled; Looking for an empty channel */ + for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) { + value = snd_soc_read(sn95031_codec, + SN95031_ADC_CHNL_START_ADDR + i); + if (value & SN95031_STOPBIT_MASK) { + ret = i; + break; + } + } + return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret; +} + +/* Initialize the ADC for reading micbias values. Can sleep. */ +static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec) +{ + int base_addr, chnl_addr; + int value; + static int channel_index; + + /* Index of the first channel in which the stop bit is set */ + channel_index = find_free_channel(sn95031_codec); + if (channel_index < 0) { + pr_err("No free ADC channels"); + return channel_index; + } + + base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index; + + if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) { + /* Reset stop bit for channels other than 0 and 12 */ + value = snd_soc_read(sn95031_codec, base_addr); + /* Set the stop bit to zero */ + snd_soc_write(sn95031_codec, base_addr, value & 0xEF); + /* Index of the first free channel */ + base_addr++; + channel_index++; + } + + /* Since this is the last channel, set the stop bit + to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ + snd_soc_write(sn95031_codec, base_addr, + SN95031_AUDIO_DETECT_CODE | 0x10); + + chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index; + pr_debug("mid_initialize : %x", chnl_addr); + configure_adc(sn95031_codec, 1); + return chnl_addr; +} + + +/* reads the ADC registers and gets the mic bias value in mV. */ +static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec) +{ + u16 adc_adr = sn95031_initialize_adc(codec); + u16 adc_val1, adc_val2; + unsigned int mic_bias; + + sn95031_enable_mic_bias(codec); + + /* Enable the sound card for conversion before reading */ + snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05); + /* Re-toggle the RRDATARD bit */ + snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04); + + /* Read the higher bits of data */ + msleep(1000); + adc_val1 = snd_soc_read(codec, adc_adr); + adc_adr++; + adc_val2 = snd_soc_read(codec, adc_adr); + + /* Adding lower two bits to the higher bits */ + mic_bias = (adc_val1 << 2) + (adc_val2 & 3); + mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000; + pr_debug("mic bias = %dmV\n", mic_bias); + return mic_bias; +} +EXPORT_SYMBOL_GPL(sn95031_get_mic_bias); +/*end - adc helper functions */ + +static inline unsigned int sn95031_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 value = 0; + int ret; + + ret = intel_scu_ipc_ioread8(reg, &value); + if (ret) + pr_err("read of %x failed, err %d\n", reg, ret); + return value; + +} + +static inline int sn95031_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int ret; + + ret = intel_scu_ipc_iowrite8(reg, value); + if (ret) + pr_err("write of %x failed, err %d\n", reg, ret); + return ret; +} + +static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + pr_debug("vaud_bias powering up pll\n"); + /* power up the pll */ + snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5)); + /* enable pcm 2 */ + snd_soc_update_bits(codec, SN95031_PCM2C2, + BIT(0), BIT(0)); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + pr_debug("vaud_bias power up rail\n"); + /* power up the rail */ + snd_soc_write(codec, SN95031_VAUD, + BIT(2)|BIT(1)|BIT(0)); + msleep(1); + } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + /* turn off pcm */ + pr_debug("vaud_bias power dn pcm\n"); + snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0); + snd_soc_write(codec, SN95031_AUDPLLCTRL, 0); + } + break; + + + case SND_SOC_BIAS_OFF: + pr_debug("vaud_bias _OFF doing rail shutdown\n"); + snd_soc_write(codec, SN95031_VAUD, BIT(3)); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int sn95031_vhs_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n"); + /* power up the rail */ + snd_soc_write(w->codec, SN95031_VHSP, 0x3D); + snd_soc_write(w->codec, SN95031_VHSN, 0x3F); + msleep(1); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n"); + snd_soc_write(w->codec, SN95031_VHSP, 0xC4); + snd_soc_write(w->codec, SN95031_VHSN, 0x04); + } + return 0; +} + +static int sn95031_vihf_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n"); + /* power up the rail */ + snd_soc_write(w->codec, SN95031_VIHF, 0x27); + msleep(1); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n"); + snd_soc_write(w->codec, SN95031_VIHF, 0x24); + } + return 0; +} + +static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0, clk_dir = 0, data_dir = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ldo = BIT(5)|BIT(4); + clk_dir = BIT(0); + data_dir = BIT(7); + } + /* program DMIC LDO, clock and set clock */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(0), clk_dir); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(7), data_dir); + return 0; +} + +static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0, clk_dir = 0, data_dir = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ldo = BIT(5)|BIT(4); + clk_dir = BIT(2); + data_dir = BIT(1); + } + /* program DMIC LDO, clock and set clock */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(2), clk_dir); + snd_soc_update_bits(w->codec, SN95031_DMICBUF45, BIT(1), data_dir); + return 0; +} + +static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ldo = BIT(7)|BIT(6); + + /* program DMIC LDO */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo); + return 0; +} + +/* mux controls */ +static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" }; + +static const struct soc_enum sn95031_micl_enum = + SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts); + +static const struct snd_kcontrol_new sn95031_micl_mux_control = + SOC_DAPM_ENUM("Route", sn95031_micl_enum); + +static const struct soc_enum sn95031_micr_enum = + SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts); + +static const struct snd_kcontrol_new sn95031_micr_mux_control = + SOC_DAPM_ENUM("Route", sn95031_micr_enum); + +static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", + "ADC Left", "ADC Right" }; + +static const struct soc_enum sn95031_input1_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input1_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input1_enum); + +static const struct soc_enum sn95031_input2_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input2_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input2_enum); + +static const struct soc_enum sn95031_input3_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input3_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input3_enum); + +static const struct soc_enum sn95031_input4_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input4_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input4_enum); + +/* capture path controls */ + +static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"}; + +/* 0dB to 30dB in 10dB steps */ +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0); + +static const struct soc_enum sn95031_micmode1_enum = + SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text); +static const struct soc_enum sn95031_micmode2_enum = + SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text); + +static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"}; + +static const struct soc_enum sn95031_dmic12_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text); +static const struct soc_enum sn95031_dmic34_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text); +static const struct soc_enum sn95031_dmic56_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text); + +static const struct snd_kcontrol_new sn95031_snd_controls[] = { + SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum), + SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum), + SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum), + SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum), + SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum), + SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1, + 2, 4, 0, mic_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2, + 2, 4, 0, mic_tlv), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { + + /* all end points mic, hs etc */ + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("EPOUT"), + SND_SOC_DAPM_OUTPUT("IHFOUTL"), + SND_SOC_DAPM_OUTPUT("IHFOUTR"), + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + SND_SOC_DAPM_OUTPUT("VIB1OUT"), + SND_SOC_DAPM_OUTPUT("VIB2OUT"), + + SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */ + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("DMIC5"), + SND_SOC_DAPM_INPUT("DMIC6"), + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + + SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0), + SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0), + SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0), + SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0), + SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0), + + SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0, + sn95031_dmic12_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0, + sn95031_dmic34_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0, + sn95031_dmic56_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0, + sn95031_vhs_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0, + sn95031_vihf_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* playback path driver enables */ + SND_SOC_DAPM_PGA("Headset Left Playback", + SN95031_DRIVEREN, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset Right Playback", + SN95031_DRIVEREN, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Left Playback", + SN95031_DRIVEREN, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Right Playback", + SN95031_DRIVEREN, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Vibra1 Playback", + SN95031_DRIVEREN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Vibra2 Playback", + SN95031_DRIVEREN, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Earpiece Playback", + SN95031_DRIVEREN, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout Left Playback", + SN95031_LOCTL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout Right Playback", + SN95031_LOCTL, 4, 0, NULL, 0), + + /* playback path filter enable */ + SND_SOC_DAPM_PGA("Headset Left Filter", + SN95031_HSEPRXCTRL, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset Right Filter", + SN95031_HSEPRXCTRL, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Left Filter", + SN95031_IHFRXCTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Right Filter", + SN95031_IHFRXCTRL, 1, 0, NULL, 0), + + /* DACs */ + SND_SOC_DAPM_DAC("HSDAC Left", "Headset", + SN95031_DACCONFIG, 0, 0), + SND_SOC_DAPM_DAC("HSDAC Right", "Headset", + SN95031_DACCONFIG, 1, 0), + SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker", + SN95031_DACCONFIG, 2, 0), + SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker", + SN95031_DACCONFIG, 3, 0), + SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1", + SN95031_VIB1C5, 1, 0), + SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2", + SN95031_VIB2C5, 1, 0), + + /* capture widgets */ + SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0), + + /* ADC have null stream as they will be turned ON by TX path */ + SND_SOC_DAPM_ADC("ADC Left", NULL, + SN95031_ADCCONFIG, 0, 0), + SND_SOC_DAPM_ADC("ADC Right", NULL, + SN95031_ADCCONFIG, 2, 0), + + SND_SOC_DAPM_MUX("Mic_InputL Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control), + SND_SOC_DAPM_MUX("Mic_InputR Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control), + + SND_SOC_DAPM_MUX("Txpath1 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control), + SND_SOC_DAPM_MUX("Txpath2 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control), + SND_SOC_DAPM_MUX("Txpath3 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control), + SND_SOC_DAPM_MUX("Txpath4 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control), + +}; + +static const struct snd_soc_dapm_route sn95031_audio_map[] = { + /* headset and earpiece map */ + { "HPOUTL", NULL, "Headset Rail"}, + { "HPOUTR", NULL, "Headset Rail"}, + { "HPOUTL", NULL, "Headset Left Playback" }, + { "HPOUTR", NULL, "Headset Right Playback" }, + { "EPOUT", NULL, "Earpiece Playback" }, + { "Headset Left Playback", NULL, "Headset Left Filter"}, + { "Headset Right Playback", NULL, "Headset Right Filter"}, + { "Earpiece Playback", NULL, "Headset Left Filter"}, + { "Headset Left Filter", NULL, "HSDAC Left"}, + { "Headset Right Filter", NULL, "HSDAC Right"}, + + /* speaker map */ + { "IHFOUTL", NULL, "Speaker Rail"}, + { "IHFOUTR", NULL, "Speaker Rail"}, + { "IHFOUTL", "NULL", "Speaker Left Playback"}, + { "IHFOUTR", "NULL", "Speaker Right Playback"}, + { "Speaker Left Playback", NULL, "Speaker Left Filter"}, + { "Speaker Right Playback", NULL, "Speaker Right Filter"}, + { "Speaker Left Filter", NULL, "IHFDAC Left"}, + { "Speaker Right Filter", NULL, "IHFDAC Right"}, + + /* vibra map */ + { "VIB1OUT", NULL, "Vibra1 Playback"}, + { "Vibra1 Playback", NULL, "Vibra1 DAC"}, + + { "VIB2OUT", NULL, "Vibra2 Playback"}, + { "Vibra2 Playback", NULL, "Vibra2 DAC"}, + + /* lineout */ + { "LINEOUTL", NULL, "Lineout Left Playback"}, + { "LINEOUTR", NULL, "Lineout Right Playback"}, + { "Lineout Left Playback", NULL, "Headset Left Filter"}, + { "Lineout Left Playback", NULL, "Speaker Left Filter"}, + { "Lineout Left Playback", NULL, "Vibra1 DAC"}, + { "Lineout Right Playback", NULL, "Headset Right Filter"}, + { "Lineout Right Playback", NULL, "Speaker Right Filter"}, + { "Lineout Right Playback", NULL, "Vibra2 DAC"}, + + /* Headset (AMIC1) mic */ + { "AMIC1Bias", NULL, "AMIC1"}, + { "MIC1 Enable", NULL, "AMIC1Bias"}, + { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"}, + + /* AMIC2 */ + { "AMIC2Bias", NULL, "AMIC2"}, + { "MIC2 Enable", NULL, "AMIC2Bias"}, + { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"}, + + + /* Linein */ + { "LineIn Enable Left", NULL, "LINEINL"}, + { "LineIn Enable Right", NULL, "LINEINR"}, + { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"}, + { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"}, + + /* ADC connection */ + { "ADC Left", NULL, "Mic_InputL Capture Route"}, + { "ADC Right", NULL, "Mic_InputR Capture Route"}, + + /*DMIC connections */ + { "DMIC1", NULL, "DMIC12supply"}, + { "DMIC2", NULL, "DMIC12supply"}, + { "DMIC3", NULL, "DMIC34supply"}, + { "DMIC4", NULL, "DMIC34supply"}, + { "DMIC5", NULL, "DMIC56supply"}, + { "DMIC6", NULL, "DMIC56supply"}, + + { "DMIC12Bias", NULL, "DMIC1"}, + { "DMIC12Bias", NULL, "DMIC2"}, + { "DMIC34Bias", NULL, "DMIC3"}, + { "DMIC34Bias", NULL, "DMIC4"}, + { "DMIC56Bias", NULL, "DMIC5"}, + { "DMIC56Bias", NULL, "DMIC6"}, + + /*TX path inputs*/ + { "Txpath1 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath2 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath3 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath4 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath1 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath2 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath3 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath4 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath1 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath2 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath3 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath4 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath1 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath2 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath3 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath4 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath1 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath2 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath3 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath4 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath1 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath2 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath3 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath4 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath1 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath2 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath3 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath4 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath1 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath2 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath3 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath4 Capture Route", "DMIC6", "DMIC6"}, + + /* tx path */ + { "TX1 Enable", NULL, "Txpath1 Capture Route"}, + { "TX2 Enable", NULL, "Txpath2 Capture Route"}, + { "TX3 Enable", NULL, "Txpath3 Capture Route"}, + { "TX4 Enable", NULL, "Txpath4 Capture Route"}, + { "PCM_Out", NULL, "TX1 Enable"}, + { "PCM_Out", NULL, "TX2 Enable"}, + { "PCM_Out", NULL, "TX3 Enable"}, + { "PCM_Out", NULL, "TX4 Enable"}, + +}; + +/* speaker and headset mutes, for audio pops and clicks */ +static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, + SN95031_HSLVOLCTRL, BIT(7), (!mute << 7)); + snd_soc_update_bits(dai->codec, + SN95031_HSRVOLCTRL, BIT(7), (!mute << 7)); + return 0; +} + +static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, + SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7)); + snd_soc_update_bits(dai->codec, + SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7)); + return 0; +} + +int sn95031_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + unsigned int format, rate; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + format = BIT(4)|BIT(5); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + format = 0; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(dai->codec, SN95031_PCM2C2, + BIT(4)|BIT(5), format); + + switch (params_rate(params)) { + case 48000: + pr_debug("RATE_48000\n"); + rate = 0; + break; + + case 44100: + pr_debug("RATE_44100\n"); + rate = BIT(7); + break; + + default: + pr_err("ERR rate %d\n", params_rate(params)); + return -EINVAL; + } + snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate); + + return 0; +} + +/* Codec DAI section */ +static struct snd_soc_dai_ops sn95031_headset_dai_ops = { + .digital_mute = sn95031_pcm_hs_mute, + .hw_params = sn95031_pcm_hw_params, +}; + +static struct snd_soc_dai_ops sn95031_speaker_dai_ops = { + .digital_mute = sn95031_pcm_spkr_mute, + .hw_params = sn95031_pcm_hw_params, +}; + +static struct snd_soc_dai_ops sn95031_vib1_dai_ops = { + .hw_params = sn95031_pcm_hw_params, +}; + +static struct snd_soc_dai_ops sn95031_vib2_dai_ops = { + .hw_params = sn95031_pcm_hw_params, +}; + +struct snd_soc_dai_driver sn95031_dais[] = { +{ + .name = "SN95031 Headset", + .playback = { + .stream_name = "Headset", + .channels_min = 2, + .channels_max = 2, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_headset_dai_ops, +}, +{ .name = "SN95031 Speaker", + .playback = { + .stream_name = "Speaker", + .channels_min = 2, + .channels_max = 2, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_speaker_dai_ops, +}, +{ .name = "SN95031 Vibra1", + .playback = { + .stream_name = "Vibra1", + .channels_min = 1, + .channels_max = 1, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_vib1_dai_ops, +}, +{ .name = "SN95031 Vibra2", + .playback = { + .stream_name = "Vibra2", + .channels_min = 1, + .channels_max = 1, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_vib2_dai_ops, +}, +}; + +static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_BTNCTRL2, 0x00); +} + +static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_BTNCTRL1, 0x77); + snd_soc_write(codec, SN95031_BTNCTRL2, 0x01); +} + +static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack) +{ + int micbias = sn95031_get_mic_bias(mfld_jack->codec); + + int jack_type = snd_soc_jack_get_type(mfld_jack, micbias); + + pr_debug("jack type detected = %d\n", jack_type); + if (jack_type == SND_JACK_HEADSET) + sn95031_enable_jack_btn(mfld_jack->codec); + return jack_type; +} + +void sn95031_jack_detection(struct mfld_jack_data *jack_data) +{ + unsigned int status; + unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; + + pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id); + if (jack_data->intr_id & 0x1) { + pr_debug("short_push detected\n"); + status = SND_JACK_HEADSET | SND_JACK_BTN_0; + } else if (jack_data->intr_id & 0x2) { + pr_debug("long_push detected\n"); + status = SND_JACK_HEADSET | SND_JACK_BTN_1; + } else if (jack_data->intr_id & 0x4) { + pr_debug("headset or headphones inserted\n"); + status = sn95031_get_headset_state(jack_data->mfld_jack); + } else if (jack_data->intr_id & 0x8) { + pr_debug("headset or headphones removed\n"); + status = 0; + sn95031_disable_jack_btn(jack_data->mfld_jack->codec); + } else { + pr_err("unidentified interrupt\n"); + return; + } + + snd_soc_jack_report(jack_data->mfld_jack, status, mask); + /*button pressed and released so we send explicit button release */ + if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1)) + snd_soc_jack_report(jack_data->mfld_jack, + SND_JACK_HEADSET, mask); +} +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; + codec->dapm.idle_bias_off = 1; + + /* PCM interface config + * This sets the pcm rx slot conguration to max 6 slots + * for max 4 dais (2 stereo and 2 mono) + */ + snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10); + snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32); + snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54); + snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10); + snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32); + /* pcm port setting + * This sets the pcm port to slave and clock at 19.2Mhz which + * can support 6slots, sampling rate set per stream in hw-params + */ + snd_soc_write(codec, SN95031_PCM1C1, 0x00); + snd_soc_write(codec, SN95031_PCM2C1, 0x01); + snd_soc_write(codec, SN95031_PCM2C2, 0x0A); + snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4)); + /* vendor vibra workround, the vibras are muted by + * custom register so unmute them + */ + snd_soc_write(codec, SN95031_SSR5, 0x80); + snd_soc_write(codec, SN95031_SSR6, 0x80); + snd_soc_write(codec, SN95031_VIB1C5, 0x00); + snd_soc_write(codec, SN95031_VIB2C5, 0x00); + /* configure vibras for pcm port */ + snd_soc_write(codec, SN95031_VIB1C3, 0x00); + snd_soc_write(codec, SN95031_VIB2C3, 0x00); + + /* soft mute ramp time */ + snd_soc_write(codec, SN95031_SOFTMUTE, 0x3); + /* fix the initial volume at 1dB, + * default in +9dB, + * 1dB give optimal swing on DAC, amps + */ + snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08); + snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08); + snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08); + snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08); + /* dac mode and lineout workaround */ + snd_soc_write(codec, SN95031_SSR2, 0x10); + snd_soc_write(codec, SN95031_SSR3, 0x40); + + 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; +} + +static int sn95031_codec_remove(struct snd_soc_codec *codec) +{ + pr_debug("codec_remove called\n"); + sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +struct snd_soc_codec_driver sn95031_codec = { + .probe = sn95031_codec_probe, + .remove = sn95031_codec_remove, + .read = sn95031_read, + .write = sn95031_write, + .set_bias_level = sn95031_set_vaud_bias, +}; + +static int __devinit sn95031_device_probe(struct platform_device *pdev) +{ + pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev)); + return snd_soc_register_codec(&pdev->dev, &sn95031_codec, + sn95031_dais, ARRAY_SIZE(sn95031_dais)); +} + +static int __devexit sn95031_device_remove(struct platform_device *pdev) +{ + pr_debug("codec device remove called\n"); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver sn95031_codec_driver = { + .driver = { + .name = "sn95031", + .owner = THIS_MODULE, + }, + .probe = sn95031_device_probe, + .remove = __devexit_p(sn95031_device_remove), +}; + +static int __init sn95031_init(void) +{ + pr_debug("driver init called\n"); + return platform_driver_register(&sn95031_codec_driver); +} +module_init(sn95031_init); + +static void __exit sn95031_exit(void) +{ + pr_debug("driver exit called\n"); + platform_driver_unregister(&sn95031_codec_driver); +} +module_exit(sn95031_exit); + +MODULE_DESCRIPTION("ASoC TI SN95031 codec driver"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sn95031"); diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h new file mode 100644 index 000000000000..20376d234fb8 --- /dev/null +++ b/sound/soc/codecs/sn95031.h @@ -0,0 +1,132 @@ +/* + * sn95031.h - TI sn95031 Codec driver + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#ifndef _SN95031_H +#define _SN95031_H + +/*register map*/ +#define SN95031_VAUD 0xDB +#define SN95031_VHSP 0xDC +#define SN95031_VHSN 0xDD +#define SN95031_VIHF 0xC9 + +#define SN95031_AUDPLLCTRL 0x240 +#define SN95031_DMICBUF0123 0x241 +#define SN95031_DMICBUF45 0x242 +#define SN95031_DMICGPO 0x244 +#define SN95031_DMICMUX 0x245 +#define SN95031_DMICLK 0x246 +#define SN95031_MICBIAS 0x247 +#define SN95031_ADCCONFIG 0x248 +#define SN95031_MICAMP1 0x249 +#define SN95031_MICAMP2 0x24A +#define SN95031_NOISEMUX 0x24B +#define SN95031_AUDIOMUX12 0x24C +#define SN95031_AUDIOMUX34 0x24D +#define SN95031_AUDIOSINC 0x24E +#define SN95031_AUDIOTXEN 0x24F +#define SN95031_HSEPRXCTRL 0x250 +#define SN95031_IHFRXCTRL 0x251 +#define SN95031_HSMIXER 0x256 +#define SN95031_DACCONFIG 0x257 +#define SN95031_SOFTMUTE 0x258 +#define SN95031_HSLVOLCTRL 0x259 +#define SN95031_HSRVOLCTRL 0x25A +#define SN95031_IHFLVOLCTRL 0x25B +#define SN95031_IHFRVOLCTRL 0x25C +#define SN95031_DRIVEREN 0x25D +#define SN95031_LOCTL 0x25E +#define SN95031_VIB1C1 0x25F +#define SN95031_VIB1C2 0x260 +#define SN95031_VIB1C3 0x261 +#define SN95031_VIB1SPIPCM1 0x262 +#define SN95031_VIB1SPIPCM2 0x263 +#define SN95031_VIB1C5 0x264 +#define SN95031_VIB2C1 0x265 +#define SN95031_VIB2C2 0x266 +#define SN95031_VIB2C3 0x267 +#define SN95031_VIB2SPIPCM1 0x268 +#define SN95031_VIB2SPIPCM2 0x269 +#define SN95031_VIB2C5 0x26A +#define SN95031_BTNCTRL1 0x26B +#define SN95031_BTNCTRL2 0x26C +#define SN95031_PCM1TXSLOT01 0x26D +#define SN95031_PCM1TXSLOT23 0x26E +#define SN95031_PCM1TXSLOT45 0x26F +#define SN95031_PCM1RXSLOT0_3 0x270 +#define SN95031_PCM1RXSLOT45 0x271 +#define SN95031_PCM2TXSLOT01 0x272 +#define SN95031_PCM2TXSLOT23 0x273 +#define SN95031_PCM2TXSLOT45 0x274 +#define SN95031_PCM2RXSLOT01 0x275 +#define SN95031_PCM2RXSLOT23 0x276 +#define SN95031_PCM2RXSLOT45 0x277 +#define SN95031_PCM1C1 0x278 +#define SN95031_PCM1C2 0x279 +#define SN95031_PCM1C3 0x27A +#define SN95031_PCM2C1 0x27B +#define SN95031_PCM2C2 0x27C +/*end codec register defn*/ + +/*vendor defn these are not part of avp*/ +#define SN95031_SSR2 0x381 +#define SN95031_SSR3 0x382 +#define SN95031_SSR5 0x384 +#define SN95031_SSR6 0x385 + +/* ADC registers */ + +#define SN95031_ADC1CNTL1 0x1C0 +#define SN95031_ADC_ENBL 0x10 +#define SN95031_ADC_START 0x08 +#define SN95031_ADC1CNTL3 0x1C2 +#define SN95031_ADCTHERM_ENBL 0x04 +#define SN95031_ADCRRDATA_ENBL 0x05 +#define SN95031_STOPBIT_MASK 16 +#define SN95031_ADCTHERM_MASK 4 +#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */ +#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1) +#define SN95031_ADC_NO_LOOP 0x07 +#define SN95031_AUDIO_GPIO_CTRL 0x070 + +/* ADC channel code values */ +#define SN95031_AUDIO_DETECT_CODE 0x06 + +/* ADC base addresses */ +#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ +#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ +/* multipier to convert to mV */ +#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346 + + +struct mfld_jack_data { + int intr_id; + int micbias_vol; + struct snd_soc_jack *mfld_jack; +}; + +extern void sn95031_jack_detection(struct mfld_jack_data *jack_data); + +#endif diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 6f38d619bf8a..2727befd158e 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -38,7 +38,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "ssm2602.h" @@ -207,10 +206,11 @@ static const struct snd_soc_dapm_route audio_conn[] = { static int ssm2602_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, ssm2602_dapm_widgets, - ARRAY_SIZE(ssm2602_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn)); + 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; } @@ -493,7 +493,7 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 061f9e5a497b..78b2b50271e2 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -236,7 +236,7 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index e8652b1ae326..54a30ef0ec8b 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -30,7 +30,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/initval.h> @@ -391,11 +390,12 @@ static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk, static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, - ARRAY_SIZE(tlv320aic23_dapm_widgets)); + 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(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); return 0; } @@ -574,7 +574,7 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, tlv320aic23_write(codec, TLV320AIC23_PWR, 0xffff); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 6b7d71ec0004..e2a7608d3944 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -18,7 +18,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "tlv320aic26.h" @@ -31,7 +30,6 @@ MODULE_LICENSE("GPL"); struct aic26 { struct spi_device *spi; struct snd_soc_codec codec; - u16 reg_cache[AIC26_NUM_REGS]; /* shadow registers */ int master; int datfm; int mclk; @@ -355,7 +353,6 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); */ static int aic26_probe(struct snd_soc_codec *codec) { - struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec); int ret, err, i, reg; dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n"); @@ -373,7 +370,7 @@ static int aic26_probe(struct snd_soc_codec *codec) aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); /* Fill register cache */ - for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++) + for (i = 0; i < codec->driver->reg_cache_size; i++) aic26_reg_read(codec, i); /* Register the sysfs files for debugging */ diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h index 62b1f2261429..67f19c3bebe6 100644 --- a/sound/soc/codecs/tlv320aic26.h +++ b/sound/soc/codecs/tlv320aic26.h @@ -14,14 +14,14 @@ #define AIC26_PAGE_ADDR(page, offset) ((page << 6) | offset) #define AIC26_NUM_REGS AIC26_PAGE_ADDR(3, 0) -/* Page 0: Auxillary data registers */ +/* Page 0: Auxiliary data registers */ #define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05) #define AIC26_REG_BAT2 AIC26_PAGE_ADDR(0, 0x06) #define AIC26_REG_AUX AIC26_PAGE_ADDR(0, 0x07) #define AIC26_REG_TEMP1 AIC26_PAGE_ADDR(0, 0x09) #define AIC26_REG_TEMP2 AIC26_PAGE_ADDR(0, 0x0A) -/* Page 1: Auxillary control registers */ +/* Page 1: Auxiliary control registers */ #define AIC26_REG_AUX_ADC AIC26_PAGE_ADDR(1, 0x00) #define AIC26_REG_STATUS AIC26_PAGE_ADDR(1, 0x01) #define AIC26_REG_REFERENCE AIC26_PAGE_ADDR(1, 0x03) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c new file mode 100644 index 000000000000..e93b9d1ae1dd --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -0,0 +1,794 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin <javier.martin@vista-silicon.com> + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * 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. + * + * 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 Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#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/cdev.h> +#include <linux/slab.h> + +#include <sound/tlv320aic32x4.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "tlv320aic32x4.h" + +struct aic32x4_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +}; + +struct aic32x4_priv { + u32 sysclk; + s32 master; + u8 page_no; + void *control_data; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +/* 0dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); + +static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, + 0, 0x0F, 0), +}; + +static const struct aic32x4_rate_divs aic32x4_divs[] = { + /* 8k rate */ + {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4} +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0), + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + /* Left Output */ + {"HPL Output Mixer", "L_DAC Switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L Switch", "IN1_L"}, + + {"HPL Power", NULL, "HPL Output Mixer"}, + {"HPL", NULL, "HPL Power"}, + + {"LOL Output Mixer", "L_DAC Switch", "Left DAC"}, + + {"LOL Power", NULL, "LOL Output Mixer"}, + {"LOL", NULL, "LOL Power"}, + + /* Right Output */ + {"HPR Output Mixer", "R_DAC Switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R Switch", "IN1_R"}, + + {"HPR Power", NULL, "HPR Output Mixer"}, + {"HPR", NULL, "HPR Power"}, + + {"LOR Output Mixer", "R_DAC Switch", "Right DAC"}, + + {"LOR Power", NULL, "LOR Output Mixer"}, + {"LOR", NULL, "LOR Power"}, + + /* Left input */ + {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, + + {"Left ADC", NULL, "Left Input Mixer"}, + + /* Right Input */ + {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, + + {"Right ADC", NULL, "Right Input Mixer"}, +}; + +static inline int aic32x4_change_page(struct snd_soc_codec *codec, + unsigned int new_page) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data[2]; + int ret; + + data[0] = 0x00; + data[1] = new_page & 0xff; + + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) { + aic32x4->page_no = new_page; + return 0; + } else { + return ret; + } +} + +static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + u8 data[2]; + int ret; + + /* A write to AIC32X4_PSEL is really a non-explicit page change */ + if (reg == AIC32X4_PSEL) + return aic32x4_change_page(codec, val); + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + + data[0] = fixed_reg & 0xff; + data[1] = val & 0xff; + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + int ret; + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff); +} + +static inline int aic32x4_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) { + if ((aic32x4_divs[i].rate == rate) + && (aic32x4_divs[i].mclk == mclk)) { + return i; + } + } + printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n"); + return -EINVAL; +} + +static int aic32x4_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets, + ARRAY_SIZE(aic32x4_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes, + ARRAY_SIZE(aic32x4_dapm_routes)); + + snd_soc_dapm_new_widgets(&codec->dapm); + return 0; +} + +static int aic32x4_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 aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case AIC32X4_FREQ_12000000: + case AIC32X4_FREQ_24000000: + case AIC32X4_FREQ_25000000: + aic32x4->sysclk = freq; + return 0; + } + printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n"); + return -EINVAL; +} + +static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 iface_reg_1; + u8 iface_reg_2; + u8 iface_reg_3; + + iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1); + iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); + iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2); + iface_reg_2 = 0; + iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3); + iface_reg_3 = iface_reg_3 & ~(1 << 3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic32x4->master = 1; + iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic32x4->master = 0; + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_2 = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= + (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= + (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1); + snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2); + snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3); + return 0; +} + +static int aic32x4_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 aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data; + int i; + + i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); + if (i < 0) { + printk(KERN_ERR "aic32x4: sampling rate not supported\n"); + return i; + } + + /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN); + snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK); + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + data = snd_soc_read(codec, AIC32X4_PLLPR); + data &= ~(7 << 4); + snd_soc_write(codec, AIC32X4_PLLPR, + (data | (aic32x4_divs[i].p_val << 4) | 0x01)); + + snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); + + snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); + snd_soc_write(codec, AIC32X4_PLLDLSB, + (aic32x4_divs[i].pll_d & 0xff)); + + /* NDAC divider value */ + data = snd_soc_read(codec, AIC32X4_NDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac); + + /* MDAC divider value */ + data = snd_soc_read(codec, AIC32X4_MDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac); + + /* DOSR MSB & LSB values */ + snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); + snd_soc_write(codec, AIC32X4_DOSRLSB, + (aic32x4_divs[i].dosr & 0xff)); + + /* NADC divider value */ + data = snd_soc_read(codec, AIC32X4_NADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc); + + /* MADC divider value */ + data = snd_soc_read(codec, AIC32X4_MADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc); + + /* AOSR value */ + snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr); + + /* BCLK N divider */ + data = snd_soc_read(codec, AIC32X4_BCLKN); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N); + + data = snd_soc_read(codec, AIC32X4_IFACE1); + data = data & ~(3 << 4); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT); + break; + } + snd_soc_write(codec, AIC32X4_IFACE1, data); + + return 0; +} + +static int aic32x4_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dac_reg; + + dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON; + if (mute) + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON); + else + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg); + return 0; +} + +static int aic32x4_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 value; + + switch (level) { + case SND_SOC_BIAS_ON: + if (aic32x4->master) { + /* Switch on PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value | AIC32X4_PLLEN)); + + /* Switch on NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value | AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value | AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value | AIC32X4_MDACEN); + + /* Switch on MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value | AIC32X4_MDACEN); + + /* Switch on BCLK_N Divider */ + value = snd_soc_read(codec, AIC32X4_BCLKN); + snd_soc_write(codec, AIC32X4_BCLKN, + value | AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (aic32x4->master) { + /* Switch off PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value & ~AIC32X4_PLLEN)); + + /* Switch off NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value & ~AIC32X4_NDACEN); + + /* Switch off MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value & ~AIC32X4_MDACEN); + + /* Switch off NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value & ~AIC32X4_NDACEN); + + /* Switch off MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value & ~AIC32X4_MDACEN); + value = snd_soc_read(codec, AIC32X4_BCLKN); + + /* Switch off BCLK_N Divider */ + snd_soc_write(codec, AIC32X4_BCLKN, + value & ~AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000 +#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops aic32x4_ops = { + .hw_params = aic32x4_hw_params, + .digital_mute = aic32x4_mute, + .set_fmt = aic32x4_set_dai_fmt, + .set_sysclk = aic32x4_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver aic32x4_dai = { + .name = "tlv320aic32x4-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rates = 1, +}; + +static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int aic32x4_resume(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int aic32x4_probe(struct snd_soc_codec *codec) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u32 tmp_reg; + + codec->hw_write = (hw_write_t) i2c_master_send; + codec->control_data = aic32x4->control_data; + + snd_soc_write(codec, AIC32X4_RESET, 0x01); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { + snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { + snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); + } + tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { + tmp_reg |= AIC32X4_LDOIN_18_36; + } + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) { + tmp_reg |= AIC32X4_LDOIN2HP; + } + snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); + + /* Do DACs need to be swapped? */ + if (aic32x4->swapdacs) { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); + } else { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); + } + + /* Mic PGA routing */ + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); + } + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); + } + + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_add_controls(codec, aic32x4_snd_controls, + ARRAY_SIZE(aic32x4_snd_controls)); + aic32x4_add_widgets(codec); + + return 0; +} + +static int aic32x4_remove(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { + .read = aic32x4_read, + .write = aic32x4_write, + .probe = aic32x4_probe, + .remove = aic32x4_remove, + .suspend = aic32x4_suspend, + .resume = aic32x4_resume, + .set_bias_level = aic32x4_set_bias_level, +}; + +static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic32x4_pdata *pdata = i2c->dev.platform_data; + struct aic32x4_priv *aic32x4; + int ret; + + aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL); + if (aic32x4 == NULL) + return -ENOMEM; + + aic32x4->control_data = i2c; + i2c_set_clientdata(i2c, aic32x4); + + if (pdata) { + aic32x4->power_cfg = pdata->power_cfg; + aic32x4->swapdacs = pdata->swapdacs; + aic32x4->micpga_routing = pdata->micpga_routing; + } else { + aic32x4->power_cfg = 0; + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aic32x4, &aic32x4_dai, 1); + if (ret < 0) + kfree(aic32x4); + return ret; +} + +static __devexit int aic32x4_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 aic32x4_i2c_id[] = { + { "tlv320aic32x4", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + }, + .probe = aic32x4_i2c_probe, + .remove = __devexit_p(aic32x4_i2c_remove), + .id_table = aic32x4_i2c_id, +}; + +static int __init aic32x4_modinit(void) +{ + int ret = 0; + + ret = i2c_add_driver(&aic32x4_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n", + ret); + } + return ret; +} +module_init(aic32x4_modinit); + +static void __exit aic32x4_exit(void) +{ + i2c_del_driver(&aic32x4_i2c_driver); +} +module_exit(aic32x4_exit); + +MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h new file mode 100644 index 000000000000..aae2b2440398 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -0,0 +1,143 @@ +/* + * tlv320aic32x4.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 _TLV320AIC32X4_H +#define _TLV320AIC32X4_H + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define AIC32X4_PAGE1 128 + +#define AIC32X4_PSEL 0 +#define AIC32X4_RESET 1 +#define AIC32X4_CLKMUX 4 +#define AIC32X4_PLLPR 5 +#define AIC32X4_PLLJ 6 +#define AIC32X4_PLLDMSB 7 +#define AIC32X4_PLLDLSB 8 +#define AIC32X4_NDAC 11 +#define AIC32X4_MDAC 12 +#define AIC32X4_DOSRMSB 13 +#define AIC32X4_DOSRLSB 14 +#define AIC32X4_NADC 18 +#define AIC32X4_MADC 19 +#define AIC32X4_AOSR 20 +#define AIC32X4_CLKMUX2 25 +#define AIC32X4_CLKOUTM 26 +#define AIC32X4_IFACE1 27 +#define AIC32X4_IFACE2 28 +#define AIC32X4_IFACE3 29 +#define AIC32X4_BCLKN 30 +#define AIC32X4_IFACE4 31 +#define AIC32X4_IFACE5 32 +#define AIC32X4_IFACE6 33 +#define AIC32X4_DOUTCTL 53 +#define AIC32X4_DINCTL 54 +#define AIC32X4_DACSPB 60 +#define AIC32X4_ADCSPB 61 +#define AIC32X4_DACSETUP 63 +#define AIC32X4_DACMUTE 64 +#define AIC32X4_LDACVOL 65 +#define AIC32X4_RDACVOL 66 +#define AIC32X4_ADCSETUP 81 +#define AIC32X4_ADCFGA 82 +#define AIC32X4_LADCVOL 83 +#define AIC32X4_RADCVOL 84 +#define AIC32X4_LAGC1 86 +#define AIC32X4_LAGC2 87 +#define AIC32X4_LAGC3 88 +#define AIC32X4_LAGC4 89 +#define AIC32X4_LAGC5 90 +#define AIC32X4_LAGC6 91 +#define AIC32X4_LAGC7 92 +#define AIC32X4_RAGC1 94 +#define AIC32X4_RAGC2 95 +#define AIC32X4_RAGC3 96 +#define AIC32X4_RAGC4 97 +#define AIC32X4_RAGC5 98 +#define AIC32X4_RAGC6 99 +#define AIC32X4_RAGC7 100 +#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1) +#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2) +#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9) +#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10) +#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12) +#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13) +#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14) +#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15) +#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16) +#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17) +#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18) +#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19) +#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20) +#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51) +#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52) +#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54) +#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55) +#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57) +#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58) +#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59) +#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60) + +#define AIC32X4_FREQ_12000000 12000000 +#define AIC32X4_FREQ_24000000 24000000 +#define AIC32X4_FREQ_25000000 25000000 + +#define AIC32X4_WORD_LEN_16BITS 0x00 +#define AIC32X4_WORD_LEN_20BITS 0x01 +#define AIC32X4_WORD_LEN_24BITS 0x02 +#define AIC32X4_WORD_LEN_32BITS 0x03 + +#define AIC32X4_I2S_MODE 0x00 +#define AIC32X4_DSP_MODE 0x01 +#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03 + +#define AIC32X4_AVDDWEAKDISABLE 0x08 +#define AIC32X4_LDOCTLEN 0x01 + +#define AIC32X4_LDOIN_18_36 0x01 +#define AIC32X4_LDOIN2HP 0x02 + +#define AIC32X4_DACSPBLOCK_MASK 0x1f +#define AIC32X4_ADCSPBLOCK_MASK 0x1f + +#define AIC32X4_PLLJ_SHIFT 6 +#define AIC32X4_DOSRMSB_SHIFT 4 + +#define AIC32X4_PLLCLKIN 0x03 + +#define AIC32X4_MICBIAS_LDOIN 0x08 +#define AIC32X4_MICBIAS_2075V 0x60 + +#define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_RMICPGANIN_IN1L_10K 0x10 + +#define AIC32X4_LMICPGAVOL_NOGAIN 0x80 +#define AIC32X4_RMICPGAVOL_NOGAIN 0x80 + +#define AIC32X4_BCLKMASTER 0x08 +#define AIC32X4_WCLKMASTER 0x04 +#define AIC32X4_PLLEN (0x01 << 7) +#define AIC32X4_NDACEN (0x01 << 7) +#define AIC32X4_MDACEN (0x01 << 7) +#define AIC32X4_NADCEN (0x01 << 7) +#define AIC32X4_MADCEN (0x01 << 7) +#define AIC32X4_BCLKEN (0x01 << 7) +#define AIC32X4_DACEN (0x03 << 6) +#define AIC32X4_RDAC2LCHN (0x02 << 2) +#define AIC32X4_LDAC2RCHN (0x02 << 4) +#define AIC32X4_LDAC2LCHN (0x01 << 4) +#define AIC32X4_RDAC2RCHN (0x01 << 2) + +#define AIC32X4_SSTEP2WCLK 0x01 +#define AIC32X4_MUTEON 0x0C +#define AIC32X4_DACMOD2BCLK 0x01 + +#endif /* _TLV320AIC32X4_H */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 77b8f9ae29be..6c43c13f0430 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -46,7 +46,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/tlv320aic3x.h> @@ -61,6 +60,8 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { "DRVDD", /* ADC Analog and Output Driver Voltage */ }; +static LIST_HEAD(reset_list); + struct aic3x_priv; struct aic3x_disable_nb { @@ -77,6 +78,7 @@ struct aic3x_priv { struct aic3x_setup_data *setup; void *control_data; unsigned int sysclk; + struct list_head list; int master; int gpio_reset; int power; @@ -183,7 +185,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->codec->dapm_paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -199,7 +201,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, } if (found) - snd_soc_dapm_sync(widget->codec); + snd_soc_dapm_sync(widget->dapm); } ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); @@ -788,17 +790,19 @@ static const struct snd_soc_dapm_route intercon_3007[] = { static int aic3x_add_widgets(struct snd_soc_codec *codec) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, + snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, ARRAY_SIZE(aic3x_dapm_widgets)); /* set up audio path interconnects */ - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); if (aic3x->model == AIC3X_MODEL_3007) { - snd_soc_dapm_new_controls(codec, aic3007_dapm_widgets, + snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets, ARRAY_SIZE(aic3007_dapm_widgets)); - snd_soc_dapm_add_routes(codec, intercon_3007, ARRAY_SIZE(intercon_3007)); + snd_soc_dapm_add_routes(dapm, intercon_3007, + ARRAY_SIZE(intercon_3007)); } return 0; @@ -880,7 +884,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, if (bypass_pll) return 0; - /* Use PLL, compute apropriate setup for j, d, r and p, the closest + /* Use PLL, compute appropriate setup for j, d, r and p, the closest * one wins the game. Try with d==0 first, next with d!=0. * Constraints for j are according to the datasheet. * The sysclk is divided by 1000 to prevent integer overflows. @@ -1075,7 +1079,7 @@ static int aic3x_regulator_event(struct notifier_block *nb, * Put codec to reset and require cache sync as at least one * of the supplies was disabled */ - if (aic3x->gpio_reset >= 0) + if (gpio_is_valid(aic3x->gpio_reset)) gpio_set_value(aic3x->gpio_reset, 0); aic3x->codec->cache_sync = 1; } @@ -1102,7 +1106,7 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) if (!codec->cache_sync) goto out; - if (aic3x->gpio_reset >= 0) { + if (gpio_is_valid(aic3x->gpio_reset)) { udelay(1); gpio_set_value(aic3x->gpio_reset, 1); } @@ -1135,7 +1139,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - if (codec->bias_level == SND_SOC_BIAS_STANDBY && + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY && aic3x->master) { /* enable pll */ reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); @@ -1146,7 +1150,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (!aic3x->power) aic3x_set_power(codec, 1); - if (codec->bias_level == SND_SOC_BIAS_PREPARE && + if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE && aic3x->master) { /* disable pll */ reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG); @@ -1159,7 +1163,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, aic3x_set_power(codec, 0); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1344,14 +1348,28 @@ static int aic3x_init(struct snd_soc_codec *codec) return 0; } +static bool aic3x_is_shared_reset(struct aic3x_priv *aic3x) +{ + struct aic3x_priv *a; + + list_for_each_entry(a, &reset_list, list) { + if (gpio_is_valid(aic3x->gpio_reset) && + aic3x->gpio_reset == a->gpio_reset) + return true; + } + + return false; +} + static int aic3x_probe(struct snd_soc_codec *codec) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); int ret, i; + INIT_LIST_HEAD(&aic3x->list); codec->control_data = aic3x->control_data; aic3x->codec = codec; - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type); if (ret != 0) { @@ -1359,7 +1377,8 @@ static int aic3x_probe(struct snd_soc_codec *codec) return ret; } - if (aic3x->gpio_reset >= 0) { + if (gpio_is_valid(aic3x->gpio_reset) && + !aic3x_is_shared_reset(aic3x)) { ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset"); if (ret != 0) goto err_gpio; @@ -1405,6 +1424,7 @@ static int aic3x_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); aic3x_add_widgets(codec); + list_add(&aic3x->list, &reset_list); return 0; @@ -1414,10 +1434,10 @@ err_notif: &aic3x->disable_nb[i].nb); regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); err_get: - if (aic3x->gpio_reset >= 0) + if (gpio_is_valid(aic3x->gpio_reset) && + !aic3x_is_shared_reset(aic3x)) gpio_free(aic3x->gpio_reset); err_gpio: - kfree(aic3x); return ret; } @@ -1427,7 +1447,9 @@ static int aic3x_remove(struct snd_soc_codec *codec) int i; aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (aic3x->gpio_reset >= 0) { + list_del(&aic3x->list); + if (gpio_is_valid(aic3x->gpio_reset) && + !aic3x_is_shared_reset(aic3x)) { gpio_set_value(aic3x->gpio_reset, 0); gpio_free(aic3x->gpio_reset); } @@ -1523,21 +1545,6 @@ static struct i2c_driver aic3x_i2c_driver = { .remove = aic3x_i2c_remove, .id_table = aic3x_i2c_id, }; - -static inline void aic3x_i2c_init(void) -{ - int ret; - - ret = i2c_add_driver(&aic3x_i2c_driver); - if (ret) - printk(KERN_ERR "%s: error regsitering i2c driver, %d\n", - __func__, ret); -} - -static inline void aic3x_i2c_exit(void) -{ - i2c_del_driver(&aic3x_i2c_driver); -} #endif static int __init aic3x_modinit(void) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index c5ab8c805771..082e9d51963f 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -36,21 +36,21 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/tlv320dac33-plat.h> #include "tlv320dac33.h" -#define DAC33_BUFFER_SIZE_BYTES 24576 /* bytes, 12288 16 bit words, - * 6144 stereo */ -#define DAC33_BUFFER_SIZE_SAMPLES 6144 - -#define NSAMPLE_MAX 5700 - -#define MODE7_LTHR 10 -#define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10) +/* + * The internal FIFO is 24576 bytes long + * It can be configured to hold 16bit or 24bit samples + * In 16bit configuration the FIFO can hold 6144 stereo samples + * In 24bit configuration the FIFO can hold 4096 stereo samples + */ +#define DAC33_FIFO_SIZE_16BIT 6144 +#define DAC33_FIFO_SIZE_24BIT 4096 +#define DAC33_MODE7_MARGIN 10 /* Safety margin for FIFO in Mode7 */ #define BURST_BASEFREQ_HZ 49152000 @@ -100,16 +100,11 @@ struct tlv320dac33_priv { unsigned int refclk; unsigned int alarm_threshold; /* set to be half of LATENCY_TIME_MS */ - unsigned int nsample_min; /* nsample should not be lower than - * this */ - unsigned int nsample_max; /* nsample should not be higher than - * this */ enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */ + unsigned int fifo_size; /* Size of the FIFO in samples */ unsigned int nsample; /* burst read amount from host */ int mode1_latency; /* latency caused by the i2c writes in * us */ - int auto_fifo_config; /* Configure the FIFO based on the - * period size */ u8 burst_bclkdiv; /* BCLK divider value in burst mode */ unsigned int burst_rate; /* Interface speed in Burst modes */ @@ -303,7 +298,6 @@ static void dac33_init_chip(struct snd_soc_codec *codec) if (unlikely(!dac33->chip_power)) return; - /* 44-46: DAC Control Registers */ /* A : DAC sample rate Fsref/1.5 */ dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0)); /* B : DAC src=normal, not muted */ @@ -316,8 +310,6 @@ static void dac33_init_chip(struct snd_soc_codec *codec) clock source = internal osc (?) */ dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN); - dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB); - /* Restore only selected registers (gains mostly) */ dac33_write(codec, DAC33_LDAC_DIG_VOL_CTRL, dac33_read_reg_cache(codec, DAC33_LDAC_DIG_VOL_CTRL)); @@ -328,6 +320,14 @@ static void dac33_init_chip(struct snd_soc_codec *codec) dac33_read_reg_cache(codec, DAC33_LINEL_TO_LLO_VOL)); dac33_write(codec, DAC33_LINER_TO_RLO_VOL, dac33_read_reg_cache(codec, DAC33_LINER_TO_RLO_VOL)); + + dac33_write(codec, DAC33_OUT_AMP_CTRL, + dac33_read_reg_cache(codec, DAC33_OUT_AMP_CTRL)); + + dac33_write(codec, DAC33_LDAC_PWR_CTRL, + dac33_read_reg_cache(codec, DAC33_LDAC_PWR_CTRL)); + dac33_write(codec, DAC33_RDAC_PWR_CTRL, + dac33_read_reg_cache(codec, DAC33_RDAC_PWR_CTRL)); } static inline int dac33_read_id(struct snd_soc_codec *codec) @@ -357,6 +357,21 @@ static inline void dac33_soft_power(struct snd_soc_codec *codec, int power) dac33_write(codec, DAC33_PWR_CTRL, reg); } +static inline void dac33_disable_digital(struct snd_soc_codec *codec) +{ + u8 reg; + + /* Stop the DAI clock */ + reg = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B); + reg &= ~DAC33_BCLKON; + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg); + + /* Power down the Oscillator, and DACs */ + reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL); + reg &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB); + dac33_write(codec, DAC33_PWR_CTRL, reg); +} + static int dac33_hard_power(struct snd_soc_codec *codec, int power) { struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); @@ -405,7 +420,7 @@ exit: return ret; } -static int playback_event(struct snd_soc_dapm_widget *w, +static int dac33_playback_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(w->codec); @@ -417,77 +432,13 @@ static int playback_event(struct snd_soc_dapm_widget *w, dac33_prepare_chip(dac33->substream); } break; + case SND_SOC_DAPM_POST_PMD: + dac33_disable_digital(w->codec); + break; } return 0; } -static int dac33_get_nsample(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = dac33->nsample; - - return 0; -} - -static int dac33_set_nsample(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - int ret = 0; - - if (dac33->nsample == ucontrol->value.integer.value[0]) - return 0; - - if (ucontrol->value.integer.value[0] < dac33->nsample_min || - ucontrol->value.integer.value[0] > dac33->nsample_max) { - ret = -EINVAL; - } else { - dac33->nsample = ucontrol->value.integer.value[0]; - /* Re calculate the burst time */ - dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, - dac33->nsample); - } - - return ret; -} - -static int dac33_get_uthr(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = dac33->uthr; - - return 0; -} - -static int dac33_set_uthr(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - int ret = 0; - - if (dac33->substream) - return -EBUSY; - - if (dac33->uthr == ucontrol->value.integer.value[0]) - return 0; - - if (ucontrol->value.integer.value[0] < (MODE7_LTHR + 10) || - ucontrol->value.integer.value[0] > MODE7_UTHR) - ret = -EINVAL; - else - dac33->uthr = ucontrol->value.integer.value[0]; - - return ret; -} - static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -572,13 +523,6 @@ static const struct snd_kcontrol_new dac33_mode_snd_controls[] = { dac33_get_fifo_mode, dac33_set_fifo_mode), }; -static const struct snd_kcontrol_new dac33_fifo_snd_controls[] = { - SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0, - dac33_get_nsample, dac33_set_nsample), - SOC_SINGLE_EXT("UTHR", 0, 0, MODE7_UTHR, 0, - dac33_get_uthr, dac33_set_uthr), -}; - /* Analog bypass */ static const struct snd_kcontrol_new dac33_dapm_abypassl_control = SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1); @@ -586,6 +530,25 @@ static const struct snd_kcontrol_new dac33_dapm_abypassl_control = static const struct snd_kcontrol_new dac33_dapm_abypassr_control = SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1); +/* LOP L/R invert selection */ +static const char *dac33_lr_lom_texts[] = {"DAC", "LOP"}; + +static const struct soc_enum dac33_left_lom_enum = + SOC_ENUM_SINGLE(DAC33_OUT_AMP_CTRL, 3, + ARRAY_SIZE(dac33_lr_lom_texts), + dac33_lr_lom_texts); + +static const struct snd_kcontrol_new dac33_dapm_left_lom_control = +SOC_DAPM_ENUM("Route", dac33_left_lom_enum); + +static const struct soc_enum dac33_right_lom_enum = + SOC_ENUM_SINGLE(DAC33_OUT_AMP_CTRL, 2, + ARRAY_SIZE(dac33_lr_lom_texts), + dac33_lr_lom_texts); + +static const struct snd_kcontrol_new dac33_dapm_right_lom_control = +SOC_DAPM_ENUM("Route", dac33_right_lom_enum); + static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("LEFT_LO"), SND_SOC_DAPM_OUTPUT("RIGHT_LO"), @@ -593,8 +556,8 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = { SND_SOC_DAPM_INPUT("LINEL"), SND_SOC_DAPM_INPUT("LINER"), - SND_SOC_DAPM_DAC("DACL", "Left Playback", DAC33_LDAC_PWR_CTRL, 2, 0), - SND_SOC_DAPM_DAC("DACR", "Right Playback", DAC33_RDAC_PWR_CTRL, 2, 0), + SND_SOC_DAPM_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0), /* Analog bypass */ SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0, @@ -602,12 +565,30 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = { SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0, &dac33_dapm_abypassr_control), - SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amp Power", + SND_SOC_DAPM_MUX("Left LOM Inverted From", SND_SOC_NOPM, 0, 0, + &dac33_dapm_left_lom_control), + SND_SOC_DAPM_MUX("Right LOM Inverted From", SND_SOC_NOPM, 0, 0, + &dac33_dapm_right_lom_control), + /* + * For DAPM path, when only the anlog bypass path is enabled, and the + * LOP inverted from the corresponding DAC side. + * This is needed, so we can attach the DAC power supply in this case. + */ + SND_SOC_DAPM_PGA("Left Bypass PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Bypass PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amplifier", DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0), - SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power", + SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amplifier", DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0), - SND_SOC_DAPM_PRE("Prepare Playback", playback_event), + SND_SOC_DAPM_SUPPLY("Left DAC Power", + DAC33_LDAC_PWR_CTRL, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right DAC Power", + DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0), + + SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event), + SND_SOC_DAPM_POST("Post Playback", dac33_playback_event), }; static const struct snd_soc_dapm_route audio_map[] = { @@ -615,24 +596,39 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Analog Left Bypass", "Switch", "LINEL"}, {"Analog Right Bypass", "Switch", "LINER"}, - {"Output Left Amp Power", NULL, "DACL"}, - {"Output Right Amp Power", NULL, "DACR"}, + {"Output Left Amplifier", NULL, "DACL"}, + {"Output Right Amplifier", NULL, "DACR"}, - {"Output Left Amp Power", NULL, "Analog Left Bypass"}, - {"Output Right Amp Power", NULL, "Analog Right Bypass"}, + {"Left Bypass PGA", NULL, "Analog Left Bypass"}, + {"Right Bypass PGA", NULL, "Analog Right Bypass"}, + + {"Left LOM Inverted From", "DAC", "Left Bypass PGA"}, + {"Right LOM Inverted From", "DAC", "Right Bypass PGA"}, + {"Left LOM Inverted From", "LOP", "Analog Left Bypass"}, + {"Right LOM Inverted From", "LOP", "Analog Right Bypass"}, + + {"Output Left Amplifier", NULL, "Left LOM Inverted From"}, + {"Output Right Amplifier", NULL, "Right LOM Inverted From"}, + + {"DACL", NULL, "Left DAC Power"}, + {"DACR", NULL, "Right DAC Power"}, + + {"Left Bypass PGA", NULL, "Left DAC Power"}, + {"Right Bypass PGA", NULL, "Right DAC Power"}, /* output */ - {"LEFT_LO", NULL, "Output Left Amp Power"}, - {"RIGHT_LO", NULL, "Output Right Amp Power"}, + {"LEFT_LO", NULL, "Output Left Amplifier"}, + {"RIGHT_LO", NULL, "Output Right Amplifier"}, }; static int dac33_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, dac33_dapm_widgets, - ARRAY_SIZE(dac33_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_dapm_new_controls(dapm, dac33_dapm_widgets, + ARRAY_SIZE(dac33_dapm_widgets)); /* set up audio path interconnects */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -640,16 +636,18 @@ 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: - dac33_soft_power(codec, 1); + if (!dac33->substream) + dac33_soft_power(codec, 1); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Coming from OFF, switch on the codec */ ret = dac33_hard_power(codec, 1); if (ret != 0) @@ -660,14 +658,14 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: /* Do not power off, when the codec is already off */ - if (codec->bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) return 0; ret = dac33_hard_power(codec, 0); if (ret != 0) return ret; break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -676,6 +674,7 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_codec *codec = dac33->codec; unsigned int delay; + unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: @@ -683,10 +682,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) DAC33_THRREG(dac33->nsample)); /* Take the timestamps */ - spin_lock_irq(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp2 = ktime_to_us(ktime_get()); dac33->t_stamp1 = dac33->t_stamp2; - spin_unlock_irq(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); @@ -698,14 +697,14 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) break; case DAC33_FIFO_MODE7: /* Take the timestamp */ - spin_lock_irq(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp1 = ktime_to_us(ktime_get()); /* Move back the timestamp with drain time */ dac33->t_stamp1 -= dac33->mode7_us_to_lthr; - spin_unlock_irq(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); dac33_write16(codec, DAC33_PREFILL_MSB, - DAC33_THRREG(MODE7_LTHR)); + DAC33_THRREG(DAC33_MODE7_MARGIN)); /* Enable Upper Threshold IRQ */ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MUT); @@ -720,13 +719,14 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) { struct snd_soc_codec *codec = dac33->codec; + unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: /* Take the timestamp */ - spin_lock_irq(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp2 = ktime_to_us(ktime_get()); - spin_unlock_irq(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); dac33_write16(codec, DAC33_NSAMPLE_MSB, DAC33_THRREG(dac33->nsample)); @@ -779,10 +779,11 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev) { struct snd_soc_codec *codec = dev; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); + unsigned long flags; - spin_lock(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); dac33->t_stamp1 = ktime_to_us(ktime_get()); - spin_unlock(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); /* Do not schedule the workqueue in Mode7 */ if (dac33->fifo_mode != DAC33_FIFO_MODE7) @@ -815,6 +816,8 @@ static int dac33_startup(struct snd_pcm_substream *substream, /* Stream started, save the substream pointer */ dac33->substream = substream; + snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); + return 0; } @@ -826,18 +829,17 @@ static void dac33_shutdown(struct snd_pcm_substream *substream, struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); dac33->substream = NULL; - - /* Reset the nSample restrictions */ - dac33->nsample_min = 0; - dac33->nsample_max = NSAMPLE_MAX; } +#define CALC_BURST_RATE(bclkdiv, bclk_per_sample) \ + (BURST_BASEFREQ_HZ / bclkdiv / bclk_per_sample) static int dac33_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 tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); /* Check parameters for validity */ switch (params_rate(params)) { @@ -852,6 +854,12 @@ static int dac33_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: + dac33->fifo_size = DAC33_FIFO_SIZE_16BIT; + dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 32); + break; + case SNDRV_PCM_FORMAT_S32_LE: + dac33->fifo_size = DAC33_FIFO_SIZE_24BIT; + dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 64); break; default: dev_err(codec->dev, "unsupported format %d\n", @@ -906,6 +914,9 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16); fifoctrl_a |= DAC33_WIDTH; break; + case SNDRV_PCM_FORMAT_S32_LE: + aictrl_a |= (DAC33_NCYCL_32 | DAC33_WLEN_24); + break; default: dev_err(codec->dev, "unsupported format %d\n", substream->runtime->format); @@ -1016,7 +1027,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) /* * For FIFO bypass mode: * Enable the FIFO bypass (Disable the FIFO use) - * Set the BCLK as continous + * Set the BCLK as continuous */ fifoctrl_a |= DAC33_FBYPAS; aictrl_b |= DAC33_BCLKON; @@ -1040,7 +1051,10 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, dac33->burst_bclkdiv); else - dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32); + if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE) + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32); + else + dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 16); switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: @@ -1053,7 +1067,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) * at the bottom, and also at the top of the FIFO */ dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr)); - dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR)); + dac33_write16(codec, DAC33_LTHR_MSB, + DAC33_THRREG(DAC33_MODE7_MARGIN)); break; default: break; @@ -1082,42 +1097,21 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream) /* Number of samples under i2c latency */ dac33->alarm_threshold = US_TO_SAMPLES(rate, dac33->mode1_latency); - nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - - dac33->alarm_threshold; - - if (dac33->auto_fifo_config) { - if (period_size <= dac33->alarm_threshold) - /* - * Configure nSamaple to number of periods, - * which covers the latency requironment. - */ - dac33->nsample = period_size * - ((dac33->alarm_threshold / period_size) + - (dac33->alarm_threshold % period_size ? - 1 : 0)); - else if (period_size > nsample_limit) - dac33->nsample = nsample_limit; - else - dac33->nsample = period_size; - } else { - /* nSample time shall not be shorter than i2c latency */ - dac33->nsample_min = dac33->alarm_threshold; + nsample_limit = dac33->fifo_size - dac33->alarm_threshold; + + if (period_size <= dac33->alarm_threshold) /* - * nSample should not be bigger than alsa buffer minus - * size of one period to avoid overruns + * Configure nSamaple to number of periods, + * which covers the latency requironment. */ - dac33->nsample_max = substream->runtime->buffer_size - - period_size; - - if (dac33->nsample_max > nsample_limit) - dac33->nsample_max = nsample_limit; - - /* Correct the nSample if it is outside of the ranges */ - if (dac33->nsample < dac33->nsample_min) - dac33->nsample = dac33->nsample_min; - if (dac33->nsample > dac33->nsample_max) - dac33->nsample = dac33->nsample_max; - } + dac33->nsample = period_size * + ((dac33->alarm_threshold / period_size) + + (dac33->alarm_threshold % period_size ? + 1 : 0)); + else if (period_size > nsample_limit) + dac33->nsample = nsample_limit; + else + dac33->nsample = period_size; dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, dac33->nsample); @@ -1125,19 +1119,16 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream) dac33->t_stamp2 = 0; break; case DAC33_FIFO_MODE7: - if (dac33->auto_fifo_config) { - dac33->uthr = UTHR_FROM_PERIOD_SIZE( - period_size, - rate, - dac33->burst_rate) + 9; - if (dac33->uthr > MODE7_UTHR) - dac33->uthr = MODE7_UTHR; - if (dac33->uthr < (MODE7_LTHR + 10)) - dac33->uthr = (MODE7_LTHR + 10); - } + dac33->uthr = UTHR_FROM_PERIOD_SIZE(period_size, rate, + dac33->burst_rate) + 9; + if (dac33->uthr > (dac33->fifo_size - DAC33_MODE7_MARGIN)) + dac33->uthr = dac33->fifo_size - DAC33_MODE7_MARGIN; + if (dac33->uthr < (DAC33_MODE7_MARGIN + 10)) + dac33->uthr = (DAC33_MODE7_MARGIN + 10); + dac33->mode7_us_to_lthr = SAMPLES_TO_US(substream->runtime->rate, - dac33->uthr - MODE7_LTHR + 1); + dac33->uthr - DAC33_MODE7_MARGIN + 1); dac33->t_stamp1 = 0; break; default: @@ -1189,15 +1180,16 @@ static snd_pcm_sframes_t dac33_dai_delay( unsigned int time_delta, uthr; int samples_out, samples_in, samples; snd_pcm_sframes_t delay = 0; + unsigned long flags; switch (dac33->fifo_mode) { case DAC33_FIFO_BYPASS: break; case DAC33_FIFO_MODE1: - spin_lock(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); t0 = dac33->t_stamp1; t1 = dac33->t_stamp2; - spin_unlock(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ @@ -1255,17 +1247,17 @@ static snd_pcm_sframes_t dac33_dai_delay( samples += (samples_in - samples_out); if (likely(samples > 0)) - delay = samples > DAC33_BUFFER_SIZE_SAMPLES ? - DAC33_BUFFER_SIZE_SAMPLES : samples; + delay = samples > dac33->fifo_size ? + dac33->fifo_size : samples; else delay = 0; } break; case DAC33_FIFO_MODE7: - spin_lock(&dac33->lock); + spin_lock_irqsave(&dac33->lock, flags); t0 = dac33->t_stamp1; uthr = dac33->uthr; - spin_unlock(&dac33->lock); + spin_unlock_irqrestore(&dac33->lock, flags); t_now = ktime_to_us(ktime_get()); /* We have not started to fill the FIFO yet, delay is 0 */ @@ -1308,7 +1300,7 @@ static snd_pcm_sframes_t dac33_dai_delay( samples_in = US_TO_SAMPLES( dac33->burst_rate, time_delta); - delay = MODE7_LTHR + samples_in - samples_out; + delay = DAC33_MODE7_MARGIN + samples_in - samples_out; if (unlikely(delay > uthr)) delay = uthr; @@ -1415,7 +1407,7 @@ static int dac33_soc_probe(struct snd_soc_codec *codec) codec->control_data = dac33->control_data; codec->hw_write = (hw_write_t) i2c_master_send; - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; dac33->codec = codec; /* Read the tlv320dac33 ID registers */ @@ -1459,14 +1451,10 @@ static int dac33_soc_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, dac33_snd_controls, ARRAY_SIZE(dac33_snd_controls)); /* Only add the FIFO controls, if we have valid IRQ number */ - if (dac33->irq >= 0) { + if (dac33->irq >= 0) snd_soc_add_controls(codec, dac33_mode_snd_controls, ARRAY_SIZE(dac33_mode_snd_controls)); - /* FIFO usage controls only, if autoio config is not selected */ - if (!dac33->auto_fifo_config) - snd_soc_add_controls(codec, dac33_fifo_snd_controls, - ARRAY_SIZE(dac33_fifo_snd_controls)); - } + dac33_add_widgets(codec); err_power: @@ -1515,7 +1503,7 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = { #define DAC33_RATES (SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000) -#define DAC33_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#define DAC33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_ops dac33_dai_ops = { .startup = dac33_startup, @@ -1563,17 +1551,11 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client, dac33->power_gpio = pdata->power_gpio; dac33->burst_bclkdiv = pdata->burst_bclkdiv; - /* Pre calculate the burst rate */ - dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32; dac33->keep_bclk = pdata->keep_bclk; - dac33->auto_fifo_config = pdata->auto_fifo_config; dac33->mode1_latency = pdata->mode1_latency; if (!dac33->mode1_latency) dac33->mode1_latency = 10000; /* 10ms */ dac33->irq = client->irq; - dac33->nsample = NSAMPLE_MAX; - dac33->nsample_max = NSAMPLE_MAX; - dac33->uthr = MODE7_UTHR; /* Disable FIFO use by default */ dac33->fifo_mode = DAC33_FIFO_BYPASS; @@ -1641,6 +1623,7 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = { }, { }, }; +MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id); static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index d2c243095673..1f1ac8110bef 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -29,7 +29,6 @@ #include <linux/slab.h> #include <sound/tpa6130a2-plat.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include "tpa6130a2.h" @@ -42,7 +41,7 @@ struct tpa6130a2_data { unsigned char regs[TPA6130A2_CACHEREGNUM]; struct regulator *supply; int power_gpio; - unsigned char power_state; + u8 power_state:1; enum tpa_model id; }; @@ -117,7 +116,7 @@ static int tpa6130a2_initialize(void) return ret; } -static int tpa6130a2_power(int power) +static int tpa6130a2_power(u8 power) { struct tpa6130a2_data *data; u8 val; @@ -127,17 +126,19 @@ static int tpa6130a2_power(int power) data = i2c_get_clientdata(tpa6130a2_client); mutex_lock(&data->mutex); - if (power && !data->power_state) { - /* Power on */ - if (data->power_gpio >= 0) - gpio_set_value(data->power_gpio, 1); + if (power == data->power_state) + goto exit; + if (power) { ret = regulator_enable(data->supply); if (ret != 0) { dev_err(&tpa6130a2_client->dev, "Failed to enable supply: %d\n", ret); goto exit; } + /* Power on */ + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 1); data->power_state = 1; ret = tpa6130a2_initialize(); @@ -150,12 +151,7 @@ static int tpa6130a2_power(int power) data->power_state = 0; goto exit; } - - /* Clear SWS */ - val = tpa6130a2_read(TPA6130A2_REG_CONTROL); - val &= ~TPA6130A2_SWS; - tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); - } else if (!power && data->power_state) { + } else { /* set SWS */ val = tpa6130a2_read(TPA6130A2_REG_CONTROL); val |= TPA6130A2_SWS; @@ -300,6 +296,7 @@ static void tpa6130a2_channel_enable(u8 channel, int enable) /* Enable amplifier */ val = tpa6130a2_read(TPA6130A2_REG_CONTROL); val |= channel; + val &= ~TPA6130A2_SWS; tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); /* Unmute channel */ @@ -320,72 +317,24 @@ static void tpa6130a2_channel_enable(u8 channel, int enable) } } -static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - switch (event) { - case SND_SOC_DAPM_POST_PMU: - tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1); - break; - case SND_SOC_DAPM_POST_PMD: - tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0); - break; - } - return 0; -} - -static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - switch (event) { - case SND_SOC_DAPM_POST_PMU: - tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1); - break; - case SND_SOC_DAPM_POST_PMD: - tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0); - break; - } - return 0; -} - -static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable) { int ret = 0; - - switch (event) { - case SND_SOC_DAPM_POST_PMU: + if (enable) { ret = tpa6130a2_power(1); - break; - case SND_SOC_DAPM_POST_PMD: + if (ret < 0) + return ret; + tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L, + 1); + } else { + tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L, + 0); ret = tpa6130a2_power(0); - break; } + return ret; } - -static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { - SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM, - 0, 0, NULL, 0, tpa6130a2_left_event, - SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM, - 0, 0, NULL, 0, tpa6130a2_right_event, - SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM, - 0, 0, tpa6130a2_supply_event, - SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), - /* Outputs */ - SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Left"), - SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Right"), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"}, - {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"}, - - {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"}, - {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"}, -}; +EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable); int tpa6130a2_add_controls(struct snd_soc_codec *codec) { @@ -396,18 +345,12 @@ int tpa6130a2_add_controls(struct snd_soc_codec *codec) data = i2c_get_clientdata(tpa6130a2_client); - snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, - ARRAY_SIZE(tpa6130a2_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - if (data->id == TPA6140A2) return snd_soc_add_controls(codec, tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls)); else return snd_soc_add_controls(codec, tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls)); - } EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h index 57e867fd86d1..5df49c8756b2 100644 --- a/sound/soc/codecs/tpa6130a2.h +++ b/sound/soc/codecs/tpa6130a2.h @@ -57,5 +57,6 @@ #define TPA6130A2_VERSION_MASK (0x0f) extern int tpa6130a2_add_controls(struct snd_soc_codec *codec); +extern int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable); #endif /* __TPA6130A2_H__ */ diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index cbebec6ba1ba..575238d68e5e 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -26,13 +26,13 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/i2c/twl.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -233,6 +233,16 @@ static int twl4030_write(struct snd_soc_codec *codec, return 0; } +static inline void twl4030_wait_ms(int time) +{ + if (time < 60) { + time *= 1000; + usleep_range(time, time + 500); + } else { + msleep(time); + } +} + static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) { struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); @@ -271,7 +281,7 @@ static inline void twl4030_check_defaults(struct snd_soc_codec *codec) i, val, twl4030_reg[i]); } } - dev_dbg(codec->dev, "Found %d non maching registers. %s\n", + dev_dbg(codec->dev, "Found %d non-matching registers. %s\n", difference, difference ? "Not OK" : "OK"); } @@ -338,10 +348,14 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) twl4030_write(codec, TWL4030_REG_ANAMICL, reg | TWL4030_CNCL_OFFSET_START); - /* wait for offset cancellation to complete */ + /* + * Wait for offset cancellation to complete. + * Since this takes a while, do not slam the i2c. + * Start polling the status after ~20ms. + */ + msleep(20); do { - /* this takes a little while, so don't slam i2c */ - udelay(2000); + usleep_range(1000, 2000); twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, TWL4030_REG_ANAMICL); } while ((i++ < 100) && @@ -719,15 +733,19 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; + struct twl4030_codec_audio_data *pdata = + mfd_get_data(to_platform_device(codec->dev)); unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864}; + unsigned int delay; hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); + delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / + twl4030->sysclk) + 1; /* Enable external mute control, this dramatically reduces * the pop-noise */ @@ -751,16 +769,14 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) hs_pop |= TWL4030_RAMP_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); /* Wait ramp delay time + 1, so the VMID can settle */ - mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / - twl4030->sysclk) + 1); + twl4030_wait_ms(delay); } else { /* Headset ramp-down _not_ according to * the TRM, but in a way that it is working */ hs_pop &= ~TWL4030_RAMP_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); /* Wait ramp delay time + 1, so the VMID can settle */ - mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / - twl4030->sysclk) + 1); + twl4030_wait_ms(delay); /* Bypass the reg_cache to mute the headset */ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f), @@ -835,7 +851,7 @@ static int digimic_event(struct snd_soc_dapm_widget *w, struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); if (twl4030->digimic_delay) - mdelay(twl4030->digimic_delay); + twl4030_wait_ms(twl4030->digimic_delay); return 0; } @@ -1621,10 +1637,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int twl4030_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets, - ARRAY_SIZE(twl4030_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_new_controls(dapm, twl4030_dapm_widgets, + ARRAY_SIZE(twl4030_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); return 0; } @@ -1638,14 +1655,14 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) twl4030_codec_enable(codec, 1); break; case SND_SOC_BIAS_OFF: twl4030_codec_enable(codec, 0); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1709,6 +1726,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); + snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); if (twl4030->master_substream) { twl4030->slave_substream = substream; /* The DAI has one configuration for playback and capture, so @@ -1833,7 +1851,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: format |= TWL4030_DATA_WIDTH_16S_16W; break; - case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: format |= TWL4030_DATA_WIDTH_32S_24W; break; default: @@ -2000,7 +2018,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, u8 mode; /* If the system master clock is not 26MHz, the voice PCM interface is - * not avilable. + * not available. */ if (twl4030->sysclk != 26000) { dev_err(codec->dev, "The board is configured for %u Hz, while" @@ -2010,7 +2028,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, } /* If the codec mode is not option2, the voice PCM interface is not - * avilable. + * available. */ mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & TWL4030_OPT_MODE; @@ -2166,7 +2184,7 @@ static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate) } #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) -#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) +#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_ops twl4030_dai_hifi_ops = { .startup = twl4030_startup, @@ -2245,7 +2263,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, twl4030); /* Set the defaults, and power up the codec */ twl4030->sysclk = twl4030_codec_get_mclk() / 1000; - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; twl4030_init_chip(codec); @@ -2257,9 +2275,12 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) static int twl4030_soc_remove(struct snd_soc_codec *codec) { + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); + /* Reset registers to their chip default before leaving */ twl4030_reset_registers(codec); twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); + kfree(twl4030); return 0; } @@ -2278,7 +2299,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_audio_data *pdata = mfd_get_data(pdev); if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); @@ -2291,10 +2312,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) static int __devexit twl4030_codec_remove(struct platform_device *pdev) { - struct twl4030_priv *twl4030 = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_codec(&pdev->dev); - kfree(twl4030); return 0; } diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 10f6e5214511..255901c4460d 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -34,14 +34,46 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include "twl6040.h" -#define TWL6040_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) -#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) +#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000 +#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) + +#define TWL6040_OUTHS_0dB 0x00 +#define TWL6040_OUTHS_M30dB 0x0F +#define TWL6040_OUTHF_0dB 0x03 +#define TWL6040_OUTHF_M52dB 0x1D + +#define TWL6040_RAMP_NONE 0 +#define TWL6040_RAMP_UP 1 +#define TWL6040_RAMP_DOWN 2 + +#define TWL6040_HSL_VOL_MASK 0x0F +#define TWL6040_HSL_VOL_SHIFT 0 +#define TWL6040_HSR_VOL_MASK 0xF0 +#define TWL6040_HSR_VOL_SHIFT 4 +#define TWL6040_HF_VOL_MASK 0x1F +#define TWL6040_HF_VOL_SHIFT 0 + +struct twl6040_output { + u16 active; + u16 left_vol; + u16 right_vol; + u16 left_step; + u16 right_step; + unsigned int step_delay; + u16 ramp; + u16 mute; + struct completion ramp_done; +}; + +struct twl6040_jack_data { + struct snd_soc_jack *jack; + int report; +}; /* codec private data */ struct twl6040_data { @@ -53,6 +85,17 @@ struct twl6040_data { unsigned int sysclk; struct snd_pcm_hw_constraint_list *sysclk_constraints; struct completion ready; + struct twl6040_jack_data hs_jack; + struct snd_soc_codec *codec; + struct workqueue_struct *workqueue; + struct delayed_work delayed_work; + struct mutex mutex; + struct twl6040_output headset; + struct twl6040_output handsfree; + struct workqueue_struct *hf_workqueue; + struct workqueue_struct *hs_workqueue; + struct delayed_work hs_delayed_work; + struct delayed_work hf_delayed_work; }; /* @@ -201,7 +244,7 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, if (reg >= TWL6040_CACHEREGNUM) return -EIO; - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg); + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg); twl6040_write_reg_cache(codec, reg, value); return value; @@ -217,7 +260,7 @@ static int twl6040_write(struct snd_soc_codec *codec, return -EIO; twl6040_write_reg_cache(codec, reg, value); - return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); + return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg); } static void twl6040_init_vio_regs(struct snd_soc_codec *codec) @@ -254,6 +297,305 @@ static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) } } +/* + * Ramp HS PGA volume to minimise pops at stream startup and shutdown. + */ +static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, + unsigned int left_step, unsigned int right_step) +{ + + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *headset = &priv->headset; + int left_complete = 0, right_complete = 0; + u8 reg, val; + + /* left channel */ + left_step = (left_step > 0xF) ? 0xF : left_step; + reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); + val = (~reg & TWL6040_HSL_VOL_MASK); + + if (headset->ramp == TWL6040_RAMP_UP) { + /* ramp step up */ + if (val < headset->left_vol) { + val += left_step; + reg &= ~TWL6040_HSL_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HSGAIN, + (reg | (~val & TWL6040_HSL_VOL_MASK))); + } else { + left_complete = 1; + } + } else if (headset->ramp == TWL6040_RAMP_DOWN) { + /* ramp step down */ + if (val > 0x0) { + val -= left_step; + reg &= ~TWL6040_HSL_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HSGAIN, reg | + (~val & TWL6040_HSL_VOL_MASK)); + } else { + left_complete = 1; + } + } + + /* right channel */ + right_step = (right_step > 0xF) ? 0xF : right_step; + reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN); + val = (~reg & TWL6040_HSR_VOL_MASK) >> TWL6040_HSR_VOL_SHIFT; + + if (headset->ramp == TWL6040_RAMP_UP) { + /* ramp step up */ + if (val < headset->right_vol) { + val += right_step; + reg &= ~TWL6040_HSR_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HSGAIN, + (reg | (~val << TWL6040_HSR_VOL_SHIFT))); + } else { + right_complete = 1; + } + } else if (headset->ramp == TWL6040_RAMP_DOWN) { + /* ramp step down */ + if (val > 0x0) { + val -= right_step; + reg &= ~TWL6040_HSR_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HSGAIN, + reg | (~val << TWL6040_HSR_VOL_SHIFT)); + } else { + right_complete = 1; + } + } + + return left_complete & right_complete; +} + +/* + * Ramp HF PGA volume to minimise pops at stream startup and shutdown. + */ +static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, + unsigned int left_step, unsigned int right_step) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *handsfree = &priv->handsfree; + int left_complete = 0, right_complete = 0; + u16 reg, val; + + /* left channel */ + left_step = (left_step > 0x1D) ? 0x1D : left_step; + reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN); + reg = 0x1D - reg; + val = (reg & TWL6040_HF_VOL_MASK); + if (handsfree->ramp == TWL6040_RAMP_UP) { + /* ramp step up */ + if (val < handsfree->left_vol) { + val += left_step; + reg &= ~TWL6040_HF_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HFLGAIN, + reg | (0x1D - val)); + } else { + left_complete = 1; + } + } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { + /* ramp step down */ + if (val > 0) { + val -= left_step; + reg &= ~TWL6040_HF_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HFLGAIN, + reg | (0x1D - val)); + } else { + left_complete = 1; + } + } + + /* right channel */ + right_step = (right_step > 0x1D) ? 0x1D : right_step; + reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN); + reg = 0x1D - reg; + val = (reg & TWL6040_HF_VOL_MASK); + if (handsfree->ramp == TWL6040_RAMP_UP) { + /* ramp step up */ + if (val < handsfree->right_vol) { + val += right_step; + reg &= ~TWL6040_HF_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HFRGAIN, + reg | (0x1D - val)); + } else { + right_complete = 1; + } + } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { + /* ramp step down */ + if (val > 0) { + val -= right_step; + reg &= ~TWL6040_HF_VOL_MASK; + twl6040_write(codec, TWL6040_REG_HFRGAIN, + reg | (0x1D - val)); + } + } + + return left_complete & right_complete; +} + +/* + * This work ramps both output PGAs at stream start/stop time to + * minimise pop associated with DAPM power switching. + */ +static void twl6040_pga_hs_work(struct work_struct *work) +{ + struct twl6040_data *priv = + container_of(work, struct twl6040_data, hs_delayed_work.work); + struct snd_soc_codec *codec = priv->codec; + struct twl6040_output *headset = &priv->headset; + unsigned int delay = headset->step_delay; + int i, headset_complete; + + /* do we need to ramp at all ? */ + if (headset->ramp == TWL6040_RAMP_NONE) + return; + + /* HS PGA volumes have 4 bits of resolution to ramp */ + for (i = 0; i <= 16; i++) { + headset_complete = 1; + if (headset->ramp != TWL6040_RAMP_NONE) + headset_complete = twl6040_hs_ramp_step(codec, + headset->left_step, + headset->right_step); + + /* ramp finished ? */ + if (headset_complete) + break; + + /* + * TODO: tune: delay is longer over 0dB + * as increases are larger. + */ + if (i >= 8) + schedule_timeout_interruptible(msecs_to_jiffies(delay + + (delay >> 1))); + else + schedule_timeout_interruptible(msecs_to_jiffies(delay)); + } + + if (headset->ramp == TWL6040_RAMP_DOWN) { + headset->active = 0; + complete(&headset->ramp_done); + } else { + headset->active = 1; + } + headset->ramp = TWL6040_RAMP_NONE; +} + +static void twl6040_pga_hf_work(struct work_struct *work) +{ + struct twl6040_data *priv = + container_of(work, struct twl6040_data, hf_delayed_work.work); + struct snd_soc_codec *codec = priv->codec; + struct twl6040_output *handsfree = &priv->handsfree; + unsigned int delay = handsfree->step_delay; + int i, handsfree_complete; + + /* do we need to ramp at all ? */ + if (handsfree->ramp == TWL6040_RAMP_NONE) + return; + + /* HF PGA volumes have 5 bits of resolution to ramp */ + for (i = 0; i <= 32; i++) { + handsfree_complete = 1; + if (handsfree->ramp != TWL6040_RAMP_NONE) + handsfree_complete = twl6040_hf_ramp_step(codec, + handsfree->left_step, + handsfree->right_step); + + /* ramp finished ? */ + if (handsfree_complete) + break; + + /* + * TODO: tune: delay is longer over 0dB + * as increases are larger. + */ + if (i >= 16) + schedule_timeout_interruptible(msecs_to_jiffies(delay + + (delay >> 1))); + else + schedule_timeout_interruptible(msecs_to_jiffies(delay)); + } + + + if (handsfree->ramp == TWL6040_RAMP_DOWN) { + handsfree->active = 0; + complete(&handsfree->ramp_done); + } else + handsfree->active = 1; + handsfree->ramp = TWL6040_RAMP_NONE; +} + +static int pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *out; + struct delayed_work *work; + struct workqueue_struct *queue; + + switch (w->shift) { + case 2: + case 3: + out = &priv->headset; + work = &priv->hs_delayed_work; + queue = priv->hs_workqueue; + out->step_delay = 5; /* 5 ms between volume ramp steps */ + break; + case 4: + out = &priv->handsfree; + work = &priv->hf_delayed_work; + queue = priv->hf_workqueue; + out->step_delay = 5; /* 5 ms between volume ramp steps */ + if (SND_SOC_DAPM_EVENT_ON(event)) + priv->non_lp++; + else + priv->non_lp--; + break; + default: + return -1; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (out->active) + break; + + /* don't use volume ramp for power-up */ + out->left_step = out->left_vol; + out->right_step = out->right_vol; + + if (!delayed_work_pending(work)) { + out->ramp = TWL6040_RAMP_UP; + queue_delayed_work(queue, work, + msecs_to_jiffies(1)); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + if (!out->active) + break; + + if (!delayed_work_pending(work)) { + /* use volume ramp for power-down */ + out->left_step = 1; + out->right_step = 1; + out->ramp = TWL6040_RAMP_DOWN; + INIT_COMPLETION(out->ramp_done); + + queue_delayed_work(queue, work, + msecs_to_jiffies(1)); + + wait_for_completion_timeout(&out->ramp_done, + msecs_to_jiffies(2000)); + } + break; + } + + return 0; +} + /* twl6040 codec manual power-up sequence */ static void twl6040_power_up(struct snd_soc_codec *codec) { @@ -382,6 +724,47 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, return 0; } +static void twl6040_hs_jack_report(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int status; + + mutex_lock(&priv->mutex); + + /* Sync status */ + status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS); + if (status & TWL6040_PLUGCOMP) + snd_soc_jack_report(jack, report, report); + else + snd_soc_jack_report(jack, 0, report); + + mutex_unlock(&priv->mutex); +} + +void twl6040_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_jack_data *hs_jack = &priv->hs_jack; + + hs_jack->jack = jack; + hs_jack->report = report; + + twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report); +} +EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect); + +static void twl6040_accessory_work(struct work_struct *work) +{ + struct twl6040_data *priv = container_of(work, + struct twl6040_data, delayed_work.work); + struct snd_soc_codec *codec = priv->codec; + struct twl6040_jack_data *hs_jack = &priv->hs_jack; + + twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report); +} + /* audio interrupt handler */ static irqreturn_t twl6040_naudint_handler(int irq, void *data) { @@ -389,33 +772,180 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data) struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 intid; - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); - switch (intid) { - case TWL6040_THINT: + if (intid & TWL6040_THINT) dev_alert(codec->dev, "die temp over-limit detection\n"); + + if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) + queue_delayed_work(priv->workqueue, &priv->delayed_work, + msecs_to_jiffies(200)); + + if (intid & TWL6040_HOOKINT) + dev_info(codec->dev, "hook detection\n"); + + if (intid & TWL6040_HFINT) + dev_alert(codec->dev, "hf drivers over current detection\n"); + + if (intid & TWL6040_VIBINT) + dev_alert(codec->dev, "vib drivers over current detection\n"); + + if (intid & TWL6040_READYINT) + complete(&priv->ready); + + return IRQ_HANDLED; +} + +static int twl6040_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *out = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int ret; + unsigned int reg = mc->reg; + + /* For HS and HF we shadow the values and only actually write + * them out when active in order to ensure the amplifier comes on + * as quietly as possible. */ + switch (reg) { + case TWL6040_REG_HSGAIN: + out = &twl6040_priv->headset; break; - case TWL6040_PLUGINT: - case TWL6040_UNPLUGINT: - case TWL6040_HOOKINT: + default: break; - case TWL6040_HFINT: - dev_alert(codec->dev, "hf drivers over current detection\n"); + } + + if (out) { + out->left_vol = ucontrol->value.integer.value[0]; + out->right_vol = ucontrol->value.integer.value[1]; + if (!out->active) + return 1; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + return 1; +} + +static int twl6040_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *out = &twl6040_priv->headset; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + + switch (reg) { + case TWL6040_REG_HSGAIN: + out = &twl6040_priv->headset; + ucontrol->value.integer.value[0] = out->left_vol; + ucontrol->value.integer.value[1] = out->right_vol; + return 0; + + default: break; - case TWL6040_VIBINT: - dev_alert(codec->dev, "vib drivers over current detection\n"); + } + + return snd_soc_get_volsw(kcontrol, ucontrol); +} + +static int twl6040_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *out = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int ret; + unsigned int reg = mc->reg; + + /* For HS and HF we shadow the values and only actually write + * them out when active in order to ensure the amplifier comes on + * as quietly as possible. */ + switch (reg) { + case TWL6040_REG_HFLGAIN: + case TWL6040_REG_HFRGAIN: + out = &twl6040_priv->handsfree; break; - case TWL6040_READYINT: - complete(&priv->ready); + default: break; + } + + if (out) { + out->left_vol = ucontrol->value.integer.value[0]; + out->right_vol = ucontrol->value.integer.value[1]; + if (!out->active) + return 1; + } + + ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + if (ret < 0) + return ret; + + return 1; +} + +static int twl6040_get_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec); + struct twl6040_output *out = &twl6040_priv->handsfree; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + + /* If these are cached registers use the cache */ + switch (reg) { + case TWL6040_REG_HFLGAIN: + case TWL6040_REG_HFRGAIN: + out = &twl6040_priv->handsfree; + ucontrol->value.integer.value[0] = out->left_vol; + ucontrol->value.integer.value[1] = out->right_vol; + return 0; + default: - dev_err(codec->dev, "unknown audio interrupt %d\n", intid); break; } - return IRQ_HANDLED; + return snd_soc_get_volsw_2r(kcontrol, ucontrol); } +/* double control with volume update */ +#define SOC_TWL6040_DOUBLE_TLV(xname, xreg, shift_left, shift_right, xmax,\ + xinvert, tlv_array)\ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = twl6040_get_volsw, \ + .put = twl6040_put_volsw, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = shift_left, .rshift = shift_right,\ + .max = xmax, .platform_max = xmax, .invert = xinvert} } + +/* double control with volume update */ +#define SOC_TWL6040_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax,\ + xinvert, tlv_array)\ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_2r, \ + .get = twl6040_get_volsw_2r, .put = twl6040_put_volsw_2r_vu, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ + .rshift = xshift, .max = xmax, .invert = xinvert}, } + /* * MICATT volume control: * from -6 to 0 dB in 6 dB steps @@ -424,9 +954,15 @@ static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0); /* * MICGAIN volume control: - * from 6 to 30 dB in 6 dB steps + * from -6 to 30 dB in 6 dB steps + */ +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0); + +/* + * AFMGAIN volume control: + * from 18 to 24 dB in 6 dB steps */ -static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0); +static DECLARE_TLV_DB_SCALE(afm_amp_tlv, 1800, 600, 0); /* * HSGAIN volume control: @@ -455,8 +991,30 @@ static const char *twl6040_amicr_texts[] = {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"}; static const struct soc_enum twl6040_enum[] = { - SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 3, twl6040_amicl_texts), - SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 3, twl6040_amicr_texts), + SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 4, twl6040_amicl_texts), + SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 4, twl6040_amicr_texts), +}; + +static const char *twl6040_hs_texts[] = { + "Off", "HS DAC", "Line-In amp" +}; + +static const struct soc_enum twl6040_hs_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_HSLCTL, 5, ARRAY_SIZE(twl6040_hs_texts), + twl6040_hs_texts), + SOC_ENUM_SINGLE(TWL6040_REG_HSRCTL, 5, ARRAY_SIZE(twl6040_hs_texts), + twl6040_hs_texts), +}; + +static const char *twl6040_hf_texts[] = { + "Off", "HF DAC", "Line-In amp" +}; + +static const struct soc_enum twl6040_hf_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_HFLCTL, 2, ARRAY_SIZE(twl6040_hf_texts), + twl6040_hf_texts), + SOC_ENUM_SINGLE(TWL6040_REG_HFRCTL, 2, ARRAY_SIZE(twl6040_hf_texts), + twl6040_hf_texts), }; static const struct snd_kcontrol_new amicl_control = @@ -466,18 +1024,18 @@ static const struct snd_kcontrol_new amicr_control = SOC_DAPM_ENUM("Route", twl6040_enum[1]); /* Headset DAC playback switches */ -static const struct snd_kcontrol_new hsdacl_switch_controls = - SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 5, 1, 0); +static const struct snd_kcontrol_new hsl_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hs_enum[0]); -static const struct snd_kcontrol_new hsdacr_switch_controls = - SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 5, 1, 0); +static const struct snd_kcontrol_new hsr_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hs_enum[1]); /* Handsfree DAC playback switches */ -static const struct snd_kcontrol_new hfdacl_switch_controls = - SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 2, 1, 0); +static const struct snd_kcontrol_new hfl_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hf_enum[0]); -static const struct snd_kcontrol_new hfdacr_switch_controls = - SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0); +static const struct snd_kcontrol_new hfr_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]); static const struct snd_kcontrol_new ep_driver_switch_controls = SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); @@ -489,10 +1047,14 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { SOC_DOUBLE_TLV("Capture Volume", TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv), + /* AFM gains */ + SOC_DOUBLE_TLV("Aux FM Volume", + TWL6040_REG_LINEGAIN, 0, 4, 0xF, 0, afm_amp_tlv), + /* Playback gains */ - SOC_DOUBLE_TLV("Headset Playback Volume", + SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume", TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), - SOC_DOUBLE_R_TLV("Handsfree Playback Volume", + SOC_TWL6040_DOUBLE_R_TLV("Handsfree Playback Volume", TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), @@ -525,6 +1087,12 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_PGA("MicAmpR", TWL6040_REG_MICRCTL, 0, 0, NULL, 0), + /* Auxiliary FM PGAs */ + SND_SOC_DAPM_PGA("AFMAmpL", + TWL6040_REG_MICLCTL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("AFMAmpR", + TWL6040_REG_MICRCTL, 1, 0, NULL, 0), + /* ADCs */ SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture", TWL6040_REG_MICLCTL, 2, 0), @@ -559,29 +1127,33 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { twl6040_power_mode_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - /* Analog playback switches */ - SND_SOC_DAPM_SWITCH("HSDAC Left Playback", - SND_SOC_NOPM, 0, 0, &hsdacl_switch_controls), - SND_SOC_DAPM_SWITCH("HSDAC Right Playback", - SND_SOC_NOPM, 0, 0, &hsdacr_switch_controls), - SND_SOC_DAPM_SWITCH("HFDAC Left Playback", - SND_SOC_NOPM, 0, 0, &hfdacl_switch_controls), - SND_SOC_DAPM_SWITCH("HFDAC Right Playback", - SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls), + SND_SOC_DAPM_MUX("HF Left Playback", + SND_SOC_NOPM, 0, 0, &hfl_mux_controls), + SND_SOC_DAPM_MUX("HF Right Playback", + SND_SOC_NOPM, 0, 0, &hfr_mux_controls), + /* Analog playback Muxes */ + SND_SOC_DAPM_MUX("HS Left Playback", + SND_SOC_NOPM, 0, 0, &hsl_mux_controls), + SND_SOC_DAPM_MUX("HS Right Playback", + SND_SOC_NOPM, 0, 0, &hsr_mux_controls), /* Analog playback drivers */ - SND_SOC_DAPM_PGA_E("Handsfree Left Driver", + SND_SOC_DAPM_OUT_DRV_E("Handsfree Left Driver", TWL6040_REG_HFLCTL, 4, 0, NULL, 0, - twl6040_power_mode_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("Handsfree Right Driver", + pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("Handsfree Right Driver", TWL6040_REG_HFRCTL, 4, 0, NULL, 0, - twl6040_power_mode_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA("Headset Left Driver", - TWL6040_REG_HSLCTL, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("Headset Right Driver", - TWL6040_REG_HSRCTL, 2, 0, NULL, 0), + pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("Headset Left Driver", + TWL6040_REG_HSLCTL, 2, 0, NULL, 0, + pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("Headset Right Driver", + TWL6040_REG_HSRCTL, 2, 0, NULL, 0, + pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SWITCH_E("Earphone Driver", SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, twl6040_power_mode_event, @@ -611,12 +1183,18 @@ static const struct snd_soc_dapm_route intercon[] = { {"ADC Left", NULL, "MicAmpL"}, {"ADC Right", NULL, "MicAmpR"}, - /* Headset playback path */ - {"HSDAC Left Playback", "Switch", "HSDAC Left"}, - {"HSDAC Right Playback", "Switch", "HSDAC Right"}, + /* AFM path */ + {"AFMAmpL", "NULL", "AFML"}, + {"AFMAmpR", "NULL", "AFMR"}, - {"Headset Left Driver", NULL, "HSDAC Left Playback"}, - {"Headset Right Driver", NULL, "HSDAC Right Playback"}, + {"HS Left Playback", "HS DAC", "HSDAC Left"}, + {"HS Left Playback", "Line-In amp", "AFMAmpL"}, + + {"HS Right Playback", "HS DAC", "HSDAC Right"}, + {"HS Right Playback", "Line-In amp", "AFMAmpR"}, + + {"Headset Left Driver", "NULL", "HS Left Playback"}, + {"Headset Right Driver", "NULL", "HS Right Playback"}, {"HSOL", NULL, "Headset Left Driver"}, {"HSOR", NULL, "Headset Right Driver"}, @@ -625,12 +1203,14 @@ static const struct snd_soc_dapm_route intercon[] = { {"Earphone Driver", "Switch", "HSDAC Left"}, {"EP", NULL, "Earphone Driver"}, - /* Handsfree playback path */ - {"HFDAC Left Playback", "Switch", "HFDAC Left"}, - {"HFDAC Right Playback", "Switch", "HFDAC Right"}, + {"HF Left Playback", "HF DAC", "HFDAC Left"}, + {"HF Left Playback", "Line-In amp", "AFMAmpL"}, - {"HFDAC Left PGA", NULL, "HFDAC Left Playback"}, - {"HFDAC Right PGA", NULL, "HFDAC Right Playback"}, + {"HF Right Playback", "HF DAC", "HFDAC Right"}, + {"HF Right Playback", "Line-In amp", "AFMAmpR"}, + + {"HFDAC Left PGA", NULL, "HF Left Playback"}, + {"HFDAC Right PGA", NULL, "HF Right Playback"}, {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"}, {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"}, @@ -641,12 +1221,12 @@ static const struct snd_soc_dapm_route intercon[] = { static int twl6040_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, twl6040_dapm_widgets, - ARRAY_SIZE(twl6040_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); - - snd_soc_dapm_new_widgets(codec); + snd_soc_dapm_new_controls(dapm, twl6040_dapm_widgets, + ARRAY_SIZE(twl6040_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_new_widgets(dapm); return 0; } @@ -659,10 +1239,10 @@ static int twl6040_power_up_completion(struct snd_soc_codec *codec, u8 intid; time_left = wait_for_completion_timeout(&priv->ready, - msecs_to_jiffies(48)); + msecs_to_jiffies(144)); if (!time_left) { - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { dev_err(codec->dev, "timeout waiting for READYINT\n"); @@ -713,6 +1293,15 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, /* initialize vdd/vss registers with reg_cache */ twl6040_init_vdd_regs(codec); + + /* Set external boost GPO */ + twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); + + /* Set initial minimal gain values */ + twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); + twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E); + twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); + twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); break; case SND_SOC_BIAS_OFF: if (!priv->codec_powered) @@ -739,7 +1328,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -772,23 +1361,6 @@ static int twl6040_startup(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = rtd->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - if (!priv->sysclk) { - dev_err(codec->dev, - "no mclk configured, call set_sysclk() on init\n"); - return -EINVAL; - } - - /* - * capture is not supported at 17.64 MHz, - * it's reserved for headset low-power playback scenario - */ - if ((priv->sysclk == 17640000) && substream->stream) { - dev_err(codec->dev, - "capture mode is not supported at %dHz\n", - priv->sysclk); - return -EINVAL; - } - snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, priv->sysclk_constraints); @@ -814,10 +1386,17 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, rate = params_rate(params); switch (rate) { + case 11250: + case 22500: + case 44100: case 88200: lppllctl |= TWL6040_LPLLFIN; priv->sysclk = 17640000; break; + case 8000: + case 16000: + case 32000: + case 48000: case 96000: lppllctl &= ~TWL6040_LPLLFIN; priv->sysclk = 19200000; @@ -832,31 +1411,37 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, return 0; } -static int twl6040_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int twl6040_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; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - /* - * low-power playback mode is restricted - * for headset path only - */ - if ((priv->sysclk == 17640000) && priv->non_lp) { + if (!priv->sysclk) { + dev_err(codec->dev, + "no mclk configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + /* + * capture is not supported at 17.64 MHz, + * it's reserved for headset low-power playback scenario + */ + if ((priv->sysclk == 17640000) && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(codec->dev, + "capture mode is not supported at %dHz\n", + priv->sysclk); + return -EINVAL; + } + + if ((priv->sysclk == 17640000) && priv->non_lp) { dev_err(codec->dev, "some enabled paths aren't supported at %dHz\n", priv->sysclk); return -EPERM; - } - break; - default: - break; } - return 0; } @@ -970,7 +1555,7 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, static struct snd_soc_dai_ops twl6040_dai_ops = { .startup = twl6040_startup, .hw_params = twl6040_hw_params, - .trigger = twl6040_trigger, + .prepare = twl6040_prepare, .set_sysclk = twl6040_set_dai_sysclk, }; @@ -1004,6 +1589,7 @@ static int twl6040_suspend(struct snd_soc_codec *codec, pm_message_t state) static int twl6040_resume(struct snd_soc_codec *codec) { twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + twl6040_set_bias_level(codec, codec->dapm.suspend_bias_level); return 0; } @@ -1018,24 +1604,43 @@ static int twl6040_probe(struct snd_soc_codec *codec) struct twl6040_data *priv; int audpwron, naudint; int ret = 0; + u8 icrev, intmr = TWL6040_ALLINT_MSK; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); - if (twl_codec) { + priv->codec = codec; + + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV); + + if (twl_codec && (icrev > 0)) audpwron = twl_codec->audpwron_gpio; - naudint = twl_codec->naudint_irq; - } else { + else audpwron = -EINVAL; + + if (twl_codec) + naudint = twl_codec->naudint_irq; + else naudint = 0; - } priv->audpwron = audpwron; priv->naudint = naudint; + priv->workqueue = create_singlethread_workqueue("twl6040-codec"); + + if (!priv->workqueue) { + ret = -ENOMEM; + goto work_err; + } + + INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work); + + mutex_init(&priv->mutex); init_completion(&priv->ready); + init_completion(&priv->headset.ramp_done); + init_completion(&priv->handsfree.ramp_done); if (gpio_is_valid(audpwron)) { ret = gpio_request(audpwron, "audpwron"); @@ -1047,7 +1652,14 @@ static int twl6040_probe(struct snd_soc_codec *codec) goto gpio2_err; priv->codec_powered = 0; + + /* enable only codec ready interrupt */ + intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK); + + /* reset interrupt status to allow correct power up sequence */ + twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); } + twl6040_write(codec, TWL6040_REG_INTMR, intmr); if (naudint) { /* audio interrupt */ @@ -1057,25 +1669,29 @@ static int twl6040_probe(struct snd_soc_codec *codec) "twl6040_codec", codec); if (ret) goto gpio2_err; - } else { - if (gpio_is_valid(audpwron)) { - /* enable only codec ready interrupt */ - twl6040_write_reg_cache(codec, TWL6040_REG_INTMR, - ~TWL6040_READYMSK & TWL6040_ALLINT_MSK); - } else { - /* no interrupts at all */ - twl6040_write_reg_cache(codec, TWL6040_REG_INTMR, - TWL6040_ALLINT_MSK); - } } /* init vio registers */ twl6040_init_vio_regs(codec); + priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); + if (priv->hf_workqueue == NULL) { + ret = -ENOMEM; + goto irq_err; + } + priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); + if (priv->hs_workqueue == NULL) { + ret = -ENOMEM; + goto wq_err; + } + + INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); + INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); + /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) - goto irq_err; + goto bias_err; snd_soc_add_controls(codec, twl6040_snd_controls, ARRAY_SIZE(twl6040_snd_controls)); @@ -1083,6 +1699,10 @@ static int twl6040_probe(struct snd_soc_codec *codec) return 0; +bias_err: + destroy_workqueue(priv->hs_workqueue); +wq_err: + destroy_workqueue(priv->hf_workqueue); irq_err: if (naudint) free_irq(naudint, codec); @@ -1090,6 +1710,8 @@ gpio2_err: if (gpio_is_valid(audpwron)) gpio_free(audpwron); gpio1_err: + destroy_workqueue(priv->workqueue); +work_err: kfree(priv); return ret; } @@ -1108,6 +1730,9 @@ static int twl6040_remove(struct snd_soc_codec *codec) if (naudint) free_irq(naudint, codec); + destroy_workqueue(priv->workqueue); + destroy_workqueue(priv->hf_workqueue); + destroy_workqueue(priv->hs_workqueue); kfree(priv); return 0; diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index f7c77fa58a3c..23aeed0963e6 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -79,6 +79,7 @@ /* INTMR (0x04) fields */ +#define TWL6040_PLUGMSK 0x02 #define TWL6040_READYMSK 0x40 #define TWL6040_ALLINT_MSK 0x7B @@ -135,4 +136,11 @@ #define TWL6040_HPPLL_ID 1 #define TWL6040_LPPLL_ID 2 +/* STATUS (0x2E) fields */ + +#define TWL6040_PLUGCOMP 0x02 + +void twl6040_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report); + #endif /* End of __TWL6040_H__ */ diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 464f0cfa4c7a..48ffd406a71d 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -19,7 +19,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/uda134x.h> @@ -389,7 +388,7 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, pd->power(0); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -487,7 +486,8 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; - struct uda134x_platform_data *pd = dev_get_drvdata(codec->card->dev); + struct uda134x_platform_data *pd = codec->card->dev->platform_data; + int ret; printk(KERN_INFO "UDA134X SoC Audio Codec\n"); diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 0c6c725736c6..c5ca8cfea60f 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -27,7 +27,6 @@ #include <sound/control.h> #include <sound/initval.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/uda1380.h> @@ -36,7 +35,6 @@ /* codec private data */ struct uda1380_priv { struct snd_soc_codec *codec; - u16 reg_cache[UDA1380_CACHEREGNUM]; unsigned int dac_clk; struct work_struct work; void *control_data; @@ -414,10 +412,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int uda1380_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets, - ARRAY_SIZE(uda1380_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets, + ARRAY_SIZE(uda1380_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -603,7 +602,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, int reg; struct uda1380_platform_data *pdata = codec->dev->platform_data; - if (codec->bias_level == level) + if (codec->dapm.bias_level == level) return 0; switch (level) { @@ -613,7 +612,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm); break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { if (gpio_is_valid(pdata->gpio_power)) { gpio_set_value(pdata->gpio_power, 1); mdelay(1); @@ -636,7 +635,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++) set_bit(reg - 0x10, &uda1380_cache_dirty); } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 0c47c788ccdf..c8a874d0d4ca 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -3,7 +3,7 @@ * * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com> * - * Copyright: (C) 2010 Nokia Corporation + * Copyright: (C) 2010, 2011 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,8 +25,7 @@ #include <linux/slab.h> #include <sound/pcm.h> #include <sound/pcm_params.h> -#include <sound/soc-dai.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include <sound/initval.h> #include "wl1273.h" @@ -43,7 +42,7 @@ struct wl1273_priv { static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, int rate, int width) { - struct device *dev = &core->i2c_dev->dev; + struct device *dev = &core->client->dev; int r = 0; u16 mode; @@ -124,13 +123,13 @@ static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, dev_dbg(dev, "mode: 0x%04x\n", mode); if (core->i2s_mode != mode) { - r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, mode); + r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode); if (r) goto out; core->i2s_mode = mode; - r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, - WL1273_AUDIO_ENABLE_I2S); + r = core->write(core, WL1273_AUDIO_ENABLE, + WL1273_AUDIO_ENABLE_I2S); if (r) goto out; } @@ -143,8 +142,7 @@ out: static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, int channel_number) { - struct i2c_client *client = core->i2c_dev; - struct device *dev = &client->dev; + struct device *dev = &core->client->dev; int r = 0; dev_dbg(dev, "%s\n", __func__); @@ -155,17 +153,13 @@ static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, goto out; if (channel_number == 1 && core->mode == WL1273_MODE_RX) - r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, - WL1273_RX_MONO); + r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); else if (channel_number == 1 && core->mode == WL1273_MODE_TX) - r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, - WL1273_TX_MONO); + r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); else if (channel_number == 2 && core->mode == WL1273_MODE_RX) - r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, - WL1273_RX_STEREO); + r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); else if (channel_number == 2 && core->mode == WL1273_MODE_TX) - r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, - WL1273_TX_STEREO); + r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO); else r = -EINVAL; out: @@ -185,7 +179,12 @@ static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, return 0; } -static const char *wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; +/* + * TODO: Implement the audio routing in the driver. Now this control + * only indicates the setting that has been done elsewhere (in the user + * space). + */ +static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -238,14 +237,14 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, if (wl1273->core->audio_mode == val) return 0; - r = wl1273_fm_set_audio(wl1273->core, val); + r = wl1273->core->set_audio(wl1273->core, val); if (r < 0) return r; return 1; } -static const char *wl1273_audio_strings[] = { "Digital", "Analog" }; +static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; static const struct soc_enum wl1273_audio_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings), @@ -273,8 +272,8 @@ static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, dev_dbg(codec->dev, "%s: enter.\n", __func__); - r = wl1273_fm_set_volume(wl1273->core, - ucontrol->value.integer.value[0]); + r = wl1273->core->set_volume(wl1273->core, + ucontrol->value.integer.value[0]); if (r) return r; @@ -442,7 +441,8 @@ EXPORT_SYMBOL_GPL(wl1273_get_format); static int wl1273_probe(struct snd_soc_codec *codec) { - struct wl1273_core **core = codec->dev->platform_data; + struct wl1273_core **core = + mfd_get_data(to_platform_device(codec->dev)); struct wl1273_priv *wl1273; int r; diff --git a/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h index 14ed027fdcfc..43ec7e668c51 100644 --- a/sound/soc/codecs/wl1273.h +++ b/sound/soc/codecs/wl1273.h @@ -25,77 +25,6 @@ #ifndef __WL1273_CODEC_H__ #define __WL1273_CODEC_H__ -/* I2S protocol, left channel first, data width 16 bits */ -#define WL1273_PCM_DEF_MODE 0x00 - -/* Rx */ -#define WL1273_AUDIO_ENABLE_I2S (1 << 0) -#define WL1273_AUDIO_ENABLE_ANALOG (1 << 1) - -/* Tx */ -#define WL1273_AUDIO_IO_SET_ANALOG 0 -#define WL1273_AUDIO_IO_SET_I2S 1 - -#define WL1273_POWER_SET_OFF 0 -#define WL1273_POWER_SET_FM (1 << 0) -#define WL1273_POWER_SET_RDS (1 << 1) -#define WL1273_POWER_SET_RETENTION (1 << 4) - -#define WL1273_PUPD_SET_OFF 0x00 -#define WL1273_PUPD_SET_ON 0x01 -#define WL1273_PUPD_SET_RETENTION 0x10 - -/* I2S mode */ -#define WL1273_IS2_WIDTH_32 0x0 -#define WL1273_IS2_WIDTH_40 0x1 -#define WL1273_IS2_WIDTH_22_23 0x2 -#define WL1273_IS2_WIDTH_23_22 0x3 -#define WL1273_IS2_WIDTH_48 0x4 -#define WL1273_IS2_WIDTH_50 0x5 -#define WL1273_IS2_WIDTH_60 0x6 -#define WL1273_IS2_WIDTH_64 0x7 -#define WL1273_IS2_WIDTH_80 0x8 -#define WL1273_IS2_WIDTH_96 0x9 -#define WL1273_IS2_WIDTH_128 0xa -#define WL1273_IS2_WIDTH 0xf - -#define WL1273_IS2_FORMAT_STD (0x0 << 4) -#define WL1273_IS2_FORMAT_LEFT (0x1 << 4) -#define WL1273_IS2_FORMAT_RIGHT (0x2 << 4) -#define WL1273_IS2_FORMAT_USER (0x3 << 4) - -#define WL1273_IS2_MASTER (0x0 << 6) -#define WL1273_IS2_SLAVEW (0x1 << 6) - -#define WL1273_IS2_TRI_AFTER_SENDING (0x0 << 7) -#define WL1273_IS2_TRI_ALWAYS_ACTIVE (0x1 << 7) - -#define WL1273_IS2_SDOWS_RR (0x0 << 8) -#define WL1273_IS2_SDOWS_RF (0x1 << 8) -#define WL1273_IS2_SDOWS_FR (0x2 << 8) -#define WL1273_IS2_SDOWS_FF (0x3 << 8) - -#define WL1273_IS2_TRI_OPT (0x0 << 10) -#define WL1273_IS2_TRI_ALWAYS (0x1 << 10) - -#define WL1273_IS2_RATE_48K (0x0 << 12) -#define WL1273_IS2_RATE_44_1K (0x1 << 12) -#define WL1273_IS2_RATE_32K (0x2 << 12) -#define WL1273_IS2_RATE_22_05K (0x4 << 12) -#define WL1273_IS2_RATE_16K (0x5 << 12) -#define WL1273_IS2_RATE_12K (0x8 << 12) -#define WL1273_IS2_RATE_11_025 (0x9 << 12) -#define WL1273_IS2_RATE_8K (0xa << 12) -#define WL1273_IS2_RATE (0xf << 12) - -#define WL1273_I2S_DEF_MODE (WL1273_IS2_WIDTH_32 | \ - WL1273_IS2_FORMAT_STD | \ - WL1273_IS2_MASTER | \ - WL1273_IS2_TRI_AFTER_SENDING | \ - WL1273_IS2_SDOWS_RR | \ - WL1273_IS2_TRI_OPT | \ - WL1273_IS2_RATE_48K) - int wl1273_get_format(struct snd_soc_codec *codec, unsigned int *fmt); #endif /* End of __WL1273_CODEC_H__ */ diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 4bcd168794e1..a3b9cbb20ee9 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -36,7 +36,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -705,6 +704,7 @@ static const struct snd_soc_dapm_route audio_map[] = { /* Called from the machine driver */ int wm2000_add_controls(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; if (!wm2000_i2c) { @@ -712,12 +712,12 @@ int wm2000_add_controls(struct snd_soc_codec *codec) return -ENODEV; } - ret = snd_soc_dapm_new_controls(codec, wm2000_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, wm2000_dapm_widgets, ARRAY_SIZE(wm2000_dapm_widgets)); if (ret < 0) return ret; - ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); if (ret < 0) return ret; @@ -836,24 +836,25 @@ static void wm2000_i2c_shutdown(struct i2c_client *i2c) } #ifdef CONFIG_PM -static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +static int wm2000_i2c_suspend(struct device *dev) { + struct i2c_client *i2c = to_i2c_client(dev); struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); return wm2000_anc_transition(wm2000, ANC_OFF); } -static int wm2000_i2c_resume(struct i2c_client *i2c) +static int wm2000_i2c_resume(struct device *dev) { + struct i2c_client *i2c = to_i2c_client(dev); struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); return wm2000_anc_set_mode(wm2000); } -#else -#define wm2000_i2c_suspend NULL -#define wm2000_i2c_resume NULL #endif +static SIMPLE_DEV_PM_OPS(wm2000_pm, wm2000_i2c_suspend, wm2000_i2c_resume); + static const struct i2c_device_id wm2000_i2c_id[] = { { "wm2000", 0 }, { } @@ -864,11 +865,10 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", .owner = THIS_MODULE, + .pm = &wm2000_pm, }, .probe = wm2000_i2c_probe, .remove = __devexit_p(wm2000_i2c_remove), - .suspend = wm2000_i2c_suspend, - .resume = wm2000_i2c_resume, .shutdown = wm2000_i2c_shutdown, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 7611add7f8c3..6d6dc9efe914 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -24,9 +24,9 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> +#include <trace/events/asoc.h> #include "wm8350.h" @@ -54,6 +54,7 @@ struct wm8350_output { struct wm8350_jack_data { struct snd_soc_jack *jack; + struct delayed_work work; int report; int short_report; }; @@ -230,8 +231,9 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) */ static void wm8350_pga_work(struct work_struct *work) { - struct snd_soc_codec *codec = - container_of(work, struct snd_soc_codec, delayed_work.work); + struct snd_soc_dapm_context *dapm = + container_of(work, struct snd_soc_dapm_context, delayed_work.work); + struct snd_soc_codec *codec = dapm->codec; struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1, *out2 = &wm8350_data->out2; @@ -302,8 +304,8 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_UP; out->active = 1; - if (!delayed_work_pending(&codec->delayed_work)) - schedule_delayed_work(&codec->delayed_work, + if (!delayed_work_pending(&codec->dapm.delayed_work)) + schedule_delayed_work(&codec->dapm.delayed_work, msecs_to_jiffies(1)); break; @@ -311,8 +313,8 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_DOWN; out->active = 0; - if (!delayed_work_pending(&codec->delayed_work)) - schedule_delayed_work(&codec->delayed_work, + if (!delayed_work_pending(&codec->dapm.delayed_work)) + schedule_delayed_work(&codec->dapm.delayed_work, msecs_to_jiffies(1)); break; } @@ -786,9 +788,10 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8350_add_widgets(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - ret = snd_soc_dapm_new_controls(codec, + ret = snd_soc_dapm_new_controls(dapm, wm8350_dapm_widgets, ARRAY_SIZE(wm8350_dapm_widgets)); if (ret != 0) { @@ -797,7 +800,7 @@ static int wm8350_add_widgets(struct snd_soc_codec *codec) } /* set up audio paths */ - ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); if (ret != 0) { dev_err(codec->dev, "DAPM route register failed\n"); return ret; @@ -1184,7 +1187,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) @@ -1317,7 +1320,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, priv->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1334,45 +1337,69 @@ static int wm8350_resume(struct snd_soc_codec *codec) return 0; } -static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) +static void wm8350_hp_work(struct wm8350_data *priv, + struct wm8350_jack_data *jack, + u16 mask) { - struct wm8350_data *priv = data; struct wm8350 *wm8350 = priv->codec.control_data; u16 reg; int report; - int mask; + + reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); + if (reg & mask) + report = jack->report; + else + report = 0; + + snd_soc_jack_report(jack->jack, report, jack->report); + +} + +static void wm8350_hpl_work(struct work_struct *work) +{ + struct wm8350_data *priv = + container_of(work, struct wm8350_data, hpl.work.work); + + wm8350_hp_work(priv, &priv->hpl, WM8350_JACK_L_LVL); +} + +static void wm8350_hpr_work(struct work_struct *work) +{ + struct wm8350_data *priv = + container_of(work, struct wm8350_data, hpr.work.work); + + wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL); +} + +static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->codec.control_data; struct wm8350_jack_data *jack = NULL; switch (irq - wm8350->irq_base) { case WM8350_IRQ_CODEC_JCK_DET_L: +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 HPL"); +#endif jack = &priv->hpl; - mask = WM8350_JACK_L_LVL; break; case WM8350_IRQ_CODEC_JCK_DET_R: +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 HPR"); +#endif jack = &priv->hpr; - mask = WM8350_JACK_R_LVL; break; default: BUG(); } - if (!jack->jack) { - dev_warn(wm8350->dev, "Jack interrupt called with no jack\n"); - return IRQ_NONE; - } - - /* Debounce */ - msleep(200); - - reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); - if (reg & mask) - report = jack->report; - else - report = 0; + if (device_may_wakeup(wm8350->dev)) + pm_wakeup_event(wm8350->dev, 250); - snd_soc_jack_report(jack->jack, report, jack->report); + schedule_delayed_work(&jack->work, 200); return IRQ_HANDLED; } @@ -1436,6 +1463,10 @@ static irqreturn_t wm8350_mic_handler(int irq, void *data) u16 reg; int report = 0; +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 mic"); +#endif + reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); if (reg & WM8350_JACK_MICSCD_LVL) report |= priv->mic.short_report; @@ -1550,7 +1581,9 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); - INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work); + INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8350_pga_work); + INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work); + INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work); /* Enable the codec */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); @@ -1626,7 +1659,6 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) { struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); struct wm8350 *wm8350 = dev_get_platdata(codec->dev); - int ret; wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); @@ -1641,15 +1673,12 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) priv->hpr.jack = NULL; priv->mic.jack = NULL; - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(&codec->delayed_work); + cancel_delayed_work_sync(&priv->hpl.work); + cancel_delayed_work_sync(&priv->hpr.work); /* if there was any work waiting then we run it now and * wait for its completion */ - if (ret) { - schedule_delayed_work(&codec->delayed_work, 0); - flush_scheduled_work(); - } + flush_delayed_work_sync(&codec->dapm.delayed_work); wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 850299786e02..736b785e3756 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -22,11 +22,11 @@ #include <linux/regulator/consumer.h> #include <linux/mfd/wm8400-audio.h> #include <linux/mfd/wm8400-private.h> +#include <linux/mfd/core.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -911,10 +911,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8400_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8400_dapm_widgets, - ARRAY_SIZE(wm8400_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8400_dapm_widgets, + ARRAY_SIZE(wm8400_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -1219,7 +1220,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(power), &power[0]); if (ret != 0) { @@ -1306,7 +1307,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1377,7 +1378,7 @@ static void wm8400_probe_deferred(struct work_struct *work) static int wm8400_codec_probe(struct snd_soc_codec *codec) { - struct wm8400 *wm8400 = dev_get_platdata(codec->dev); + struct wm8400 *wm8400 = mfd_get_data(to_platform_device(codec->dev)); struct wm8400_priv *priv; int ret; u16 reg; diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 8f107095760e..db0dced74843 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "wm8510.h" @@ -216,10 +215,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8510_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8510_dapm_widgets, - ARRAY_SIZE(wm8510_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8510_dapm_widgets, + ARRAY_SIZE(wm8510_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -478,7 +478,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8510_POWER1, power1 | 0x3); mdelay(100); @@ -495,7 +495,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 9a433a5396cb..4fd4d8dca0fc 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -41,7 +40,6 @@ static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = { /* codec private data */ struct wm8523_priv { enum snd_soc_control_type control_type; - u16 reg_cache[WM8523_REGISTER_COUNT]; struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES]; unsigned int sysclk; unsigned int rate_constraint_list[WM8523_NUM_RATES]; @@ -60,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = { 0x0000, /* R8 - ZERO_DETECT */ }; -static int wm8523_volatile_register(unsigned int reg) +static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8523_DEVICE_ID: @@ -110,10 +108,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int wm8523_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8523_dapm_widgets, - ARRAY_SIZE(wm8523_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_new_controls(dapm, wm8523_dapm_widgets, + ARRAY_SIZE(wm8523_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); return 0; } @@ -314,6 +313,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec); + u16 *reg_cache = codec->reg_cache; int ret, i; switch (level) { @@ -327,7 +327,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); if (ret != 0) { @@ -344,7 +344,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, /* Sync back default/cached values */ for (i = WM8523_AIF_CTRL1; i < WM8523_MAX_REGISTER; i++) - snd_soc_write(codec, i, wm8523->reg_cache[i]); + snd_soc_write(codec, i, reg_cache[i]); msleep(100); @@ -366,7 +366,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, wm8523->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -470,8 +470,9 @@ static int wm8523_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU and enable ZC */ - wm8523->reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU; - wm8523->reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC; + snd_soc_update_bits(codec, WM8523_DAC_GAINR, + WM8523_DACR_VU, WM8523_DACR_VU); + snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC); wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index a2e0ed59b376..4bbc0a79f01e 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -31,7 +31,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/initval.h> #include <asm/div64.h> @@ -161,7 +160,7 @@ static const u16 wm8580_reg[] = { 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/ 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/ - 0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/ + 0x0010, 0x0002, 0x0002, 0x00c2, /*R11*/ 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/ 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/ @@ -191,7 +190,6 @@ static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { struct wm8580_priv { enum snd_soc_control_type control_type; struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; - u16 reg_cache[WM8580_MAX_REGISTER + 1]; struct pll_state a; struct pll_state b; int sysclk[2]; @@ -302,10 +300,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8580_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets, - ARRAY_SIZE(wm8580_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8580_dapm_widgets, + ARRAY_SIZE(wm8580_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -491,29 +490,29 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, paifa |= 0x8; break; case SNDRV_PCM_FORMAT_S20_3LE: - paifa |= 0x10; + paifa |= 0x0; paifb |= WM8580_AIF_LENGTH_20; break; case SNDRV_PCM_FORMAT_S24_LE: - paifa |= 0x10; + paifa |= 0x0; paifb |= WM8580_AIF_LENGTH_24; break; case SNDRV_PCM_FORMAT_S32_LE: - paifa |= 0x10; - paifb |= WM8580_AIF_LENGTH_24; + paifa |= 0x0; + paifb |= WM8580_AIF_LENGTH_32; break; default: return -EINVAL; } /* Look up the SYSCLK ratio; accept only exact matches */ - ratio = wm8580->sysclk[dai->id] / params_rate(params); + ratio = wm8580->sysclk[dai->driver->id] / params_rate(params); for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) if (ratio == wm8580_sysclk_ratios[i]) break; if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { dev_err(codec->dev, "Invalid clock ratio %d/%d\n", - wm8580->sysclk[dai->id], params_rate(params)); + wm8580->sysclk[dai->driver->id], params_rate(params)); return -EINVAL; } paifa |= i; @@ -716,7 +715,7 @@ static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, switch (clk_id) { case WM8580_CLKSRC_ADCMCLK: - if (dai->id != WM8580_DAI_PAIFTX) + if (dai->driver->id != WM8580_DAI_PAIFTX) return -EINVAL; sel = 0 << sel_shift; break; @@ -735,7 +734,7 @@ static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, } /* We really should validate PLL settings but not yet */ - wm8580->sysclk[dai->id] = freq; + wm8580->sysclk[dai->driver->id] = freq; return snd_soc_update_bits(codec, WM8580_CLKSEL, sel_mask, sel); } @@ -767,13 +766,13 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Power up and get individual control of the DACs */ reg = snd_soc_read(codec, WM8580_PWRDN1); reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD); snd_soc_write(codec, WM8580_PWRDN1, reg); - /* Make VMID high impedence */ + /* Make VMID high impedance */ reg = snd_soc_read(codec, WM8580_ADC_CONTROL1); reg &= ~0x100; snd_soc_write(codec, WM8580_ADC_CONTROL1, reg); @@ -785,7 +784,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -905,7 +904,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8580 = { .set_bias_level = wm8580_set_bias_level, .reg_cache_size = ARRAY_SIZE(wm8580_reg), .reg_word_size = sizeof(u16), - .reg_cache_default = &wm8580_reg, + .reg_cache_default = wm8580_reg, }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 54fbd76c8bca..97c30382d3ff 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -25,7 +25,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/initval.h> @@ -34,7 +33,6 @@ /* codec private data */ struct wm8711_priv { enum snd_soc_control_type bus_type; - u16 reg_cache[WM8711_CACHEREGNUM]; unsigned int sysclk; }; @@ -93,10 +91,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int wm8711_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets, - ARRAY_SIZE(wm8711_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + 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; } @@ -318,7 +317,7 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8711_PWR, 0xffff); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 075f35e4f4cb..736b0352d0a7 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -23,7 +23,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -73,10 +72,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int wm8728_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets, - ARRAY_SIZE(wm8728_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + 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; } @@ -180,7 +180,7 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Power everything up... */ reg = snd_soc_read(codec, WM8728_DACCTL); snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4); @@ -197,7 +197,7 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8728_DACCTL, reg | 0x4); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index e725c09a3e79..0a67c31b2663 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -26,7 +26,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -44,9 +43,10 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { struct wm8731_priv { enum snd_soc_control_type control_type; struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; - u16 reg_cache[WM8731_CACHEREGNUM]; unsigned int sysclk; int sysclk_type; + int playback_fs; + bool deemph; }; @@ -65,16 +65,79 @@ static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) static const char *wm8731_input_select[] = {"Line In", "Mic"}; -static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; -static const struct soc_enum wm8731_enum[] = { - SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), - SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), -}; +static const struct soc_enum wm8731_insel_enum = + SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); + +static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8731_set_deemph(struct snd_soc_codec *codec) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8731->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { + if (abs(wm8731_deemph[i] - wm8731->playback_fs) < + abs(wm8731_deemph[best] - wm8731->playback_fs)) + best = i; + } + + val = best << 1; + } else { + best = 0; + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", + best, wm8731_deemph[best]); + + return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val); +} + +static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8731->deemph; + + return 0; +} + +static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&codec->mutex); + if (wm8731->deemph != deemph) { + wm8731->deemph = deemph; + + wm8731_set_deemph(codec); + + ret = 1; + } + mutex_unlock(&codec->mutex); + + return ret; +} static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); static const struct snd_kcontrol_new wm8731_snd_controls[] = { @@ -87,7 +150,7 @@ SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, in_tlv), SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), -SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), +SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv), SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, @@ -96,7 +159,8 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), -SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8731_get_deemph, wm8731_put_deemph), }; /* Output Mixer */ @@ -108,7 +172,7 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), /* Input mux */ static const struct snd_kcontrol_new wm8731_input_mux_controls = -SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); +SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), @@ -165,10 +229,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int wm8731_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, - ARRAY_SIZE(wm8731_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + 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; } @@ -239,6 +304,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, u16 srate = (coeff_div[i].sr << 2) | (coeff_div[i].bosr << 1) | coeff_div[i].usb; + wm8731->playback_fs = params_rate(params); + snd_soc_write(codec, WM8731_SRATE, srate); /* bit size */ @@ -253,6 +320,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, break; } + wm8731_set_deemph(codec); + snd_soc_write(codec, WM8731_IFACE, iface); return 0; } @@ -319,7 +388,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; } - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(&codec->dapm); return 0; } @@ -399,7 +468,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) @@ -428,7 +497,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, wm8731->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -542,7 +611,6 @@ err_regulator_enable: err_regulator_get: regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); - kfree(wm8731); return ret; } diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c new file mode 100644 index 000000000000..30c67d06a904 --- /dev/null +++ b/sound/soc/codecs/wm8737.c @@ -0,0 +1,754 @@ +/* + * wm8737.c -- WM8737 ALSA SoC Audio driver + * + * Copyright 2010 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/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8737.h" + +#define WM8737_NUM_SUPPLIES 4 +static const char *wm8737_supply_names[WM8737_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "MVDD", +}; + +/* codec private data */ +struct wm8737_priv { + enum snd_soc_control_type control_type; + struct regulator_bulk_data supplies[WM8737_NUM_SUPPLIES]; + unsigned int mclk; +}; + +static const u16 wm8737_reg[WM8737_REGISTER_COUNT] = { + 0x00C3, /* R0 - Left PGA volume */ + 0x00C3, /* R1 - Right PGA volume */ + 0x0007, /* R2 - AUDIO path L */ + 0x0007, /* R3 - AUDIO path R */ + 0x0000, /* R4 - 3D Enhance */ + 0x0000, /* R5 - ADC Control */ + 0x0000, /* R6 - Power Management */ + 0x000A, /* R7 - Audio Format */ + 0x0000, /* R8 - Clocking */ + 0x000F, /* R9 - MIC Preamp Control */ + 0x0003, /* R10 - Misc Bias Control */ + 0x0000, /* R11 - Noise Gate */ + 0x007C, /* R12 - ALC1 */ + 0x0000, /* R13 - ALC2 */ + 0x0032, /* R14 - ALC3 */ +}; + +static int wm8737_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, WM8737_RESET, 0); +} + +static const unsigned int micboost_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 0, TLV_DB_SCALE_ITEM(1300, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2800, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0), +}; +static const DECLARE_TLV_DB_SCALE(pga_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(ng_tlv, -7800, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -1800, 100, 0); + +static const char *micbias_enum_text[] = { + "25%", + "50%", + "75%", + "100%", +}; + +static const struct soc_enum micbias_enum = + SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 0, 4, micbias_enum_text); + +static const char *low_cutoff_text[] = { + "Low", "High" +}; + +static const struct soc_enum low_3d = + SOC_ENUM_SINGLE(WM8737_3D_ENHANCE, 6, 2, low_cutoff_text); + +static const char *high_cutoff_text[] = { + "High", "Low" +}; + +static const struct soc_enum high_3d = + SOC_ENUM_SINGLE(WM8737_3D_ENHANCE, 5, 2, high_cutoff_text); + +static const char *alc_fn_text[] = { + "Disabled", "Right", "Left", "Stereo" +}; + +static const struct soc_enum alc_fn = + SOC_ENUM_SINGLE(WM8737_ALC1, 7, 4, alc_fn_text); + +static const char *alc_hold_text[] = { + "0", "2.67ms", "5.33ms", "10.66ms", "21.32ms", "42.64ms", "85.28ms", + "170.56ms", "341.12ms", "682.24ms", "1.364s", "2.728s", "5.458s", + "10.916s", "21.832s", "43.691s" +}; + +static const struct soc_enum alc_hold = + SOC_ENUM_SINGLE(WM8737_ALC2, 0, 16, alc_hold_text); + +static const char *alc_atk_text[] = { + "8.4ms", "16.8ms", "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", + "1.075s", "2.15s", "4.3s", "8.6s" +}; + +static const struct soc_enum alc_atk = + SOC_ENUM_SINGLE(WM8737_ALC3, 0, 11, alc_atk_text); + +static const char *alc_dcy_text[] = { + "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", "1.075s", "2.15s", + "4.3s", "8.6s", "17.2s", "34.41s" +}; + +static const struct soc_enum alc_dcy = + SOC_ENUM_SINGLE(WM8737_ALC3, 4, 11, alc_dcy_text); + +static const struct snd_kcontrol_new wm8737_snd_controls[] = { +SOC_DOUBLE_R_TLV("Mic Boost Volume", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 6, 3, 0, micboost_tlv), +SOC_DOUBLE_R("Mic Boost Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 4, 1, 0), +SOC_DOUBLE("Mic ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 3, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8737_LEFT_PGA_VOLUME, + WM8737_RIGHT_PGA_VOLUME, 0, 255, 0, pga_tlv), +SOC_DOUBLE("Capture ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 2, 1, 0), + +SOC_DOUBLE("INPUT1 DC Bias Switch", WM8737_MISC_BIAS_CONTROL, 0, 1, 1, 0), + +SOC_ENUM("Mic PGA Bias", micbias_enum), +SOC_SINGLE("ADC Low Power Switch", WM8737_ADC_CONTROL, 2, 1, 0), +SOC_SINGLE("High Pass Filter Switch", WM8737_ADC_CONTROL, 0, 1, 1), +SOC_DOUBLE("Polarity Invert Switch", WM8737_ADC_CONTROL, 5, 6, 1, 0), + +SOC_SINGLE("3D Switch", WM8737_3D_ENHANCE, 0, 1, 0), +SOC_SINGLE("3D Depth", WM8737_3D_ENHANCE, 1, 15, 0), +SOC_ENUM("3D Low Cut-off", low_3d), +SOC_ENUM("3D High Cut-off", low_3d), +SOC_SINGLE_TLV("3D ADC Volume", WM8737_3D_ENHANCE, 7, 1, 1, adc_tlv), + +SOC_SINGLE("Noise Gate Switch", WM8737_NOISE_GATE, 0, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", WM8737_NOISE_GATE, 2, 7, 0, + ng_tlv), + +SOC_ENUM("ALC", alc_fn), +SOC_SINGLE_TLV("ALC Max Gain Volume", WM8737_ALC1, 4, 7, 0, alc_max_tlv), +SOC_SINGLE_TLV("ALC Target Volume", WM8737_ALC1, 0, 15, 0, alc_target_tlv), +SOC_ENUM("ALC Hold Time", alc_hold), +SOC_SINGLE("ALC ZC Switch", WM8737_ALC2, 4, 1, 0), +SOC_ENUM("ALC Attack Time", alc_atk), +SOC_ENUM("ALC Decay Time", alc_dcy), +}; + +static const char *linsel_text[] = { + "LINPUT1", "LINPUT2", "LINPUT3", "LINPUT1 DC", +}; + +static const struct soc_enum linsel_enum = + SOC_ENUM_SINGLE(WM8737_AUDIO_PATH_L, 7, 4, linsel_text); + +static const struct snd_kcontrol_new linsel_mux = + SOC_DAPM_ENUM("LINSEL", linsel_enum); + + +static const char *rinsel_text[] = { + "RINPUT1", "RINPUT2", "RINPUT3", "RINPUT1 DC", +}; + +static const struct soc_enum rinsel_enum = + SOC_ENUM_SINGLE(WM8737_AUDIO_PATH_R, 7, 4, rinsel_text); + +static const struct snd_kcontrol_new rinsel_mux = + SOC_DAPM_ENUM("RINSEL", rinsel_enum); + +static const char *bypass_text[] = { + "Direct", "Preamp" +}; + +static const struct soc_enum lbypass_enum = + SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 2, 2, bypass_text); + +static const struct snd_kcontrol_new lbypass_mux = + SOC_DAPM_ENUM("Left Bypass", lbypass_enum); + + +static const struct soc_enum rbypass_enum = + SOC_ENUM_SINGLE(WM8737_MIC_PREAMP_CONTROL, 3, 2, bypass_text); + +static const struct snd_kcontrol_new rbypass_mux = + SOC_DAPM_ENUM("Left Bypass", rbypass_enum); + +static const struct snd_soc_dapm_widget wm8737_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("LINPUT1"), +SND_SOC_DAPM_INPUT("LINPUT2"), +SND_SOC_DAPM_INPUT("LINPUT3"), +SND_SOC_DAPM_INPUT("RINPUT1"), +SND_SOC_DAPM_INPUT("RINPUT2"), +SND_SOC_DAPM_INPUT("RINPUT3"), +SND_SOC_DAPM_INPUT("LACIN"), +SND_SOC_DAPM_INPUT("RACIN"), + +SND_SOC_DAPM_MUX("LINSEL", SND_SOC_NOPM, 0, 0, &linsel_mux), +SND_SOC_DAPM_MUX("RINSEL", SND_SOC_NOPM, 0, 0, &rinsel_mux), + +SND_SOC_DAPM_MUX("Left Preamp Mux", SND_SOC_NOPM, 0, 0, &lbypass_mux), +SND_SOC_DAPM_MUX("Right Preamp Mux", SND_SOC_NOPM, 0, 0, &rbypass_mux), + +SND_SOC_DAPM_PGA("PGAL", WM8737_POWER_MANAGEMENT, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("PGAR", WM8737_POWER_MANAGEMENT, 4, 0, NULL, 0), + +SND_SOC_DAPM_DAC("ADCL", NULL, WM8737_POWER_MANAGEMENT, 3, 0), +SND_SOC_DAPM_DAC("ADCR", NULL, WM8737_POWER_MANAGEMENT, 2, 0), + +SND_SOC_DAPM_AIF_OUT("AIF", "Capture", 0, WM8737_POWER_MANAGEMENT, 6, 0), +}; + +static const struct snd_soc_dapm_route intercon[] = { + { "LINSEL", "LINPUT1", "LINPUT1" }, + { "LINSEL", "LINPUT2", "LINPUT2" }, + { "LINSEL", "LINPUT3", "LINPUT3" }, + { "LINSEL", "LINPUT1 DC", "LINPUT1" }, + + { "RINSEL", "RINPUT1", "RINPUT1" }, + { "RINSEL", "RINPUT2", "RINPUT2" }, + { "RINSEL", "RINPUT3", "RINPUT3" }, + { "RINSEL", "RINPUT1 DC", "RINPUT1" }, + + { "Left Preamp Mux", "Preamp", "LINSEL" }, + { "Left Preamp Mux", "Direct", "LACIN" }, + + { "Right Preamp Mux", "Preamp", "RINSEL" }, + { "Right Preamp Mux", "Direct", "RACIN" }, + + { "PGAL", NULL, "Left Preamp Mux" }, + { "PGAR", NULL, "Right Preamp Mux" }, + + { "ADCL", NULL, "PGAL" }, + { "ADCR", NULL, "PGAR" }, + + { "AIF", NULL, "ADCL" }, + { "AIF", NULL, "ADCR" }, +}; + +static int wm8737_add_widgets(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_new_controls(dapm, wm8737_dapm_widgets, + ARRAY_SIZE(wm8737_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + return 0; +} + +/* codec mclk clock divider coefficients */ +static const struct { + u32 mclk; + u32 rate; + u8 usb; + u8 sr; +} coeff_div[] = { + { 12288000, 8000, 0, 0x4 }, + { 12288000, 12000, 0, 0x8 }, + { 12288000, 16000, 0, 0xa }, + { 12288000, 24000, 0, 0x1c }, + { 12288000, 32000, 0, 0xc }, + { 12288000, 48000, 0, 0 }, + { 12288000, 96000, 0, 0xe }, + + { 11289600, 8000, 0, 0x14 }, + { 11289600, 11025, 0, 0x18 }, + { 11289600, 22050, 0, 0x1a }, + { 11289600, 44100, 0, 0x10 }, + { 11289600, 88200, 0, 0x1e }, + + { 18432000, 8000, 0, 0x5 }, + { 18432000, 12000, 0, 0x9 }, + { 18432000, 16000, 0, 0xb }, + { 18432000, 24000, 0, 0x1b }, + { 18432000, 32000, 0, 0xd }, + { 18432000, 48000, 0, 0x1 }, + { 18432000, 96000, 0, 0x1f }, + + { 16934400, 8000, 0, 0x15 }, + { 16934400, 11025, 0, 0x19 }, + { 16934400, 22050, 0, 0x1b }, + { 16934400, 44100, 0, 0x11 }, + { 16934400, 88200, 0, 0x1f }, + + { 12000000, 8000, 1, 0x4 }, + { 12000000, 11025, 1, 0x19 }, + { 12000000, 12000, 1, 0x8 }, + { 12000000, 16000, 1, 0xa }, + { 12000000, 22050, 1, 0x1b }, + { 12000000, 24000, 1, 0x1c }, + { 12000000, 32000, 1, 0xc }, + { 12000000, 44100, 1, 0x11 }, + { 12000000, 48000, 1, 0x0 }, + { 12000000, 88200, 1, 0x1f }, + { 12000000, 96000, 1, 0xe }, +}; + +static int wm8737_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 wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); + int i; + u16 clocking = 0; + u16 af = 0; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate != params_rate(params)) + continue; + + if (coeff_div[i].mclk == wm8737->mclk) + break; + + if (coeff_div[i].mclk == wm8737->mclk * 2) { + clocking |= WM8737_CLKDIV2; + break; + } + } + + if (i == ARRAY_SIZE(coeff_div)) { + dev_err(codec->dev, "%dHz MCLK can't support %dHz\n", + wm8737->mclk, params_rate(params)); + return -EINVAL; + } + + clocking |= coeff_div[i].usb | (coeff_div[i].sr << WM8737_SR_SHIFT); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + af |= 0x8; + break; + case SNDRV_PCM_FORMAT_S24_LE: + af |= 0x10; + break; + case SNDRV_PCM_FORMAT_S32_LE: + af |= 0x18; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT, WM8737_WL_MASK, af); + snd_soc_update_bits(codec, WM8737_CLOCKING, + WM8737_USB_MODE | WM8737_CLKDIV2 | WM8737_SR_MASK, + clocking); + + return 0; +} + +static int wm8737_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 wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (freq == coeff_div[i].mclk || + freq == coeff_div[i].mclk * 2) { + wm8737->mclk = freq; + return 0; + } + } + + dev_err(codec->dev, "MCLK rate %dHz not supported\n", freq); + + return -EINVAL; +} + + +static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 af = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + af |= WM8737_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + af |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + af |= 0x1; + break; + case SND_SOC_DAIFMT_DSP_A: + af |= 0x3; + break; + case SND_SOC_DAIFMT_DSP_B: + af |= 0x13; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + af |= WM8737_LRP; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8737_AUDIO_FORMAT, + WM8737_FORMAT_MASK | WM8737_LRP | WM8737_MS, af); + + return 0; +} + +static int wm8737_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID at 2*75k */ + snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, + WM8737_VMIDSEL_MASK, 0); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(codec->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + snd_soc_cache_sync(codec); + + /* Fast VMID ramp at 2*2.5k */ + snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, + WM8737_VMIDSEL_MASK, 0x4); + + /* Bring VMID up */ + snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT, + WM8737_VMID_MASK | + WM8737_VREF_MASK, + WM8737_VMID_MASK | + WM8737_VREF_MASK); + + msleep(500); + } + + /* VMID at 2*300k */ + snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL, + WM8737_VMIDSEL_MASK, 2); + + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, WM8737_POWER_MANAGEMENT, + WM8737_VMID_MASK | WM8737_VREF_MASK, 0); + + regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#define WM8737_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8737_FORMATS (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 wm8737_dai_ops = { + .hw_params = wm8737_hw_params, + .set_sysclk = wm8737_set_dai_sysclk, + .set_fmt = wm8737_set_dai_fmt, +}; + +static struct snd_soc_dai_driver wm8737_dai = { + .name = "wm8737", + .capture = { + .stream_name = "Capture", + .channels_min = 2, /* Mono modes not yet supported */ + .channels_max = 2, + .rates = WM8737_RATES, + .formats = WM8737_FORMATS, + }, + .ops = &wm8737_dai_ops, +}; + +#ifdef CONFIG_PM +static int wm8737_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8737_resume(struct snd_soc_codec *codec) +{ + wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define wm8737_suspend NULL +#define wm8737_resume NULL +#endif + +static int wm8737_probe(struct snd_soc_codec *codec) +{ + struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); + int ret, i; + + ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8737->control_type); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++) + wm8737->supplies[i].supply = wm8737_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_get; + } + + ret = wm8737_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + goto err_enable; + } + + snd_soc_update_bits(codec, WM8737_LEFT_PGA_VOLUME, WM8737_LVU, + WM8737_LVU); + snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU, + WM8737_RVU); + + wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); + + snd_soc_add_controls(codec, wm8737_snd_controls, + ARRAY_SIZE(wm8737_snd_controls)); + wm8737_add_widgets(codec); + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); +err_get: + regulator_bulk_free(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); + + return ret; +} + +static int wm8737_remove(struct snd_soc_codec *codec) +{ + struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec); + + wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_free(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_wm8737 = { + .probe = wm8737_probe, + .remove = wm8737_remove, + .suspend = wm8737_suspend, + .resume = wm8737_resume, + .set_bias_level = wm8737_set_bias_level, + + .reg_cache_size = WM8737_REGISTER_COUNT - 1, /* Skip reset */ + .reg_word_size = sizeof(u16), + .reg_cache_default = wm8737_reg, +}; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int wm8737_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8737_priv *wm8737; + int ret; + + wm8737 = kzalloc(sizeof(struct wm8737_priv), GFP_KERNEL); + if (wm8737 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8737); + wm8737->control_type = SND_SOC_I2C; + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8737, &wm8737_dai, 1); + if (ret < 0) + kfree(wm8737); + return ret; + +} + +static __devexit int wm8737_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 wm8737_i2c_id[] = { + { "wm8737", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id); + +static struct i2c_driver wm8737_i2c_driver = { + .driver = { + .name = "wm8737", + .owner = THIS_MODULE, + }, + .probe = wm8737_i2c_probe, + .remove = __devexit_p(wm8737_i2c_remove), + .id_table = wm8737_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8737_spi_probe(struct spi_device *spi) +{ + struct wm8737_priv *wm8737; + int ret; + + wm8737 = kzalloc(sizeof(struct wm8737_priv), GFP_KERNEL); + if (wm8737 == NULL) + return -ENOMEM; + + wm8737->control_type = SND_SOC_SPI; + spi_set_drvdata(spi, wm8737); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8737, &wm8737_dai, 1); + if (ret < 0) + kfree(wm8737); + return ret; +} + +static int __devexit wm8737_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + kfree(spi_get_drvdata(spi)); + return 0; +} + +static struct spi_driver wm8737_spi_driver = { + .driver = { + .name = "wm8737", + .owner = THIS_MODULE, + }, + .probe = wm8737_spi_probe, + .remove = __devexit_p(wm8737_spi_remove), +}; +#endif /* CONFIG_SPI_MASTER */ + +static int __init wm8737_modinit(void) +{ + int ret; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8737_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8737 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8737_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8737 SPI driver: %d\n", + ret); + } +#endif + return 0; +} +module_init(wm8737_modinit); + +static void __exit wm8737_exit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8737_spi_driver); +#endif +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8737_i2c_driver); +#endif +} +module_exit(wm8737_exit); + +MODULE_DESCRIPTION("ASoC WM8737 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8737.h b/sound/soc/codecs/wm8737.h new file mode 100644 index 000000000000..23d14c8ff6e7 --- /dev/null +++ b/sound/soc/codecs/wm8737.h @@ -0,0 +1,322 @@ +#ifndef _WM8737_H +#define _WM8737_H + +/* + * wm8737.c -- WM8523 ALSA SoC Audio driver + * + * Copyright 2010 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. + */ + +/* + * Register values. + */ +#define WM8737_LEFT_PGA_VOLUME 0x00 +#define WM8737_RIGHT_PGA_VOLUME 0x01 +#define WM8737_AUDIO_PATH_L 0x02 +#define WM8737_AUDIO_PATH_R 0x03 +#define WM8737_3D_ENHANCE 0x04 +#define WM8737_ADC_CONTROL 0x05 +#define WM8737_POWER_MANAGEMENT 0x06 +#define WM8737_AUDIO_FORMAT 0x07 +#define WM8737_CLOCKING 0x08 +#define WM8737_MIC_PREAMP_CONTROL 0x09 +#define WM8737_MISC_BIAS_CONTROL 0x0A +#define WM8737_NOISE_GATE 0x0B +#define WM8737_ALC1 0x0C +#define WM8737_ALC2 0x0D +#define WM8737_ALC3 0x0E +#define WM8737_RESET 0x0F + +#define WM8737_REGISTER_COUNT 16 +#define WM8737_MAX_REGISTER 0x0F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Left PGA volume + */ +#define WM8737_LVU 0x0100 /* LVU */ +#define WM8737_LVU_MASK 0x0100 /* LVU */ +#define WM8737_LVU_SHIFT 8 /* LVU */ +#define WM8737_LVU_WIDTH 1 /* LVU */ +#define WM8737_LINVOL_MASK 0x00FF /* LINVOL - [7:0] */ +#define WM8737_LINVOL_SHIFT 0 /* LINVOL - [7:0] */ +#define WM8737_LINVOL_WIDTH 8 /* LINVOL - [7:0] */ + +/* + * R1 (0x01) - Right PGA volume + */ +#define WM8737_RVU 0x0100 /* RVU */ +#define WM8737_RVU_MASK 0x0100 /* RVU */ +#define WM8737_RVU_SHIFT 8 /* RVU */ +#define WM8737_RVU_WIDTH 1 /* RVU */ +#define WM8737_RINVOL_MASK 0x00FF /* RINVOL - [7:0] */ +#define WM8737_RINVOL_SHIFT 0 /* RINVOL - [7:0] */ +#define WM8737_RINVOL_WIDTH 8 /* RINVOL - [7:0] */ + +/* + * R2 (0x02) - AUDIO path L + */ +#define WM8737_LINSEL_MASK 0x0180 /* LINSEL - [8:7] */ +#define WM8737_LINSEL_SHIFT 7 /* LINSEL - [8:7] */ +#define WM8737_LINSEL_WIDTH 2 /* LINSEL - [8:7] */ +#define WM8737_LMICBOOST_MASK 0x0060 /* LMICBOOST - [6:5] */ +#define WM8737_LMICBOOST_SHIFT 5 /* LMICBOOST - [6:5] */ +#define WM8737_LMICBOOST_WIDTH 2 /* LMICBOOST - [6:5] */ +#define WM8737_LMBE 0x0010 /* LMBE */ +#define WM8737_LMBE_MASK 0x0010 /* LMBE */ +#define WM8737_LMBE_SHIFT 4 /* LMBE */ +#define WM8737_LMBE_WIDTH 1 /* LMBE */ +#define WM8737_LMZC 0x0008 /* LMZC */ +#define WM8737_LMZC_MASK 0x0008 /* LMZC */ +#define WM8737_LMZC_SHIFT 3 /* LMZC */ +#define WM8737_LMZC_WIDTH 1 /* LMZC */ +#define WM8737_LPZC 0x0004 /* LPZC */ +#define WM8737_LPZC_MASK 0x0004 /* LPZC */ +#define WM8737_LPZC_SHIFT 2 /* LPZC */ +#define WM8737_LPZC_WIDTH 1 /* LPZC */ +#define WM8737_LZCTO_MASK 0x0003 /* LZCTO - [1:0] */ +#define WM8737_LZCTO_SHIFT 0 /* LZCTO - [1:0] */ +#define WM8737_LZCTO_WIDTH 2 /* LZCTO - [1:0] */ + +/* + * R3 (0x03) - AUDIO path R + */ +#define WM8737_RINSEL_MASK 0x0180 /* RINSEL - [8:7] */ +#define WM8737_RINSEL_SHIFT 7 /* RINSEL - [8:7] */ +#define WM8737_RINSEL_WIDTH 2 /* RINSEL - [8:7] */ +#define WM8737_RMICBOOST_MASK 0x0060 /* RMICBOOST - [6:5] */ +#define WM8737_RMICBOOST_SHIFT 5 /* RMICBOOST - [6:5] */ +#define WM8737_RMICBOOST_WIDTH 2 /* RMICBOOST - [6:5] */ +#define WM8737_RMBE 0x0010 /* RMBE */ +#define WM8737_RMBE_MASK 0x0010 /* RMBE */ +#define WM8737_RMBE_SHIFT 4 /* RMBE */ +#define WM8737_RMBE_WIDTH 1 /* RMBE */ +#define WM8737_RMZC 0x0008 /* RMZC */ +#define WM8737_RMZC_MASK 0x0008 /* RMZC */ +#define WM8737_RMZC_SHIFT 3 /* RMZC */ +#define WM8737_RMZC_WIDTH 1 /* RMZC */ +#define WM8737_RPZC 0x0004 /* RPZC */ +#define WM8737_RPZC_MASK 0x0004 /* RPZC */ +#define WM8737_RPZC_SHIFT 2 /* RPZC */ +#define WM8737_RPZC_WIDTH 1 /* RPZC */ +#define WM8737_RZCTO_MASK 0x0003 /* RZCTO - [1:0] */ +#define WM8737_RZCTO_SHIFT 0 /* RZCTO - [1:0] */ +#define WM8737_RZCTO_WIDTH 2 /* RZCTO - [1:0] */ + +/* + * R4 (0x04) - 3D Enhance + */ +#define WM8737_DIV2 0x0080 /* DIV2 */ +#define WM8737_DIV2_MASK 0x0080 /* DIV2 */ +#define WM8737_DIV2_SHIFT 7 /* DIV2 */ +#define WM8737_DIV2_WIDTH 1 /* DIV2 */ +#define WM8737_3DLC 0x0040 /* 3DLC */ +#define WM8737_3DLC_MASK 0x0040 /* 3DLC */ +#define WM8737_3DLC_SHIFT 6 /* 3DLC */ +#define WM8737_3DLC_WIDTH 1 /* 3DLC */ +#define WM8737_3DUC 0x0020 /* 3DUC */ +#define WM8737_3DUC_MASK 0x0020 /* 3DUC */ +#define WM8737_3DUC_SHIFT 5 /* 3DUC */ +#define WM8737_3DUC_WIDTH 1 /* 3DUC */ +#define WM8737_3DDEPTH_MASK 0x001E /* 3DDEPTH - [4:1] */ +#define WM8737_3DDEPTH_SHIFT 1 /* 3DDEPTH - [4:1] */ +#define WM8737_3DDEPTH_WIDTH 4 /* 3DDEPTH - [4:1] */ +#define WM8737_3DE 0x0001 /* 3DE */ +#define WM8737_3DE_MASK 0x0001 /* 3DE */ +#define WM8737_3DE_SHIFT 0 /* 3DE */ +#define WM8737_3DE_WIDTH 1 /* 3DE */ + +/* + * R5 (0x05) - ADC Control + */ +#define WM8737_MONOMIX_MASK 0x0180 /* MONOMIX - [8:7] */ +#define WM8737_MONOMIX_SHIFT 7 /* MONOMIX - [8:7] */ +#define WM8737_MONOMIX_WIDTH 2 /* MONOMIX - [8:7] */ +#define WM8737_POLARITY_MASK 0x0060 /* POLARITY - [6:5] */ +#define WM8737_POLARITY_SHIFT 5 /* POLARITY - [6:5] */ +#define WM8737_POLARITY_WIDTH 2 /* POLARITY - [6:5] */ +#define WM8737_HPOR 0x0010 /* HPOR */ +#define WM8737_HPOR_MASK 0x0010 /* HPOR */ +#define WM8737_HPOR_SHIFT 4 /* HPOR */ +#define WM8737_HPOR_WIDTH 1 /* HPOR */ +#define WM8737_LP 0x0004 /* LP */ +#define WM8737_LP_MASK 0x0004 /* LP */ +#define WM8737_LP_SHIFT 2 /* LP */ +#define WM8737_LP_WIDTH 1 /* LP */ +#define WM8737_MONOUT 0x0002 /* MONOUT */ +#define WM8737_MONOUT_MASK 0x0002 /* MONOUT */ +#define WM8737_MONOUT_SHIFT 1 /* MONOUT */ +#define WM8737_MONOUT_WIDTH 1 /* MONOUT */ +#define WM8737_ADCHPD 0x0001 /* ADCHPD */ +#define WM8737_ADCHPD_MASK 0x0001 /* ADCHPD */ +#define WM8737_ADCHPD_SHIFT 0 /* ADCHPD */ +#define WM8737_ADCHPD_WIDTH 1 /* ADCHPD */ + +/* + * R6 (0x06) - Power Management + */ +#define WM8737_VMID 0x0100 /* VMID */ +#define WM8737_VMID_MASK 0x0100 /* VMID */ +#define WM8737_VMID_SHIFT 8 /* VMID */ +#define WM8737_VMID_WIDTH 1 /* VMID */ +#define WM8737_VREF 0x0080 /* VREF */ +#define WM8737_VREF_MASK 0x0080 /* VREF */ +#define WM8737_VREF_SHIFT 7 /* VREF */ +#define WM8737_VREF_WIDTH 1 /* VREF */ +#define WM8737_AI 0x0040 /* AI */ +#define WM8737_AI_MASK 0x0040 /* AI */ +#define WM8737_AI_SHIFT 6 /* AI */ +#define WM8737_AI_WIDTH 1 /* AI */ +#define WM8737_PGL 0x0020 /* PGL */ +#define WM8737_PGL_MASK 0x0020 /* PGL */ +#define WM8737_PGL_SHIFT 5 /* PGL */ +#define WM8737_PGL_WIDTH 1 /* PGL */ +#define WM8737_PGR 0x0010 /* PGR */ +#define WM8737_PGR_MASK 0x0010 /* PGR */ +#define WM8737_PGR_SHIFT 4 /* PGR */ +#define WM8737_PGR_WIDTH 1 /* PGR */ +#define WM8737_ADL 0x0008 /* ADL */ +#define WM8737_ADL_MASK 0x0008 /* ADL */ +#define WM8737_ADL_SHIFT 3 /* ADL */ +#define WM8737_ADL_WIDTH 1 /* ADL */ +#define WM8737_ADR 0x0004 /* ADR */ +#define WM8737_ADR_MASK 0x0004 /* ADR */ +#define WM8737_ADR_SHIFT 2 /* ADR */ +#define WM8737_ADR_WIDTH 1 /* ADR */ +#define WM8737_MICBIAS_MASK 0x0003 /* MICBIAS - [1:0] */ +#define WM8737_MICBIAS_SHIFT 0 /* MICBIAS - [1:0] */ +#define WM8737_MICBIAS_WIDTH 2 /* MICBIAS - [1:0] */ + +/* + * R7 (0x07) - Audio Format + */ +#define WM8737_SDODIS 0x0080 /* SDODIS */ +#define WM8737_SDODIS_MASK 0x0080 /* SDODIS */ +#define WM8737_SDODIS_SHIFT 7 /* SDODIS */ +#define WM8737_SDODIS_WIDTH 1 /* SDODIS */ +#define WM8737_MS 0x0040 /* MS */ +#define WM8737_MS_MASK 0x0040 /* MS */ +#define WM8737_MS_SHIFT 6 /* MS */ +#define WM8737_MS_WIDTH 1 /* MS */ +#define WM8737_LRP 0x0010 /* LRP */ +#define WM8737_LRP_MASK 0x0010 /* LRP */ +#define WM8737_LRP_SHIFT 4 /* LRP */ +#define WM8737_LRP_WIDTH 1 /* LRP */ +#define WM8737_WL_MASK 0x000C /* WL - [3:2] */ +#define WM8737_WL_SHIFT 2 /* WL - [3:2] */ +#define WM8737_WL_WIDTH 2 /* WL - [3:2] */ +#define WM8737_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */ +#define WM8737_FORMAT_SHIFT 0 /* FORMAT - [1:0] */ +#define WM8737_FORMAT_WIDTH 2 /* FORMAT - [1:0] */ + +/* + * R8 (0x08) - Clocking + */ +#define WM8737_AUTODETECT 0x0080 /* AUTODETECT */ +#define WM8737_AUTODETECT_MASK 0x0080 /* AUTODETECT */ +#define WM8737_AUTODETECT_SHIFT 7 /* AUTODETECT */ +#define WM8737_AUTODETECT_WIDTH 1 /* AUTODETECT */ +#define WM8737_CLKDIV2 0x0040 /* CLKDIV2 */ +#define WM8737_CLKDIV2_MASK 0x0040 /* CLKDIV2 */ +#define WM8737_CLKDIV2_SHIFT 6 /* CLKDIV2 */ +#define WM8737_CLKDIV2_WIDTH 1 /* CLKDIV2 */ +#define WM8737_SR_MASK 0x003E /* SR - [5:1] */ +#define WM8737_SR_SHIFT 1 /* SR - [5:1] */ +#define WM8737_SR_WIDTH 5 /* SR - [5:1] */ +#define WM8737_USB_MODE 0x0001 /* USB MODE */ +#define WM8737_USB_MODE_MASK 0x0001 /* USB MODE */ +#define WM8737_USB_MODE_SHIFT 0 /* USB MODE */ +#define WM8737_USB_MODE_WIDTH 1 /* USB MODE */ + +/* + * R9 (0x09) - MIC Preamp Control + */ +#define WM8737_RBYPEN 0x0008 /* RBYPEN */ +#define WM8737_RBYPEN_MASK 0x0008 /* RBYPEN */ +#define WM8737_RBYPEN_SHIFT 3 /* RBYPEN */ +#define WM8737_RBYPEN_WIDTH 1 /* RBYPEN */ +#define WM8737_LBYPEN 0x0004 /* LBYPEN */ +#define WM8737_LBYPEN_MASK 0x0004 /* LBYPEN */ +#define WM8737_LBYPEN_SHIFT 2 /* LBYPEN */ +#define WM8737_LBYPEN_WIDTH 1 /* LBYPEN */ +#define WM8737_MBCTRL_MASK 0x0003 /* MBCTRL - [1:0] */ +#define WM8737_MBCTRL_SHIFT 0 /* MBCTRL - [1:0] */ +#define WM8737_MBCTRL_WIDTH 2 /* MBCTRL - [1:0] */ + +/* + * R10 (0x0A) - Misc Bias Control + */ +#define WM8737_VMIDSEL_MASK 0x000C /* VMIDSEL - [3:2] */ +#define WM8737_VMIDSEL_SHIFT 2 /* VMIDSEL - [3:2] */ +#define WM8737_VMIDSEL_WIDTH 2 /* VMIDSEL - [3:2] */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE 0x0002 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE_MASK 0x0002 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE_SHIFT 1 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE_WIDTH 1 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE 0x0001 /* RINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE_MASK 0x0001 /* RINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE_SHIFT 0 /* RINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE_WIDTH 1 /* RINPUT1 DC BIAS ENABLE */ + +/* + * R11 (0x0B) - Noise Gate + */ +#define WM8737_NGTH_MASK 0x001C /* NGTH - [4:2] */ +#define WM8737_NGTH_SHIFT 2 /* NGTH - [4:2] */ +#define WM8737_NGTH_WIDTH 3 /* NGTH - [4:2] */ +#define WM8737_NGAT 0x0001 /* NGAT */ +#define WM8737_NGAT_MASK 0x0001 /* NGAT */ +#define WM8737_NGAT_SHIFT 0 /* NGAT */ +#define WM8737_NGAT_WIDTH 1 /* NGAT */ + +/* + * R12 (0x0C) - ALC1 + */ +#define WM8737_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8737_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8737_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8737_MAX_GAIN_MASK 0x0070 /* MAX GAIN - [6:4] */ +#define WM8737_MAX_GAIN_SHIFT 4 /* MAX GAIN - [6:4] */ +#define WM8737_MAX_GAIN_WIDTH 3 /* MAX GAIN - [6:4] */ +#define WM8737_ALCL_MASK 0x000F /* ALCL - [3:0] */ +#define WM8737_ALCL_SHIFT 0 /* ALCL - [3:0] */ +#define WM8737_ALCL_WIDTH 4 /* ALCL - [3:0] */ + +/* + * R13 (0x0D) - ALC2 + */ +#define WM8737_ALCZCE 0x0010 /* ALCZCE */ +#define WM8737_ALCZCE_MASK 0x0010 /* ALCZCE */ +#define WM8737_ALCZCE_SHIFT 4 /* ALCZCE */ +#define WM8737_ALCZCE_WIDTH 1 /* ALCZCE */ +#define WM8737_HLD_MASK 0x000F /* HLD - [3:0] */ +#define WM8737_HLD_SHIFT 0 /* HLD - [3:0] */ +#define WM8737_HLD_WIDTH 4 /* HLD - [3:0] */ + +/* + * R14 (0x0E) - ALC3 + */ +#define WM8737_DCY_MASK 0x00F0 /* DCY - [7:4] */ +#define WM8737_DCY_SHIFT 4 /* DCY - [7:4] */ +#define WM8737_DCY_WIDTH 4 /* DCY - [7:4] */ +#define WM8737_ATK_MASK 0x000F /* ATK - [3:0] */ +#define WM8737_ATK_SHIFT 0 /* ATK - [3:0] */ +#define WM8737_ATK_WIDTH 4 /* ATK - [3:0] */ + +/* + * R15 (0x0F) - Reset + */ +#define WM8737_RESET_MASK 0x01FF /* RESET - [8:0] */ +#define WM8737_RESET_SHIFT 0 /* RESET - [8:0] */ +#define WM8737_RESET_WIDTH 9 /* RESET - [8:0] */ + +#endif diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 90e31e9aa6f7..25af901fe813 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -41,7 +40,6 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { /* codec private data */ struct wm8741_priv { enum snd_soc_control_type control_type; - u16 reg_cache[WM8741_REGISTER_COUNT]; struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; unsigned int sysclk; struct snd_pcm_hw_constraint_list *sysclk_constraints; @@ -95,10 +93,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int wm8741_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8741_dapm_widgets, - ARRAY_SIZE(wm8741_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_new_controls(dapm, wm8741_dapm_widgets, + ARRAY_SIZE(wm8741_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); return 0; } @@ -437,10 +436,14 @@ static int wm8741_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU */ - wm8741->reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL; - wm8741->reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM; - wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL; - wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM; + snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, + WM8741_UPDATELL, WM8741_UPDATELL); + snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, + WM8741_UPDATELM, WM8741_UPDATELM); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERL, WM8741_UPDATERL); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERM, WM8741_UPDATERM); snd_soc_add_controls(codec, wm8741_snd_controls, ARRAY_SIZE(wm8741_snd_controls)); @@ -455,7 +458,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = { .resume = wm8741_resume, .reg_cache_size = ARRAY_SIZE(wm8741_reg_defaults), .reg_word_size = sizeof(u16), - .reg_cache_default = &wm8741_reg_defaults, + .reg_cache_default = wm8741_reg_defaults, }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 6c924cd2cfd4..38f38fddd190 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -25,7 +25,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "wm8750.h" @@ -53,7 +52,6 @@ static const u16 wm8750_reg[] = { struct wm8750_priv { unsigned int sysclk; enum snd_soc_control_type control_type; - u16 reg_cache[ARRAY_SIZE(wm8750_reg)]; }; #define wm8750_reset(c) snd_soc_write(c, WM8750_RESET, 0) @@ -399,10 +397,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8750_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, - ARRAY_SIZE(wm8750_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, + ARRAY_SIZE(wm8750_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -615,7 +614,7 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Set VMID to 5k */ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1); @@ -630,7 +629,7 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8750_PWR1, 0x0001); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 8f679a13f2bc..ffa2ffe5ec11 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -45,7 +45,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <asm/div64.h> @@ -56,8 +55,10 @@ static int caps_charge = 2000; module_param(caps_charge, int, 0); MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)"); -static void wm8753_set_dai_mode(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, unsigned int hifi); +static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt); +static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt); /* * wm8753 register cache @@ -65,22 +66,22 @@ static void wm8753_set_dai_mode(struct snd_soc_codec *codec, * are using 2 wire for device control, so we cache them instead. */ static const u16 wm8753_reg[] = { - 0x0008, 0x0000, 0x000a, 0x000a, - 0x0033, 0x0000, 0x0007, 0x00ff, - 0x00ff, 0x000f, 0x000f, 0x007b, - 0x0000, 0x0032, 0x0000, 0x00c3, - 0x00c3, 0x00c0, 0x0000, 0x0000, + 0x0000, 0x0008, 0x0000, 0x000a, + 0x000a, 0x0033, 0x0000, 0x0007, + 0x00ff, 0x00ff, 0x000f, 0x000f, + 0x007b, 0x0000, 0x0032, 0x0000, + 0x00c3, 0x00c3, 0x00c0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0055, - 0x0005, 0x0050, 0x0055, 0x0050, - 0x0055, 0x0050, 0x0055, 0x0079, - 0x0079, 0x0079, 0x0079, 0x0079, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0097, 0x0097, 0x0000, 0x0004, - 0x0000, 0x0083, 0x0024, 0x01ba, - 0x0000, 0x0083, 0x0024, 0x01ba, - 0x0000, 0x0000, 0x0000 + 0x0055, 0x0005, 0x0050, 0x0055, + 0x0050, 0x0055, 0x0050, 0x0055, + 0x0079, 0x0079, 0x0079, 0x0079, + 0x0079, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0097, 0x0097, 0x0000, + 0x0004, 0x0000, 0x0083, 0x0024, + 0x01ba, 0x0000, 0x0083, 0x0024, + 0x01ba, 0x0000, 0x0000, 0x0000 }; /* codec private data */ @@ -88,57 +89,14 @@ struct wm8753_priv { enum snd_soc_control_type control_type; unsigned int sysclk; unsigned int pcmclk; - u16 reg_cache[ARRAY_SIZE(wm8753_reg)]; - int dai_func; -}; - -/* - * read wm8753 register cache - */ -static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - if (reg < 1 || reg >= (ARRAY_SIZE(wm8753_reg) + 1)) - return -1; - return cache[reg - 1]; -} - -/* - * write wm8753 register cache - */ -static inline void wm8753_write_reg_cache(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - u16 *cache = codec->reg_cache; - if (reg < 1 || reg >= (ARRAY_SIZE(wm8753_reg) + 1)) - return; - cache[reg - 1] = value; -} - -/* - * write to the WM8753 register space - */ -static int wm8753_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - /* data is - * D15..D9 WM8753 register offset - * D8...D0 register data - */ - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; + unsigned int voice_fmt; + unsigned int hifi_fmt; - wm8753_write_reg_cache(codec, reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; -} + int dai_func; +}; -#define wm8753_reset(c) wm8753_write(c, WM8753_RESET, 0) +#define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0) /* * WM8753 Controls @@ -218,9 +176,9 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL); + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = (mode & 0xc) >> 2; + ucontrol->value.integer.value[0] = wm8753->dai_func; return 0; } @@ -228,16 +186,26 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL); struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + u16 ioctl; - if (((mode & 0xc) >> 2) == ucontrol->value.integer.value[0]) - return 0; + if (codec->active) + return -EBUSY; - mode &= 0xfff3; - mode |= (ucontrol->value.integer.value[0] << 2); + ioctl = snd_soc_read(codec, WM8753_IOCTL); + + wm8753->dai_func = ucontrol->value.integer.value[0]; + + if (((ioctl >> 2) & 0x3) == wm8753->dai_func) + return 1; + + ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2); + snd_soc_write(codec, WM8753_IOCTL, ioctl); + + + wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt); + wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt); - wm8753->dai_func = ucontrol->value.integer.value[0]; return 1; } @@ -670,10 +638,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8753_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets, + ARRAY_SIZE(wm8753_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -738,17 +707,17 @@ static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (pll_id == WM8753_PLL1) { offset = 0; enable = 0x10; - reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xffef; + reg = snd_soc_read(codec, WM8753_CLOCK) & 0xffef; } else { offset = 4; enable = 0x8; - reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfff7; + reg = snd_soc_read(codec, WM8753_CLOCK) & 0xfff7; } if (!freq_in || !freq_out) { /* disable PLL */ - wm8753_write(codec, WM8753_PLL1CTL1 + offset, 0x0026); - wm8753_write(codec, WM8753_CLOCK, reg); + snd_soc_write(codec, WM8753_PLL1CTL1 + offset, 0x0026); + snd_soc_write(codec, WM8753_CLOCK, reg); return 0; } else { u16 value = 0; @@ -759,20 +728,20 @@ static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, /* set up N and K PLL divisor ratios */ /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */ value = (pll_div.n << 5) + ((pll_div.k & 0x3c0000) >> 18); - wm8753_write(codec, WM8753_PLL1CTL2 + offset, value); + snd_soc_write(codec, WM8753_PLL1CTL2 + offset, value); /* bits 8:0 = PLL_K[17:9] */ value = (pll_div.k & 0x03fe00) >> 9; - wm8753_write(codec, WM8753_PLL1CTL3 + offset, value); + snd_soc_write(codec, WM8753_PLL1CTL3 + offset, value); /* bits 8:0 = PLL_K[8:0] */ value = pll_div.k & 0x0001ff; - wm8753_write(codec, WM8753_PLL1CTL4 + offset, value); + snd_soc_write(codec, WM8753_PLL1CTL4 + offset, value); /* set PLL as input and enable */ - wm8753_write(codec, WM8753_PLL1CTL1 + offset, 0x0027 | + snd_soc_write(codec, WM8753_PLL1CTL1 + offset, 0x0027 | (pll_div.div2 << 3)); - wm8753_write(codec, WM8753_CLOCK, reg | enable); + snd_soc_write(codec, WM8753_CLOCK, reg | enable); } return 0; } @@ -875,11 +844,10 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai, /* * Set's ADC and Voice DAC format. */ -static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; - u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01ec; + u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec; /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -901,14 +869,7 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8753_write(codec, WM8753_PCM, voice); - return 0; -} - -static int wm8753_pcm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - wm8753_set_dai_mode(dai->codec, dai, 0); + snd_soc_write(codec, WM8753_PCM, voice); return 0; } @@ -922,8 +883,8 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3; - u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f; + u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01f3; + u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x017f; /* bit size */ switch (params_format(params)) { @@ -943,23 +904,22 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, /* sample rate */ if (params_rate(params) * 384 == wm8753->pcmclk) srate |= 0x80; - wm8753_write(codec, WM8753_SRATE1, srate); + snd_soc_write(codec, WM8753_SRATE1, srate); - wm8753_write(codec, WM8753_PCM, voice); + snd_soc_write(codec, WM8753_PCM, voice); return 0; } /* * Set's PCM dai fmt and BCLK. */ -static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 voice, ioctl; - voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x011f; - ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x015d; + voice = snd_soc_read(codec, WM8753_PCM) & 0x011f; + ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x015d; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1013,8 +973,8 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8753_write(codec, WM8753_PCM, voice); - wm8753_write(codec, WM8753_IOCTL, ioctl); + snd_soc_write(codec, WM8753_PCM, voice); + snd_soc_write(codec, WM8753_IOCTL, ioctl); return 0; } @@ -1026,16 +986,16 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai, switch (div_id) { case WM8753_PCMDIV: - reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x003f; - wm8753_write(codec, WM8753_CLOCK, reg | div); + reg = snd_soc_read(codec, WM8753_CLOCK) & 0x003f; + snd_soc_write(codec, WM8753_CLOCK, reg | div); break; case WM8753_BCLKDIV: - reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x01c7; - wm8753_write(codec, WM8753_SRATE2, reg | div); + reg = snd_soc_read(codec, WM8753_SRATE2) & 0x01c7; + snd_soc_write(codec, WM8753_SRATE2, reg | div); break; case WM8753_VXCLKDIV: - reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x003f; - wm8753_write(codec, WM8753_SRATE2, reg | div); + reg = snd_soc_read(codec, WM8753_SRATE2) & 0x003f; + snd_soc_write(codec, WM8753_SRATE2, reg | div); break; default: return -EINVAL; @@ -1046,11 +1006,10 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai, /* * Set's HiFi DAC format. */ -static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; - u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01e0; + u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0; /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -1072,21 +1031,20 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8753_write(codec, WM8753_HIFI, hifi); + snd_soc_write(codec, WM8753_HIFI, hifi); return 0; } /* * Set's I2S DAI format. */ -static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 ioctl, hifi; - hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x011f; - ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x00ae; + hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f; + ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x00ae; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1140,15 +1098,8 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8753_write(codec, WM8753_HIFI, hifi); - wm8753_write(codec, WM8753_IOCTL, ioctl); - return 0; -} - -static int wm8753_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - wm8753_set_dai_mode(dai->codec, dai, 1); + snd_soc_write(codec, WM8753_HIFI, hifi); + snd_soc_write(codec, WM8753_IOCTL, ioctl); return 0; } @@ -1162,8 +1113,8 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0; - u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3; + u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x01c0; + u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01f3; int coeff; /* is digital filter coefficient valid ? */ @@ -1172,7 +1123,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, printk(KERN_ERR "wm8753 invalid MCLK or rate\n"); return coeff; } - wm8753_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) | + snd_soc_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); /* bit size */ @@ -1190,81 +1141,137 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, break; } - wm8753_write(codec, WM8753_HIFI, hifi); + snd_soc_write(codec, WM8753_HIFI, hifi); return 0; } -static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ - clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb; - wm8753_write(codec, WM8753_CLOCK, clock); + clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; + snd_soc_write(codec, WM8753_CLOCK, clock); - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_pcm_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } -static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_hdac_set_dai_fmt(codec, fmt); } -static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ - clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb; - wm8753_write(codec, WM8753_CLOCK, clock); + clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; + snd_soc_write(codec, WM8753_CLOCK, clock); - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } -static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as mclk */ - clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb; - wm8753_write(codec, WM8753_CLOCK, clock | 0x4); + clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; + snd_soc_write(codec, WM8753_CLOCK, clock | 0x4); - if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) + if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0) return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } +static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (wm8753->dai_func) { + case 0: + ret = wm8753_mode1h_set_dai_fmt(codec, fmt); + break; + case 1: + ret = wm8753_mode2_set_dai_fmt(codec, fmt); + break; + case 2: + case 3: + ret = wm8753_mode3_4_set_dai_fmt(codec, fmt); + break; + default: + break; + } + if (ret) + return ret; + + return wm8753_i2s_set_dai_fmt(codec, fmt); +} + +static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + + wm8753->hifi_fmt = fmt; + + return wm8753_hifi_write_dai_fmt(codec, fmt); +}; + +static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (wm8753->dai_func != 0) + return 0; + + ret = wm8753_mode1v_set_dai_fmt(codec, fmt); + if (ret) + return ret; + ret = wm8753_pcm_set_dai_fmt(codec, fmt); + if (ret) + return ret; + + return 0; +}; + +static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + + wm8753->voice_fmt = fmt; + + return wm8753_voice_write_dai_fmt(codec, fmt); +}; + static int wm8753_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7; + u16 mute_reg = snd_soc_read(codec, WM8753_DAC) & 0xfff7; struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); /* the digital mute covers the HiFi and Voice DAC's on the WM8753. * make sure we check if they are not both active when we mute */ if (mute && wm8753->dai_func == 1) { if (!codec->active) - wm8753_write(codec, WM8753_DAC, mute_reg | 0x8); + snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8); } else { if (mute) - wm8753_write(codec, WM8753_DAC, mute_reg | 0x8); + snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8); else - wm8753_write(codec, WM8753_DAC, mute_reg); + snd_soc_write(codec, WM8753_DAC, mute_reg); } return 0; @@ -1273,26 +1280,26 @@ static int wm8753_mute(struct snd_soc_dai *dai, int mute) static int wm8753_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e; + u16 pwr_reg = snd_soc_read(codec, WM8753_PWR1) & 0xfe3e; switch (level) { case SND_SOC_BIAS_ON: /* set vmid to 50k and unmute dac */ - wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); + snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); break; case SND_SOC_BIAS_PREPARE: /* set vmid to 5k for quick power up */ - wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); + snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); break; case SND_SOC_BIAS_STANDBY: /* mute dac and set vmid to 500k, enable VREF */ - wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141); + snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141); break; case SND_SOC_BIAS_OFF: - wm8753_write(codec, WM8753_PWR1, 0x0001); + snd_soc_write(codec, WM8753_PWR1, 0x0001); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1305,7 +1312,7 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, SNDRV_PCM_FMTBIT_S24_LE) /* - * The WM8753 supports upto 4 different and mutually exclusive DAI + * The WM8753 supports up to 4 different and mutually exclusive DAI * configurations. This gives 2 PCM's available for use, hifi and voice. * NOTE: The Voice PCM cannot play or capture audio to the CPU as it's DAI * is connected between the wm8753 and a BT codec or GSM modem. @@ -1315,57 +1322,25 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, * 3. Voice disabled - HIFI over HIFI * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture */ -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = { - .startup = wm8753_i2s_startup, +static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = { .hw_params = wm8753_i2s_hw_params, .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode1h_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = { - .startup = wm8753_pcm_startup, - .hw_params = wm8753_pcm_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode1v_set_dai_fmt, + .set_fmt = wm8753_hifi_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, }; -static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = { - .startup = wm8753_pcm_startup, +static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = { .hw_params = wm8753_pcm_hw_params, .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode2_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3 = { - .startup = wm8753_i2s_startup, - .hw_params = wm8753_i2s_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode3_4_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4 = { - .startup = wm8753_i2s_startup, - .hw_params = wm8753_i2s_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode3_4_set_dai_fmt, + .set_fmt = wm8753_voice_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, }; -static struct snd_soc_dai_driver wm8753_all_dai[] = { +static struct snd_soc_dai_driver wm8753_dai[] = { /* DAI HiFi mode 1 */ { .name = "wm8753-hifi", .playback = { @@ -1373,14 +1348,16 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = { .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS}, + .formats = WM8753_FORMATS + }, .capture = { /* dummy for fast DAI switching */ .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS}, - .ops = &wm8753_dai_ops_hifi_mode1, + .formats = WM8753_FORMATS + }, + .ops = &wm8753_dai_ops_hifi_mode, }, /* DAI Voice mode 1 */ { .name = "wm8753-voice", @@ -1389,102 +1366,26 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = { .channels_min = 1, .channels_max = 1, .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_voice_mode1, -}, -/* DAI HiFi mode 2 - dummy */ -{ .name = "wm8753-hifi", -}, -/* DAI Voice mode 2 */ -{ .name = "wm8753-voice", - .playback = { - .stream_name = "Voice Playback", - .channels_min = 1, - .channels_max = 1, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_voice_mode2, -}, -/* DAI HiFi mode 3 */ -{ .name = "wm8753-hifi", - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_hifi_mode3, -}, -/* DAI Voice mode 3 - dummy */ -{ .name = "wm8753-voice", -}, -/* DAI HiFi mode 4 */ -{ .name = "wm8753-hifi", - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, + .formats = WM8753_FORMATS, + }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_hifi_mode4, -}, -/* DAI Voice mode 4 - dummy */ -{ .name = "wm8753-voice", -}, -}; - -static struct snd_soc_dai_driver wm8753_dai[] = { - { - .name = "wm8753-aif0", - }, - { - .name = "wm8753-aif1", + .formats = WM8753_FORMATS, }, + .ops = &wm8753_dai_ops_voice_mode, +}, }; -static void wm8753_set_dai_mode(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, unsigned int hifi) -{ - struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - - if (wm8753->dai_func < 4) { - if (hifi) - dai->driver = &wm8753_all_dai[wm8753->dai_func << 1]; - else - dai->driver = &wm8753_all_dai[(wm8753->dai_func << 1) + 1]; - } - wm8753_write(codec, WM8753_IOCTL, wm8753->dai_func); -} - static void wm8753_work(struct work_struct *work) { - struct snd_soc_codec *codec = - container_of(work, struct snd_soc_codec, delayed_work.work); - wm8753_set_bias_level(codec, codec->bias_level); + struct snd_soc_dapm_context *dapm = + container_of(work, struct snd_soc_dapm_context, + delayed_work.work); + struct snd_soc_codec *codec = dapm->codec; + wm8753_set_bias_level(codec, dapm->bias_level); } static int wm8753_suspend(struct snd_soc_codec *codec, pm_message_t state) @@ -1495,62 +1396,40 @@ static int wm8753_suspend(struct snd_soc_codec *codec, pm_message_t state) static int wm8753_resume(struct snd_soc_codec *codec) { + u16 *reg_cache = codec->reg_cache; int i; - u8 data[2]; - u16 *cache = codec->reg_cache; /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) { - if (i + 1 == WM8753_RESET) + for (i = 1; i < ARRAY_SIZE(wm8753_reg); i++) { + if (i == WM8753_RESET) continue; /* No point in writing hardware default values back */ - if (cache[i] == wm8753_reg[i]) + if (reg_cache[i] == wm8753_reg[i]) continue; - data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001); - data[1] = cache[i] & 0x00ff; - codec->hw_write(codec->control_data, data, 2); + snd_soc_write(codec, i, reg_cache[i]); } wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* charge wm8753 caps */ - if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - codec->bias_level = SND_SOC_BIAS_ON; - schedule_delayed_work(&codec->delayed_work, + codec->dapm.bias_level = SND_SOC_BIAS_ON; + schedule_delayed_work(&codec->dapm.delayed_work, msecs_to_jiffies(caps_charge)); } return 0; } -/* - * This function forces any delayed work to be queued and run. - */ -static int run_delayed_work(struct delayed_work *dwork) -{ - int ret; - - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(dwork); - - /* if there was any work waiting then we run it now and - * wait for it's completion */ - if (ret) { - schedule_delayed_work(dwork, 0); - flush_scheduled_work(); - } - return ret; -} - static int wm8753_probe(struct snd_soc_codec *codec) { struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - int ret = 0, reg; + int ret; - INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work); + INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8753_work); ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8753->control_type); if (ret < 0) { @@ -1569,30 +1448,20 @@ static int wm8753_probe(struct snd_soc_codec *codec) /* charge output caps */ wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - schedule_delayed_work(&codec->delayed_work, + schedule_delayed_work(&codec->dapm.delayed_work, msecs_to_jiffies(caps_charge)); /* set the update bits */ - reg = wm8753_read_reg_cache(codec, WM8753_LDAC); - wm8753_write(codec, WM8753_LDAC, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_RDAC); - wm8753_write(codec, WM8753_RDAC, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_LADC); - wm8753_write(codec, WM8753_LADC, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_RADC); - wm8753_write(codec, WM8753_RADC, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V); - wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V); - wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V); - wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V); - wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_LINVOL); - wm8753_write(codec, WM8753_LINVOL, reg | 0x0100); - reg = wm8753_read_reg_cache(codec, WM8753_RINVOL); - wm8753_write(codec, WM8753_RINVOL, reg | 0x0100); + snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_LOUT1V, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_ROUT1V, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_LOUT2V, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_ROUT2V, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_LINVOL, 0x0100, 0x0100); + snd_soc_update_bits(codec, WM8753_RINVOL, 0x0100, 0x0100); snd_soc_add_controls(codec, wm8753_snd_controls, ARRAY_SIZE(wm8753_snd_controls)); @@ -1604,7 +1473,7 @@ static int wm8753_probe(struct snd_soc_codec *codec) /* power down chip */ static int wm8753_remove(struct snd_soc_codec *codec) { - run_delayed_work(&codec->delayed_work); + flush_delayed_work_sync(&codec->dapm.delayed_work); wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c new file mode 100644 index 000000000000..19b92baa9e8c --- /dev/null +++ b/sound/soc/codecs/wm8770.c @@ -0,0 +1,749 @@ +/* + * wm8770.c -- WM8770 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos <dp@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/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8770.h" + +#define WM8770_NUM_SUPPLIES 3 +static const char *wm8770_supply_names[WM8770_NUM_SUPPLIES] = { + "AVDD1", + "AVDD2", + "DVDD" +}; + +static const u16 wm8770_reg_defs[WM8770_CACHEREGNUM] = { + 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0, 0x90, 0, + 0, 0x22, 0x22, 0x3e, + 0xc, 0xc, 0x100, 0x189, + 0x189, 0x8770 +}; + +struct wm8770_priv { + enum snd_soc_control_type control_type; + struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8770_NUM_SUPPLIES]; + struct snd_soc_codec *codec; + int sysclk; +}; + +static int vout12supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int vout34supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +/* + * 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 WM8770_REGULATOR_EVENT(n) \ +static int wm8770_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + wm8770->codec->cache_sync = 1; \ + } \ + return 0; \ +} + +WM8770_REGULATOR_EVENT(0) +WM8770_REGULATOR_EVENT(1) +WM8770_REGULATOR_EVENT(2) + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(dac_alg_tlv, -12700, 100, 1); + +static const char *dac_phase_text[][2] = { + { "DAC1 Normal", "DAC1 Inverted" }, + { "DAC2 Normal", "DAC2 Inverted" }, + { "DAC3 Normal", "DAC3 Inverted" }, + { "DAC4 Normal", "DAC4 Inverted" }, +}; + +static const struct soc_enum dac_phase[] = { + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 0, 1, 2, dac_phase_text[0]), + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 2, 3, 2, dac_phase_text[1]), + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 4, 5, 2, dac_phase_text[2]), + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 6, 7, 2, dac_phase_text[3]), +}; + +static const struct snd_kcontrol_new wm8770_snd_controls[] = { + /* global DAC playback controls */ + SOC_SINGLE_TLV("DAC Playback Volume", WM8770_MSDIGVOL, 0, 255, 0, + dac_dig_tlv), + SOC_SINGLE("DAC Playback Switch", WM8770_DACMUTE, 4, 1, 1), + SOC_SINGLE("DAC Playback ZC Switch", WM8770_DACCTRL1, 0, 1, 0), + + /* global VOUT playback controls */ + SOC_SINGLE_TLV("VOUT Playback Volume", WM8770_MSALGVOL, 0, 127, 0, + dac_alg_tlv), + SOC_SINGLE("VOUT Playback ZC Switch", WM8770_MSALGVOL, 7, 1, 0), + + /* VOUT1/2/3/4 specific controls */ + SOC_DOUBLE_R_TLV("VOUT1 Playback Volume", WM8770_VOUT1LVOL, + WM8770_VOUT1RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT1 Playback ZC Switch", WM8770_VOUT1LVOL, + WM8770_VOUT1RVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("VOUT2 Playback Volume", WM8770_VOUT2LVOL, + WM8770_VOUT2RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT2 Playback ZC Switch", WM8770_VOUT2LVOL, + WM8770_VOUT2RVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("VOUT3 Playback Volume", WM8770_VOUT3LVOL, + WM8770_VOUT3RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT3 Playback ZC Switch", WM8770_VOUT3LVOL, + WM8770_VOUT3RVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("VOUT4 Playback Volume", WM8770_VOUT4LVOL, + WM8770_VOUT4RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT4 Playback ZC Switch", WM8770_VOUT4LVOL, + WM8770_VOUT4RVOL, 7, 1, 0), + + /* DAC1/2/3/4 specific controls */ + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", WM8770_DAC1LVOL, + WM8770_DAC1RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC1 Deemphasis Switch", WM8770_DACCTRL2, 0, 1, 0), + SOC_ENUM("DAC1 Phase", dac_phase[0]), + SOC_DOUBLE_R_TLV("DAC2 Playback Volume", WM8770_DAC2LVOL, + WM8770_DAC2RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC2 Deemphasis Switch", WM8770_DACCTRL2, 1, 1, 0), + SOC_ENUM("DAC2 Phase", dac_phase[1]), + SOC_DOUBLE_R_TLV("DAC3 Playback Volume", WM8770_DAC3LVOL, + WM8770_DAC3RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC3 Deemphasis Switch", WM8770_DACCTRL2, 2, 1, 0), + SOC_ENUM("DAC3 Phase", dac_phase[2]), + SOC_DOUBLE_R_TLV("DAC4 Playback Volume", WM8770_DAC4LVOL, + WM8770_DAC4RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC4 Deemphasis Switch", WM8770_DACCTRL2, 3, 1, 0), + SOC_ENUM("DAC4 Phase", dac_phase[3]), + + /* ADC specific controls */ + SOC_DOUBLE_R_TLV("Capture Volume", WM8770_ADCLCTRL, WM8770_ADCRCTRL, + 0, 31, 0, adc_tlv), + SOC_DOUBLE_R("Capture Switch", WM8770_ADCLCTRL, WM8770_ADCRCTRL, + 5, 1, 1), + + /* other controls */ + SOC_SINGLE("ADC 128x Oversampling Switch", WM8770_MSTRCTRL, 3, 1, 0), + SOC_SINGLE("ADC Highpass Filter Switch", WM8770_IFACECTRL, 8, 1, 1) +}; + +static const char *ain_text[] = { + "AIN1", "AIN2", "AIN3", "AIN4", + "AIN5", "AIN6", "AIN7", "AIN8" +}; + +static const struct soc_enum ain_enum = + SOC_ENUM_DOUBLE(WM8770_ADCMUX, 0, 4, 8, ain_text); + +static const struct snd_kcontrol_new ain_mux = + SOC_DAPM_ENUM("Capture Mux", ain_enum); + +static const struct snd_kcontrol_new vout1_mix_controls[] = { + SOC_DAPM_SINGLE("DAC1 Switch", WM8770_OUTMUX1, 0, 1, 0), + SOC_DAPM_SINGLE("AUX1 Switch", WM8770_OUTMUX1, 1, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 2, 1, 0) +}; + +static const struct snd_kcontrol_new vout2_mix_controls[] = { + SOC_DAPM_SINGLE("DAC2 Switch", WM8770_OUTMUX1, 3, 1, 0), + SOC_DAPM_SINGLE("AUX2 Switch", WM8770_OUTMUX1, 4, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 5, 1, 0) +}; + +static const struct snd_kcontrol_new vout3_mix_controls[] = { + SOC_DAPM_SINGLE("DAC3 Switch", WM8770_OUTMUX2, 0, 1, 0), + SOC_DAPM_SINGLE("AUX3 Switch", WM8770_OUTMUX2, 1, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 2, 1, 0) +}; + +static const struct snd_kcontrol_new vout4_mix_controls[] = { + SOC_DAPM_SINGLE("DAC4 Switch", WM8770_OUTMUX2, 3, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 4, 1, 0) +}; + +static const struct snd_soc_dapm_widget wm8770_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AUX1"), + SND_SOC_DAPM_INPUT("AUX2"), + SND_SOC_DAPM_INPUT("AUX3"), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + SND_SOC_DAPM_INPUT("AIN5"), + SND_SOC_DAPM_INPUT("AIN6"), + SND_SOC_DAPM_INPUT("AIN7"), + SND_SOC_DAPM_INPUT("AIN8"), + + SND_SOC_DAPM_MUX("Capture Mux", WM8770_ADCMUX, 8, 1, &ain_mux), + + SND_SOC_DAPM_ADC("ADC", "Capture", WM8770_PWDNCTRL, 1, 1), + + SND_SOC_DAPM_DAC("DAC1", "Playback", WM8770_PWDNCTRL, 2, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", WM8770_PWDNCTRL, 3, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", WM8770_PWDNCTRL, 4, 1), + SND_SOC_DAPM_DAC("DAC4", "Playback", WM8770_PWDNCTRL, 5, 1), + + SND_SOC_DAPM_SUPPLY("VOUT12 Supply", SND_SOC_NOPM, 0, 0, + vout12supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VOUT34 Supply", SND_SOC_NOPM, 0, 0, + vout34supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("VOUT1 Mixer", SND_SOC_NOPM, 0, 0, + vout1_mix_controls, ARRAY_SIZE(vout1_mix_controls)), + SND_SOC_DAPM_MIXER("VOUT2 Mixer", SND_SOC_NOPM, 0, 0, + vout2_mix_controls, ARRAY_SIZE(vout2_mix_controls)), + SND_SOC_DAPM_MIXER("VOUT3 Mixer", SND_SOC_NOPM, 0, 0, + vout3_mix_controls, ARRAY_SIZE(vout3_mix_controls)), + SND_SOC_DAPM_MIXER("VOUT4 Mixer", SND_SOC_NOPM, 0, 0, + vout4_mix_controls, ARRAY_SIZE(vout4_mix_controls)), + + SND_SOC_DAPM_OUTPUT("VOUT1"), + SND_SOC_DAPM_OUTPUT("VOUT2"), + SND_SOC_DAPM_OUTPUT("VOUT3"), + SND_SOC_DAPM_OUTPUT("VOUT4") +}; + +static const struct snd_soc_dapm_route wm8770_intercon[] = { + { "Capture Mux", "AIN1", "AIN1" }, + { "Capture Mux", "AIN2", "AIN2" }, + { "Capture Mux", "AIN3", "AIN3" }, + { "Capture Mux", "AIN4", "AIN4" }, + { "Capture Mux", "AIN5", "AIN5" }, + { "Capture Mux", "AIN6", "AIN6" }, + { "Capture Mux", "AIN7", "AIN7" }, + { "Capture Mux", "AIN8", "AIN8" }, + + { "ADC", NULL, "Capture Mux" }, + + { "VOUT1 Mixer", NULL, "VOUT12 Supply" }, + { "VOUT1 Mixer", "DAC1 Switch", "DAC1" }, + { "VOUT1 Mixer", "AUX1 Switch", "AUX1" }, + { "VOUT1 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT2 Mixer", NULL, "VOUT12 Supply" }, + { "VOUT2 Mixer", "DAC2 Switch", "DAC2" }, + { "VOUT2 Mixer", "AUX2 Switch", "AUX2" }, + { "VOUT2 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT3 Mixer", NULL, "VOUT34 Supply" }, + { "VOUT3 Mixer", "DAC3 Switch", "DAC3" }, + { "VOUT3 Mixer", "AUX3 Switch", "AUX3" }, + { "VOUT3 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT4 Mixer", NULL, "VOUT34 Supply" }, + { "VOUT4 Mixer", "DAC4 Switch", "DAC4" }, + { "VOUT4 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT1", NULL, "VOUT1 Mixer" }, + { "VOUT2", NULL, "VOUT2 Mixer" }, + { "VOUT3", NULL, "VOUT3 Mixer" }, + { "VOUT4", NULL, "VOUT4 Mixer" } +}; + +static int vout12supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec; + + codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WM8770_OUTMUX1, 0x180, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WM8770_OUTMUX1, 0x180, 0x180); + break; + } + + return 0; +} + +static int vout34supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec; + + codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WM8770_OUTMUX2, 0x180, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WM8770_OUTMUX2, 0x180, 0x180); + break; + } + + return 0; +} + +static int wm8770_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, WM8770_RESET, 0); +} + +static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec; + int iface, master; + + codec = dai->codec; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master = 0x100; + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + return -EINVAL; + } + + iface = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0xc; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x8; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x4; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8770_IFACECTRL, 0xf, iface); + snd_soc_update_bits(codec, WM8770_MSTRCTRL, 0x100, master); + + return 0; +} + +static const int mclk_ratios[] = { + 128, + 192, + 256, + 384, + 512, + 768 +}; + +static int wm8770_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec; + struct wm8770_priv *wm8770; + int i; + int iface; + int shift; + int ratio; + + codec = dai->codec; + wm8770 = snd_soc_codec_get_drvdata(codec); + + iface = 0; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x10; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x20; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x30; + break; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + i = 0; + shift = 4; + break; + case SNDRV_PCM_STREAM_CAPTURE: + i = 2; + shift = 0; + break; + default: + return -EINVAL; + } + + /* Only need to set MCLK/LRCLK ratio if we're master */ + if (snd_soc_read(codec, WM8770_MSTRCTRL) & 0x100) { + for (; i < ARRAY_SIZE(mclk_ratios); ++i) { + ratio = wm8770->sysclk / params_rate(params); + if (ratio == mclk_ratios[i]) + break; + } + + if (i == ARRAY_SIZE(mclk_ratios)) { + dev_err(codec->dev, + "Unable to configure MCLK ratio %d/%d\n", + wm8770->sysclk, params_rate(params)); + return -EINVAL; + } + + dev_dbg(codec->dev, "MCLK is %dfs\n", mclk_ratios[i]); + + snd_soc_update_bits(codec, WM8770_MSTRCTRL, 0x7 << shift, + i << shift); + } + + snd_soc_update_bits(codec, WM8770_IFACECTRL, 0x30, iface); + + return 0; +} + +static int wm8770_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec; + + codec = dai->codec; + return snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10, + !!mute << 4); +} + +static int wm8770_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec; + struct wm8770_priv *wm8770; + + codec = dai->codec; + wm8770 = snd_soc_codec_get_drvdata(codec); + wm8770->sysclk = freq; + return 0; +} + +static void wm8770_sync_cache(struct snd_soc_codec *codec) +{ + int i; + u16 *cache; + + if (!codec->cache_sync) + return; + + codec->cache_only = 0; + cache = codec->reg_cache; + for (i = 0; i < codec->driver->reg_cache_size; i++) { + if (i == WM8770_RESET || cache[i] == wm8770_reg_defs[i]) + continue; + snd_soc_write(codec, i, cache[i]); + } + codec->cache_sync = 0; +} + +static int wm8770_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + struct wm8770_priv *wm8770; + + wm8770 = snd_soc_codec_get_drvdata(codec); + + 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 = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + if (ret) { + dev_err(codec->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + wm8770_sync_cache(codec); + /* global powerup */ + snd_soc_write(codec, WM8770_PWDNCTRL, 0); + } + break; + case SND_SOC_BIAS_OFF: + /* global powerdown */ + snd_soc_write(codec, WM8770_PWDNCTRL, 1); + regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#define WM8770_FORMATS (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 wm8770_dai_ops = { + .digital_mute = wm8770_mute, + .hw_params = wm8770_hw_params, + .set_fmt = wm8770_set_fmt, + .set_sysclk = wm8770_set_sysclk, +}; + +static struct snd_soc_dai_driver wm8770_dai = { + .name = "wm8770-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = WM8770_FORMATS + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8770_FORMATS + }, + .ops = &wm8770_dai_ops, + .symmetric_rates = 1 +}; + +#ifdef CONFIG_PM +static int wm8770_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + wm8770_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8770_resume(struct snd_soc_codec *codec) +{ + wm8770_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define wm8770_suspend NULL +#define wm8770_resume NULL +#endif + +static int wm8770_probe(struct snd_soc_codec *codec) +{ + struct wm8770_priv *wm8770; + int ret; + int i; + + wm8770 = snd_soc_codec_get_drvdata(codec); + wm8770->codec = codec; + + codec->dapm.idle_bias_off = 1; + + ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8770->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) + wm8770->supplies[i].supply = wm8770_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + if (ret) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0; + wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1; + wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { + ret = regulator_register_notifier(wm8770->supplies[i].consumer, + &wm8770->disable_nb[i]); + if (ret) { + dev_err(codec->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_reg_get; + } + + ret = wm8770_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + + wm8770_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* latch the volume update bits */ + snd_soc_update_bits(codec, WM8770_MSDIGVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_MSALGVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_VOUT1RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_VOUT2RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_VOUT3RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_VOUT4RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_DAC1RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_DAC2RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_DAC3RVOL, 0x100, 0x100); + snd_soc_update_bits(codec, WM8770_DAC4RVOL, 0x100, 0x100); + + /* mute all DACs */ + snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10, 0x10); + + snd_soc_add_controls(codec, wm8770_snd_controls, + ARRAY_SIZE(wm8770_snd_controls)); + snd_soc_dapm_new_controls(&codec->dapm, wm8770_dapm_widgets, + ARRAY_SIZE(wm8770_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, wm8770_intercon, + ARRAY_SIZE(wm8770_intercon)); + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); +err_reg_get: + regulator_bulk_free(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); + return ret; +} + +static int wm8770_remove(struct snd_soc_codec *codec) +{ + struct wm8770_priv *wm8770; + int i; + + wm8770 = snd_soc_codec_get_drvdata(codec); + wm8770_set_bias_level(codec, SND_SOC_BIAS_OFF); + + for (i = 0; i < ARRAY_SIZE(wm8770->supplies); ++i) + regulator_unregister_notifier(wm8770->supplies[i].consumer, + &wm8770->disable_nb[i]); + regulator_bulk_free(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_wm8770 = { + .probe = wm8770_probe, + .remove = wm8770_remove, + .suspend = wm8770_suspend, + .resume = wm8770_resume, + .set_bias_level = wm8770_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm8770_reg_defs), + .reg_word_size = sizeof (u16), + .reg_cache_default = wm8770_reg_defs +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8770_spi_probe(struct spi_device *spi) +{ + struct wm8770_priv *wm8770; + int ret; + + wm8770 = kzalloc(sizeof(struct wm8770_priv), GFP_KERNEL); + if (!wm8770) + return -ENOMEM; + + wm8770->control_type = SND_SOC_SPI; + spi_set_drvdata(spi, wm8770); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8770, &wm8770_dai, 1); + if (ret < 0) + kfree(wm8770); + return ret; +} + +static int __devexit wm8770_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + kfree(spi_get_drvdata(spi)); + return 0; +} + +static struct spi_driver wm8770_spi_driver = { + .driver = { + .name = "wm8770", + .owner = THIS_MODULE, + }, + .probe = wm8770_spi_probe, + .remove = __devexit_p(wm8770_spi_remove) +}; +#endif + +static int __init wm8770_modinit(void) +{ + int ret = 0; + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8770_spi_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8770 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8770_modinit); + +static void __exit wm8770_exit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8770_spi_driver); +#endif +} +module_exit(wm8770_exit); + +MODULE_DESCRIPTION("ASoC WM8770 driver"); +MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8770.h b/sound/soc/codecs/wm8770.h new file mode 100644 index 000000000000..5f1b3bda6cc8 --- /dev/null +++ b/sound/soc/codecs/wm8770.h @@ -0,0 +1,51 @@ +/* + * wm8770.h -- WM8770 ASoC driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8770_H +#define _WM8770_H + +/* Registers */ +#define WM8770_VOUT1LVOL 0 +#define WM8770_VOUT1RVOL 0x1 +#define WM8770_VOUT2LVOL 0x2 +#define WM8770_VOUT2RVOL 0x3 +#define WM8770_VOUT3LVOL 0x4 +#define WM8770_VOUT3RVOL 0x5 +#define WM8770_VOUT4LVOL 0x6 +#define WM8770_VOUT4RVOL 0x7 +#define WM8770_MSALGVOL 0x8 +#define WM8770_DAC1LVOL 0x9 +#define WM8770_DAC1RVOL 0xa +#define WM8770_DAC2LVOL 0xb +#define WM8770_DAC2RVOL 0xc +#define WM8770_DAC3LVOL 0xd +#define WM8770_DAC3RVOL 0xe +#define WM8770_DAC4LVOL 0xf +#define WM8770_DAC4RVOL 0x10 +#define WM8770_MSDIGVOL 0x11 +#define WM8770_DACPHASE 0x12 +#define WM8770_DACCTRL1 0x13 +#define WM8770_DACMUTE 0x14 +#define WM8770_DACCTRL2 0x15 +#define WM8770_IFACECTRL 0x16 +#define WM8770_MSTRCTRL 0x17 +#define WM8770_PWDNCTRL 0x18 +#define WM8770_ADCLCTRL 0x19 +#define WM8770_ADCRCTRL 0x1a +#define WM8770_ADCMUX 0x1b +#define WM8770_OUTMUX1 0x1c +#define WM8770_OUTMUX2 0x1d +#define WM8770_RESET 0x31 + +#define WM8770_CACHEREGNUM 0x20 + +#endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 0132a27140ae..8e7953b1b790 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -25,7 +25,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -306,7 +305,7 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Disable the global powerdown; DAPM does the rest */ snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 0); } @@ -317,7 +316,7 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -404,6 +403,7 @@ static int wm8776_resume(struct snd_soc_codec *codec) static int wm8776_probe(struct snd_soc_codec *codec) { struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret = 0; ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8776->control_type); @@ -427,9 +427,9 @@ static int wm8776_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8776_snd_controls, ARRAY_SIZE(wm8776_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8776_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8776_dapm_widgets, ARRAY_SIZE(wm8776_dapm_widgets)); - snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes)); + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); return ret; } diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 4599e8e95aa2..6785688f8806 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -23,7 +23,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -176,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, return 0; } -static int wm8804_volatile(unsigned int reg) +static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8804_RST_DEVID1: @@ -515,7 +514,7 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); if (ret) { @@ -537,7 +536,7 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -581,7 +580,7 @@ static int wm8804_probe(struct snd_soc_codec *codec) wm8804 = snd_soc_codec_get_drvdata(codec); wm8804->codec = codec; - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; ret = snd_soc_codec_set_cache_io(codec, 8, 8, wm8804->control_type); if (ret < 0) { diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index aca4b1ea10bb..449ea09a193d 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -30,7 +30,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -140,7 +139,6 @@ struct wm8900_priv { enum snd_soc_control_type control_type; - u16 reg_cache[WM8900_MAXREG]; u32 fll_in; /* FLL input frequency */ u32 fll_out; /* FLL output frequency */ @@ -182,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { /* Remaining registers all zero */ }; -static int wm8900_volatile_register(unsigned int reg) +static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8900_REG_ID: @@ -611,10 +609,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8900_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets, - ARRAY_SIZE(wm8900_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8900_dapm_widgets, + ARRAY_SIZE(wm8900_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -1051,7 +1050,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: /* Charge capacitors if initial power up */ - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* STARTUP_BIAS_ENA on */ snd_soc_write(codec, WM8900_REG_POWER1, WM8900_REG_POWER1_STARTUP_BIAS_ENA); @@ -1119,7 +1118,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec, WM8900_REG_POWER2_SYSCLK_ENA); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 622b60238a82..f52b623bb692 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2,6 +2,7 @@ * wm8903.c -- WM8903 ALSA SoC Audio driver * * Copyright 2008 Wolfson Microelectronics + * Copyright 2011 NVIDIA, Inc. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -19,6 +20,7 @@ #include <linux/init.h> #include <linux/completion.h> #include <linux/delay.h> +#include <linux/gpio.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> @@ -29,9 +31,9 @@ #include <sound/pcm_params.h> #include <sound/tlv.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/wm8903.h> +#include <trace/events/asoc.h> #include "wm8903.h" @@ -213,18 +215,19 @@ static u16 wm8903_reg_defaults[] = { }; struct wm8903_priv { - - u16 reg_cache[ARRAY_SIZE(wm8903_reg_defaults)]; + struct snd_soc_codec *codec; int sysclk; int irq; - /* Reference counts */ - int class_w_users; - int playback_active; - int capture_active; + int fs; + int deemph; - struct completion wseq; + int dcs_pending; + int dcs_cache[4]; + + /* Reference count */ + int class_w_users; struct snd_soc_jack *mic_jack; int mic_det; @@ -232,17 +235,22 @@ struct wm8903_priv { int mic_last_report; int mic_delay; - struct snd_pcm_substream *master_substream; - struct snd_pcm_substream *slave_substream; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif }; -static int wm8903_volatile_register(unsigned int reg) +static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8903_SW_RESET_AND_ID: case WM8903_REVISION_NUMBER: case WM8903_INTERRUPT_STATUS_1: case WM8903_WRITE_SEQUENCER_4: + case WM8903_DC_SERVO_READBACK_1: + case WM8903_DC_SERVO_READBACK_2: + case WM8903_DC_SERVO_READBACK_3: + case WM8903_DC_SERVO_READBACK_4: return 1; default: @@ -250,50 +258,6 @@ static int wm8903_volatile_register(unsigned int reg) } } -static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) -{ - u16 reg[5]; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - BUG_ON(start > 48); - - /* Enable the sequencer if it's not already on */ - reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, - reg[0] | WM8903_WSEQ_ENA); - - dev_dbg(codec->dev, "Starting sequence at %d\n", start); - - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3, - start | WM8903_WSEQ_START); - - /* Wait for it to complete. If we have the interrupt wired up then - * that will break us out of the poll early. - */ - do { - wait_for_completion_timeout(&wm8903->wseq, - msecs_to_jiffies(10)); - - reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4); - } while (reg[4] & WM8903_WSEQ_BUSY); - - dev_dbg(codec->dev, "Sequence complete\n"); - - /* Disable the sequencer again if we enabled it */ - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]); - - return 0; -} - -static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache) -{ - int i; - - /* There really ought to be something better we can do here :/ */ - for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++) - cache[i] = codec->hw_read(codec, i); -} - static void wm8903_reset(struct snd_soc_codec *codec) { snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0); @@ -301,11 +265,6 @@ static void wm8903_reset(struct snd_soc_codec *codec) sizeof(wm8903_reg_defaults)); } -#define WM8903_OUTPUT_SHORT 0x8 -#define WM8903_OUTPUT_OUT 0x4 -#define WM8903_OUTPUT_INT 0x2 -#define WM8903_OUTPUT_IN 0x1 - static int wm8903_cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -315,97 +274,101 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w, return 0; } -/* - * Event for headphone and line out amplifier power changes. Special - * power up/down sequences are required in order to maximise pop/click - * performance. - */ -static int wm8903_output_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int wm8903_dcs_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; - u16 val; - u16 reg; - u16 dcs_reg; - u16 dcs_bit; - int shift; - - switch (w->reg) { - case WM8903_POWER_MANAGEMENT_2: - reg = WM8903_ANALOGUE_HP_0; - dcs_bit = 0 + w->shift; - break; - case WM8903_POWER_MANAGEMENT_3: - reg = WM8903_ANALOGUE_LINEOUT_0; - dcs_bit = 2 + w->shift; - break; - default: - BUG(); - return -EINVAL; /* Spurious warning from some compilers */ - } + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - switch (w->shift) { - case 0: - shift = 0; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wm8903->dcs_pending |= 1 << w->shift; break; - case 1: - shift = 4; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WM8903_DC_SERVO_0, + 1 << w->shift, 0); break; - default: - BUG(); - return -EINVAL; /* Spurious warning from some compilers */ } - if (event & SND_SOC_DAPM_PRE_PMU) { - val = snd_soc_read(codec, reg); + return 0; +} - /* Short the output */ - val &= ~(WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); - } +#define WM8903_DCS_MODE_WRITE_STOP 0 +#define WM8903_DCS_MODE_START_STOP 2 + +static void wm8903_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 wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int dcs_mode = WM8903_DCS_MODE_WRITE_STOP; + int i, val; - if (event & SND_SOC_DAPM_POST_PMU) { - val = snd_soc_read(codec, reg); + /* Complete any pending DC servo starts */ + if (wm8903->dcs_pending) { + dev_dbg(codec->dev, "Starting DC servo for %x\n", + wm8903->dcs_pending); - val |= (WM8903_OUTPUT_IN << shift); - snd_soc_write(codec, reg, val); + /* If we've no cached values then we need to do startup */ + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; - val |= (WM8903_OUTPUT_INT << shift); - snd_soc_write(codec, reg, val); + if (wm8903->dcs_cache[i]) { + dev_dbg(codec->dev, + "Restore DC servo %d value %x\n", + 3 - i, wm8903->dcs_cache[i]); + + snd_soc_write(codec, WM8903_DC_SERVO_4 + i, + wm8903->dcs_cache[i] & 0xff); + } else { + dev_dbg(codec->dev, + "Calibrate DC servo %d\n", 3 - i); + dcs_mode = WM8903_DCS_MODE_START_STOP; + } + } - /* Turn on the output ENA_OUTP */ - val |= (WM8903_OUTPUT_OUT << shift); - snd_soc_write(codec, reg, val); + /* Don't trust the cache for analogue */ + if (wm8903->class_w_users) + dcs_mode = WM8903_DCS_MODE_START_STOP; - /* Enable the DC servo */ - dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); - dcs_reg |= dcs_bit; - snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); + snd_soc_update_bits(codec, WM8903_DC_SERVO_2, + WM8903_DCS_MODE_MASK, dcs_mode); - /* Remove the short */ - val |= (WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); - } + snd_soc_update_bits(codec, WM8903_DC_SERVO_0, + WM8903_DCS_ENA_MASK, wm8903->dcs_pending); - if (event & SND_SOC_DAPM_PRE_PMD) { - val = snd_soc_read(codec, reg); + switch (dcs_mode) { + case WM8903_DCS_MODE_WRITE_STOP: + break; - /* Short the output */ - val &= ~(WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); + case WM8903_DCS_MODE_START_STOP: + msleep(270); - /* Disable the DC servo */ - dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); - dcs_reg &= ~dcs_bit; - snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); + /* Cache the measured offsets for digital */ + if (wm8903->class_w_users) + break; - /* Then disable the intermediate and output stages */ - val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT | - WM8903_OUTPUT_IN) << shift); - snd_soc_write(codec, reg, val); - } + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; - return 0; + val = snd_soc_read(codec, + WM8903_DC_SERVO_READBACK_1 + i); + dev_dbg(codec->dev, "DC servo %d: %x\n", + 3 - i, val); + wm8903->dcs_cache[i] = val; + } + break; + + default: + pr_warn("DCS mode %d delay not set\n", dcs_mode); + break; + } + + wm8903->dcs_pending = 0; + } } /* @@ -463,6 +426,72 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +static int wm8903_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8903_set_deemph(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8903->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8903_deemph); i++) { + if (abs(wm8903_deemph[i] - wm8903->fs) < + abs(wm8903_deemph[best] - wm8903->fs)) + best = i; + } + + val = best << WM8903_DEEMPH_SHIFT; + } else { + best = 0; + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", + best, wm8903_deemph[best]); + + return snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, + WM8903_DEEMPH_MASK, val); +} + +static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8903->deemph; + + return 0; +} + +static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&codec->mutex); + if (wm8903->deemph != deemph) { + wm8903->deemph = deemph; + + wm8903_set_deemph(codec); + + ret = 1; + } + mutex_unlock(&codec->mutex); + + return ret; +} + /* ALSA can only do steps of .01dB */ static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); @@ -475,6 +504,23 @@ static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); +static const char *hpf_mode_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3" +}; + +static const struct soc_enum hpf_mode = + SOC_ENUM_SINGLE(WM8903_ADC_DIGITAL_0, 5, 4, hpf_mode_text); + +static const char *osr_text[] = { + "Low power", "High performance" +}; + +static const struct soc_enum adc_osr = + SOC_ENUM_SINGLE(WM8903_ANALOGUE_ADC_0, 0, 2, osr_text); + +static const struct soc_enum dac_osr = + SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 0, 2, osr_text); + static const char *drc_slope_text[] = { "1", "1/2", "1/4", "1/8", "1/16", "0" }; @@ -537,13 +583,6 @@ static const char *mute_mode_text[] = { static const struct soc_enum mute_mode = SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); -static const char *dac_deemphasis_text[] = { - "Disabled", "32kHz", "44.1kHz", "48kHz" -}; - -static const struct soc_enum dac_deemphasis = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text); - static const char *companding_text[] = { "ulaw", "alaw" }; @@ -595,6 +634,22 @@ 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 *aif_text[] = { + "Left", "Right" +}; + +static const struct soc_enum lcapture_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text); + +static const struct soc_enum rcapture_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text); + +static const struct soc_enum lplay_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text); + +static const struct soc_enum rplay_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text); + static const struct snd_kcontrol_new wm8903_snd_controls[] = { /* Input PGAs - No TLV since the scale depends on PGA mode */ @@ -613,6 +668,9 @@ SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, 6, 1, 0), /* ADCs */ +SOC_ENUM("ADC OSR", adc_osr), +SOC_SINGLE("HPF Switch", WM8903_ADC_DIGITAL_0, 4, 1, 0), +SOC_ENUM("HPF Mode", hpf_mode), SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), @@ -642,14 +700,16 @@ SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8, 12, 0, digital_sidetone_tlv), /* DAC */ +SOC_ENUM("DAC OSR", dac_osr), SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), SOC_ENUM("DAC Soft Mute Rate", soft_mute), SOC_ENUM("DAC Mute Mode", mute_mode), SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), -SOC_ENUM("DAC De-emphasis", dac_deemphasis), SOC_ENUM("DAC Companding Mode", dac_companding), SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8903_get_deemph, wm8903_put_deemph), /* Headphones */ SOC_DOUBLE_R("Headphone Switch", @@ -707,6 +767,18 @@ 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 lcapture_mux = + SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum); + +static const struct snd_kcontrol_new rcapture_mux = + SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum); + +static const struct snd_kcontrol_new lplay_mux = + SOC_DAPM_ENUM("Left Playback Mux", lplay_enum); + +static const struct snd_kcontrol_new rplay_mux = + SOC_DAPM_ENUM("Right Playback Mux", rplay_enum); + static const struct snd_kcontrol_new left_output_mixer[] = { SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), @@ -770,14 +842,26 @@ 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_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0), -SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0), +SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0), + +SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux), +SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux), + +SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux), SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux), -SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0), -SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0), +SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux), +SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux), + +SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0), SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, left_output_mixer, ARRAY_SIZE(left_output_mixer)), @@ -789,23 +873,51 @@ SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), -SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, - 1, 0, NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, - 0, 0, NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), - -SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0, - NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0, - NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_POWER_MANAGEMENT_2, + 1, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_POWER_MANAGEMENT_2, + 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_POWER_MANAGEMENT_3, 1, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_POWER_MANAGEMENT_3, 0, 0, + NULL, 0), + +SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 2, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA", 1, WM8903_ANALOGUE_HP_0, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 2, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA", 1, WM8903_ANALOGUE_HP_0, 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 2, WM8903_ANALOGUE_LINEOUT_0, 5, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA", 1, WM8903_ANALOGUE_LINEOUT_0, 4, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 2, WM8903_ANALOGUE_LINEOUT_0, 1, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA", 1, WM8903_ANALOGUE_LINEOUT_0, 0, 0, + NULL, 0), + +SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, NULL, 0), @@ -815,10 +927,18 @@ SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0, wm8903_cp_event, SND_SOC_DAPM_POST_PMU), 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[] = { + { "CLK_DSP", NULL, "CLK_SYS" }, + { "Mic Bias", NULL, "CLK_SYS" }, + { "HPL_DCS", NULL, "CLK_SYS" }, + { "HPR_DCS", NULL, "CLK_SYS" }, + { "LINEOUTL_DCS", NULL, "CLK_SYS" }, + { "LINEOUTR_DCS", NULL, "CLK_SYS" }, + { "Left Input Mux", "IN1L", "IN1L" }, { "Left Input Mux", "IN2L", "IN2L" }, { "Left Input Mux", "IN3L", "IN3L" }, @@ -859,18 +979,36 @@ 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 Capture Mux", "Left", "ADCL" }, + { "Left Capture Mux", "Right", "ADCR" }, + + { "Right Capture Mux", "Left", "ADCL" }, + { "Right Capture Mux", "Right", "ADCR" }, + + { "AIFTXL", NULL, "Left Capture Mux" }, + { "AIFTXR", NULL, "Right Capture Mux" }, + { "ADCL", NULL, "Left Input PGA" }, { "ADCL", NULL, "CLK_DSP" }, { "ADCR", NULL, "Right Input PGA" }, { "ADCR", NULL, "CLK_DSP" }, + { "Left Playback Mux", "Left", "AIFRXL" }, + { "Left Playback Mux", "Right", "AIFRXR" }, + + { "Right Playback Mux", "Left", "AIFRXL" }, + { "Right Playback Mux", "Right", "AIFRXR" }, + { "DACL Sidetone", "Left", "ADCL" }, { "DACL Sidetone", "Right", "ADCR" }, { "DACR Sidetone", "Left", "ADCL" }, { "DACR Sidetone", "Right", "ADCR" }, + { "DACL", NULL, "Left Playback Mux" }, { "DACL", NULL, "DACL Sidetone" }, { "DACL", NULL, "CLK_DSP" }, + + { "DACR", NULL, "Right Playback Mux" }, { "DACR", NULL, "DACR Sidetone" }, { "DACR", NULL, "CLK_DSP" }, @@ -903,11 +1041,39 @@ static const struct snd_soc_dapm_route intercon[] = { { "Left Speaker PGA", NULL, "Left Speaker Mixer" }, { "Right Speaker PGA", NULL, "Right Speaker Mixer" }, - { "HPOUTL", NULL, "Left Headphone Output PGA" }, - { "HPOUTR", NULL, "Right Headphone Output PGA" }, - - { "LINEOUTL", NULL, "Left Line Output PGA" }, - { "LINEOUTR", NULL, "Right Line Output PGA" }, + { "HPL_ENA", NULL, "Left Headphone Output PGA" }, + { "HPR_ENA", NULL, "Right Headphone Output PGA" }, + { "HPL_ENA_DLY", NULL, "HPL_ENA" }, + { "HPR_ENA_DLY", NULL, "HPR_ENA" }, + { "LINEOUTL_ENA", NULL, "Left Line Output PGA" }, + { "LINEOUTR_ENA", NULL, "Right Line Output PGA" }, + { "LINEOUTL_ENA_DLY", NULL, "LINEOUTL_ENA" }, + { "LINEOUTR_ENA_DLY", NULL, "LINEOUTR_ENA" }, + + { "HPL_DCS", NULL, "DCS Master" }, + { "HPR_DCS", NULL, "DCS Master" }, + { "LINEOUTL_DCS", NULL, "DCS Master" }, + { "LINEOUTR_DCS", NULL, "DCS Master" }, + + { "HPL_DCS", NULL, "HPL_ENA_DLY" }, + { "HPR_DCS", NULL, "HPR_ENA_DLY" }, + { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" }, + { "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" }, + + { "HPL_ENA_OUTP", NULL, "HPL_DCS" }, + { "HPR_ENA_OUTP", NULL, "HPR_DCS" }, + { "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" }, + { "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" }, + + { "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" }, + { "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" }, + { "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" }, + { "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" }, + + { "HPOUTL", NULL, "HPL_RMV_SHORT" }, + { "HPOUTR", NULL, "HPR_RMV_SHORT" }, + { "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" }, + { "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" }, { "LOP", NULL, "Left Speaker PGA" }, { "LON", NULL, "Left Speaker PGA" }, @@ -923,10 +1089,11 @@ static const struct snd_soc_dapm_route intercon[] = { static int wm8903_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets, - ARRAY_SIZE(wm8903_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + 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; } @@ -934,64 +1101,113 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec) static int wm8903_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg, reg2; - switch (level) { case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: - reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); - reg &= ~(WM8903_VMID_RES_MASK); - reg |= WM8903_VMID_RES_50K; - snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { - snd_soc_write(codec, WM8903_CLOCK_RATES_2, - WM8903_CLK_SYS_ENA); - - /* Change DC servo dither level in startup sequence */ - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2); - - wm8903_run_sequence(codec, 0); - wm8903_sync_reg_cache(codec, codec->reg_cache); - - /* Enable low impedence charge pump output */ - reg = snd_soc_read(codec, - WM8903_CONTROL_INTERFACE_TEST_1); - snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, - reg | WM8903_TEST_KEY); - reg2 = snd_soc_read(codec, WM8903_CHARGE_PUMP_TEST_1); - snd_soc_write(codec, WM8903_CHARGE_PUMP_TEST_1, - reg2 | WM8903_CP_SW_KELVIN_MODE_MASK); - snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, - reg); + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_POBCTRL | WM8903_ISEL_MASK | + WM8903_STARTUP_BIAS_ENA | + WM8903_BIAS_ENA, + WM8903_POBCTRL | + (2 << WM8903_ISEL_SHIFT) | + WM8903_STARTUP_BIAS_ENA); + + snd_soc_update_bits(codec, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, + WM8903_SPK_DISCHARGE); + + msleep(33); + + snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + WM8903_SPKL_ENA | WM8903_SPKR_ENA); + + snd_soc_update_bits(codec, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_RES_MASK | + WM8903_VMID_BUF_ENA, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + (2 << WM8903_VMID_SOFT_SHIFT) | + WM8903_VMID_RES_250K | + WM8903_VMID_BUF_ENA); + + msleep(129); + + snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); + + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA | WM8903_POBCTRL, + WM8903_BIAS_ENA); /* By default no bypass paths are enabled so * enable Class W support. */ dev_dbg(codec->dev, "Enabling Class W\n"); - snd_soc_write(codec, WM8903_CLASS_W_0, reg | - WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); + snd_soc_update_bits(codec, WM8903_CLASS_W_0, + WM8903_CP_DYN_FREQ | + WM8903_CP_DYN_V, + WM8903_CP_DYN_FREQ | + WM8903_CP_DYN_V); } - reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); - reg &= ~(WM8903_VMID_RES_MASK); - reg |= WM8903_VMID_RES_250K; - snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_250K); break; case SND_SOC_BIAS_OFF: - wm8903_run_sequence(codec, 32); - reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2); - reg &= ~WM8903_CLK_SYS_ENA; - snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg); + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, + 2 << WM8903_VMID_SOFT_SHIFT); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_BUF_ENA, 0); + + msleep(290); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_BUF_ENA, 0); + + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_STARTUP_BIAS_ENA, 0); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1222,58 +1438,6 @@ static struct { { 0, 0 }, }; -static int wm8903_startup(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; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - struct snd_pcm_runtime *master_runtime; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - wm8903->playback_active++; - else - wm8903->capture_active++; - - /* The DAI has shared clocks so if we already have a playback or - * capture going then constrain this substream to match it. - */ - if (wm8903->master_substream) { - master_runtime = wm8903->master_substream->runtime; - - dev_dbg(codec->dev, "Constraining to %d bits\n", - master_runtime->sample_bits); - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - master_runtime->sample_bits, - master_runtime->sample_bits); - - wm8903->slave_substream = substream; - } else - wm8903->master_substream = substream; - - return 0; -} - -static void wm8903_shutdown(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; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - wm8903->playback_active--; - else - wm8903->capture_active--; - - if (wm8903->master_substream == substream) - wm8903->master_substream = wm8903->slave_substream; - - wm8903->slave_substream = NULL; -} - static int wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1298,11 +1462,6 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1); u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); - if (substream == wm8903->slave_substream) { - dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); - return 0; - } - /* Enable sloping stopband filter for low sample rates */ if (fs <= 24000) dac_digital1 |= WM8903_DAC_SB_FILT; @@ -1320,19 +1479,6 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, } } - /* Constraints should stop us hitting this but let's make sure */ - if (wm8903->capture_active) - switch (sample_rates[dsp_config].rate) { - case 88200: - case 96000: - dev_err(codec->dev, "%dHz unsupported by ADC\n", - fs); - return -EINVAL; - - default: - break; - } - dev_dbg(codec->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); clock1 &= ~WM8903_SAMPLE_RATE_MASK; clock1 |= sample_rates[dsp_config].value; @@ -1428,6 +1574,9 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, aif2 |= bclk_divs[bclk_div].div; aif3 |= bclk / fs; + wm8903->fs = params_rate(params); + wm8903_set_deemph(codec); + snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0); snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1); snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); @@ -1479,7 +1628,7 @@ int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, WM8903_MICDET_EINT | WM8903_MICSHRT_EINT, irq_mask); - if (det && shrt) { + if (det || shrt) { /* Enable mic detection, this may not have been set through * platform data (eg, if the defaults are OK). */ snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, @@ -1507,8 +1656,7 @@ static irqreturn_t wm8903_irq(int irq, void *data) int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask; if (int_val & WM8903_WSEQ_BUSY_EINT) { - dev_dbg(codec->dev, "Write sequencer done\n"); - complete(&wm8903->wseq); + dev_warn(codec->dev, "Write sequencer done\n"); } /* @@ -1521,6 +1669,11 @@ static irqreturn_t wm8903_irq(int irq, void *data) mic_report = wm8903->mic_last_report; int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1); +#ifndef CONFIG_SND_SOC_WM8903_MODULE + if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT)) + trace_snd_soc_jack_irq(dev_name(codec->dev)); +#endif + if (int_val & WM8903_MICSHRT_EINT) { dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol); @@ -1571,8 +1724,6 @@ static irqreturn_t wm8903_irq(int irq, void *data) SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_ops wm8903_dai_ops = { - .startup = wm8903_startup, - .shutdown = wm8903_shutdown, .hw_params = wm8903_hw_params, .digital_mute = wm8903_digital_mute, .set_fmt = wm8903_set_dai_fmt, @@ -1629,6 +1780,120 @@ static int wm8903_resume(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_GPIOLIB +static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8903_priv, gpio_chip); +} + +static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= WM8903_NUM_GPIO) + return -EINVAL; + + return 0; +} + +static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; + val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | + WM8903_GP1_DIR; + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + int reg; + + reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + + return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; +} + +static int wm8903_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; + val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | + (value << WM8903_GP2_LVL_SHIFT); + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + + snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); +} + +static struct gpio_chip wm8903_template_chip = { + .label = "wm8903", + .owner = THIS_MODULE, + .request = wm8903_gpio_request, + .direction_input = wm8903_gpio_direction_in, + .get = wm8903_gpio_get, + .direction_output = wm8903_gpio_direction_out, + .set = wm8903_gpio_set, + .can_sleep = 1, +}; + +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); + int ret; + + wm8903->gpio_chip = wm8903_template_chip; + wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; + wm8903->gpio_chip.dev = codec->dev; + + if (pdata && pdata->gpio_base) + wm8903->gpio_chip.base = pdata->gpio_base; + else + wm8903->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = gpiochip_remove(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret); +} +#else +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ +} +#endif + static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); @@ -1637,7 +1902,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) int trigger, irq_pol; u16 val; - init_completion(&wm8903->wseq); + wm8903->codec = codec; ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); if (ret != 0) { @@ -1653,19 +1918,33 @@ static int wm8903_probe(struct snd_soc_codec *codec) } val = snd_soc_read(codec, WM8903_REVISION_NUMBER); - dev_info(codec->dev, "WM8903 revision %d\n", - val & WM8903_CHIP_REV_MASK); + dev_info(codec->dev, "WM8903 revision %c\n", + (val & WM8903_CHIP_REV_MASK) + 'A'); wm8903_reset(codec); /* Set up GPIOs and microphone detection */ if (pdata) { + bool mic_gpio = false; + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if (!pdata->gpio_cfg[i]) + if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) continue; snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, pdata->gpio_cfg[i] & 0xffff); + + val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) + >> WM8903_GP1_FN_SHIFT; + + switch (val) { + case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: + case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: + mic_gpio = true; + break; + default: + break; + } } snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, @@ -1676,6 +1955,14 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + /* If microphone detection is enabled by pdata but + * detected via IRQ then interrupts can be lost before + * the machine driver has set up microphone detection + * IRQs as the IRQs are clear on read. The detection + * will be enabled when the machine driver configures. + */ + WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); + wm8903->mic_delay = pdata->micdet_delay; } @@ -1735,20 +2022,23 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); /* Enable DAC soft mute by default */ - val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); - val |= WM8903_DAC_MUTEMODE; - snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val); + snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); snd_soc_add_controls(codec, wm8903_snd_controls, ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(codec); + wm8903_init_gpio(codec); + return ret; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { + wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1763,6 +2053,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { .reg_word_size = sizeof(u16), .reg_cache_default = wm8903_reg_defaults, .volatile_register = wm8903_volatile_register, + .seq_notifier = wm8903_seq_notifier, }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) @@ -1801,7 +2092,7 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); static struct i2c_driver wm8903_i2c_driver = { .driver = { - .name = "wm8903-codec", + .name = "wm8903", .owner = THIS_MODULE, }, .probe = wm8903_i2c_probe, diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h index 996435e681e5..db949311c0f2 100644 --- a/sound/soc/codecs/wm8903.h +++ b/sound/soc/codecs/wm8903.h @@ -19,10 +19,6 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int det, int shrt); -#define WM8903_MCLK_DIV_2 1 -#define WM8903_CLK_SYS 2 -#define WM8903_BCLK 3 -#define WM8903_LRCLK 4 /* * Register values. @@ -79,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41 #define WM8903_DC_SERVO_0 0x43 #define WM8903_DC_SERVO_2 0x45 +#define WM8903_DC_SERVO_4 0x47 +#define WM8903_DC_SERVO_5 0x48 +#define WM8903_DC_SERVO_6 0x49 +#define WM8903_DC_SERVO_7 0x4A +#define WM8903_DC_SERVO_READBACK_1 0x51 +#define WM8903_DC_SERVO_READBACK_2 0x52 +#define WM8903_DC_SERVO_READBACK_3 0x53 +#define WM8903_DC_SERVO_READBACK_4 0x54 #define WM8903_ANALOGUE_HP_0 0x5A #define WM8903_ANALOGUE_LINEOUT_0 0x5E #define WM8903_CHARGE_PUMP_0 0x62 @@ -98,8 +102,6 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_INTERRUPT_STATUS_1_MASK 0x7A #define WM8903_INTERRUPT_POLARITY_1 0x7B #define WM8903_INTERRUPT_CONTROL 0x7E -#define WM8903_CONTROL_INTERFACE_TEST_1 0x81 -#define WM8903_CHARGE_PUMP_TEST_1 0x95 #define WM8903_CLOCK_RATE_TEST_4 0xA4 #define WM8903_ANALOGUE_OUTPUT_BIAS_0 0xAC @@ -171,7 +173,7 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_VMID_RES_50K 2 #define WM8903_VMID_RES_250K 3 -#define WM8903_VMID_RES_5K 4 +#define WM8903_VMID_RES_5K 6 /* * R8 (0x08) - Analogue DAC 0 @@ -1206,25 +1208,6 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_IRQ_POL_WIDTH 1 /* IRQ_POL */ /* - * R129 (0x81) - Control Interface Test 1 - */ -#define WM8903_USER_KEY 0x0002 /* USER_KEY */ -#define WM8903_USER_KEY_MASK 0x0002 /* USER_KEY */ -#define WM8903_USER_KEY_SHIFT 1 /* USER_KEY */ -#define WM8903_USER_KEY_WIDTH 1 /* USER_KEY */ -#define WM8903_TEST_KEY 0x0001 /* TEST_KEY */ -#define WM8903_TEST_KEY_MASK 0x0001 /* TEST_KEY */ -#define WM8903_TEST_KEY_SHIFT 0 /* TEST_KEY */ -#define WM8903_TEST_KEY_WIDTH 1 /* TEST_KEY */ - -/* - * R149 (0x95) - Charge Pump Test 1 - */ -#define WM8903_CP_SW_KELVIN_MODE_MASK 0x0006 /* CP_SW_KELVIN_MODE - [2:1] */ -#define WM8903_CP_SW_KELVIN_MODE_SHIFT 1 /* CP_SW_KELVIN_MODE - [2:1] */ -#define WM8903_CP_SW_KELVIN_MODE_WIDTH 2 /* CP_SW_KELVIN_MODE - [2:1] */ - -/* * R164 (0xA4) - Clock Rate Test 4 */ #define WM8903_ADC_DIG_MIC 0x0200 /* ADC_DIG_MIC */ diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index fca60a0b57b8..9b3bba4df5b3 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/wm8904.h> @@ -50,8 +49,6 @@ static const char *wm8904_supply_names[WM8904_NUM_SUPPLIES] = { /* codec private data */ struct wm8904_priv { - u16 reg_cache[WM8904_MAX_REGISTER + 1]; - enum wm8904_type devtype; void *control_data; @@ -599,7 +596,7 @@ static struct { { 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */ }; -static int wm8904_volatile_register(unsigned int reg) +static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return wm8904_access[reg].vol; } @@ -818,7 +815,8 @@ static int wm8904_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - return wm8904->deemph; + ucontrol->value.enumerated.item[0] = wm8904->deemph; + return 0; } static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, @@ -1428,10 +1426,11 @@ static const struct snd_soc_dapm_route wm8912_intercon[] = { static int wm8904_add_widgets(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_new_controls(codec, wm8904_core_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets, ARRAY_SIZE(wm8904_core_dapm_widgets)); - snd_soc_dapm_add_routes(codec, core_intercon, + snd_soc_dapm_add_routes(dapm, core_intercon, ARRAY_SIZE(core_intercon)); switch (wm8904->devtype) { @@ -1443,20 +1442,20 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8904_snd_controls, ARRAY_SIZE(wm8904_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8904_adc_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8904_adc_dapm_widgets, ARRAY_SIZE(wm8904_adc_dapm_widgets)); - snd_soc_dapm_new_controls(codec, wm8904_dac_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets, ARRAY_SIZE(wm8904_dac_dapm_widgets)); - snd_soc_dapm_new_controls(codec, wm8904_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8904_dapm_widgets, ARRAY_SIZE(wm8904_dapm_widgets)); - snd_soc_dapm_add_routes(codec, core_intercon, + snd_soc_dapm_add_routes(dapm, core_intercon, ARRAY_SIZE(core_intercon)); - snd_soc_dapm_add_routes(codec, adc_intercon, + snd_soc_dapm_add_routes(dapm, adc_intercon, ARRAY_SIZE(adc_intercon)); - snd_soc_dapm_add_routes(codec, dac_intercon, + snd_soc_dapm_add_routes(dapm, dac_intercon, ARRAY_SIZE(dac_intercon)); - snd_soc_dapm_add_routes(codec, wm8904_intercon, + snd_soc_dapm_add_routes(dapm, wm8904_intercon, ARRAY_SIZE(wm8904_intercon)); break; @@ -1464,17 +1463,17 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8904_dac_snd_controls, ARRAY_SIZE(wm8904_dac_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8904_dac_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets, ARRAY_SIZE(wm8904_dac_dapm_widgets)); - snd_soc_dapm_add_routes(codec, dac_intercon, + snd_soc_dapm_add_routes(dapm, dac_intercon, ARRAY_SIZE(dac_intercon)); - snd_soc_dapm_add_routes(codec, wm8912_intercon, + snd_soc_dapm_add_routes(dapm, wm8912_intercon, ARRAY_SIZE(wm8912_intercon)); break; } - snd_soc_dapm_new_widgets(codec); + snd_soc_dapm_new_widgets(dapm); return 0; } @@ -1590,7 +1589,7 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream, - wm8904->fs); for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { cur_val = abs((wm8904->sysclk_rate / - clk_sys_rates[i].ratio) - wm8904->fs);; + clk_sys_rates[i].ratio) - wm8904->fs); if (cur_val < best_val) { best = i; best_val = cur_val; @@ -1896,7 +1895,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, pr_debug("Fvco=%dHz\n", target); - /* Find an appropraite FLL_FRATIO and factor it out of the target */ + /* Find an appropriate 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; @@ -2093,7 +2092,7 @@ static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute) static void wm8904_sync_cache(struct snd_soc_codec *codec) { - struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); + u16 *reg_cache = codec->reg_cache; int i; if (!codec->cache_sync) @@ -2104,14 +2103,14 @@ static void wm8904_sync_cache(struct snd_soc_codec *codec) /* Sync back cached values if they're different from the * hardware default. */ - for (i = 1; i < ARRAY_SIZE(wm8904->reg_cache); i++) { + for (i = 1; i < codec->driver->reg_cache_size; i++) { if (!wm8904_access[i].writable) continue; - if (wm8904->reg_cache[i] == wm8904_reg[i]) + if (reg_cache[i] == wm8904_reg[i]) continue; - snd_soc_write(codec, i, wm8904->reg_cache[i]); + snd_soc_write(codec, i, reg_cache[i]); } codec->cache_sync = 0; @@ -2139,7 +2138,7 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); if (ret != 0) { @@ -2198,7 +2197,7 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, wm8904->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -2370,10 +2369,11 @@ static int wm8904_probe(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); struct wm8904_pdata *pdata = wm8904->pdata; + u16 *reg_cache = codec->reg_cache; int ret, i; codec->cache_sync = 1; - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; switch (wm8904->devtype) { case WM8904: @@ -2436,19 +2436,28 @@ static int wm8904_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU and enable ZC */ - wm8904->reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU; - wm8904->reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU; - wm8904->reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU; - wm8904->reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU; - wm8904->reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU | - WM8904_HPOUTLZC; - wm8904->reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU | - WM8904_HPOUTRZC; - wm8904->reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU | - WM8904_LINEOUTLZC; - wm8904->reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU | - WM8904_LINEOUTRZC; - wm8904->reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE; + snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT, + WM8904_ADC_VU, WM8904_ADC_VU); + snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT, + WM8904_ADC_VU, WM8904_ADC_VU); + snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT, + WM8904_DAC_VU, WM8904_DAC_VU); + snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT, + WM8904_DAC_VU, WM8904_DAC_VU); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT, + WM8904_HPOUT_VU | WM8904_HPOUTLZC, + WM8904_HPOUT_VU | WM8904_HPOUTLZC); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT, + WM8904_HPOUT_VU | WM8904_HPOUTRZC, + WM8904_HPOUT_VU | WM8904_HPOUTRZC); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC); + snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0, + WM8904_SR_MODE, 0); /* Apply configuration from the platform data. */ if (wm8904->pdata) { @@ -2456,23 +2465,25 @@ static int wm8904_probe(struct snd_soc_codec *codec) if (!pdata->gpio_cfg[i]) continue; - wm8904->reg_cache[WM8904_GPIO_CONTROL_1 + i] + reg_cache[WM8904_GPIO_CONTROL_1 + i] = pdata->gpio_cfg[i] & 0xffff; } /* Zero is the default value for these anyway */ for (i = 0; i < WM8904_MIC_REGS; i++) - wm8904->reg_cache[WM8904_MIC_BIAS_CONTROL_0 + i] + reg_cache[WM8904_MIC_BIAS_CONTROL_0 + i] = pdata->mic_cfg[i]; } /* Set Class W by default - this will be managed by the Class * G widget at runtime where bypass paths are available. */ - wm8904->reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR; + snd_soc_update_bits(codec, WM8904_CLASS_W_0, + WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR); /* Use normal bias source */ - wm8904->reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL; + snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0, + WM8904_POBCTRL, 0); wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 2cb16f895c46..25580e3ee7c4 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -35,7 +35,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -43,7 +42,6 @@ struct wm8940_priv { unsigned int sysclk; - u16 reg_cache[WM8940_CACHEREGNUM]; enum snd_soc_control_type control_type; void *control_data; }; @@ -291,13 +289,14 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8940_add_widgets(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - ret = snd_soc_dapm_new_controls(codec, wm8940_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets, ARRAY_SIZE(wm8940_dapm_widgets)); if (ret) goto error_ret; - ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); if (ret) goto error_ret; @@ -735,7 +734,6 @@ static int wm8940_probe(struct snd_soc_codec *codec) return ret; return ret; -; } static int wm8940_remove(struct snd_soc_codec *codec) @@ -768,6 +766,7 @@ static __devinit int wm8940_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8940); wm8940->control_data = i2c; + wm8940->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8940, &wm8940_dai, 1); diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index f89ad6c9a80b..3c7198779c31 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -23,7 +23,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/wm8955.h> @@ -42,8 +41,6 @@ static const char *wm8955_supply_names[WM8955_NUM_SUPPLIES] = { struct wm8955_priv { enum snd_soc_control_type control_type; - u16 reg_cache[WM8955_MAX_REGISTER + 1]; - unsigned int mclk_rate; int deemph; @@ -179,7 +176,7 @@ static int wm8995_pll_factors(struct device *dev, return 0; } -/* Lookup table specifiying SRATE (table 25 in datasheet); some of the +/* Lookup table specifying SRATE (table 25 in datasheet); some of the * output frequencies have been rounded to the standard frequencies * they are intended to match where the error is slight. */ static struct { @@ -380,7 +377,8 @@ static int wm8955_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - return wm8955->deemph; + ucontrol->value.enumerated.item[0] = wm8955->deemph; + return 0; } static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, @@ -577,13 +575,14 @@ static const struct snd_soc_dapm_route wm8955_intercon[] = { static int wm8955_add_widgets(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_add_controls(codec, wm8955_snd_controls, ARRAY_SIZE(wm8955_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8955_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8955_dapm_widgets, ARRAY_SIZE(wm8955_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, wm8955_intercon, + snd_soc_dapm_add_routes(dapm, wm8955_intercon, ARRAY_SIZE(wm8955_intercon)); return 0; @@ -767,6 +766,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); + u16 *reg_cache = codec->reg_cache; int ret, i; switch (level) { @@ -786,7 +786,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); if (ret != 0) { @@ -799,14 +799,14 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, /* Sync back cached values if they're * different from the hardware default. */ - for (i = 0; i < ARRAY_SIZE(wm8955->reg_cache); i++) { + for (i = 0; i < codec->driver->reg_cache_size; i++) { if (i == WM8955_RESET) continue; - if (wm8955->reg_cache[i] == wm8955_reg[i]) + if (reg_cache[i] == wm8955_reg[i]) continue; - snd_soc_write(codec, i, wm8955->reg_cache[i]); + snd_soc_write(codec, i, reg_cache[i]); } /* Enable VREF and VMID */ @@ -850,7 +850,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, wm8955->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -901,6 +901,7 @@ static int wm8955_probe(struct snd_soc_codec *codec) { struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); struct wm8955_pdata *pdata = dev_get_platdata(codec->dev); + u16 *reg_cache = codec->reg_cache; int ret, i; ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8955->control_type); @@ -933,25 +934,36 @@ static int wm8955_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU and enable ZC */ - wm8955->reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU; - wm8955->reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU; - wm8955->reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC; - wm8955->reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC; - wm8955->reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC; - wm8955->reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC; - wm8955->reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC; + snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME, + WM8955_LDVU, WM8955_LDVU); + snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME, + WM8955_RDVU, WM8955_RDVU); + snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME, + WM8955_LO1VU | WM8955_LO1ZC, + WM8955_LO1VU | WM8955_LO1ZC); + snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME, + WM8955_RO1VU | WM8955_RO1ZC, + WM8955_RO1VU | WM8955_RO1ZC); + snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME, + WM8955_LO2VU | WM8955_LO2ZC, + WM8955_LO2VU | WM8955_LO2ZC); + snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME, + WM8955_RO2VU | WM8955_RO2ZC, + WM8955_RO2VU | WM8955_RO2ZC); + snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME, + WM8955_MOZC, WM8955_MOZC); /* Also enable adaptive bass boost by default */ - wm8955->reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB; + snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB); /* Set platform data values */ if (pdata) { if (pdata->out2_speaker) - wm8955->reg_cache[WM8955_ADDITIONAL_CONTROL_2] + reg_cache[WM8955_ADDITIONAL_CONTROL_2] |= WM8955_ROUT2INV; if (pdata->monoin_diff) - wm8955->reg_cache[WM8955_MONO_OUT_MIX_1] + reg_cache[WM8955_MONO_OUT_MIX_1] |= WM8955_DMEN; } @@ -1002,6 +1014,7 @@ static __devinit int wm8955_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, wm8955); + wm8955->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8955, &wm8955_dai, 1); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 8d5efb333c33..4393394b7bc1 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -20,7 +20,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/wm8960.h> @@ -72,7 +71,6 @@ static const u16 wm8960_reg[WM8960_CACHEREGNUM] = { }; struct wm8960_priv { - u16 reg_cache[WM8960_CACHEREGNUM]; enum snd_soc_control_type control_type; void *control_data; int (*set_bias_level)(struct snd_soc_codec *, @@ -138,7 +136,8 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - return wm8960->deemph; + ucontrol->value.enumerated.item[0] = wm8960->deemph; + return 0; } static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, @@ -388,27 +387,28 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) { struct wm8960_data *pdata = codec->dev->platform_data; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_widget *w; - snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, ARRAY_SIZE(wm8960_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); /* In capless mode OUT3 is used to provide VMID for the * headphone outputs, otherwise it is used as a mono mixer. */ if (pdata && pdata->capless) { - snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_capless, + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, ARRAY_SIZE(wm8960_dapm_widgets_capless)); - snd_soc_dapm_add_routes(codec, audio_paths_capless, + snd_soc_dapm_add_routes(dapm, audio_paths_capless, ARRAY_SIZE(audio_paths_capless)); } else { - snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_out3, + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, ARRAY_SIZE(wm8960_dapm_widgets_out3)); - snd_soc_dapm_add_routes(codec, audio_paths_out3, + snd_soc_dapm_add_routes(dapm, audio_paths_out3, ARRAY_SIZE(audio_paths_out3)); } @@ -417,7 +417,9 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * list each time to find the desired power state do so now * and save the result. */ - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != &codec->dapm) + continue; if (strcmp(w->name, "LOUT1 PGA") == 0) wm8960->lout1 = w; if (strcmp(w->name, "ROUT1 PGA") == 0) @@ -572,7 +574,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Enable anti-pop features */ snd_soc_write(codec, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | @@ -610,7 +612,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -626,7 +628,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (codec->bias_level) { + switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: /* Enable anti pop mode */ snd_soc_update_bits(codec, WM8960_APOP1, @@ -681,7 +683,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - switch (codec->bias_level) { + switch (codec->dapm.bias_level) { case SND_SOC_BIAS_PREPARE: /* Disable HP discharge */ snd_soc_update_bits(codec, WM8960_APOP2, @@ -705,7 +707,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1012,6 +1014,7 @@ static __devinit int wm8960_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, wm8960); + wm8960->control_type = SND_SOC_I2C; wm8960->control_data = i2c; ret = snd_soc_register_codec(&i2c->dev, diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 8340485c9851..cdee8103d09b 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -23,7 +23,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -290,10 +289,9 @@ static u16 wm8961_reg_defaults[] = { struct wm8961_priv { enum snd_soc_control_type control_type; int sysclk; - u16 reg_cache[WM8961_MAX_REGISTER]; }; -static int wm8961_volatile_register(unsigned int reg) +static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8961_SOFTWARE_RESET: @@ -882,7 +880,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->bias_level == SND_SOC_BIAS_STANDBY) { + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { /* Enable bias generation */ reg = snd_soc_read(codec, WM8961_ANTI_POP); reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; @@ -897,7 +895,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_PREPARE) { + if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { /* VREF off */ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); reg &= ~WM8961_VREF; @@ -919,7 +917,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -959,6 +957,7 @@ static struct snd_soc_dai_driver wm8961_dai = { static int wm8961_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret = 0; u16 reg; @@ -1024,9 +1023,9 @@ static int wm8961_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8961_snd_controls, ARRAY_SIZE(wm8961_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8961_dapm_widgets, ARRAY_SIZE(wm8961_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); return 0; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index e8092745a207..500011eb8b2b 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -29,10 +29,10 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <sound/wm8962.h> +#include <trace/events/asoc.h> #include "wm8962.h" @@ -52,8 +52,6 @@ static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = { struct wm8962_priv { struct snd_soc_codec *codec; - u16 reg_cache[WM8962_MAX_REGISTER + 1]; - int sysclk; int sysclk_rate; @@ -1940,7 +1938,7 @@ static const struct wm8962_reg_access { [21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */ }; -static int wm8962_volatile_register(unsigned int reg) +static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].vol) return 1; @@ -1948,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg) return 0; } -static int wm8962_readable_register(unsigned int reg) +static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].read) return 1; @@ -1958,7 +1956,7 @@ static int wm8962_readable_register(unsigned int reg) static int wm8962_reset(struct snd_soc_codec *codec) { - return snd_soc_write(codec, WM8962_SOFTWARE_RESET, 0); + return snd_soc_write(codec, WM8962_SOFTWARE_RESET, 0x6243); } static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0); @@ -1991,8 +1989,7 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - u16 *reg_cache = wm8962->reg_cache; + u16 *reg_cache = codec->reg_cache; int ret; /* Apply the update (if any) */ @@ -2020,8 +2017,7 @@ static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - u16 *reg_cache = wm8962->reg_cache; + u16 *reg_cache = codec->reg_cache; int ret; /* Apply the update (if any) */ @@ -2329,8 +2325,7 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - u16 *reg_cache = wm8962->reg_cache; + u16 *reg_cache = codec->reg_cache; int reg; switch (w->shift) { @@ -2682,6 +2677,7 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = { static int wm8962_add_widgets(struct snd_soc_codec *codec) { struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); + struct snd_soc_dapm_context *dapm = &codec->dapm; snd_soc_add_controls(codec, wm8962_snd_controls, ARRAY_SIZE(wm8962_snd_controls)); @@ -2693,33 +2689,33 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec) ARRAY_SIZE(wm8962_spk_stereo_controls)); - snd_soc_dapm_new_controls(codec, wm8962_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets, ARRAY_SIZE(wm8962_dapm_widgets)); if (pdata && pdata->spk_mono) - snd_soc_dapm_new_controls(codec, wm8962_dapm_spk_mono_widgets, + snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets, ARRAY_SIZE(wm8962_dapm_spk_mono_widgets)); else - snd_soc_dapm_new_controls(codec, wm8962_dapm_spk_stereo_widgets, + snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_stereo_widgets, ARRAY_SIZE(wm8962_dapm_spk_stereo_widgets)); - snd_soc_dapm_add_routes(codec, wm8962_intercon, + snd_soc_dapm_add_routes(dapm, wm8962_intercon, ARRAY_SIZE(wm8962_intercon)); if (pdata && pdata->spk_mono) - snd_soc_dapm_add_routes(codec, wm8962_spk_mono_intercon, + snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon, ARRAY_SIZE(wm8962_spk_mono_intercon)); else - snd_soc_dapm_add_routes(codec, wm8962_spk_stereo_intercon, + snd_soc_dapm_add_routes(dapm, wm8962_spk_stereo_intercon, ARRAY_SIZE(wm8962_spk_stereo_intercon)); - snd_soc_dapm_disable_pin(codec, "Beep"); + snd_soc_dapm_disable_pin(dapm, "Beep"); return 0; } static void wm8962_sync_cache(struct snd_soc_codec *codec) { - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + u16 *reg_cache = codec->reg_cache; int i; if (!codec->cache_sync) @@ -2732,13 +2728,13 @@ static void wm8962_sync_cache(struct snd_soc_codec *codec) /* Sync back cached values if they're different from the * hardware default. */ - for (i = 1; i < ARRAY_SIZE(wm8962->reg_cache); i++) { + for (i = 1; i < codec->driver->reg_cache_size; i++) { if (i == WM8962_SOFTWARE_RESET) continue; - if (wm8962->reg_cache[i] == wm8962_reg[i]) + if (reg_cache[i] == wm8962_reg[i]) continue; - snd_soc_write(codec, i, wm8962->reg_cache[i]); + snd_soc_write(codec, i, reg_cache[i]); } codec->cache_sync = 0; @@ -2819,7 +2815,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int ret; - if (level == codec->bias_level) + if (level == codec->dapm.bias_level) return 0; switch (level) { @@ -2833,7 +2829,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); if (ret != 0) { @@ -2883,7 +2879,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, wm8962->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -3141,7 +3137,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, pr_debug("FLL Fvco=%dHz\n", target); - /* Find an appropraite FLL_FRATIO and factor it out of the target */ + /* Find an appropriate 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; @@ -3339,7 +3335,7 @@ static irqreturn_t wm8962_irq(int irq, void *data) int mask; int active; - mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); + mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2_MASK); active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); active &= ~mask; @@ -3353,6 +3349,12 @@ static irqreturn_t wm8962_irq(int irq, void *data) if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) { dev_dbg(codec->dev, "Microphone event detected\n"); +#ifndef CONFIG_SND_SOC_WM8962_MODULE + trace_snd_soc_jack_irq(dev_name(codec->dev)); +#endif + + pm_wakeup_event(codec->dev, 300); + schedule_delayed_work(&wm8962->mic_work, msecs_to_jiffies(250)); } @@ -3406,12 +3408,11 @@ EXPORT_SYMBOL_GPL(wm8962_mic_detect); #ifdef CONFIG_PM static int wm8962_resume(struct snd_soc_codec *codec) { - struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); u16 *reg_cache = codec->reg_cache; int i; /* Restore the registers */ - for (i = 1; i < ARRAY_SIZE(wm8962->reg_cache); i++) { + for (i = 1; i < codec->driver->reg_cache_size; i++) { switch (i) { case WM8962_SOFTWARE_RESET: continue; @@ -3439,6 +3440,7 @@ static void wm8962_beep_work(struct work_struct *work) struct wm8962_priv *wm8962 = container_of(work, struct wm8962_priv, beep_work); struct snd_soc_codec *codec = wm8962->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int i; int reg = 0; int best = 0; @@ -3455,16 +3457,16 @@ static void wm8962_beep_work(struct work_struct *work) reg = WM8962_BEEP_ENA | (best << WM8962_BEEP_RATE_SHIFT); - snd_soc_dapm_enable_pin(codec, "Beep"); + snd_soc_dapm_enable_pin(dapm, "Beep"); } else { dev_dbg(codec->dev, "Disabling beep\n"); - snd_soc_dapm_disable_pin(codec, "Beep"); + snd_soc_dapm_disable_pin(dapm, "Beep"); } snd_soc_update_bits(codec, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA | WM8962_BEEP_RATE_MASK, reg); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } /* For usability define a way of injecting beep events for the device - @@ -3633,7 +3635,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value) struct snd_soc_codec *codec = wm8962->codec; snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset, - WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT); + WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT); } static int wm8962_gpio_direction_out(struct gpio_chip *chip, @@ -3705,13 +3707,14 @@ static int wm8962_probe(struct snd_soc_codec *codec) struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); struct i2c_client *i2c = container_of(codec->dev, struct i2c_client, dev); + u16 *reg_cache = codec->reg_cache; int i, trigger, irq_pol; wm8962->codec = codec; INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work); codec->cache_sync = 1; - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); if (ret != 0) { @@ -3804,7 +3807,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) /* Put the speakers into mono mode? */ if (pdata->spk_mono) - wm8962->reg_cache[WM8962_CLASS_D_CONTROL_2] + reg_cache[WM8962_CLASS_D_CONTROL_2] |= WM8962_SPK_MONO; /* Micbias setup, detection enable and detection @@ -3819,16 +3822,26 @@ static int wm8962_probe(struct snd_soc_codec *codec) } /* Latch volume update bits */ - wm8962->reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU; - wm8962->reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU; - wm8962->reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU; - wm8962->reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU; - wm8962->reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU; - wm8962->reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU; - wm8962->reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU; - wm8962->reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU; - wm8962->reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU; - wm8962->reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU; + snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); + snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); wm8962_add_widgets(codec); @@ -3870,7 +3883,6 @@ err_enable: err_get: regulator_bulk_free(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); err: - kfree(wm8962); return ret; } diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 63f6dbf5d070..572bb80627a4 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -25,7 +25,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "wm8971.h" @@ -333,10 +332,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8971_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8971_dapm_widgets, - ARRAY_SIZE(wm8971_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8971_dapm_widgets, + ARRAY_SIZE(wm8971_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -553,7 +553,7 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8971_PWR1, 0x0001); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -590,9 +590,11 @@ static struct snd_soc_dai_driver wm8971_dai = { static void wm8971_work(struct work_struct *work) { - struct snd_soc_codec *codec = - container_of(work, struct snd_soc_codec, delayed_work.work); - wm8971_set_bias_level(codec, codec->bias_level); + struct snd_soc_dapm_context *dapm = + container_of(work, struct snd_soc_dapm_context, + delayed_work.work); + struct snd_soc_codec *codec = dapm->codec; + wm8971_set_bias_level(codec, codec->dapm.bias_level); } static int wm8971_suspend(struct snd_soc_codec *codec, pm_message_t state) @@ -620,11 +622,11 @@ static int wm8971_resume(struct snd_soc_codec *codec) wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* charge wm8971 caps */ - if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); - codec->bias_level = SND_SOC_BIAS_ON; - queue_delayed_work(wm8971_workq, &codec->delayed_work, + codec->dapm.bias_level = SND_SOC_BIAS_ON; + queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, msecs_to_jiffies(1000)); } @@ -643,7 +645,7 @@ static int wm8971_probe(struct snd_soc_codec *codec) return ret; } - INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work); + INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8971_work); wm8971_workq = create_workqueue("wm8971"); if (wm8971_workq == NULL) return -ENOMEM; @@ -653,8 +655,8 @@ static int wm8971_probe(struct snd_soc_codec *codec) /* charge output caps - set vmid to 5k for quick power up */ reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); - codec->bias_level = SND_SOC_BIAS_STANDBY; - queue_delayed_work(wm8971_workq, &codec->delayed_work, + codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; + queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, msecs_to_jiffies(1000)); /* set the update bits */ @@ -718,6 +720,7 @@ static __devinit int wm8971_i2c_probe(struct i2c_client *i2c, if (wm8971 == NULL) return -ENOMEM; + wm8971->control_type = SND_SOC_I2C; i2c_set_clientdata(i2c, wm8971); ret = snd_soc_register_codec(&i2c->dev, diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index b4363f6d19b3..ca646a822444 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -23,7 +23,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -52,7 +51,6 @@ static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { struct wm8974_priv { enum snd_soc_control_type control_type; - u16 reg_cache[WM8974_CACHEREGNUM]; }; #define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0) @@ -274,10 +272,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8974_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets, - ARRAY_SIZE(wm8974_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_controls(dapm, wm8974_dapm_widgets, + ARRAY_SIZE(wm8974_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -530,7 +529,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8974_POWER1, power1 | 0x3); mdelay(100); @@ -547,7 +546,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 13b979a71a7c..85e3e630e763 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <asm/div64.h> @@ -60,7 +59,6 @@ struct wm8978_priv { unsigned int f_opclk; int mclk_idx; enum wm8978_sysclk_src sysclk; - u16 reg_cache[WM8978_CACHEREGNUM]; }; static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"}; @@ -95,6 +93,7 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0); static const struct snd_kcontrol_new wm8978_snd_controls[] = { @@ -146,19 +145,19 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = { SOC_SINGLE("DAC Playback Limiter Threshold", WM8978_DAC_LIMITER_2, 4, 7, 0), - SOC_SINGLE("DAC Playback Limiter Boost", - WM8978_DAC_LIMITER_2, 0, 15, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Volume", + WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv), SOC_ENUM("ALC Enable Switch", alc1), SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0), SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0), - SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 7, 0), + SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0), SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0), SOC_ENUM("ALC Capture Mode", alc3), - SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 15, 0), - SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 15, 0), + SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0), + SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 10, 0), SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0), SOC_SINGLE("ALC Capture Noise Gate Threshold", @@ -213,8 +212,10 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = { WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1), /* DAC / ADC oversampling */ - SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0), - SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0), + SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, + 5, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, + 5, 1, 0), }; /* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */ @@ -355,11 +356,12 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8978_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8978_dapm_widgets, - ARRAY_SIZE(wm8978_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_dapm_new_controls(dapm, wm8978_dapm_widgets, + ARRAY_SIZE(wm8978_dapm_widgets)); /* set up the WM8978 audio map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -837,7 +839,7 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec, /* bit 3: enable bias, bit 2: enable I/O tie off buffer */ power1 |= 0xc; - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1 | 0x3); @@ -857,7 +859,7 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1); - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -966,7 +968,7 @@ static int wm8978_probe(struct snd_soc_codec *codec) * written. */ for (i = 0; i < ARRAY_SIZE(update_reg); i++) - ((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100; + snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100); /* Reset the codec */ ret = snd_soc_write(codec, WM8978_RESET, 0); diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index fd2e7cca1228..bae510acdec8 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -26,7 +26,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -533,10 +532,11 @@ static int eqmode_put(struct snd_kcontrol *kcontrol, static int wm8985_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8985_dapm_widgets, - ARRAY_SIZE(wm8985_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, + snd_soc_dapm_new_controls(dapm, wm8985_dapm_widgets, + ARRAY_SIZE(wm8985_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -879,7 +879,7 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, 1 << WM8985_VMIDSEL_SHIFT); break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies), wm8985->supplies); if (ret) { @@ -939,7 +939,7 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index d7f259711970..d7170f1381aa 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -25,7 +25,6 @@ #include <sound/pcm_params.h> #include <sound/tlv.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "wm8988.h" @@ -54,7 +53,6 @@ struct wm8988_priv { unsigned int sysclk; enum snd_soc_control_type control_type; struct snd_pcm_hw_constraint_list *sysclk_constraints; - u16 reg_cache[WM8988_NUM_REG]; }; @@ -677,7 +675,7 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* VREF, VMID=2x5k */ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1); @@ -693,7 +691,7 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8988_PWR1, 0x0000); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -759,6 +757,7 @@ static int wm8988_resume(struct snd_soc_codec *codec) static int wm8988_probe(struct snd_soc_codec *codec) { struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret = 0; u16 reg; @@ -790,9 +789,9 @@ static int wm8988_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm8988_snd_controls, ARRAY_SIZE(wm8988_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8988_dapm_widgets, ARRAY_SIZE(wm8988_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 264828e4e67c..100aeee5ba96 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -23,7 +23,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include <asm/div64.h> @@ -914,11 +913,12 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm8990_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm8990_dapm_widgets, - ARRAY_SIZE(wm8990_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_dapm_new_controls(dapm, wm8990_dapm_widgets, + ARRAY_SIZE(wm8990_dapm_widgets)); /* set up the WM8990 audio map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -1170,7 +1170,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Enable all output discharge bits */ snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE | WM8990_DIS_RLINE | WM8990_DIS_OUT3 | @@ -1183,7 +1183,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, WM8990_VMIDTOG); /* Delay to allow output caps to discharge */ - msleep(msecs_to_jiffies(300)); + msleep(300); /* Disable VMIDTOG */ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST | @@ -1195,17 +1195,17 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, /* Enable outputs */ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00); - msleep(msecs_to_jiffies(50)); + msleep(50); /* Enable VMID at 2x50k */ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02); - msleep(msecs_to_jiffies(100)); + msleep(100); /* Enable VREF */ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03); - msleep(msecs_to_jiffies(600)); + msleep(600); /* Enable BUFIOEN */ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST | @@ -1250,7 +1250,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, /* Disable VMID */ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01); - msleep(msecs_to_jiffies(300)); + msleep(300); /* Enable all output discharge bits */ snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE | @@ -1266,7 +1266,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c new file mode 100644 index 000000000000..3c2ee1bb73cd --- /dev/null +++ b/sound/soc/codecs/wm8991.c @@ -0,0 +1,1427 @@ +/* + * wm8991.c -- WM8991 ALSA Soc Audio driver + * + * Copyright 2007-2010 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * 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 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/version.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 <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <asm/div64.h> + +#include "wm8991.h" + +struct wm8991_priv { + enum snd_soc_control_type control_type; + unsigned int pcmclk; +}; + +static const u16 wm8991_reg_defs[] = { + 0x8991, /* R0 - Reset */ + 0x0000, /* R1 - Power Management (1) */ + 0x6000, /* R2 - Power Management (2) */ + 0x0000, /* R3 - Power Management (3) */ + 0x4050, /* R4 - Audio Interface (1) */ + 0x4000, /* R5 - Audio Interface (2) */ + 0x01C8, /* R6 - Clocking (1) */ + 0x0000, /* R7 - Clocking (2) */ + 0x0040, /* R8 - Audio Interface (3) */ + 0x0040, /* R9 - Audio Interface (4) */ + 0x0004, /* R10 - DAC CTRL */ + 0x00C0, /* R11 - Left DAC Digital Volume */ + 0x00C0, /* R12 - Right DAC Digital Volume */ + 0x0000, /* R13 - Digital Side Tone */ + 0x0100, /* R14 - ADC CTRL */ + 0x00C0, /* R15 - Left ADC Digital Volume */ + 0x00C0, /* R16 - Right ADC Digital Volume */ + 0x0000, /* R17 */ + 0x0000, /* R18 - GPIO CTRL 1 */ + 0x1000, /* R19 - GPIO1 & GPIO2 */ + 0x1010, /* R20 - GPIO3 & GPIO4 */ + 0x1010, /* R21 - GPIO5 & GPIO6 */ + 0x8000, /* R22 - GPIOCTRL 2 */ + 0x0800, /* R23 - GPIO_POL */ + 0x008B, /* R24 - Left Line Input 1&2 Volume */ + 0x008B, /* R25 - Left Line Input 3&4 Volume */ + 0x008B, /* R26 - Right Line Input 1&2 Volume */ + 0x008B, /* R27 - Right Line Input 3&4 Volume */ + 0x0000, /* R28 - Left Output Volume */ + 0x0000, /* R29 - Right Output Volume */ + 0x0066, /* R30 - Line Outputs Volume */ + 0x0022, /* R31 - Out3/4 Volume */ + 0x0079, /* R32 - Left OPGA Volume */ + 0x0079, /* R33 - Right OPGA Volume */ + 0x0003, /* R34 - Speaker Volume */ + 0x0003, /* R35 - ClassD1 */ + 0x0000, /* R36 */ + 0x0100, /* R37 - ClassD3 */ + 0x0000, /* R38 */ + 0x0000, /* R39 - Input Mixer1 */ + 0x0000, /* R40 - Input Mixer2 */ + 0x0000, /* R41 - Input Mixer3 */ + 0x0000, /* R42 - Input Mixer4 */ + 0x0000, /* R43 - Input Mixer5 */ + 0x0000, /* R44 - Input Mixer6 */ + 0x0000, /* R45 - Output Mixer1 */ + 0x0000, /* R46 - Output Mixer2 */ + 0x0000, /* R47 - Output Mixer3 */ + 0x0000, /* R48 - Output Mixer4 */ + 0x0000, /* R49 - Output Mixer5 */ + 0x0000, /* R50 - Output Mixer6 */ + 0x0180, /* R51 - Out3/4 Mixer */ + 0x0000, /* R52 - Line Mixer1 */ + 0x0000, /* R53 - Line Mixer2 */ + 0x0000, /* R54 - Speaker Mixer */ + 0x0000, /* R55 - Additional Control */ + 0x0000, /* R56 - AntiPOP1 */ + 0x0000, /* R57 - AntiPOP2 */ + 0x0000, /* R58 - MICBIAS */ + 0x0000, /* R59 */ + 0x0008, /* R60 - PLL1 */ + 0x0031, /* R61 - PLL2 */ + 0x0026, /* R62 - PLL3 */ +}; + +#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0) + +static const unsigned int rec_mix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(-1500, 600), +}; + +static const unsigned int in_pga_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000), +}; + +static const unsigned int out_mix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(0, -2100), +}; + +static const unsigned int out_pga_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 127, TLV_DB_LINEAR_ITEM(-7300, 600), +}; + +static const unsigned int out_omix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(-600, 0), +}; + +static const unsigned int out_dac_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 255, TLV_DB_LINEAR_ITEM(-7163, 0), +}; + +static const unsigned int in_adc_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763), +}; + +static const unsigned int out_sidetone_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 31, TLV_DB_LINEAR_ITEM(-3600, 0), +}; + +static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int ret; + u16 val; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* now hit the volume update bits (always bit 8) */ + val = snd_soc_read(codec, reg); + return snd_soc_write(codec, reg, val | 0x0100); +} + +static const char *wm8991_digital_sidetone[] = +{"None", "Left ADC", "Right ADC", "Reserved"}; + +static const struct soc_enum wm8991_left_digital_sidetone_enum = + SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE, + WM8991_ADC_TO_DACL_SHIFT, + WM8991_ADC_TO_DACL_MASK, + wm8991_digital_sidetone); + +static const struct soc_enum wm8991_right_digital_sidetone_enum = + SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE, + WM8991_ADC_TO_DACR_SHIFT, + WM8991_ADC_TO_DACR_MASK, + wm8991_digital_sidetone); + +static const char *wm8991_adcmode[] = +{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; + +static const struct soc_enum wm8991_right_adcmode_enum = + SOC_ENUM_SINGLE(WM8991_ADC_CTRL, + WM8991_ADC_HPF_CUT_SHIFT, + WM8991_ADC_HPF_CUT_MASK, + wm8991_adcmode); + +static const struct snd_kcontrol_new wm8991_snd_controls[] = { + /* INMIXL */ + SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0), + SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0), + /* INMIXR */ + SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0), + SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0), + + /* LOMIX */ + SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv), + + /* ROMIX */ + SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv), + + /* LOUT */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME, + WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0), + + /* ROUT */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME, + WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0), + + /* LOPGA */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME, + WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME, + WM8991_LOPGAZC_BIT, 1, 0), + + /* ROPGA */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME, + WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME, + WM8991_ROPGAZC_BIT, 1, 0), + + SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LONMUTE_BIT, 1, 0), + SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LOPMUTE_BIT, 1, 0), + SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LOATTN_BIT, 1, 0), + SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_RONMUTE_BIT, 1, 0), + SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_ROPMUTE_BIT, 1, 0), + SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_ROATTN_BIT, 1, 0), + + SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT3MUTE_BIT, 1, 0), + SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT3ATTN_BIT, 1, 0), + + SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT4MUTE_BIT, 1, 0), + SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT4ATTN_BIT, 1, 0), + + SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1, + WM8991_CDMODE_BIT, 1, 0), + + SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME, + WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0), + SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3, + WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0), + SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3, + WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume", + WM8991_LEFT_DAC_DIGITAL_VOLUME, + WM8991_DACL_VOL_SHIFT, + WM8991_DACL_VOL_MASK, + 0, + out_dac_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume", + WM8991_RIGHT_DAC_DIGITAL_VOLUME, + WM8991_DACR_VOL_SHIFT, + WM8991_DACR_VOL_MASK, + 0, + out_dac_tlv), + + SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum), + SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum), + + SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE, + WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE, + WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + + SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL, + WM8991_ADC_HPF_ENA_BIT, 1, 0), + + SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume", + WM8991_LEFT_ADC_DIGITAL_VOLUME, + WM8991_ADCL_VOL_SHIFT, + WM8991_ADCL_VOL_MASK, + 0, + in_adc_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume", + WM8991_RIGHT_ADC_DIGITAL_VOLUME, + WM8991_ADCR_VOL_SHIFT, + WM8991_ADCR_VOL_MASK, + 0, + in_adc_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume", + WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LIN12VOL_SHIFT, + WM8991_LIN12VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LI12ZC_BIT, 1, 0), + + SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LI12MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume", + WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LIN34VOL_SHIFT, + WM8991_LIN34VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LI34ZC_BIT, 1, 0), + + SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LI34MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume", + WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RIN12VOL_SHIFT, + WM8991_RIN12VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RI12ZC_BIT, 1, 0), + + SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RI12MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume", + WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RIN34VOL_SHIFT, + WM8991_RIN34VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RI34ZC_BIT, 1, 0), + + SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RI34MUTE_BIT, 1, 0), +}; + +/* + * _DAPM_ Controls + */ +static int inmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + u16 reg, fakepower; + + reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2); + fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS); + + if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) | + (1 << WM8991_AINLMUX_PWR_BIT))) + reg |= WM8991_AINL_ENA; + else + reg &= ~WM8991_AINL_ENA; + + if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) | + (1 << WM8991_AINRMUX_PWR_BIT))) + reg |= WM8991_AINR_ENA; + else + reg &= ~WM8991_AINL_ENA; + + snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg); + return 0; +} + +static int outmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + u32 reg_shift = kcontrol->private_value & 0xfff; + int ret = 0; + u16 reg; + + switch (reg_shift) { + case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER1); + if (reg & WM8991_LDLO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 1 LDLO Set\n"); + ret = -1; + } + break; + + case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER2); + if (reg & WM8991_RDRO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 2 RDRO Set\n"); + ret = -1; + } + break; + + case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER); + if (reg & WM8991_LDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer LDSPK Set\n"); + ret = -1; + } + break; + + case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER); + if (reg & WM8991_RDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer RDSPK Set\n"); + ret = -1; + } + break; + } + + return ret; +} + +/* INMIX dB values */ +static const unsigned int in_mix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600), +}; + +/* Left In PGA Connections */ +static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = { + SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0), + SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = { + SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0), + SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0), +}; + +/* Right In PGA Connections */ +static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = { + SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0), + SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = { + SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0), + SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0), +}; + +/* INMIXL */ +static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = { + SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3, + WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT, + 7, 0, in_mix_tlv), + SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT, + 1, 0), + SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT, + 1, 0), +}; + +/* INMIXR */ +static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = { + SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4, + WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT, + 7, 0, in_mix_tlv), + SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT, + 1, 0), + SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT, + 1, 0), +}; + +/* AINLMUX */ +static const char *wm8991_ainlmux[] = +{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; + +static const struct soc_enum wm8991_ainlmux_enum = + SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT, + ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux); + +static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls = + SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum); + +/* DIFFINL */ + +/* AINRMUX */ +static const char *wm8991_ainrmux[] = +{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; + +static const struct soc_enum wm8991_ainrmux_enum = + SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT, + ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux); + +static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls = + SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum); + +/* RXVOICE */ +static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = { + SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT, + WM8991_LR4BVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT, + WM8991_RL4BVOL_MASK, 0, in_mix_tlv), +}; + +/* LOMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = { + SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LRBLO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LLBLO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LRI3LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LLI3LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LR12LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LL12LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1, + WM8991_LDLO_BIT, 1, 0), +}; + +/* ROMIX */ +static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = { + SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RLBRO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RRBRO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RLI3RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RRI3RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RL12RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RR12RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2, + WM8991_RDRO_BIT, 1, 0), +}; + +/* LONMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = { + SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LLOPGALON_BIT, 1, 0), + SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LROPGALON_BIT, 1, 0), + SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1, + WM8991_LOPLON_BIT, 1, 0), +}; + +/* LOPMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = { + SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1, + WM8991_LR12LOP_BIT, 1, 0), + SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1, + WM8991_LL12LOP_BIT, 1, 0), + SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LLOPGALOP_BIT, 1, 0), +}; + +/* RONMIX */ +static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = { + SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RROPGARON_BIT, 1, 0), + SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RLOPGARON_BIT, 1, 0), + SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2, + WM8991_ROPRON_BIT, 1, 0), +}; + +/* ROPMIX */ +static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = { + SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2, + WM8991_RL12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2, + WM8991_RR12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RROPGAROP_BIT, 1, 0), +}; + +/* OUT3MIX */ +static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = { + SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER, + WM8991_LI4O3_BIT, 1, 0), + SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER, + WM8991_LPGAO3_BIT, 1, 0), +}; + +/* OUT4MIX */ +static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = { + SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER, + WM8991_RPGAO4_BIT, 1, 0), + SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER, + WM8991_RI4O4_BIT, 1, 0), +}; + +/* SPKMIX */ +static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = { + SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_LI2SPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_LB2SPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER, + WM8991_LOPGASPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER, + WM8991_LDSPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER, + WM8991_RDSPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER, + WM8991_ROPGASPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_RL12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_RI2SPK_BIT, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = { + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + SND_SOC_DAPM_INPUT("LIN3"), + SND_SOC_DAPM_INPUT("LIN4RXN"), + SND_SOC_DAPM_INPUT("RIN3"), + SND_SOC_DAPM_INPUT("RIN4RXP"), + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + SND_SOC_DAPM_INPUT("Internal ADC Source"), + + /* DACs */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2, + WM8991_ADCL_ENA_BIT, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2, + WM8991_ADCR_ENA_BIT, 0), + + /* Input PGAs */ + SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT, + 0, &wm8991_dapm_lin12_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)), + SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT, + 0, &wm8991_dapm_lin34_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)), + SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT, + 0, &wm8991_dapm_rin12_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)), + SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT, + 0, &wm8991_dapm_rin34_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)), + + /* INMIXL */ + SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0, + &wm8991_dapm_inmixl_controls[0], + ARRAY_SIZE(wm8991_dapm_inmixl_controls), + inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* AINLMUX */ + SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0, + &wm8991_dapm_ainlmux_controls, inmixer_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* INMIXR */ + SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0, + &wm8991_dapm_inmixr_controls[0], + ARRAY_SIZE(wm8991_dapm_inmixr_controls), + inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* AINRMUX */ + SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0, + &wm8991_dapm_ainrmux_controls, inmixer_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3, + WM8991_DACL_ENA_BIT, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3, + WM8991_DACR_ENA_BIT, 0), + + /* LOMIX */ + SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT, + 0, &wm8991_dapm_lomix_controls[0], + ARRAY_SIZE(wm8991_dapm_lomix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + + /* LONMIX */ + SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0, + &wm8991_dapm_lonmix_controls[0], + ARRAY_SIZE(wm8991_dapm_lonmix_controls)), + + /* LOPMIX */ + SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0, + &wm8991_dapm_lopmix_controls[0], + ARRAY_SIZE(wm8991_dapm_lopmix_controls)), + + /* OUT3MIX */ + SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0, + &wm8991_dapm_out3mix_controls[0], + ARRAY_SIZE(wm8991_dapm_out3mix_controls)), + + /* SPKMIX */ + SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0, + &wm8991_dapm_spkmix_controls[0], + ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event, + SND_SOC_DAPM_PRE_REG), + + /* OUT4MIX */ + SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0, + &wm8991_dapm_out4mix_controls[0], + ARRAY_SIZE(wm8991_dapm_out4mix_controls)), + + /* ROPMIX */ + SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0, + &wm8991_dapm_ropmix_controls[0], + ARRAY_SIZE(wm8991_dapm_ropmix_controls)), + + /* RONMIX */ + SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0, + &wm8991_dapm_ronmix_controls[0], + ARRAY_SIZE(wm8991_dapm_ronmix_controls)), + + /* ROMIX */ + SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT, + 0, &wm8991_dapm_romix_controls[0], + ARRAY_SIZE(wm8991_dapm_romix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + + /* LOUT PGA */ + SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0, + NULL, 0), + + /* ROUT PGA */ + SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0, + NULL, 0), + + /* LOPGA */ + SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0, + NULL, 0), + + /* ROPGA */ + SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0, + NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_MICBIAS("MICBIAS", WM8991_POWER_MANAGEMENT_1, + WM8991_MICBIAS_ENA_BIT, 0), + + SND_SOC_DAPM_OUTPUT("LON"), + SND_SOC_DAPM_OUTPUT("LOP"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("SPKN"), + SND_SOC_DAPM_OUTPUT("SPKP"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("OUT4"), + SND_SOC_DAPM_OUTPUT("ROP"), + SND_SOC_DAPM_OUTPUT("RON"), + SND_SOC_DAPM_OUTPUT("OUT"), + + SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Make DACs turn on when playing even if not mixed into any outputs */ + {"Internal DAC Sink", NULL, "Left DAC"}, + {"Internal DAC Sink", NULL, "Right DAC"}, + + /* Make ADCs turn on when recording even if not mixed from any inputs */ + {"Left ADC", NULL, "Internal ADC Source"}, + {"Right ADC", NULL, "Internal ADC Source"}, + + /* Input Side */ + /* LIN12 PGA */ + {"LIN12 PGA", "LIN1 Switch", "LIN1"}, + {"LIN12 PGA", "LIN2 Switch", "LIN2"}, + /* LIN34 PGA */ + {"LIN34 PGA", "LIN3 Switch", "LIN3"}, + {"LIN34 PGA", "LIN4 Switch", "LIN4RXN"}, + /* INMIXL */ + {"INMIXL", "Record Left Volume", "LOMIX"}, + {"INMIXL", "LIN2 Volume", "LIN2"}, + {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, + {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, + /* AINLMUX */ + {"AINLMUX", "INMIXL Mix", "INMIXL"}, + {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"}, + {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"}, + {"AINLMUX", "RXVOICE Mix", "LIN4RXN"}, + {"AINLMUX", "RXVOICE Mix", "RIN4RXP"}, + /* ADC */ + {"Left ADC", NULL, "AINLMUX"}, + + /* RIN12 PGA */ + {"RIN12 PGA", "RIN1 Switch", "RIN1"}, + {"RIN12 PGA", "RIN2 Switch", "RIN2"}, + /* RIN34 PGA */ + {"RIN34 PGA", "RIN3 Switch", "RIN3"}, + {"RIN34 PGA", "RIN4 Switch", "RIN4RXP"}, + /* INMIXL */ + {"INMIXR", "Record Right Volume", "ROMIX"}, + {"INMIXR", "RIN2 Volume", "RIN2"}, + {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, + {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, + /* AINRMUX */ + {"AINRMUX", "INMIXR Mix", "INMIXR"}, + {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"}, + {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"}, + {"AINRMUX", "RXVOICE Mix", "LIN4RXN"}, + {"AINRMUX", "RXVOICE Mix", "RIN4RXP"}, + /* ADC */ + {"Right ADC", NULL, "AINRMUX"}, + + /* LOMIX */ + {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"}, + {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"}, + {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"}, + {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"}, + {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"}, + + /* ROMIX */ + {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"}, + {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"}, + {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"}, + {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"}, + {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"}, + + /* SPKMIX */ + {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"}, + {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"}, + {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"}, + {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"}, + {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"}, + {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"}, + {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"}, + {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"}, + + /* LONMIX */ + {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"}, + {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"}, + {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"}, + + /* LOPMIX */ + {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"}, + + /* OUT3MIX */ + {"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"}, + {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"}, + + /* OUT4MIX */ + {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"}, + {"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"}, + + /* RONMIX */ + {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"}, + {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"}, + {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"}, + + /* ROPMIX */ + {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"}, + + /* Out Mixer PGAs */ + {"LOPGA", NULL, "LOMIX"}, + {"ROPGA", NULL, "ROMIX"}, + + {"LOUT PGA", NULL, "LOMIX"}, + {"ROUT PGA", NULL, "ROMIX"}, + + /* Output Pins */ + {"LON", NULL, "LONMIX"}, + {"LOP", NULL, "LOPMIX"}, + {"OUT", NULL, "OUT3MIX"}, + {"LOUT", NULL, "LOUT PGA"}, + {"SPKN", NULL, "SPKMIX"}, + {"ROUT", NULL, "ROUT PGA"}, + {"OUT4", NULL, "OUT4MIX"}, + {"ROP", NULL, "ROPMIX"}, + {"RON", NULL, "RONMIX"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 div2; + u32 n; + u32 k; +}; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 16) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } else + pll_div->div2 = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8991 N value outwith recommended range! N = %d\n", Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, int src, unsigned int freq_in, unsigned int freq_out) +{ + u16 reg; + struct snd_soc_codec *codec = codec_dai->codec; + struct _pll_div pll_div; + + if (freq_in && freq_out) { + pll_factors(&pll_div, freq_out * 4, freq_in); + + /* Turn on PLL */ + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2); + reg |= WM8991_PLL_ENA; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg); + + /* sysclk comes from PLL */ + reg = snd_soc_read(codec, WM8991_CLOCKING_2); + snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC); + + /* set up N , fractional mode and pre-divisor if necessary */ + snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM | + (pll_div.div2 ? WM8991_PRESCALE : 0)); + snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8)); + snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF)); + } else { + /* Turn on PLL */ + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2); + reg &= ~WM8991_PLL_ENA; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg); + } + return 0; +} + +/* + * Set's ADC and Voice DAC format. + */ +static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 audio1, audio3; + + audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1); + audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + audio3 &= ~WM8991_AIF_MSTR1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + audio3 |= WM8991_AIF_MSTR1; + break; + default: + return -EINVAL; + } + + audio1 &= ~WM8991_AIF_FMT_MASK; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + audio1 |= WM8991_AIF_TMF_I2S; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audio1 |= WM8991_AIF_TMF_RIGHTJ; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_LEFT_J: + audio1 |= WM8991_AIF_TMF_LEFTJ; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_A: + audio1 |= WM8991_AIF_TMF_DSP; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_B: + audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1); + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3); + return 0; +} + +static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + + switch (div_id) { + case WM8991_MCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_2) & + ~WM8991_MCLK_DIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_DACCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_2) & + ~WM8991_DAC_CLKDIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_ADCCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_2) & + ~WM8991_ADC_CLKDIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_BCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_1) & + ~WM8991_BCLK_DIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_1, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8991_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; + u16 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1); + + audio1 &= ~WM8991_AIF_WL_MASK; + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + audio1 |= WM8991_AIF_WL_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + audio1 |= WM8991_AIF_WL_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio1 |= WM8991_AIF_WL_32BITS; + break; + } + + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1); + return 0; +} + +static int wm8991_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 val; + + val = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE; + if (mute) + snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE); + else + snd_soc_write(codec, WM8991_DAC_CTRL, val); + return 0; +} + +static int wm8991_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 val; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID=2*50k */ + val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) & + ~WM8991_VMID_MODE_MASK; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_cache_sync(codec); + /* Enable all output discharge bits */ + snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | + WM8991_DIS_RLINE | WM8991_DIS_OUT3 | + WM8991_DIS_OUT4 | WM8991_DIS_LOUT | + WM8991_DIS_ROUT); + + /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_VMIDTOG); + + /* Delay to allow output caps to discharge */ + msleep(300); + + /* Disable VMIDTOG */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL); + + /* disable all output discharge bits */ + snd_soc_write(codec, WM8991_ANTIPOP1, 0); + + /* Enable outputs */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00); + + msleep(50); + + /* Enable VMID at 2x50k */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02); + + msleep(100); + + /* Enable VREF */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03); + + msleep(600); + + /* Enable BUFIOEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_BUFIOEN); + + /* Disable outputs */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN); + } + + /* VMID=2*250k */ + val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) & + ~WM8991_VMID_MODE_MASK; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4); + break; + + case SND_SOC_BIAS_OFF: + /* Enable POBCTRL and SOFT_ST */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_POBCTRL | WM8991_BUFIOEN); + + /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_BUFIOEN); + + /* mute DAC */ + val = snd_soc_read(codec, WM8991_DAC_CTRL); + snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE); + + /* Enable any disabled outputs */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03); + + /* Disable VMID */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01); + + msleep(300); + + /* Enable all output discharge bits */ + snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | + WM8991_DIS_RLINE | WM8991_DIS_OUT3 | + WM8991_DIS_OUT4 | WM8991_DIS_LOUT | + WM8991_DIS_ROUT); + + /* Disable VREF */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, 0x0); + codec->cache_sync = 1; + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int wm8991_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8991_resume(struct snd_soc_codec *codec) +{ + wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +/* power down chip */ +static int wm8991_remove(struct snd_soc_codec *codec) +{ + wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8991_probe(struct snd_soc_codec *codec) +{ + struct wm8991_priv *wm8991; + int ret; + unsigned int reg; + + wm8991 = snd_soc_codec_get_drvdata(codec); + + ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); + return ret; + } + + ret = wm8991_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + return ret; + } + + wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + reg = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_4); + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_4, reg | WM8991_ALRCGPIO1); + + reg = snd_soc_read(codec, WM8991_GPIO1_GPIO2) & + ~WM8991_GPIO1_SEL_MASK; + snd_soc_write(codec, WM8991_GPIO1_GPIO2, reg | 1); + + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1); + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, reg | WM8991_VREF_ENA| + WM8991_VMID_MODE_MASK); + + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2); + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg | WM8991_OPCLK_ENA); + + snd_soc_write(codec, WM8991_DAC_CTRL, 0); + snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); + + snd_soc_add_controls(codec, wm8991_snd_controls, + ARRAY_SIZE(wm8991_snd_controls)); + + snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets, + ARRAY_SIZE(wm8991_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, audio_map, + ARRAY_SIZE(audio_map)); + return 0; +} + +#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops wm8991_ops = { + .hw_params = wm8991_hw_params, + .digital_mute = wm8991_mute, + .set_fmt = wm8991_set_dai_fmt, + .set_clkdiv = wm8991_set_dai_clkdiv, + .set_pll = wm8991_set_dai_pll +}; + +/* + * The WM8991 supports 2 different and mutually exclusive DAI + * configurations. + * + * 1. ADC/DAC on Primary Interface + * 2. ADC on Primary Interface/DAC on secondary + */ +static struct snd_soc_dai_driver wm8991_dai = { + /* ADC/DAC on primary */ + .name = "wm8991", + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8991_FORMATS + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8991_FORMATS + }, + .ops = &wm8991_ops +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8991 = { + .probe = wm8991_probe, + .remove = wm8991_remove, + .suspend = wm8991_suspend, + .resume = wm8991_resume, + .set_bias_level = wm8991_set_bias_level, + .reg_cache_size = WM8991_MAX_REGISTER + 1, + .reg_word_size = sizeof(u16), + .reg_cache_default = wm8991_reg_defs +}; + +static __devinit int wm8991_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8991_priv *wm8991; + int ret; + + wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL); + if (!wm8991) + return -ENOMEM; + + wm8991->control_type = SND_SOC_I2C; + i2c_set_clientdata(i2c, wm8991); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8991, &wm8991_dai, 1); + if (ret < 0) + kfree(wm8991); + return ret; +} + +static __devexit int wm8991_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 wm8991_i2c_id[] = { + { "wm8991", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id); + +static struct i2c_driver wm8991_i2c_driver = { + .driver = { + .name = "wm8991", + .owner = THIS_MODULE, + }, + .probe = wm8991_i2c_probe, + .remove = __devexit_p(wm8991_i2c_remove), + .id_table = wm8991_i2c_id, +}; + +static int __init wm8991_modinit(void) +{ + int ret; + ret = i2c_add_driver(&wm8991_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n", + ret); + } + return 0; +} +module_init(wm8991_modinit); + +static void __exit wm8991_exit(void) +{ + i2c_del_driver(&wm8991_i2c_driver); +} +module_exit(wm8991_exit); + +MODULE_DESCRIPTION("ASoC WM8991 driver"); +MODULE_AUTHOR("Graeme Gregory"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h new file mode 100644 index 000000000000..8a942efd18a5 --- /dev/null +++ b/sound/soc/codecs/wm8991.h @@ -0,0 +1,833 @@ +/* + * wm8991.h -- audio driver for WM8991 + * + * 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _WM8991_H +#define _WM8991_H + +/* + * Register values. + */ +#define WM8991_RESET 0x00 +#define WM8991_POWER_MANAGEMENT_1 0x01 +#define WM8991_POWER_MANAGEMENT_2 0x02 +#define WM8991_POWER_MANAGEMENT_3 0x03 +#define WM8991_AUDIO_INTERFACE_1 0x04 +#define WM8991_AUDIO_INTERFACE_2 0x05 +#define WM8991_CLOCKING_1 0x06 +#define WM8991_CLOCKING_2 0x07 +#define WM8991_AUDIO_INTERFACE_3 0x08 +#define WM8991_AUDIO_INTERFACE_4 0x09 +#define WM8991_DAC_CTRL 0x0A +#define WM8991_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8991_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8991_DIGITAL_SIDE_TONE 0x0D +#define WM8991_ADC_CTRL 0x0E +#define WM8991_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8991_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8991_GPIO_CTRL_1 0x12 +#define WM8991_GPIO1_GPIO2 0x13 +#define WM8991_GPIO3_GPIO4 0x14 +#define WM8991_GPIO5_GPIO6 0x15 +#define WM8991_GPIOCTRL_2 0x16 +#define WM8991_GPIO_POL 0x17 +#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8991_LEFT_OUTPUT_VOLUME 0x1C +#define WM8991_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8991_LINE_OUTPUTS_VOLUME 0x1E +#define WM8991_OUT3_4_VOLUME 0x1F +#define WM8991_LEFT_OPGA_VOLUME 0x20 +#define WM8991_RIGHT_OPGA_VOLUME 0x21 +#define WM8991_SPEAKER_VOLUME 0x22 +#define WM8991_CLASSD1 0x23 +#define WM8991_CLASSD3 0x25 +#define WM8991_INPUT_MIXER1 0x27 +#define WM8991_INPUT_MIXER2 0x28 +#define WM8991_INPUT_MIXER3 0x29 +#define WM8991_INPUT_MIXER4 0x2A +#define WM8991_INPUT_MIXER5 0x2B +#define WM8991_INPUT_MIXER6 0x2C +#define WM8991_OUTPUT_MIXER1 0x2D +#define WM8991_OUTPUT_MIXER2 0x2E +#define WM8991_OUTPUT_MIXER3 0x2F +#define WM8991_OUTPUT_MIXER4 0x30 +#define WM8991_OUTPUT_MIXER5 0x31 +#define WM8991_OUTPUT_MIXER6 0x32 +#define WM8991_OUT3_4_MIXER 0x33 +#define WM8991_LINE_MIXER1 0x34 +#define WM8991_LINE_MIXER2 0x35 +#define WM8991_SPEAKER_MIXER 0x36 +#define WM8991_ADDITIONAL_CONTROL 0x37 +#define WM8991_ANTIPOP1 0x38 +#define WM8991_ANTIPOP2 0x39 +#define WM8991_MICBIAS 0x3A +#define WM8991_PLL1 0x3C +#define WM8991_PLL2 0x3D +#define WM8991_PLL3 0x3E +#define WM8991_INTDRIVBITS 0x3F + +#define WM8991_REGISTER_COUNT 60 +#define WM8991_MAX_REGISTER 0x3F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset + */ +#define WM8991_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8991_SPK_ENA 0x1000 /* SPK_ENA */ +#define WM8991_SPK_ENA_BIT 12 +#define WM8991_OUT3_ENA 0x0800 /* OUT3_ENA */ +#define WM8991_OUT3_ENA_BIT 11 +#define WM8991_OUT4_ENA 0x0400 /* OUT4_ENA */ +#define WM8991_OUT4_ENA_BIT 10 +#define WM8991_LOUT_ENA 0x0200 /* LOUT_ENA */ +#define WM8991_LOUT_ENA_BIT 9 +#define WM8991_ROUT_ENA 0x0100 /* ROUT_ENA */ +#define WM8991_ROUT_ENA_BIT 8 +#define WM8991_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */ +#define WM8991_MICBIAS_ENA_BIT 4 +#define WM8991_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */ +#define WM8991_VREF_ENA 0x0001 /* VREF_ENA */ +#define WM8991_VREF_ENA_BIT 0 + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8991_PLL_ENA 0x8000 /* PLL_ENA */ +#define WM8991_PLL_ENA_BIT 15 +#define WM8991_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8991_TSHUT_ENA_BIT 14 +#define WM8991_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8991_TSHUT_OPDIS_BIT 13 +#define WM8991_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8991_OPCLK_ENA_BIT 11 +#define WM8991_AINL_ENA 0x0200 /* AINL_ENA */ +#define WM8991_AINL_ENA_BIT 9 +#define WM8991_AINR_ENA 0x0100 /* AINR_ENA */ +#define WM8991_AINR_ENA_BIT 8 +#define WM8991_LIN34_ENA 0x0080 /* LIN34_ENA */ +#define WM8991_LIN34_ENA_BIT 7 +#define WM8991_LIN12_ENA 0x0040 /* LIN12_ENA */ +#define WM8991_LIN12_ENA_BIT 6 +#define WM8991_RIN34_ENA 0x0020 /* RIN34_ENA */ +#define WM8991_RIN34_ENA_BIT 5 +#define WM8991_RIN12_ENA 0x0010 /* RIN12_ENA */ +#define WM8991_RIN12_ENA_BIT 4 +#define WM8991_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8991_ADCL_ENA_BIT 1 +#define WM8991_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8991_ADCR_ENA_BIT 0 + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8991_LON_ENA 0x2000 /* LON_ENA */ +#define WM8991_LON_ENA_BIT 13 +#define WM8991_LOP_ENA 0x1000 /* LOP_ENA */ +#define WM8991_LOP_ENA_BIT 12 +#define WM8991_RON_ENA 0x0800 /* RON_ENA */ +#define WM8991_RON_ENA_BIT 11 +#define WM8991_ROP_ENA 0x0400 /* ROP_ENA */ +#define WM8991_ROP_ENA_BIT 10 +#define WM8991_LOPGA_ENA 0x0080 /* LOPGA_ENA */ +#define WM8991_LOPGA_ENA_BIT 7 +#define WM8991_ROPGA_ENA 0x0040 /* ROPGA_ENA */ +#define WM8991_ROPGA_ENA_BIT 6 +#define WM8991_LOMIX_ENA 0x0020 /* LOMIX_ENA */ +#define WM8991_LOMIX_ENA_BIT 5 +#define WM8991_ROMIX_ENA 0x0010 /* ROMIX_ENA */ +#define WM8991_ROMIX_ENA_BIT 4 +#define WM8991_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8991_DACL_ENA_BIT 1 +#define WM8991_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8991_DACR_ENA_BIT 0 + +/* + * R4 (0x04) - Audio Interface (1) + */ +#define WM8991_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8991_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8991_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8991_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8991_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8991_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8991_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8991_AIF_WL_16BITS (0 << 5) +#define WM8991_AIF_WL_20BITS (1 << 5) +#define WM8991_AIF_WL_24BITS (2 << 5) +#define WM8991_AIF_WL_32BITS (3 << 5) +#define WM8991_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8991_AIF_TMF_RIGHTJ (0 << 3) +#define WM8991_AIF_TMF_LEFTJ (1 << 3) +#define WM8991_AIF_TMF_I2S (2 << 3) +#define WM8991_AIF_TMF_DSP (3 << 3) + +/* + * R5 (0x05) - Audio Interface (2) + */ +#define WM8991_DACL_SRC 0x8000 /* DACL_SRC */ +#define WM8991_DACR_SRC 0x4000 /* DACR_SRC */ +#define WM8991_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8991_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8991_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8991_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8991_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8991_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8991_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8991_LOOPBACK 0x0001 /* LOOPBACK */ + +/* + * R6 (0x06) - Clocking (1) + */ +#define WM8991_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8991_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8991_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */ +#define WM8991_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8991_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8991_BCLK_DIV_1 (0x0 << 1) +#define WM8991_BCLK_DIV_1_5 (0x1 << 1) +#define WM8991_BCLK_DIV_2 (0x2 << 1) +#define WM8991_BCLK_DIV_3 (0x3 << 1) +#define WM8991_BCLK_DIV_4 (0x4 << 1) +#define WM8991_BCLK_DIV_5_5 (0x5 << 1) +#define WM8991_BCLK_DIV_6 (0x6 << 1) +#define WM8991_BCLK_DIV_8 (0x7 << 1) +#define WM8991_BCLK_DIV_11 (0x8 << 1) +#define WM8991_BCLK_DIV_12 (0x9 << 1) +#define WM8991_BCLK_DIV_16 (0xA << 1) +#define WM8991_BCLK_DIV_22 (0xB << 1) +#define WM8991_BCLK_DIV_24 (0xC << 1) +#define WM8991_BCLK_DIV_32 (0xD << 1) +#define WM8991_BCLK_DIV_44 (0xE << 1) +#define WM8991_BCLK_DIV_48 (0xF << 1) + +/* + * R7 (0x07) - Clocking (2) + */ +#define WM8991_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8991_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8991_CLK_FORCE 0x2000 /* CLK_FORCE */ +#define WM8991_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */ +#define WM8991_MCLK_DIV_1 (0 << 11) +#define WM8991_MCLK_DIV_2 ( 2 << 11) +#define WM8991_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8991_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */ +#define WM8991_ADC_CLKDIV_1 (0 << 5) +#define WM8991_ADC_CLKDIV_1_5 (1 << 5) +#define WM8991_ADC_CLKDIV_2 (2 << 5) +#define WM8991_ADC_CLKDIV_3 (3 << 5) +#define WM8991_ADC_CLKDIV_4 (4 << 5) +#define WM8991_ADC_CLKDIV_5_5 (5 << 5) +#define WM8991_ADC_CLKDIV_6 (6 << 5) +#define WM8991_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */ +#define WM8991_DAC_CLKDIV_1 (0 << 2) +#define WM8991_DAC_CLKDIV_1_5 (1 << 2) +#define WM8991_DAC_CLKDIV_2 (2 << 2) +#define WM8991_DAC_CLKDIV_3 (3 << 2) +#define WM8991_DAC_CLKDIV_4 (4 << 2) +#define WM8991_DAC_CLKDIV_5_5 (5 << 2) +#define WM8991_DAC_CLKDIV_6 (6 << 2) + +/* + * R8 (0x08) - Audio Interface (3) + */ +#define WM8991_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8991_AIF_MSTR2 0x4000 /* AIF_MSTR2 */ +#define WM8991_AIF_SEL 0x2000 /* AIF_SEL */ +#define WM8991_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */ +#define WM8991_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */ + +/* + * R9 (0x09) - Audio Interface (4) + */ +#define WM8991_ALRCGPIO1 0x8000 /* ALRCGPIO1 */ +#define WM8991_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */ +#define WM8991_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8991_DACLRC_DIR 0x0800 /* DACLRC_DIR */ +#define WM8991_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */ + +/* + * R10 (0x0A) - DAC CTRL + */ +#define WM8991_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */ +#define WM8991_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8991_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8991_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8991_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */ +#define WM8991_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */ +#define WM8991_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8991_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8991_DACR_DATINV 0x0001 /* DACR_DATINV */ + +/* + * R11 (0x0B) - Left DAC Digital Volume + */ +#define WM8991_DAC_VU 0x0100 /* DAC_VU */ +#define WM8991_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8991_DACL_VOL_SHIFT 0 +/* + * R12 (0x0C) - Right DAC Digital Volume + */ +#define WM8991_DAC_VU 0x0100 /* DAC_VU */ +#define WM8991_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8991_DACR_VOL_SHIFT 0 +/* + * R13 (0x0D) - Digital Side Tone + */ +#define WM8991_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL - [12:9] */ +#define WM8991_ADCL_DAC_SVOL_SHIFT 9 +#define WM8991_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL - [8:5] */ +#define WM8991_ADCR_DAC_SVOL_SHIFT 5 +#define WM8991_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */ +#define WM8991_ADC_TO_DACL_SHIFT 2 +#define WM8991_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */ +#define WM8991_ADC_TO_DACR_SHIFT 0 + +/* + * R14 (0x0E) - ADC CTRL + */ +#define WM8991_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */ +#define WM8991_ADC_HPF_ENA_BIT 8 +#define WM8991_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */ +#define WM8991_ADC_HPF_CUT_SHIFT 5 +#define WM8991_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8991_ADCL_DATINV_BIT 1 +#define WM8991_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8991_ADCR_DATINV_BIT 0 + +/* + * R15 (0x0F) - Left ADC Digital Volume + */ +#define WM8991_ADC_VU 0x0100 /* ADC_VU */ +#define WM8991_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8991_ADCL_VOL_SHIFT 0 + +/* + * R16 (0x10) - Right ADC Digital Volume + */ +#define WM8991_ADC_VU 0x0100 /* ADC_VU */ +#define WM8991_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8991_ADCR_VOL_SHIFT 0 + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8991_IRQ 0x1000 /* IRQ */ +#define WM8991_TEMPOK 0x0800 /* TEMPOK */ +#define WM8991_MICSHRT 0x0400 /* MICSHRT */ +#define WM8991_MICDET 0x0200 /* MICDET */ +#define WM8991_PLL_LCK 0x0100 /* PLL_LCK */ +#define WM8991_GPI8_STATUS 0x0080 /* GPI8_STATUS */ +#define WM8991_GPI7_STATUS 0x0040 /* GPI7_STATUS */ +#define WM8991_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */ +#define WM8991_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */ +#define WM8991_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */ +#define WM8991_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */ +#define WM8991_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */ +#define WM8991_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */ + +/* + * R19 (0x13) - GPIO1 & GPIO2 + */ +#define WM8991_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */ +#define WM8991_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8991_GPIO2_PU 0x2000 /* GPIO2_PU */ +#define WM8991_GPIO2_PD 0x1000 /* GPIO2_PD */ +#define WM8991_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */ +#define WM8991_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */ +#define WM8991_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8991_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8991_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8991_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - GPIO3 & GPIO4 + */ +#define WM8991_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */ +#define WM8991_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8991_GPIO4_PU 0x2000 /* GPIO4_PU */ +#define WM8991_GPIO4_PD 0x1000 /* GPIO4_PD */ +#define WM8991_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */ +#define WM8991_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */ +#define WM8991_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8991_GPIO3_PU 0x0020 /* GPIO3_PU */ +#define WM8991_GPIO3_PD 0x0010 /* GPIO3_PD */ +#define WM8991_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */ + +/* + * R21 (0x15) - GPIO5 & GPIO6 + */ +#define WM8991_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */ +#define WM8991_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8991_GPIO6_PU 0x2000 /* GPIO6_PU */ +#define WM8991_GPIO6_PD 0x1000 /* GPIO6_PD */ +#define WM8991_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */ +#define WM8991_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */ +#define WM8991_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8991_GPIO5_PU 0x0020 /* GPIO5_PU */ +#define WM8991_GPIO5_PD 0x0010 /* GPIO5_PD */ +#define WM8991_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8991_RD_3W_ENA 0x8000 /* RD_3W_ENA */ +#define WM8991_MODE_3W4W 0x4000 /* MODE_3W4W */ +#define WM8991_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8991_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */ +#define WM8991_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */ +#define WM8991_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */ +#define WM8991_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */ +#define WM8991_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */ +#define WM8991_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8991_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */ +#define WM8991_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */ +#define WM8991_GPI7_ENA 0x0001 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8991_IRQ_INV 0x1000 /* IRQ_INV */ +#define WM8991_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8991_MICSHRT_POL 0x0400 /* MICSHRT_POL */ +#define WM8991_MICDET_POL 0x0200 /* MICDET_POL */ +#define WM8991_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */ +#define WM8991_GPI8_POL 0x0080 /* GPI8_POL */ +#define WM8991_GPI7_POL 0x0040 /* GPI7_POL */ +#define WM8991_GPIO6_POL 0x0020 /* GPIO6_POL */ +#define WM8991_GPIO5_POL 0x0010 /* GPIO5_POL */ +#define WM8991_GPIO4_POL 0x0008 /* GPIO4_POL */ +#define WM8991_GPIO3_POL 0x0004 /* GPIO3_POL */ +#define WM8991_GPIO2_POL 0x0002 /* GPIO2_POL */ +#define WM8991_GPIO1_POL 0x0001 /* GPIO1_POL */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_LI12MUTE 0x0080 /* LI12MUTE */ +#define WM8991_LI12MUTE_BIT 7 +#define WM8991_LI12ZC 0x0040 /* LI12ZC */ +#define WM8991_LI12ZC_BIT 6 +#define WM8991_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */ +#define WM8991_LIN12VOL_SHIFT 0 +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_LI34MUTE 0x0080 /* LI34MUTE */ +#define WM8991_LI34MUTE_BIT 7 +#define WM8991_LI34ZC 0x0040 /* LI34ZC */ +#define WM8991_LI34ZC_BIT 6 +#define WM8991_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */ +#define WM8991_LIN34VOL_SHIFT 0 + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_RI12MUTE 0x0080 /* RI12MUTE */ +#define WM8991_RI12MUTE_BIT 7 +#define WM8991_RI12ZC 0x0040 /* RI12ZC */ +#define WM8991_RI12ZC_BIT 6 +#define WM8991_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */ +#define WM8991_RIN12VOL_SHIFT 0 + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_RI34MUTE 0x0080 /* RI34MUTE */ +#define WM8991_RI34MUTE_BIT 7 +#define WM8991_RI34ZC 0x0040 /* RI34ZC */ +#define WM8991_RI34ZC_BIT 6 +#define WM8991_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */ +#define WM8991_RIN34VOL_SHIFT 0 + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_LOZC 0x0080 /* LOZC */ +#define WM8991_LOZC_BIT 7 +#define WM8991_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8991_LOUTVOL_SHIFT 0 +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_ROZC 0x0080 /* ROZC */ +#define WM8991_ROZC_BIT 7 +#define WM8991_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8991_ROUTVOL_SHIFT 0 +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8991_LONMUTE 0x0040 /* LONMUTE */ +#define WM8991_LONMUTE_BIT 6 +#define WM8991_LOPMUTE 0x0020 /* LOPMUTE */ +#define WM8991_LOPMUTE_BIT 5 +#define WM8991_LOATTN 0x0010 /* LOATTN */ +#define WM8991_LOATTN_BIT 4 +#define WM8991_RONMUTE 0x0004 /* RONMUTE */ +#define WM8991_RONMUTE_BIT 2 +#define WM8991_ROPMUTE 0x0002 /* ROPMUTE */ +#define WM8991_ROPMUTE_BIT 1 +#define WM8991_ROATTN 0x0001 /* ROATTN */ +#define WM8991_ROATTN_BIT 0 + +/* + * R31 (0x1F) - Out3/4 Volume + */ +#define WM8991_OUT3MUTE 0x0020 /* OUT3MUTE */ +#define WM8991_OUT3MUTE_BIT 5 +#define WM8991_OUT3ATTN 0x0010 /* OUT3ATTN */ +#define WM8991_OUT3ATTN_BIT 4 +#define WM8991_OUT4MUTE 0x0002 /* OUT4MUTE */ +#define WM8991_OUT4MUTE_BIT 1 +#define WM8991_OUT4ATTN 0x0001 /* OUT4ATTN */ +#define WM8991_OUT4ATTN_BIT 0 + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_LOPGAZC 0x0080 /* LOPGAZC */ +#define WM8991_LOPGAZC_BIT 7 +#define WM8991_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */ +#define WM8991_LOPGAVOL_SHIFT 0 + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_ROPGAZC 0x0080 /* ROPGAZC */ +#define WM8991_ROPGAZC_BIT 7 +#define WM8991_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */ +#define WM8991_ROPGAVOL_SHIFT 0 +/* + * R34 (0x22) - Speaker Volume + */ +#define WM8991_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */ +#define WM8991_SPKVOL_SHIFT 0 + +/* + * R35 (0x23) - ClassD1 + */ +#define WM8991_CDMODE 0x0100 /* CDMODE */ +#define WM8991_CDMODE_BIT 8 + +/* + * R37 (0x25) - ClassD3 + */ +#define WM8991_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */ +#define WM8991_DCGAIN_SHIFT 3 +#define WM8991_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */ +#define WM8991_ACGAIN_SHIFT 0 +/* + * R39 (0x27) - Input Mixer1 + */ +#define WM8991_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */ +#define WM8991_AINLMODE_SHIFT 2 +#define WM8991_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */ +#define WM8991_AINRMODE_SHIFT 0 + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8991_LMP4 0x0080 /* LMP4 */ +#define WM8991_LMP4_BIT 7 /* LMP4 */ +#define WM8991_LMN3 0x0040 /* LMN3 */ +#define WM8991_LMN3_BIT 6 /* LMN3 */ +#define WM8991_LMP2 0x0020 /* LMP2 */ +#define WM8991_LMP2_BIT 5 /* LMP2 */ +#define WM8991_LMN1 0x0010 /* LMN1 */ +#define WM8991_LMN1_BIT 4 /* LMN1 */ +#define WM8991_RMP4 0x0008 /* RMP4 */ +#define WM8991_RMP4_BIT 3 /* RMP4 */ +#define WM8991_RMN3 0x0004 /* RMN3 */ +#define WM8991_RMN3_BIT 2 /* RMN3 */ +#define WM8991_RMP2 0x0002 /* RMP2 */ +#define WM8991_RMP2_BIT 1 /* RMP2 */ +#define WM8991_RMN1 0x0001 /* RMN1 */ +#define WM8991_RMN1_BIT 0 /* RMN1 */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8991_L34MNB 0x0100 /* L34MNB */ +#define WM8991_L34MNB_BIT 8 +#define WM8991_L34MNBST 0x0080 /* L34MNBST */ +#define WM8991_L34MNBST_BIT 7 +#define WM8991_L12MNB 0x0020 /* L12MNB */ +#define WM8991_L12MNB_BIT 5 +#define WM8991_L12MNBST 0x0010 /* L12MNBST */ +#define WM8991_L12MNBST_BIT 4 +#define WM8991_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */ +#define WM8991_LDBVOL_SHIFT 0 + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8991_R34MNB 0x0100 /* R34MNB */ +#define WM8991_R34MNB_BIT 8 +#define WM8991_R34MNBST 0x0080 /* R34MNBST */ +#define WM8991_R34MNBST_BIT 7 +#define WM8991_R12MNB 0x0020 /* R12MNB */ +#define WM8991_R12MNB_BIT 5 +#define WM8991_R12MNBST 0x0010 /* R12MNBST */ +#define WM8991_R12MNBST_BIT 4 +#define WM8991_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */ +#define WM8991_RDBVOL_SHIFT 0 + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8991_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */ +#define WM8991_LI2BVOL_SHIFT 6 +#define WM8991_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */ +#define WM8991_LR4BVOL_SHIFT 3 +#define WM8991_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */ +#define WM8991_LL4BVOL_SHIFT 0 + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8991_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */ +#define WM8991_RI2BVOL_SHIFT 6 +#define WM8991_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */ +#define WM8991_RL4BVOL_SHIFT 3 +#define WM8991_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */ +#define WM8991_RR4BVOL_SHIFT 0 + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8991_LRBLO 0x0080 /* LRBLO */ +#define WM8991_LRBLO_BIT 7 +#define WM8991_LLBLO 0x0040 /* LLBLO */ +#define WM8991_LLBLO_BIT 6 +#define WM8991_LRI3LO 0x0020 /* LRI3LO */ +#define WM8991_LRI3LO_BIT 5 +#define WM8991_LLI3LO 0x0010 /* LLI3LO */ +#define WM8991_LLI3LO_BIT 4 +#define WM8991_LR12LO 0x0008 /* LR12LO */ +#define WM8991_LR12LO_BIT 3 +#define WM8991_LL12LO 0x0004 /* LL12LO */ +#define WM8991_LL12LO_BIT 2 +#define WM8991_LDLO 0x0001 /* LDLO */ +#define WM8991_LDLO_BIT 0 + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8991_RLBRO 0x0080 /* RLBRO */ +#define WM8991_RLBRO_BIT 7 +#define WM8991_RRBRO 0x0040 /* RRBRO */ +#define WM8991_RRBRO_BIT 6 +#define WM8991_RLI3RO 0x0020 /* RLI3RO */ +#define WM8991_RLI3RO_BIT 5 +#define WM8991_RRI3RO 0x0010 /* RRI3RO */ +#define WM8991_RRI3RO_BIT 4 +#define WM8991_RL12RO 0x0008 /* RL12RO */ +#define WM8991_RL12RO_BIT 3 +#define WM8991_RR12RO 0x0004 /* RR12RO */ +#define WM8991_RR12RO_BIT 2 +#define WM8991_RDRO 0x0001 /* RDRO */ +#define WM8991_RDRO_BIT 0 + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8991_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */ +#define WM8991_LLI3LOVOL_SHIFT 6 +#define WM8991_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */ +#define WM8991_LR12LOVOL_SHIFT 3 +#define WM8991_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */ +#define WM8991_LL12LOVOL_SHIFT 0 + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8991_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */ +#define WM8991_RRI3ROVOL_SHIFT 6 +#define WM8991_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */ +#define WM8991_RL12ROVOL_SHIFT 3 +#define WM8991_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */ +#define WM8991_RR12ROVOL_SHIFT 0 + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8991_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */ +#define WM8991_LRI3LOVOL_SHIFT 6 +#define WM8991_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */ +#define WM8991_LRBLOVOL_SHIFT 3 +#define WM8991_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */ +#define WM8991_LLBLOVOL_SHIFT 0 + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8991_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */ +#define WM8991_RLI3ROVOL_SHIFT 6 +#define WM8991_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */ +#define WM8991_RLBROVOL_SHIFT 3 +#define WM8991_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */ +#define WM8991_RRBROVOL_SHIFT 0 + +/* + * R51 (0x33) - Out3/4 Mixer + */ +#define WM8991_VSEL_MASK 0x0180 /* VSEL - [8:7] */ +#define WM8991_LI4O3 0x0020 /* LI4O3 */ +#define WM8991_LI4O3_BIT 5 +#define WM8991_LPGAO3 0x0010 /* LPGAO3 */ +#define WM8991_LPGAO3_BIT 4 +#define WM8991_RI4O4 0x0002 /* RI4O4 */ +#define WM8991_RI4O4_BIT 1 +#define WM8991_RPGAO4 0x0001 /* RPGAO4 */ +#define WM8991_RPGAO4_BIT 0 +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8991_LLOPGALON 0x0040 /* LLOPGALON */ +#define WM8991_LLOPGALON_BIT 6 +#define WM8991_LROPGALON 0x0020 /* LROPGALON */ +#define WM8991_LROPGALON_BIT 5 +#define WM8991_LOPLON 0x0010 /* LOPLON */ +#define WM8991_LOPLON_BIT 4 +#define WM8991_LR12LOP 0x0004 /* LR12LOP */ +#define WM8991_LR12LOP_BIT 2 +#define WM8991_LL12LOP 0x0002 /* LL12LOP */ +#define WM8991_LL12LOP_BIT 1 +#define WM8991_LLOPGALOP 0x0001 /* LLOPGALOP */ +#define WM8991_LLOPGALOP_BIT 0 +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8991_RROPGARON 0x0040 /* RROPGARON */ +#define WM8991_RROPGARON_BIT 6 +#define WM8991_RLOPGARON 0x0020 /* RLOPGARON */ +#define WM8991_RLOPGARON_BIT 5 +#define WM8991_ROPRON 0x0010 /* ROPRON */ +#define WM8991_ROPRON_BIT 4 +#define WM8991_RL12ROP 0x0004 /* RL12ROP */ +#define WM8991_RL12ROP_BIT 2 +#define WM8991_RR12ROP 0x0002 /* RR12ROP */ +#define WM8991_RR12ROP_BIT 1 +#define WM8991_RROPGAROP 0x0001 /* RROPGAROP */ +#define WM8991_RROPGAROP_BIT 0 + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8991_LB2SPK 0x0080 /* LB2SPK */ +#define WM8991_LB2SPK_BIT 7 +#define WM8991_RB2SPK 0x0040 /* RB2SPK */ +#define WM8991_RB2SPK_BIT 6 +#define WM8991_LI2SPK 0x0020 /* LI2SPK */ +#define WM8991_LI2SPK_BIT 5 +#define WM8991_RI2SPK 0x0010 /* RI2SPK */ +#define WM8991_RI2SPK_BIT 4 +#define WM8991_LOPGASPK 0x0008 /* LOPGASPK */ +#define WM8991_LOPGASPK_BIT 3 +#define WM8991_ROPGASPK 0x0004 /* ROPGASPK */ +#define WM8991_ROPGASPK_BIT 2 +#define WM8991_LDSPK 0x0002 /* LDSPK */ +#define WM8991_LDSPK_BIT 1 +#define WM8991_RDSPK 0x0001 /* RDSPK */ +#define WM8991_RDSPK_BIT 0 + +/* + * R55 (0x37) - Additional Control + */ +#define WM8991_VROI 0x0001 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8991_DIS_LLINE 0x0020 /* DIS_LLINE */ +#define WM8991_DIS_RLINE 0x0010 /* DIS_RLINE */ +#define WM8991_DIS_OUT3 0x0008 /* DIS_OUT3 */ +#define WM8991_DIS_OUT4 0x0004 /* DIS_OUT4 */ +#define WM8991_DIS_LOUT 0x0002 /* DIS_LOUT */ +#define WM8991_DIS_ROUT 0x0001 /* DIS_ROUT */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8991_SOFTST 0x0040 /* SOFTST */ +#define WM8991_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8991_BUFDCOPEN 0x0004 /* BUFDCOPEN */ +#define WM8991_POBCTRL 0x0002 /* POBCTRL */ +#define WM8991_VMIDTOG 0x0001 /* VMIDTOG */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8991_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */ +#define WM8991_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */ +#define WM8991_MCD 0x0004 /* MCD */ +#define WM8991_MBSEL 0x0001 /* MBSEL */ + +/* + * R60 (0x3C) - PLL1 + */ +#define WM8991_SDM 0x0080 /* SDM */ +#define WM8991_PRESCALE 0x0040 /* PRESCALE */ +#define WM8991_PLLN_MASK 0x000F /* PLLN - [3:0] */ + +/* + * R61 (0x3D) - PLL2 + */ +#define WM8991_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */ + +/* + * R62 (0x3E) - PLL3 + */ +#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */ + +/* + * R63 (0x3F) - Internal Driver Bits + */ +#define WM8991_INMIXL_PWR_BIT 0 +#define WM8991_AINLMUX_PWR_BIT 1 +#define WM8991_INMIXR_PWR_BIT 2 +#define WM8991_AINRMUX_PWR_BIT 3 + +#define WM8991_MCLK_DIV 0 +#define WM8991_DACCLK_DIV 1 +#define WM8991_ADCCLK_DIV 2 +#define WM8991_BCLK_DIV 3 + +#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\ + tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +#endif /* _WM8991_H */ diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 589e3fa24734..056aef904347 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -24,7 +24,6 @@ #include <sound/pcm_params.h> #include <sound/tlv.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/wm8993.h> @@ -226,7 +225,6 @@ static struct { struct wm8993_priv { struct wm_hubs_data hubs_data; - u16 reg_cache[WM8993_REGISTER_COUNT]; struct regulator_bulk_data supplies[WM8993_NUM_SUPPLIES]; struct wm8993_platform_data pdata; enum snd_soc_control_type control_type; @@ -244,7 +242,7 @@ struct wm8993_priv { int fll_src; }; -static int wm8993_volatile(unsigned int reg) +static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8993_SOFTWARE_RESET: @@ -326,7 +324,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, pr_debug("Fvco=%dHz\n", target); - /* Find an appropraite FLL_FRATIO and factor it out of the target */ + /* Find an appropriate 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; @@ -735,6 +733,7 @@ static int class_w_put(struct snd_kcontrol *kcontrol, 0); } wm8993->class_w_users++; + wm8993->hubs_data.class_w = true; } /* Implement the change */ @@ -751,6 +750,7 @@ static int class_w_put(struct snd_kcontrol *kcontrol, WM8993_CP_DYN_V); } wm8993->class_w_users--; + wm8993->hubs_data.class_w = false; } dev_dbg(codec->dev, "Indirect DAC use count now %d\n", @@ -968,7 +968,7 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); if (ret != 0) @@ -1029,6 +1029,12 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA, 0); + snd_soc_update_bits(codec, WM8993_ANTIPOP2, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC, 0); + #ifdef CONFIG_REGULATOR /* Post 2.6.34 we will be able to get a callback when * the regulators are disabled which we can use but @@ -1043,7 +1049,7 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1225,7 +1231,7 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream, - wm8993->fs); for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { cur_val = abs((wm8993->sysclk_rate / - clk_sys_rates[i].ratio) - wm8993->fs);; + clk_sys_rates[i].ratio) - wm8993->fs); if (cur_val < best_val) { best = i; best_val = cur_val; @@ -1422,6 +1428,7 @@ static struct snd_soc_dai_driver wm8993_dai = { static int wm8993_probe(struct snd_soc_codec *codec) { struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret, i, val; wm8993->hubs_data.hp_startup_mode = 1; @@ -1503,11 +1510,11 @@ static int wm8993_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8993_eq_controls)); } - snd_soc_dapm_new_controls(codec, wm8993_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets, ARRAY_SIZE(wm8993_dapm_widgets)); wm_hubs_add_analogue_controls(codec); - snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes)); + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff, wm8993->pdata.lineout2_diff); diff --git a/sound/soc/codecs/wm8994-tables.c b/sound/soc/codecs/wm8994-tables.c new file mode 100644 index 000000000000..a87adbd05ee1 --- /dev/null +++ b/sound/soc/codecs/wm8994-tables.c @@ -0,0 +1,3147 @@ +#include "wm8994.h" + +const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { + { 0xFFFF, 0xFFFF }, /* R0 - Software Reset */ + { 0x3B37, 0x3B37 }, /* R1 - Power Management (1) */ + { 0x6BF0, 0x6BF0 }, /* R2 - Power Management (2) */ + { 0x3FF0, 0x3FF0 }, /* R3 - Power Management (3) */ + { 0x3F3F, 0x3F3F }, /* R4 - Power Management (4) */ + { 0x3F0F, 0x3F0F }, /* R5 - Power Management (5) */ + { 0x003F, 0x003F }, /* R6 - Power Management (6) */ + { 0x0000, 0x0000 }, /* R7 */ + { 0x0000, 0x0000 }, /* R8 */ + { 0x0000, 0x0000 }, /* R9 */ + { 0x0000, 0x0000 }, /* R10 */ + { 0x0000, 0x0000 }, /* R11 */ + { 0x0000, 0x0000 }, /* R12 */ + { 0x0000, 0x0000 }, /* R13 */ + { 0x0000, 0x0000 }, /* R14 */ + { 0x0000, 0x0000 }, /* R15 */ + { 0x0000, 0x0000 }, /* R16 */ + { 0x0000, 0x0000 }, /* R17 */ + { 0x0000, 0x0000 }, /* R18 */ + { 0x0000, 0x0000 }, /* R19 */ + { 0x0000, 0x0000 }, /* R20 */ + { 0x01C0, 0x01C0 }, /* R21 - Input Mixer (1) */ + { 0x0000, 0x0000 }, /* R22 */ + { 0x0000, 0x0000 }, /* R23 */ + { 0x00DF, 0x01DF }, /* R24 - Left Line Input 1&2 Volume */ + { 0x00DF, 0x01DF }, /* R25 - Left Line Input 3&4 Volume */ + { 0x00DF, 0x01DF }, /* R26 - Right Line Input 1&2 Volume */ + { 0x00DF, 0x01DF }, /* R27 - Right Line Input 3&4 Volume */ + { 0x00FF, 0x01FF }, /* R28 - Left Output Volume */ + { 0x00FF, 0x01FF }, /* R29 - Right Output Volume */ + { 0x0077, 0x0077 }, /* R30 - Line Outputs Volume */ + { 0x0030, 0x0030 }, /* R31 - HPOUT2 Volume */ + { 0x00FF, 0x01FF }, /* R32 - Left OPGA Volume */ + { 0x00FF, 0x01FF }, /* R33 - Right OPGA Volume */ + { 0x007F, 0x007F }, /* R34 - SPKMIXL Attenuation */ + { 0x017F, 0x017F }, /* R35 - SPKMIXR Attenuation */ + { 0x003F, 0x003F }, /* R36 - SPKOUT Mixers */ + { 0x003F, 0x003F }, /* R37 - ClassD */ + { 0x00FF, 0x01FF }, /* R38 - Speaker Volume Left */ + { 0x00FF, 0x01FF }, /* R39 - Speaker Volume Right */ + { 0x00FF, 0x00FF }, /* R40 - Input Mixer (2) */ + { 0x01B7, 0x01B7 }, /* R41 - Input Mixer (3) */ + { 0x01B7, 0x01B7 }, /* R42 - Input Mixer (4) */ + { 0x01C7, 0x01C7 }, /* R43 - Input Mixer (5) */ + { 0x01C7, 0x01C7 }, /* R44 - Input Mixer (6) */ + { 0x01FF, 0x01FF }, /* R45 - Output Mixer (1) */ + { 0x01FF, 0x01FF }, /* R46 - Output Mixer (2) */ + { 0x0FFF, 0x0FFF }, /* R47 - Output Mixer (3) */ + { 0x0FFF, 0x0FFF }, /* R48 - Output Mixer (4) */ + { 0x0FFF, 0x0FFF }, /* R49 - Output Mixer (5) */ + { 0x0FFF, 0x0FFF }, /* R50 - Output Mixer (6) */ + { 0x0038, 0x0038 }, /* R51 - HPOUT2 Mixer */ + { 0x0077, 0x0077 }, /* R52 - Line Mixer (1) */ + { 0x0077, 0x0077 }, /* R53 - Line Mixer (2) */ + { 0x03FF, 0x03FF }, /* R54 - Speaker Mixer */ + { 0x00C1, 0x00C1 }, /* R55 - Additional Control */ + { 0x00F0, 0x00F0 }, /* R56 - AntiPOP (1) */ + { 0x01EF, 0x01EF }, /* R57 - AntiPOP (2) */ + { 0x00FF, 0x00FF }, /* R58 - MICBIAS */ + { 0x000F, 0x000F }, /* R59 - LDO 1 */ + { 0x0007, 0x0007 }, /* R60 - LDO 2 */ + { 0xFFFF, 0xFFFF }, /* R61 */ + { 0xFFFF, 0xFFFF }, /* R62 */ + { 0x0000, 0x0000 }, /* R63 */ + { 0x0000, 0x0000 }, /* R64 */ + { 0x0000, 0x0000 }, /* R65 */ + { 0x0000, 0x0000 }, /* R66 */ + { 0x0000, 0x0000 }, /* R67 */ + { 0x0000, 0x0000 }, /* R68 */ + { 0x0000, 0x0000 }, /* R69 */ + { 0x0000, 0x0000 }, /* R70 */ + { 0x0000, 0x0000 }, /* R71 */ + { 0x0000, 0x0000 }, /* R72 */ + { 0x0000, 0x0000 }, /* R73 */ + { 0x0000, 0x0000 }, /* R74 */ + { 0x0000, 0x0000 }, /* R75 */ + { 0x8000, 0x8000 }, /* R76 - Charge Pump (1) */ + { 0x0000, 0x0000 }, /* R77 */ + { 0x0000, 0x0000 }, /* R78 */ + { 0x0000, 0x0000 }, /* R79 */ + { 0x0000, 0x0000 }, /* R80 */ + { 0x0301, 0x0301 }, /* R81 - Class W (1) */ + { 0x0000, 0x0000 }, /* R82 */ + { 0x0000, 0x0000 }, /* R83 */ + { 0x333F, 0x333F }, /* R84 - DC Servo (1) */ + { 0x0FEF, 0x0FEF }, /* R85 - DC Servo (2) */ + { 0x0000, 0x0000 }, /* R86 */ + { 0xFFFF, 0xFFFF }, /* R87 - DC Servo (4) */ + { 0x0333, 0x0000 }, /* R88 - DC Servo Readback */ + { 0x0000, 0x0000 }, /* R89 */ + { 0x0000, 0x0000 }, /* R90 */ + { 0x0000, 0x0000 }, /* R91 */ + { 0x0000, 0x0000 }, /* R92 */ + { 0x0000, 0x0000 }, /* R93 */ + { 0x0000, 0x0000 }, /* R94 */ + { 0x0000, 0x0000 }, /* R95 */ + { 0x00EE, 0x00EE }, /* R96 - Analogue HP (1) */ + { 0x0000, 0x0000 }, /* R97 */ + { 0x0000, 0x0000 }, /* R98 */ + { 0x0000, 0x0000 }, /* R99 */ + { 0x0000, 0x0000 }, /* R100 */ + { 0x0000, 0x0000 }, /* R101 */ + { 0x0000, 0x0000 }, /* R102 */ + { 0x0000, 0x0000 }, /* R103 */ + { 0x0000, 0x0000 }, /* R104 */ + { 0x0000, 0x0000 }, /* R105 */ + { 0x0000, 0x0000 }, /* R106 */ + { 0x0000, 0x0000 }, /* R107 */ + { 0x0000, 0x0000 }, /* R108 */ + { 0x0000, 0x0000 }, /* R109 */ + { 0x0000, 0x0000 }, /* R110 */ + { 0x0000, 0x0000 }, /* R111 */ + { 0x0000, 0x0000 }, /* R112 */ + { 0x0000, 0x0000 }, /* R113 */ + { 0x0000, 0x0000 }, /* R114 */ + { 0x0000, 0x0000 }, /* R115 */ + { 0x0000, 0x0000 }, /* R116 */ + { 0x0000, 0x0000 }, /* R117 */ + { 0x0000, 0x0000 }, /* R118 */ + { 0x0000, 0x0000 }, /* R119 */ + { 0x0000, 0x0000 }, /* R120 */ + { 0x0000, 0x0000 }, /* R121 */ + { 0x0000, 0x0000 }, /* R122 */ + { 0x0000, 0x0000 }, /* R123 */ + { 0x0000, 0x0000 }, /* R124 */ + { 0x0000, 0x0000 }, /* R125 */ + { 0x0000, 0x0000 }, /* R126 */ + { 0x0000, 0x0000 }, /* R127 */ + { 0x0000, 0x0000 }, /* R128 */ + { 0x0000, 0x0000 }, /* R129 */ + { 0x0000, 0x0000 }, /* R130 */ + { 0x0000, 0x0000 }, /* R131 */ + { 0x0000, 0x0000 }, /* R132 */ + { 0x0000, 0x0000 }, /* R133 */ + { 0x0000, 0x0000 }, /* R134 */ + { 0x0000, 0x0000 }, /* R135 */ + { 0x0000, 0x0000 }, /* R136 */ + { 0x0000, 0x0000 }, /* R137 */ + { 0x0000, 0x0000 }, /* R138 */ + { 0x0000, 0x0000 }, /* R139 */ + { 0x0000, 0x0000 }, /* R140 */ + { 0x0000, 0x0000 }, /* R141 */ + { 0x0000, 0x0000 }, /* R142 */ + { 0x0000, 0x0000 }, /* R143 */ + { 0x0000, 0x0000 }, /* R144 */ + { 0x0000, 0x0000 }, /* R145 */ + { 0x0000, 0x0000 }, /* R146 */ + { 0x0000, 0x0000 }, /* R147 */ + { 0x0000, 0x0000 }, /* R148 */ + { 0x0000, 0x0000 }, /* R149 */ + { 0x0000, 0x0000 }, /* R150 */ + { 0x0000, 0x0000 }, /* R151 */ + { 0x0000, 0x0000 }, /* R152 */ + { 0x0000, 0x0000 }, /* R153 */ + { 0x0000, 0x0000 }, /* R154 */ + { 0x0000, 0x0000 }, /* R155 */ + { 0x0000, 0x0000 }, /* R156 */ + { 0x0000, 0x0000 }, /* R157 */ + { 0x0000, 0x0000 }, /* R158 */ + { 0x0000, 0x0000 }, /* R159 */ + { 0x0000, 0x0000 }, /* R160 */ + { 0x0000, 0x0000 }, /* R161 */ + { 0x0000, 0x0000 }, /* R162 */ + { 0x0000, 0x0000 }, /* R163 */ + { 0x0000, 0x0000 }, /* R164 */ + { 0x0000, 0x0000 }, /* R165 */ + { 0x0000, 0x0000 }, /* R166 */ + { 0x0000, 0x0000 }, /* R167 */ + { 0x0000, 0x0000 }, /* R168 */ + { 0x0000, 0x0000 }, /* R169 */ + { 0x0000, 0x0000 }, /* R170 */ + { 0x0000, 0x0000 }, /* R171 */ + { 0x0000, 0x0000 }, /* R172 */ + { 0x0000, 0x0000 }, /* R173 */ + { 0x0000, 0x0000 }, /* R174 */ + { 0x0000, 0x0000 }, /* R175 */ + { 0x0000, 0x0000 }, /* R176 */ + { 0x0000, 0x0000 }, /* R177 */ + { 0x0000, 0x0000 }, /* R178 */ + { 0x0000, 0x0000 }, /* R179 */ + { 0x0000, 0x0000 }, /* R180 */ + { 0x0000, 0x0000 }, /* R181 */ + { 0x0000, 0x0000 }, /* R182 */ + { 0x0000, 0x0000 }, /* R183 */ + { 0x0000, 0x0000 }, /* R184 */ + { 0x0000, 0x0000 }, /* R185 */ + { 0x0000, 0x0000 }, /* R186 */ + { 0x0000, 0x0000 }, /* R187 */ + { 0x0000, 0x0000 }, /* R188 */ + { 0x0000, 0x0000 }, /* R189 */ + { 0x0000, 0x0000 }, /* R190 */ + { 0x0000, 0x0000 }, /* R191 */ + { 0x0000, 0x0000 }, /* R192 */ + { 0x0000, 0x0000 }, /* R193 */ + { 0x0000, 0x0000 }, /* R194 */ + { 0x0000, 0x0000 }, /* R195 */ + { 0x0000, 0x0000 }, /* R196 */ + { 0x0000, 0x0000 }, /* R197 */ + { 0x0000, 0x0000 }, /* R198 */ + { 0x0000, 0x0000 }, /* R199 */ + { 0x0000, 0x0000 }, /* R200 */ + { 0x0000, 0x0000 }, /* R201 */ + { 0x0000, 0x0000 }, /* R202 */ + { 0x0000, 0x0000 }, /* R203 */ + { 0x0000, 0x0000 }, /* R204 */ + { 0x0000, 0x0000 }, /* R205 */ + { 0x0000, 0x0000 }, /* R206 */ + { 0x0000, 0x0000 }, /* R207 */ + { 0xFFFF, 0xFFFF }, /* R208 */ + { 0xFFFF, 0xFFFF }, /* R209 */ + { 0xFFFF, 0xFFFF }, /* R210 */ + { 0x0000, 0x0000 }, /* R211 */ + { 0x0000, 0x0000 }, /* R212 */ + { 0x0000, 0x0000 }, /* R213 */ + { 0x0000, 0x0000 }, /* R214 */ + { 0x0000, 0x0000 }, /* R215 */ + { 0x0000, 0x0000 }, /* R216 */ + { 0x0000, 0x0000 }, /* R217 */ + { 0x0000, 0x0000 }, /* R218 */ + { 0x0000, 0x0000 }, /* R219 */ + { 0x0000, 0x0000 }, /* R220 */ + { 0x0000, 0x0000 }, /* R221 */ + { 0x0000, 0x0000 }, /* R222 */ + { 0x0000, 0x0000 }, /* R223 */ + { 0x0000, 0x0000 }, /* R224 */ + { 0x0000, 0x0000 }, /* R225 */ + { 0x0000, 0x0000 }, /* R226 */ + { 0x0000, 0x0000 }, /* R227 */ + { 0x0000, 0x0000 }, /* R228 */ + { 0x0000, 0x0000 }, /* R229 */ + { 0x0000, 0x0000 }, /* R230 */ + { 0x0000, 0x0000 }, /* R231 */ + { 0x0000, 0x0000 }, /* R232 */ + { 0x0000, 0x0000 }, /* R233 */ + { 0x0000, 0x0000 }, /* R234 */ + { 0x0000, 0x0000 }, /* R235 */ + { 0x0000, 0x0000 }, /* R236 */ + { 0x0000, 0x0000 }, /* R237 */ + { 0x0000, 0x0000 }, /* R238 */ + { 0x0000, 0x0000 }, /* R239 */ + { 0x0000, 0x0000 }, /* R240 */ + { 0x0000, 0x0000 }, /* R241 */ + { 0x0000, 0x0000 }, /* R242 */ + { 0x0000, 0x0000 }, /* R243 */ + { 0x0000, 0x0000 }, /* R244 */ + { 0x0000, 0x0000 }, /* R245 */ + { 0x0000, 0x0000 }, /* R246 */ + { 0x0000, 0x0000 }, /* R247 */ + { 0x0000, 0x0000 }, /* R248 */ + { 0x0000, 0x0000 }, /* R249 */ + { 0x0000, 0x0000 }, /* R250 */ + { 0x0000, 0x0000 }, /* R251 */ + { 0x0000, 0x0000 }, /* R252 */ + { 0x0000, 0x0000 }, /* R253 */ + { 0x0000, 0x0000 }, /* R254 */ + { 0x0000, 0x0000 }, /* R255 */ + { 0x000F, 0x0000 }, /* R256 - Chip Revision */ + { 0x0074, 0x0074 }, /* R257 - Control Interface */ + { 0x0000, 0x0000 }, /* R258 */ + { 0x0000, 0x0000 }, /* R259 */ + { 0x0000, 0x0000 }, /* R260 */ + { 0x0000, 0x0000 }, /* R261 */ + { 0x0000, 0x0000 }, /* R262 */ + { 0x0000, 0x0000 }, /* R263 */ + { 0x0000, 0x0000 }, /* R264 */ + { 0x0000, 0x0000 }, /* R265 */ + { 0x0000, 0x0000 }, /* R266 */ + { 0x0000, 0x0000 }, /* R267 */ + { 0x0000, 0x0000 }, /* R268 */ + { 0x0000, 0x0000 }, /* R269 */ + { 0x0000, 0x0000 }, /* R270 */ + { 0x0000, 0x0000 }, /* R271 */ + { 0x807F, 0x837F }, /* R272 - Write Sequencer Ctrl (1) */ + { 0x017F, 0x0000 }, /* R273 - Write Sequencer Ctrl (2) */ + { 0x0000, 0x0000 }, /* R274 */ + { 0x0000, 0x0000 }, /* R275 */ + { 0x0000, 0x0000 }, /* R276 */ + { 0x0000, 0x0000 }, /* R277 */ + { 0x0000, 0x0000 }, /* R278 */ + { 0x0000, 0x0000 }, /* R279 */ + { 0x0000, 0x0000 }, /* R280 */ + { 0x0000, 0x0000 }, /* R281 */ + { 0x0000, 0x0000 }, /* R282 */ + { 0x0000, 0x0000 }, /* R283 */ + { 0x0000, 0x0000 }, /* R284 */ + { 0x0000, 0x0000 }, /* R285 */ + { 0x0000, 0x0000 }, /* R286 */ + { 0x0000, 0x0000 }, /* R287 */ + { 0x0000, 0x0000 }, /* R288 */ + { 0x0000, 0x0000 }, /* R289 */ + { 0x0000, 0x0000 }, /* R290 */ + { 0x0000, 0x0000 }, /* R291 */ + { 0x0000, 0x0000 }, /* R292 */ + { 0x0000, 0x0000 }, /* R293 */ + { 0x0000, 0x0000 }, /* R294 */ + { 0x0000, 0x0000 }, /* R295 */ + { 0x0000, 0x0000 }, /* R296 */ + { 0x0000, 0x0000 }, /* R297 */ + { 0x0000, 0x0000 }, /* R298 */ + { 0x0000, 0x0000 }, /* R299 */ + { 0x0000, 0x0000 }, /* R300 */ + { 0x0000, 0x0000 }, /* R301 */ + { 0x0000, 0x0000 }, /* R302 */ + { 0x0000, 0x0000 }, /* R303 */ + { 0x0000, 0x0000 }, /* R304 */ + { 0x0000, 0x0000 }, /* R305 */ + { 0x0000, 0x0000 }, /* R306 */ + { 0x0000, 0x0000 }, /* R307 */ + { 0x0000, 0x0000 }, /* R308 */ + { 0x0000, 0x0000 }, /* R309 */ + { 0x0000, 0x0000 }, /* R310 */ + { 0x0000, 0x0000 }, /* R311 */ + { 0x0000, 0x0000 }, /* R312 */ + { 0x0000, 0x0000 }, /* R313 */ + { 0x0000, 0x0000 }, /* R314 */ + { 0x0000, 0x0000 }, /* R315 */ + { 0x0000, 0x0000 }, /* R316 */ + { 0x0000, 0x0000 }, /* R317 */ + { 0x0000, 0x0000 }, /* R318 */ + { 0x0000, 0x0000 }, /* R319 */ + { 0x0000, 0x0000 }, /* R320 */ + { 0x0000, 0x0000 }, /* R321 */ + { 0x0000, 0x0000 }, /* R322 */ + { 0x0000, 0x0000 }, /* R323 */ + { 0x0000, 0x0000 }, /* R324 */ + { 0x0000, 0x0000 }, /* R325 */ + { 0x0000, 0x0000 }, /* R326 */ + { 0x0000, 0x0000 }, /* R327 */ + { 0x0000, 0x0000 }, /* R328 */ + { 0x0000, 0x0000 }, /* R329 */ + { 0x0000, 0x0000 }, /* R330 */ + { 0x0000, 0x0000 }, /* R331 */ + { 0x0000, 0x0000 }, /* R332 */ + { 0x0000, 0x0000 }, /* R333 */ + { 0x0000, 0x0000 }, /* R334 */ + { 0x0000, 0x0000 }, /* R335 */ + { 0x0000, 0x0000 }, /* R336 */ + { 0x0000, 0x0000 }, /* R337 */ + { 0x0000, 0x0000 }, /* R338 */ + { 0x0000, 0x0000 }, /* R339 */ + { 0x0000, 0x0000 }, /* R340 */ + { 0x0000, 0x0000 }, /* R341 */ + { 0x0000, 0x0000 }, /* R342 */ + { 0x0000, 0x0000 }, /* R343 */ + { 0x0000, 0x0000 }, /* R344 */ + { 0x0000, 0x0000 }, /* R345 */ + { 0x0000, 0x0000 }, /* R346 */ + { 0x0000, 0x0000 }, /* R347 */ + { 0x0000, 0x0000 }, /* R348 */ + { 0x0000, 0x0000 }, /* R349 */ + { 0x0000, 0x0000 }, /* R350 */ + { 0x0000, 0x0000 }, /* R351 */ + { 0x0000, 0x0000 }, /* R352 */ + { 0x0000, 0x0000 }, /* R353 */ + { 0x0000, 0x0000 }, /* R354 */ + { 0x0000, 0x0000 }, /* R355 */ + { 0x0000, 0x0000 }, /* R356 */ + { 0x0000, 0x0000 }, /* R357 */ + { 0x0000, 0x0000 }, /* R358 */ + { 0x0000, 0x0000 }, /* R359 */ + { 0x0000, 0x0000 }, /* R360 */ + { 0x0000, 0x0000 }, /* R361 */ + { 0x0000, 0x0000 }, /* R362 */ + { 0x0000, 0x0000 }, /* R363 */ + { 0x0000, 0x0000 }, /* R364 */ + { 0x0000, 0x0000 }, /* R365 */ + { 0x0000, 0x0000 }, /* R366 */ + { 0x0000, 0x0000 }, /* R367 */ + { 0x0000, 0x0000 }, /* R368 */ + { 0x0000, 0x0000 }, /* R369 */ + { 0x0000, 0x0000 }, /* R370 */ + { 0x0000, 0x0000 }, /* R371 */ + { 0x0000, 0x0000 }, /* R372 */ + { 0x0000, 0x0000 }, /* R373 */ + { 0x0000, 0x0000 }, /* R374 */ + { 0x0000, 0x0000 }, /* R375 */ + { 0x0000, 0x0000 }, /* R376 */ + { 0x0000, 0x0000 }, /* R377 */ + { 0x0000, 0x0000 }, /* R378 */ + { 0x0000, 0x0000 }, /* R379 */ + { 0x0000, 0x0000 }, /* R380 */ + { 0x0000, 0x0000 }, /* R381 */ + { 0x0000, 0x0000 }, /* R382 */ + { 0x0000, 0x0000 }, /* R383 */ + { 0x0000, 0x0000 }, /* R384 */ + { 0x0000, 0x0000 }, /* R385 */ + { 0x0000, 0x0000 }, /* R386 */ + { 0x0000, 0x0000 }, /* R387 */ + { 0x0000, 0x0000 }, /* R388 */ + { 0x0000, 0x0000 }, /* R389 */ + { 0x0000, 0x0000 }, /* R390 */ + { 0x0000, 0x0000 }, /* R391 */ + { 0x0000, 0x0000 }, /* R392 */ + { 0x0000, 0x0000 }, /* R393 */ + { 0x0000, 0x0000 }, /* R394 */ + { 0x0000, 0x0000 }, /* R395 */ + { 0x0000, 0x0000 }, /* R396 */ + { 0x0000, 0x0000 }, /* R397 */ + { 0x0000, 0x0000 }, /* R398 */ + { 0x0000, 0x0000 }, /* R399 */ + { 0x0000, 0x0000 }, /* R400 */ + { 0x0000, 0x0000 }, /* R401 */ + { 0x0000, 0x0000 }, /* R402 */ + { 0x0000, 0x0000 }, /* R403 */ + { 0x0000, 0x0000 }, /* R404 */ + { 0x0000, 0x0000 }, /* R405 */ + { 0x0000, 0x0000 }, /* R406 */ + { 0x0000, 0x0000 }, /* R407 */ + { 0x0000, 0x0000 }, /* R408 */ + { 0x0000, 0x0000 }, /* R409 */ + { 0x0000, 0x0000 }, /* R410 */ + { 0x0000, 0x0000 }, /* R411 */ + { 0x0000, 0x0000 }, /* R412 */ + { 0x0000, 0x0000 }, /* R413 */ + { 0x0000, 0x0000 }, /* R414 */ + { 0x0000, 0x0000 }, /* R415 */ + { 0x0000, 0x0000 }, /* R416 */ + { 0x0000, 0x0000 }, /* R417 */ + { 0x0000, 0x0000 }, /* R418 */ + { 0x0000, 0x0000 }, /* R419 */ + { 0x0000, 0x0000 }, /* R420 */ + { 0x0000, 0x0000 }, /* R421 */ + { 0x0000, 0x0000 }, /* R422 */ + { 0x0000, 0x0000 }, /* R423 */ + { 0x0000, 0x0000 }, /* R424 */ + { 0x0000, 0x0000 }, /* R425 */ + { 0x0000, 0x0000 }, /* R426 */ + { 0x0000, 0x0000 }, /* R427 */ + { 0x0000, 0x0000 }, /* R428 */ + { 0x0000, 0x0000 }, /* R429 */ + { 0x0000, 0x0000 }, /* R430 */ + { 0x0000, 0x0000 }, /* R431 */ + { 0x0000, 0x0000 }, /* R432 */ + { 0x0000, 0x0000 }, /* R433 */ + { 0x0000, 0x0000 }, /* R434 */ + { 0x0000, 0x0000 }, /* R435 */ + { 0x0000, 0x0000 }, /* R436 */ + { 0x0000, 0x0000 }, /* R437 */ + { 0x0000, 0x0000 }, /* R438 */ + { 0x0000, 0x0000 }, /* R439 */ + { 0x0000, 0x0000 }, /* R440 */ + { 0x0000, 0x0000 }, /* R441 */ + { 0x0000, 0x0000 }, /* R442 */ + { 0x0000, 0x0000 }, /* R443 */ + { 0x0000, 0x0000 }, /* R444 */ + { 0x0000, 0x0000 }, /* R445 */ + { 0x0000, 0x0000 }, /* R446 */ + { 0x0000, 0x0000 }, /* R447 */ + { 0x0000, 0x0000 }, /* R448 */ + { 0x0000, 0x0000 }, /* R449 */ + { 0x0000, 0x0000 }, /* R450 */ + { 0x0000, 0x0000 }, /* R451 */ + { 0x0000, 0x0000 }, /* R452 */ + { 0x0000, 0x0000 }, /* R453 */ + { 0x0000, 0x0000 }, /* R454 */ + { 0x0000, 0x0000 }, /* R455 */ + { 0x0000, 0x0000 }, /* R456 */ + { 0x0000, 0x0000 }, /* R457 */ + { 0x0000, 0x0000 }, /* R458 */ + { 0x0000, 0x0000 }, /* R459 */ + { 0x0000, 0x0000 }, /* R460 */ + { 0x0000, 0x0000 }, /* R461 */ + { 0x0000, 0x0000 }, /* R462 */ + { 0x0000, 0x0000 }, /* R463 */ + { 0x0000, 0x0000 }, /* R464 */ + { 0x0000, 0x0000 }, /* R465 */ + { 0x0000, 0x0000 }, /* R466 */ + { 0x0000, 0x0000 }, /* R467 */ + { 0x0000, 0x0000 }, /* R468 */ + { 0x0000, 0x0000 }, /* R469 */ + { 0x0000, 0x0000 }, /* R470 */ + { 0x0000, 0x0000 }, /* R471 */ + { 0x0000, 0x0000 }, /* R472 */ + { 0x0000, 0x0000 }, /* R473 */ + { 0x0000, 0x0000 }, /* R474 */ + { 0x0000, 0x0000 }, /* R475 */ + { 0x0000, 0x0000 }, /* R476 */ + { 0x0000, 0x0000 }, /* R477 */ + { 0x0000, 0x0000 }, /* R478 */ + { 0x0000, 0x0000 }, /* R479 */ + { 0x0000, 0x0000 }, /* R480 */ + { 0x0000, 0x0000 }, /* R481 */ + { 0x0000, 0x0000 }, /* R482 */ + { 0x0000, 0x0000 }, /* R483 */ + { 0x0000, 0x0000 }, /* R484 */ + { 0x0000, 0x0000 }, /* R485 */ + { 0x0000, 0x0000 }, /* R486 */ + { 0x0000, 0x0000 }, /* R487 */ + { 0x0000, 0x0000 }, /* R488 */ + { 0x0000, 0x0000 }, /* R489 */ + { 0x0000, 0x0000 }, /* R490 */ + { 0x0000, 0x0000 }, /* R491 */ + { 0x0000, 0x0000 }, /* R492 */ + { 0x0000, 0x0000 }, /* R493 */ + { 0x0000, 0x0000 }, /* R494 */ + { 0x0000, 0x0000 }, /* R495 */ + { 0x0000, 0x0000 }, /* R496 */ + { 0x0000, 0x0000 }, /* R497 */ + { 0x0000, 0x0000 }, /* R498 */ + { 0x0000, 0x0000 }, /* R499 */ + { 0x0000, 0x0000 }, /* R500 */ + { 0x0000, 0x0000 }, /* R501 */ + { 0x0000, 0x0000 }, /* R502 */ + { 0x0000, 0x0000 }, /* R503 */ + { 0x0000, 0x0000 }, /* R504 */ + { 0x0000, 0x0000 }, /* R505 */ + { 0x0000, 0x0000 }, /* R506 */ + { 0x0000, 0x0000 }, /* R507 */ + { 0x0000, 0x0000 }, /* R508 */ + { 0x0000, 0x0000 }, /* R509 */ + { 0x0000, 0x0000 }, /* R510 */ + { 0x0000, 0x0000 }, /* R511 */ + { 0x001F, 0x001F }, /* R512 - AIF1 Clocking (1) */ + { 0x003F, 0x003F }, /* R513 - AIF1 Clocking (2) */ + { 0x0000, 0x0000 }, /* R514 */ + { 0x0000, 0x0000 }, /* R515 */ + { 0x001F, 0x001F }, /* R516 - AIF2 Clocking (1) */ + { 0x003F, 0x003F }, /* R517 - AIF2 Clocking (2) */ + { 0x0000, 0x0000 }, /* R518 */ + { 0x0000, 0x0000 }, /* R519 */ + { 0x001F, 0x001F }, /* R520 - Clocking (1) */ + { 0x0777, 0x0777 }, /* R521 - Clocking (2) */ + { 0x0000, 0x0000 }, /* R522 */ + { 0x0000, 0x0000 }, /* R523 */ + { 0x0000, 0x0000 }, /* R524 */ + { 0x0000, 0x0000 }, /* R525 */ + { 0x0000, 0x0000 }, /* R526 */ + { 0x0000, 0x0000 }, /* R527 */ + { 0x00FF, 0x00FF }, /* R528 - AIF1 Rate */ + { 0x00FF, 0x00FF }, /* R529 - AIF2 Rate */ + { 0x000F, 0x0000 }, /* R530 - Rate Status */ + { 0x0000, 0x0000 }, /* R531 */ + { 0x0000, 0x0000 }, /* R532 */ + { 0x0000, 0x0000 }, /* R533 */ + { 0x0000, 0x0000 }, /* R534 */ + { 0x0000, 0x0000 }, /* R535 */ + { 0x0000, 0x0000 }, /* R536 */ + { 0x0000, 0x0000 }, /* R537 */ + { 0x0000, 0x0000 }, /* R538 */ + { 0x0000, 0x0000 }, /* R539 */ + { 0x0000, 0x0000 }, /* R540 */ + { 0x0000, 0x0000 }, /* R541 */ + { 0x0000, 0x0000 }, /* R542 */ + { 0x0000, 0x0000 }, /* R543 */ + { 0x0007, 0x0007 }, /* R544 - FLL1 Control (1) */ + { 0x3F77, 0x3F77 }, /* R545 - FLL1 Control (2) */ + { 0xFFFF, 0xFFFF }, /* R546 - FLL1 Control (3) */ + { 0x7FEF, 0x7FEF }, /* R547 - FLL1 Control (4) */ + { 0x1FDB, 0x1FDB }, /* R548 - FLL1 Control (5) */ + { 0x0000, 0x0000 }, /* R549 */ + { 0x0000, 0x0000 }, /* R550 */ + { 0x0000, 0x0000 }, /* R551 */ + { 0x0000, 0x0000 }, /* R552 */ + { 0x0000, 0x0000 }, /* R553 */ + { 0x0000, 0x0000 }, /* R554 */ + { 0x0000, 0x0000 }, /* R555 */ + { 0x0000, 0x0000 }, /* R556 */ + { 0x0000, 0x0000 }, /* R557 */ + { 0x0000, 0x0000 }, /* R558 */ + { 0x0000, 0x0000 }, /* R559 */ + { 0x0000, 0x0000 }, /* R560 */ + { 0x0000, 0x0000 }, /* R561 */ + { 0x0000, 0x0000 }, /* R562 */ + { 0x0000, 0x0000 }, /* R563 */ + { 0x0000, 0x0000 }, /* R564 */ + { 0x0000, 0x0000 }, /* R565 */ + { 0x0000, 0x0000 }, /* R566 */ + { 0x0000, 0x0000 }, /* R567 */ + { 0x0000, 0x0000 }, /* R568 */ + { 0x0000, 0x0000 }, /* R569 */ + { 0x0000, 0x0000 }, /* R570 */ + { 0x0000, 0x0000 }, /* R571 */ + { 0x0000, 0x0000 }, /* R572 */ + { 0x0000, 0x0000 }, /* R573 */ + { 0x0000, 0x0000 }, /* R574 */ + { 0x0000, 0x0000 }, /* R575 */ + { 0x0007, 0x0007 }, /* R576 - FLL2 Control (1) */ + { 0x3F77, 0x3F77 }, /* R577 - FLL2 Control (2) */ + { 0xFFFF, 0xFFFF }, /* R578 - FLL2 Control (3) */ + { 0x7FEF, 0x7FEF }, /* R579 - FLL2 Control (4) */ + { 0x1FDB, 0x1FDB }, /* R580 - FLL2 Control (5) */ + { 0x0000, 0x0000 }, /* R581 */ + { 0x0000, 0x0000 }, /* R582 */ + { 0x0000, 0x0000 }, /* R583 */ + { 0x0000, 0x0000 }, /* R584 */ + { 0x0000, 0x0000 }, /* R585 */ + { 0x0000, 0x0000 }, /* R586 */ + { 0x0000, 0x0000 }, /* R587 */ + { 0x0000, 0x0000 }, /* R588 */ + { 0x0000, 0x0000 }, /* R589 */ + { 0x0000, 0x0000 }, /* R590 */ + { 0x0000, 0x0000 }, /* R591 */ + { 0x0000, 0x0000 }, /* R592 */ + { 0x0000, 0x0000 }, /* R593 */ + { 0x0000, 0x0000 }, /* R594 */ + { 0x0000, 0x0000 }, /* R595 */ + { 0x0000, 0x0000 }, /* R596 */ + { 0x0000, 0x0000 }, /* R597 */ + { 0x0000, 0x0000 }, /* R598 */ + { 0x0000, 0x0000 }, /* R599 */ + { 0x0000, 0x0000 }, /* R600 */ + { 0x0000, 0x0000 }, /* R601 */ + { 0x0000, 0x0000 }, /* R602 */ + { 0x0000, 0x0000 }, /* R603 */ + { 0x0000, 0x0000 }, /* R604 */ + { 0x0000, 0x0000 }, /* R605 */ + { 0x0000, 0x0000 }, /* R606 */ + { 0x0000, 0x0000 }, /* R607 */ + { 0x0000, 0x0000 }, /* R608 */ + { 0x0000, 0x0000 }, /* R609 */ + { 0x0000, 0x0000 }, /* R610 */ + { 0x0000, 0x0000 }, /* R611 */ + { 0x0000, 0x0000 }, /* R612 */ + { 0x0000, 0x0000 }, /* R613 */ + { 0x0000, 0x0000 }, /* R614 */ + { 0x0000, 0x0000 }, /* R615 */ + { 0x0000, 0x0000 }, /* R616 */ + { 0x0000, 0x0000 }, /* R617 */ + { 0x0000, 0x0000 }, /* R618 */ + { 0x0000, 0x0000 }, /* R619 */ + { 0x0000, 0x0000 }, /* R620 */ + { 0x0000, 0x0000 }, /* R621 */ + { 0x0000, 0x0000 }, /* R622 */ + { 0x0000, 0x0000 }, /* R623 */ + { 0x0000, 0x0000 }, /* R624 */ + { 0x0000, 0x0000 }, /* R625 */ + { 0x0000, 0x0000 }, /* R626 */ + { 0x0000, 0x0000 }, /* R627 */ + { 0x0000, 0x0000 }, /* R628 */ + { 0x0000, 0x0000 }, /* R629 */ + { 0x0000, 0x0000 }, /* R630 */ + { 0x0000, 0x0000 }, /* R631 */ + { 0x0000, 0x0000 }, /* R632 */ + { 0x0000, 0x0000 }, /* R633 */ + { 0x0000, 0x0000 }, /* R634 */ + { 0x0000, 0x0000 }, /* R635 */ + { 0x0000, 0x0000 }, /* R636 */ + { 0x0000, 0x0000 }, /* R637 */ + { 0x0000, 0x0000 }, /* R638 */ + { 0x0000, 0x0000 }, /* R639 */ + { 0x0000, 0x0000 }, /* R640 */ + { 0x0000, 0x0000 }, /* R641 */ + { 0x0000, 0x0000 }, /* R642 */ + { 0x0000, 0x0000 }, /* R643 */ + { 0x0000, 0x0000 }, /* R644 */ + { 0x0000, 0x0000 }, /* R645 */ + { 0x0000, 0x0000 }, /* R646 */ + { 0x0000, 0x0000 }, /* R647 */ + { 0x0000, 0x0000 }, /* R648 */ + { 0x0000, 0x0000 }, /* R649 */ + { 0x0000, 0x0000 }, /* R650 */ + { 0x0000, 0x0000 }, /* R651 */ + { 0x0000, 0x0000 }, /* R652 */ + { 0x0000, 0x0000 }, /* R653 */ + { 0x0000, 0x0000 }, /* R654 */ + { 0x0000, 0x0000 }, /* R655 */ + { 0x0000, 0x0000 }, /* R656 */ + { 0x0000, 0x0000 }, /* R657 */ + { 0x0000, 0x0000 }, /* R658 */ + { 0x0000, 0x0000 }, /* R659 */ + { 0x0000, 0x0000 }, /* R660 */ + { 0x0000, 0x0000 }, /* R661 */ + { 0x0000, 0x0000 }, /* R662 */ + { 0x0000, 0x0000 }, /* R663 */ + { 0x0000, 0x0000 }, /* R664 */ + { 0x0000, 0x0000 }, /* R665 */ + { 0x0000, 0x0000 }, /* R666 */ + { 0x0000, 0x0000 }, /* R667 */ + { 0x0000, 0x0000 }, /* R668 */ + { 0x0000, 0x0000 }, /* R669 */ + { 0x0000, 0x0000 }, /* R670 */ + { 0x0000, 0x0000 }, /* R671 */ + { 0x0000, 0x0000 }, /* R672 */ + { 0x0000, 0x0000 }, /* R673 */ + { 0x0000, 0x0000 }, /* R674 */ + { 0x0000, 0x0000 }, /* R675 */ + { 0x0000, 0x0000 }, /* R676 */ + { 0x0000, 0x0000 }, /* R677 */ + { 0x0000, 0x0000 }, /* R678 */ + { 0x0000, 0x0000 }, /* R679 */ + { 0x0000, 0x0000 }, /* R680 */ + { 0x0000, 0x0000 }, /* R681 */ + { 0x0000, 0x0000 }, /* R682 */ + { 0x0000, 0x0000 }, /* R683 */ + { 0x0000, 0x0000 }, /* R684 */ + { 0x0000, 0x0000 }, /* R685 */ + { 0x0000, 0x0000 }, /* R686 */ + { 0x0000, 0x0000 }, /* R687 */ + { 0x0000, 0x0000 }, /* R688 */ + { 0x0000, 0x0000 }, /* R689 */ + { 0x0000, 0x0000 }, /* R690 */ + { 0x0000, 0x0000 }, /* R691 */ + { 0x0000, 0x0000 }, /* R692 */ + { 0x0000, 0x0000 }, /* R693 */ + { 0x0000, 0x0000 }, /* R694 */ + { 0x0000, 0x0000 }, /* R695 */ + { 0x0000, 0x0000 }, /* R696 */ + { 0x0000, 0x0000 }, /* R697 */ + { 0x0000, 0x0000 }, /* R698 */ + { 0x0000, 0x0000 }, /* R699 */ + { 0x0000, 0x0000 }, /* R700 */ + { 0x0000, 0x0000 }, /* R701 */ + { 0x0000, 0x0000 }, /* R702 */ + { 0x0000, 0x0000 }, /* R703 */ + { 0x0000, 0x0000 }, /* R704 */ + { 0x0000, 0x0000 }, /* R705 */ + { 0x0000, 0x0000 }, /* R706 */ + { 0x0000, 0x0000 }, /* R707 */ + { 0x0000, 0x0000 }, /* R708 */ + { 0x0000, 0x0000 }, /* R709 */ + { 0x0000, 0x0000 }, /* R710 */ + { 0x0000, 0x0000 }, /* R711 */ + { 0x0000, 0x0000 }, /* R712 */ + { 0x0000, 0x0000 }, /* R713 */ + { 0x0000, 0x0000 }, /* R714 */ + { 0x0000, 0x0000 }, /* R715 */ + { 0x0000, 0x0000 }, /* R716 */ + { 0x0000, 0x0000 }, /* R717 */ + { 0x0000, 0x0000 }, /* R718 */ + { 0x0000, 0x0000 }, /* R719 */ + { 0x0000, 0x0000 }, /* R720 */ + { 0x0000, 0x0000 }, /* R721 */ + { 0x0000, 0x0000 }, /* R722 */ + { 0x0000, 0x0000 }, /* R723 */ + { 0x0000, 0x0000 }, /* R724 */ + { 0x0000, 0x0000 }, /* R725 */ + { 0x0000, 0x0000 }, /* R726 */ + { 0x0000, 0x0000 }, /* R727 */ + { 0x0000, 0x0000 }, /* R728 */ + { 0x0000, 0x0000 }, /* R729 */ + { 0x0000, 0x0000 }, /* R730 */ + { 0x0000, 0x0000 }, /* R731 */ + { 0x0000, 0x0000 }, /* R732 */ + { 0x0000, 0x0000 }, /* R733 */ + { 0x0000, 0x0000 }, /* R734 */ + { 0x0000, 0x0000 }, /* R735 */ + { 0x0000, 0x0000 }, /* R736 */ + { 0x0000, 0x0000 }, /* R737 */ + { 0x0000, 0x0000 }, /* R738 */ + { 0x0000, 0x0000 }, /* R739 */ + { 0x0000, 0x0000 }, /* R740 */ + { 0x0000, 0x0000 }, /* R741 */ + { 0x0000, 0x0000 }, /* R742 */ + { 0x0000, 0x0000 }, /* R743 */ + { 0x0000, 0x0000 }, /* R744 */ + { 0x0000, 0x0000 }, /* R745 */ + { 0x0000, 0x0000 }, /* R746 */ + { 0x0000, 0x0000 }, /* R747 */ + { 0x0000, 0x0000 }, /* R748 */ + { 0x0000, 0x0000 }, /* R749 */ + { 0x0000, 0x0000 }, /* R750 */ + { 0x0000, 0x0000 }, /* R751 */ + { 0x0000, 0x0000 }, /* R752 */ + { 0x0000, 0x0000 }, /* R753 */ + { 0x0000, 0x0000 }, /* R754 */ + { 0x0000, 0x0000 }, /* R755 */ + { 0x0000, 0x0000 }, /* R756 */ + { 0x0000, 0x0000 }, /* R757 */ + { 0x0000, 0x0000 }, /* R758 */ + { 0x0000, 0x0000 }, /* R759 */ + { 0x0000, 0x0000 }, /* R760 */ + { 0x0000, 0x0000 }, /* R761 */ + { 0x0000, 0x0000 }, /* R762 */ + { 0x0000, 0x0000 }, /* R763 */ + { 0x0000, 0x0000 }, /* R764 */ + { 0x0000, 0x0000 }, /* R765 */ + { 0x0000, 0x0000 }, /* R766 */ + { 0x0000, 0x0000 }, /* R767 */ + { 0xE1F8, 0xE1F8 }, /* R768 - AIF1 Control (1) */ + { 0xCD1F, 0xCD1F }, /* R769 - AIF1 Control (2) */ + { 0xF000, 0xF000 }, /* R770 - AIF1 Master/Slave */ + { 0x01F0, 0x01F0 }, /* R771 - AIF1 BCLK */ + { 0x0FFF, 0x0FFF }, /* R772 - AIF1ADC LRCLK */ + { 0x0FFF, 0x0FFF }, /* R773 - AIF1DAC LRCLK */ + { 0x0003, 0x0003 }, /* R774 - AIF1DAC Data */ + { 0x0003, 0x0003 }, /* R775 - AIF1ADC Data */ + { 0x0000, 0x0000 }, /* R776 */ + { 0x0000, 0x0000 }, /* R777 */ + { 0x0000, 0x0000 }, /* R778 */ + { 0x0000, 0x0000 }, /* R779 */ + { 0x0000, 0x0000 }, /* R780 */ + { 0x0000, 0x0000 }, /* R781 */ + { 0x0000, 0x0000 }, /* R782 */ + { 0x0000, 0x0000 }, /* R783 */ + { 0xF1F8, 0xF1F8 }, /* R784 - AIF2 Control (1) */ + { 0xFD1F, 0xFD1F }, /* R785 - AIF2 Control (2) */ + { 0xF000, 0xF000 }, /* R786 - AIF2 Master/Slave */ + { 0x01F0, 0x01F0 }, /* R787 - AIF2 BCLK */ + { 0x0FFF, 0x0FFF }, /* R788 - AIF2ADC LRCLK */ + { 0x0FFF, 0x0FFF }, /* R789 - AIF2DAC LRCLK */ + { 0x0003, 0x0003 }, /* R790 - AIF2DAC Data */ + { 0x0003, 0x0003 }, /* R791 - AIF2ADC Data */ + { 0x0000, 0x0000 }, /* R792 */ + { 0x0000, 0x0000 }, /* R793 */ + { 0x0000, 0x0000 }, /* R794 */ + { 0x0000, 0x0000 }, /* R795 */ + { 0x0000, 0x0000 }, /* R796 */ + { 0x0000, 0x0000 }, /* R797 */ + { 0x0000, 0x0000 }, /* R798 */ + { 0x0000, 0x0000 }, /* R799 */ + { 0x0000, 0x0000 }, /* R800 */ + { 0x0000, 0x0000 }, /* R801 */ + { 0x0000, 0x0000 }, /* R802 */ + { 0x0000, 0x0000 }, /* R803 */ + { 0x0000, 0x0000 }, /* R804 */ + { 0x0000, 0x0000 }, /* R805 */ + { 0x0000, 0x0000 }, /* R806 */ + { 0x0000, 0x0000 }, /* R807 */ + { 0x0000, 0x0000 }, /* R808 */ + { 0x0000, 0x0000 }, /* R809 */ + { 0x0000, 0x0000 }, /* R810 */ + { 0x0000, 0x0000 }, /* R811 */ + { 0x0000, 0x0000 }, /* R812 */ + { 0x0000, 0x0000 }, /* R813 */ + { 0x0000, 0x0000 }, /* R814 */ + { 0x0000, 0x0000 }, /* R815 */ + { 0x0000, 0x0000 }, /* R816 */ + { 0x0000, 0x0000 }, /* R817 */ + { 0x0000, 0x0000 }, /* R818 */ + { 0x0000, 0x0000 }, /* R819 */ + { 0x0000, 0x0000 }, /* R820 */ + { 0x0000, 0x0000 }, /* R821 */ + { 0x0000, 0x0000 }, /* R822 */ + { 0x0000, 0x0000 }, /* R823 */ + { 0x0000, 0x0000 }, /* R824 */ + { 0x0000, 0x0000 }, /* R825 */ + { 0x0000, 0x0000 }, /* R826 */ + { 0x0000, 0x0000 }, /* R827 */ + { 0x0000, 0x0000 }, /* R828 */ + { 0x0000, 0x0000 }, /* R829 */ + { 0x0000, 0x0000 }, /* R830 */ + { 0x0000, 0x0000 }, /* R831 */ + { 0x0000, 0x0000 }, /* R832 */ + { 0x0000, 0x0000 }, /* R833 */ + { 0x0000, 0x0000 }, /* R834 */ + { 0x0000, 0x0000 }, /* R835 */ + { 0x0000, 0x0000 }, /* R836 */ + { 0x0000, 0x0000 }, /* R837 */ + { 0x0000, 0x0000 }, /* R838 */ + { 0x0000, 0x0000 }, /* R839 */ + { 0x0000, 0x0000 }, /* R840 */ + { 0x0000, 0x0000 }, /* R841 */ + { 0x0000, 0x0000 }, /* R842 */ + { 0x0000, 0x0000 }, /* R843 */ + { 0x0000, 0x0000 }, /* R844 */ + { 0x0000, 0x0000 }, /* R845 */ + { 0x0000, 0x0000 }, /* R846 */ + { 0x0000, 0x0000 }, /* R847 */ + { 0x0000, 0x0000 }, /* R848 */ + { 0x0000, 0x0000 }, /* R849 */ + { 0x0000, 0x0000 }, /* R850 */ + { 0x0000, 0x0000 }, /* R851 */ + { 0x0000, 0x0000 }, /* R852 */ + { 0x0000, 0x0000 }, /* R853 */ + { 0x0000, 0x0000 }, /* R854 */ + { 0x0000, 0x0000 }, /* R855 */ + { 0x0000, 0x0000 }, /* R856 */ + { 0x0000, 0x0000 }, /* R857 */ + { 0x0000, 0x0000 }, /* R858 */ + { 0x0000, 0x0000 }, /* R859 */ + { 0x0000, 0x0000 }, /* R860 */ + { 0x0000, 0x0000 }, /* R861 */ + { 0x0000, 0x0000 }, /* R862 */ + { 0x0000, 0x0000 }, /* R863 */ + { 0x0000, 0x0000 }, /* R864 */ + { 0x0000, 0x0000 }, /* R865 */ + { 0x0000, 0x0000 }, /* R866 */ + { 0x0000, 0x0000 }, /* R867 */ + { 0x0000, 0x0000 }, /* R868 */ + { 0x0000, 0x0000 }, /* R869 */ + { 0x0000, 0x0000 }, /* R870 */ + { 0x0000, 0x0000 }, /* R871 */ + { 0x0000, 0x0000 }, /* R872 */ + { 0x0000, 0x0000 }, /* R873 */ + { 0x0000, 0x0000 }, /* R874 */ + { 0x0000, 0x0000 }, /* R875 */ + { 0x0000, 0x0000 }, /* R876 */ + { 0x0000, 0x0000 }, /* R877 */ + { 0x0000, 0x0000 }, /* R878 */ + { 0x0000, 0x0000 }, /* R879 */ + { 0x0000, 0x0000 }, /* R880 */ + { 0x0000, 0x0000 }, /* R881 */ + { 0x0000, 0x0000 }, /* R882 */ + { 0x0000, 0x0000 }, /* R883 */ + { 0x0000, 0x0000 }, /* R884 */ + { 0x0000, 0x0000 }, /* R885 */ + { 0x0000, 0x0000 }, /* R886 */ + { 0x0000, 0x0000 }, /* R887 */ + { 0x0000, 0x0000 }, /* R888 */ + { 0x0000, 0x0000 }, /* R889 */ + { 0x0000, 0x0000 }, /* R890 */ + { 0x0000, 0x0000 }, /* R891 */ + { 0x0000, 0x0000 }, /* R892 */ + { 0x0000, 0x0000 }, /* R893 */ + { 0x0000, 0x0000 }, /* R894 */ + { 0x0000, 0x0000 }, /* R895 */ + { 0x0000, 0x0000 }, /* R896 */ + { 0x0000, 0x0000 }, /* R897 */ + { 0x0000, 0x0000 }, /* R898 */ + { 0x0000, 0x0000 }, /* R899 */ + { 0x0000, 0x0000 }, /* R900 */ + { 0x0000, 0x0000 }, /* R901 */ + { 0x0000, 0x0000 }, /* R902 */ + { 0x0000, 0x0000 }, /* R903 */ + { 0x0000, 0x0000 }, /* R904 */ + { 0x0000, 0x0000 }, /* R905 */ + { 0x0000, 0x0000 }, /* R906 */ + { 0x0000, 0x0000 }, /* R907 */ + { 0x0000, 0x0000 }, /* R908 */ + { 0x0000, 0x0000 }, /* R909 */ + { 0x0000, 0x0000 }, /* R910 */ + { 0x0000, 0x0000 }, /* R911 */ + { 0x0000, 0x0000 }, /* R912 */ + { 0x0000, 0x0000 }, /* R913 */ + { 0x0000, 0x0000 }, /* R914 */ + { 0x0000, 0x0000 }, /* R915 */ + { 0x0000, 0x0000 }, /* R916 */ + { 0x0000, 0x0000 }, /* R917 */ + { 0x0000, 0x0000 }, /* R918 */ + { 0x0000, 0x0000 }, /* R919 */ + { 0x0000, 0x0000 }, /* R920 */ + { 0x0000, 0x0000 }, /* R921 */ + { 0x0000, 0x0000 }, /* R922 */ + { 0x0000, 0x0000 }, /* R923 */ + { 0x0000, 0x0000 }, /* R924 */ + { 0x0000, 0x0000 }, /* R925 */ + { 0x0000, 0x0000 }, /* R926 */ + { 0x0000, 0x0000 }, /* R927 */ + { 0x0000, 0x0000 }, /* R928 */ + { 0x0000, 0x0000 }, /* R929 */ + { 0x0000, 0x0000 }, /* R930 */ + { 0x0000, 0x0000 }, /* R931 */ + { 0x0000, 0x0000 }, /* R932 */ + { 0x0000, 0x0000 }, /* R933 */ + { 0x0000, 0x0000 }, /* R934 */ + { 0x0000, 0x0000 }, /* R935 */ + { 0x0000, 0x0000 }, /* R936 */ + { 0x0000, 0x0000 }, /* R937 */ + { 0x0000, 0x0000 }, /* R938 */ + { 0x0000, 0x0000 }, /* R939 */ + { 0x0000, 0x0000 }, /* R940 */ + { 0x0000, 0x0000 }, /* R941 */ + { 0x0000, 0x0000 }, /* R942 */ + { 0x0000, 0x0000 }, /* R943 */ + { 0x0000, 0x0000 }, /* R944 */ + { 0x0000, 0x0000 }, /* R945 */ + { 0x0000, 0x0000 }, /* R946 */ + { 0x0000, 0x0000 }, /* R947 */ + { 0x0000, 0x0000 }, /* R948 */ + { 0x0000, 0x0000 }, /* R949 */ + { 0x0000, 0x0000 }, /* R950 */ + { 0x0000, 0x0000 }, /* R951 */ + { 0x0000, 0x0000 }, /* R952 */ + { 0x0000, 0x0000 }, /* R953 */ + { 0x0000, 0x0000 }, /* R954 */ + { 0x0000, 0x0000 }, /* R955 */ + { 0x0000, 0x0000 }, /* R956 */ + { 0x0000, 0x0000 }, /* R957 */ + { 0x0000, 0x0000 }, /* R958 */ + { 0x0000, 0x0000 }, /* R959 */ + { 0x0000, 0x0000 }, /* R960 */ + { 0x0000, 0x0000 }, /* R961 */ + { 0x0000, 0x0000 }, /* R962 */ + { 0x0000, 0x0000 }, /* R963 */ + { 0x0000, 0x0000 }, /* R964 */ + { 0x0000, 0x0000 }, /* R965 */ + { 0x0000, 0x0000 }, /* R966 */ + { 0x0000, 0x0000 }, /* R967 */ + { 0x0000, 0x0000 }, /* R968 */ + { 0x0000, 0x0000 }, /* R969 */ + { 0x0000, 0x0000 }, /* R970 */ + { 0x0000, 0x0000 }, /* R971 */ + { 0x0000, 0x0000 }, /* R972 */ + { 0x0000, 0x0000 }, /* R973 */ + { 0x0000, 0x0000 }, /* R974 */ + { 0x0000, 0x0000 }, /* R975 */ + { 0x0000, 0x0000 }, /* R976 */ + { 0x0000, 0x0000 }, /* R977 */ + { 0x0000, 0x0000 }, /* R978 */ + { 0x0000, 0x0000 }, /* R979 */ + { 0x0000, 0x0000 }, /* R980 */ + { 0x0000, 0x0000 }, /* R981 */ + { 0x0000, 0x0000 }, /* R982 */ + { 0x0000, 0x0000 }, /* R983 */ + { 0x0000, 0x0000 }, /* R984 */ + { 0x0000, 0x0000 }, /* R985 */ + { 0x0000, 0x0000 }, /* R986 */ + { 0x0000, 0x0000 }, /* R987 */ + { 0x0000, 0x0000 }, /* R988 */ + { 0x0000, 0x0000 }, /* R989 */ + { 0x0000, 0x0000 }, /* R990 */ + { 0x0000, 0x0000 }, /* R991 */ + { 0x0000, 0x0000 }, /* R992 */ + { 0x0000, 0x0000 }, /* R993 */ + { 0x0000, 0x0000 }, /* R994 */ + { 0x0000, 0x0000 }, /* R995 */ + { 0x0000, 0x0000 }, /* R996 */ + { 0x0000, 0x0000 }, /* R997 */ + { 0x0000, 0x0000 }, /* R998 */ + { 0x0000, 0x0000 }, /* R999 */ + { 0x0000, 0x0000 }, /* R1000 */ + { 0x0000, 0x0000 }, /* R1001 */ + { 0x0000, 0x0000 }, /* R1002 */ + { 0x0000, 0x0000 }, /* R1003 */ + { 0x0000, 0x0000 }, /* R1004 */ + { 0x0000, 0x0000 }, /* R1005 */ + { 0x0000, 0x0000 }, /* R1006 */ + { 0x0000, 0x0000 }, /* R1007 */ + { 0x0000, 0x0000 }, /* R1008 */ + { 0x0000, 0x0000 }, /* R1009 */ + { 0x0000, 0x0000 }, /* R1010 */ + { 0x0000, 0x0000 }, /* R1011 */ + { 0x0000, 0x0000 }, /* R1012 */ + { 0x0000, 0x0000 }, /* R1013 */ + { 0x0000, 0x0000 }, /* R1014 */ + { 0x0000, 0x0000 }, /* R1015 */ + { 0x0000, 0x0000 }, /* R1016 */ + { 0x0000, 0x0000 }, /* R1017 */ + { 0x0000, 0x0000 }, /* R1018 */ + { 0x0000, 0x0000 }, /* R1019 */ + { 0x0000, 0x0000 }, /* R1020 */ + { 0x0000, 0x0000 }, /* R1021 */ + { 0x0000, 0x0000 }, /* R1022 */ + { 0x0000, 0x0000 }, /* R1023 */ + { 0x00FF, 0x01FF }, /* R1024 - AIF1 ADC1 Left Volume */ + { 0x00FF, 0x01FF }, /* R1025 - AIF1 ADC1 Right Volume */ + { 0x00FF, 0x01FF }, /* R1026 - AIF1 DAC1 Left Volume */ + { 0x00FF, 0x01FF }, /* R1027 - AIF1 DAC1 Right Volume */ + { 0x00FF, 0x01FF }, /* R1028 - AIF1 ADC2 Left Volume */ + { 0x00FF, 0x01FF }, /* R1029 - AIF1 ADC2 Right Volume */ + { 0x00FF, 0x01FF }, /* R1030 - AIF1 DAC2 Left Volume */ + { 0x00FF, 0x01FF }, /* R1031 - AIF1 DAC2 Right Volume */ + { 0x0000, 0x0000 }, /* R1032 */ + { 0x0000, 0x0000 }, /* R1033 */ + { 0x0000, 0x0000 }, /* R1034 */ + { 0x0000, 0x0000 }, /* R1035 */ + { 0x0000, 0x0000 }, /* R1036 */ + { 0x0000, 0x0000 }, /* R1037 */ + { 0x0000, 0x0000 }, /* R1038 */ + { 0x0000, 0x0000 }, /* R1039 */ + { 0xF800, 0xF800 }, /* R1040 - AIF1 ADC1 Filters */ + { 0x7800, 0x7800 }, /* R1041 - AIF1 ADC2 Filters */ + { 0x0000, 0x0000 }, /* R1042 */ + { 0x0000, 0x0000 }, /* R1043 */ + { 0x0000, 0x0000 }, /* R1044 */ + { 0x0000, 0x0000 }, /* R1045 */ + { 0x0000, 0x0000 }, /* R1046 */ + { 0x0000, 0x0000 }, /* R1047 */ + { 0x0000, 0x0000 }, /* R1048 */ + { 0x0000, 0x0000 }, /* R1049 */ + { 0x0000, 0x0000 }, /* R1050 */ + { 0x0000, 0x0000 }, /* R1051 */ + { 0x0000, 0x0000 }, /* R1052 */ + { 0x0000, 0x0000 }, /* R1053 */ + { 0x0000, 0x0000 }, /* R1054 */ + { 0x0000, 0x0000 }, /* R1055 */ + { 0x02B6, 0x02B6 }, /* R1056 - AIF1 DAC1 Filters (1) */ + { 0x3F00, 0x3F00 }, /* R1057 - AIF1 DAC1 Filters (2) */ + { 0x02B6, 0x02B6 }, /* R1058 - AIF1 DAC2 Filters (1) */ + { 0x3F00, 0x3F00 }, /* R1059 - AIF1 DAC2 Filters (2) */ + { 0x0000, 0x0000 }, /* R1060 */ + { 0x0000, 0x0000 }, /* R1061 */ + { 0x0000, 0x0000 }, /* R1062 */ + { 0x0000, 0x0000 }, /* R1063 */ + { 0x0000, 0x0000 }, /* R1064 */ + { 0x0000, 0x0000 }, /* R1065 */ + { 0x0000, 0x0000 }, /* R1066 */ + { 0x0000, 0x0000 }, /* R1067 */ + { 0x0000, 0x0000 }, /* R1068 */ + { 0x0000, 0x0000 }, /* R1069 */ + { 0x0000, 0x0000 }, /* R1070 */ + { 0x0000, 0x0000 }, /* R1071 */ + { 0x0000, 0x0000 }, /* R1072 */ + { 0x0000, 0x0000 }, /* R1073 */ + { 0x0000, 0x0000 }, /* R1074 */ + { 0x0000, 0x0000 }, /* R1075 */ + { 0x0000, 0x0000 }, /* R1076 */ + { 0x0000, 0x0000 }, /* R1077 */ + { 0x0000, 0x0000 }, /* R1078 */ + { 0x0000, 0x0000 }, /* R1079 */ + { 0x0000, 0x0000 }, /* R1080 */ + { 0x0000, 0x0000 }, /* R1081 */ + { 0x0000, 0x0000 }, /* R1082 */ + { 0x0000, 0x0000 }, /* R1083 */ + { 0x0000, 0x0000 }, /* R1084 */ + { 0x0000, 0x0000 }, /* R1085 */ + { 0x0000, 0x0000 }, /* R1086 */ + { 0x0000, 0x0000 }, /* R1087 */ + { 0xFFFF, 0xFFFF }, /* R1088 - AIF1 DRC1 (1) */ + { 0x1FFF, 0x1FFF }, /* R1089 - AIF1 DRC1 (2) */ + { 0xFFFF, 0xFFFF }, /* R1090 - AIF1 DRC1 (3) */ + { 0x07FF, 0x07FF }, /* R1091 - AIF1 DRC1 (4) */ + { 0x03FF, 0x03FF }, /* R1092 - AIF1 DRC1 (5) */ + { 0x0000, 0x0000 }, /* R1093 */ + { 0x0000, 0x0000 }, /* R1094 */ + { 0x0000, 0x0000 }, /* R1095 */ + { 0x0000, 0x0000 }, /* R1096 */ + { 0x0000, 0x0000 }, /* R1097 */ + { 0x0000, 0x0000 }, /* R1098 */ + { 0x0000, 0x0000 }, /* R1099 */ + { 0x0000, 0x0000 }, /* R1100 */ + { 0x0000, 0x0000 }, /* R1101 */ + { 0x0000, 0x0000 }, /* R1102 */ + { 0x0000, 0x0000 }, /* R1103 */ + { 0xFFFF, 0xFFFF }, /* R1104 - AIF1 DRC2 (1) */ + { 0x1FFF, 0x1FFF }, /* R1105 - AIF1 DRC2 (2) */ + { 0xFFFF, 0xFFFF }, /* R1106 - AIF1 DRC2 (3) */ + { 0x07FF, 0x07FF }, /* R1107 - AIF1 DRC2 (4) */ + { 0x03FF, 0x03FF }, /* R1108 - AIF1 DRC2 (5) */ + { 0x0000, 0x0000 }, /* R1109 */ + { 0x0000, 0x0000 }, /* R1110 */ + { 0x0000, 0x0000 }, /* R1111 */ + { 0x0000, 0x0000 }, /* R1112 */ + { 0x0000, 0x0000 }, /* R1113 */ + { 0x0000, 0x0000 }, /* R1114 */ + { 0x0000, 0x0000 }, /* R1115 */ + { 0x0000, 0x0000 }, /* R1116 */ + { 0x0000, 0x0000 }, /* R1117 */ + { 0x0000, 0x0000 }, /* R1118 */ + { 0x0000, 0x0000 }, /* R1119 */ + { 0x0000, 0x0000 }, /* R1120 */ + { 0x0000, 0x0000 }, /* R1121 */ + { 0x0000, 0x0000 }, /* R1122 */ + { 0x0000, 0x0000 }, /* R1123 */ + { 0x0000, 0x0000 }, /* R1124 */ + { 0x0000, 0x0000 }, /* R1125 */ + { 0x0000, 0x0000 }, /* R1126 */ + { 0x0000, 0x0000 }, /* R1127 */ + { 0x0000, 0x0000 }, /* R1128 */ + { 0x0000, 0x0000 }, /* R1129 */ + { 0x0000, 0x0000 }, /* R1130 */ + { 0x0000, 0x0000 }, /* R1131 */ + { 0x0000, 0x0000 }, /* R1132 */ + { 0x0000, 0x0000 }, /* R1133 */ + { 0x0000, 0x0000 }, /* R1134 */ + { 0x0000, 0x0000 }, /* R1135 */ + { 0x0000, 0x0000 }, /* R1136 */ + { 0x0000, 0x0000 }, /* R1137 */ + { 0x0000, 0x0000 }, /* R1138 */ + { 0x0000, 0x0000 }, /* R1139 */ + { 0x0000, 0x0000 }, /* R1140 */ + { 0x0000, 0x0000 }, /* R1141 */ + { 0x0000, 0x0000 }, /* R1142 */ + { 0x0000, 0x0000 }, /* R1143 */ + { 0x0000, 0x0000 }, /* R1144 */ + { 0x0000, 0x0000 }, /* R1145 */ + { 0x0000, 0x0000 }, /* R1146 */ + { 0x0000, 0x0000 }, /* R1147 */ + { 0x0000, 0x0000 }, /* R1148 */ + { 0x0000, 0x0000 }, /* R1149 */ + { 0x0000, 0x0000 }, /* R1150 */ + { 0x0000, 0x0000 }, /* R1151 */ + { 0xFFFF, 0xFFFF }, /* R1152 - AIF1 DAC1 EQ Gains (1) */ + { 0xFFC0, 0xFFC0 }, /* R1153 - AIF1 DAC1 EQ Gains (2) */ + { 0xFFFF, 0xFFFF }, /* R1154 - AIF1 DAC1 EQ Band 1 A */ + { 0xFFFF, 0xFFFF }, /* R1155 - AIF1 DAC1 EQ Band 1 B */ + { 0xFFFF, 0xFFFF }, /* R1156 - AIF1 DAC1 EQ Band 1 PG */ + { 0xFFFF, 0xFFFF }, /* R1157 - AIF1 DAC1 EQ Band 2 A */ + { 0xFFFF, 0xFFFF }, /* R1158 - AIF1 DAC1 EQ Band 2 B */ + { 0xFFFF, 0xFFFF }, /* R1159 - AIF1 DAC1 EQ Band 2 C */ + { 0xFFFF, 0xFFFF }, /* R1160 - AIF1 DAC1 EQ Band 2 PG */ + { 0xFFFF, 0xFFFF }, /* R1161 - AIF1 DAC1 EQ Band 3 A */ + { 0xFFFF, 0xFFFF }, /* R1162 - AIF1 DAC1 EQ Band 3 B */ + { 0xFFFF, 0xFFFF }, /* R1163 - AIF1 DAC1 EQ Band 3 C */ + { 0xFFFF, 0xFFFF }, /* R1164 - AIF1 DAC1 EQ Band 3 PG */ + { 0xFFFF, 0xFFFF }, /* R1165 - AIF1 DAC1 EQ Band 4 A */ + { 0xFFFF, 0xFFFF }, /* R1166 - AIF1 DAC1 EQ Band 4 B */ + { 0xFFFF, 0xFFFF }, /* R1167 - AIF1 DAC1 EQ Band 4 C */ + { 0xFFFF, 0xFFFF }, /* R1168 - AIF1 DAC1 EQ Band 4 PG */ + { 0xFFFF, 0xFFFF }, /* R1169 - AIF1 DAC1 EQ Band 5 A */ + { 0xFFFF, 0xFFFF }, /* R1170 - AIF1 DAC1 EQ Band 5 B */ + { 0xFFFF, 0xFFFF }, /* R1171 - AIF1 DAC1 EQ Band 5 PG */ + { 0x0000, 0x0000 }, /* R1172 */ + { 0x0000, 0x0000 }, /* R1173 */ + { 0x0000, 0x0000 }, /* R1174 */ + { 0x0000, 0x0000 }, /* R1175 */ + { 0x0000, 0x0000 }, /* R1176 */ + { 0x0000, 0x0000 }, /* R1177 */ + { 0x0000, 0x0000 }, /* R1178 */ + { 0x0000, 0x0000 }, /* R1179 */ + { 0x0000, 0x0000 }, /* R1180 */ + { 0x0000, 0x0000 }, /* R1181 */ + { 0x0000, 0x0000 }, /* R1182 */ + { 0x0000, 0x0000 }, /* R1183 */ + { 0xFFFF, 0xFFFF }, /* R1184 - AIF1 DAC2 EQ Gains (1) */ + { 0xFFC0, 0xFFC0 }, /* R1185 - AIF1 DAC2 EQ Gains (2) */ + { 0xFFFF, 0xFFFF }, /* R1186 - AIF1 DAC2 EQ Band 1 A */ + { 0xFFFF, 0xFFFF }, /* R1187 - AIF1 DAC2 EQ Band 1 B */ + { 0xFFFF, 0xFFFF }, /* R1188 - AIF1 DAC2 EQ Band 1 PG */ + { 0xFFFF, 0xFFFF }, /* R1189 - AIF1 DAC2 EQ Band 2 A */ + { 0xFFFF, 0xFFFF }, /* R1190 - AIF1 DAC2 EQ Band 2 B */ + { 0xFFFF, 0xFFFF }, /* R1191 - AIF1 DAC2 EQ Band 2 C */ + { 0xFFFF, 0xFFFF }, /* R1192 - AIF1 DAC2 EQ Band 2 PG */ + { 0xFFFF, 0xFFFF }, /* R1193 - AIF1 DAC2 EQ Band 3 A */ + { 0xFFFF, 0xFFFF }, /* R1194 - AIF1 DAC2 EQ Band 3 B */ + { 0xFFFF, 0xFFFF }, /* R1195 - AIF1 DAC2 EQ Band 3 C */ + { 0xFFFF, 0xFFFF }, /* R1196 - AIF1 DAC2 EQ Band 3 PG */ + { 0xFFFF, 0xFFFF }, /* R1197 - AIF1 DAC2 EQ Band 4 A */ + { 0xFFFF, 0xFFFF }, /* R1198 - AIF1 DAC2 EQ Band 4 B */ + { 0xFFFF, 0xFFFF }, /* R1199 - AIF1 DAC2 EQ Band 4 C */ + { 0xFFFF, 0xFFFF }, /* R1200 - AIF1 DAC2 EQ Band 4 PG */ + { 0xFFFF, 0xFFFF }, /* R1201 - AIF1 DAC2 EQ Band 5 A */ + { 0xFFFF, 0xFFFF }, /* R1202 - AIF1 DAC2 EQ Band 5 B */ + { 0xFFFF, 0xFFFF }, /* R1203 - AIF1 DAC2 EQ Band 5 PG */ + { 0x0000, 0x0000 }, /* R1204 */ + { 0x0000, 0x0000 }, /* R1205 */ + { 0x0000, 0x0000 }, /* R1206 */ + { 0x0000, 0x0000 }, /* R1207 */ + { 0x0000, 0x0000 }, /* R1208 */ + { 0x0000, 0x0000 }, /* R1209 */ + { 0x0000, 0x0000 }, /* R1210 */ + { 0x0000, 0x0000 }, /* R1211 */ + { 0x0000, 0x0000 }, /* R1212 */ + { 0x0000, 0x0000 }, /* R1213 */ + { 0x0000, 0x0000 }, /* R1214 */ + { 0x0000, 0x0000 }, /* R1215 */ + { 0x0000, 0x0000 }, /* R1216 */ + { 0x0000, 0x0000 }, /* R1217 */ + { 0x0000, 0x0000 }, /* R1218 */ + { 0x0000, 0x0000 }, /* R1219 */ + { 0x0000, 0x0000 }, /* R1220 */ + { 0x0000, 0x0000 }, /* R1221 */ + { 0x0000, 0x0000 }, /* R1222 */ + { 0x0000, 0x0000 }, /* R1223 */ + { 0x0000, 0x0000 }, /* R1224 */ + { 0x0000, 0x0000 }, /* R1225 */ + { 0x0000, 0x0000 }, /* R1226 */ + { 0x0000, 0x0000 }, /* R1227 */ + { 0x0000, 0x0000 }, /* R1228 */ + { 0x0000, 0x0000 }, /* R1229 */ + { 0x0000, 0x0000 }, /* R1230 */ + { 0x0000, 0x0000 }, /* R1231 */ + { 0x0000, 0x0000 }, /* R1232 */ + { 0x0000, 0x0000 }, /* R1233 */ + { 0x0000, 0x0000 }, /* R1234 */ + { 0x0000, 0x0000 }, /* R1235 */ + { 0x0000, 0x0000 }, /* R1236 */ + { 0x0000, 0x0000 }, /* R1237 */ + { 0x0000, 0x0000 }, /* R1238 */ + { 0x0000, 0x0000 }, /* R1239 */ + { 0x0000, 0x0000 }, /* R1240 */ + { 0x0000, 0x0000 }, /* R1241 */ + { 0x0000, 0x0000 }, /* R1242 */ + { 0x0000, 0x0000 }, /* R1243 */ + { 0x0000, 0x0000 }, /* R1244 */ + { 0x0000, 0x0000 }, /* R1245 */ + { 0x0000, 0x0000 }, /* R1246 */ + { 0x0000, 0x0000 }, /* R1247 */ + { 0x0000, 0x0000 }, /* R1248 */ + { 0x0000, 0x0000 }, /* R1249 */ + { 0x0000, 0x0000 }, /* R1250 */ + { 0x0000, 0x0000 }, /* R1251 */ + { 0x0000, 0x0000 }, /* R1252 */ + { 0x0000, 0x0000 }, /* R1253 */ + { 0x0000, 0x0000 }, /* R1254 */ + { 0x0000, 0x0000 }, /* R1255 */ + { 0x0000, 0x0000 }, /* R1256 */ + { 0x0000, 0x0000 }, /* R1257 */ + { 0x0000, 0x0000 }, /* R1258 */ + { 0x0000, 0x0000 }, /* R1259 */ + { 0x0000, 0x0000 }, /* R1260 */ + { 0x0000, 0x0000 }, /* R1261 */ + { 0x0000, 0x0000 }, /* R1262 */ + { 0x0000, 0x0000 }, /* R1263 */ + { 0x0000, 0x0000 }, /* R1264 */ + { 0x0000, 0x0000 }, /* R1265 */ + { 0x0000, 0x0000 }, /* R1266 */ + { 0x0000, 0x0000 }, /* R1267 */ + { 0x0000, 0x0000 }, /* R1268 */ + { 0x0000, 0x0000 }, /* R1269 */ + { 0x0000, 0x0000 }, /* R1270 */ + { 0x0000, 0x0000 }, /* R1271 */ + { 0x0000, 0x0000 }, /* R1272 */ + { 0x0000, 0x0000 }, /* R1273 */ + { 0x0000, 0x0000 }, /* R1274 */ + { 0x0000, 0x0000 }, /* R1275 */ + { 0x0000, 0x0000 }, /* R1276 */ + { 0x0000, 0x0000 }, /* R1277 */ + { 0x0000, 0x0000 }, /* R1278 */ + { 0x0000, 0x0000 }, /* R1279 */ + { 0x00FF, 0x01FF }, /* R1280 - AIF2 ADC Left Volume */ + { 0x00FF, 0x01FF }, /* R1281 - AIF2 ADC Right Volume */ + { 0x00FF, 0x01FF }, /* R1282 - AIF2 DAC Left Volume */ + { 0x00FF, 0x01FF }, /* R1283 - AIF2 DAC Right Volume */ + { 0x0000, 0x0000 }, /* R1284 */ + { 0x0000, 0x0000 }, /* R1285 */ + { 0x0000, 0x0000 }, /* R1286 */ + { 0x0000, 0x0000 }, /* R1287 */ + { 0x0000, 0x0000 }, /* R1288 */ + { 0x0000, 0x0000 }, /* R1289 */ + { 0x0000, 0x0000 }, /* R1290 */ + { 0x0000, 0x0000 }, /* R1291 */ + { 0x0000, 0x0000 }, /* R1292 */ + { 0x0000, 0x0000 }, /* R1293 */ + { 0x0000, 0x0000 }, /* R1294 */ + { 0x0000, 0x0000 }, /* R1295 */ + { 0xF800, 0xF800 }, /* R1296 - AIF2 ADC Filters */ + { 0x0000, 0x0000 }, /* R1297 */ + { 0x0000, 0x0000 }, /* R1298 */ + { 0x0000, 0x0000 }, /* R1299 */ + { 0x0000, 0x0000 }, /* R1300 */ + { 0x0000, 0x0000 }, /* R1301 */ + { 0x0000, 0x0000 }, /* R1302 */ + { 0x0000, 0x0000 }, /* R1303 */ + { 0x0000, 0x0000 }, /* R1304 */ + { 0x0000, 0x0000 }, /* R1305 */ + { 0x0000, 0x0000 }, /* R1306 */ + { 0x0000, 0x0000 }, /* R1307 */ + { 0x0000, 0x0000 }, /* R1308 */ + { 0x0000, 0x0000 }, /* R1309 */ + { 0x0000, 0x0000 }, /* R1310 */ + { 0x0000, 0x0000 }, /* R1311 */ + { 0x02B6, 0x02B6 }, /* R1312 - AIF2 DAC Filters (1) */ + { 0x3F00, 0x3F00 }, /* R1313 - AIF2 DAC Filters (2) */ + { 0x0000, 0x0000 }, /* R1314 */ + { 0x0000, 0x0000 }, /* R1315 */ + { 0x0000, 0x0000 }, /* R1316 */ + { 0x0000, 0x0000 }, /* R1317 */ + { 0x0000, 0x0000 }, /* R1318 */ + { 0x0000, 0x0000 }, /* R1319 */ + { 0x0000, 0x0000 }, /* R1320 */ + { 0x0000, 0x0000 }, /* R1321 */ + { 0x0000, 0x0000 }, /* R1322 */ + { 0x0000, 0x0000 }, /* R1323 */ + { 0x0000, 0x0000 }, /* R1324 */ + { 0x0000, 0x0000 }, /* R1325 */ + { 0x0000, 0x0000 }, /* R1326 */ + { 0x0000, 0x0000 }, /* R1327 */ + { 0x0000, 0x0000 }, /* R1328 */ + { 0x0000, 0x0000 }, /* R1329 */ + { 0x0000, 0x0000 }, /* R1330 */ + { 0x0000, 0x0000 }, /* R1331 */ + { 0x0000, 0x0000 }, /* R1332 */ + { 0x0000, 0x0000 }, /* R1333 */ + { 0x0000, 0x0000 }, /* R1334 */ + { 0x0000, 0x0000 }, /* R1335 */ + { 0x0000, 0x0000 }, /* R1336 */ + { 0x0000, 0x0000 }, /* R1337 */ + { 0x0000, 0x0000 }, /* R1338 */ + { 0x0000, 0x0000 }, /* R1339 */ + { 0x0000, 0x0000 }, /* R1340 */ + { 0x0000, 0x0000 }, /* R1341 */ + { 0x0000, 0x0000 }, /* R1342 */ + { 0x0000, 0x0000 }, /* R1343 */ + { 0xFFFF, 0xFFFF }, /* R1344 - AIF2 DRC (1) */ + { 0x1FFF, 0x1FFF }, /* R1345 - AIF2 DRC (2) */ + { 0xFFFF, 0xFFFF }, /* R1346 - AIF2 DRC (3) */ + { 0x07FF, 0x07FF }, /* R1347 - AIF2 DRC (4) */ + { 0x03FF, 0x03FF }, /* R1348 - AIF2 DRC (5) */ + { 0x0000, 0x0000 }, /* R1349 */ + { 0x0000, 0x0000 }, /* R1350 */ + { 0x0000, 0x0000 }, /* R1351 */ + { 0x0000, 0x0000 }, /* R1352 */ + { 0x0000, 0x0000 }, /* R1353 */ + { 0x0000, 0x0000 }, /* R1354 */ + { 0x0000, 0x0000 }, /* R1355 */ + { 0x0000, 0x0000 }, /* R1356 */ + { 0x0000, 0x0000 }, /* R1357 */ + { 0x0000, 0x0000 }, /* R1358 */ + { 0x0000, 0x0000 }, /* R1359 */ + { 0x0000, 0x0000 }, /* R1360 */ + { 0x0000, 0x0000 }, /* R1361 */ + { 0x0000, 0x0000 }, /* R1362 */ + { 0x0000, 0x0000 }, /* R1363 */ + { 0x0000, 0x0000 }, /* R1364 */ + { 0x0000, 0x0000 }, /* R1365 */ + { 0x0000, 0x0000 }, /* R1366 */ + { 0x0000, 0x0000 }, /* R1367 */ + { 0x0000, 0x0000 }, /* R1368 */ + { 0x0000, 0x0000 }, /* R1369 */ + { 0x0000, 0x0000 }, /* R1370 */ + { 0x0000, 0x0000 }, /* R1371 */ + { 0x0000, 0x0000 }, /* R1372 */ + { 0x0000, 0x0000 }, /* R1373 */ + { 0x0000, 0x0000 }, /* R1374 */ + { 0x0000, 0x0000 }, /* R1375 */ + { 0x0000, 0x0000 }, /* R1376 */ + { 0x0000, 0x0000 }, /* R1377 */ + { 0x0000, 0x0000 }, /* R1378 */ + { 0x0000, 0x0000 }, /* R1379 */ + { 0x0000, 0x0000 }, /* R1380 */ + { 0x0000, 0x0000 }, /* R1381 */ + { 0x0000, 0x0000 }, /* R1382 */ + { 0x0000, 0x0000 }, /* R1383 */ + { 0x0000, 0x0000 }, /* R1384 */ + { 0x0000, 0x0000 }, /* R1385 */ + { 0x0000, 0x0000 }, /* R1386 */ + { 0x0000, 0x0000 }, /* R1387 */ + { 0x0000, 0x0000 }, /* R1388 */ + { 0x0000, 0x0000 }, /* R1389 */ + { 0x0000, 0x0000 }, /* R1390 */ + { 0x0000, 0x0000 }, /* R1391 */ + { 0x0000, 0x0000 }, /* R1392 */ + { 0x0000, 0x0000 }, /* R1393 */ + { 0x0000, 0x0000 }, /* R1394 */ + { 0x0000, 0x0000 }, /* R1395 */ + { 0x0000, 0x0000 }, /* R1396 */ + { 0x0000, 0x0000 }, /* R1397 */ + { 0x0000, 0x0000 }, /* R1398 */ + { 0x0000, 0x0000 }, /* R1399 */ + { 0x0000, 0x0000 }, /* R1400 */ + { 0x0000, 0x0000 }, /* R1401 */ + { 0x0000, 0x0000 }, /* R1402 */ + { 0x0000, 0x0000 }, /* R1403 */ + { 0x0000, 0x0000 }, /* R1404 */ + { 0x0000, 0x0000 }, /* R1405 */ + { 0x0000, 0x0000 }, /* R1406 */ + { 0x0000, 0x0000 }, /* R1407 */ + { 0xFFFF, 0xFFFF }, /* R1408 - AIF2 EQ Gains (1) */ + { 0xFFC0, 0xFFC0 }, /* R1409 - AIF2 EQ Gains (2) */ + { 0xFFFF, 0xFFFF }, /* R1410 - AIF2 EQ Band 1 A */ + { 0xFFFF, 0xFFFF }, /* R1411 - AIF2 EQ Band 1 B */ + { 0xFFFF, 0xFFFF }, /* R1412 - AIF2 EQ Band 1 PG */ + { 0xFFFF, 0xFFFF }, /* R1413 - AIF2 EQ Band 2 A */ + { 0xFFFF, 0xFFFF }, /* R1414 - AIF2 EQ Band 2 B */ + { 0xFFFF, 0xFFFF }, /* R1415 - AIF2 EQ Band 2 C */ + { 0xFFFF, 0xFFFF }, /* R1416 - AIF2 EQ Band 2 PG */ + { 0xFFFF, 0xFFFF }, /* R1417 - AIF2 EQ Band 3 A */ + { 0xFFFF, 0xFFFF }, /* R1418 - AIF2 EQ Band 3 B */ + { 0xFFFF, 0xFFFF }, /* R1419 - AIF2 EQ Band 3 C */ + { 0xFFFF, 0xFFFF }, /* R1420 - AIF2 EQ Band 3 PG */ + { 0xFFFF, 0xFFFF }, /* R1421 - AIF2 EQ Band 4 A */ + { 0xFFFF, 0xFFFF }, /* R1422 - AIF2 EQ Band 4 B */ + { 0xFFFF, 0xFFFF }, /* R1423 - AIF2 EQ Band 4 C */ + { 0xFFFF, 0xFFFF }, /* R1424 - AIF2 EQ Band 4 PG */ + { 0xFFFF, 0xFFFF }, /* R1425 - AIF2 EQ Band 5 A */ + { 0xFFFF, 0xFFFF }, /* R1426 - AIF2 EQ Band 5 B */ + { 0xFFFF, 0xFFFF }, /* R1427 - AIF2 EQ Band 5 PG */ + { 0x0000, 0x0000 }, /* R1428 */ + { 0x0000, 0x0000 }, /* R1429 */ + { 0x0000, 0x0000 }, /* R1430 */ + { 0x0000, 0x0000 }, /* R1431 */ + { 0x0000, 0x0000 }, /* R1432 */ + { 0x0000, 0x0000 }, /* R1433 */ + { 0x0000, 0x0000 }, /* R1434 */ + { 0x0000, 0x0000 }, /* R1435 */ + { 0x0000, 0x0000 }, /* R1436 */ + { 0x0000, 0x0000 }, /* R1437 */ + { 0x0000, 0x0000 }, /* R1438 */ + { 0x0000, 0x0000 }, /* R1439 */ + { 0x0000, 0x0000 }, /* R1440 */ + { 0x0000, 0x0000 }, /* R1441 */ + { 0x0000, 0x0000 }, /* R1442 */ + { 0x0000, 0x0000 }, /* R1443 */ + { 0x0000, 0x0000 }, /* R1444 */ + { 0x0000, 0x0000 }, /* R1445 */ + { 0x0000, 0x0000 }, /* R1446 */ + { 0x0000, 0x0000 }, /* R1447 */ + { 0x0000, 0x0000 }, /* R1448 */ + { 0x0000, 0x0000 }, /* R1449 */ + { 0x0000, 0x0000 }, /* R1450 */ + { 0x0000, 0x0000 }, /* R1451 */ + { 0x0000, 0x0000 }, /* R1452 */ + { 0x0000, 0x0000 }, /* R1453 */ + { 0x0000, 0x0000 }, /* R1454 */ + { 0x0000, 0x0000 }, /* R1455 */ + { 0x0000, 0x0000 }, /* R1456 */ + { 0x0000, 0x0000 }, /* R1457 */ + { 0x0000, 0x0000 }, /* R1458 */ + { 0x0000, 0x0000 }, /* R1459 */ + { 0x0000, 0x0000 }, /* R1460 */ + { 0x0000, 0x0000 }, /* R1461 */ + { 0x0000, 0x0000 }, /* R1462 */ + { 0x0000, 0x0000 }, /* R1463 */ + { 0x0000, 0x0000 }, /* R1464 */ + { 0x0000, 0x0000 }, /* R1465 */ + { 0x0000, 0x0000 }, /* R1466 */ + { 0x0000, 0x0000 }, /* R1467 */ + { 0x0000, 0x0000 }, /* R1468 */ + { 0x0000, 0x0000 }, /* R1469 */ + { 0x0000, 0x0000 }, /* R1470 */ + { 0x0000, 0x0000 }, /* R1471 */ + { 0x0000, 0x0000 }, /* R1472 */ + { 0x0000, 0x0000 }, /* R1473 */ + { 0x0000, 0x0000 }, /* R1474 */ + { 0x0000, 0x0000 }, /* R1475 */ + { 0x0000, 0x0000 }, /* R1476 */ + { 0x0000, 0x0000 }, /* R1477 */ + { 0x0000, 0x0000 }, /* R1478 */ + { 0x0000, 0x0000 }, /* R1479 */ + { 0x0000, 0x0000 }, /* R1480 */ + { 0x0000, 0x0000 }, /* R1481 */ + { 0x0000, 0x0000 }, /* R1482 */ + { 0x0000, 0x0000 }, /* R1483 */ + { 0x0000, 0x0000 }, /* R1484 */ + { 0x0000, 0x0000 }, /* R1485 */ + { 0x0000, 0x0000 }, /* R1486 */ + { 0x0000, 0x0000 }, /* R1487 */ + { 0x0000, 0x0000 }, /* R1488 */ + { 0x0000, 0x0000 }, /* R1489 */ + { 0x0000, 0x0000 }, /* R1490 */ + { 0x0000, 0x0000 }, /* R1491 */ + { 0x0000, 0x0000 }, /* R1492 */ + { 0x0000, 0x0000 }, /* R1493 */ + { 0x0000, 0x0000 }, /* R1494 */ + { 0x0000, 0x0000 }, /* R1495 */ + { 0x0000, 0x0000 }, /* R1496 */ + { 0x0000, 0x0000 }, /* R1497 */ + { 0x0000, 0x0000 }, /* R1498 */ + { 0x0000, 0x0000 }, /* R1499 */ + { 0x0000, 0x0000 }, /* R1500 */ + { 0x0000, 0x0000 }, /* R1501 */ + { 0x0000, 0x0000 }, /* R1502 */ + { 0x0000, 0x0000 }, /* R1503 */ + { 0x0000, 0x0000 }, /* R1504 */ + { 0x0000, 0x0000 }, /* R1505 */ + { 0x0000, 0x0000 }, /* R1506 */ + { 0x0000, 0x0000 }, /* R1507 */ + { 0x0000, 0x0000 }, /* R1508 */ + { 0x0000, 0x0000 }, /* R1509 */ + { 0x0000, 0x0000 }, /* R1510 */ + { 0x0000, 0x0000 }, /* R1511 */ + { 0x0000, 0x0000 }, /* R1512 */ + { 0x0000, 0x0000 }, /* R1513 */ + { 0x0000, 0x0000 }, /* R1514 */ + { 0x0000, 0x0000 }, /* R1515 */ + { 0x0000, 0x0000 }, /* R1516 */ + { 0x0000, 0x0000 }, /* R1517 */ + { 0x0000, 0x0000 }, /* R1518 */ + { 0x0000, 0x0000 }, /* R1519 */ + { 0x0000, 0x0000 }, /* R1520 */ + { 0x0000, 0x0000 }, /* R1521 */ + { 0x0000, 0x0000 }, /* R1522 */ + { 0x0000, 0x0000 }, /* R1523 */ + { 0x0000, 0x0000 }, /* R1524 */ + { 0x0000, 0x0000 }, /* R1525 */ + { 0x0000, 0x0000 }, /* R1526 */ + { 0x0000, 0x0000 }, /* R1527 */ + { 0x0000, 0x0000 }, /* R1528 */ + { 0x0000, 0x0000 }, /* R1529 */ + { 0x0000, 0x0000 }, /* R1530 */ + { 0x0000, 0x0000 }, /* R1531 */ + { 0x0000, 0x0000 }, /* R1532 */ + { 0x0000, 0x0000 }, /* R1533 */ + { 0x0000, 0x0000 }, /* R1534 */ + { 0x0000, 0x0000 }, /* R1535 */ + { 0x01EF, 0x01EF }, /* R1536 - DAC1 Mixer Volumes */ + { 0x0037, 0x0037 }, /* R1537 - DAC1 Left Mixer Routing */ + { 0x0037, 0x0037 }, /* R1538 - DAC1 Right Mixer Routing */ + { 0x01EF, 0x01EF }, /* R1539 - DAC2 Mixer Volumes */ + { 0x0037, 0x0037 }, /* R1540 - DAC2 Left Mixer Routing */ + { 0x0037, 0x0037 }, /* R1541 - DAC2 Right Mixer Routing */ + { 0x0003, 0x0003 }, /* R1542 - AIF1 ADC1 Left Mixer Routing */ + { 0x0003, 0x0003 }, /* R1543 - AIF1 ADC1 Right Mixer Routing */ + { 0x0003, 0x0003 }, /* R1544 - AIF1 ADC2 Left Mixer Routing */ + { 0x0003, 0x0003 }, /* R1545 - AIF1 ADC2 Right mixer Routing */ + { 0x0000, 0x0000 }, /* R1546 */ + { 0x0000, 0x0000 }, /* R1547 */ + { 0x0000, 0x0000 }, /* R1548 */ + { 0x0000, 0x0000 }, /* R1549 */ + { 0x0000, 0x0000 }, /* R1550 */ + { 0x0000, 0x0000 }, /* R1551 */ + { 0x02FF, 0x03FF }, /* R1552 - DAC1 Left Volume */ + { 0x02FF, 0x03FF }, /* R1553 - DAC1 Right Volume */ + { 0x02FF, 0x03FF }, /* R1554 - DAC2 Left Volume */ + { 0x02FF, 0x03FF }, /* R1555 - DAC2 Right Volume */ + { 0x0003, 0x0003 }, /* R1556 - DAC Softmute */ + { 0x0000, 0x0000 }, /* R1557 */ + { 0x0000, 0x0000 }, /* R1558 */ + { 0x0000, 0x0000 }, /* R1559 */ + { 0x0000, 0x0000 }, /* R1560 */ + { 0x0000, 0x0000 }, /* R1561 */ + { 0x0000, 0x0000 }, /* R1562 */ + { 0x0000, 0x0000 }, /* R1563 */ + { 0x0000, 0x0000 }, /* R1564 */ + { 0x0000, 0x0000 }, /* R1565 */ + { 0x0000, 0x0000 }, /* R1566 */ + { 0x0000, 0x0000 }, /* R1567 */ + { 0x0003, 0x0003 }, /* R1568 - Oversampling */ + { 0x03C3, 0x03C3 }, /* R1569 - Sidetone */ +}; + +const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = { + 0x8994, /* R0 - Software Reset */ + 0x0000, /* R1 - Power Management (1) */ + 0x6000, /* R2 - Power Management (2) */ + 0x0000, /* R3 - Power Management (3) */ + 0x0000, /* R4 - Power Management (4) */ + 0x0000, /* R5 - Power Management (5) */ + 0x0000, /* R6 - Power Management (6) */ + 0x0000, /* R7 */ + 0x0000, /* R8 */ + 0x0000, /* R9 */ + 0x0000, /* R10 */ + 0x0000, /* R11 */ + 0x0000, /* R12 */ + 0x0000, /* R13 */ + 0x0000, /* R14 */ + 0x0000, /* R15 */ + 0x0000, /* R16 */ + 0x0000, /* R17 */ + 0x0000, /* R18 */ + 0x0000, /* R19 */ + 0x0000, /* R20 */ + 0x0000, /* R21 - Input Mixer (1) */ + 0x0000, /* R22 */ + 0x0000, /* R23 */ + 0x008B, /* R24 - Left Line Input 1&2 Volume */ + 0x008B, /* R25 - Left Line Input 3&4 Volume */ + 0x008B, /* R26 - Right Line Input 1&2 Volume */ + 0x008B, /* R27 - Right Line Input 3&4 Volume */ + 0x006D, /* R28 - Left Output Volume */ + 0x006D, /* R29 - Right Output Volume */ + 0x0066, /* R30 - Line Outputs Volume */ + 0x0020, /* R31 - HPOUT2 Volume */ + 0x0079, /* R32 - Left OPGA Volume */ + 0x0079, /* R33 - Right OPGA Volume */ + 0x0003, /* R34 - SPKMIXL Attenuation */ + 0x0003, /* R35 - SPKMIXR Attenuation */ + 0x0011, /* R36 - SPKOUT Mixers */ + 0x0140, /* R37 - ClassD */ + 0x0079, /* R38 - Speaker Volume Left */ + 0x0079, /* R39 - Speaker Volume Right */ + 0x0000, /* R40 - Input Mixer (2) */ + 0x0000, /* R41 - Input Mixer (3) */ + 0x0000, /* R42 - Input Mixer (4) */ + 0x0000, /* R43 - Input Mixer (5) */ + 0x0000, /* R44 - Input Mixer (6) */ + 0x0000, /* R45 - Output Mixer (1) */ + 0x0000, /* R46 - Output Mixer (2) */ + 0x0000, /* R47 - Output Mixer (3) */ + 0x0000, /* R48 - Output Mixer (4) */ + 0x0000, /* R49 - Output Mixer (5) */ + 0x0000, /* R50 - Output Mixer (6) */ + 0x0000, /* R51 - HPOUT2 Mixer */ + 0x0000, /* R52 - Line Mixer (1) */ + 0x0000, /* R53 - Line Mixer (2) */ + 0x0000, /* R54 - Speaker Mixer */ + 0x0000, /* R55 - Additional Control */ + 0x0000, /* R56 - AntiPOP (1) */ + 0x0000, /* R57 - AntiPOP (2) */ + 0x0000, /* R58 - MICBIAS */ + 0x000D, /* R59 - LDO 1 */ + 0x0003, /* R60 - LDO 2 */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x0000, /* R64 */ + 0x0000, /* R65 */ + 0x0000, /* R66 */ + 0x0000, /* R67 */ + 0x0000, /* R68 */ + 0x0000, /* R69 */ + 0x0000, /* R70 */ + 0x0000, /* R71 */ + 0x0000, /* R72 */ + 0x0000, /* R73 */ + 0x0000, /* R74 */ + 0x0000, /* R75 */ + 0x1F25, /* R76 - Charge Pump (1) */ + 0x0000, /* R77 */ + 0x0000, /* R78 */ + 0x0000, /* R79 */ + 0x0000, /* R80 */ + 0x0004, /* R81 - Class W (1) */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 - DC Servo (1) */ + 0x054A, /* R85 - DC Servo (2) */ + 0x0000, /* R86 */ + 0x0000, /* R87 - DC Servo (4) */ + 0x0000, /* R88 - DC Servo Readback */ + 0x0000, /* R89 */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 */ + 0x0000, /* R93 */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Analogue HP (1) */ + 0x0000, /* R97 */ + 0x0000, /* R98 */ + 0x0000, /* R99 */ + 0x0000, /* R100 */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x0000, /* R104 */ + 0x0000, /* R105 */ + 0x0000, /* R106 */ + 0x0000, /* R107 */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 */ + 0x0000, /* R112 */ + 0x0000, /* R113 */ + 0x0000, /* R114 */ + 0x0000, /* R115 */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x0000, /* R128 */ + 0x0000, /* R129 */ + 0x0000, /* R130 */ + 0x0000, /* R131 */ + 0x0000, /* R132 */ + 0x0000, /* R133 */ + 0x0000, /* R134 */ + 0x0000, /* R135 */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0000, /* R140 */ + 0x0000, /* R141 */ + 0x0000, /* R142 */ + 0x0000, /* R143 */ + 0x0000, /* R144 */ + 0x0000, /* R145 */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x0000, /* R152 */ + 0x0000, /* R153 */ + 0x0000, /* R154 */ + 0x0000, /* R155 */ + 0x0000, /* R156 */ + 0x0000, /* R157 */ + 0x0000, /* R158 */ + 0x0000, /* R159 */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 */ + 0x0000, /* R164 */ + 0x0000, /* R165 */ + 0x0000, /* R166 */ + 0x0000, /* R167 */ + 0x0000, /* R168 */ + 0x0000, /* R169 */ + 0x0000, /* R170 */ + 0x0000, /* R171 */ + 0x0000, /* R172 */ + 0x0000, /* R173 */ + 0x0000, /* R174 */ + 0x0000, /* R175 */ + 0x0000, /* R176 */ + 0x0000, /* R177 */ + 0x0000, /* R178 */ + 0x0000, /* R179 */ + 0x0000, /* R180 */ + 0x0000, /* R181 */ + 0x0000, /* R182 */ + 0x0000, /* R183 */ + 0x0000, /* R184 */ + 0x0000, /* R185 */ + 0x0000, /* R186 */ + 0x0000, /* R187 */ + 0x0000, /* R188 */ + 0x0000, /* R189 */ + 0x0000, /* R190 */ + 0x0000, /* R191 */ + 0x0000, /* R192 */ + 0x0000, /* R193 */ + 0x0000, /* R194 */ + 0x0000, /* R195 */ + 0x0000, /* R196 */ + 0x0000, /* R197 */ + 0x0000, /* R198 */ + 0x0000, /* R199 */ + 0x0000, /* R200 */ + 0x0000, /* R201 */ + 0x0000, /* R202 */ + 0x0000, /* R203 */ + 0x0000, /* R204 */ + 0x0000, /* R205 */ + 0x0000, /* R206 */ + 0x0000, /* R207 */ + 0x0000, /* R208 */ + 0x0000, /* R209 */ + 0x0000, /* R210 */ + 0x0000, /* R211 */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 */ + 0x0000, /* R216 */ + 0x0000, /* R217 */ + 0x0000, /* R218 */ + 0x0000, /* R219 */ + 0x0000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0x0000, /* R230 */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ + 0x0003, /* R256 - Chip Revision */ + 0x8004, /* R257 - Control Interface */ + 0x0000, /* R258 */ + 0x0000, /* R259 */ + 0x0000, /* R260 */ + 0x0000, /* R261 */ + 0x0000, /* R262 */ + 0x0000, /* R263 */ + 0x0000, /* R264 */ + 0x0000, /* R265 */ + 0x0000, /* R266 */ + 0x0000, /* R267 */ + 0x0000, /* R268 */ + 0x0000, /* R269 */ + 0x0000, /* R270 */ + 0x0000, /* R271 */ + 0x0000, /* R272 - Write Sequencer Ctrl (1) */ + 0x0000, /* R273 - Write Sequencer Ctrl (2) */ + 0x0000, /* R274 */ + 0x0000, /* R275 */ + 0x0000, /* R276 */ + 0x0000, /* R277 */ + 0x0000, /* R278 */ + 0x0000, /* R279 */ + 0x0000, /* R280 */ + 0x0000, /* R281 */ + 0x0000, /* R282 */ + 0x0000, /* R283 */ + 0x0000, /* R284 */ + 0x0000, /* R285 */ + 0x0000, /* R286 */ + 0x0000, /* R287 */ + 0x0000, /* R288 */ + 0x0000, /* R289 */ + 0x0000, /* R290 */ + 0x0000, /* R291 */ + 0x0000, /* R292 */ + 0x0000, /* R293 */ + 0x0000, /* R294 */ + 0x0000, /* R295 */ + 0x0000, /* R296 */ + 0x0000, /* R297 */ + 0x0000, /* R298 */ + 0x0000, /* R299 */ + 0x0000, /* R300 */ + 0x0000, /* R301 */ + 0x0000, /* R302 */ + 0x0000, /* R303 */ + 0x0000, /* R304 */ + 0x0000, /* R305 */ + 0x0000, /* R306 */ + 0x0000, /* R307 */ + 0x0000, /* R308 */ + 0x0000, /* R309 */ + 0x0000, /* R310 */ + 0x0000, /* R311 */ + 0x0000, /* R312 */ + 0x0000, /* R313 */ + 0x0000, /* R314 */ + 0x0000, /* R315 */ + 0x0000, /* R316 */ + 0x0000, /* R317 */ + 0x0000, /* R318 */ + 0x0000, /* R319 */ + 0x0000, /* R320 */ + 0x0000, /* R321 */ + 0x0000, /* R322 */ + 0x0000, /* R323 */ + 0x0000, /* R324 */ + 0x0000, /* R325 */ + 0x0000, /* R326 */ + 0x0000, /* R327 */ + 0x0000, /* R328 */ + 0x0000, /* R329 */ + 0x0000, /* R330 */ + 0x0000, /* R331 */ + 0x0000, /* R332 */ + 0x0000, /* R333 */ + 0x0000, /* R334 */ + 0x0000, /* R335 */ + 0x0000, /* R336 */ + 0x0000, /* R337 */ + 0x0000, /* R338 */ + 0x0000, /* R339 */ + 0x0000, /* R340 */ + 0x0000, /* R341 */ + 0x0000, /* R342 */ + 0x0000, /* R343 */ + 0x0000, /* R344 */ + 0x0000, /* R345 */ + 0x0000, /* R346 */ + 0x0000, /* R347 */ + 0x0000, /* R348 */ + 0x0000, /* R349 */ + 0x0000, /* R350 */ + 0x0000, /* R351 */ + 0x0000, /* R352 */ + 0x0000, /* R353 */ + 0x0000, /* R354 */ + 0x0000, /* R355 */ + 0x0000, /* R356 */ + 0x0000, /* R357 */ + 0x0000, /* R358 */ + 0x0000, /* R359 */ + 0x0000, /* R360 */ + 0x0000, /* R361 */ + 0x0000, /* R362 */ + 0x0000, /* R363 */ + 0x0000, /* R364 */ + 0x0000, /* R365 */ + 0x0000, /* R366 */ + 0x0000, /* R367 */ + 0x0000, /* R368 */ + 0x0000, /* R369 */ + 0x0000, /* R370 */ + 0x0000, /* R371 */ + 0x0000, /* R372 */ + 0x0000, /* R373 */ + 0x0000, /* R374 */ + 0x0000, /* R375 */ + 0x0000, /* R376 */ + 0x0000, /* R377 */ + 0x0000, /* R378 */ + 0x0000, /* R379 */ + 0x0000, /* R380 */ + 0x0000, /* R381 */ + 0x0000, /* R382 */ + 0x0000, /* R383 */ + 0x0000, /* R384 */ + 0x0000, /* R385 */ + 0x0000, /* R386 */ + 0x0000, /* R387 */ + 0x0000, /* R388 */ + 0x0000, /* R389 */ + 0x0000, /* R390 */ + 0x0000, /* R391 */ + 0x0000, /* R392 */ + 0x0000, /* R393 */ + 0x0000, /* R394 */ + 0x0000, /* R395 */ + 0x0000, /* R396 */ + 0x0000, /* R397 */ + 0x0000, /* R398 */ + 0x0000, /* R399 */ + 0x0000, /* R400 */ + 0x0000, /* R401 */ + 0x0000, /* R402 */ + 0x0000, /* R403 */ + 0x0000, /* R404 */ + 0x0000, /* R405 */ + 0x0000, /* R406 */ + 0x0000, /* R407 */ + 0x0000, /* R408 */ + 0x0000, /* R409 */ + 0x0000, /* R410 */ + 0x0000, /* R411 */ + 0x0000, /* R412 */ + 0x0000, /* R413 */ + 0x0000, /* R414 */ + 0x0000, /* R415 */ + 0x0000, /* R416 */ + 0x0000, /* R417 */ + 0x0000, /* R418 */ + 0x0000, /* R419 */ + 0x0000, /* R420 */ + 0x0000, /* R421 */ + 0x0000, /* R422 */ + 0x0000, /* R423 */ + 0x0000, /* R424 */ + 0x0000, /* R425 */ + 0x0000, /* R426 */ + 0x0000, /* R427 */ + 0x0000, /* R428 */ + 0x0000, /* R429 */ + 0x0000, /* R430 */ + 0x0000, /* R431 */ + 0x0000, /* R432 */ + 0x0000, /* R433 */ + 0x0000, /* R434 */ + 0x0000, /* R435 */ + 0x0000, /* R436 */ + 0x0000, /* R437 */ + 0x0000, /* R438 */ + 0x0000, /* R439 */ + 0x0000, /* R440 */ + 0x0000, /* R441 */ + 0x0000, /* R442 */ + 0x0000, /* R443 */ + 0x0000, /* R444 */ + 0x0000, /* R445 */ + 0x0000, /* R446 */ + 0x0000, /* R447 */ + 0x0000, /* R448 */ + 0x0000, /* R449 */ + 0x0000, /* R450 */ + 0x0000, /* R451 */ + 0x0000, /* R452 */ + 0x0000, /* R453 */ + 0x0000, /* R454 */ + 0x0000, /* R455 */ + 0x0000, /* R456 */ + 0x0000, /* R457 */ + 0x0000, /* R458 */ + 0x0000, /* R459 */ + 0x0000, /* R460 */ + 0x0000, /* R461 */ + 0x0000, /* R462 */ + 0x0000, /* R463 */ + 0x0000, /* R464 */ + 0x0000, /* R465 */ + 0x0000, /* R466 */ + 0x0000, /* R467 */ + 0x0000, /* R468 */ + 0x0000, /* R469 */ + 0x0000, /* R470 */ + 0x0000, /* R471 */ + 0x0000, /* R472 */ + 0x0000, /* R473 */ + 0x0000, /* R474 */ + 0x0000, /* R475 */ + 0x0000, /* R476 */ + 0x0000, /* R477 */ + 0x0000, /* R478 */ + 0x0000, /* R479 */ + 0x0000, /* R480 */ + 0x0000, /* R481 */ + 0x0000, /* R482 */ + 0x0000, /* R483 */ + 0x0000, /* R484 */ + 0x0000, /* R485 */ + 0x0000, /* R486 */ + 0x0000, /* R487 */ + 0x0000, /* R488 */ + 0x0000, /* R489 */ + 0x0000, /* R490 */ + 0x0000, /* R491 */ + 0x0000, /* R492 */ + 0x0000, /* R493 */ + 0x0000, /* R494 */ + 0x0000, /* R495 */ + 0x0000, /* R496 */ + 0x0000, /* R497 */ + 0x0000, /* R498 */ + 0x0000, /* R499 */ + 0x0000, /* R500 */ + 0x0000, /* R501 */ + 0x0000, /* R502 */ + 0x0000, /* R503 */ + 0x0000, /* R504 */ + 0x0000, /* R505 */ + 0x0000, /* R506 */ + 0x0000, /* R507 */ + 0x0000, /* R508 */ + 0x0000, /* R509 */ + 0x0000, /* R510 */ + 0x0000, /* R511 */ + 0x0000, /* R512 - AIF1 Clocking (1) */ + 0x0000, /* R513 - AIF1 Clocking (2) */ + 0x0000, /* R514 */ + 0x0000, /* R515 */ + 0x0000, /* R516 - AIF2 Clocking (1) */ + 0x0000, /* R517 - AIF2 Clocking (2) */ + 0x0000, /* R518 */ + 0x0000, /* R519 */ + 0x0000, /* R520 - Clocking (1) */ + 0x0000, /* R521 - Clocking (2) */ + 0x0000, /* R522 */ + 0x0000, /* R523 */ + 0x0000, /* R524 */ + 0x0000, /* R525 */ + 0x0000, /* R526 */ + 0x0000, /* R527 */ + 0x0083, /* R528 - AIF1 Rate */ + 0x0083, /* R529 - AIF2 Rate */ + 0x0000, /* R530 - Rate Status */ + 0x0000, /* R531 */ + 0x0000, /* R532 */ + 0x0000, /* R533 */ + 0x0000, /* R534 */ + 0x0000, /* R535 */ + 0x0000, /* R536 */ + 0x0000, /* R537 */ + 0x0000, /* R538 */ + 0x0000, /* R539 */ + 0x0000, /* R540 */ + 0x0000, /* R541 */ + 0x0000, /* R542 */ + 0x0000, /* R543 */ + 0x0000, /* R544 - FLL1 Control (1) */ + 0x0000, /* R545 - FLL1 Control (2) */ + 0x0000, /* R546 - FLL1 Control (3) */ + 0x0000, /* R547 - FLL1 Control (4) */ + 0x0C80, /* R548 - FLL1 Control (5) */ + 0x0000, /* R549 */ + 0x0000, /* R550 */ + 0x0000, /* R551 */ + 0x0000, /* R552 */ + 0x0000, /* R553 */ + 0x0000, /* R554 */ + 0x0000, /* R555 */ + 0x0000, /* R556 */ + 0x0000, /* R557 */ + 0x0000, /* R558 */ + 0x0000, /* R559 */ + 0x0000, /* R560 */ + 0x0000, /* R561 */ + 0x0000, /* R562 */ + 0x0000, /* R563 */ + 0x0000, /* R564 */ + 0x0000, /* R565 */ + 0x0000, /* R566 */ + 0x0000, /* R567 */ + 0x0000, /* R568 */ + 0x0000, /* R569 */ + 0x0000, /* R570 */ + 0x0000, /* R571 */ + 0x0000, /* R572 */ + 0x0000, /* R573 */ + 0x0000, /* R574 */ + 0x0000, /* R575 */ + 0x0000, /* R576 - FLL2 Control (1) */ + 0x0000, /* R577 - FLL2 Control (2) */ + 0x0000, /* R578 - FLL2 Control (3) */ + 0x0000, /* R579 - FLL2 Control (4) */ + 0x0C80, /* R580 - FLL2 Control (5) */ + 0x0000, /* R581 */ + 0x0000, /* R582 */ + 0x0000, /* R583 */ + 0x0000, /* R584 */ + 0x0000, /* R585 */ + 0x0000, /* R586 */ + 0x0000, /* R587 */ + 0x0000, /* R588 */ + 0x0000, /* R589 */ + 0x0000, /* R590 */ + 0x0000, /* R591 */ + 0x0000, /* R592 */ + 0x0000, /* R593 */ + 0x0000, /* R594 */ + 0x0000, /* R595 */ + 0x0000, /* R596 */ + 0x0000, /* R597 */ + 0x0000, /* R598 */ + 0x0000, /* R599 */ + 0x0000, /* R600 */ + 0x0000, /* R601 */ + 0x0000, /* R602 */ + 0x0000, /* R603 */ + 0x0000, /* R604 */ + 0x0000, /* R605 */ + 0x0000, /* R606 */ + 0x0000, /* R607 */ + 0x0000, /* R608 */ + 0x0000, /* R609 */ + 0x0000, /* R610 */ + 0x0000, /* R611 */ + 0x0000, /* R612 */ + 0x0000, /* R613 */ + 0x0000, /* R614 */ + 0x0000, /* R615 */ + 0x0000, /* R616 */ + 0x0000, /* R617 */ + 0x0000, /* R618 */ + 0x0000, /* R619 */ + 0x0000, /* R620 */ + 0x0000, /* R621 */ + 0x0000, /* R622 */ + 0x0000, /* R623 */ + 0x0000, /* R624 */ + 0x0000, /* R625 */ + 0x0000, /* R626 */ + 0x0000, /* R627 */ + 0x0000, /* R628 */ + 0x0000, /* R629 */ + 0x0000, /* R630 */ + 0x0000, /* R631 */ + 0x0000, /* R632 */ + 0x0000, /* R633 */ + 0x0000, /* R634 */ + 0x0000, /* R635 */ + 0x0000, /* R636 */ + 0x0000, /* R637 */ + 0x0000, /* R638 */ + 0x0000, /* R639 */ + 0x0000, /* R640 */ + 0x0000, /* R641 */ + 0x0000, /* R642 */ + 0x0000, /* R643 */ + 0x0000, /* R644 */ + 0x0000, /* R645 */ + 0x0000, /* R646 */ + 0x0000, /* R647 */ + 0x0000, /* R648 */ + 0x0000, /* R649 */ + 0x0000, /* R650 */ + 0x0000, /* R651 */ + 0x0000, /* R652 */ + 0x0000, /* R653 */ + 0x0000, /* R654 */ + 0x0000, /* R655 */ + 0x0000, /* R656 */ + 0x0000, /* R657 */ + 0x0000, /* R658 */ + 0x0000, /* R659 */ + 0x0000, /* R660 */ + 0x0000, /* R661 */ + 0x0000, /* R662 */ + 0x0000, /* R663 */ + 0x0000, /* R664 */ + 0x0000, /* R665 */ + 0x0000, /* R666 */ + 0x0000, /* R667 */ + 0x0000, /* R668 */ + 0x0000, /* R669 */ + 0x0000, /* R670 */ + 0x0000, /* R671 */ + 0x0000, /* R672 */ + 0x0000, /* R673 */ + 0x0000, /* R674 */ + 0x0000, /* R675 */ + 0x0000, /* R676 */ + 0x0000, /* R677 */ + 0x0000, /* R678 */ + 0x0000, /* R679 */ + 0x0000, /* R680 */ + 0x0000, /* R681 */ + 0x0000, /* R682 */ + 0x0000, /* R683 */ + 0x0000, /* R684 */ + 0x0000, /* R685 */ + 0x0000, /* R686 */ + 0x0000, /* R687 */ + 0x0000, /* R688 */ + 0x0000, /* R689 */ + 0x0000, /* R690 */ + 0x0000, /* R691 */ + 0x0000, /* R692 */ + 0x0000, /* R693 */ + 0x0000, /* R694 */ + 0x0000, /* R695 */ + 0x0000, /* R696 */ + 0x0000, /* R697 */ + 0x0000, /* R698 */ + 0x0000, /* R699 */ + 0x0000, /* R700 */ + 0x0000, /* R701 */ + 0x0000, /* R702 */ + 0x0000, /* R703 */ + 0x0000, /* R704 */ + 0x0000, /* R705 */ + 0x0000, /* R706 */ + 0x0000, /* R707 */ + 0x0000, /* R708 */ + 0x0000, /* R709 */ + 0x0000, /* R710 */ + 0x0000, /* R711 */ + 0x0000, /* R712 */ + 0x0000, /* R713 */ + 0x0000, /* R714 */ + 0x0000, /* R715 */ + 0x0000, /* R716 */ + 0x0000, /* R717 */ + 0x0000, /* R718 */ + 0x0000, /* R719 */ + 0x0000, /* R720 */ + 0x0000, /* R721 */ + 0x0000, /* R722 */ + 0x0000, /* R723 */ + 0x0000, /* R724 */ + 0x0000, /* R725 */ + 0x0000, /* R726 */ + 0x0000, /* R727 */ + 0x0000, /* R728 */ + 0x0000, /* R729 */ + 0x0000, /* R730 */ + 0x0000, /* R731 */ + 0x0000, /* R732 */ + 0x0000, /* R733 */ + 0x0000, /* R734 */ + 0x0000, /* R735 */ + 0x0000, /* R736 */ + 0x0000, /* R737 */ + 0x0000, /* R738 */ + 0x0000, /* R739 */ + 0x0000, /* R740 */ + 0x0000, /* R741 */ + 0x0000, /* R742 */ + 0x0000, /* R743 */ + 0x0000, /* R744 */ + 0x0000, /* R745 */ + 0x0000, /* R746 */ + 0x0000, /* R747 */ + 0x0000, /* R748 */ + 0x0000, /* R749 */ + 0x0000, /* R750 */ + 0x0000, /* R751 */ + 0x0000, /* R752 */ + 0x0000, /* R753 */ + 0x0000, /* R754 */ + 0x0000, /* R755 */ + 0x0000, /* R756 */ + 0x0000, /* R757 */ + 0x0000, /* R758 */ + 0x0000, /* R759 */ + 0x0000, /* R760 */ + 0x0000, /* R761 */ + 0x0000, /* R762 */ + 0x0000, /* R763 */ + 0x0000, /* R764 */ + 0x0000, /* R765 */ + 0x0000, /* R766 */ + 0x0000, /* R767 */ + 0x4050, /* R768 - AIF1 Control (1) */ + 0x4000, /* R769 - AIF1 Control (2) */ + 0x0000, /* R770 - AIF1 Master/Slave */ + 0x0040, /* R771 - AIF1 BCLK */ + 0x0040, /* R772 - AIF1ADC LRCLK */ + 0x0040, /* R773 - AIF1DAC LRCLK */ + 0x0004, /* R774 - AIF1DAC Data */ + 0x0100, /* R775 - AIF1ADC Data */ + 0x0000, /* R776 */ + 0x0000, /* R777 */ + 0x0000, /* R778 */ + 0x0000, /* R779 */ + 0x0000, /* R780 */ + 0x0000, /* R781 */ + 0x0000, /* R782 */ + 0x0000, /* R783 */ + 0x4050, /* R784 - AIF2 Control (1) */ + 0x4000, /* R785 - AIF2 Control (2) */ + 0x0000, /* R786 - AIF2 Master/Slave */ + 0x0040, /* R787 - AIF2 BCLK */ + 0x0040, /* R788 - AIF2ADC LRCLK */ + 0x0040, /* R789 - AIF2DAC LRCLK */ + 0x0000, /* R790 - AIF2DAC Data */ + 0x0000, /* R791 - AIF2ADC Data */ + 0x0000, /* R792 */ + 0x0000, /* R793 */ + 0x0000, /* R794 */ + 0x0000, /* R795 */ + 0x0000, /* R796 */ + 0x0000, /* R797 */ + 0x0000, /* R798 */ + 0x0000, /* R799 */ + 0x0000, /* R800 */ + 0x0000, /* R801 */ + 0x0000, /* R802 */ + 0x0000, /* R803 */ + 0x0000, /* R804 */ + 0x0000, /* R805 */ + 0x0000, /* R806 */ + 0x0000, /* R807 */ + 0x0000, /* R808 */ + 0x0000, /* R809 */ + 0x0000, /* R810 */ + 0x0000, /* R811 */ + 0x0000, /* R812 */ + 0x0000, /* R813 */ + 0x0000, /* R814 */ + 0x0000, /* R815 */ + 0x0000, /* R816 */ + 0x0000, /* R817 */ + 0x0000, /* R818 */ + 0x0000, /* R819 */ + 0x0000, /* R820 */ + 0x0000, /* R821 */ + 0x0000, /* R822 */ + 0x0000, /* R823 */ + 0x0000, /* R824 */ + 0x0000, /* R825 */ + 0x0000, /* R826 */ + 0x0000, /* R827 */ + 0x0000, /* R828 */ + 0x0000, /* R829 */ + 0x0000, /* R830 */ + 0x0000, /* R831 */ + 0x0000, /* R832 */ + 0x0000, /* R833 */ + 0x0000, /* R834 */ + 0x0000, /* R835 */ + 0x0000, /* R836 */ + 0x0000, /* R837 */ + 0x0000, /* R838 */ + 0x0000, /* R839 */ + 0x0000, /* R840 */ + 0x0000, /* R841 */ + 0x0000, /* R842 */ + 0x0000, /* R843 */ + 0x0000, /* R844 */ + 0x0000, /* R845 */ + 0x0000, /* R846 */ + 0x0000, /* R847 */ + 0x0000, /* R848 */ + 0x0000, /* R849 */ + 0x0000, /* R850 */ + 0x0000, /* R851 */ + 0x0000, /* R852 */ + 0x0000, /* R853 */ + 0x0000, /* R854 */ + 0x0000, /* R855 */ + 0x0000, /* R856 */ + 0x0000, /* R857 */ + 0x0000, /* R858 */ + 0x0000, /* R859 */ + 0x0000, /* R860 */ + 0x0000, /* R861 */ + 0x0000, /* R862 */ + 0x0000, /* R863 */ + 0x0000, /* R864 */ + 0x0000, /* R865 */ + 0x0000, /* R866 */ + 0x0000, /* R867 */ + 0x0000, /* R868 */ + 0x0000, /* R869 */ + 0x0000, /* R870 */ + 0x0000, /* R871 */ + 0x0000, /* R872 */ + 0x0000, /* R873 */ + 0x0000, /* R874 */ + 0x0000, /* R875 */ + 0x0000, /* R876 */ + 0x0000, /* R877 */ + 0x0000, /* R878 */ + 0x0000, /* R879 */ + 0x0000, /* R880 */ + 0x0000, /* R881 */ + 0x0000, /* R882 */ + 0x0000, /* R883 */ + 0x0000, /* R884 */ + 0x0000, /* R885 */ + 0x0000, /* R886 */ + 0x0000, /* R887 */ + 0x0000, /* R888 */ + 0x0000, /* R889 */ + 0x0000, /* R890 */ + 0x0000, /* R891 */ + 0x0000, /* R892 */ + 0x0000, /* R893 */ + 0x0000, /* R894 */ + 0x0000, /* R895 */ + 0x0000, /* R896 */ + 0x0000, /* R897 */ + 0x0000, /* R898 */ + 0x0000, /* R899 */ + 0x0000, /* R900 */ + 0x0000, /* R901 */ + 0x0000, /* R902 */ + 0x0000, /* R903 */ + 0x0000, /* R904 */ + 0x0000, /* R905 */ + 0x0000, /* R906 */ + 0x0000, /* R907 */ + 0x0000, /* R908 */ + 0x0000, /* R909 */ + 0x0000, /* R910 */ + 0x0000, /* R911 */ + 0x0000, /* R912 */ + 0x0000, /* R913 */ + 0x0000, /* R914 */ + 0x0000, /* R915 */ + 0x0000, /* R916 */ + 0x0000, /* R917 */ + 0x0000, /* R918 */ + 0x0000, /* R919 */ + 0x0000, /* R920 */ + 0x0000, /* R921 */ + 0x0000, /* R922 */ + 0x0000, /* R923 */ + 0x0000, /* R924 */ + 0x0000, /* R925 */ + 0x0000, /* R926 */ + 0x0000, /* R927 */ + 0x0000, /* R928 */ + 0x0000, /* R929 */ + 0x0000, /* R930 */ + 0x0000, /* R931 */ + 0x0000, /* R932 */ + 0x0000, /* R933 */ + 0x0000, /* R934 */ + 0x0000, /* R935 */ + 0x0000, /* R936 */ + 0x0000, /* R937 */ + 0x0000, /* R938 */ + 0x0000, /* R939 */ + 0x0000, /* R940 */ + 0x0000, /* R941 */ + 0x0000, /* R942 */ + 0x0000, /* R943 */ + 0x0000, /* R944 */ + 0x0000, /* R945 */ + 0x0000, /* R946 */ + 0x0000, /* R947 */ + 0x0000, /* R948 */ + 0x0000, /* R949 */ + 0x0000, /* R950 */ + 0x0000, /* R951 */ + 0x0000, /* R952 */ + 0x0000, /* R953 */ + 0x0000, /* R954 */ + 0x0000, /* R955 */ + 0x0000, /* R956 */ + 0x0000, /* R957 */ + 0x0000, /* R958 */ + 0x0000, /* R959 */ + 0x0000, /* R960 */ + 0x0000, /* R961 */ + 0x0000, /* R962 */ + 0x0000, /* R963 */ + 0x0000, /* R964 */ + 0x0000, /* R965 */ + 0x0000, /* R966 */ + 0x0000, /* R967 */ + 0x0000, /* R968 */ + 0x0000, /* R969 */ + 0x0000, /* R970 */ + 0x0000, /* R971 */ + 0x0000, /* R972 */ + 0x0000, /* R973 */ + 0x0000, /* R974 */ + 0x0000, /* R975 */ + 0x0000, /* R976 */ + 0x0000, /* R977 */ + 0x0000, /* R978 */ + 0x0000, /* R979 */ + 0x0000, /* R980 */ + 0x0000, /* R981 */ + 0x0000, /* R982 */ + 0x0000, /* R983 */ + 0x0000, /* R984 */ + 0x0000, /* R985 */ + 0x0000, /* R986 */ + 0x0000, /* R987 */ + 0x0000, /* R988 */ + 0x0000, /* R989 */ + 0x0000, /* R990 */ + 0x0000, /* R991 */ + 0x0000, /* R992 */ + 0x0000, /* R993 */ + 0x0000, /* R994 */ + 0x0000, /* R995 */ + 0x0000, /* R996 */ + 0x0000, /* R997 */ + 0x0000, /* R998 */ + 0x0000, /* R999 */ + 0x0000, /* R1000 */ + 0x0000, /* R1001 */ + 0x0000, /* R1002 */ + 0x0000, /* R1003 */ + 0x0000, /* R1004 */ + 0x0000, /* R1005 */ + 0x0000, /* R1006 */ + 0x0000, /* R1007 */ + 0x0000, /* R1008 */ + 0x0000, /* R1009 */ + 0x0000, /* R1010 */ + 0x0000, /* R1011 */ + 0x0000, /* R1012 */ + 0x0000, /* R1013 */ + 0x0000, /* R1014 */ + 0x0000, /* R1015 */ + 0x0000, /* R1016 */ + 0x0000, /* R1017 */ + 0x0000, /* R1018 */ + 0x0000, /* R1019 */ + 0x0000, /* R1020 */ + 0x0000, /* R1021 */ + 0x0000, /* R1022 */ + 0x0000, /* R1023 */ + 0x00C0, /* R1024 - AIF1 ADC1 Left Volume */ + 0x00C0, /* R1025 - AIF1 ADC1 Right Volume */ + 0x00C0, /* R1026 - AIF1 DAC1 Left Volume */ + 0x00C0, /* R1027 - AIF1 DAC1 Right Volume */ + 0x00C0, /* R1028 - AIF1 ADC2 Left Volume */ + 0x00C0, /* R1029 - AIF1 ADC2 Right Volume */ + 0x00C0, /* R1030 - AIF1 DAC2 Left Volume */ + 0x00C0, /* R1031 - AIF1 DAC2 Right Volume */ + 0x0000, /* R1032 */ + 0x0000, /* R1033 */ + 0x0000, /* R1034 */ + 0x0000, /* R1035 */ + 0x0000, /* R1036 */ + 0x0000, /* R1037 */ + 0x0000, /* R1038 */ + 0x0000, /* R1039 */ + 0x0000, /* R1040 - AIF1 ADC1 Filters */ + 0x0000, /* R1041 - AIF1 ADC2 Filters */ + 0x0000, /* R1042 */ + 0x0000, /* R1043 */ + 0x0000, /* R1044 */ + 0x0000, /* R1045 */ + 0x0000, /* R1046 */ + 0x0000, /* R1047 */ + 0x0000, /* R1048 */ + 0x0000, /* R1049 */ + 0x0000, /* R1050 */ + 0x0000, /* R1051 */ + 0x0000, /* R1052 */ + 0x0000, /* R1053 */ + 0x0000, /* R1054 */ + 0x0000, /* R1055 */ + 0x0200, /* R1056 - AIF1 DAC1 Filters (1) */ + 0x0010, /* R1057 - AIF1 DAC1 Filters (2) */ + 0x0200, /* R1058 - AIF1 DAC2 Filters (1) */ + 0x0010, /* R1059 - AIF1 DAC2 Filters (2) */ + 0x0000, /* R1060 */ + 0x0000, /* R1061 */ + 0x0000, /* R1062 */ + 0x0000, /* R1063 */ + 0x0000, /* R1064 */ + 0x0000, /* R1065 */ + 0x0000, /* R1066 */ + 0x0000, /* R1067 */ + 0x0000, /* R1068 */ + 0x0000, /* R1069 */ + 0x0000, /* R1070 */ + 0x0000, /* R1071 */ + 0x0000, /* R1072 */ + 0x0000, /* R1073 */ + 0x0000, /* R1074 */ + 0x0000, /* R1075 */ + 0x0000, /* R1076 */ + 0x0000, /* R1077 */ + 0x0000, /* R1078 */ + 0x0000, /* R1079 */ + 0x0000, /* R1080 */ + 0x0000, /* R1081 */ + 0x0000, /* R1082 */ + 0x0000, /* R1083 */ + 0x0000, /* R1084 */ + 0x0000, /* R1085 */ + 0x0000, /* R1086 */ + 0x0000, /* R1087 */ + 0x0098, /* R1088 - AIF1 DRC1 (1) */ + 0x0845, /* R1089 - AIF1 DRC1 (2) */ + 0x0000, /* R1090 - AIF1 DRC1 (3) */ + 0x0000, /* R1091 - AIF1 DRC1 (4) */ + 0x0000, /* R1092 - AIF1 DRC1 (5) */ + 0x0000, /* R1093 */ + 0x0000, /* R1094 */ + 0x0000, /* R1095 */ + 0x0000, /* R1096 */ + 0x0000, /* R1097 */ + 0x0000, /* R1098 */ + 0x0000, /* R1099 */ + 0x0000, /* R1100 */ + 0x0000, /* R1101 */ + 0x0000, /* R1102 */ + 0x0000, /* R1103 */ + 0x0098, /* R1104 - AIF1 DRC2 (1) */ + 0x0845, /* R1105 - AIF1 DRC2 (2) */ + 0x0000, /* R1106 - AIF1 DRC2 (3) */ + 0x0000, /* R1107 - AIF1 DRC2 (4) */ + 0x0000, /* R1108 - AIF1 DRC2 (5) */ + 0x0000, /* R1109 */ + 0x0000, /* R1110 */ + 0x0000, /* R1111 */ + 0x0000, /* R1112 */ + 0x0000, /* R1113 */ + 0x0000, /* R1114 */ + 0x0000, /* R1115 */ + 0x0000, /* R1116 */ + 0x0000, /* R1117 */ + 0x0000, /* R1118 */ + 0x0000, /* R1119 */ + 0x0000, /* R1120 */ + 0x0000, /* R1121 */ + 0x0000, /* R1122 */ + 0x0000, /* R1123 */ + 0x0000, /* R1124 */ + 0x0000, /* R1125 */ + 0x0000, /* R1126 */ + 0x0000, /* R1127 */ + 0x0000, /* R1128 */ + 0x0000, /* R1129 */ + 0x0000, /* R1130 */ + 0x0000, /* R1131 */ + 0x0000, /* R1132 */ + 0x0000, /* R1133 */ + 0x0000, /* R1134 */ + 0x0000, /* R1135 */ + 0x0000, /* R1136 */ + 0x0000, /* R1137 */ + 0x0000, /* R1138 */ + 0x0000, /* R1139 */ + 0x0000, /* R1140 */ + 0x0000, /* R1141 */ + 0x0000, /* R1142 */ + 0x0000, /* R1143 */ + 0x0000, /* R1144 */ + 0x0000, /* R1145 */ + 0x0000, /* R1146 */ + 0x0000, /* R1147 */ + 0x0000, /* R1148 */ + 0x0000, /* R1149 */ + 0x0000, /* R1150 */ + 0x0000, /* R1151 */ + 0x6318, /* R1152 - AIF1 DAC1 EQ Gains (1) */ + 0x6300, /* R1153 - AIF1 DAC1 EQ Gains (2) */ + 0x0FCA, /* R1154 - AIF1 DAC1 EQ Band 1 A */ + 0x0400, /* R1155 - AIF1 DAC1 EQ Band 1 B */ + 0x00D8, /* R1156 - AIF1 DAC1 EQ Band 1 PG */ + 0x1EB5, /* R1157 - AIF1 DAC1 EQ Band 2 A */ + 0xF145, /* R1158 - AIF1 DAC1 EQ Band 2 B */ + 0x0B75, /* R1159 - AIF1 DAC1 EQ Band 2 C */ + 0x01C5, /* R1160 - AIF1 DAC1 EQ Band 2 PG */ + 0x1C58, /* R1161 - AIF1 DAC1 EQ Band 3 A */ + 0xF373, /* R1162 - AIF1 DAC1 EQ Band 3 B */ + 0x0A54, /* R1163 - AIF1 DAC1 EQ Band 3 C */ + 0x0558, /* R1164 - AIF1 DAC1 EQ Band 3 PG */ + 0x168E, /* R1165 - AIF1 DAC1 EQ Band 4 A */ + 0xF829, /* R1166 - AIF1 DAC1 EQ Band 4 B */ + 0x07AD, /* R1167 - AIF1 DAC1 EQ Band 4 C */ + 0x1103, /* R1168 - AIF1 DAC1 EQ Band 4 PG */ + 0x0564, /* R1169 - AIF1 DAC1 EQ Band 5 A */ + 0x0559, /* R1170 - AIF1 DAC1 EQ Band 5 B */ + 0x4000, /* R1171 - AIF1 DAC1 EQ Band 5 PG */ + 0x0000, /* R1172 */ + 0x0000, /* R1173 */ + 0x0000, /* R1174 */ + 0x0000, /* R1175 */ + 0x0000, /* R1176 */ + 0x0000, /* R1177 */ + 0x0000, /* R1178 */ + 0x0000, /* R1179 */ + 0x0000, /* R1180 */ + 0x0000, /* R1181 */ + 0x0000, /* R1182 */ + 0x0000, /* R1183 */ + 0x6318, /* R1184 - AIF1 DAC2 EQ Gains (1) */ + 0x6300, /* R1185 - AIF1 DAC2 EQ Gains (2) */ + 0x0FCA, /* R1186 - AIF1 DAC2 EQ Band 1 A */ + 0x0400, /* R1187 - AIF1 DAC2 EQ Band 1 B */ + 0x00D8, /* R1188 - AIF1 DAC2 EQ Band 1 PG */ + 0x1EB5, /* R1189 - AIF1 DAC2 EQ Band 2 A */ + 0xF145, /* R1190 - AIF1 DAC2 EQ Band 2 B */ + 0x0B75, /* R1191 - AIF1 DAC2 EQ Band 2 C */ + 0x01C5, /* R1192 - AIF1 DAC2 EQ Band 2 PG */ + 0x1C58, /* R1193 - AIF1 DAC2 EQ Band 3 A */ + 0xF373, /* R1194 - AIF1 DAC2 EQ Band 3 B */ + 0x0A54, /* R1195 - AIF1 DAC2 EQ Band 3 C */ + 0x0558, /* R1196 - AIF1 DAC2 EQ Band 3 PG */ + 0x168E, /* R1197 - AIF1 DAC2 EQ Band 4 A */ + 0xF829, /* R1198 - AIF1 DAC2 EQ Band 4 B */ + 0x07AD, /* R1199 - AIF1 DAC2 EQ Band 4 C */ + 0x1103, /* R1200 - AIF1 DAC2 EQ Band 4 PG */ + 0x0564, /* R1201 - AIF1 DAC2 EQ Band 5 A */ + 0x0559, /* R1202 - AIF1 DAC2 EQ Band 5 B */ + 0x4000, /* R1203 - AIF1 DAC2 EQ Band 5 PG */ + 0x0000, /* R1204 */ + 0x0000, /* R1205 */ + 0x0000, /* R1206 */ + 0x0000, /* R1207 */ + 0x0000, /* R1208 */ + 0x0000, /* R1209 */ + 0x0000, /* R1210 */ + 0x0000, /* R1211 */ + 0x0000, /* R1212 */ + 0x0000, /* R1213 */ + 0x0000, /* R1214 */ + 0x0000, /* R1215 */ + 0x0000, /* R1216 */ + 0x0000, /* R1217 */ + 0x0000, /* R1218 */ + 0x0000, /* R1219 */ + 0x0000, /* R1220 */ + 0x0000, /* R1221 */ + 0x0000, /* R1222 */ + 0x0000, /* R1223 */ + 0x0000, /* R1224 */ + 0x0000, /* R1225 */ + 0x0000, /* R1226 */ + 0x0000, /* R1227 */ + 0x0000, /* R1228 */ + 0x0000, /* R1229 */ + 0x0000, /* R1230 */ + 0x0000, /* R1231 */ + 0x0000, /* R1232 */ + 0x0000, /* R1233 */ + 0x0000, /* R1234 */ + 0x0000, /* R1235 */ + 0x0000, /* R1236 */ + 0x0000, /* R1237 */ + 0x0000, /* R1238 */ + 0x0000, /* R1239 */ + 0x0000, /* R1240 */ + 0x0000, /* R1241 */ + 0x0000, /* R1242 */ + 0x0000, /* R1243 */ + 0x0000, /* R1244 */ + 0x0000, /* R1245 */ + 0x0000, /* R1246 */ + 0x0000, /* R1247 */ + 0x0000, /* R1248 */ + 0x0000, /* R1249 */ + 0x0000, /* R1250 */ + 0x0000, /* R1251 */ + 0x0000, /* R1252 */ + 0x0000, /* R1253 */ + 0x0000, /* R1254 */ + 0x0000, /* R1255 */ + 0x0000, /* R1256 */ + 0x0000, /* R1257 */ + 0x0000, /* R1258 */ + 0x0000, /* R1259 */ + 0x0000, /* R1260 */ + 0x0000, /* R1261 */ + 0x0000, /* R1262 */ + 0x0000, /* R1263 */ + 0x0000, /* R1264 */ + 0x0000, /* R1265 */ + 0x0000, /* R1266 */ + 0x0000, /* R1267 */ + 0x0000, /* R1268 */ + 0x0000, /* R1269 */ + 0x0000, /* R1270 */ + 0x0000, /* R1271 */ + 0x0000, /* R1272 */ + 0x0000, /* R1273 */ + 0x0000, /* R1274 */ + 0x0000, /* R1275 */ + 0x0000, /* R1276 */ + 0x0000, /* R1277 */ + 0x0000, /* R1278 */ + 0x0000, /* R1279 */ + 0x00C0, /* R1280 - AIF2 ADC Left Volume */ + 0x00C0, /* R1281 - AIF2 ADC Right Volume */ + 0x00C0, /* R1282 - AIF2 DAC Left Volume */ + 0x00C0, /* R1283 - AIF2 DAC Right Volume */ + 0x0000, /* R1284 */ + 0x0000, /* R1285 */ + 0x0000, /* R1286 */ + 0x0000, /* R1287 */ + 0x0000, /* R1288 */ + 0x0000, /* R1289 */ + 0x0000, /* R1290 */ + 0x0000, /* R1291 */ + 0x0000, /* R1292 */ + 0x0000, /* R1293 */ + 0x0000, /* R1294 */ + 0x0000, /* R1295 */ + 0x0000, /* R1296 - AIF2 ADC Filters */ + 0x0000, /* R1297 */ + 0x0000, /* R1298 */ + 0x0000, /* R1299 */ + 0x0000, /* R1300 */ + 0x0000, /* R1301 */ + 0x0000, /* R1302 */ + 0x0000, /* R1303 */ + 0x0000, /* R1304 */ + 0x0000, /* R1305 */ + 0x0000, /* R1306 */ + 0x0000, /* R1307 */ + 0x0000, /* R1308 */ + 0x0000, /* R1309 */ + 0x0000, /* R1310 */ + 0x0000, /* R1311 */ + 0x0200, /* R1312 - AIF2 DAC Filters (1) */ + 0x0010, /* R1313 - AIF2 DAC Filters (2) */ + 0x0000, /* R1314 */ + 0x0000, /* R1315 */ + 0x0000, /* R1316 */ + 0x0000, /* R1317 */ + 0x0000, /* R1318 */ + 0x0000, /* R1319 */ + 0x0000, /* R1320 */ + 0x0000, /* R1321 */ + 0x0000, /* R1322 */ + 0x0000, /* R1323 */ + 0x0000, /* R1324 */ + 0x0000, /* R1325 */ + 0x0000, /* R1326 */ + 0x0000, /* R1327 */ + 0x0000, /* R1328 */ + 0x0000, /* R1329 */ + 0x0000, /* R1330 */ + 0x0000, /* R1331 */ + 0x0000, /* R1332 */ + 0x0000, /* R1333 */ + 0x0000, /* R1334 */ + 0x0000, /* R1335 */ + 0x0000, /* R1336 */ + 0x0000, /* R1337 */ + 0x0000, /* R1338 */ + 0x0000, /* R1339 */ + 0x0000, /* R1340 */ + 0x0000, /* R1341 */ + 0x0000, /* R1342 */ + 0x0000, /* R1343 */ + 0x0098, /* R1344 - AIF2 DRC (1) */ + 0x0845, /* R1345 - AIF2 DRC (2) */ + 0x0000, /* R1346 - AIF2 DRC (3) */ + 0x0000, /* R1347 - AIF2 DRC (4) */ + 0x0000, /* R1348 - AIF2 DRC (5) */ + 0x0000, /* R1349 */ + 0x0000, /* R1350 */ + 0x0000, /* R1351 */ + 0x0000, /* R1352 */ + 0x0000, /* R1353 */ + 0x0000, /* R1354 */ + 0x0000, /* R1355 */ + 0x0000, /* R1356 */ + 0x0000, /* R1357 */ + 0x0000, /* R1358 */ + 0x0000, /* R1359 */ + 0x0000, /* R1360 */ + 0x0000, /* R1361 */ + 0x0000, /* R1362 */ + 0x0000, /* R1363 */ + 0x0000, /* R1364 */ + 0x0000, /* R1365 */ + 0x0000, /* R1366 */ + 0x0000, /* R1367 */ + 0x0000, /* R1368 */ + 0x0000, /* R1369 */ + 0x0000, /* R1370 */ + 0x0000, /* R1371 */ + 0x0000, /* R1372 */ + 0x0000, /* R1373 */ + 0x0000, /* R1374 */ + 0x0000, /* R1375 */ + 0x0000, /* R1376 */ + 0x0000, /* R1377 */ + 0x0000, /* R1378 */ + 0x0000, /* R1379 */ + 0x0000, /* R1380 */ + 0x0000, /* R1381 */ + 0x0000, /* R1382 */ + 0x0000, /* R1383 */ + 0x0000, /* R1384 */ + 0x0000, /* R1385 */ + 0x0000, /* R1386 */ + 0x0000, /* R1387 */ + 0x0000, /* R1388 */ + 0x0000, /* R1389 */ + 0x0000, /* R1390 */ + 0x0000, /* R1391 */ + 0x0000, /* R1392 */ + 0x0000, /* R1393 */ + 0x0000, /* R1394 */ + 0x0000, /* R1395 */ + 0x0000, /* R1396 */ + 0x0000, /* R1397 */ + 0x0000, /* R1398 */ + 0x0000, /* R1399 */ + 0x0000, /* R1400 */ + 0x0000, /* R1401 */ + 0x0000, /* R1402 */ + 0x0000, /* R1403 */ + 0x0000, /* R1404 */ + 0x0000, /* R1405 */ + 0x0000, /* R1406 */ + 0x0000, /* R1407 */ + 0x6318, /* R1408 - AIF2 EQ Gains (1) */ + 0x6300, /* R1409 - AIF2 EQ Gains (2) */ + 0x0FCA, /* R1410 - AIF2 EQ Band 1 A */ + 0x0400, /* R1411 - AIF2 EQ Band 1 B */ + 0x00D8, /* R1412 - AIF2 EQ Band 1 PG */ + 0x1EB5, /* R1413 - AIF2 EQ Band 2 A */ + 0xF145, /* R1414 - AIF2 EQ Band 2 B */ + 0x0B75, /* R1415 - AIF2 EQ Band 2 C */ + 0x01C5, /* R1416 - AIF2 EQ Band 2 PG */ + 0x1C58, /* R1417 - AIF2 EQ Band 3 A */ + 0xF373, /* R1418 - AIF2 EQ Band 3 B */ + 0x0A54, /* R1419 - AIF2 EQ Band 3 C */ + 0x0558, /* R1420 - AIF2 EQ Band 3 PG */ + 0x168E, /* R1421 - AIF2 EQ Band 4 A */ + 0xF829, /* R1422 - AIF2 EQ Band 4 B */ + 0x07AD, /* R1423 - AIF2 EQ Band 4 C */ + 0x1103, /* R1424 - AIF2 EQ Band 4 PG */ + 0x0564, /* R1425 - AIF2 EQ Band 5 A */ + 0x0559, /* R1426 - AIF2 EQ Band 5 B */ + 0x4000, /* R1427 - AIF2 EQ Band 5 PG */ + 0x0000, /* R1428 */ + 0x0000, /* R1429 */ + 0x0000, /* R1430 */ + 0x0000, /* R1431 */ + 0x0000, /* R1432 */ + 0x0000, /* R1433 */ + 0x0000, /* R1434 */ + 0x0000, /* R1435 */ + 0x0000, /* R1436 */ + 0x0000, /* R1437 */ + 0x0000, /* R1438 */ + 0x0000, /* R1439 */ + 0x0000, /* R1440 */ + 0x0000, /* R1441 */ + 0x0000, /* R1442 */ + 0x0000, /* R1443 */ + 0x0000, /* R1444 */ + 0x0000, /* R1445 */ + 0x0000, /* R1446 */ + 0x0000, /* R1447 */ + 0x0000, /* R1448 */ + 0x0000, /* R1449 */ + 0x0000, /* R1450 */ + 0x0000, /* R1451 */ + 0x0000, /* R1452 */ + 0x0000, /* R1453 */ + 0x0000, /* R1454 */ + 0x0000, /* R1455 */ + 0x0000, /* R1456 */ + 0x0000, /* R1457 */ + 0x0000, /* R1458 */ + 0x0000, /* R1459 */ + 0x0000, /* R1460 */ + 0x0000, /* R1461 */ + 0x0000, /* R1462 */ + 0x0000, /* R1463 */ + 0x0000, /* R1464 */ + 0x0000, /* R1465 */ + 0x0000, /* R1466 */ + 0x0000, /* R1467 */ + 0x0000, /* R1468 */ + 0x0000, /* R1469 */ + 0x0000, /* R1470 */ + 0x0000, /* R1471 */ + 0x0000, /* R1472 */ + 0x0000, /* R1473 */ + 0x0000, /* R1474 */ + 0x0000, /* R1475 */ + 0x0000, /* R1476 */ + 0x0000, /* R1477 */ + 0x0000, /* R1478 */ + 0x0000, /* R1479 */ + 0x0000, /* R1480 */ + 0x0000, /* R1481 */ + 0x0000, /* R1482 */ + 0x0000, /* R1483 */ + 0x0000, /* R1484 */ + 0x0000, /* R1485 */ + 0x0000, /* R1486 */ + 0x0000, /* R1487 */ + 0x0000, /* R1488 */ + 0x0000, /* R1489 */ + 0x0000, /* R1490 */ + 0x0000, /* R1491 */ + 0x0000, /* R1492 */ + 0x0000, /* R1493 */ + 0x0000, /* R1494 */ + 0x0000, /* R1495 */ + 0x0000, /* R1496 */ + 0x0000, /* R1497 */ + 0x0000, /* R1498 */ + 0x0000, /* R1499 */ + 0x0000, /* R1500 */ + 0x0000, /* R1501 */ + 0x0000, /* R1502 */ + 0x0000, /* R1503 */ + 0x0000, /* R1504 */ + 0x0000, /* R1505 */ + 0x0000, /* R1506 */ + 0x0000, /* R1507 */ + 0x0000, /* R1508 */ + 0x0000, /* R1509 */ + 0x0000, /* R1510 */ + 0x0000, /* R1511 */ + 0x0000, /* R1512 */ + 0x0000, /* R1513 */ + 0x0000, /* R1514 */ + 0x0000, /* R1515 */ + 0x0000, /* R1516 */ + 0x0000, /* R1517 */ + 0x0000, /* R1518 */ + 0x0000, /* R1519 */ + 0x0000, /* R1520 */ + 0x0000, /* R1521 */ + 0x0000, /* R1522 */ + 0x0000, /* R1523 */ + 0x0000, /* R1524 */ + 0x0000, /* R1525 */ + 0x0000, /* R1526 */ + 0x0000, /* R1527 */ + 0x0000, /* R1528 */ + 0x0000, /* R1529 */ + 0x0000, /* R1530 */ + 0x0000, /* R1531 */ + 0x0000, /* R1532 */ + 0x0000, /* R1533 */ + 0x0000, /* R1534 */ + 0x0000, /* R1535 */ + 0x0000, /* R1536 - DAC1 Mixer Volumes */ + 0x0000, /* R1537 - DAC1 Left Mixer Routing */ + 0x0000, /* R1538 - DAC1 Right Mixer Routing */ + 0x0000, /* R1539 - DAC2 Mixer Volumes */ + 0x0000, /* R1540 - DAC2 Left Mixer Routing */ + 0x0000, /* R1541 - DAC2 Right Mixer Routing */ + 0x0000, /* R1542 - AIF1 ADC1 Left Mixer Routing */ + 0x0000, /* R1543 - AIF1 ADC1 Right Mixer Routing */ + 0x0000, /* R1544 - AIF1 ADC2 Left Mixer Routing */ + 0x0000, /* R1545 - AIF1 ADC2 Right mixer Routing */ + 0x0000, /* R1546 */ + 0x0000, /* R1547 */ + 0x0000, /* R1548 */ + 0x0000, /* R1549 */ + 0x0000, /* R1550 */ + 0x0000, /* R1551 */ + 0x02C0, /* R1552 - DAC1 Left Volume */ + 0x02C0, /* R1553 - DAC1 Right Volume */ + 0x02C0, /* R1554 - DAC2 Left Volume */ + 0x02C0, /* R1555 - DAC2 Right Volume */ + 0x0000, /* R1556 - DAC Softmute */ + 0x0000, /* R1557 */ + 0x0000, /* R1558 */ + 0x0000, /* R1559 */ + 0x0000, /* R1560 */ + 0x0000, /* R1561 */ + 0x0000, /* R1562 */ + 0x0000, /* R1563 */ + 0x0000, /* R1564 */ + 0x0000, /* R1565 */ + 0x0000, /* R1566 */ + 0x0000, /* R1567 */ + 0x0002, /* R1568 - Oversampling */ + 0x0000, /* R1569 - Sidetone */ +}; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 4d3e6f1ac584..84e1bd1d2822 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -18,15 +18,17 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.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 <sound/soc-dapm.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> @@ -57,8 +59,6 @@ static int wm8994_retune_mobile_base[] = { WM8994_AIF2_EQ_GAINS_1, }; -#define WM8994_REG_CACHE_SIZE 0x621 - struct wm8994_micdet { struct snd_soc_jack *jack; int det; @@ -71,7 +71,6 @@ struct wm8994_priv { enum snd_soc_control_type control_type; void *control_data; struct snd_soc_codec *codec; - u16 reg_cache[WM8994_REG_CACHE_SIZE + 1]; int sysclk[2]; int sysclk_rate[2]; int mclk[2]; @@ -81,1600 +80,41 @@ struct wm8994_priv { int dac_rates[2]; int lrclk_shared[2]; - /* Platform dependant DRC configuration */ + int mbc_ena[3]; + + /* Platform dependent DRC configuration */ const char **drc_texts; int drc_cfg[WM8994_NUM_DRC]; struct soc_enum drc_enum; - /* Platform dependant ReTune mobile configuration */ + /* 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; -}; -static const struct { - unsigned short readable; /* Mask of readable bits */ - unsigned short writable; /* Mask of writable bits */ -} access_masks[] = { - { 0xFFFF, 0xFFFF }, /* R0 - Software Reset */ - { 0x3B37, 0x3B37 }, /* R1 - Power Management (1) */ - { 0x6BF0, 0x6BF0 }, /* R2 - Power Management (2) */ - { 0x3FF0, 0x3FF0 }, /* R3 - Power Management (3) */ - { 0x3F3F, 0x3F3F }, /* R4 - Power Management (4) */ - { 0x3F0F, 0x3F0F }, /* R5 - Power Management (5) */ - { 0x003F, 0x003F }, /* R6 - Power Management (6) */ - { 0x0000, 0x0000 }, /* R7 */ - { 0x0000, 0x0000 }, /* R8 */ - { 0x0000, 0x0000 }, /* R9 */ - { 0x0000, 0x0000 }, /* R10 */ - { 0x0000, 0x0000 }, /* R11 */ - { 0x0000, 0x0000 }, /* R12 */ - { 0x0000, 0x0000 }, /* R13 */ - { 0x0000, 0x0000 }, /* R14 */ - { 0x0000, 0x0000 }, /* R15 */ - { 0x0000, 0x0000 }, /* R16 */ - { 0x0000, 0x0000 }, /* R17 */ - { 0x0000, 0x0000 }, /* R18 */ - { 0x0000, 0x0000 }, /* R19 */ - { 0x0000, 0x0000 }, /* R20 */ - { 0x01C0, 0x01C0 }, /* R21 - Input Mixer (1) */ - { 0x0000, 0x0000 }, /* R22 */ - { 0x0000, 0x0000 }, /* R23 */ - { 0x00DF, 0x01DF }, /* R24 - Left Line Input 1&2 Volume */ - { 0x00DF, 0x01DF }, /* R25 - Left Line Input 3&4 Volume */ - { 0x00DF, 0x01DF }, /* R26 - Right Line Input 1&2 Volume */ - { 0x00DF, 0x01DF }, /* R27 - Right Line Input 3&4 Volume */ - { 0x00FF, 0x01FF }, /* R28 - Left Output Volume */ - { 0x00FF, 0x01FF }, /* R29 - Right Output Volume */ - { 0x0077, 0x0077 }, /* R30 - Line Outputs Volume */ - { 0x0030, 0x0030 }, /* R31 - HPOUT2 Volume */ - { 0x00FF, 0x01FF }, /* R32 - Left OPGA Volume */ - { 0x00FF, 0x01FF }, /* R33 - Right OPGA Volume */ - { 0x007F, 0x007F }, /* R34 - SPKMIXL Attenuation */ - { 0x017F, 0x017F }, /* R35 - SPKMIXR Attenuation */ - { 0x003F, 0x003F }, /* R36 - SPKOUT Mixers */ - { 0x003F, 0x003F }, /* R37 - ClassD */ - { 0x00FF, 0x01FF }, /* R38 - Speaker Volume Left */ - { 0x00FF, 0x01FF }, /* R39 - Speaker Volume Right */ - { 0x00FF, 0x00FF }, /* R40 - Input Mixer (2) */ - { 0x01B7, 0x01B7 }, /* R41 - Input Mixer (3) */ - { 0x01B7, 0x01B7 }, /* R42 - Input Mixer (4) */ - { 0x01C7, 0x01C7 }, /* R43 - Input Mixer (5) */ - { 0x01C7, 0x01C7 }, /* R44 - Input Mixer (6) */ - { 0x01FF, 0x01FF }, /* R45 - Output Mixer (1) */ - { 0x01FF, 0x01FF }, /* R46 - Output Mixer (2) */ - { 0x0FFF, 0x0FFF }, /* R47 - Output Mixer (3) */ - { 0x0FFF, 0x0FFF }, /* R48 - Output Mixer (4) */ - { 0x0FFF, 0x0FFF }, /* R49 - Output Mixer (5) */ - { 0x0FFF, 0x0FFF }, /* R50 - Output Mixer (6) */ - { 0x0038, 0x0038 }, /* R51 - HPOUT2 Mixer */ - { 0x0077, 0x0077 }, /* R52 - Line Mixer (1) */ - { 0x0077, 0x0077 }, /* R53 - Line Mixer (2) */ - { 0x03FF, 0x03FF }, /* R54 - Speaker Mixer */ - { 0x00C1, 0x00C1 }, /* R55 - Additional Control */ - { 0x00F0, 0x00F0 }, /* R56 - AntiPOP (1) */ - { 0x01EF, 0x01EF }, /* R57 - AntiPOP (2) */ - { 0x00FF, 0x00FF }, /* R58 - MICBIAS */ - { 0x000F, 0x000F }, /* R59 - LDO 1 */ - { 0x0007, 0x0007 }, /* R60 - LDO 2 */ - { 0x0000, 0x0000 }, /* R61 */ - { 0x0000, 0x0000 }, /* R62 */ - { 0x0000, 0x0000 }, /* R63 */ - { 0x0000, 0x0000 }, /* R64 */ - { 0x0000, 0x0000 }, /* R65 */ - { 0x0000, 0x0000 }, /* R66 */ - { 0x0000, 0x0000 }, /* R67 */ - { 0x0000, 0x0000 }, /* R68 */ - { 0x0000, 0x0000 }, /* R69 */ - { 0x0000, 0x0000 }, /* R70 */ - { 0x0000, 0x0000 }, /* R71 */ - { 0x0000, 0x0000 }, /* R72 */ - { 0x0000, 0x0000 }, /* R73 */ - { 0x0000, 0x0000 }, /* R74 */ - { 0x0000, 0x0000 }, /* R75 */ - { 0x8000, 0x8000 }, /* R76 - Charge Pump (1) */ - { 0x0000, 0x0000 }, /* R77 */ - { 0x0000, 0x0000 }, /* R78 */ - { 0x0000, 0x0000 }, /* R79 */ - { 0x0000, 0x0000 }, /* R80 */ - { 0x0301, 0x0301 }, /* R81 - Class W (1) */ - { 0x0000, 0x0000 }, /* R82 */ - { 0x0000, 0x0000 }, /* R83 */ - { 0x333F, 0x333F }, /* R84 - DC Servo (1) */ - { 0x0FEF, 0x0FEF }, /* R85 - DC Servo (2) */ - { 0x0000, 0x0000 }, /* R86 */ - { 0xFFFF, 0xFFFF }, /* R87 - DC Servo (4) */ - { 0x0333, 0x0000 }, /* R88 - DC Servo Readback */ - { 0x0000, 0x0000 }, /* R89 */ - { 0x0000, 0x0000 }, /* R90 */ - { 0x0000, 0x0000 }, /* R91 */ - { 0x0000, 0x0000 }, /* R92 */ - { 0x0000, 0x0000 }, /* R93 */ - { 0x0000, 0x0000 }, /* R94 */ - { 0x0000, 0x0000 }, /* R95 */ - { 0x00EE, 0x00EE }, /* R96 - Analogue HP (1) */ - { 0x0000, 0x0000 }, /* R97 */ - { 0x0000, 0x0000 }, /* R98 */ - { 0x0000, 0x0000 }, /* R99 */ - { 0x0000, 0x0000 }, /* R100 */ - { 0x0000, 0x0000 }, /* R101 */ - { 0x0000, 0x0000 }, /* R102 */ - { 0x0000, 0x0000 }, /* R103 */ - { 0x0000, 0x0000 }, /* R104 */ - { 0x0000, 0x0000 }, /* R105 */ - { 0x0000, 0x0000 }, /* R106 */ - { 0x0000, 0x0000 }, /* R107 */ - { 0x0000, 0x0000 }, /* R108 */ - { 0x0000, 0x0000 }, /* R109 */ - { 0x0000, 0x0000 }, /* R110 */ - { 0x0000, 0x0000 }, /* R111 */ - { 0x0000, 0x0000 }, /* R112 */ - { 0x0000, 0x0000 }, /* R113 */ - { 0x0000, 0x0000 }, /* R114 */ - { 0x0000, 0x0000 }, /* R115 */ - { 0x0000, 0x0000 }, /* R116 */ - { 0x0000, 0x0000 }, /* R117 */ - { 0x0000, 0x0000 }, /* R118 */ - { 0x0000, 0x0000 }, /* R119 */ - { 0x0000, 0x0000 }, /* R120 */ - { 0x0000, 0x0000 }, /* R121 */ - { 0x0000, 0x0000 }, /* R122 */ - { 0x0000, 0x0000 }, /* R123 */ - { 0x0000, 0x0000 }, /* R124 */ - { 0x0000, 0x0000 }, /* R125 */ - { 0x0000, 0x0000 }, /* R126 */ - { 0x0000, 0x0000 }, /* R127 */ - { 0x0000, 0x0000 }, /* R128 */ - { 0x0000, 0x0000 }, /* R129 */ - { 0x0000, 0x0000 }, /* R130 */ - { 0x0000, 0x0000 }, /* R131 */ - { 0x0000, 0x0000 }, /* R132 */ - { 0x0000, 0x0000 }, /* R133 */ - { 0x0000, 0x0000 }, /* R134 */ - { 0x0000, 0x0000 }, /* R135 */ - { 0x0000, 0x0000 }, /* R136 */ - { 0x0000, 0x0000 }, /* R137 */ - { 0x0000, 0x0000 }, /* R138 */ - { 0x0000, 0x0000 }, /* R139 */ - { 0x0000, 0x0000 }, /* R140 */ - { 0x0000, 0x0000 }, /* R141 */ - { 0x0000, 0x0000 }, /* R142 */ - { 0x0000, 0x0000 }, /* R143 */ - { 0x0000, 0x0000 }, /* R144 */ - { 0x0000, 0x0000 }, /* R145 */ - { 0x0000, 0x0000 }, /* R146 */ - { 0x0000, 0x0000 }, /* R147 */ - { 0x0000, 0x0000 }, /* R148 */ - { 0x0000, 0x0000 }, /* R149 */ - { 0x0000, 0x0000 }, /* R150 */ - { 0x0000, 0x0000 }, /* R151 */ - { 0x0000, 0x0000 }, /* R152 */ - { 0x0000, 0x0000 }, /* R153 */ - { 0x0000, 0x0000 }, /* R154 */ - { 0x0000, 0x0000 }, /* R155 */ - { 0x0000, 0x0000 }, /* R156 */ - { 0x0000, 0x0000 }, /* R157 */ - { 0x0000, 0x0000 }, /* R158 */ - { 0x0000, 0x0000 }, /* R159 */ - { 0x0000, 0x0000 }, /* R160 */ - { 0x0000, 0x0000 }, /* R161 */ - { 0x0000, 0x0000 }, /* R162 */ - { 0x0000, 0x0000 }, /* R163 */ - { 0x0000, 0x0000 }, /* R164 */ - { 0x0000, 0x0000 }, /* R165 */ - { 0x0000, 0x0000 }, /* R166 */ - { 0x0000, 0x0000 }, /* R167 */ - { 0x0000, 0x0000 }, /* R168 */ - { 0x0000, 0x0000 }, /* R169 */ - { 0x0000, 0x0000 }, /* R170 */ - { 0x0000, 0x0000 }, /* R171 */ - { 0x0000, 0x0000 }, /* R172 */ - { 0x0000, 0x0000 }, /* R173 */ - { 0x0000, 0x0000 }, /* R174 */ - { 0x0000, 0x0000 }, /* R175 */ - { 0x0000, 0x0000 }, /* R176 */ - { 0x0000, 0x0000 }, /* R177 */ - { 0x0000, 0x0000 }, /* R178 */ - { 0x0000, 0x0000 }, /* R179 */ - { 0x0000, 0x0000 }, /* R180 */ - { 0x0000, 0x0000 }, /* R181 */ - { 0x0000, 0x0000 }, /* R182 */ - { 0x0000, 0x0000 }, /* R183 */ - { 0x0000, 0x0000 }, /* R184 */ - { 0x0000, 0x0000 }, /* R185 */ - { 0x0000, 0x0000 }, /* R186 */ - { 0x0000, 0x0000 }, /* R187 */ - { 0x0000, 0x0000 }, /* R188 */ - { 0x0000, 0x0000 }, /* R189 */ - { 0x0000, 0x0000 }, /* R190 */ - { 0x0000, 0x0000 }, /* R191 */ - { 0x0000, 0x0000 }, /* R192 */ - { 0x0000, 0x0000 }, /* R193 */ - { 0x0000, 0x0000 }, /* R194 */ - { 0x0000, 0x0000 }, /* R195 */ - { 0x0000, 0x0000 }, /* R196 */ - { 0x0000, 0x0000 }, /* R197 */ - { 0x0000, 0x0000 }, /* R198 */ - { 0x0000, 0x0000 }, /* R199 */ - { 0x0000, 0x0000 }, /* R200 */ - { 0x0000, 0x0000 }, /* R201 */ - { 0x0000, 0x0000 }, /* R202 */ - { 0x0000, 0x0000 }, /* R203 */ - { 0x0000, 0x0000 }, /* R204 */ - { 0x0000, 0x0000 }, /* R205 */ - { 0x0000, 0x0000 }, /* R206 */ - { 0x0000, 0x0000 }, /* R207 */ - { 0x0000, 0x0000 }, /* R208 */ - { 0x0000, 0x0000 }, /* R209 */ - { 0x0000, 0x0000 }, /* R210 */ - { 0x0000, 0x0000 }, /* R211 */ - { 0x0000, 0x0000 }, /* R212 */ - { 0x0000, 0x0000 }, /* R213 */ - { 0x0000, 0x0000 }, /* R214 */ - { 0x0000, 0x0000 }, /* R215 */ - { 0x0000, 0x0000 }, /* R216 */ - { 0x0000, 0x0000 }, /* R217 */ - { 0x0000, 0x0000 }, /* R218 */ - { 0x0000, 0x0000 }, /* R219 */ - { 0x0000, 0x0000 }, /* R220 */ - { 0x0000, 0x0000 }, /* R221 */ - { 0x0000, 0x0000 }, /* R222 */ - { 0x0000, 0x0000 }, /* R223 */ - { 0x0000, 0x0000 }, /* R224 */ - { 0x0000, 0x0000 }, /* R225 */ - { 0x0000, 0x0000 }, /* R226 */ - { 0x0000, 0x0000 }, /* R227 */ - { 0x0000, 0x0000 }, /* R228 */ - { 0x0000, 0x0000 }, /* R229 */ - { 0x0000, 0x0000 }, /* R230 */ - { 0x0000, 0x0000 }, /* R231 */ - { 0x0000, 0x0000 }, /* R232 */ - { 0x0000, 0x0000 }, /* R233 */ - { 0x0000, 0x0000 }, /* R234 */ - { 0x0000, 0x0000 }, /* R235 */ - { 0x0000, 0x0000 }, /* R236 */ - { 0x0000, 0x0000 }, /* R237 */ - { 0x0000, 0x0000 }, /* R238 */ - { 0x0000, 0x0000 }, /* R239 */ - { 0x0000, 0x0000 }, /* R240 */ - { 0x0000, 0x0000 }, /* R241 */ - { 0x0000, 0x0000 }, /* R242 */ - { 0x0000, 0x0000 }, /* R243 */ - { 0x0000, 0x0000 }, /* R244 */ - { 0x0000, 0x0000 }, /* R245 */ - { 0x0000, 0x0000 }, /* R246 */ - { 0x0000, 0x0000 }, /* R247 */ - { 0x0000, 0x0000 }, /* R248 */ - { 0x0000, 0x0000 }, /* R249 */ - { 0x0000, 0x0000 }, /* R250 */ - { 0x0000, 0x0000 }, /* R251 */ - { 0x0000, 0x0000 }, /* R252 */ - { 0x0000, 0x0000 }, /* R253 */ - { 0x0000, 0x0000 }, /* R254 */ - { 0x0000, 0x0000 }, /* R255 */ - { 0x000F, 0x0000 }, /* R256 - Chip Revision */ - { 0x0074, 0x0074 }, /* R257 - Control Interface */ - { 0x0000, 0x0000 }, /* R258 */ - { 0x0000, 0x0000 }, /* R259 */ - { 0x0000, 0x0000 }, /* R260 */ - { 0x0000, 0x0000 }, /* R261 */ - { 0x0000, 0x0000 }, /* R262 */ - { 0x0000, 0x0000 }, /* R263 */ - { 0x0000, 0x0000 }, /* R264 */ - { 0x0000, 0x0000 }, /* R265 */ - { 0x0000, 0x0000 }, /* R266 */ - { 0x0000, 0x0000 }, /* R267 */ - { 0x0000, 0x0000 }, /* R268 */ - { 0x0000, 0x0000 }, /* R269 */ - { 0x0000, 0x0000 }, /* R270 */ - { 0x0000, 0x0000 }, /* R271 */ - { 0x807F, 0x837F }, /* R272 - Write Sequencer Ctrl (1) */ - { 0x017F, 0x0000 }, /* R273 - Write Sequencer Ctrl (2) */ - { 0x0000, 0x0000 }, /* R274 */ - { 0x0000, 0x0000 }, /* R275 */ - { 0x0000, 0x0000 }, /* R276 */ - { 0x0000, 0x0000 }, /* R277 */ - { 0x0000, 0x0000 }, /* R278 */ - { 0x0000, 0x0000 }, /* R279 */ - { 0x0000, 0x0000 }, /* R280 */ - { 0x0000, 0x0000 }, /* R281 */ - { 0x0000, 0x0000 }, /* R282 */ - { 0x0000, 0x0000 }, /* R283 */ - { 0x0000, 0x0000 }, /* R284 */ - { 0x0000, 0x0000 }, /* R285 */ - { 0x0000, 0x0000 }, /* R286 */ - { 0x0000, 0x0000 }, /* R287 */ - { 0x0000, 0x0000 }, /* R288 */ - { 0x0000, 0x0000 }, /* R289 */ - { 0x0000, 0x0000 }, /* R290 */ - { 0x0000, 0x0000 }, /* R291 */ - { 0x0000, 0x0000 }, /* R292 */ - { 0x0000, 0x0000 }, /* R293 */ - { 0x0000, 0x0000 }, /* R294 */ - { 0x0000, 0x0000 }, /* R295 */ - { 0x0000, 0x0000 }, /* R296 */ - { 0x0000, 0x0000 }, /* R297 */ - { 0x0000, 0x0000 }, /* R298 */ - { 0x0000, 0x0000 }, /* R299 */ - { 0x0000, 0x0000 }, /* R300 */ - { 0x0000, 0x0000 }, /* R301 */ - { 0x0000, 0x0000 }, /* R302 */ - { 0x0000, 0x0000 }, /* R303 */ - { 0x0000, 0x0000 }, /* R304 */ - { 0x0000, 0x0000 }, /* R305 */ - { 0x0000, 0x0000 }, /* R306 */ - { 0x0000, 0x0000 }, /* R307 */ - { 0x0000, 0x0000 }, /* R308 */ - { 0x0000, 0x0000 }, /* R309 */ - { 0x0000, 0x0000 }, /* R310 */ - { 0x0000, 0x0000 }, /* R311 */ - { 0x0000, 0x0000 }, /* R312 */ - { 0x0000, 0x0000 }, /* R313 */ - { 0x0000, 0x0000 }, /* R314 */ - { 0x0000, 0x0000 }, /* R315 */ - { 0x0000, 0x0000 }, /* R316 */ - { 0x0000, 0x0000 }, /* R317 */ - { 0x0000, 0x0000 }, /* R318 */ - { 0x0000, 0x0000 }, /* R319 */ - { 0x0000, 0x0000 }, /* R320 */ - { 0x0000, 0x0000 }, /* R321 */ - { 0x0000, 0x0000 }, /* R322 */ - { 0x0000, 0x0000 }, /* R323 */ - { 0x0000, 0x0000 }, /* R324 */ - { 0x0000, 0x0000 }, /* R325 */ - { 0x0000, 0x0000 }, /* R326 */ - { 0x0000, 0x0000 }, /* R327 */ - { 0x0000, 0x0000 }, /* R328 */ - { 0x0000, 0x0000 }, /* R329 */ - { 0x0000, 0x0000 }, /* R330 */ - { 0x0000, 0x0000 }, /* R331 */ - { 0x0000, 0x0000 }, /* R332 */ - { 0x0000, 0x0000 }, /* R333 */ - { 0x0000, 0x0000 }, /* R334 */ - { 0x0000, 0x0000 }, /* R335 */ - { 0x0000, 0x0000 }, /* R336 */ - { 0x0000, 0x0000 }, /* R337 */ - { 0x0000, 0x0000 }, /* R338 */ - { 0x0000, 0x0000 }, /* R339 */ - { 0x0000, 0x0000 }, /* R340 */ - { 0x0000, 0x0000 }, /* R341 */ - { 0x0000, 0x0000 }, /* R342 */ - { 0x0000, 0x0000 }, /* R343 */ - { 0x0000, 0x0000 }, /* R344 */ - { 0x0000, 0x0000 }, /* R345 */ - { 0x0000, 0x0000 }, /* R346 */ - { 0x0000, 0x0000 }, /* R347 */ - { 0x0000, 0x0000 }, /* R348 */ - { 0x0000, 0x0000 }, /* R349 */ - { 0x0000, 0x0000 }, /* R350 */ - { 0x0000, 0x0000 }, /* R351 */ - { 0x0000, 0x0000 }, /* R352 */ - { 0x0000, 0x0000 }, /* R353 */ - { 0x0000, 0x0000 }, /* R354 */ - { 0x0000, 0x0000 }, /* R355 */ - { 0x0000, 0x0000 }, /* R356 */ - { 0x0000, 0x0000 }, /* R357 */ - { 0x0000, 0x0000 }, /* R358 */ - { 0x0000, 0x0000 }, /* R359 */ - { 0x0000, 0x0000 }, /* R360 */ - { 0x0000, 0x0000 }, /* R361 */ - { 0x0000, 0x0000 }, /* R362 */ - { 0x0000, 0x0000 }, /* R363 */ - { 0x0000, 0x0000 }, /* R364 */ - { 0x0000, 0x0000 }, /* R365 */ - { 0x0000, 0x0000 }, /* R366 */ - { 0x0000, 0x0000 }, /* R367 */ - { 0x0000, 0x0000 }, /* R368 */ - { 0x0000, 0x0000 }, /* R369 */ - { 0x0000, 0x0000 }, /* R370 */ - { 0x0000, 0x0000 }, /* R371 */ - { 0x0000, 0x0000 }, /* R372 */ - { 0x0000, 0x0000 }, /* R373 */ - { 0x0000, 0x0000 }, /* R374 */ - { 0x0000, 0x0000 }, /* R375 */ - { 0x0000, 0x0000 }, /* R376 */ - { 0x0000, 0x0000 }, /* R377 */ - { 0x0000, 0x0000 }, /* R378 */ - { 0x0000, 0x0000 }, /* R379 */ - { 0x0000, 0x0000 }, /* R380 */ - { 0x0000, 0x0000 }, /* R381 */ - { 0x0000, 0x0000 }, /* R382 */ - { 0x0000, 0x0000 }, /* R383 */ - { 0x0000, 0x0000 }, /* R384 */ - { 0x0000, 0x0000 }, /* R385 */ - { 0x0000, 0x0000 }, /* R386 */ - { 0x0000, 0x0000 }, /* R387 */ - { 0x0000, 0x0000 }, /* R388 */ - { 0x0000, 0x0000 }, /* R389 */ - { 0x0000, 0x0000 }, /* R390 */ - { 0x0000, 0x0000 }, /* R391 */ - { 0x0000, 0x0000 }, /* R392 */ - { 0x0000, 0x0000 }, /* R393 */ - { 0x0000, 0x0000 }, /* R394 */ - { 0x0000, 0x0000 }, /* R395 */ - { 0x0000, 0x0000 }, /* R396 */ - { 0x0000, 0x0000 }, /* R397 */ - { 0x0000, 0x0000 }, /* R398 */ - { 0x0000, 0x0000 }, /* R399 */ - { 0x0000, 0x0000 }, /* R400 */ - { 0x0000, 0x0000 }, /* R401 */ - { 0x0000, 0x0000 }, /* R402 */ - { 0x0000, 0x0000 }, /* R403 */ - { 0x0000, 0x0000 }, /* R404 */ - { 0x0000, 0x0000 }, /* R405 */ - { 0x0000, 0x0000 }, /* R406 */ - { 0x0000, 0x0000 }, /* R407 */ - { 0x0000, 0x0000 }, /* R408 */ - { 0x0000, 0x0000 }, /* R409 */ - { 0x0000, 0x0000 }, /* R410 */ - { 0x0000, 0x0000 }, /* R411 */ - { 0x0000, 0x0000 }, /* R412 */ - { 0x0000, 0x0000 }, /* R413 */ - { 0x0000, 0x0000 }, /* R414 */ - { 0x0000, 0x0000 }, /* R415 */ - { 0x0000, 0x0000 }, /* R416 */ - { 0x0000, 0x0000 }, /* R417 */ - { 0x0000, 0x0000 }, /* R418 */ - { 0x0000, 0x0000 }, /* R419 */ - { 0x0000, 0x0000 }, /* R420 */ - { 0x0000, 0x0000 }, /* R421 */ - { 0x0000, 0x0000 }, /* R422 */ - { 0x0000, 0x0000 }, /* R423 */ - { 0x0000, 0x0000 }, /* R424 */ - { 0x0000, 0x0000 }, /* R425 */ - { 0x0000, 0x0000 }, /* R426 */ - { 0x0000, 0x0000 }, /* R427 */ - { 0x0000, 0x0000 }, /* R428 */ - { 0x0000, 0x0000 }, /* R429 */ - { 0x0000, 0x0000 }, /* R430 */ - { 0x0000, 0x0000 }, /* R431 */ - { 0x0000, 0x0000 }, /* R432 */ - { 0x0000, 0x0000 }, /* R433 */ - { 0x0000, 0x0000 }, /* R434 */ - { 0x0000, 0x0000 }, /* R435 */ - { 0x0000, 0x0000 }, /* R436 */ - { 0x0000, 0x0000 }, /* R437 */ - { 0x0000, 0x0000 }, /* R438 */ - { 0x0000, 0x0000 }, /* R439 */ - { 0x0000, 0x0000 }, /* R440 */ - { 0x0000, 0x0000 }, /* R441 */ - { 0x0000, 0x0000 }, /* R442 */ - { 0x0000, 0x0000 }, /* R443 */ - { 0x0000, 0x0000 }, /* R444 */ - { 0x0000, 0x0000 }, /* R445 */ - { 0x0000, 0x0000 }, /* R446 */ - { 0x0000, 0x0000 }, /* R447 */ - { 0x0000, 0x0000 }, /* R448 */ - { 0x0000, 0x0000 }, /* R449 */ - { 0x0000, 0x0000 }, /* R450 */ - { 0x0000, 0x0000 }, /* R451 */ - { 0x0000, 0x0000 }, /* R452 */ - { 0x0000, 0x0000 }, /* R453 */ - { 0x0000, 0x0000 }, /* R454 */ - { 0x0000, 0x0000 }, /* R455 */ - { 0x0000, 0x0000 }, /* R456 */ - { 0x0000, 0x0000 }, /* R457 */ - { 0x0000, 0x0000 }, /* R458 */ - { 0x0000, 0x0000 }, /* R459 */ - { 0x0000, 0x0000 }, /* R460 */ - { 0x0000, 0x0000 }, /* R461 */ - { 0x0000, 0x0000 }, /* R462 */ - { 0x0000, 0x0000 }, /* R463 */ - { 0x0000, 0x0000 }, /* R464 */ - { 0x0000, 0x0000 }, /* R465 */ - { 0x0000, 0x0000 }, /* R466 */ - { 0x0000, 0x0000 }, /* R467 */ - { 0x0000, 0x0000 }, /* R468 */ - { 0x0000, 0x0000 }, /* R469 */ - { 0x0000, 0x0000 }, /* R470 */ - { 0x0000, 0x0000 }, /* R471 */ - { 0x0000, 0x0000 }, /* R472 */ - { 0x0000, 0x0000 }, /* R473 */ - { 0x0000, 0x0000 }, /* R474 */ - { 0x0000, 0x0000 }, /* R475 */ - { 0x0000, 0x0000 }, /* R476 */ - { 0x0000, 0x0000 }, /* R477 */ - { 0x0000, 0x0000 }, /* R478 */ - { 0x0000, 0x0000 }, /* R479 */ - { 0x0000, 0x0000 }, /* R480 */ - { 0x0000, 0x0000 }, /* R481 */ - { 0x0000, 0x0000 }, /* R482 */ - { 0x0000, 0x0000 }, /* R483 */ - { 0x0000, 0x0000 }, /* R484 */ - { 0x0000, 0x0000 }, /* R485 */ - { 0x0000, 0x0000 }, /* R486 */ - { 0x0000, 0x0000 }, /* R487 */ - { 0x0000, 0x0000 }, /* R488 */ - { 0x0000, 0x0000 }, /* R489 */ - { 0x0000, 0x0000 }, /* R490 */ - { 0x0000, 0x0000 }, /* R491 */ - { 0x0000, 0x0000 }, /* R492 */ - { 0x0000, 0x0000 }, /* R493 */ - { 0x0000, 0x0000 }, /* R494 */ - { 0x0000, 0x0000 }, /* R495 */ - { 0x0000, 0x0000 }, /* R496 */ - { 0x0000, 0x0000 }, /* R497 */ - { 0x0000, 0x0000 }, /* R498 */ - { 0x0000, 0x0000 }, /* R499 */ - { 0x0000, 0x0000 }, /* R500 */ - { 0x0000, 0x0000 }, /* R501 */ - { 0x0000, 0x0000 }, /* R502 */ - { 0x0000, 0x0000 }, /* R503 */ - { 0x0000, 0x0000 }, /* R504 */ - { 0x0000, 0x0000 }, /* R505 */ - { 0x0000, 0x0000 }, /* R506 */ - { 0x0000, 0x0000 }, /* R507 */ - { 0x0000, 0x0000 }, /* R508 */ - { 0x0000, 0x0000 }, /* R509 */ - { 0x0000, 0x0000 }, /* R510 */ - { 0x0000, 0x0000 }, /* R511 */ - { 0x001F, 0x001F }, /* R512 - AIF1 Clocking (1) */ - { 0x003F, 0x003F }, /* R513 - AIF1 Clocking (2) */ - { 0x0000, 0x0000 }, /* R514 */ - { 0x0000, 0x0000 }, /* R515 */ - { 0x001F, 0x001F }, /* R516 - AIF2 Clocking (1) */ - { 0x003F, 0x003F }, /* R517 - AIF2 Clocking (2) */ - { 0x0000, 0x0000 }, /* R518 */ - { 0x0000, 0x0000 }, /* R519 */ - { 0x001F, 0x001F }, /* R520 - Clocking (1) */ - { 0x0777, 0x0777 }, /* R521 - Clocking (2) */ - { 0x0000, 0x0000 }, /* R522 */ - { 0x0000, 0x0000 }, /* R523 */ - { 0x0000, 0x0000 }, /* R524 */ - { 0x0000, 0x0000 }, /* R525 */ - { 0x0000, 0x0000 }, /* R526 */ - { 0x0000, 0x0000 }, /* R527 */ - { 0x00FF, 0x00FF }, /* R528 - AIF1 Rate */ - { 0x00FF, 0x00FF }, /* R529 - AIF2 Rate */ - { 0x000F, 0x0000 }, /* R530 - Rate Status */ - { 0x0000, 0x0000 }, /* R531 */ - { 0x0000, 0x0000 }, /* R532 */ - { 0x0000, 0x0000 }, /* R533 */ - { 0x0000, 0x0000 }, /* R534 */ - { 0x0000, 0x0000 }, /* R535 */ - { 0x0000, 0x0000 }, /* R536 */ - { 0x0000, 0x0000 }, /* R537 */ - { 0x0000, 0x0000 }, /* R538 */ - { 0x0000, 0x0000 }, /* R539 */ - { 0x0000, 0x0000 }, /* R540 */ - { 0x0000, 0x0000 }, /* R541 */ - { 0x0000, 0x0000 }, /* R542 */ - { 0x0000, 0x0000 }, /* R543 */ - { 0x0007, 0x0007 }, /* R544 - FLL1 Control (1) */ - { 0x3F77, 0x3F77 }, /* R545 - FLL1 Control (2) */ - { 0xFFFF, 0xFFFF }, /* R546 - FLL1 Control (3) */ - { 0x7FEF, 0x7FEF }, /* R547 - FLL1 Control (4) */ - { 0x1FDB, 0x1FDB }, /* R548 - FLL1 Control (5) */ - { 0x0000, 0x0000 }, /* R549 */ - { 0x0000, 0x0000 }, /* R550 */ - { 0x0000, 0x0000 }, /* R551 */ - { 0x0000, 0x0000 }, /* R552 */ - { 0x0000, 0x0000 }, /* R553 */ - { 0x0000, 0x0000 }, /* R554 */ - { 0x0000, 0x0000 }, /* R555 */ - { 0x0000, 0x0000 }, /* R556 */ - { 0x0000, 0x0000 }, /* R557 */ - { 0x0000, 0x0000 }, /* R558 */ - { 0x0000, 0x0000 }, /* R559 */ - { 0x0000, 0x0000 }, /* R560 */ - { 0x0000, 0x0000 }, /* R561 */ - { 0x0000, 0x0000 }, /* R562 */ - { 0x0000, 0x0000 }, /* R563 */ - { 0x0000, 0x0000 }, /* R564 */ - { 0x0000, 0x0000 }, /* R565 */ - { 0x0000, 0x0000 }, /* R566 */ - { 0x0000, 0x0000 }, /* R567 */ - { 0x0000, 0x0000 }, /* R568 */ - { 0x0000, 0x0000 }, /* R569 */ - { 0x0000, 0x0000 }, /* R570 */ - { 0x0000, 0x0000 }, /* R571 */ - { 0x0000, 0x0000 }, /* R572 */ - { 0x0000, 0x0000 }, /* R573 */ - { 0x0000, 0x0000 }, /* R574 */ - { 0x0000, 0x0000 }, /* R575 */ - { 0x0007, 0x0007 }, /* R576 - FLL2 Control (1) */ - { 0x3F77, 0x3F77 }, /* R577 - FLL2 Control (2) */ - { 0xFFFF, 0xFFFF }, /* R578 - FLL2 Control (3) */ - { 0x7FEF, 0x7FEF }, /* R579 - FLL2 Control (4) */ - { 0x1FDB, 0x1FDB }, /* R580 - FLL2 Control (5) */ - { 0x0000, 0x0000 }, /* R581 */ - { 0x0000, 0x0000 }, /* R582 */ - { 0x0000, 0x0000 }, /* R583 */ - { 0x0000, 0x0000 }, /* R584 */ - { 0x0000, 0x0000 }, /* R585 */ - { 0x0000, 0x0000 }, /* R586 */ - { 0x0000, 0x0000 }, /* R587 */ - { 0x0000, 0x0000 }, /* R588 */ - { 0x0000, 0x0000 }, /* R589 */ - { 0x0000, 0x0000 }, /* R590 */ - { 0x0000, 0x0000 }, /* R591 */ - { 0x0000, 0x0000 }, /* R592 */ - { 0x0000, 0x0000 }, /* R593 */ - { 0x0000, 0x0000 }, /* R594 */ - { 0x0000, 0x0000 }, /* R595 */ - { 0x0000, 0x0000 }, /* R596 */ - { 0x0000, 0x0000 }, /* R597 */ - { 0x0000, 0x0000 }, /* R598 */ - { 0x0000, 0x0000 }, /* R599 */ - { 0x0000, 0x0000 }, /* R600 */ - { 0x0000, 0x0000 }, /* R601 */ - { 0x0000, 0x0000 }, /* R602 */ - { 0x0000, 0x0000 }, /* R603 */ - { 0x0000, 0x0000 }, /* R604 */ - { 0x0000, 0x0000 }, /* R605 */ - { 0x0000, 0x0000 }, /* R606 */ - { 0x0000, 0x0000 }, /* R607 */ - { 0x0000, 0x0000 }, /* R608 */ - { 0x0000, 0x0000 }, /* R609 */ - { 0x0000, 0x0000 }, /* R610 */ - { 0x0000, 0x0000 }, /* R611 */ - { 0x0000, 0x0000 }, /* R612 */ - { 0x0000, 0x0000 }, /* R613 */ - { 0x0000, 0x0000 }, /* R614 */ - { 0x0000, 0x0000 }, /* R615 */ - { 0x0000, 0x0000 }, /* R616 */ - { 0x0000, 0x0000 }, /* R617 */ - { 0x0000, 0x0000 }, /* R618 */ - { 0x0000, 0x0000 }, /* R619 */ - { 0x0000, 0x0000 }, /* R620 */ - { 0x0000, 0x0000 }, /* R621 */ - { 0x0000, 0x0000 }, /* R622 */ - { 0x0000, 0x0000 }, /* R623 */ - { 0x0000, 0x0000 }, /* R624 */ - { 0x0000, 0x0000 }, /* R625 */ - { 0x0000, 0x0000 }, /* R626 */ - { 0x0000, 0x0000 }, /* R627 */ - { 0x0000, 0x0000 }, /* R628 */ - { 0x0000, 0x0000 }, /* R629 */ - { 0x0000, 0x0000 }, /* R630 */ - { 0x0000, 0x0000 }, /* R631 */ - { 0x0000, 0x0000 }, /* R632 */ - { 0x0000, 0x0000 }, /* R633 */ - { 0x0000, 0x0000 }, /* R634 */ - { 0x0000, 0x0000 }, /* R635 */ - { 0x0000, 0x0000 }, /* R636 */ - { 0x0000, 0x0000 }, /* R637 */ - { 0x0000, 0x0000 }, /* R638 */ - { 0x0000, 0x0000 }, /* R639 */ - { 0x0000, 0x0000 }, /* R640 */ - { 0x0000, 0x0000 }, /* R641 */ - { 0x0000, 0x0000 }, /* R642 */ - { 0x0000, 0x0000 }, /* R643 */ - { 0x0000, 0x0000 }, /* R644 */ - { 0x0000, 0x0000 }, /* R645 */ - { 0x0000, 0x0000 }, /* R646 */ - { 0x0000, 0x0000 }, /* R647 */ - { 0x0000, 0x0000 }, /* R648 */ - { 0x0000, 0x0000 }, /* R649 */ - { 0x0000, 0x0000 }, /* R650 */ - { 0x0000, 0x0000 }, /* R651 */ - { 0x0000, 0x0000 }, /* R652 */ - { 0x0000, 0x0000 }, /* R653 */ - { 0x0000, 0x0000 }, /* R654 */ - { 0x0000, 0x0000 }, /* R655 */ - { 0x0000, 0x0000 }, /* R656 */ - { 0x0000, 0x0000 }, /* R657 */ - { 0x0000, 0x0000 }, /* R658 */ - { 0x0000, 0x0000 }, /* R659 */ - { 0x0000, 0x0000 }, /* R660 */ - { 0x0000, 0x0000 }, /* R661 */ - { 0x0000, 0x0000 }, /* R662 */ - { 0x0000, 0x0000 }, /* R663 */ - { 0x0000, 0x0000 }, /* R664 */ - { 0x0000, 0x0000 }, /* R665 */ - { 0x0000, 0x0000 }, /* R666 */ - { 0x0000, 0x0000 }, /* R667 */ - { 0x0000, 0x0000 }, /* R668 */ - { 0x0000, 0x0000 }, /* R669 */ - { 0x0000, 0x0000 }, /* R670 */ - { 0x0000, 0x0000 }, /* R671 */ - { 0x0000, 0x0000 }, /* R672 */ - { 0x0000, 0x0000 }, /* R673 */ - { 0x0000, 0x0000 }, /* R674 */ - { 0x0000, 0x0000 }, /* R675 */ - { 0x0000, 0x0000 }, /* R676 */ - { 0x0000, 0x0000 }, /* R677 */ - { 0x0000, 0x0000 }, /* R678 */ - { 0x0000, 0x0000 }, /* R679 */ - { 0x0000, 0x0000 }, /* R680 */ - { 0x0000, 0x0000 }, /* R681 */ - { 0x0000, 0x0000 }, /* R682 */ - { 0x0000, 0x0000 }, /* R683 */ - { 0x0000, 0x0000 }, /* R684 */ - { 0x0000, 0x0000 }, /* R685 */ - { 0x0000, 0x0000 }, /* R686 */ - { 0x0000, 0x0000 }, /* R687 */ - { 0x0000, 0x0000 }, /* R688 */ - { 0x0000, 0x0000 }, /* R689 */ - { 0x0000, 0x0000 }, /* R690 */ - { 0x0000, 0x0000 }, /* R691 */ - { 0x0000, 0x0000 }, /* R692 */ - { 0x0000, 0x0000 }, /* R693 */ - { 0x0000, 0x0000 }, /* R694 */ - { 0x0000, 0x0000 }, /* R695 */ - { 0x0000, 0x0000 }, /* R696 */ - { 0x0000, 0x0000 }, /* R697 */ - { 0x0000, 0x0000 }, /* R698 */ - { 0x0000, 0x0000 }, /* R699 */ - { 0x0000, 0x0000 }, /* R700 */ - { 0x0000, 0x0000 }, /* R701 */ - { 0x0000, 0x0000 }, /* R702 */ - { 0x0000, 0x0000 }, /* R703 */ - { 0x0000, 0x0000 }, /* R704 */ - { 0x0000, 0x0000 }, /* R705 */ - { 0x0000, 0x0000 }, /* R706 */ - { 0x0000, 0x0000 }, /* R707 */ - { 0x0000, 0x0000 }, /* R708 */ - { 0x0000, 0x0000 }, /* R709 */ - { 0x0000, 0x0000 }, /* R710 */ - { 0x0000, 0x0000 }, /* R711 */ - { 0x0000, 0x0000 }, /* R712 */ - { 0x0000, 0x0000 }, /* R713 */ - { 0x0000, 0x0000 }, /* R714 */ - { 0x0000, 0x0000 }, /* R715 */ - { 0x0000, 0x0000 }, /* R716 */ - { 0x0000, 0x0000 }, /* R717 */ - { 0x0000, 0x0000 }, /* R718 */ - { 0x0000, 0x0000 }, /* R719 */ - { 0x0000, 0x0000 }, /* R720 */ - { 0x0000, 0x0000 }, /* R721 */ - { 0x0000, 0x0000 }, /* R722 */ - { 0x0000, 0x0000 }, /* R723 */ - { 0x0000, 0x0000 }, /* R724 */ - { 0x0000, 0x0000 }, /* R725 */ - { 0x0000, 0x0000 }, /* R726 */ - { 0x0000, 0x0000 }, /* R727 */ - { 0x0000, 0x0000 }, /* R728 */ - { 0x0000, 0x0000 }, /* R729 */ - { 0x0000, 0x0000 }, /* R730 */ - { 0x0000, 0x0000 }, /* R731 */ - { 0x0000, 0x0000 }, /* R732 */ - { 0x0000, 0x0000 }, /* R733 */ - { 0x0000, 0x0000 }, /* R734 */ - { 0x0000, 0x0000 }, /* R735 */ - { 0x0000, 0x0000 }, /* R736 */ - { 0x0000, 0x0000 }, /* R737 */ - { 0x0000, 0x0000 }, /* R738 */ - { 0x0000, 0x0000 }, /* R739 */ - { 0x0000, 0x0000 }, /* R740 */ - { 0x0000, 0x0000 }, /* R741 */ - { 0x0000, 0x0000 }, /* R742 */ - { 0x0000, 0x0000 }, /* R743 */ - { 0x0000, 0x0000 }, /* R744 */ - { 0x0000, 0x0000 }, /* R745 */ - { 0x0000, 0x0000 }, /* R746 */ - { 0x0000, 0x0000 }, /* R747 */ - { 0x0000, 0x0000 }, /* R748 */ - { 0x0000, 0x0000 }, /* R749 */ - { 0x0000, 0x0000 }, /* R750 */ - { 0x0000, 0x0000 }, /* R751 */ - { 0x0000, 0x0000 }, /* R752 */ - { 0x0000, 0x0000 }, /* R753 */ - { 0x0000, 0x0000 }, /* R754 */ - { 0x0000, 0x0000 }, /* R755 */ - { 0x0000, 0x0000 }, /* R756 */ - { 0x0000, 0x0000 }, /* R757 */ - { 0x0000, 0x0000 }, /* R758 */ - { 0x0000, 0x0000 }, /* R759 */ - { 0x0000, 0x0000 }, /* R760 */ - { 0x0000, 0x0000 }, /* R761 */ - { 0x0000, 0x0000 }, /* R762 */ - { 0x0000, 0x0000 }, /* R763 */ - { 0x0000, 0x0000 }, /* R764 */ - { 0x0000, 0x0000 }, /* R765 */ - { 0x0000, 0x0000 }, /* R766 */ - { 0x0000, 0x0000 }, /* R767 */ - { 0xE1F8, 0xE1F8 }, /* R768 - AIF1 Control (1) */ - { 0xCD1F, 0xCD1F }, /* R769 - AIF1 Control (2) */ - { 0xF000, 0xF000 }, /* R770 - AIF1 Master/Slave */ - { 0x01F0, 0x01F0 }, /* R771 - AIF1 BCLK */ - { 0x0FFF, 0x0FFF }, /* R772 - AIF1ADC LRCLK */ - { 0x0FFF, 0x0FFF }, /* R773 - AIF1DAC LRCLK */ - { 0x0003, 0x0003 }, /* R774 - AIF1DAC Data */ - { 0x0003, 0x0003 }, /* R775 - AIF1ADC Data */ - { 0x0000, 0x0000 }, /* R776 */ - { 0x0000, 0x0000 }, /* R777 */ - { 0x0000, 0x0000 }, /* R778 */ - { 0x0000, 0x0000 }, /* R779 */ - { 0x0000, 0x0000 }, /* R780 */ - { 0x0000, 0x0000 }, /* R781 */ - { 0x0000, 0x0000 }, /* R782 */ - { 0x0000, 0x0000 }, /* R783 */ - { 0xF1F8, 0xF1F8 }, /* R784 - AIF2 Control (1) */ - { 0xFD1F, 0xFD1F }, /* R785 - AIF2 Control (2) */ - { 0xF000, 0xF000 }, /* R786 - AIF2 Master/Slave */ - { 0x01F0, 0x01F0 }, /* R787 - AIF2 BCLK */ - { 0x0FFF, 0x0FFF }, /* R788 - AIF2ADC LRCLK */ - { 0x0FFF, 0x0FFF }, /* R789 - AIF2DAC LRCLK */ - { 0x0003, 0x0003 }, /* R790 - AIF2DAC Data */ - { 0x0003, 0x0003 }, /* R791 - AIF2ADC Data */ - { 0x0000, 0x0000 }, /* R792 */ - { 0x0000, 0x0000 }, /* R793 */ - { 0x0000, 0x0000 }, /* R794 */ - { 0x0000, 0x0000 }, /* R795 */ - { 0x0000, 0x0000 }, /* R796 */ - { 0x0000, 0x0000 }, /* R797 */ - { 0x0000, 0x0000 }, /* R798 */ - { 0x0000, 0x0000 }, /* R799 */ - { 0x0000, 0x0000 }, /* R800 */ - { 0x0000, 0x0000 }, /* R801 */ - { 0x0000, 0x0000 }, /* R802 */ - { 0x0000, 0x0000 }, /* R803 */ - { 0x0000, 0x0000 }, /* R804 */ - { 0x0000, 0x0000 }, /* R805 */ - { 0x0000, 0x0000 }, /* R806 */ - { 0x0000, 0x0000 }, /* R807 */ - { 0x0000, 0x0000 }, /* R808 */ - { 0x0000, 0x0000 }, /* R809 */ - { 0x0000, 0x0000 }, /* R810 */ - { 0x0000, 0x0000 }, /* R811 */ - { 0x0000, 0x0000 }, /* R812 */ - { 0x0000, 0x0000 }, /* R813 */ - { 0x0000, 0x0000 }, /* R814 */ - { 0x0000, 0x0000 }, /* R815 */ - { 0x0000, 0x0000 }, /* R816 */ - { 0x0000, 0x0000 }, /* R817 */ - { 0x0000, 0x0000 }, /* R818 */ - { 0x0000, 0x0000 }, /* R819 */ - { 0x0000, 0x0000 }, /* R820 */ - { 0x0000, 0x0000 }, /* R821 */ - { 0x0000, 0x0000 }, /* R822 */ - { 0x0000, 0x0000 }, /* R823 */ - { 0x0000, 0x0000 }, /* R824 */ - { 0x0000, 0x0000 }, /* R825 */ - { 0x0000, 0x0000 }, /* R826 */ - { 0x0000, 0x0000 }, /* R827 */ - { 0x0000, 0x0000 }, /* R828 */ - { 0x0000, 0x0000 }, /* R829 */ - { 0x0000, 0x0000 }, /* R830 */ - { 0x0000, 0x0000 }, /* R831 */ - { 0x0000, 0x0000 }, /* R832 */ - { 0x0000, 0x0000 }, /* R833 */ - { 0x0000, 0x0000 }, /* R834 */ - { 0x0000, 0x0000 }, /* R835 */ - { 0x0000, 0x0000 }, /* R836 */ - { 0x0000, 0x0000 }, /* R837 */ - { 0x0000, 0x0000 }, /* R838 */ - { 0x0000, 0x0000 }, /* R839 */ - { 0x0000, 0x0000 }, /* R840 */ - { 0x0000, 0x0000 }, /* R841 */ - { 0x0000, 0x0000 }, /* R842 */ - { 0x0000, 0x0000 }, /* R843 */ - { 0x0000, 0x0000 }, /* R844 */ - { 0x0000, 0x0000 }, /* R845 */ - { 0x0000, 0x0000 }, /* R846 */ - { 0x0000, 0x0000 }, /* R847 */ - { 0x0000, 0x0000 }, /* R848 */ - { 0x0000, 0x0000 }, /* R849 */ - { 0x0000, 0x0000 }, /* R850 */ - { 0x0000, 0x0000 }, /* R851 */ - { 0x0000, 0x0000 }, /* R852 */ - { 0x0000, 0x0000 }, /* R853 */ - { 0x0000, 0x0000 }, /* R854 */ - { 0x0000, 0x0000 }, /* R855 */ - { 0x0000, 0x0000 }, /* R856 */ - { 0x0000, 0x0000 }, /* R857 */ - { 0x0000, 0x0000 }, /* R858 */ - { 0x0000, 0x0000 }, /* R859 */ - { 0x0000, 0x0000 }, /* R860 */ - { 0x0000, 0x0000 }, /* R861 */ - { 0x0000, 0x0000 }, /* R862 */ - { 0x0000, 0x0000 }, /* R863 */ - { 0x0000, 0x0000 }, /* R864 */ - { 0x0000, 0x0000 }, /* R865 */ - { 0x0000, 0x0000 }, /* R866 */ - { 0x0000, 0x0000 }, /* R867 */ - { 0x0000, 0x0000 }, /* R868 */ - { 0x0000, 0x0000 }, /* R869 */ - { 0x0000, 0x0000 }, /* R870 */ - { 0x0000, 0x0000 }, /* R871 */ - { 0x0000, 0x0000 }, /* R872 */ - { 0x0000, 0x0000 }, /* R873 */ - { 0x0000, 0x0000 }, /* R874 */ - { 0x0000, 0x0000 }, /* R875 */ - { 0x0000, 0x0000 }, /* R876 */ - { 0x0000, 0x0000 }, /* R877 */ - { 0x0000, 0x0000 }, /* R878 */ - { 0x0000, 0x0000 }, /* R879 */ - { 0x0000, 0x0000 }, /* R880 */ - { 0x0000, 0x0000 }, /* R881 */ - { 0x0000, 0x0000 }, /* R882 */ - { 0x0000, 0x0000 }, /* R883 */ - { 0x0000, 0x0000 }, /* R884 */ - { 0x0000, 0x0000 }, /* R885 */ - { 0x0000, 0x0000 }, /* R886 */ - { 0x0000, 0x0000 }, /* R887 */ - { 0x0000, 0x0000 }, /* R888 */ - { 0x0000, 0x0000 }, /* R889 */ - { 0x0000, 0x0000 }, /* R890 */ - { 0x0000, 0x0000 }, /* R891 */ - { 0x0000, 0x0000 }, /* R892 */ - { 0x0000, 0x0000 }, /* R893 */ - { 0x0000, 0x0000 }, /* R894 */ - { 0x0000, 0x0000 }, /* R895 */ - { 0x0000, 0x0000 }, /* R896 */ - { 0x0000, 0x0000 }, /* R897 */ - { 0x0000, 0x0000 }, /* R898 */ - { 0x0000, 0x0000 }, /* R899 */ - { 0x0000, 0x0000 }, /* R900 */ - { 0x0000, 0x0000 }, /* R901 */ - { 0x0000, 0x0000 }, /* R902 */ - { 0x0000, 0x0000 }, /* R903 */ - { 0x0000, 0x0000 }, /* R904 */ - { 0x0000, 0x0000 }, /* R905 */ - { 0x0000, 0x0000 }, /* R906 */ - { 0x0000, 0x0000 }, /* R907 */ - { 0x0000, 0x0000 }, /* R908 */ - { 0x0000, 0x0000 }, /* R909 */ - { 0x0000, 0x0000 }, /* R910 */ - { 0x0000, 0x0000 }, /* R911 */ - { 0x0000, 0x0000 }, /* R912 */ - { 0x0000, 0x0000 }, /* R913 */ - { 0x0000, 0x0000 }, /* R914 */ - { 0x0000, 0x0000 }, /* R915 */ - { 0x0000, 0x0000 }, /* R916 */ - { 0x0000, 0x0000 }, /* R917 */ - { 0x0000, 0x0000 }, /* R918 */ - { 0x0000, 0x0000 }, /* R919 */ - { 0x0000, 0x0000 }, /* R920 */ - { 0x0000, 0x0000 }, /* R921 */ - { 0x0000, 0x0000 }, /* R922 */ - { 0x0000, 0x0000 }, /* R923 */ - { 0x0000, 0x0000 }, /* R924 */ - { 0x0000, 0x0000 }, /* R925 */ - { 0x0000, 0x0000 }, /* R926 */ - { 0x0000, 0x0000 }, /* R927 */ - { 0x0000, 0x0000 }, /* R928 */ - { 0x0000, 0x0000 }, /* R929 */ - { 0x0000, 0x0000 }, /* R930 */ - { 0x0000, 0x0000 }, /* R931 */ - { 0x0000, 0x0000 }, /* R932 */ - { 0x0000, 0x0000 }, /* R933 */ - { 0x0000, 0x0000 }, /* R934 */ - { 0x0000, 0x0000 }, /* R935 */ - { 0x0000, 0x0000 }, /* R936 */ - { 0x0000, 0x0000 }, /* R937 */ - { 0x0000, 0x0000 }, /* R938 */ - { 0x0000, 0x0000 }, /* R939 */ - { 0x0000, 0x0000 }, /* R940 */ - { 0x0000, 0x0000 }, /* R941 */ - { 0x0000, 0x0000 }, /* R942 */ - { 0x0000, 0x0000 }, /* R943 */ - { 0x0000, 0x0000 }, /* R944 */ - { 0x0000, 0x0000 }, /* R945 */ - { 0x0000, 0x0000 }, /* R946 */ - { 0x0000, 0x0000 }, /* R947 */ - { 0x0000, 0x0000 }, /* R948 */ - { 0x0000, 0x0000 }, /* R949 */ - { 0x0000, 0x0000 }, /* R950 */ - { 0x0000, 0x0000 }, /* R951 */ - { 0x0000, 0x0000 }, /* R952 */ - { 0x0000, 0x0000 }, /* R953 */ - { 0x0000, 0x0000 }, /* R954 */ - { 0x0000, 0x0000 }, /* R955 */ - { 0x0000, 0x0000 }, /* R956 */ - { 0x0000, 0x0000 }, /* R957 */ - { 0x0000, 0x0000 }, /* R958 */ - { 0x0000, 0x0000 }, /* R959 */ - { 0x0000, 0x0000 }, /* R960 */ - { 0x0000, 0x0000 }, /* R961 */ - { 0x0000, 0x0000 }, /* R962 */ - { 0x0000, 0x0000 }, /* R963 */ - { 0x0000, 0x0000 }, /* R964 */ - { 0x0000, 0x0000 }, /* R965 */ - { 0x0000, 0x0000 }, /* R966 */ - { 0x0000, 0x0000 }, /* R967 */ - { 0x0000, 0x0000 }, /* R968 */ - { 0x0000, 0x0000 }, /* R969 */ - { 0x0000, 0x0000 }, /* R970 */ - { 0x0000, 0x0000 }, /* R971 */ - { 0x0000, 0x0000 }, /* R972 */ - { 0x0000, 0x0000 }, /* R973 */ - { 0x0000, 0x0000 }, /* R974 */ - { 0x0000, 0x0000 }, /* R975 */ - { 0x0000, 0x0000 }, /* R976 */ - { 0x0000, 0x0000 }, /* R977 */ - { 0x0000, 0x0000 }, /* R978 */ - { 0x0000, 0x0000 }, /* R979 */ - { 0x0000, 0x0000 }, /* R980 */ - { 0x0000, 0x0000 }, /* R981 */ - { 0x0000, 0x0000 }, /* R982 */ - { 0x0000, 0x0000 }, /* R983 */ - { 0x0000, 0x0000 }, /* R984 */ - { 0x0000, 0x0000 }, /* R985 */ - { 0x0000, 0x0000 }, /* R986 */ - { 0x0000, 0x0000 }, /* R987 */ - { 0x0000, 0x0000 }, /* R988 */ - { 0x0000, 0x0000 }, /* R989 */ - { 0x0000, 0x0000 }, /* R990 */ - { 0x0000, 0x0000 }, /* R991 */ - { 0x0000, 0x0000 }, /* R992 */ - { 0x0000, 0x0000 }, /* R993 */ - { 0x0000, 0x0000 }, /* R994 */ - { 0x0000, 0x0000 }, /* R995 */ - { 0x0000, 0x0000 }, /* R996 */ - { 0x0000, 0x0000 }, /* R997 */ - { 0x0000, 0x0000 }, /* R998 */ - { 0x0000, 0x0000 }, /* R999 */ - { 0x0000, 0x0000 }, /* R1000 */ - { 0x0000, 0x0000 }, /* R1001 */ - { 0x0000, 0x0000 }, /* R1002 */ - { 0x0000, 0x0000 }, /* R1003 */ - { 0x0000, 0x0000 }, /* R1004 */ - { 0x0000, 0x0000 }, /* R1005 */ - { 0x0000, 0x0000 }, /* R1006 */ - { 0x0000, 0x0000 }, /* R1007 */ - { 0x0000, 0x0000 }, /* R1008 */ - { 0x0000, 0x0000 }, /* R1009 */ - { 0x0000, 0x0000 }, /* R1010 */ - { 0x0000, 0x0000 }, /* R1011 */ - { 0x0000, 0x0000 }, /* R1012 */ - { 0x0000, 0x0000 }, /* R1013 */ - { 0x0000, 0x0000 }, /* R1014 */ - { 0x0000, 0x0000 }, /* R1015 */ - { 0x0000, 0x0000 }, /* R1016 */ - { 0x0000, 0x0000 }, /* R1017 */ - { 0x0000, 0x0000 }, /* R1018 */ - { 0x0000, 0x0000 }, /* R1019 */ - { 0x0000, 0x0000 }, /* R1020 */ - { 0x0000, 0x0000 }, /* R1021 */ - { 0x0000, 0x0000 }, /* R1022 */ - { 0x0000, 0x0000 }, /* R1023 */ - { 0x00FF, 0x01FF }, /* R1024 - AIF1 ADC1 Left Volume */ - { 0x00FF, 0x01FF }, /* R1025 - AIF1 ADC1 Right Volume */ - { 0x00FF, 0x01FF }, /* R1026 - AIF1 DAC1 Left Volume */ - { 0x00FF, 0x01FF }, /* R1027 - AIF1 DAC1 Right Volume */ - { 0x00FF, 0x01FF }, /* R1028 - AIF1 ADC2 Left Volume */ - { 0x00FF, 0x01FF }, /* R1029 - AIF1 ADC2 Right Volume */ - { 0x00FF, 0x01FF }, /* R1030 - AIF1 DAC2 Left Volume */ - { 0x00FF, 0x01FF }, /* R1031 - AIF1 DAC2 Right Volume */ - { 0x0000, 0x0000 }, /* R1032 */ - { 0x0000, 0x0000 }, /* R1033 */ - { 0x0000, 0x0000 }, /* R1034 */ - { 0x0000, 0x0000 }, /* R1035 */ - { 0x0000, 0x0000 }, /* R1036 */ - { 0x0000, 0x0000 }, /* R1037 */ - { 0x0000, 0x0000 }, /* R1038 */ - { 0x0000, 0x0000 }, /* R1039 */ - { 0xF800, 0xF800 }, /* R1040 - AIF1 ADC1 Filters */ - { 0x7800, 0x7800 }, /* R1041 - AIF1 ADC2 Filters */ - { 0x0000, 0x0000 }, /* R1042 */ - { 0x0000, 0x0000 }, /* R1043 */ - { 0x0000, 0x0000 }, /* R1044 */ - { 0x0000, 0x0000 }, /* R1045 */ - { 0x0000, 0x0000 }, /* R1046 */ - { 0x0000, 0x0000 }, /* R1047 */ - { 0x0000, 0x0000 }, /* R1048 */ - { 0x0000, 0x0000 }, /* R1049 */ - { 0x0000, 0x0000 }, /* R1050 */ - { 0x0000, 0x0000 }, /* R1051 */ - { 0x0000, 0x0000 }, /* R1052 */ - { 0x0000, 0x0000 }, /* R1053 */ - { 0x0000, 0x0000 }, /* R1054 */ - { 0x0000, 0x0000 }, /* R1055 */ - { 0x02B6, 0x02B6 }, /* R1056 - AIF1 DAC1 Filters (1) */ - { 0x3F00, 0x3F00 }, /* R1057 - AIF1 DAC1 Filters (2) */ - { 0x02B6, 0x02B6 }, /* R1058 - AIF1 DAC2 Filters (1) */ - { 0x3F00, 0x3F00 }, /* R1059 - AIF1 DAC2 Filters (2) */ - { 0x0000, 0x0000 }, /* R1060 */ - { 0x0000, 0x0000 }, /* R1061 */ - { 0x0000, 0x0000 }, /* R1062 */ - { 0x0000, 0x0000 }, /* R1063 */ - { 0x0000, 0x0000 }, /* R1064 */ - { 0x0000, 0x0000 }, /* R1065 */ - { 0x0000, 0x0000 }, /* R1066 */ - { 0x0000, 0x0000 }, /* R1067 */ - { 0x0000, 0x0000 }, /* R1068 */ - { 0x0000, 0x0000 }, /* R1069 */ - { 0x0000, 0x0000 }, /* R1070 */ - { 0x0000, 0x0000 }, /* R1071 */ - { 0x0000, 0x0000 }, /* R1072 */ - { 0x0000, 0x0000 }, /* R1073 */ - { 0x0000, 0x0000 }, /* R1074 */ - { 0x0000, 0x0000 }, /* R1075 */ - { 0x0000, 0x0000 }, /* R1076 */ - { 0x0000, 0x0000 }, /* R1077 */ - { 0x0000, 0x0000 }, /* R1078 */ - { 0x0000, 0x0000 }, /* R1079 */ - { 0x0000, 0x0000 }, /* R1080 */ - { 0x0000, 0x0000 }, /* R1081 */ - { 0x0000, 0x0000 }, /* R1082 */ - { 0x0000, 0x0000 }, /* R1083 */ - { 0x0000, 0x0000 }, /* R1084 */ - { 0x0000, 0x0000 }, /* R1085 */ - { 0x0000, 0x0000 }, /* R1086 */ - { 0x0000, 0x0000 }, /* R1087 */ - { 0xFFFF, 0xFFFF }, /* R1088 - AIF1 DRC1 (1) */ - { 0x1FFF, 0x1FFF }, /* R1089 - AIF1 DRC1 (2) */ - { 0xFFFF, 0xFFFF }, /* R1090 - AIF1 DRC1 (3) */ - { 0x07FF, 0x07FF }, /* R1091 - AIF1 DRC1 (4) */ - { 0x03FF, 0x03FF }, /* R1092 - AIF1 DRC1 (5) */ - { 0x0000, 0x0000 }, /* R1093 */ - { 0x0000, 0x0000 }, /* R1094 */ - { 0x0000, 0x0000 }, /* R1095 */ - { 0x0000, 0x0000 }, /* R1096 */ - { 0x0000, 0x0000 }, /* R1097 */ - { 0x0000, 0x0000 }, /* R1098 */ - { 0x0000, 0x0000 }, /* R1099 */ - { 0x0000, 0x0000 }, /* R1100 */ - { 0x0000, 0x0000 }, /* R1101 */ - { 0x0000, 0x0000 }, /* R1102 */ - { 0x0000, 0x0000 }, /* R1103 */ - { 0xFFFF, 0xFFFF }, /* R1104 - AIF1 DRC2 (1) */ - { 0x1FFF, 0x1FFF }, /* R1105 - AIF1 DRC2 (2) */ - { 0xFFFF, 0xFFFF }, /* R1106 - AIF1 DRC2 (3) */ - { 0x07FF, 0x07FF }, /* R1107 - AIF1 DRC2 (4) */ - { 0x03FF, 0x03FF }, /* R1108 - AIF1 DRC2 (5) */ - { 0x0000, 0x0000 }, /* R1109 */ - { 0x0000, 0x0000 }, /* R1110 */ - { 0x0000, 0x0000 }, /* R1111 */ - { 0x0000, 0x0000 }, /* R1112 */ - { 0x0000, 0x0000 }, /* R1113 */ - { 0x0000, 0x0000 }, /* R1114 */ - { 0x0000, 0x0000 }, /* R1115 */ - { 0x0000, 0x0000 }, /* R1116 */ - { 0x0000, 0x0000 }, /* R1117 */ - { 0x0000, 0x0000 }, /* R1118 */ - { 0x0000, 0x0000 }, /* R1119 */ - { 0x0000, 0x0000 }, /* R1120 */ - { 0x0000, 0x0000 }, /* R1121 */ - { 0x0000, 0x0000 }, /* R1122 */ - { 0x0000, 0x0000 }, /* R1123 */ - { 0x0000, 0x0000 }, /* R1124 */ - { 0x0000, 0x0000 }, /* R1125 */ - { 0x0000, 0x0000 }, /* R1126 */ - { 0x0000, 0x0000 }, /* R1127 */ - { 0x0000, 0x0000 }, /* R1128 */ - { 0x0000, 0x0000 }, /* R1129 */ - { 0x0000, 0x0000 }, /* R1130 */ - { 0x0000, 0x0000 }, /* R1131 */ - { 0x0000, 0x0000 }, /* R1132 */ - { 0x0000, 0x0000 }, /* R1133 */ - { 0x0000, 0x0000 }, /* R1134 */ - { 0x0000, 0x0000 }, /* R1135 */ - { 0x0000, 0x0000 }, /* R1136 */ - { 0x0000, 0x0000 }, /* R1137 */ - { 0x0000, 0x0000 }, /* R1138 */ - { 0x0000, 0x0000 }, /* R1139 */ - { 0x0000, 0x0000 }, /* R1140 */ - { 0x0000, 0x0000 }, /* R1141 */ - { 0x0000, 0x0000 }, /* R1142 */ - { 0x0000, 0x0000 }, /* R1143 */ - { 0x0000, 0x0000 }, /* R1144 */ - { 0x0000, 0x0000 }, /* R1145 */ - { 0x0000, 0x0000 }, /* R1146 */ - { 0x0000, 0x0000 }, /* R1147 */ - { 0x0000, 0x0000 }, /* R1148 */ - { 0x0000, 0x0000 }, /* R1149 */ - { 0x0000, 0x0000 }, /* R1150 */ - { 0x0000, 0x0000 }, /* R1151 */ - { 0xFFFF, 0xFFFF }, /* R1152 - AIF1 DAC1 EQ Gains (1) */ - { 0xFFC0, 0xFFC0 }, /* R1153 - AIF1 DAC1 EQ Gains (2) */ - { 0xFFFF, 0xFFFF }, /* R1154 - AIF1 DAC1 EQ Band 1 A */ - { 0xFFFF, 0xFFFF }, /* R1155 - AIF1 DAC1 EQ Band 1 B */ - { 0xFFFF, 0xFFFF }, /* R1156 - AIF1 DAC1 EQ Band 1 PG */ - { 0xFFFF, 0xFFFF }, /* R1157 - AIF1 DAC1 EQ Band 2 A */ - { 0xFFFF, 0xFFFF }, /* R1158 - AIF1 DAC1 EQ Band 2 B */ - { 0xFFFF, 0xFFFF }, /* R1159 - AIF1 DAC1 EQ Band 2 C */ - { 0xFFFF, 0xFFFF }, /* R1160 - AIF1 DAC1 EQ Band 2 PG */ - { 0xFFFF, 0xFFFF }, /* R1161 - AIF1 DAC1 EQ Band 3 A */ - { 0xFFFF, 0xFFFF }, /* R1162 - AIF1 DAC1 EQ Band 3 B */ - { 0xFFFF, 0xFFFF }, /* R1163 - AIF1 DAC1 EQ Band 3 C */ - { 0xFFFF, 0xFFFF }, /* R1164 - AIF1 DAC1 EQ Band 3 PG */ - { 0xFFFF, 0xFFFF }, /* R1165 - AIF1 DAC1 EQ Band 4 A */ - { 0xFFFF, 0xFFFF }, /* R1166 - AIF1 DAC1 EQ Band 4 B */ - { 0xFFFF, 0xFFFF }, /* R1167 - AIF1 DAC1 EQ Band 4 C */ - { 0xFFFF, 0xFFFF }, /* R1168 - AIF1 DAC1 EQ Band 4 PG */ - { 0xFFFF, 0xFFFF }, /* R1169 - AIF1 DAC1 EQ Band 5 A */ - { 0xFFFF, 0xFFFF }, /* R1170 - AIF1 DAC1 EQ Band 5 B */ - { 0xFFFF, 0xFFFF }, /* R1171 - AIF1 DAC1 EQ Band 5 PG */ - { 0x0000, 0x0000 }, /* R1172 */ - { 0x0000, 0x0000 }, /* R1173 */ - { 0x0000, 0x0000 }, /* R1174 */ - { 0x0000, 0x0000 }, /* R1175 */ - { 0x0000, 0x0000 }, /* R1176 */ - { 0x0000, 0x0000 }, /* R1177 */ - { 0x0000, 0x0000 }, /* R1178 */ - { 0x0000, 0x0000 }, /* R1179 */ - { 0x0000, 0x0000 }, /* R1180 */ - { 0x0000, 0x0000 }, /* R1181 */ - { 0x0000, 0x0000 }, /* R1182 */ - { 0x0000, 0x0000 }, /* R1183 */ - { 0xFFFF, 0xFFFF }, /* R1184 - AIF1 DAC2 EQ Gains (1) */ - { 0xFFC0, 0xFFC0 }, /* R1185 - AIF1 DAC2 EQ Gains (2) */ - { 0xFFFF, 0xFFFF }, /* R1186 - AIF1 DAC2 EQ Band 1 A */ - { 0xFFFF, 0xFFFF }, /* R1187 - AIF1 DAC2 EQ Band 1 B */ - { 0xFFFF, 0xFFFF }, /* R1188 - AIF1 DAC2 EQ Band 1 PG */ - { 0xFFFF, 0xFFFF }, /* R1189 - AIF1 DAC2 EQ Band 2 A */ - { 0xFFFF, 0xFFFF }, /* R1190 - AIF1 DAC2 EQ Band 2 B */ - { 0xFFFF, 0xFFFF }, /* R1191 - AIF1 DAC2 EQ Band 2 C */ - { 0xFFFF, 0xFFFF }, /* R1192 - AIF1 DAC2 EQ Band 2 PG */ - { 0xFFFF, 0xFFFF }, /* R1193 - AIF1 DAC2 EQ Band 3 A */ - { 0xFFFF, 0xFFFF }, /* R1194 - AIF1 DAC2 EQ Band 3 B */ - { 0xFFFF, 0xFFFF }, /* R1195 - AIF1 DAC2 EQ Band 3 C */ - { 0xFFFF, 0xFFFF }, /* R1196 - AIF1 DAC2 EQ Band 3 PG */ - { 0xFFFF, 0xFFFF }, /* R1197 - AIF1 DAC2 EQ Band 4 A */ - { 0xFFFF, 0xFFFF }, /* R1198 - AIF1 DAC2 EQ Band 4 B */ - { 0xFFFF, 0xFFFF }, /* R1199 - AIF1 DAC2 EQ Band 4 C */ - { 0xFFFF, 0xFFFF }, /* R1200 - AIF1 DAC2 EQ Band 4 PG */ - { 0xFFFF, 0xFFFF }, /* R1201 - AIF1 DAC2 EQ Band 5 A */ - { 0xFFFF, 0xFFFF }, /* R1202 - AIF1 DAC2 EQ Band 5 B */ - { 0xFFFF, 0xFFFF }, /* R1203 - AIF1 DAC2 EQ Band 5 PG */ - { 0x0000, 0x0000 }, /* R1204 */ - { 0x0000, 0x0000 }, /* R1205 */ - { 0x0000, 0x0000 }, /* R1206 */ - { 0x0000, 0x0000 }, /* R1207 */ - { 0x0000, 0x0000 }, /* R1208 */ - { 0x0000, 0x0000 }, /* R1209 */ - { 0x0000, 0x0000 }, /* R1210 */ - { 0x0000, 0x0000 }, /* R1211 */ - { 0x0000, 0x0000 }, /* R1212 */ - { 0x0000, 0x0000 }, /* R1213 */ - { 0x0000, 0x0000 }, /* R1214 */ - { 0x0000, 0x0000 }, /* R1215 */ - { 0x0000, 0x0000 }, /* R1216 */ - { 0x0000, 0x0000 }, /* R1217 */ - { 0x0000, 0x0000 }, /* R1218 */ - { 0x0000, 0x0000 }, /* R1219 */ - { 0x0000, 0x0000 }, /* R1220 */ - { 0x0000, 0x0000 }, /* R1221 */ - { 0x0000, 0x0000 }, /* R1222 */ - { 0x0000, 0x0000 }, /* R1223 */ - { 0x0000, 0x0000 }, /* R1224 */ - { 0x0000, 0x0000 }, /* R1225 */ - { 0x0000, 0x0000 }, /* R1226 */ - { 0x0000, 0x0000 }, /* R1227 */ - { 0x0000, 0x0000 }, /* R1228 */ - { 0x0000, 0x0000 }, /* R1229 */ - { 0x0000, 0x0000 }, /* R1230 */ - { 0x0000, 0x0000 }, /* R1231 */ - { 0x0000, 0x0000 }, /* R1232 */ - { 0x0000, 0x0000 }, /* R1233 */ - { 0x0000, 0x0000 }, /* R1234 */ - { 0x0000, 0x0000 }, /* R1235 */ - { 0x0000, 0x0000 }, /* R1236 */ - { 0x0000, 0x0000 }, /* R1237 */ - { 0x0000, 0x0000 }, /* R1238 */ - { 0x0000, 0x0000 }, /* R1239 */ - { 0x0000, 0x0000 }, /* R1240 */ - { 0x0000, 0x0000 }, /* R1241 */ - { 0x0000, 0x0000 }, /* R1242 */ - { 0x0000, 0x0000 }, /* R1243 */ - { 0x0000, 0x0000 }, /* R1244 */ - { 0x0000, 0x0000 }, /* R1245 */ - { 0x0000, 0x0000 }, /* R1246 */ - { 0x0000, 0x0000 }, /* R1247 */ - { 0x0000, 0x0000 }, /* R1248 */ - { 0x0000, 0x0000 }, /* R1249 */ - { 0x0000, 0x0000 }, /* R1250 */ - { 0x0000, 0x0000 }, /* R1251 */ - { 0x0000, 0x0000 }, /* R1252 */ - { 0x0000, 0x0000 }, /* R1253 */ - { 0x0000, 0x0000 }, /* R1254 */ - { 0x0000, 0x0000 }, /* R1255 */ - { 0x0000, 0x0000 }, /* R1256 */ - { 0x0000, 0x0000 }, /* R1257 */ - { 0x0000, 0x0000 }, /* R1258 */ - { 0x0000, 0x0000 }, /* R1259 */ - { 0x0000, 0x0000 }, /* R1260 */ - { 0x0000, 0x0000 }, /* R1261 */ - { 0x0000, 0x0000 }, /* R1262 */ - { 0x0000, 0x0000 }, /* R1263 */ - { 0x0000, 0x0000 }, /* R1264 */ - { 0x0000, 0x0000 }, /* R1265 */ - { 0x0000, 0x0000 }, /* R1266 */ - { 0x0000, 0x0000 }, /* R1267 */ - { 0x0000, 0x0000 }, /* R1268 */ - { 0x0000, 0x0000 }, /* R1269 */ - { 0x0000, 0x0000 }, /* R1270 */ - { 0x0000, 0x0000 }, /* R1271 */ - { 0x0000, 0x0000 }, /* R1272 */ - { 0x0000, 0x0000 }, /* R1273 */ - { 0x0000, 0x0000 }, /* R1274 */ - { 0x0000, 0x0000 }, /* R1275 */ - { 0x0000, 0x0000 }, /* R1276 */ - { 0x0000, 0x0000 }, /* R1277 */ - { 0x0000, 0x0000 }, /* R1278 */ - { 0x0000, 0x0000 }, /* R1279 */ - { 0x00FF, 0x01FF }, /* R1280 - AIF2 ADC Left Volume */ - { 0x00FF, 0x01FF }, /* R1281 - AIF2 ADC Right Volume */ - { 0x00FF, 0x01FF }, /* R1282 - AIF2 DAC Left Volume */ - { 0x00FF, 0x01FF }, /* R1283 - AIF2 DAC Right Volume */ - { 0x0000, 0x0000 }, /* R1284 */ - { 0x0000, 0x0000 }, /* R1285 */ - { 0x0000, 0x0000 }, /* R1286 */ - { 0x0000, 0x0000 }, /* R1287 */ - { 0x0000, 0x0000 }, /* R1288 */ - { 0x0000, 0x0000 }, /* R1289 */ - { 0x0000, 0x0000 }, /* R1290 */ - { 0x0000, 0x0000 }, /* R1291 */ - { 0x0000, 0x0000 }, /* R1292 */ - { 0x0000, 0x0000 }, /* R1293 */ - { 0x0000, 0x0000 }, /* R1294 */ - { 0x0000, 0x0000 }, /* R1295 */ - { 0xF800, 0xF800 }, /* R1296 - AIF2 ADC Filters */ - { 0x0000, 0x0000 }, /* R1297 */ - { 0x0000, 0x0000 }, /* R1298 */ - { 0x0000, 0x0000 }, /* R1299 */ - { 0x0000, 0x0000 }, /* R1300 */ - { 0x0000, 0x0000 }, /* R1301 */ - { 0x0000, 0x0000 }, /* R1302 */ - { 0x0000, 0x0000 }, /* R1303 */ - { 0x0000, 0x0000 }, /* R1304 */ - { 0x0000, 0x0000 }, /* R1305 */ - { 0x0000, 0x0000 }, /* R1306 */ - { 0x0000, 0x0000 }, /* R1307 */ - { 0x0000, 0x0000 }, /* R1308 */ - { 0x0000, 0x0000 }, /* R1309 */ - { 0x0000, 0x0000 }, /* R1310 */ - { 0x0000, 0x0000 }, /* R1311 */ - { 0x02B6, 0x02B6 }, /* R1312 - AIF2 DAC Filters (1) */ - { 0x3F00, 0x3F00 }, /* R1313 - AIF2 DAC Filters (2) */ - { 0x0000, 0x0000 }, /* R1314 */ - { 0x0000, 0x0000 }, /* R1315 */ - { 0x0000, 0x0000 }, /* R1316 */ - { 0x0000, 0x0000 }, /* R1317 */ - { 0x0000, 0x0000 }, /* R1318 */ - { 0x0000, 0x0000 }, /* R1319 */ - { 0x0000, 0x0000 }, /* R1320 */ - { 0x0000, 0x0000 }, /* R1321 */ - { 0x0000, 0x0000 }, /* R1322 */ - { 0x0000, 0x0000 }, /* R1323 */ - { 0x0000, 0x0000 }, /* R1324 */ - { 0x0000, 0x0000 }, /* R1325 */ - { 0x0000, 0x0000 }, /* R1326 */ - { 0x0000, 0x0000 }, /* R1327 */ - { 0x0000, 0x0000 }, /* R1328 */ - { 0x0000, 0x0000 }, /* R1329 */ - { 0x0000, 0x0000 }, /* R1330 */ - { 0x0000, 0x0000 }, /* R1331 */ - { 0x0000, 0x0000 }, /* R1332 */ - { 0x0000, 0x0000 }, /* R1333 */ - { 0x0000, 0x0000 }, /* R1334 */ - { 0x0000, 0x0000 }, /* R1335 */ - { 0x0000, 0x0000 }, /* R1336 */ - { 0x0000, 0x0000 }, /* R1337 */ - { 0x0000, 0x0000 }, /* R1338 */ - { 0x0000, 0x0000 }, /* R1339 */ - { 0x0000, 0x0000 }, /* R1340 */ - { 0x0000, 0x0000 }, /* R1341 */ - { 0x0000, 0x0000 }, /* R1342 */ - { 0x0000, 0x0000 }, /* R1343 */ - { 0xFFFF, 0xFFFF }, /* R1344 - AIF2 DRC (1) */ - { 0x1FFF, 0x1FFF }, /* R1345 - AIF2 DRC (2) */ - { 0xFFFF, 0xFFFF }, /* R1346 - AIF2 DRC (3) */ - { 0x07FF, 0x07FF }, /* R1347 - AIF2 DRC (4) */ - { 0x03FF, 0x03FF }, /* R1348 - AIF2 DRC (5) */ - { 0x0000, 0x0000 }, /* R1349 */ - { 0x0000, 0x0000 }, /* R1350 */ - { 0x0000, 0x0000 }, /* R1351 */ - { 0x0000, 0x0000 }, /* R1352 */ - { 0x0000, 0x0000 }, /* R1353 */ - { 0x0000, 0x0000 }, /* R1354 */ - { 0x0000, 0x0000 }, /* R1355 */ - { 0x0000, 0x0000 }, /* R1356 */ - { 0x0000, 0x0000 }, /* R1357 */ - { 0x0000, 0x0000 }, /* R1358 */ - { 0x0000, 0x0000 }, /* R1359 */ - { 0x0000, 0x0000 }, /* R1360 */ - { 0x0000, 0x0000 }, /* R1361 */ - { 0x0000, 0x0000 }, /* R1362 */ - { 0x0000, 0x0000 }, /* R1363 */ - { 0x0000, 0x0000 }, /* R1364 */ - { 0x0000, 0x0000 }, /* R1365 */ - { 0x0000, 0x0000 }, /* R1366 */ - { 0x0000, 0x0000 }, /* R1367 */ - { 0x0000, 0x0000 }, /* R1368 */ - { 0x0000, 0x0000 }, /* R1369 */ - { 0x0000, 0x0000 }, /* R1370 */ - { 0x0000, 0x0000 }, /* R1371 */ - { 0x0000, 0x0000 }, /* R1372 */ - { 0x0000, 0x0000 }, /* R1373 */ - { 0x0000, 0x0000 }, /* R1374 */ - { 0x0000, 0x0000 }, /* R1375 */ - { 0x0000, 0x0000 }, /* R1376 */ - { 0x0000, 0x0000 }, /* R1377 */ - { 0x0000, 0x0000 }, /* R1378 */ - { 0x0000, 0x0000 }, /* R1379 */ - { 0x0000, 0x0000 }, /* R1380 */ - { 0x0000, 0x0000 }, /* R1381 */ - { 0x0000, 0x0000 }, /* R1382 */ - { 0x0000, 0x0000 }, /* R1383 */ - { 0x0000, 0x0000 }, /* R1384 */ - { 0x0000, 0x0000 }, /* R1385 */ - { 0x0000, 0x0000 }, /* R1386 */ - { 0x0000, 0x0000 }, /* R1387 */ - { 0x0000, 0x0000 }, /* R1388 */ - { 0x0000, 0x0000 }, /* R1389 */ - { 0x0000, 0x0000 }, /* R1390 */ - { 0x0000, 0x0000 }, /* R1391 */ - { 0x0000, 0x0000 }, /* R1392 */ - { 0x0000, 0x0000 }, /* R1393 */ - { 0x0000, 0x0000 }, /* R1394 */ - { 0x0000, 0x0000 }, /* R1395 */ - { 0x0000, 0x0000 }, /* R1396 */ - { 0x0000, 0x0000 }, /* R1397 */ - { 0x0000, 0x0000 }, /* R1398 */ - { 0x0000, 0x0000 }, /* R1399 */ - { 0x0000, 0x0000 }, /* R1400 */ - { 0x0000, 0x0000 }, /* R1401 */ - { 0x0000, 0x0000 }, /* R1402 */ - { 0x0000, 0x0000 }, /* R1403 */ - { 0x0000, 0x0000 }, /* R1404 */ - { 0x0000, 0x0000 }, /* R1405 */ - { 0x0000, 0x0000 }, /* R1406 */ - { 0x0000, 0x0000 }, /* R1407 */ - { 0xFFFF, 0xFFFF }, /* R1408 - AIF2 EQ Gains (1) */ - { 0xFFC0, 0xFFC0 }, /* R1409 - AIF2 EQ Gains (2) */ - { 0xFFFF, 0xFFFF }, /* R1410 - AIF2 EQ Band 1 A */ - { 0xFFFF, 0xFFFF }, /* R1411 - AIF2 EQ Band 1 B */ - { 0xFFFF, 0xFFFF }, /* R1412 - AIF2 EQ Band 1 PG */ - { 0xFFFF, 0xFFFF }, /* R1413 - AIF2 EQ Band 2 A */ - { 0xFFFF, 0xFFFF }, /* R1414 - AIF2 EQ Band 2 B */ - { 0xFFFF, 0xFFFF }, /* R1415 - AIF2 EQ Band 2 C */ - { 0xFFFF, 0xFFFF }, /* R1416 - AIF2 EQ Band 2 PG */ - { 0xFFFF, 0xFFFF }, /* R1417 - AIF2 EQ Band 3 A */ - { 0xFFFF, 0xFFFF }, /* R1418 - AIF2 EQ Band 3 B */ - { 0xFFFF, 0xFFFF }, /* R1419 - AIF2 EQ Band 3 C */ - { 0xFFFF, 0xFFFF }, /* R1420 - AIF2 EQ Band 3 PG */ - { 0xFFFF, 0xFFFF }, /* R1421 - AIF2 EQ Band 4 A */ - { 0xFFFF, 0xFFFF }, /* R1422 - AIF2 EQ Band 4 B */ - { 0xFFFF, 0xFFFF }, /* R1423 - AIF2 EQ Band 4 C */ - { 0xFFFF, 0xFFFF }, /* R1424 - AIF2 EQ Band 4 PG */ - { 0xFFFF, 0xFFFF }, /* R1425 - AIF2 EQ Band 5 A */ - { 0xFFFF, 0xFFFF }, /* R1426 - AIF2 EQ Band 5 B */ - { 0xFFFF, 0xFFFF }, /* R1427 - AIF2 EQ Band 5 PG */ - { 0x0000, 0x0000 }, /* R1428 */ - { 0x0000, 0x0000 }, /* R1429 */ - { 0x0000, 0x0000 }, /* R1430 */ - { 0x0000, 0x0000 }, /* R1431 */ - { 0x0000, 0x0000 }, /* R1432 */ - { 0x0000, 0x0000 }, /* R1433 */ - { 0x0000, 0x0000 }, /* R1434 */ - { 0x0000, 0x0000 }, /* R1435 */ - { 0x0000, 0x0000 }, /* R1436 */ - { 0x0000, 0x0000 }, /* R1437 */ - { 0x0000, 0x0000 }, /* R1438 */ - { 0x0000, 0x0000 }, /* R1439 */ - { 0x0000, 0x0000 }, /* R1440 */ - { 0x0000, 0x0000 }, /* R1441 */ - { 0x0000, 0x0000 }, /* R1442 */ - { 0x0000, 0x0000 }, /* R1443 */ - { 0x0000, 0x0000 }, /* R1444 */ - { 0x0000, 0x0000 }, /* R1445 */ - { 0x0000, 0x0000 }, /* R1446 */ - { 0x0000, 0x0000 }, /* R1447 */ - { 0x0000, 0x0000 }, /* R1448 */ - { 0x0000, 0x0000 }, /* R1449 */ - { 0x0000, 0x0000 }, /* R1450 */ - { 0x0000, 0x0000 }, /* R1451 */ - { 0x0000, 0x0000 }, /* R1452 */ - { 0x0000, 0x0000 }, /* R1453 */ - { 0x0000, 0x0000 }, /* R1454 */ - { 0x0000, 0x0000 }, /* R1455 */ - { 0x0000, 0x0000 }, /* R1456 */ - { 0x0000, 0x0000 }, /* R1457 */ - { 0x0000, 0x0000 }, /* R1458 */ - { 0x0000, 0x0000 }, /* R1459 */ - { 0x0000, 0x0000 }, /* R1460 */ - { 0x0000, 0x0000 }, /* R1461 */ - { 0x0000, 0x0000 }, /* R1462 */ - { 0x0000, 0x0000 }, /* R1463 */ - { 0x0000, 0x0000 }, /* R1464 */ - { 0x0000, 0x0000 }, /* R1465 */ - { 0x0000, 0x0000 }, /* R1466 */ - { 0x0000, 0x0000 }, /* R1467 */ - { 0x0000, 0x0000 }, /* R1468 */ - { 0x0000, 0x0000 }, /* R1469 */ - { 0x0000, 0x0000 }, /* R1470 */ - { 0x0000, 0x0000 }, /* R1471 */ - { 0x0000, 0x0000 }, /* R1472 */ - { 0x0000, 0x0000 }, /* R1473 */ - { 0x0000, 0x0000 }, /* R1474 */ - { 0x0000, 0x0000 }, /* R1475 */ - { 0x0000, 0x0000 }, /* R1476 */ - { 0x0000, 0x0000 }, /* R1477 */ - { 0x0000, 0x0000 }, /* R1478 */ - { 0x0000, 0x0000 }, /* R1479 */ - { 0x0000, 0x0000 }, /* R1480 */ - { 0x0000, 0x0000 }, /* R1481 */ - { 0x0000, 0x0000 }, /* R1482 */ - { 0x0000, 0x0000 }, /* R1483 */ - { 0x0000, 0x0000 }, /* R1484 */ - { 0x0000, 0x0000 }, /* R1485 */ - { 0x0000, 0x0000 }, /* R1486 */ - { 0x0000, 0x0000 }, /* R1487 */ - { 0x0000, 0x0000 }, /* R1488 */ - { 0x0000, 0x0000 }, /* R1489 */ - { 0x0000, 0x0000 }, /* R1490 */ - { 0x0000, 0x0000 }, /* R1491 */ - { 0x0000, 0x0000 }, /* R1492 */ - { 0x0000, 0x0000 }, /* R1493 */ - { 0x0000, 0x0000 }, /* R1494 */ - { 0x0000, 0x0000 }, /* R1495 */ - { 0x0000, 0x0000 }, /* R1496 */ - { 0x0000, 0x0000 }, /* R1497 */ - { 0x0000, 0x0000 }, /* R1498 */ - { 0x0000, 0x0000 }, /* R1499 */ - { 0x0000, 0x0000 }, /* R1500 */ - { 0x0000, 0x0000 }, /* R1501 */ - { 0x0000, 0x0000 }, /* R1502 */ - { 0x0000, 0x0000 }, /* R1503 */ - { 0x0000, 0x0000 }, /* R1504 */ - { 0x0000, 0x0000 }, /* R1505 */ - { 0x0000, 0x0000 }, /* R1506 */ - { 0x0000, 0x0000 }, /* R1507 */ - { 0x0000, 0x0000 }, /* R1508 */ - { 0x0000, 0x0000 }, /* R1509 */ - { 0x0000, 0x0000 }, /* R1510 */ - { 0x0000, 0x0000 }, /* R1511 */ - { 0x0000, 0x0000 }, /* R1512 */ - { 0x0000, 0x0000 }, /* R1513 */ - { 0x0000, 0x0000 }, /* R1514 */ - { 0x0000, 0x0000 }, /* R1515 */ - { 0x0000, 0x0000 }, /* R1516 */ - { 0x0000, 0x0000 }, /* R1517 */ - { 0x0000, 0x0000 }, /* R1518 */ - { 0x0000, 0x0000 }, /* R1519 */ - { 0x0000, 0x0000 }, /* R1520 */ - { 0x0000, 0x0000 }, /* R1521 */ - { 0x0000, 0x0000 }, /* R1522 */ - { 0x0000, 0x0000 }, /* R1523 */ - { 0x0000, 0x0000 }, /* R1524 */ - { 0x0000, 0x0000 }, /* R1525 */ - { 0x0000, 0x0000 }, /* R1526 */ - { 0x0000, 0x0000 }, /* R1527 */ - { 0x0000, 0x0000 }, /* R1528 */ - { 0x0000, 0x0000 }, /* R1529 */ - { 0x0000, 0x0000 }, /* R1530 */ - { 0x0000, 0x0000 }, /* R1531 */ - { 0x0000, 0x0000 }, /* R1532 */ - { 0x0000, 0x0000 }, /* R1533 */ - { 0x0000, 0x0000 }, /* R1534 */ - { 0x0000, 0x0000 }, /* R1535 */ - { 0x01EF, 0x01EF }, /* R1536 - DAC1 Mixer Volumes */ - { 0x0037, 0x0037 }, /* R1537 - DAC1 Left Mixer Routing */ - { 0x0037, 0x0037 }, /* R1538 - DAC1 Right Mixer Routing */ - { 0x01EF, 0x01EF }, /* R1539 - DAC2 Mixer Volumes */ - { 0x0037, 0x0037 }, /* R1540 - DAC2 Left Mixer Routing */ - { 0x0037, 0x0037 }, /* R1541 - DAC2 Right Mixer Routing */ - { 0x0003, 0x0003 }, /* R1542 - AIF1 ADC1 Left Mixer Routing */ - { 0x0003, 0x0003 }, /* R1543 - AIF1 ADC1 Right Mixer Routing */ - { 0x0003, 0x0003 }, /* R1544 - AIF1 ADC2 Left Mixer Routing */ - { 0x0003, 0x0003 }, /* R1545 - AIF1 ADC2 Right mixer Routing */ - { 0x0000, 0x0000 }, /* R1546 */ - { 0x0000, 0x0000 }, /* R1547 */ - { 0x0000, 0x0000 }, /* R1548 */ - { 0x0000, 0x0000 }, /* R1549 */ - { 0x0000, 0x0000 }, /* R1550 */ - { 0x0000, 0x0000 }, /* R1551 */ - { 0x02FF, 0x03FF }, /* R1552 - DAC1 Left Volume */ - { 0x02FF, 0x03FF }, /* R1553 - DAC1 Right Volume */ - { 0x02FF, 0x03FF }, /* R1554 - DAC2 Left Volume */ - { 0x02FF, 0x03FF }, /* R1555 - DAC2 Right Volume */ - { 0x0003, 0x0003 }, /* R1556 - DAC Softmute */ - { 0x0000, 0x0000 }, /* R1557 */ - { 0x0000, 0x0000 }, /* R1558 */ - { 0x0000, 0x0000 }, /* R1559 */ - { 0x0000, 0x0000 }, /* R1560 */ - { 0x0000, 0x0000 }, /* R1561 */ - { 0x0000, 0x0000 }, /* R1562 */ - { 0x0000, 0x0000 }, /* R1563 */ - { 0x0000, 0x0000 }, /* R1564 */ - { 0x0000, 0x0000 }, /* R1565 */ - { 0x0000, 0x0000 }, /* R1566 */ - { 0x0000, 0x0000 }, /* R1567 */ - { 0x0003, 0x0003 }, /* R1568 - Oversampling */ - { 0x03C3, 0x03C3 }, /* R1569 - Sidetone */ + unsigned int aif1clk_enable:1; + unsigned int aif2clk_enable:1; + + unsigned int aif1clk_disable:1; + unsigned int aif2clk_disable:1; }; -static int wm8994_readable(unsigned int reg) +static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8994_GPIO_1: @@ -1696,14 +136,14 @@ static int wm8994_readable(unsigned int reg) break; } - if (reg >= ARRAY_SIZE(access_masks)) + if (reg >= WM8994_CACHE_SIZE) return 0; - return access_masks[reg].readable != 0; + return wm8994_access_masks[reg].readable != 0; } -static int wm8994_volatile(unsigned int reg) +static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg) { - if (reg >= WM8994_REG_CACHE_SIZE) + if (reg >= WM8994_CACHE_SIZE) return 1; switch (reg) { @@ -1714,6 +154,8 @@ static int wm8994_volatile(unsigned int reg) case WM8994_RATE_STATUS: case WM8994_LDO_1: case WM8994_LDO_2: + case WM8958_DSP2_EXECCONTROL: + case WM8958_MIC_DETECT_3: return 1; default: return 0; @@ -1723,14 +165,16 @@ static int wm8994_volatile(unsigned int reg) static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + int ret; BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg)) - wm8994->reg_cache[reg] = value; - - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); + if (!wm8994_volatile(codec, reg)) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret != 0) + dev_err(codec->dev, "Cache write to %x failed: %d\n", + reg, ret); + } return wm8994_reg_write(codec->control_data, reg, value); } @@ -1738,14 +182,22 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg, static unsigned int wm8994_read(struct snd_soc_codec *codec, unsigned int reg) { - u16 *reg_cache = codec->reg_cache; + unsigned int val; + int ret; BUG_ON(reg > WM8994_MAX_REGISTER); - if (wm8994_volatile(reg)) - return wm8994_reg_read(codec->control_data, reg); - else - return reg_cache[reg]; + if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_read(codec, reg, &val); + if (ret >= 0) + return val; + else + dev_err(codec->dev, "Cache read from %x failed: %d\n", + reg, ret); + } + + return wm8994_reg_read(codec->control_data, reg); } static int configure_aif_clock(struct snd_soc_codec *codec, int aif) @@ -1837,7 +289,7 @@ static int configure_clock(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, new); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(&codec->dapm); return 0; } @@ -1864,6 +316,19 @@ static const char *sidetone_hpf_text[] = { static const struct soc_enum sidetone_hpf = SOC_ENUM_SINGLE(WM8994_SIDETONE, 7, 7, sidetone_hpf_text); +static const char *adc_hpf_text[] = { + "HiFi", "Voice 1", "Voice 2", "Voice 3" +}; + +static const struct soc_enum aif1adc1_hpf = + SOC_ENUM_SINGLE(WM8994_AIF1_ADC1_FILTERS, 13, 4, adc_hpf_text); + +static const struct soc_enum aif1adc2_hpf = + SOC_ENUM_SINGLE(WM8994_AIF1_ADC2_FILTERS, 13, 4, adc_hpf_text); + +static const struct soc_enum aif2adc_hpf = + SOC_ENUM_SINGLE(WM8994_AIF2_ADC_FILTERS, 13, 4, adc_hpf_text); + static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0); @@ -2063,7 +528,7 @@ static int wm8994_get_retune_mobile_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_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int block = wm8994_get_retune_mobile_block(kcontrol->id.name); ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block]; @@ -2071,21 +536,252 @@ static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, return 0; } -static const char *aifdac_src_text[] = { +static const char *aif_chan_src_text[] = { "Left", "Right" }; +static const struct soc_enum aif1adcl_src = + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_1, 15, 2, aif_chan_src_text); + +static const struct soc_enum aif1adcr_src = + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_1, 14, 2, aif_chan_src_text); + +static const struct soc_enum aif2adcl_src = + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_1, 15, 2, aif_chan_src_text); + +static const struct soc_enum aif2adcr_src = + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_1, 14, 2, aif_chan_src_text); + static const struct soc_enum aif1dacl_src = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, 15, 2, aifdac_src_text); + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, 15, 2, aif_chan_src_text); static const struct soc_enum aif1dacr_src = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, 14, 2, aifdac_src_text); + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, 14, 2, aif_chan_src_text); static const struct soc_enum aif2dacl_src = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 15, 2, aifdac_src_text); + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 15, 2, aif_chan_src_text); static const struct soc_enum aif2dacr_src = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aifdac_src_text); + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text); + +static const char *osr_text[] = { + "Low Power", "High Performance", +}; + +static const struct soc_enum dac_osr = + SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 0, 2, osr_text); + +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, @@ -2098,10 +794,15 @@ SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME, WM8994_AIF2_ADC_RIGHT_VOLUME, 1, 119, 0, digital_tlv), +SOC_ENUM("AIF1ADCL Source", aif1adcl_src), +SOC_ENUM("AIF1ADCR Source", aif1adcr_src), +SOC_ENUM("AIF2ADCL Source", aif2adcl_src), +SOC_ENUM("AIF2ADCR Source", aif2adcr_src), + SOC_ENUM("AIF1DACL Source", aif1dacl_src), SOC_ENUM("AIF1DACR Source", aif1dacr_src), -SOC_ENUM("AIF2DACL Source", aif1dacl_src), -SOC_ENUM("AIF2DACR Source", aif1dacr_src), +SOC_ENUM("AIF2DACL Source", aif2dacl_src), +SOC_ENUM("AIF2DACR Source", aif2dacr_src), SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME, WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), @@ -2140,6 +841,18 @@ SOC_SINGLE_TLV("DAC2 Left Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES, SOC_ENUM("Sidetone HPF Mux", sidetone_hpf), SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0), +SOC_ENUM("AIF1ADC1 HPF Mode", aif1adc1_hpf), +SOC_DOUBLE("AIF1ADC1 HPF Switch", WM8994_AIF1_ADC1_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf), +SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf), +SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("ADC OSR", adc_osr), +SOC_ENUM("DAC OSR", dac_osr), + SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME, WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME, @@ -2162,15 +875,15 @@ SOC_SINGLE_TLV("SPKR DAC1 Volume", WM8994_SPKMIXR_ATTENUATION, SOC_SINGLE_TLV("AIF1DAC1 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2, 10, 15, 0, wm8994_3d_tlv), -SOC_SINGLE("AIF1DAC1 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2, +SOC_SINGLE("AIF1DAC1 3D Stereo Switch", WM8994_AIF1_DAC1_FILTERS_2, 8, 1, 0), SOC_SINGLE_TLV("AIF1DAC2 3D Stereo Volume", WM8994_AIF1_DAC2_FILTERS_2, 10, 15, 0, wm8994_3d_tlv), SOC_SINGLE("AIF1DAC2 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2, 8, 1, 0), -SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2, +SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF2_DAC_FILTERS_2, 10, 15, 0, wm8994_3d_tlv), -SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2, +SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2, 8, 1, 0), }; @@ -2209,6 +922,13 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0, eq_tlv), }; +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, struct snd_kcontrol *kcontrol, int event) { @@ -2228,6 +948,7 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, static void wm8994_update_class_w(struct snd_soc_codec *codec) { + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int enable = 1; int source = 0; /* GCC flow analysis can't track enable */ int reg, reg_r; @@ -2278,14 +999,127 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) WM8994_CP_DYN_PWR | WM8994_CP_DYN_SRC_SEL_MASK, source | WM8994_CP_DYN_PWR); + wm8994->hubs.class_w = true; } else { dev_dbg(codec->dev, "Class W disabled\n"); snd_soc_update_bits(codec, WM8994_CLASS_W_1, WM8994_CP_DYN_PWR, 0); + wm8994->hubs.class_w = false; } } +static int late_enable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wm8994->aif1clk_enable) { + snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + wm8994->aif1clk_enable = 0; + } + if (wm8994->aif2clk_enable) { + snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, + WM8994_AIF2CLK_ENA); + wm8994->aif2clk_enable = 0; + } + break; + } + + return 0; +} + +static int late_disable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + if (wm8994->aif1clk_disable) { + snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, 0); + wm8994->aif1clk_disable = 0; + } + if (wm8994->aif2clk_disable) { + snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, 0); + wm8994->aif2clk_disable = 0; + } + break; + } + + return 0; +} + +static int aif1clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif1clk_enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + wm8994->aif1clk_disable = 1; + break; + } + + return 0; +} + +static int aif2clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif2clk_enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + wm8994->aif2clk_disable = 1; + break; + } + + return 0; +} + +static int adc_mux_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + +static int micbias_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + +static int dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int mask = 1 << w->shift; + + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, mask); + return 0; +} + static const char *hp_mux_text[] = { "Mixer", "DAC", @@ -2512,20 +1346,110 @@ static const struct snd_kcontrol_new aif2adc_mux = SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum); static const char *aif3adc_text[] = { - "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", + "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", "Mono PCM", }; -static const struct soc_enum aif3adc_enum = +static const struct soc_enum wm8994_aif3adc_enum = SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text); -static const struct snd_kcontrol_new aif3adc_mux = - SOC_DAPM_ENUM("AIF3ADC Mux", aif3adc_enum); +static const struct snd_kcontrol_new wm8994_aif3adc_mux = + SOC_DAPM_ENUM("AIF3ADC Mux", wm8994_aif3adc_enum); + +static const struct soc_enum wm8958_aif3adc_enum = + SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 4, aif3adc_text); + +static const struct snd_kcontrol_new wm8958_aif3adc_mux = + SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum); + +static const char *mono_pcm_out_text[] = { + "None", "AIF2ADCL", "AIF2ADCR", +}; + +static const struct soc_enum mono_pcm_out_enum = + SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 9, 3, mono_pcm_out_text); + +static const struct snd_kcontrol_new mono_pcm_out_mux = + SOC_DAPM_ENUM("Mono PCM Out Mux", mono_pcm_out_enum); + +static const char *aif2dac_src_text[] = { + "AIF2", "AIF3", +}; + +/* Note that these two control shouldn't be simultaneously switched to AIF3 */ +static const struct soc_enum aif2dacl_src_enum = + SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 7, 2, aif2dac_src_text); + +static const struct snd_kcontrol_new aif2dacl_src_mux = + SOC_DAPM_ENUM("AIF2DACL Mux", aif2dacl_src_enum); + +static const struct soc_enum aif2dacr_src_enum = + SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 8, 2, aif2dac_src_text); + +static const struct snd_kcontrol_new aif2dacr_src_mux = + SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); + +static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) +}; + +static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0) +}; + +static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { +SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = { +SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), +SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), +SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), +SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), +}; + +static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = { +SND_SOC_DAPM_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux, + adc_mux_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux, + adc_mux_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = { +SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), +SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), +}; static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC1DAT"), SND_SOC_DAPM_INPUT("DMIC2DAT"), SND_SOC_DAPM_INPUT("Clock"), +SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0), +SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -2533,26 +1457,27 @@ SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8994_CLOCKING_1, 3, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0), - -SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL, 0, WM8994_POWER_MANAGEMENT_4, 9, 0), -SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL, 0, WM8994_POWER_MANAGEMENT_4, 8, 0), -SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0, - WM8994_POWER_MANAGEMENT_5, 9, 0), -SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0, - WM8994_POWER_MANAGEMENT_5, 8, 0), - -SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture", +SND_SOC_DAPM_AIF_IN_E("AIF1DAC1L", NULL, 0, + WM8994_POWER_MANAGEMENT_5, 9, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC1R", NULL, 0, + WM8994_POWER_MANAGEMENT_5, 8, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", NULL, 0, WM8994_POWER_MANAGEMENT_4, 11, 0), -SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", NULL, 0, WM8994_POWER_MANAGEMENT_4, 10, 0), -SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0, - WM8994_POWER_MANAGEMENT_5, 11, 0), -SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0, - WM8994_POWER_MANAGEMENT_5, 10, 0), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC2L", NULL, 0, + WM8994_POWER_MANAGEMENT_5, 11, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC2R", NULL, 0, + WM8994_POWER_MANAGEMENT_5, 10, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)), @@ -2581,19 +1506,21 @@ SND_SOC_DAPM_AIF_OUT("AIF2ADCL", NULL, 0, WM8994_POWER_MANAGEMENT_4, 13, 0), SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0, WM8994_POWER_MANAGEMENT_4, 12, 0), -SND_SOC_DAPM_AIF_IN("AIF2DACL", NULL, 0, - WM8994_POWER_MANAGEMENT_5, 13, 0), -SND_SOC_DAPM_AIF_IN("AIF2DACR", NULL, 0, - WM8994_POWER_MANAGEMENT_5, 12, 0), +SND_SOC_DAPM_AIF_IN_E("AIF2DACL", NULL, 0, + WM8994_POWER_MANAGEMENT_5, 13, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0, + WM8994_POWER_MANAGEMENT_5, 12, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux), SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux), SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux), -SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &aif3adc_mux), SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), @@ -2612,14 +1539,6 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), -SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), - -SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), -SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), -SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), -SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), - SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), @@ -2631,8 +1550,18 @@ SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, SND_SOC_DAPM_POST("Debug log", post_ev), }; -static const struct snd_soc_dapm_route intercon[] = { +static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = { +SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux), +}; + +static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux), +SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux), +SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux), +SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8958_aif3adc_mux), +}; +static const struct snd_soc_dapm_route intercon[] = { { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys }, { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys }, @@ -2740,9 +1669,6 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, - { "AIF2DACL", NULL, "AIF2DAC Mux" }, - { "AIF2DACR", NULL, "AIF2DAC Mux" }, - { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, @@ -2752,14 +1678,12 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" }, /* DAC1 inputs */ - { "DAC1L", NULL, "DAC1L Mixer" }, { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" }, { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, - { "DAC1R", NULL, "DAC1R Mixer" }, { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" }, { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, @@ -2768,7 +1692,6 @@ static const struct snd_soc_dapm_route intercon[] = { /* DAC2/AIF2 outputs */ { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" }, - { "DAC2L", NULL, "AIF2DAC2L Mixer" }, { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" }, { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, @@ -2776,13 +1699,17 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" }, - { "DAC2R", NULL, "AIF2DAC2R Mixer" }, { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" }, { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, { "AIF2DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" }, { "AIF2DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + { "AIF1ADCDAT", NULL, "AIF1ADC1L" }, + { "AIF1ADCDAT", NULL, "AIF1ADC1R" }, + { "AIF1ADCDAT", NULL, "AIF1ADC2L" }, + { "AIF1ADCDAT", NULL, "AIF1ADC2R" }, + { "AIF2ADCDAT", NULL, "AIF2ADC Mux" }, /* AIF3 output */ @@ -2815,6 +1742,53 @@ static const struct snd_soc_dapm_route intercon[] = { { "Right Headphone Mux", "DAC", "DAC1R" }, }; +static const struct snd_soc_dapm_route wm8994_lateclk_revd_intercon[] = { + { "DAC1L", NULL, "Late DAC1L Enable PGA" }, + { "Late DAC1L Enable PGA", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "Late DAC1R Enable PGA" }, + { "Late DAC1R Enable PGA", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "Late DAC2L Enable PGA" }, + { "Late DAC2L Enable PGA", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "Late DAC2R Enable PGA" }, + { "Late DAC2R Enable PGA", NULL, "AIF2DAC2R Mixer" } +}; + +static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "AIF2DAC2R Mixer" }, +}; + +static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { + { "AIF1DACDAT", NULL, "AIF2DACDAT" }, + { "AIF2DACDAT", NULL, "AIF1DACDAT" }, + { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, + { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, + { "MICBIAS", NULL, "CLK_SYS" }, + { "MICBIAS", NULL, "MICBIAS Supply" }, +}; + +static const struct snd_soc_dapm_route wm8994_intercon[] = { + { "AIF2DACL", NULL, "AIF2DAC Mux" }, + { "AIF2DACR", NULL, "AIF2DAC Mux" }, +}; + +static const struct snd_soc_dapm_route wm8958_intercon[] = { + { "AIF2DACL", NULL, "AIF2DACL Mux" }, + { "AIF2DACR", NULL, "AIF2DACR Mux" }, + + { "AIF2DACL Mux", "AIF2", "AIF2DAC Mux" }, + { "AIF2DACL Mux", "AIF3", "AIF3DACDAT" }, + { "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" }, + { "AIF2DACR Mux", "AIF3", "AIF3DACDAT" }, + + { "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" }, + { "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" }, + + { "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" }, +}; + /* The size in bits of the FLL divide multiplied by 10 * to allow rounding later */ #define FIXED_FLL_SIZE ((1 << 16) * 10) @@ -2930,6 +1904,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, /* Allow no source specification when stopping */ if (freq_out) return -EINVAL; + src = wm8994->fll[id].src; break; case WM8994_FLL_SRC_MCLK1: case WM8994_FLL_SRC_MCLK2: @@ -3094,6 +2069,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, static int wm8994_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8994 *control = codec->control_data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); switch (level) { @@ -3107,16 +2083,36 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { - /* Tweak DC servo and DSP configuration for - * improved performance. */ - if (wm8994->revision < 4) { - /* Tweak DC servo and DSP configuration for - * improved performance. */ - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0x56, 0x3); - snd_soc_write(codec, 0x817, 0); - snd_soc_write(codec, 0x102, 0); + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + pm_runtime_get_sync(codec->dev); + + switch (control->type) { + case WM8994: + if (wm8994->revision < 4) { + /* Tweak DC servo and DSP + * configuration for improved + * performance. */ + snd_soc_write(codec, 0x102, 0x3); + snd_soc_write(codec, 0x56, 0x3); + snd_soc_write(codec, 0x817, 0); + snd_soc_write(codec, 0x102, 0); + } + break; + + case WM8958: + if (wm8994->revision == 0) { + /* Optimise performance for rev A */ + snd_soc_write(codec, 0x102, 0x3); + snd_soc_write(codec, 0xcb, 0x81); + snd_soc_write(codec, 0x817, 0); + snd_soc_write(codec, 0x102, 0); + + snd_soc_update_bits(codec, + WM8958_CHARGE_PUMP_2, + WM8958_CP_DISCH, + WM8958_CP_DISCH); + } + break; } /* Discharge LINEOUT1 & 2 */ @@ -3151,7 +2147,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: - if (codec->bias_level == SND_SOC_BIAS_STANDBY) { + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { /* Switch over to startup biases */ snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM8994_BIAS_SRC | @@ -3183,16 +2179,19 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, WM8994_STARTUP_BIAS_ENA | WM8994_VMID_BUF_ENA | WM8994_VMID_RAMP_MASK, 0); + + pm_runtime_put(codec->dev); } break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; + struct wm8994 *control = codec->control_data; int ms_reg; int aif1_reg; int ms = 0; @@ -3277,6 +2276,13 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } + /* The AIF2 format configuration needs to be mirrored to AIF3 + * on WM8958 if it's in use so just do it all the time. */ + if (control->type == WM8958 && dai->id == 2) + snd_soc_update_bits(codec, WM8958_AIF3_CONTROL_1, + WM8994_AIF1_LRCLK_INV | + WM8958_AIF3_FMT_MASK, aif1); + snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV | WM8994_AIF1_FMT_MASK, @@ -3317,12 +2323,15 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; + struct wm8994 *control = codec->control_data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int aif1_reg; + int aif2_reg; int bclk_reg; int lrclk_reg; int rate_reg; int aif1 = 0; + int aif2 = 0; int bclk = 0; int lrclk = 0; int rate_val = 0; @@ -3333,6 +2342,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, switch (dai->id) { case 1: aif1_reg = WM8994_AIF1_CONTROL_1; + aif2_reg = WM8994_AIF1_CONTROL_2; bclk_reg = WM8994_AIF1_BCLK; rate_reg = WM8994_AIF1_RATE; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || @@ -3345,6 +2355,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, break; case 2: aif1_reg = WM8994_AIF2_CONTROL_1; + aif2_reg = WM8994_AIF2_CONTROL_2; bclk_reg = WM8994_AIF2_BCLK; rate_reg = WM8994_AIF2_RATE; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || @@ -3355,6 +2366,14 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, dev_dbg(codec->dev, "AIF2 using split LRCLK\n"); } break; + case 3: + switch (control->type) { + case WM8958: + aif1_reg = WM8958_AIF3_CONTROL_1; + break; + default: + return 0; + } default: return -EINVAL; } @@ -3392,6 +2411,10 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n", dai->id, wm8994->aifclk[id], bclk_rate); + if (params_channels(params) == 1 && + (snd_soc_read(codec, aif1_reg) & 0x18) == 0x18) + aif2 |= WM8994_AIF1_MONO; + if (wm8994->aifclk[id] == 0) { dev_err(dai->dev, "AIF%dCLK not configured\n", dai->id); return -EINVAL; @@ -3435,6 +2458,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, lrclk, bclk_rate / lrclk); snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); + snd_soc_update_bits(codec, aif2_reg, WM8994_AIF1_MONO, aif2); snd_soc_update_bits(codec, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); snd_soc_update_bits(codec, lrclk_reg, WM8994_AIF1DAC_RATE_MASK, lrclk); @@ -3458,6 +2482,47 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, return 0; } +static int wm8994_aif3_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 wm8994 *control = codec->control_data; + int aif1_reg; + int aif1 = 0; + + switch (dai->id) { + case 3: + switch (control->type) { + case WM8958: + aif1_reg = WM8958_AIF3_CONTROL_1; + break; + default: + return 0; + } + default: + return 0; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + aif1 |= 0x20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + aif1 |= 0x40; + break; + case SNDRV_PCM_FORMAT_S32_LE: + aif1 |= 0x60; + break; + default: + return -EINVAL; + } + + return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); +} + static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; @@ -3512,7 +2577,7 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) else val = 0; - return snd_soc_update_bits(codec, reg, mask, reg); + return snd_soc_update_bits(codec, reg, mask, val); } #define WM8994_RATES SNDRV_PCM_RATE_8000_96000 @@ -3539,6 +2604,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = { }; static struct snd_soc_dai_ops wm8994_aif3_dai_ops = { + .hw_params = wm8994_aif3_hw_params, .set_tristate = wm8994_set_tristate, }; @@ -3548,14 +2614,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 1, .playback = { .stream_name = "AIF1 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, .capture = { .stream_name = "AIF1 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -3567,14 +2633,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 2, .playback = { .stream_name = "AIF2 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, .capture = { .stream_name = "AIF2 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -3586,14 +2652,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 3, .playback = { .stream_name = "AIF3 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, .capture = { .stream_name = "AIF3 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -3625,26 +2691,28 @@ 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); - u16 *reg_cache = codec->reg_cache; int i, ret; + unsigned int val, mask; + + if (wm8994->revision < 4) { + /* force a HW read */ + val = wm8994_reg_read(codec->control_data, + WM8994_POWER_MANAGEMENT_5); + + /* modify the cache only */ + codec->cache_only = 1; + mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | + WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; + val &= mask; + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, val); + codec->cache_only = 0; + } /* Restore the registers */ - for (i = 1; i < ARRAY_SIZE(wm8994->reg_cache); i++) { - switch (i) { - case WM8994_LDO_1: - case WM8994_LDO_2: - case WM8994_SOFTWARE_RESET: - /* Handled by other MFD drivers */ - continue; - default: - break; - } - - if (!access_masks[i].writable) - continue; - - wm8994_reg_write(codec->control_data, i, reg_cache[i]); - } + ret = snd_soc_cache_sync(codec); + if (ret != 0) + dev_err(codec->dev, "Failed to sync cache: %d\n", ret); wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -3794,11 +2862,46 @@ 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 snd_soc_add_controls(wm8994->codec, wm8994_eq_controls, ARRAY_SIZE(wm8994_eq_controls)); + + for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { + if (pdata->micbias[i]) { + snd_soc_write(codec, WM8958_MICBIAS1 + i, + pdata->micbias[i] & 0xffff); + } + } } /** @@ -3823,8 +2926,12 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_micdet *micdet; + struct wm8994 *control = codec->control_data; int reg; + if (control->type != WM8994) + return -EINVAL; + switch (micbias) { case 1: micdet = &wm8994->micdet[0]; @@ -3863,6 +2970,10 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) int reg; int report; +#ifndef CONFIG_SND_SOC_WM8994_MODULE + trace_snd_soc_jack_irq(dev_name(codec->dev)); +#endif + reg = snd_soc_read(codec, WM8994_INTERRUPT_RAW_STATUS_2); if (reg < 0) { dev_err(codec->dev, "Failed to read microphone status: %d\n", @@ -3891,77 +3002,233 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) return IRQ_HANDLED; } +/* Default microphone detection handler for WM8958 - the user can + * override this if they wish. + */ +static void wm8958_default_micdet(u16 status, void *data) +{ + struct snd_soc_codec *codec = data; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + int report = 0; + + /* If nothing present then clear our statuses */ + if (!(status & WM8958_MICD_STS)) + goto done; + + report = SND_JACK_MICROPHONE; + + /* Everything else is buttons; just assign slots */ + if (status & 0x1c0) + report |= SND_JACK_BTN_0; + +done: + snd_soc_jack_report(wm8994->micdet[0].jack, report, + SND_JACK_BTN_0 | SND_JACK_MICROPHONE); +} + +/** + * wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ + * + * @codec: WM8958 codec + * @jack: jack to report detection events on + * + * Enable microphone detection functionality for the WM8958. By + * default simple detection which supports the detection of up to 6 + * buttons plus video and microphone functionality is supported. + * + * The WM8958 has an advanced jack detection facility which is able to + * support complex accessory detection, especially when used in + * conjunction with external circuitry. In order to provide maximum + * flexiblity a callback is provided which allows a completely custom + * detection algorithm. + */ +int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, + wm8958_micdet_cb cb, void *cb_data) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = codec->control_data; + + if (control->type != WM8958) + return -EINVAL; + + if (jack) { + if (!cb) { + dev_dbg(codec->dev, "Using default micdet callback\n"); + cb = wm8958_default_micdet; + cb_data = codec; + } + + wm8994->micdet[0].jack = jack; + wm8994->jack_cb = cb; + wm8994->jack_cb_data = cb_data; + + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + } else { + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, 0); + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8958_mic_detect); + +static irqreturn_t wm8958_mic_irq(int irq, void *data) +{ + struct wm8994_priv *wm8994 = data; + struct snd_soc_codec *codec = wm8994->codec; + int reg; + + reg = snd_soc_read(codec, WM8958_MIC_DETECT_3); + if (reg < 0) { + dev_err(codec->dev, "Failed to read mic detect status: %d\n", + reg); + return IRQ_NONE; + } + + if (!(reg & WM8958_MICD_VALID)) { + dev_dbg(codec->dev, "Mic detect data not valid\n"); + goto out; + } + +#ifndef CONFIG_SND_SOC_WM8994_MODULE + trace_snd_soc_jack_irq(dev_name(codec->dev)); +#endif + + if (wm8994->jack_cb) + wm8994->jack_cb(reg, wm8994->jack_cb_data); + else + dev_warn(codec->dev, "Accessory detection with no callback\n"); + +out: + return IRQ_HANDLED; +} + static int wm8994_codec_probe(struct snd_soc_codec *codec) { + struct wm8994 *control; struct wm8994_priv *wm8994; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret, i; codec->control_data = dev_get_drvdata(codec->dev->parent); + control = codec->control_data; wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL); if (wm8994 == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, wm8994); - codec->reg_cache = &wm8994->reg_cache; - wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; - /* Fill the cache with physical values we inherited; don't reset */ - ret = wm8994_bulk_read(codec->control_data, 0, - ARRAY_SIZE(wm8994->reg_cache) - 1, - codec->reg_cache); - if (ret < 0) { - dev_err(codec->dev, "Failed to fill register cache: %d\n", - ret); - goto err; - } + if (wm8994->pdata && wm8994->pdata->micdet_irq) + wm8994->micdet_irq = wm8994->pdata->micdet_irq; + else if (wm8994->pdata && wm8994->pdata->irq_base) + wm8994->micdet_irq = wm8994->pdata->irq_base + + WM8994_IRQ_MIC1_DET; - /* Clear the cached values for unreadable/volatile registers to - * avoid potential confusion. - */ - for (i = 0; i < ARRAY_SIZE(wm8994->reg_cache); i++) - if (wm8994_volatile(i) || !wm8994_readable(i)) - wm8994->reg_cache[i] = 0; + pm_runtime_enable(codec->dev); + pm_runtime_resume(codec->dev); + + /* Read our current status back from the chip - we don't want to + * reset as this may interfere with the GPIO or LDO operation. */ + for (i = 0; i < WM8994_CACHE_SIZE; i++) { + if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i)) + continue; + + ret = wm8994_reg_read(codec->control_data, i); + if (ret <= 0) + continue; + + ret = snd_soc_cache_write(codec, i, ret); + if (ret != 0) { + dev_err(codec->dev, + "Failed to initialise cache for 0x%x: %d\n", + i, ret); + goto err; + } + } /* Set revision-specific configuration */ wm8994->revision = snd_soc_read(codec, WM8994_CHIP_REVISION); - switch (wm8994->revision) { - case 2: - case 3: - wm8994->hubs.dcs_codes = -5; - wm8994->hubs.hp_startup_mode = 1; + switch (control->type) { + case WM8994: + switch (wm8994->revision) { + case 2: + case 3: + wm8994->hubs.dcs_codes = -5; + wm8994->hubs.hp_startup_mode = 1; + wm8994->hubs.dcs_readback_mode = 1; + break; + default: + wm8994->hubs.dcs_readback_mode = 1; + break; + } + + case WM8958: wm8994->hubs.dcs_readback_mode = 1; break; + default: - wm8994->hubs.dcs_readback_mode = 1; break; } - ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_DET, - wm8994_mic_irq, "Mic 1 detect", wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 detect IRQ: %d\n", ret); - - ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, - wm8994_mic_irq, "Mic 1 short", wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 short IRQ: %d\n", ret); + switch (control->type) { + case WM8994: + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8994_mic_irq, + IRQF_TRIGGER_RISING, + "Mic1 detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + } - ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC2_DET, - wm8994_mic_irq, "Mic 2 detect", wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic2 detect IRQ: %d\n", ret); + ret = wm8994_request_irq(codec->control_data, + WM8994_IRQ_MIC1_SHRT, + wm8994_mic_irq, "Mic 1 short", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 short IRQ: %d\n", + ret); + + ret = wm8994_request_irq(codec->control_data, + WM8994_IRQ_MIC2_DET, + wm8994_mic_irq, "Mic 2 detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic2 detect IRQ: %d\n", + ret); + + ret = wm8994_request_irq(codec->control_data, + WM8994_IRQ_MIC2_SHRT, + wm8994_mic_irq, "Mic 2 short", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic2 short IRQ: %d\n", + ret); + break; - ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, - wm8994_mic_irq, "Mic 2 short", wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic2 short IRQ: %d\n", ret); + case WM8958: + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8958_mic_irq, + IRQF_TRIGGER_RISING, + "Mic detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + } + } /* Remember if AIFnLRCLK is configured as a GPIO. This should be * configured on init - if a system wants to do this dynamically @@ -3994,20 +3261,36 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Latch volume updates (right only; we always do left then right). */ + snd_soc_update_bits(codec, WM8994_AIF1_DAC1_LEFT_VOLUME, + WM8994_AIF1DAC1_VU, WM8994_AIF1DAC1_VU); snd_soc_update_bits(codec, WM8994_AIF1_DAC1_RIGHT_VOLUME, WM8994_AIF1DAC1_VU, WM8994_AIF1DAC1_VU); + snd_soc_update_bits(codec, WM8994_AIF1_DAC2_LEFT_VOLUME, + WM8994_AIF1DAC2_VU, WM8994_AIF1DAC2_VU); snd_soc_update_bits(codec, WM8994_AIF1_DAC2_RIGHT_VOLUME, WM8994_AIF1DAC2_VU, WM8994_AIF1DAC2_VU); + snd_soc_update_bits(codec, WM8994_AIF2_DAC_LEFT_VOLUME, + WM8994_AIF2DAC_VU, WM8994_AIF2DAC_VU); snd_soc_update_bits(codec, WM8994_AIF2_DAC_RIGHT_VOLUME, WM8994_AIF2DAC_VU, WM8994_AIF2DAC_VU); + snd_soc_update_bits(codec, WM8994_AIF1_ADC1_LEFT_VOLUME, + WM8994_AIF1ADC1_VU, WM8994_AIF1ADC1_VU); snd_soc_update_bits(codec, WM8994_AIF1_ADC1_RIGHT_VOLUME, WM8994_AIF1ADC1_VU, WM8994_AIF1ADC1_VU); + snd_soc_update_bits(codec, WM8994_AIF1_ADC2_LEFT_VOLUME, + WM8994_AIF1ADC2_VU, WM8994_AIF1ADC2_VU); snd_soc_update_bits(codec, WM8994_AIF1_ADC2_RIGHT_VOLUME, WM8994_AIF1ADC2_VU, WM8994_AIF1ADC2_VU); + snd_soc_update_bits(codec, WM8994_AIF2_ADC_LEFT_VOLUME, + WM8994_AIF2ADC_VU, WM8994_AIF1ADC2_VU); snd_soc_update_bits(codec, WM8994_AIF2_ADC_RIGHT_VOLUME, WM8994_AIF2ADC_VU, WM8994_AIF1ADC2_VU); + snd_soc_update_bits(codec, WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU, WM8994_DAC1_VU); snd_soc_update_bits(codec, WM8994_DAC1_RIGHT_VOLUME, WM8994_DAC1_VU, WM8994_DAC1_VU); + snd_soc_update_bits(codec, WM8994_DAC2_LEFT_VOLUME, + WM8994_DAC2_VU, WM8994_DAC2_VU); snd_soc_update_bits(codec, WM8994_DAC2_RIGHT_VOLUME, WM8994_DAC2_VU, WM8994_DAC2_VU); @@ -4034,10 +3317,69 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm_hubs_add_analogue_controls(codec); snd_soc_add_controls(codec, wm8994_snd_controls, ARRAY_SIZE(wm8994_snd_controls)); - snd_soc_dapm_new_controls(codec, wm8994_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets, ARRAY_SIZE(wm8994_dapm_widgets)); + + switch (control->type) { + case WM8994: + snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, + ARRAY_SIZE(wm8994_specific_dapm_widgets)); + if (wm8994->revision < 4) { + 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; + 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)); + break; + } + + wm_hubs_add_analogue_routes(codec, 0, 0); - snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + switch (control->type) { + case WM8994: + snd_soc_dapm_add_routes(dapm, wm8994_intercon, + ARRAY_SIZE(wm8994_intercon)); + + if (wm8994->revision < 4) { + 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)); + } + 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)); + break; + } return 0; @@ -4045,7 +3387,8 @@ err_irq: wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); err: kfree(wm8994); return ret; @@ -4054,13 +3397,29 @@ err: static int wm8994_codec_remove(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = codec->control_data; wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); + pm_runtime_disable(codec->dev); + + switch (control->type) { + case WM8994: + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); + wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, + wm8994); + wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, + wm8994); + wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, + wm8994); + break; + + case WM8958: + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); + break; + } kfree(wm8994->retune_mobile_texts); kfree(wm8994->drc_texts); kfree(wm8994); @@ -4073,11 +3432,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8994 = { .remove = wm8994_codec_remove, .suspend = wm8994_suspend, .resume = wm8994_resume, - .read = wm8994_read, - .write = wm8994_write, + .read = wm8994_read, + .write = wm8994_write, .readable_register = wm8994_readable, .volatile_register = wm8994_volatile, .set_bias_level = wm8994_set_bias_level, + + .reg_cache_size = WM8994_CACHE_SIZE, + .reg_cache_default = wm8994_reg_defaults, + .reg_word_size = 2, + .compress_type = SND_SOC_RBTREE_COMPRESSION, }; static int __devinit wm8994_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index d8dce260c430..999b8851226b 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -28,7 +28,21 @@ #define WM8994_FLL_SRC_LRCLK 3 #define WM8994_FLL_SRC_BCLK 4 +typedef void (*wm8958_micdet_cb)(u16 status, void *data); + int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int micbias, int det, int shrt); +int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, + wm8958_micdet_cb cb, void *cb_data); + +#define WM8994_CACHE_SIZE 1570 + +struct wm8994_access_mask { + unsigned short readable; /* Mask of readable bits */ + unsigned short writable; /* Mask of writable bits */ +}; + +extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE]; +extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; #endif diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c new file mode 100644 index 000000000000..67eaaecbb42e --- /dev/null +++ b/sound/soc/codecs/wm8995.c @@ -0,0 +1,1911 @@ +/* + * wm8995.c -- WM8995 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> + * + * Based on wm8994.c and wm_hubs.c by Mark Brown + * + * 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/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8995.h" + +#define WM8995_NUM_SUPPLIES 8 +static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD1", + "DBVDD2", + "DBVDD3", + "AVDD1", + "AVDD2", + "CPVDD", + "MICVDD" +}; + +static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = { + [0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b, + [24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0, + [28] = 0x000f, [32] = 0x0005, [33] = 0x0005, [40] = 0x0003, + [41] = 0x0013, [48] = 0x0004, [56] = 0x09f8, [64] = 0x1f25, + [69] = 0x0004, [82] = 0xaaaa, [84] = 0x2a2a, [146] = 0x0060, + [256] = 0x0002, [257] = 0x8004, [520] = 0x0010, [528] = 0x0083, + [529] = 0x0083, [548] = 0x0c80, [580] = 0x0c80, [768] = 0x4050, + [769] = 0x4000, [771] = 0x0040, [772] = 0x0040, [773] = 0x0040, + [774] = 0x0004, [775] = 0x0100, [784] = 0x4050, [785] = 0x4000, + [787] = 0x0040, [788] = 0x0040, [789] = 0x0040, [1024] = 0x00c0, + [1025] = 0x00c0, [1026] = 0x00c0, [1027] = 0x00c0, [1028] = 0x00c0, + [1029] = 0x00c0, [1030] = 0x00c0, [1031] = 0x00c0, [1056] = 0x0200, + [1057] = 0x0010, [1058] = 0x0200, [1059] = 0x0010, [1088] = 0x0098, + [1089] = 0x0845, [1104] = 0x0098, [1105] = 0x0845, [1152] = 0x6318, + [1153] = 0x6300, [1154] = 0x0fca, [1155] = 0x0400, [1156] = 0x00d8, + [1157] = 0x1eb5, [1158] = 0xf145, [1159] = 0x0b75, [1160] = 0x01c5, + [1161] = 0x1c58, [1162] = 0xf373, [1163] = 0x0a54, [1164] = 0x0558, + [1165] = 0x168e, [1166] = 0xf829, [1167] = 0x07ad, [1168] = 0x1103, + [1169] = 0x0564, [1170] = 0x0559, [1171] = 0x4000, [1184] = 0x6318, + [1185] = 0x6300, [1186] = 0x0fca, [1187] = 0x0400, [1188] = 0x00d8, + [1189] = 0x1eb5, [1190] = 0xf145, [1191] = 0x0b75, [1192] = 0x01c5, + [1193] = 0x1c58, [1194] = 0xf373, [1195] = 0x0a54, [1196] = 0x0558, + [1197] = 0x168e, [1198] = 0xf829, [1199] = 0x07ad, [1200] = 0x1103, + [1201] = 0x0564, [1202] = 0x0559, [1203] = 0x4000, [1280] = 0x00c0, + [1281] = 0x00c0, [1282] = 0x00c0, [1283] = 0x00c0, [1312] = 0x0200, + [1313] = 0x0010, [1344] = 0x0098, [1345] = 0x0845, [1408] = 0x6318, + [1409] = 0x6300, [1410] = 0x0fca, [1411] = 0x0400, [1412] = 0x00d8, + [1413] = 0x1eb5, [1414] = 0xf145, [1415] = 0x0b75, [1416] = 0x01c5, + [1417] = 0x1c58, [1418] = 0xf373, [1419] = 0x0a54, [1420] = 0x0558, + [1421] = 0x168e, [1422] = 0xf829, [1423] = 0x07ad, [1424] = 0x1103, + [1425] = 0x0564, [1426] = 0x0559, [1427] = 0x4000, [1568] = 0x0002, + [1792] = 0xa100, [1793] = 0xa101, [1794] = 0xa101, [1795] = 0xa101, + [1796] = 0xa101, [1797] = 0xa101, [1798] = 0xa101, [1799] = 0xa101, + [1800] = 0xa101, [1801] = 0xa101, [1802] = 0xa101, [1803] = 0xa101, + [1804] = 0xa101, [1805] = 0xa101, [1825] = 0x0055, [1848] = 0x3fff, + [1849] = 0x1fff, [2049] = 0x0001, [2050] = 0x0069, [2056] = 0x0002, + [2057] = 0x0003, [2058] = 0x0069, [12288] = 0x0001, [12289] = 0x0001, + [12291] = 0x0006, [12292] = 0x0040, [12293] = 0x0001, [12294] = 0x000f, + [12295] = 0x0006, [12296] = 0x0001, [12297] = 0x0003, [12298] = 0x0104, + [12300] = 0x0060, [12301] = 0x0011, [12302] = 0x0401, [12304] = 0x0050, + [12305] = 0x0003, [12306] = 0x0100, [12308] = 0x0051, [12309] = 0x0003, + [12310] = 0x0104, [12311] = 0x000a, [12312] = 0x0060, [12313] = 0x003b, + [12314] = 0x0502, [12315] = 0x0100, [12316] = 0x2fff, [12320] = 0x2fff, + [12324] = 0x2fff, [12328] = 0x2fff, [12332] = 0x2fff, [12336] = 0x2fff, + [12340] = 0x2fff, [12344] = 0x2fff, [12348] = 0x2fff, [12352] = 0x0001, + [12353] = 0x0001, [12355] = 0x0006, [12356] = 0x0040, [12357] = 0x0001, + [12358] = 0x000f, [12359] = 0x0006, [12360] = 0x0001, [12361] = 0x0003, + [12362] = 0x0104, [12364] = 0x0060, [12365] = 0x0011, [12366] = 0x0401, + [12368] = 0x0050, [12369] = 0x0003, [12370] = 0x0100, [12372] = 0x0060, + [12373] = 0x003b, [12374] = 0x0502, [12375] = 0x0100, [12376] = 0x2fff, + [12380] = 0x2fff, [12384] = 0x2fff, [12388] = 0x2fff, [12392] = 0x2fff, + [12396] = 0x2fff, [12400] = 0x2fff, [12404] = 0x2fff, [12408] = 0x2fff, + [12412] = 0x2fff, [12416] = 0x0001, [12417] = 0x0001, [12419] = 0x0006, + [12420] = 0x0040, [12421] = 0x0001, [12422] = 0x000f, [12423] = 0x0006, + [12424] = 0x0001, [12425] = 0x0003, [12426] = 0x0106, [12428] = 0x0061, + [12429] = 0x0011, [12430] = 0x0401, [12432] = 0x0050, [12433] = 0x0003, + [12434] = 0x0102, [12436] = 0x0051, [12437] = 0x0003, [12438] = 0x0106, + [12439] = 0x000a, [12440] = 0x0061, [12441] = 0x003b, [12442] = 0x0502, + [12443] = 0x0100, [12444] = 0x2fff, [12448] = 0x2fff, [12452] = 0x2fff, + [12456] = 0x2fff, [12460] = 0x2fff, [12464] = 0x2fff, [12468] = 0x2fff, + [12472] = 0x2fff, [12476] = 0x2fff, [12480] = 0x0001, [12481] = 0x0001, + [12483] = 0x0006, [12484] = 0x0040, [12485] = 0x0001, [12486] = 0x000f, + [12487] = 0x0006, [12488] = 0x0001, [12489] = 0x0003, [12490] = 0x0106, + [12492] = 0x0061, [12493] = 0x0011, [12494] = 0x0401, [12496] = 0x0050, + [12497] = 0x0003, [12498] = 0x0102, [12500] = 0x0061, [12501] = 0x003b, + [12502] = 0x0502, [12503] = 0x0100, [12504] = 0x2fff, [12508] = 0x2fff, + [12512] = 0x2fff, [12516] = 0x2fff, [12520] = 0x2fff, [12524] = 0x2fff, + [12528] = 0x2fff, [12532] = 0x2fff, [12536] = 0x2fff, [12540] = 0x2fff, + [12544] = 0x0060, [12546] = 0x0601, [12548] = 0x0050, [12550] = 0x0100, + [12552] = 0x0001, [12554] = 0x0104, [12555] = 0x0100, [12556] = 0x2fff, + [12560] = 0x2fff, [12564] = 0x2fff, [12568] = 0x2fff, [12572] = 0x2fff, + [12576] = 0x2fff, [12580] = 0x2fff, [12584] = 0x2fff, [12588] = 0x2fff, + [12592] = 0x2fff, [12596] = 0x2fff, [12600] = 0x2fff, [12604] = 0x2fff, + [12608] = 0x0061, [12610] = 0x0601, [12612] = 0x0050, [12614] = 0x0102, + [12616] = 0x0001, [12618] = 0x0106, [12619] = 0x0100, [12620] = 0x2fff, + [12624] = 0x2fff, [12628] = 0x2fff, [12632] = 0x2fff, [12636] = 0x2fff, + [12640] = 0x2fff, [12644] = 0x2fff, [12648] = 0x2fff, [12652] = 0x2fff, + [12656] = 0x2fff, [12660] = 0x2fff, [12664] = 0x2fff, [12668] = 0x2fff, + [12672] = 0x0060, [12674] = 0x0601, [12676] = 0x0061, [12678] = 0x0601, + [12680] = 0x0050, [12682] = 0x0300, [12684] = 0x0001, [12686] = 0x0304, + [12688] = 0x0040, [12690] = 0x000f, [12692] = 0x0001, [12695] = 0x0100 +}; + +struct fll_config { + int src; + int in; + int out; +}; + +struct wm8995_priv { + enum snd_soc_control_type control_type; + int sysclk[2]; + int mclk[2]; + int aifclk[2]; + struct fll_config fll[2], fll_suspend[2]; + struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8995_NUM_SUPPLIES]; + struct snd_soc_codec *codec; +}; + +/* + * 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 WM8995_REGULATOR_EVENT(n) \ +static int wm8995_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + wm8995->codec->cache_sync = 1; \ + } \ + return 0; \ +} + +WM8995_REGULATOR_EVENT(0) +WM8995_REGULATOR_EVENT(1) +WM8995_REGULATOR_EVENT(2) +WM8995_REGULATOR_EVENT(3) +WM8995_REGULATOR_EVENT(4) +WM8995_REGULATOR_EVENT(5) +WM8995_REGULATOR_EVENT(6) +WM8995_REGULATOR_EVENT(7) + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0); +static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0); + +static const char *in1l_text[] = { + "Differential", "Single-ended IN1LN", "Single-ended IN1LP" +}; + +static const SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL, + 2, in1l_text); + +static const char *in1r_text[] = { + "Differential", "Single-ended IN1RN", "Single-ended IN1RP" +}; + +static const SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL, + 0, in1r_text); + +static const char *dmic_src_text[] = { + "DMICDAT1", "DMICDAT2", "DMICDAT3" +}; + +static const SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5, + 8, dmic_src_text); +static const SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5, + 6, dmic_src_text); + +static const struct snd_kcontrol_new wm8995_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC1 Volume", WM8995_DAC1_LEFT_VOLUME, + WM8995_DAC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R("DAC1 Switch", WM8995_DAC1_LEFT_VOLUME, + WM8995_DAC1_RIGHT_VOLUME, 9, 1, 1), + + SOC_DOUBLE_R_TLV("DAC2 Volume", WM8995_DAC2_LEFT_VOLUME, + WM8995_DAC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R("DAC2 Switch", WM8995_DAC2_LEFT_VOLUME, + WM8995_DAC2_RIGHT_VOLUME, 9, 1, 1), + + SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8995_AIF1_DAC1_LEFT_VOLUME, + WM8995_AIF1_DAC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8995_AIF1_DAC2_LEFT_VOLUME, + WM8995_AIF1_DAC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8995_AIF2_DAC_LEFT_VOLUME, + WM8995_AIF2_DAC_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + + SOC_DOUBLE_R_TLV("IN1LR Volume", WM8995_LEFT_LINE_INPUT_1_VOLUME, + WM8995_RIGHT_LINE_INPUT_1_VOLUME, 0, 31, 0, in1lr_pga_tlv), + + SOC_SINGLE_TLV("IN1L Boost", WM8995_LEFT_LINE_INPUT_CONTROL, + 4, 3, 0, in1l_boost_tlv), + + SOC_ENUM("IN1L Mode", in1l_enum), + SOC_ENUM("IN1R Mode", in1r_enum), + + SOC_ENUM("DMIC1 SRC", dmic_src1_enum), + SOC_ENUM("DMIC2 SRC", dmic_src2_enum), + + SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8995_DAC1_MIXER_VOLUMES, 0, 5, + 24, 0, sidetone_tlv), + SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8995_DAC2_MIXER_VOLUMES, 0, 5, + 24, 0, sidetone_tlv), + + SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8995_AIF1_ADC1_LEFT_VOLUME, + WM8995_AIF1_ADC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8995_AIF1_ADC2_LEFT_VOLUME, + WM8995_AIF1_ADC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8995_AIF2_ADC_LEFT_VOLUME, + WM8995_AIF2_ADC_RIGHT_VOLUME, 0, 96, 0, digital_tlv) +}; + +static void wm8995_update_class_w(struct snd_soc_codec *codec) +{ + int enable = 1; + int source = 0; /* GCC flow analysis can't track enable */ + int reg, reg_r; + + /* We also need the same setting for L/R and only one path */ + reg = snd_soc_read(codec, WM8995_DAC1_LEFT_MIXER_ROUTING); + switch (reg) { + case WM8995_AIF2DACL_TO_DAC1L: + dev_dbg(codec->dev, "Class W source AIF2DAC\n"); + source = 2 << WM8995_CP_DYN_SRC_SEL_SHIFT; + break; + case WM8995_AIF1DAC2L_TO_DAC1L: + dev_dbg(codec->dev, "Class W source AIF1DAC2\n"); + source = 1 << WM8995_CP_DYN_SRC_SEL_SHIFT; + break; + case WM8995_AIF1DAC1L_TO_DAC1L: + dev_dbg(codec->dev, "Class W source AIF1DAC1\n"); + source = 0 << WM8995_CP_DYN_SRC_SEL_SHIFT; + break; + default: + dev_dbg(codec->dev, "DAC mixer setting: %x\n", reg); + enable = 0; + break; + } + + reg_r = snd_soc_read(codec, WM8995_DAC1_RIGHT_MIXER_ROUTING); + if (reg_r != reg) { + dev_dbg(codec->dev, "Left and right DAC mixers different\n"); + enable = 0; + } + + if (enable) { + dev_dbg(codec->dev, "Class W enabled\n"); + snd_soc_update_bits(codec, WM8995_CLASS_W_1, + WM8995_CP_DYN_PWR_MASK | + WM8995_CP_DYN_SRC_SEL_MASK, + source | WM8995_CP_DYN_PWR); + } else { + dev_dbg(codec->dev, "Class W disabled\n"); + snd_soc_update_bits(codec, WM8995_CLASS_W_1, + WM8995_CP_DYN_PWR_MASK, 0); + } +} + +static int check_clk_sys(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg; + const char *clk; + + reg = snd_soc_read(source->codec, WM8995_CLOCKING_1); + /* Check what we're currently using for CLK_SYS */ + if (reg & WM8995_SYSCLK_SRC) + clk = "AIF2CLK"; + else + clk = "AIF1CLK"; + return !strcmp(source->name, clk); +} + +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_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); + return ret; +} + +static int hp_supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec; + struct wm8995_priv *wm8995; + + codec = w->codec; + wm8995 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable the headphone amp */ + snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, + WM8995_HPOUT1L_ENA_MASK | + WM8995_HPOUT1R_ENA_MASK, + WM8995_HPOUT1L_ENA | + WM8995_HPOUT1R_ENA); + + /* Enable the second stage */ + snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1, + WM8995_HPOUT1L_DLY_MASK | + WM8995_HPOUT1R_DLY_MASK, + WM8995_HPOUT1L_DLY | + WM8995_HPOUT1R_DLY); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WM8995_CHARGE_PUMP_1, + WM8995_CP_ENA_MASK, 0); + break; + } + + return 0; +} + +static void dc_servo_cmd(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val, unsigned int mask) +{ + int timeout = 10; + + dev_dbg(codec->dev, "%s: reg = %#x, val = %#x, mask = %#x\n", + __func__, reg, val, mask); + + snd_soc_write(codec, reg, val); + while (timeout--) { + msleep(10); + val = snd_soc_read(codec, WM8995_DC_SERVO_READBACK_0); + if ((val & mask) == mask) + return; + } + + dev_err(codec->dev, "Timed out waiting for DC Servo\n"); +} + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec; + unsigned int reg; + + codec = w->codec; + reg = snd_soc_read(codec, WM8995_ANALOGUE_HP_1); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, WM8995_CHARGE_PUMP_1, + WM8995_CP_ENA_MASK, WM8995_CP_ENA); + + msleep(5); + + snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, + WM8995_HPOUT1L_ENA_MASK | + WM8995_HPOUT1R_ENA_MASK, + WM8995_HPOUT1L_ENA | WM8995_HPOUT1R_ENA); + + udelay(20); + + reg |= WM8995_HPOUT1L_DLY | WM8995_HPOUT1R_DLY; + snd_soc_write(codec, WM8995_ANALOGUE_HP_1, reg); + + snd_soc_write(codec, WM8995_DC_SERVO_1, WM8995_DCS_ENA_CHAN_0 | + WM8995_DCS_ENA_CHAN_1); + + dc_servo_cmd(codec, WM8995_DC_SERVO_2, + WM8995_DCS_TRIG_STARTUP_0 | + WM8995_DCS_TRIG_STARTUP_1, + WM8995_DCS_TRIG_DAC_WR_0 | + WM8995_DCS_TRIG_DAC_WR_1); + + reg |= WM8995_HPOUT1R_OUTP | WM8995_HPOUT1R_RMV_SHORT | + WM8995_HPOUT1L_OUTP | WM8995_HPOUT1L_RMV_SHORT; + snd_soc_write(codec, WM8995_ANALOGUE_HP_1, reg); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1, + WM8995_HPOUT1L_OUTP_MASK | + WM8995_HPOUT1R_OUTP_MASK | + WM8995_HPOUT1L_RMV_SHORT_MASK | + WM8995_HPOUT1R_RMV_SHORT_MASK, 0); + + snd_soc_update_bits(codec, WM8995_ANALOGUE_HP_1, + WM8995_HPOUT1L_DLY_MASK | + WM8995_HPOUT1R_DLY_MASK, 0); + + snd_soc_write(codec, WM8995_DC_SERVO_1, 0); + + snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, + WM8995_HPOUT1L_ENA_MASK | + WM8995_HPOUT1R_ENA_MASK, + 0); + break; + } + + return 0; +} + +static int configure_aif_clock(struct snd_soc_codec *codec, int aif) +{ + struct wm8995_priv *wm8995; + int rate; + int reg1 = 0; + int offset; + + wm8995 = snd_soc_codec_get_drvdata(codec); + + if (aif) + offset = 4; + else + offset = 0; + + switch (wm8995->sysclk[aif]) { + case WM8995_SYSCLK_MCLK1: + rate = wm8995->mclk[0]; + break; + case WM8995_SYSCLK_MCLK2: + reg1 |= 0x8; + rate = wm8995->mclk[1]; + break; + case WM8995_SYSCLK_FLL1: + reg1 |= 0x10; + rate = wm8995->fll[0].out; + break; + case WM8995_SYSCLK_FLL2: + reg1 |= 0x18; + rate = wm8995->fll[1].out; + break; + default: + return -EINVAL; + } + + if (rate >= 13500000) { + rate /= 2; + reg1 |= WM8995_AIF1CLK_DIV; + + dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n", + aif + 1, rate); + } + + wm8995->aifclk[aif] = rate; + + snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1 + offset, + WM8995_AIF1CLK_SRC_MASK | WM8995_AIF1CLK_DIV_MASK, + reg1); + return 0; +} + +static int configure_clock(struct snd_soc_codec *codec) +{ + struct wm8995_priv *wm8995; + int old, new; + + wm8995 = snd_soc_codec_get_drvdata(codec); + + /* Bring up the AIF clocks first */ + configure_aif_clock(codec, 0); + configure_aif_clock(codec, 1); + + /* + * Then switch CLK_SYS over to the higher of them; a change + * can only happen as a result of a clocking change which can + * only be made outside of DAPM so we can safely redo the + * clocking. + */ + + /* If they're equal it doesn't matter which is used */ + if (wm8995->aifclk[0] == wm8995->aifclk[1]) + return 0; + + if (wm8995->aifclk[0] < wm8995->aifclk[1]) + new = WM8995_SYSCLK_SRC; + else + new = 0; + + old = snd_soc_read(codec, WM8995_CLOCKING_1) & WM8995_SYSCLK_SRC; + + /* If there's no change then we're done. */ + if (old == new) + return 0; + + snd_soc_update_bits(codec, WM8995_CLOCKING_1, + WM8995_SYSCLK_SRC_MASK, new); + + snd_soc_dapm_sync(&codec->dapm); + + return 0; +} + +static int clk_sys_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec; + + codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return configure_clock(codec); + + case SND_SOC_DAPM_POST_PMD: + configure_clock(codec); + break; + } + + return 0; +} + +static const char *sidetone_text[] = { + "ADC/DMIC1", "DMIC2", +}; + +static const struct soc_enum sidetone1_enum = + SOC_ENUM_SINGLE(WM8995_SIDETONE, 0, 2, sidetone_text); + +static const struct snd_kcontrol_new sidetone1_mux = + SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum); + +static const struct soc_enum sidetone2_enum = + SOC_ENUM_SINGLE(WM8995_SIDETONE, 1, 2, sidetone_text); + +static const struct snd_kcontrol_new sidetone2_mux = + SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum); + +static const struct snd_kcontrol_new aif1adc1l_mix[] = { + SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc1r_mix[] = { + SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc2l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc2r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1l_mix[] = { + WM8995_CLASS_W_SWITCH("Right Sidetone Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 5, 1, 0), + WM8995_CLASS_W_SWITCH("Left Sidetone Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 4, 1, 0), + WM8995_CLASS_W_SWITCH("AIF2 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 2, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.2 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 1, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.1 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1r_mix[] = { + WM8995_CLASS_W_SWITCH("Right Sidetone Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 5, 1, 0), + WM8995_CLASS_W_SWITCH("Left Sidetone Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 4, 1, 0), + WM8995_CLASS_W_SWITCH("AIF2 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 2, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.2 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 1, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.1 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif2dac2l_mix[] = { + SOC_DAPM_SINGLE("Right Sidetone Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 5, 1, 0), + SOC_DAPM_SINGLE("Left Sidetone Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 4, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 2, 1, 0), + SOC_DAPM_SINGLE("AIF1.2 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF1.1 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif2dac2r_mix[] = { + SOC_DAPM_SINGLE("Right Sidetone Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 5, 1, 0), + SOC_DAPM_SINGLE("Left Sidetone Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 4, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 2, 1, 0), + SOC_DAPM_SINGLE("AIF1.2 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF1.1 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new in1l_pga = + SOC_DAPM_SINGLE("IN1L Switch", WM8995_POWER_MANAGEMENT_2, 5, 1, 0); + +static const struct snd_kcontrol_new in1r_pga = + SOC_DAPM_SINGLE("IN1R Switch", WM8995_POWER_MANAGEMENT_2, 4, 1, 0); + +static const char *adc_mux_text[] = { + "ADC", + "DMIC", +}; + +static const struct soc_enum adc_enum = + SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text); + +static const struct snd_kcontrol_new adcl_mux = + SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum); + +static const struct snd_kcontrol_new adcr_mux = + SOC_DAPM_ENUM_VIRT("ADCR Mux", adc_enum); + +static const char *spk_src_text[] = { + "DAC1L", "DAC1R", "DAC2L", "DAC2R" +}; + +static const SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1, + 0, spk_src_text); +static const SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1, + 0, spk_src_text); +static const SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2, + 0, spk_src_text); +static const SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2, + 0, spk_src_text); + +static const struct snd_kcontrol_new spk1l_mux = + SOC_DAPM_ENUM("SPK1L SRC", spk1l_src_enum); +static const struct snd_kcontrol_new spk1r_mux = + SOC_DAPM_ENUM("SPK1R SRC", spk1r_src_enum); +static const struct snd_kcontrol_new spk2l_mux = + SOC_DAPM_ENUM("SPK2L SRC", spk2l_src_enum); +static const struct snd_kcontrol_new spk2r_mux = + SOC_DAPM_ENUM("SPK2R SRC", spk2r_src_enum); + +static const struct snd_soc_dapm_widget wm8995_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1DAT"), + SND_SOC_DAPM_INPUT("DMIC2DAT"), + + SND_SOC_DAPM_INPUT("IN1L"), + SND_SOC_DAPM_INPUT("IN1R"), + + SND_SOC_DAPM_MIXER("IN1L PGA", SND_SOC_NOPM, 0, 0, + &in1l_pga, 1), + SND_SOC_DAPM_MIXER("IN1R PGA", SND_SOC_NOPM, 0, 0, + &in1r_pga, 1), + + SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8995_POWER_MANAGEMENT_1, 8, 0), + SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8995_POWER_MANAGEMENT_1, 9, 0), + + SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8995_AIF1_CLOCKING_1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8995_AIF2_CLOCKING_1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8995_CLOCKING_1, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8995_CLOCKING_1, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SYSDSPCLK", WM8995_CLOCKING_1, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture", 0, + WM8995_POWER_MANAGEMENT_3, 9, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", 0, + WM8995_POWER_MANAGEMENT_3, 8, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture", + 0, WM8995_POWER_MANAGEMENT_3, 11, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture", + 0, WM8995_POWER_MANAGEMENT_3, 10, 0), + + SND_SOC_DAPM_VIRT_MUX("ADCL Mux", SND_SOC_NOPM, 1, 0, + &adcl_mux), + SND_SOC_DAPM_VIRT_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, + &adcr_mux), + + SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8995_POWER_MANAGEMENT_3, 5, 0), + SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8995_POWER_MANAGEMENT_3, 4, 0), + SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8995_POWER_MANAGEMENT_3, 3, 0), + SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8995_POWER_MANAGEMENT_3, 2, 0), + + SND_SOC_DAPM_ADC("ADCL", NULL, WM8995_POWER_MANAGEMENT_3, 1, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, WM8995_POWER_MANAGEMENT_3, 0, 0), + + SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, + aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)), + SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0, + aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)), + SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0, + aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)), + SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0, + aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)), + + SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 9, 0), + SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 8, 0), + SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, + 0, 0), + + SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 11, 0), + SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 10, 0), + + SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0, + aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)), + SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0, + aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)), + + SND_SOC_DAPM_DAC("DAC2L", NULL, WM8995_POWER_MANAGEMENT_4, 3, 0), + SND_SOC_DAPM_DAC("DAC2R", NULL, WM8995_POWER_MANAGEMENT_4, 2, 0), + SND_SOC_DAPM_DAC("DAC1L", NULL, WM8995_POWER_MANAGEMENT_4, 1, 0), + SND_SOC_DAPM_DAC("DAC1R", NULL, WM8995_POWER_MANAGEMENT_4, 0, 0), + + 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_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux), + SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux), + + SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, + hp_supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("SPK1L Driver", WM8995_LEFT_PDM_SPEAKER_1, + 4, 0, &spk1l_mux), + SND_SOC_DAPM_MUX("SPK1R Driver", WM8995_RIGHT_PDM_SPEAKER_1, + 4, 0, &spk1r_mux), + SND_SOC_DAPM_MUX("SPK2L Driver", WM8995_LEFT_PDM_SPEAKER_2, + 4, 0, &spk2l_mux), + SND_SOC_DAPM_MUX("SPK2R Driver", WM8995_RIGHT_PDM_SPEAKER_2, + 4, 0, &spk2r_mux), + + SND_SOC_DAPM_SUPPLY("LDO2", WM8995_POWER_MANAGEMENT_2, 1, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HP1L"), + SND_SOC_DAPM_OUTPUT("HP1R"), + SND_SOC_DAPM_OUTPUT("SPK1L"), + SND_SOC_DAPM_OUTPUT("SPK1R"), + SND_SOC_DAPM_OUTPUT("SPK2L"), + SND_SOC_DAPM_OUTPUT("SPK2R") +}; + +static const struct snd_soc_dapm_route wm8995_intercon[] = { + { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys }, + { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys }, + + { "DSP1CLK", NULL, "CLK_SYS" }, + { "DSP2CLK", NULL, "CLK_SYS" }, + { "SYSDSPCLK", NULL, "CLK_SYS" }, + + { "AIF1ADC1L", NULL, "AIF1CLK" }, + { "AIF1ADC1L", NULL, "DSP1CLK" }, + { "AIF1ADC1R", NULL, "AIF1CLK" }, + { "AIF1ADC1R", NULL, "DSP1CLK" }, + { "AIF1ADC1R", NULL, "SYSDSPCLK" }, + + { "AIF1ADC2L", NULL, "AIF1CLK" }, + { "AIF1ADC2L", NULL, "DSP1CLK" }, + { "AIF1ADC2R", NULL, "AIF1CLK" }, + { "AIF1ADC2R", NULL, "DSP1CLK" }, + { "AIF1ADC2R", NULL, "SYSDSPCLK" }, + + { "DMIC1L", NULL, "DMIC1DAT" }, + { "DMIC1L", NULL, "CLK_SYS" }, + { "DMIC1R", NULL, "DMIC1DAT" }, + { "DMIC1R", NULL, "CLK_SYS" }, + { "DMIC2L", NULL, "DMIC2DAT" }, + { "DMIC2L", NULL, "CLK_SYS" }, + { "DMIC2R", NULL, "DMIC2DAT" }, + { "DMIC2R", NULL, "CLK_SYS" }, + + { "ADCL", NULL, "AIF1CLK" }, + { "ADCL", NULL, "DSP1CLK" }, + { "ADCL", NULL, "SYSDSPCLK" }, + + { "ADCR", NULL, "AIF1CLK" }, + { "ADCR", NULL, "DSP1CLK" }, + { "ADCR", NULL, "SYSDSPCLK" }, + + { "IN1L PGA", "IN1L Switch", "IN1L" }, + { "IN1R PGA", "IN1R Switch", "IN1R" }, + { "IN1L PGA", NULL, "LDO2" }, + { "IN1R PGA", NULL, "LDO2" }, + + { "ADCL", NULL, "IN1L PGA" }, + { "ADCR", NULL, "IN1R PGA" }, + + { "ADCL Mux", "ADC", "ADCL" }, + { "ADCL Mux", "DMIC", "DMIC1L" }, + { "ADCR Mux", "ADC", "ADCR" }, + { "ADCR Mux", "DMIC", "DMIC1R" }, + + /* AIF1 outputs */ + { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" }, + { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" }, + + { "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" }, + { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" }, + + { "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" }, + { "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" }, + + { "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" }, + { "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" }, + + /* Sidetone */ + { "Left Sidetone", "ADC/DMIC1", "AIF1ADC1L" }, + { "Left Sidetone", "DMIC2", "AIF1ADC2L" }, + { "Right Sidetone", "ADC/DMIC1", "AIF1ADC1R" }, + { "Right Sidetone", "DMIC2", "AIF1ADC2R" }, + + { "AIF1DAC1L", NULL, "AIF1CLK" }, + { "AIF1DAC1L", NULL, "DSP1CLK" }, + { "AIF1DAC1R", NULL, "AIF1CLK" }, + { "AIF1DAC1R", NULL, "DSP1CLK" }, + { "AIF1DAC1R", NULL, "SYSDSPCLK" }, + + { "AIF1DAC2L", NULL, "AIF1CLK" }, + { "AIF1DAC2L", NULL, "DSP1CLK" }, + { "AIF1DAC2R", NULL, "AIF1CLK" }, + { "AIF1DAC2R", NULL, "DSP1CLK" }, + { "AIF1DAC2R", NULL, "SYSDSPCLK" }, + + { "DAC1L", NULL, "AIF1CLK" }, + { "DAC1L", NULL, "DSP1CLK" }, + { "DAC1L", NULL, "SYSDSPCLK" }, + + { "DAC1R", NULL, "AIF1CLK" }, + { "DAC1R", NULL, "DSP1CLK" }, + { "DAC1R", NULL, "SYSDSPCLK" }, + + { "AIF1DAC1L", NULL, "AIF1DACDAT" }, + { "AIF1DAC1R", NULL, "AIF1DACDAT" }, + { "AIF1DAC2L", NULL, "AIF1DACDAT" }, + { "AIF1DAC2R", NULL, "AIF1DACDAT" }, + + /* DAC1 inputs */ + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, + { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, + { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, + { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, + { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + /* DAC2/AIF2 outputs */ + { "DAC2L", NULL, "AIF2DAC2L Mixer" }, + { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, + { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, + + { "DAC2R", NULL, "AIF2DAC2R Mixer" }, + { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, + { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, + + /* Output stages */ + { "Headphone PGA", NULL, "DAC1L" }, + { "Headphone PGA", NULL, "DAC1R" }, + + { "Headphone PGA", NULL, "DAC2L" }, + { "Headphone PGA", NULL, "DAC2R" }, + + { "Headphone PGA", NULL, "Headphone Supply" }, + { "Headphone PGA", NULL, "CLK_SYS" }, + { "Headphone PGA", NULL, "LDO2" }, + + { "HP1L", NULL, "Headphone PGA" }, + { "HP1R", NULL, "Headphone PGA" }, + + { "SPK1L Driver", "DAC1L", "DAC1L" }, + { "SPK1L Driver", "DAC1R", "DAC1R" }, + { "SPK1L Driver", "DAC2L", "DAC2L" }, + { "SPK1L Driver", "DAC2R", "DAC2R" }, + { "SPK1L Driver", NULL, "CLK_SYS" }, + + { "SPK1R Driver", "DAC1L", "DAC1L" }, + { "SPK1R Driver", "DAC1R", "DAC1R" }, + { "SPK1R Driver", "DAC2L", "DAC2L" }, + { "SPK1R Driver", "DAC2R", "DAC2R" }, + { "SPK1R Driver", NULL, "CLK_SYS" }, + + { "SPK2L Driver", "DAC1L", "DAC1L" }, + { "SPK2L Driver", "DAC1R", "DAC1R" }, + { "SPK2L Driver", "DAC2L", "DAC2L" }, + { "SPK2L Driver", "DAC2R", "DAC2R" }, + { "SPK2L Driver", NULL, "CLK_SYS" }, + + { "SPK2R Driver", "DAC1L", "DAC1L" }, + { "SPK2R Driver", "DAC1R", "DAC1R" }, + { "SPK2R Driver", "DAC2L", "DAC2L" }, + { "SPK2R Driver", "DAC2R", "DAC2R" }, + { "SPK2R Driver", NULL, "CLK_SYS" }, + + { "SPK1L", NULL, "SPK1L Driver" }, + { "SPK1R", NULL, "SPK1R Driver" }, + { "SPK2L", NULL, "SPK2L Driver" }, + { "SPK2R", NULL, "SPK2R Driver" } +}; + +static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg) +{ + /* out of bounds registers are generally considered + * volatile to support register banks that are partially + * owned by something else for e.g. a DSP + */ + if (reg > WM8995_MAX_CACHED_REGISTER) + return 1; + + switch (reg) { + case WM8995_SOFTWARE_RESET: + case WM8995_DC_SERVO_READBACK_0: + case WM8995_INTERRUPT_STATUS_1: + case WM8995_INTERRUPT_STATUS_2: + case WM8995_INTERRUPT_STATUS_1_MASK: + case WM8995_INTERRUPT_STATUS_2_MASK: + case WM8995_INTERRUPT_CONTROL: + case WM8995_ACCESSORY_DETECT_MODE1: + case WM8995_ACCESSORY_DETECT_MODE2: + case WM8995_HEADPHONE_DETECT1: + case WM8995_HEADPHONE_DETECT2: + return 1; + } + + return 0; +} + +static int wm8995_aif_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int mute_reg; + + switch (dai->id) { + case 0: + mute_reg = WM8995_AIF1_DAC1_FILTERS_1; + break; + case 1: + mute_reg = WM8995_AIF2_DAC_FILTERS_1; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, mute_reg, WM8995_AIF1DAC1_MUTE_MASK, + !!mute << WM8995_AIF1DAC1_MUTE_SHIFT); + return 0; +} + +static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec; + int master; + int aif; + + codec = dai->codec; + + master = 0; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + master = WM8995_AIF1_MSTR; + break; + default: + dev_err(dai->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + aif = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8995_AIF1_LRCLK_INV; + case SND_SOC_DAIFMT_DSP_A: + aif |= (0x3 << WM8995_AIF1_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_I2S: + aif |= (0x2 << WM8995_AIF1_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif |= (0x1 << WM8995_AIF1_FMT_SHIFT); + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8995_AIF1_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8995_AIF1_BCLK_INV | WM8995_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8995_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8995_AIF1_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, WM8995_AIF1_CONTROL_1, + WM8995_AIF1_BCLK_INV_MASK | + WM8995_AIF1_LRCLK_INV_MASK | + WM8995_AIF1_FMT_MASK, aif); + snd_soc_update_bits(codec, WM8995_AIF1_MASTER_SLAVE, + WM8995_AIF1_MSTR_MASK, master); + return 0; +} + +static const int srs[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, + 48000, 88200, 96000 +}; + +static const int fs_ratios[] = { + -1 /* reserved */, + 128, 192, 256, 384, 512, 768, 1024, 1408, 1536 +}; + +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480 +}; + +static int wm8995_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec; + struct wm8995_priv *wm8995; + int aif1_reg; + int bclk_reg; + int lrclk_reg; + int rate_reg; + int bclk_rate; + int aif1; + int lrclk, bclk; + int i, rate_val, best, best_val, cur_val; + + codec = dai->codec; + wm8995 = snd_soc_codec_get_drvdata(codec); + + switch (dai->id) { + case 0: + aif1_reg = WM8995_AIF1_CONTROL_1; + bclk_reg = WM8995_AIF1_BCLK; + rate_reg = WM8995_AIF1_RATE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK /* || + wm8995->lrclk_shared[0] */) { + lrclk_reg = WM8995_AIF1DAC_LRCLK; + } else { + lrclk_reg = WM8995_AIF1ADC_LRCLK; + dev_dbg(codec->dev, "AIF1 using split LRCLK\n"); + } + break; + case 1: + aif1_reg = WM8995_AIF2_CONTROL_1; + bclk_reg = WM8995_AIF2_BCLK; + rate_reg = WM8995_AIF2_RATE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK /* || + wm8995->lrclk_shared[1] */) { + lrclk_reg = WM8995_AIF2DAC_LRCLK; + } else { + lrclk_reg = WM8995_AIF2ADC_LRCLK; + dev_dbg(codec->dev, "AIF2 using split LRCLK\n"); + } + break; + default: + return -EINVAL; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) + return bclk_rate; + + aif1 = 0; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + aif1 |= (0x1 << WM8995_AIF1_WL_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + aif1 |= (0x2 << WM8995_AIF1_WL_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + aif1 |= (0x3 << WM8995_AIF1_WL_SHIFT); + break; + default: + dev_err(dai->dev, "Unsupported word length %u\n", + params_format(params)); + return -EINVAL; + } + + /* try to find a suitable sample rate */ + for (i = 0; i < ARRAY_SIZE(srs); ++i) + if (srs[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(srs)) { + dev_err(dai->dev, "Sample rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + rate_val = i << WM8995_AIF1_SR_SHIFT; + + dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i]); + dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n", + dai->id + 1, wm8995->aifclk[dai->id], bclk_rate); + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 1; + best_val = abs((fs_ratios[1] * params_rate(params)) + - wm8995->aifclk[dai->id]); + for (i = 2; i < ARRAY_SIZE(fs_ratios); i++) { + cur_val = abs((fs_ratios[i] * params_rate(params)) + - wm8995->aifclk[dai->id]); + if (cur_val >= best_val) + continue; + best = i; + best_val = cur_val; + } + rate_val |= best; + + dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n", + dai->id + 1, fs_ratios[best]); + + /* + * We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + best = 0; + bclk = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8995->aifclk[dai->id] * 10 / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + bclk |= best << WM8995_AIF1_BCLK_DIV_SHIFT; + + bclk_rate = wm8995->aifclk[dai->id] * 10 / bclk_divs[best]; + dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n", + bclk_divs[best], bclk_rate); + + 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, aif1_reg, + WM8995_AIF1_WL_MASK, aif1); + snd_soc_update_bits(codec, bclk_reg, + WM8995_AIF1_BCLK_DIV_MASK, bclk); + snd_soc_update_bits(codec, lrclk_reg, + WM8995_AIF1DAC_RATE_MASK, lrclk); + snd_soc_update_bits(codec, rate_reg, + WM8995_AIF1_SR_MASK | + WM8995_AIF1CLK_RATE_MASK, rate_val); + return 0; +} + +static int wm8995_set_tristate(struct snd_soc_dai *codec_dai, int tristate) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int reg, val, mask; + + switch (codec_dai->id) { + case 0: + reg = WM8995_AIF1_MASTER_SLAVE; + mask = WM8995_AIF1_TRI; + break; + case 1: + reg = WM8995_AIF2_MASTER_SLAVE; + mask = WM8995_AIF2_TRI; + break; + case 2: + reg = WM8995_POWER_MANAGEMENT_5; + mask = WM8995_AIF3_TRI; + break; + default: + return -EINVAL; + } + + if (tristate) + val = mask; + else + val = 0; + + return snd_soc_update_bits(codec, reg, mask, val); +} + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +struct fll_div { + u16 outdiv; + u16 n; + u16 k; + u16 clk_ref_div; + u16 fll_fratio; +}; + +static int wm8995_get_fll_config(struct fll_div *fll, + int freq_in, int freq_out) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); + + /* Scale the input frequency down to <= 13.5MHz */ + fll->clk_ref_div = 0; + while (freq_in > 13500000) { + fll->clk_ref_div++; + freq_in /= 2; + + if (fll->clk_ref_div > 3) + return -EINVAL; + } + pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in); + + /* Scale the output to give 90MHz<=Fvco<=100MHz */ + fll->outdiv = 3; + while (freq_out * (fll->outdiv + 1) < 90000000) { + fll->outdiv++; + if (fll->outdiv > 63) + return -EINVAL; + } + freq_out *= fll->outdiv + 1; + pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out); + + if (freq_in > 1000000) { + fll->fll_fratio = 0; + } else if (freq_in > 256000) { + fll->fll_fratio = 1; + freq_in *= 2; + } else if (freq_in > 128000) { + fll->fll_fratio = 2; + freq_in *= 4; + } else if (freq_in > 64000) { + fll->fll_fratio = 3; + freq_in *= 8; + } else { + fll->fll_fratio = 4; + freq_in *= 16; + } + pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in); + + /* Now, calculate N.K */ + Ndiv = freq_out / freq_in; + + fll->n = Ndiv; + Nmod = freq_out % freq_in; + pr_debug("Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, freq_in); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll->k = K / 10; + + pr_debug("N=%x K=%x\n", fll->n, fll->k); + + return 0; +} + +static int wm8995_set_fll(struct snd_soc_dai *dai, int id, + int src, unsigned int freq_in, + unsigned int freq_out) +{ + struct snd_soc_codec *codec; + struct wm8995_priv *wm8995; + int reg_offset, ret; + struct fll_div fll; + u16 reg, aif1, aif2; + + codec = dai->codec; + wm8995 = snd_soc_codec_get_drvdata(codec); + + aif1 = snd_soc_read(codec, WM8995_AIF1_CLOCKING_1) + & WM8995_AIF1CLK_ENA; + + aif2 = snd_soc_read(codec, WM8995_AIF2_CLOCKING_1) + & WM8995_AIF2CLK_ENA; + + switch (id) { + case WM8995_FLL1: + reg_offset = 0; + id = 0; + break; + case WM8995_FLL2: + reg_offset = 0x20; + id = 1; + break; + default: + return -EINVAL; + } + + switch (src) { + case 0: + /* Allow no source specification when stopping */ + if (freq_out) + return -EINVAL; + break; + case WM8995_FLL_SRC_MCLK1: + case WM8995_FLL_SRC_MCLK2: + case WM8995_FLL_SRC_LRCLK: + case WM8995_FLL_SRC_BCLK: + break; + default: + return -EINVAL; + } + + /* Are we changing anything? */ + if (wm8995->fll[id].src == src && + wm8995->fll[id].in == freq_in && wm8995->fll[id].out == freq_out) + return 0; + + /* If we're stopping the FLL redo the old config - no + * registers will actually be written but we avoid GCC flow + * analysis bugs spewing warnings. + */ + if (freq_out) + ret = wm8995_get_fll_config(&fll, freq_in, freq_out); + else + ret = wm8995_get_fll_config(&fll, wm8995->fll[id].in, + wm8995->fll[id].out); + if (ret < 0) + return ret; + + /* Gate the AIF clocks while we reclock */ + snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1, + WM8995_AIF1CLK_ENA_MASK, 0); + snd_soc_update_bits(codec, WM8995_AIF2_CLOCKING_1, + WM8995_AIF2CLK_ENA_MASK, 0); + + /* We always need to disable the FLL while reconfiguring */ + snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_1 + reg_offset, + WM8995_FLL1_ENA_MASK, 0); + + reg = (fll.outdiv << WM8995_FLL1_OUTDIV_SHIFT) | + (fll.fll_fratio << WM8995_FLL1_FRATIO_SHIFT); + snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_2 + reg_offset, + WM8995_FLL1_OUTDIV_MASK | + WM8995_FLL1_FRATIO_MASK, reg); + + snd_soc_write(codec, WM8995_FLL1_CONTROL_3 + reg_offset, fll.k); + + snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_4 + reg_offset, + WM8995_FLL1_N_MASK, + fll.n << WM8995_FLL1_N_SHIFT); + + snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_5 + reg_offset, + WM8995_FLL1_REFCLK_DIV_MASK | + WM8995_FLL1_REFCLK_SRC_MASK, + (fll.clk_ref_div << WM8995_FLL1_REFCLK_DIV_SHIFT) | + (src - 1)); + + if (freq_out) + snd_soc_update_bits(codec, WM8995_FLL1_CONTROL_1 + reg_offset, + WM8995_FLL1_ENA_MASK, WM8995_FLL1_ENA); + + wm8995->fll[id].in = freq_in; + wm8995->fll[id].out = freq_out; + wm8995->fll[id].src = src; + + /* Enable any gated AIF clocks */ + snd_soc_update_bits(codec, WM8995_AIF1_CLOCKING_1, + WM8995_AIF1CLK_ENA_MASK, aif1); + snd_soc_update_bits(codec, WM8995_AIF2_CLOCKING_1, + WM8995_AIF2CLK_ENA_MASK, aif2); + + configure_clock(codec); + + return 0; +} + +static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec; + struct wm8995_priv *wm8995; + + codec = dai->codec; + wm8995 = snd_soc_codec_get_drvdata(codec); + + switch (dai->id) { + case 0: + case 1: + break; + default: + /* AIF3 shares clocking with AIF1/2 */ + return -EINVAL; + } + + switch (clk_id) { + case WM8995_SYSCLK_MCLK1: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1; + wm8995->mclk[0] = freq; + dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n", + dai->id + 1, freq); + break; + case WM8995_SYSCLK_MCLK2: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1; + wm8995->mclk[1] = freq; + dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n", + dai->id + 1, freq); + break; + case WM8995_SYSCLK_FLL1: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_FLL1; + dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id + 1); + break; + case WM8995_SYSCLK_FLL2: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_FLL2; + dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id + 1); + break; + case WM8995_SYSCLK_OPCLK: + default: + dev_err(dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + configure_clock(codec); + + return 0; +} + +static int wm8995_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct wm8995_priv *wm8995; + int ret; + + wm8995 = snd_soc_codec_get_drvdata(codec); + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) + return ret; + + 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, WM8995_POWER_MANAGEMENT_1, + WM8995_BG_ENA_MASK, WM8995_BG_ENA); + } + break; + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, + WM8995_BG_ENA_MASK, 0); + regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#ifdef CONFIG_PM +static int wm8995_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8995_resume(struct snd_soc_codec *codec) +{ + wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define wm8995_suspend NULL +#define wm8995_resume NULL +#endif + +static int wm8995_remove(struct snd_soc_codec *codec) +{ + struct wm8995_priv *wm8995; + struct i2c_client *i2c; + + i2c = container_of(codec->dev, struct i2c_client, dev); + wm8995 = snd_soc_codec_get_drvdata(codec); + wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8995_probe(struct snd_soc_codec *codec) +{ + struct wm8995_priv *wm8995; + int i; + int ret; + + codec->dapm.idle_bias_off = 1; + wm8995 = snd_soc_codec_get_drvdata(codec); + wm8995->codec = codec; + + ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) + wm8995->supplies[i].supply = wm8995_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0; + wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1; + wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2; + wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3; + wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4; + wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5; + wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6; + wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { + ret = regulator_register_notifier(wm8995->supplies[i].consumer, + &wm8995->disable_nb[i]); + if (ret) { + dev_err(codec->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_reg_get; + } + + ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET); + if (ret < 0) { + dev_err(codec->dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + if (ret != 0x8995) { + dev_err(codec->dev, "Invalid device ID: %#x\n", ret); + goto err_reg_enable; + } + + ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + + wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Latch volume updates (right only; we always do left then right). */ + snd_soc_update_bits(codec, WM8995_AIF1_DAC1_RIGHT_VOLUME, + WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU); + snd_soc_update_bits(codec, WM8995_AIF1_DAC2_RIGHT_VOLUME, + WM8995_AIF1DAC2_VU_MASK, WM8995_AIF1DAC2_VU); + snd_soc_update_bits(codec, WM8995_AIF2_DAC_RIGHT_VOLUME, + WM8995_AIF2DAC_VU_MASK, WM8995_AIF2DAC_VU); + snd_soc_update_bits(codec, WM8995_AIF1_ADC1_RIGHT_VOLUME, + WM8995_AIF1ADC1_VU_MASK, WM8995_AIF1ADC1_VU); + snd_soc_update_bits(codec, WM8995_AIF1_ADC2_RIGHT_VOLUME, + WM8995_AIF1ADC2_VU_MASK, WM8995_AIF1ADC2_VU); + snd_soc_update_bits(codec, WM8995_AIF2_ADC_RIGHT_VOLUME, + WM8995_AIF2ADC_VU_MASK, WM8995_AIF1ADC2_VU); + snd_soc_update_bits(codec, WM8995_DAC1_RIGHT_VOLUME, + WM8995_DAC1_VU_MASK, WM8995_DAC1_VU); + snd_soc_update_bits(codec, WM8995_DAC2_RIGHT_VOLUME, + WM8995_DAC2_VU_MASK, WM8995_DAC2_VU); + snd_soc_update_bits(codec, WM8995_RIGHT_LINE_INPUT_1_VOLUME, + WM8995_IN1_VU_MASK, WM8995_IN1_VU); + + wm8995_update_class_w(codec); + + snd_soc_add_controls(codec, wm8995_snd_controls, + ARRAY_SIZE(wm8995_snd_controls)); + snd_soc_dapm_new_controls(&codec->dapm, wm8995_dapm_widgets, + ARRAY_SIZE(wm8995_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, wm8995_intercon, + ARRAY_SIZE(wm8995_intercon)); + + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); +err_reg_get: + regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); + return ret; +} + +#define WM8995_FORMATS (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 wm8995_aif1_dai_ops = { + .set_sysclk = wm8995_set_dai_sysclk, + .set_fmt = wm8995_set_dai_fmt, + .hw_params = wm8995_hw_params, + .digital_mute = wm8995_aif_mute, + .set_pll = wm8995_set_fll, + .set_tristate = wm8995_set_tristate, +}; + +static struct snd_soc_dai_ops wm8995_aif2_dai_ops = { + .set_sysclk = wm8995_set_dai_sysclk, + .set_fmt = wm8995_set_dai_fmt, + .hw_params = wm8995_hw_params, + .digital_mute = wm8995_aif_mute, + .set_pll = wm8995_set_fll, + .set_tristate = wm8995_set_tristate, +}; + +static struct snd_soc_dai_ops wm8995_aif3_dai_ops = { + .set_tristate = wm8995_set_tristate, +}; + +static struct snd_soc_dai_driver wm8995_dai[] = { + { + .name = "wm8995-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8995_FORMATS + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8995_FORMATS + }, + .ops = &wm8995_aif1_dai_ops + }, + { + .name = "wm8995-aif2", + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8995_FORMATS + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8995_FORMATS + }, + .ops = &wm8995_aif2_dai_ops + }, + { + .name = "wm8995-aif3", + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8995_FORMATS + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8995_FORMATS + }, + .ops = &wm8995_aif3_dai_ops + } +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8995 = { + .probe = wm8995_probe, + .remove = wm8995_remove, + .suspend = wm8995_suspend, + .resume = wm8995_resume, + .set_bias_level = wm8995_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm8995_reg_defs), + .reg_word_size = sizeof(u16), + .reg_cache_default = wm8995_reg_defs, + .volatile_register = wm8995_volatile, + .compress_type = SND_SOC_RBTREE_COMPRESSION +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8995_spi_probe(struct spi_device *spi) +{ + struct wm8995_priv *wm8995; + int ret; + + wm8995 = kzalloc(sizeof *wm8995, GFP_KERNEL); + if (!wm8995) + return -ENOMEM; + + wm8995->control_type = SND_SOC_SPI; + spi_set_drvdata(spi, wm8995); + + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8995, wm8995_dai, + ARRAY_SIZE(wm8995_dai)); + if (ret < 0) + kfree(wm8995); + return ret; +} + +static int __devexit wm8995_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + kfree(spi_get_drvdata(spi)); + return 0; +} + +static struct spi_driver wm8995_spi_driver = { + .driver = { + .name = "wm8995", + .owner = THIS_MODULE, + }, + .probe = wm8995_spi_probe, + .remove = __devexit_p(wm8995_spi_remove) +}; +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int wm8995_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8995_priv *wm8995; + int ret; + + wm8995 = kzalloc(sizeof *wm8995, GFP_KERNEL); + if (!wm8995) + return -ENOMEM; + + wm8995->control_type = SND_SOC_I2C; + i2c_set_clientdata(i2c, wm8995); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8995, wm8995_dai, + ARRAY_SIZE(wm8995_dai)); + if (ret < 0) + kfree(wm8995); + return ret; +} + +static __devexit int wm8995_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 wm8995_i2c_id[] = { + {"wm8995", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id); + +static struct i2c_driver wm8995_i2c_driver = { + .driver = { + .name = "wm8995", + .owner = THIS_MODULE, + }, + .probe = wm8995_i2c_probe, + .remove = __devexit_p(wm8995_i2c_remove), + .id_table = wm8995_i2c_id +}; +#endif + +static int __init wm8995_modinit(void) +{ + int ret = 0; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8995_i2c_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8995 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8995_spi_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8995 SPI driver: %d\n", + ret); + } +#endif + return ret; +} + +module_init(wm8995_modinit); + +static void __exit wm8995_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8995_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8995_spi_driver); +#endif +} + +module_exit(wm8995_exit); + +MODULE_DESCRIPTION("ASoC WM8995 driver"); +MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h new file mode 100644 index 000000000000..5642121c4977 --- /dev/null +++ b/sound/soc/codecs/wm8995.h @@ -0,0 +1,4269 @@ +/* + * wm8995.h -- WM8995 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8995_H +#define _WM8995_H + +#include <asm/types.h> + +/* + * Register values. + */ +#define WM8995_SOFTWARE_RESET 0x00 +#define WM8995_POWER_MANAGEMENT_1 0x01 +#define WM8995_POWER_MANAGEMENT_2 0x02 +#define WM8995_POWER_MANAGEMENT_3 0x03 +#define WM8995_POWER_MANAGEMENT_4 0x04 +#define WM8995_POWER_MANAGEMENT_5 0x05 +#define WM8995_LEFT_LINE_INPUT_1_VOLUME 0x10 +#define WM8995_RIGHT_LINE_INPUT_1_VOLUME 0x11 +#define WM8995_LEFT_LINE_INPUT_CONTROL 0x12 +#define WM8995_DAC1_LEFT_VOLUME 0x18 +#define WM8995_DAC1_RIGHT_VOLUME 0x19 +#define WM8995_DAC2_LEFT_VOLUME 0x1A +#define WM8995_DAC2_RIGHT_VOLUME 0x1B +#define WM8995_OUTPUT_VOLUME_ZC_1 0x1C +#define WM8995_MICBIAS_1 0x20 +#define WM8995_MICBIAS_2 0x21 +#define WM8995_LDO_1 0x28 +#define WM8995_LDO_2 0x29 +#define WM8995_ACCESSORY_DETECT_MODE1 0x30 +#define WM8995_ACCESSORY_DETECT_MODE2 0x31 +#define WM8995_HEADPHONE_DETECT1 0x34 +#define WM8995_HEADPHONE_DETECT2 0x35 +#define WM8995_MIC_DETECT_1 0x38 +#define WM8995_MIC_DETECT_2 0x39 +#define WM8995_CHARGE_PUMP_1 0x40 +#define WM8995_CLASS_W_1 0x45 +#define WM8995_DC_SERVO_1 0x50 +#define WM8995_DC_SERVO_2 0x51 +#define WM8995_DC_SERVO_3 0x52 +#define WM8995_DC_SERVO_5 0x54 +#define WM8995_DC_SERVO_6 0x55 +#define WM8995_DC_SERVO_7 0x56 +#define WM8995_DC_SERVO_READBACK_0 0x57 +#define WM8995_ANALOGUE_HP_1 0x60 +#define WM8995_ANALOGUE_HP_2 0x61 +#define WM8995_CHIP_REVISION 0x100 +#define WM8995_CONTROL_INTERFACE_1 0x101 +#define WM8995_CONTROL_INTERFACE_2 0x102 +#define WM8995_WRITE_SEQUENCER_CTRL_1 0x110 +#define WM8995_WRITE_SEQUENCER_CTRL_2 0x111 +#define WM8995_AIF1_CLOCKING_1 0x200 +#define WM8995_AIF1_CLOCKING_2 0x201 +#define WM8995_AIF2_CLOCKING_1 0x204 +#define WM8995_AIF2_CLOCKING_2 0x205 +#define WM8995_CLOCKING_1 0x208 +#define WM8995_CLOCKING_2 0x209 +#define WM8995_AIF1_RATE 0x210 +#define WM8995_AIF2_RATE 0x211 +#define WM8995_RATE_STATUS 0x212 +#define WM8995_FLL1_CONTROL_1 0x220 +#define WM8995_FLL1_CONTROL_2 0x221 +#define WM8995_FLL1_CONTROL_3 0x222 +#define WM8995_FLL1_CONTROL_4 0x223 +#define WM8995_FLL1_CONTROL_5 0x224 +#define WM8995_FLL2_CONTROL_1 0x240 +#define WM8995_FLL2_CONTROL_2 0x241 +#define WM8995_FLL2_CONTROL_3 0x242 +#define WM8995_FLL2_CONTROL_4 0x243 +#define WM8995_FLL2_CONTROL_5 0x244 +#define WM8995_AIF1_CONTROL_1 0x300 +#define WM8995_AIF1_CONTROL_2 0x301 +#define WM8995_AIF1_MASTER_SLAVE 0x302 +#define WM8995_AIF1_BCLK 0x303 +#define WM8995_AIF1ADC_LRCLK 0x304 +#define WM8995_AIF1DAC_LRCLK 0x305 +#define WM8995_AIF1DAC_DATA 0x306 +#define WM8995_AIF1ADC_DATA 0x307 +#define WM8995_AIF2_CONTROL_1 0x310 +#define WM8995_AIF2_CONTROL_2 0x311 +#define WM8995_AIF2_MASTER_SLAVE 0x312 +#define WM8995_AIF2_BCLK 0x313 +#define WM8995_AIF2ADC_LRCLK 0x314 +#define WM8995_AIF2DAC_LRCLK 0x315 +#define WM8995_AIF2DAC_DATA 0x316 +#define WM8995_AIF2ADC_DATA 0x317 +#define WM8995_AIF1_ADC1_LEFT_VOLUME 0x400 +#define WM8995_AIF1_ADC1_RIGHT_VOLUME 0x401 +#define WM8995_AIF1_DAC1_LEFT_VOLUME 0x402 +#define WM8995_AIF1_DAC1_RIGHT_VOLUME 0x403 +#define WM8995_AIF1_ADC2_LEFT_VOLUME 0x404 +#define WM8995_AIF1_ADC2_RIGHT_VOLUME 0x405 +#define WM8995_AIF1_DAC2_LEFT_VOLUME 0x406 +#define WM8995_AIF1_DAC2_RIGHT_VOLUME 0x407 +#define WM8995_AIF1_ADC1_FILTERS 0x410 +#define WM8995_AIF1_ADC2_FILTERS 0x411 +#define WM8995_AIF1_DAC1_FILTERS_1 0x420 +#define WM8995_AIF1_DAC1_FILTERS_2 0x421 +#define WM8995_AIF1_DAC2_FILTERS_1 0x422 +#define WM8995_AIF1_DAC2_FILTERS_2 0x423 +#define WM8995_AIF1_DRC1_1 0x440 +#define WM8995_AIF1_DRC1_2 0x441 +#define WM8995_AIF1_DRC1_3 0x442 +#define WM8995_AIF1_DRC1_4 0x443 +#define WM8995_AIF1_DRC1_5 0x444 +#define WM8995_AIF1_DRC2_1 0x450 +#define WM8995_AIF1_DRC2_2 0x451 +#define WM8995_AIF1_DRC2_3 0x452 +#define WM8995_AIF1_DRC2_4 0x453 +#define WM8995_AIF1_DRC2_5 0x454 +#define WM8995_AIF1_DAC1_EQ_GAINS_1 0x480 +#define WM8995_AIF1_DAC1_EQ_GAINS_2 0x481 +#define WM8995_AIF1_DAC1_EQ_BAND_1_A 0x482 +#define WM8995_AIF1_DAC1_EQ_BAND_1_B 0x483 +#define WM8995_AIF1_DAC1_EQ_BAND_1_PG 0x484 +#define WM8995_AIF1_DAC1_EQ_BAND_2_A 0x485 +#define WM8995_AIF1_DAC1_EQ_BAND_2_B 0x486 +#define WM8995_AIF1_DAC1_EQ_BAND_2_C 0x487 +#define WM8995_AIF1_DAC1_EQ_BAND_2_PG 0x488 +#define WM8995_AIF1_DAC1_EQ_BAND_3_A 0x489 +#define WM8995_AIF1_DAC1_EQ_BAND_3_B 0x48A +#define WM8995_AIF1_DAC1_EQ_BAND_3_C 0x48B +#define WM8995_AIF1_DAC1_EQ_BAND_3_PG 0x48C +#define WM8995_AIF1_DAC1_EQ_BAND_4_A 0x48D +#define WM8995_AIF1_DAC1_EQ_BAND_4_B 0x48E +#define WM8995_AIF1_DAC1_EQ_BAND_4_C 0x48F +#define WM8995_AIF1_DAC1_EQ_BAND_4_PG 0x490 +#define WM8995_AIF1_DAC1_EQ_BAND_5_A 0x491 +#define WM8995_AIF1_DAC1_EQ_BAND_5_B 0x492 +#define WM8995_AIF1_DAC1_EQ_BAND_5_PG 0x493 +#define WM8995_AIF1_DAC2_EQ_GAINS_1 0x4A0 +#define WM8995_AIF1_DAC2_EQ_GAINS_2 0x4A1 +#define WM8995_AIF1_DAC2_EQ_BAND_1_A 0x4A2 +#define WM8995_AIF1_DAC2_EQ_BAND_1_B 0x4A3 +#define WM8995_AIF1_DAC2_EQ_BAND_1_PG 0x4A4 +#define WM8995_AIF1_DAC2_EQ_BAND_2_A 0x4A5 +#define WM8995_AIF1_DAC2_EQ_BAND_2_B 0x4A6 +#define WM8995_AIF1_DAC2_EQ_BAND_2_C 0x4A7 +#define WM8995_AIF1_DAC2_EQ_BAND_2_PG 0x4A8 +#define WM8995_AIF1_DAC2_EQ_BAND_3_A 0x4A9 +#define WM8995_AIF1_DAC2_EQ_BAND_3_B 0x4AA +#define WM8995_AIF1_DAC2_EQ_BAND_3_C 0x4AB +#define WM8995_AIF1_DAC2_EQ_BAND_3_PG 0x4AC +#define WM8995_AIF1_DAC2_EQ_BAND_4_A 0x4AD +#define WM8995_AIF1_DAC2_EQ_BAND_4_B 0x4AE +#define WM8995_AIF1_DAC2_EQ_BAND_4_C 0x4AF +#define WM8995_AIF1_DAC2_EQ_BAND_4_PG 0x4B0 +#define WM8995_AIF1_DAC2_EQ_BAND_5_A 0x4B1 +#define WM8995_AIF1_DAC2_EQ_BAND_5_B 0x4B2 +#define WM8995_AIF1_DAC2_EQ_BAND_5_PG 0x4B3 +#define WM8995_AIF2_ADC_LEFT_VOLUME 0x500 +#define WM8995_AIF2_ADC_RIGHT_VOLUME 0x501 +#define WM8995_AIF2_DAC_LEFT_VOLUME 0x502 +#define WM8995_AIF2_DAC_RIGHT_VOLUME 0x503 +#define WM8995_AIF2_ADC_FILTERS 0x510 +#define WM8995_AIF2_DAC_FILTERS_1 0x520 +#define WM8995_AIF2_DAC_FILTERS_2 0x521 +#define WM8995_AIF2_DRC_1 0x540 +#define WM8995_AIF2_DRC_2 0x541 +#define WM8995_AIF2_DRC_3 0x542 +#define WM8995_AIF2_DRC_4 0x543 +#define WM8995_AIF2_DRC_5 0x544 +#define WM8995_AIF2_EQ_GAINS_1 0x580 +#define WM8995_AIF2_EQ_GAINS_2 0x581 +#define WM8995_AIF2_EQ_BAND_1_A 0x582 +#define WM8995_AIF2_EQ_BAND_1_B 0x583 +#define WM8995_AIF2_EQ_BAND_1_PG 0x584 +#define WM8995_AIF2_EQ_BAND_2_A 0x585 +#define WM8995_AIF2_EQ_BAND_2_B 0x586 +#define WM8995_AIF2_EQ_BAND_2_C 0x587 +#define WM8995_AIF2_EQ_BAND_2_PG 0x588 +#define WM8995_AIF2_EQ_BAND_3_A 0x589 +#define WM8995_AIF2_EQ_BAND_3_B 0x58A +#define WM8995_AIF2_EQ_BAND_3_C 0x58B +#define WM8995_AIF2_EQ_BAND_3_PG 0x58C +#define WM8995_AIF2_EQ_BAND_4_A 0x58D +#define WM8995_AIF2_EQ_BAND_4_B 0x58E +#define WM8995_AIF2_EQ_BAND_4_C 0x58F +#define WM8995_AIF2_EQ_BAND_4_PG 0x590 +#define WM8995_AIF2_EQ_BAND_5_A 0x591 +#define WM8995_AIF2_EQ_BAND_5_B 0x592 +#define WM8995_AIF2_EQ_BAND_5_PG 0x593 +#define WM8995_DAC1_MIXER_VOLUMES 0x600 +#define WM8995_DAC1_LEFT_MIXER_ROUTING 0x601 +#define WM8995_DAC1_RIGHT_MIXER_ROUTING 0x602 +#define WM8995_DAC2_MIXER_VOLUMES 0x603 +#define WM8995_DAC2_LEFT_MIXER_ROUTING 0x604 +#define WM8995_DAC2_RIGHT_MIXER_ROUTING 0x605 +#define WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING 0x606 +#define WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING 0x607 +#define WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING 0x608 +#define WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING 0x609 +#define WM8995_DAC_SOFTMUTE 0x610 +#define WM8995_OVERSAMPLING 0x620 +#define WM8995_SIDETONE 0x621 +#define WM8995_GPIO_1 0x700 +#define WM8995_GPIO_2 0x701 +#define WM8995_GPIO_3 0x702 +#define WM8995_GPIO_4 0x703 +#define WM8995_GPIO_5 0x704 +#define WM8995_GPIO_6 0x705 +#define WM8995_GPIO_7 0x706 +#define WM8995_GPIO_8 0x707 +#define WM8995_GPIO_9 0x708 +#define WM8995_GPIO_10 0x709 +#define WM8995_GPIO_11 0x70A +#define WM8995_GPIO_12 0x70B +#define WM8995_GPIO_13 0x70C +#define WM8995_GPIO_14 0x70D +#define WM8995_PULL_CONTROL_1 0x720 +#define WM8995_PULL_CONTROL_2 0x721 +#define WM8995_INTERRUPT_STATUS_1 0x730 +#define WM8995_INTERRUPT_STATUS_2 0x731 +#define WM8995_INTERRUPT_RAW_STATUS_2 0x732 +#define WM8995_INTERRUPT_STATUS_1_MASK 0x738 +#define WM8995_INTERRUPT_STATUS_2_MASK 0x739 +#define WM8995_INTERRUPT_CONTROL 0x740 +#define WM8995_LEFT_PDM_SPEAKER_1 0x800 +#define WM8995_RIGHT_PDM_SPEAKER_1 0x801 +#define WM8995_PDM_SPEAKER_1_MUTE_SEQUENCE 0x802 +#define WM8995_LEFT_PDM_SPEAKER_2 0x808 +#define WM8995_RIGHT_PDM_SPEAKER_2 0x809 +#define WM8995_PDM_SPEAKER_2_MUTE_SEQUENCE 0x80A +#define WM8995_WRITE_SEQUENCER_0 0x3000 +#define WM8995_WRITE_SEQUENCER_1 0x3001 +#define WM8995_WRITE_SEQUENCER_2 0x3002 +#define WM8995_WRITE_SEQUENCER_3 0x3003 +#define WM8995_WRITE_SEQUENCER_4 0x3004 +#define WM8995_WRITE_SEQUENCER_5 0x3005 +#define WM8995_WRITE_SEQUENCER_6 0x3006 +#define WM8995_WRITE_SEQUENCER_7 0x3007 +#define WM8995_WRITE_SEQUENCER_8 0x3008 +#define WM8995_WRITE_SEQUENCER_9 0x3009 +#define WM8995_WRITE_SEQUENCER_10 0x300A +#define WM8995_WRITE_SEQUENCER_11 0x300B +#define WM8995_WRITE_SEQUENCER_12 0x300C +#define WM8995_WRITE_SEQUENCER_13 0x300D +#define WM8995_WRITE_SEQUENCER_14 0x300E +#define WM8995_WRITE_SEQUENCER_15 0x300F +#define WM8995_WRITE_SEQUENCER_16 0x3010 +#define WM8995_WRITE_SEQUENCER_17 0x3011 +#define WM8995_WRITE_SEQUENCER_18 0x3012 +#define WM8995_WRITE_SEQUENCER_19 0x3013 +#define WM8995_WRITE_SEQUENCER_20 0x3014 +#define WM8995_WRITE_SEQUENCER_21 0x3015 +#define WM8995_WRITE_SEQUENCER_22 0x3016 +#define WM8995_WRITE_SEQUENCER_23 0x3017 +#define WM8995_WRITE_SEQUENCER_24 0x3018 +#define WM8995_WRITE_SEQUENCER_25 0x3019 +#define WM8995_WRITE_SEQUENCER_26 0x301A +#define WM8995_WRITE_SEQUENCER_27 0x301B +#define WM8995_WRITE_SEQUENCER_28 0x301C +#define WM8995_WRITE_SEQUENCER_29 0x301D +#define WM8995_WRITE_SEQUENCER_30 0x301E +#define WM8995_WRITE_SEQUENCER_31 0x301F +#define WM8995_WRITE_SEQUENCER_32 0x3020 +#define WM8995_WRITE_SEQUENCER_33 0x3021 +#define WM8995_WRITE_SEQUENCER_34 0x3022 +#define WM8995_WRITE_SEQUENCER_35 0x3023 +#define WM8995_WRITE_SEQUENCER_36 0x3024 +#define WM8995_WRITE_SEQUENCER_37 0x3025 +#define WM8995_WRITE_SEQUENCER_38 0x3026 +#define WM8995_WRITE_SEQUENCER_39 0x3027 +#define WM8995_WRITE_SEQUENCER_40 0x3028 +#define WM8995_WRITE_SEQUENCER_41 0x3029 +#define WM8995_WRITE_SEQUENCER_42 0x302A +#define WM8995_WRITE_SEQUENCER_43 0x302B +#define WM8995_WRITE_SEQUENCER_44 0x302C +#define WM8995_WRITE_SEQUENCER_45 0x302D +#define WM8995_WRITE_SEQUENCER_46 0x302E +#define WM8995_WRITE_SEQUENCER_47 0x302F +#define WM8995_WRITE_SEQUENCER_48 0x3030 +#define WM8995_WRITE_SEQUENCER_49 0x3031 +#define WM8995_WRITE_SEQUENCER_50 0x3032 +#define WM8995_WRITE_SEQUENCER_51 0x3033 +#define WM8995_WRITE_SEQUENCER_52 0x3034 +#define WM8995_WRITE_SEQUENCER_53 0x3035 +#define WM8995_WRITE_SEQUENCER_54 0x3036 +#define WM8995_WRITE_SEQUENCER_55 0x3037 +#define WM8995_WRITE_SEQUENCER_56 0x3038 +#define WM8995_WRITE_SEQUENCER_57 0x3039 +#define WM8995_WRITE_SEQUENCER_58 0x303A +#define WM8995_WRITE_SEQUENCER_59 0x303B +#define WM8995_WRITE_SEQUENCER_60 0x303C +#define WM8995_WRITE_SEQUENCER_61 0x303D +#define WM8995_WRITE_SEQUENCER_62 0x303E +#define WM8995_WRITE_SEQUENCER_63 0x303F +#define WM8995_WRITE_SEQUENCER_64 0x3040 +#define WM8995_WRITE_SEQUENCER_65 0x3041 +#define WM8995_WRITE_SEQUENCER_66 0x3042 +#define WM8995_WRITE_SEQUENCER_67 0x3043 +#define WM8995_WRITE_SEQUENCER_68 0x3044 +#define WM8995_WRITE_SEQUENCER_69 0x3045 +#define WM8995_WRITE_SEQUENCER_70 0x3046 +#define WM8995_WRITE_SEQUENCER_71 0x3047 +#define WM8995_WRITE_SEQUENCER_72 0x3048 +#define WM8995_WRITE_SEQUENCER_73 0x3049 +#define WM8995_WRITE_SEQUENCER_74 0x304A +#define WM8995_WRITE_SEQUENCER_75 0x304B +#define WM8995_WRITE_SEQUENCER_76 0x304C +#define WM8995_WRITE_SEQUENCER_77 0x304D +#define WM8995_WRITE_SEQUENCER_78 0x304E +#define WM8995_WRITE_SEQUENCER_79 0x304F +#define WM8995_WRITE_SEQUENCER_80 0x3050 +#define WM8995_WRITE_SEQUENCER_81 0x3051 +#define WM8995_WRITE_SEQUENCER_82 0x3052 +#define WM8995_WRITE_SEQUENCER_83 0x3053 +#define WM8995_WRITE_SEQUENCER_84 0x3054 +#define WM8995_WRITE_SEQUENCER_85 0x3055 +#define WM8995_WRITE_SEQUENCER_86 0x3056 +#define WM8995_WRITE_SEQUENCER_87 0x3057 +#define WM8995_WRITE_SEQUENCER_88 0x3058 +#define WM8995_WRITE_SEQUENCER_89 0x3059 +#define WM8995_WRITE_SEQUENCER_90 0x305A +#define WM8995_WRITE_SEQUENCER_91 0x305B +#define WM8995_WRITE_SEQUENCER_92 0x305C +#define WM8995_WRITE_SEQUENCER_93 0x305D +#define WM8995_WRITE_SEQUENCER_94 0x305E +#define WM8995_WRITE_SEQUENCER_95 0x305F +#define WM8995_WRITE_SEQUENCER_96 0x3060 +#define WM8995_WRITE_SEQUENCER_97 0x3061 +#define WM8995_WRITE_SEQUENCER_98 0x3062 +#define WM8995_WRITE_SEQUENCER_99 0x3063 +#define WM8995_WRITE_SEQUENCER_100 0x3064 +#define WM8995_WRITE_SEQUENCER_101 0x3065 +#define WM8995_WRITE_SEQUENCER_102 0x3066 +#define WM8995_WRITE_SEQUENCER_103 0x3067 +#define WM8995_WRITE_SEQUENCER_104 0x3068 +#define WM8995_WRITE_SEQUENCER_105 0x3069 +#define WM8995_WRITE_SEQUENCER_106 0x306A +#define WM8995_WRITE_SEQUENCER_107 0x306B +#define WM8995_WRITE_SEQUENCER_108 0x306C +#define WM8995_WRITE_SEQUENCER_109 0x306D +#define WM8995_WRITE_SEQUENCER_110 0x306E +#define WM8995_WRITE_SEQUENCER_111 0x306F +#define WM8995_WRITE_SEQUENCER_112 0x3070 +#define WM8995_WRITE_SEQUENCER_113 0x3071 +#define WM8995_WRITE_SEQUENCER_114 0x3072 +#define WM8995_WRITE_SEQUENCER_115 0x3073 +#define WM8995_WRITE_SEQUENCER_116 0x3074 +#define WM8995_WRITE_SEQUENCER_117 0x3075 +#define WM8995_WRITE_SEQUENCER_118 0x3076 +#define WM8995_WRITE_SEQUENCER_119 0x3077 +#define WM8995_WRITE_SEQUENCER_120 0x3078 +#define WM8995_WRITE_SEQUENCER_121 0x3079 +#define WM8995_WRITE_SEQUENCER_122 0x307A +#define WM8995_WRITE_SEQUENCER_123 0x307B +#define WM8995_WRITE_SEQUENCER_124 0x307C +#define WM8995_WRITE_SEQUENCER_125 0x307D +#define WM8995_WRITE_SEQUENCER_126 0x307E +#define WM8995_WRITE_SEQUENCER_127 0x307F +#define WM8995_WRITE_SEQUENCER_128 0x3080 +#define WM8995_WRITE_SEQUENCER_129 0x3081 +#define WM8995_WRITE_SEQUENCER_130 0x3082 +#define WM8995_WRITE_SEQUENCER_131 0x3083 +#define WM8995_WRITE_SEQUENCER_132 0x3084 +#define WM8995_WRITE_SEQUENCER_133 0x3085 +#define WM8995_WRITE_SEQUENCER_134 0x3086 +#define WM8995_WRITE_SEQUENCER_135 0x3087 +#define WM8995_WRITE_SEQUENCER_136 0x3088 +#define WM8995_WRITE_SEQUENCER_137 0x3089 +#define WM8995_WRITE_SEQUENCER_138 0x308A +#define WM8995_WRITE_SEQUENCER_139 0x308B +#define WM8995_WRITE_SEQUENCER_140 0x308C +#define WM8995_WRITE_SEQUENCER_141 0x308D +#define WM8995_WRITE_SEQUENCER_142 0x308E +#define WM8995_WRITE_SEQUENCER_143 0x308F +#define WM8995_WRITE_SEQUENCER_144 0x3090 +#define WM8995_WRITE_SEQUENCER_145 0x3091 +#define WM8995_WRITE_SEQUENCER_146 0x3092 +#define WM8995_WRITE_SEQUENCER_147 0x3093 +#define WM8995_WRITE_SEQUENCER_148 0x3094 +#define WM8995_WRITE_SEQUENCER_149 0x3095 +#define WM8995_WRITE_SEQUENCER_150 0x3096 +#define WM8995_WRITE_SEQUENCER_151 0x3097 +#define WM8995_WRITE_SEQUENCER_152 0x3098 +#define WM8995_WRITE_SEQUENCER_153 0x3099 +#define WM8995_WRITE_SEQUENCER_154 0x309A +#define WM8995_WRITE_SEQUENCER_155 0x309B +#define WM8995_WRITE_SEQUENCER_156 0x309C +#define WM8995_WRITE_SEQUENCER_157 0x309D +#define WM8995_WRITE_SEQUENCER_158 0x309E +#define WM8995_WRITE_SEQUENCER_159 0x309F +#define WM8995_WRITE_SEQUENCER_160 0x30A0 +#define WM8995_WRITE_SEQUENCER_161 0x30A1 +#define WM8995_WRITE_SEQUENCER_162 0x30A2 +#define WM8995_WRITE_SEQUENCER_163 0x30A3 +#define WM8995_WRITE_SEQUENCER_164 0x30A4 +#define WM8995_WRITE_SEQUENCER_165 0x30A5 +#define WM8995_WRITE_SEQUENCER_166 0x30A6 +#define WM8995_WRITE_SEQUENCER_167 0x30A7 +#define WM8995_WRITE_SEQUENCER_168 0x30A8 +#define WM8995_WRITE_SEQUENCER_169 0x30A9 +#define WM8995_WRITE_SEQUENCER_170 0x30AA +#define WM8995_WRITE_SEQUENCER_171 0x30AB +#define WM8995_WRITE_SEQUENCER_172 0x30AC +#define WM8995_WRITE_SEQUENCER_173 0x30AD +#define WM8995_WRITE_SEQUENCER_174 0x30AE +#define WM8995_WRITE_SEQUENCER_175 0x30AF +#define WM8995_WRITE_SEQUENCER_176 0x30B0 +#define WM8995_WRITE_SEQUENCER_177 0x30B1 +#define WM8995_WRITE_SEQUENCER_178 0x30B2 +#define WM8995_WRITE_SEQUENCER_179 0x30B3 +#define WM8995_WRITE_SEQUENCER_180 0x30B4 +#define WM8995_WRITE_SEQUENCER_181 0x30B5 +#define WM8995_WRITE_SEQUENCER_182 0x30B6 +#define WM8995_WRITE_SEQUENCER_183 0x30B7 +#define WM8995_WRITE_SEQUENCER_184 0x30B8 +#define WM8995_WRITE_SEQUENCER_185 0x30B9 +#define WM8995_WRITE_SEQUENCER_186 0x30BA +#define WM8995_WRITE_SEQUENCER_187 0x30BB +#define WM8995_WRITE_SEQUENCER_188 0x30BC +#define WM8995_WRITE_SEQUENCER_189 0x30BD +#define WM8995_WRITE_SEQUENCER_190 0x30BE +#define WM8995_WRITE_SEQUENCER_191 0x30BF +#define WM8995_WRITE_SEQUENCER_192 0x30C0 +#define WM8995_WRITE_SEQUENCER_193 0x30C1 +#define WM8995_WRITE_SEQUENCER_194 0x30C2 +#define WM8995_WRITE_SEQUENCER_195 0x30C3 +#define WM8995_WRITE_SEQUENCER_196 0x30C4 +#define WM8995_WRITE_SEQUENCER_197 0x30C5 +#define WM8995_WRITE_SEQUENCER_198 0x30C6 +#define WM8995_WRITE_SEQUENCER_199 0x30C7 +#define WM8995_WRITE_SEQUENCER_200 0x30C8 +#define WM8995_WRITE_SEQUENCER_201 0x30C9 +#define WM8995_WRITE_SEQUENCER_202 0x30CA +#define WM8995_WRITE_SEQUENCER_203 0x30CB +#define WM8995_WRITE_SEQUENCER_204 0x30CC +#define WM8995_WRITE_SEQUENCER_205 0x30CD +#define WM8995_WRITE_SEQUENCER_206 0x30CE +#define WM8995_WRITE_SEQUENCER_207 0x30CF +#define WM8995_WRITE_SEQUENCER_208 0x30D0 +#define WM8995_WRITE_SEQUENCER_209 0x30D1 +#define WM8995_WRITE_SEQUENCER_210 0x30D2 +#define WM8995_WRITE_SEQUENCER_211 0x30D3 +#define WM8995_WRITE_SEQUENCER_212 0x30D4 +#define WM8995_WRITE_SEQUENCER_213 0x30D5 +#define WM8995_WRITE_SEQUENCER_214 0x30D6 +#define WM8995_WRITE_SEQUENCER_215 0x30D7 +#define WM8995_WRITE_SEQUENCER_216 0x30D8 +#define WM8995_WRITE_SEQUENCER_217 0x30D9 +#define WM8995_WRITE_SEQUENCER_218 0x30DA +#define WM8995_WRITE_SEQUENCER_219 0x30DB +#define WM8995_WRITE_SEQUENCER_220 0x30DC +#define WM8995_WRITE_SEQUENCER_221 0x30DD +#define WM8995_WRITE_SEQUENCER_222 0x30DE +#define WM8995_WRITE_SEQUENCER_223 0x30DF +#define WM8995_WRITE_SEQUENCER_224 0x30E0 +#define WM8995_WRITE_SEQUENCER_225 0x30E1 +#define WM8995_WRITE_SEQUENCER_226 0x30E2 +#define WM8995_WRITE_SEQUENCER_227 0x30E3 +#define WM8995_WRITE_SEQUENCER_228 0x30E4 +#define WM8995_WRITE_SEQUENCER_229 0x30E5 +#define WM8995_WRITE_SEQUENCER_230 0x30E6 +#define WM8995_WRITE_SEQUENCER_231 0x30E7 +#define WM8995_WRITE_SEQUENCER_232 0x30E8 +#define WM8995_WRITE_SEQUENCER_233 0x30E9 +#define WM8995_WRITE_SEQUENCER_234 0x30EA +#define WM8995_WRITE_SEQUENCER_235 0x30EB +#define WM8995_WRITE_SEQUENCER_236 0x30EC +#define WM8995_WRITE_SEQUENCER_237 0x30ED +#define WM8995_WRITE_SEQUENCER_238 0x30EE +#define WM8995_WRITE_SEQUENCER_239 0x30EF +#define WM8995_WRITE_SEQUENCER_240 0x30F0 +#define WM8995_WRITE_SEQUENCER_241 0x30F1 +#define WM8995_WRITE_SEQUENCER_242 0x30F2 +#define WM8995_WRITE_SEQUENCER_243 0x30F3 +#define WM8995_WRITE_SEQUENCER_244 0x30F4 +#define WM8995_WRITE_SEQUENCER_245 0x30F5 +#define WM8995_WRITE_SEQUENCER_246 0x30F6 +#define WM8995_WRITE_SEQUENCER_247 0x30F7 +#define WM8995_WRITE_SEQUENCER_248 0x30F8 +#define WM8995_WRITE_SEQUENCER_249 0x30F9 +#define WM8995_WRITE_SEQUENCER_250 0x30FA +#define WM8995_WRITE_SEQUENCER_251 0x30FB +#define WM8995_WRITE_SEQUENCER_252 0x30FC +#define WM8995_WRITE_SEQUENCER_253 0x30FD +#define WM8995_WRITE_SEQUENCER_254 0x30FE +#define WM8995_WRITE_SEQUENCER_255 0x30FF +#define WM8995_WRITE_SEQUENCER_256 0x3100 +#define WM8995_WRITE_SEQUENCER_257 0x3101 +#define WM8995_WRITE_SEQUENCER_258 0x3102 +#define WM8995_WRITE_SEQUENCER_259 0x3103 +#define WM8995_WRITE_SEQUENCER_260 0x3104 +#define WM8995_WRITE_SEQUENCER_261 0x3105 +#define WM8995_WRITE_SEQUENCER_262 0x3106 +#define WM8995_WRITE_SEQUENCER_263 0x3107 +#define WM8995_WRITE_SEQUENCER_264 0x3108 +#define WM8995_WRITE_SEQUENCER_265 0x3109 +#define WM8995_WRITE_SEQUENCER_266 0x310A +#define WM8995_WRITE_SEQUENCER_267 0x310B +#define WM8995_WRITE_SEQUENCER_268 0x310C +#define WM8995_WRITE_SEQUENCER_269 0x310D +#define WM8995_WRITE_SEQUENCER_270 0x310E +#define WM8995_WRITE_SEQUENCER_271 0x310F +#define WM8995_WRITE_SEQUENCER_272 0x3110 +#define WM8995_WRITE_SEQUENCER_273 0x3111 +#define WM8995_WRITE_SEQUENCER_274 0x3112 +#define WM8995_WRITE_SEQUENCER_275 0x3113 +#define WM8995_WRITE_SEQUENCER_276 0x3114 +#define WM8995_WRITE_SEQUENCER_277 0x3115 +#define WM8995_WRITE_SEQUENCER_278 0x3116 +#define WM8995_WRITE_SEQUENCER_279 0x3117 +#define WM8995_WRITE_SEQUENCER_280 0x3118 +#define WM8995_WRITE_SEQUENCER_281 0x3119 +#define WM8995_WRITE_SEQUENCER_282 0x311A +#define WM8995_WRITE_SEQUENCER_283 0x311B +#define WM8995_WRITE_SEQUENCER_284 0x311C +#define WM8995_WRITE_SEQUENCER_285 0x311D +#define WM8995_WRITE_SEQUENCER_286 0x311E +#define WM8995_WRITE_SEQUENCER_287 0x311F +#define WM8995_WRITE_SEQUENCER_288 0x3120 +#define WM8995_WRITE_SEQUENCER_289 0x3121 +#define WM8995_WRITE_SEQUENCER_290 0x3122 +#define WM8995_WRITE_SEQUENCER_291 0x3123 +#define WM8995_WRITE_SEQUENCER_292 0x3124 +#define WM8995_WRITE_SEQUENCER_293 0x3125 +#define WM8995_WRITE_SEQUENCER_294 0x3126 +#define WM8995_WRITE_SEQUENCER_295 0x3127 +#define WM8995_WRITE_SEQUENCER_296 0x3128 +#define WM8995_WRITE_SEQUENCER_297 0x3129 +#define WM8995_WRITE_SEQUENCER_298 0x312A +#define WM8995_WRITE_SEQUENCER_299 0x312B +#define WM8995_WRITE_SEQUENCER_300 0x312C +#define WM8995_WRITE_SEQUENCER_301 0x312D +#define WM8995_WRITE_SEQUENCER_302 0x312E +#define WM8995_WRITE_SEQUENCER_303 0x312F +#define WM8995_WRITE_SEQUENCER_304 0x3130 +#define WM8995_WRITE_SEQUENCER_305 0x3131 +#define WM8995_WRITE_SEQUENCER_306 0x3132 +#define WM8995_WRITE_SEQUENCER_307 0x3133 +#define WM8995_WRITE_SEQUENCER_308 0x3134 +#define WM8995_WRITE_SEQUENCER_309 0x3135 +#define WM8995_WRITE_SEQUENCER_310 0x3136 +#define WM8995_WRITE_SEQUENCER_311 0x3137 +#define WM8995_WRITE_SEQUENCER_312 0x3138 +#define WM8995_WRITE_SEQUENCER_313 0x3139 +#define WM8995_WRITE_SEQUENCER_314 0x313A +#define WM8995_WRITE_SEQUENCER_315 0x313B +#define WM8995_WRITE_SEQUENCER_316 0x313C +#define WM8995_WRITE_SEQUENCER_317 0x313D +#define WM8995_WRITE_SEQUENCER_318 0x313E +#define WM8995_WRITE_SEQUENCER_319 0x313F +#define WM8995_WRITE_SEQUENCER_320 0x3140 +#define WM8995_WRITE_SEQUENCER_321 0x3141 +#define WM8995_WRITE_SEQUENCER_322 0x3142 +#define WM8995_WRITE_SEQUENCER_323 0x3143 +#define WM8995_WRITE_SEQUENCER_324 0x3144 +#define WM8995_WRITE_SEQUENCER_325 0x3145 +#define WM8995_WRITE_SEQUENCER_326 0x3146 +#define WM8995_WRITE_SEQUENCER_327 0x3147 +#define WM8995_WRITE_SEQUENCER_328 0x3148 +#define WM8995_WRITE_SEQUENCER_329 0x3149 +#define WM8995_WRITE_SEQUENCER_330 0x314A +#define WM8995_WRITE_SEQUENCER_331 0x314B +#define WM8995_WRITE_SEQUENCER_332 0x314C +#define WM8995_WRITE_SEQUENCER_333 0x314D +#define WM8995_WRITE_SEQUENCER_334 0x314E +#define WM8995_WRITE_SEQUENCER_335 0x314F +#define WM8995_WRITE_SEQUENCER_336 0x3150 +#define WM8995_WRITE_SEQUENCER_337 0x3151 +#define WM8995_WRITE_SEQUENCER_338 0x3152 +#define WM8995_WRITE_SEQUENCER_339 0x3153 +#define WM8995_WRITE_SEQUENCER_340 0x3154 +#define WM8995_WRITE_SEQUENCER_341 0x3155 +#define WM8995_WRITE_SEQUENCER_342 0x3156 +#define WM8995_WRITE_SEQUENCER_343 0x3157 +#define WM8995_WRITE_SEQUENCER_344 0x3158 +#define WM8995_WRITE_SEQUENCER_345 0x3159 +#define WM8995_WRITE_SEQUENCER_346 0x315A +#define WM8995_WRITE_SEQUENCER_347 0x315B +#define WM8995_WRITE_SEQUENCER_348 0x315C +#define WM8995_WRITE_SEQUENCER_349 0x315D +#define WM8995_WRITE_SEQUENCER_350 0x315E +#define WM8995_WRITE_SEQUENCER_351 0x315F +#define WM8995_WRITE_SEQUENCER_352 0x3160 +#define WM8995_WRITE_SEQUENCER_353 0x3161 +#define WM8995_WRITE_SEQUENCER_354 0x3162 +#define WM8995_WRITE_SEQUENCER_355 0x3163 +#define WM8995_WRITE_SEQUENCER_356 0x3164 +#define WM8995_WRITE_SEQUENCER_357 0x3165 +#define WM8995_WRITE_SEQUENCER_358 0x3166 +#define WM8995_WRITE_SEQUENCER_359 0x3167 +#define WM8995_WRITE_SEQUENCER_360 0x3168 +#define WM8995_WRITE_SEQUENCER_361 0x3169 +#define WM8995_WRITE_SEQUENCER_362 0x316A +#define WM8995_WRITE_SEQUENCER_363 0x316B +#define WM8995_WRITE_SEQUENCER_364 0x316C +#define WM8995_WRITE_SEQUENCER_365 0x316D +#define WM8995_WRITE_SEQUENCER_366 0x316E +#define WM8995_WRITE_SEQUENCER_367 0x316F +#define WM8995_WRITE_SEQUENCER_368 0x3170 +#define WM8995_WRITE_SEQUENCER_369 0x3171 +#define WM8995_WRITE_SEQUENCER_370 0x3172 +#define WM8995_WRITE_SEQUENCER_371 0x3173 +#define WM8995_WRITE_SEQUENCER_372 0x3174 +#define WM8995_WRITE_SEQUENCER_373 0x3175 +#define WM8995_WRITE_SEQUENCER_374 0x3176 +#define WM8995_WRITE_SEQUENCER_375 0x3177 +#define WM8995_WRITE_SEQUENCER_376 0x3178 +#define WM8995_WRITE_SEQUENCER_377 0x3179 +#define WM8995_WRITE_SEQUENCER_378 0x317A +#define WM8995_WRITE_SEQUENCER_379 0x317B +#define WM8995_WRITE_SEQUENCER_380 0x317C +#define WM8995_WRITE_SEQUENCER_381 0x317D +#define WM8995_WRITE_SEQUENCER_382 0x317E +#define WM8995_WRITE_SEQUENCER_383 0x317F +#define WM8995_WRITE_SEQUENCER_384 0x3180 +#define WM8995_WRITE_SEQUENCER_385 0x3181 +#define WM8995_WRITE_SEQUENCER_386 0x3182 +#define WM8995_WRITE_SEQUENCER_387 0x3183 +#define WM8995_WRITE_SEQUENCER_388 0x3184 +#define WM8995_WRITE_SEQUENCER_389 0x3185 +#define WM8995_WRITE_SEQUENCER_390 0x3186 +#define WM8995_WRITE_SEQUENCER_391 0x3187 +#define WM8995_WRITE_SEQUENCER_392 0x3188 +#define WM8995_WRITE_SEQUENCER_393 0x3189 +#define WM8995_WRITE_SEQUENCER_394 0x318A +#define WM8995_WRITE_SEQUENCER_395 0x318B +#define WM8995_WRITE_SEQUENCER_396 0x318C +#define WM8995_WRITE_SEQUENCER_397 0x318D +#define WM8995_WRITE_SEQUENCER_398 0x318E +#define WM8995_WRITE_SEQUENCER_399 0x318F +#define WM8995_WRITE_SEQUENCER_400 0x3190 +#define WM8995_WRITE_SEQUENCER_401 0x3191 +#define WM8995_WRITE_SEQUENCER_402 0x3192 +#define WM8995_WRITE_SEQUENCER_403 0x3193 +#define WM8995_WRITE_SEQUENCER_404 0x3194 +#define WM8995_WRITE_SEQUENCER_405 0x3195 +#define WM8995_WRITE_SEQUENCER_406 0x3196 +#define WM8995_WRITE_SEQUENCER_407 0x3197 +#define WM8995_WRITE_SEQUENCER_408 0x3198 +#define WM8995_WRITE_SEQUENCER_409 0x3199 +#define WM8995_WRITE_SEQUENCER_410 0x319A +#define WM8995_WRITE_SEQUENCER_411 0x319B +#define WM8995_WRITE_SEQUENCER_412 0x319C +#define WM8995_WRITE_SEQUENCER_413 0x319D +#define WM8995_WRITE_SEQUENCER_414 0x319E +#define WM8995_WRITE_SEQUENCER_415 0x319F +#define WM8995_WRITE_SEQUENCER_416 0x31A0 +#define WM8995_WRITE_SEQUENCER_417 0x31A1 +#define WM8995_WRITE_SEQUENCER_418 0x31A2 +#define WM8995_WRITE_SEQUENCER_419 0x31A3 +#define WM8995_WRITE_SEQUENCER_420 0x31A4 +#define WM8995_WRITE_SEQUENCER_421 0x31A5 +#define WM8995_WRITE_SEQUENCER_422 0x31A6 +#define WM8995_WRITE_SEQUENCER_423 0x31A7 +#define WM8995_WRITE_SEQUENCER_424 0x31A8 +#define WM8995_WRITE_SEQUENCER_425 0x31A9 +#define WM8995_WRITE_SEQUENCER_426 0x31AA +#define WM8995_WRITE_SEQUENCER_427 0x31AB +#define WM8995_WRITE_SEQUENCER_428 0x31AC +#define WM8995_WRITE_SEQUENCER_429 0x31AD +#define WM8995_WRITE_SEQUENCER_430 0x31AE +#define WM8995_WRITE_SEQUENCER_431 0x31AF +#define WM8995_WRITE_SEQUENCER_432 0x31B0 +#define WM8995_WRITE_SEQUENCER_433 0x31B1 +#define WM8995_WRITE_SEQUENCER_434 0x31B2 +#define WM8995_WRITE_SEQUENCER_435 0x31B3 +#define WM8995_WRITE_SEQUENCER_436 0x31B4 +#define WM8995_WRITE_SEQUENCER_437 0x31B5 +#define WM8995_WRITE_SEQUENCER_438 0x31B6 +#define WM8995_WRITE_SEQUENCER_439 0x31B7 +#define WM8995_WRITE_SEQUENCER_440 0x31B8 +#define WM8995_WRITE_SEQUENCER_441 0x31B9 +#define WM8995_WRITE_SEQUENCER_442 0x31BA +#define WM8995_WRITE_SEQUENCER_443 0x31BB +#define WM8995_WRITE_SEQUENCER_444 0x31BC +#define WM8995_WRITE_SEQUENCER_445 0x31BD +#define WM8995_WRITE_SEQUENCER_446 0x31BE +#define WM8995_WRITE_SEQUENCER_447 0x31BF +#define WM8995_WRITE_SEQUENCER_448 0x31C0 +#define WM8995_WRITE_SEQUENCER_449 0x31C1 +#define WM8995_WRITE_SEQUENCER_450 0x31C2 +#define WM8995_WRITE_SEQUENCER_451 0x31C3 +#define WM8995_WRITE_SEQUENCER_452 0x31C4 +#define WM8995_WRITE_SEQUENCER_453 0x31C5 +#define WM8995_WRITE_SEQUENCER_454 0x31C6 +#define WM8995_WRITE_SEQUENCER_455 0x31C7 +#define WM8995_WRITE_SEQUENCER_456 0x31C8 +#define WM8995_WRITE_SEQUENCER_457 0x31C9 +#define WM8995_WRITE_SEQUENCER_458 0x31CA +#define WM8995_WRITE_SEQUENCER_459 0x31CB +#define WM8995_WRITE_SEQUENCER_460 0x31CC +#define WM8995_WRITE_SEQUENCER_461 0x31CD +#define WM8995_WRITE_SEQUENCER_462 0x31CE +#define WM8995_WRITE_SEQUENCER_463 0x31CF +#define WM8995_WRITE_SEQUENCER_464 0x31D0 +#define WM8995_WRITE_SEQUENCER_465 0x31D1 +#define WM8995_WRITE_SEQUENCER_466 0x31D2 +#define WM8995_WRITE_SEQUENCER_467 0x31D3 +#define WM8995_WRITE_SEQUENCER_468 0x31D4 +#define WM8995_WRITE_SEQUENCER_469 0x31D5 +#define WM8995_WRITE_SEQUENCER_470 0x31D6 +#define WM8995_WRITE_SEQUENCER_471 0x31D7 +#define WM8995_WRITE_SEQUENCER_472 0x31D8 +#define WM8995_WRITE_SEQUENCER_473 0x31D9 +#define WM8995_WRITE_SEQUENCER_474 0x31DA +#define WM8995_WRITE_SEQUENCER_475 0x31DB +#define WM8995_WRITE_SEQUENCER_476 0x31DC +#define WM8995_WRITE_SEQUENCER_477 0x31DD +#define WM8995_WRITE_SEQUENCER_478 0x31DE +#define WM8995_WRITE_SEQUENCER_479 0x31DF +#define WM8995_WRITE_SEQUENCER_480 0x31E0 +#define WM8995_WRITE_SEQUENCER_481 0x31E1 +#define WM8995_WRITE_SEQUENCER_482 0x31E2 +#define WM8995_WRITE_SEQUENCER_483 0x31E3 +#define WM8995_WRITE_SEQUENCER_484 0x31E4 +#define WM8995_WRITE_SEQUENCER_485 0x31E5 +#define WM8995_WRITE_SEQUENCER_486 0x31E6 +#define WM8995_WRITE_SEQUENCER_487 0x31E7 +#define WM8995_WRITE_SEQUENCER_488 0x31E8 +#define WM8995_WRITE_SEQUENCER_489 0x31E9 +#define WM8995_WRITE_SEQUENCER_490 0x31EA +#define WM8995_WRITE_SEQUENCER_491 0x31EB +#define WM8995_WRITE_SEQUENCER_492 0x31EC +#define WM8995_WRITE_SEQUENCER_493 0x31ED +#define WM8995_WRITE_SEQUENCER_494 0x31EE +#define WM8995_WRITE_SEQUENCER_495 0x31EF +#define WM8995_WRITE_SEQUENCER_496 0x31F0 +#define WM8995_WRITE_SEQUENCER_497 0x31F1 +#define WM8995_WRITE_SEQUENCER_498 0x31F2 +#define WM8995_WRITE_SEQUENCER_499 0x31F3 +#define WM8995_WRITE_SEQUENCER_500 0x31F4 +#define WM8995_WRITE_SEQUENCER_501 0x31F5 +#define WM8995_WRITE_SEQUENCER_502 0x31F6 +#define WM8995_WRITE_SEQUENCER_503 0x31F7 +#define WM8995_WRITE_SEQUENCER_504 0x31F8 +#define WM8995_WRITE_SEQUENCER_505 0x31F9 +#define WM8995_WRITE_SEQUENCER_506 0x31FA +#define WM8995_WRITE_SEQUENCER_507 0x31FB +#define WM8995_WRITE_SEQUENCER_508 0x31FC +#define WM8995_WRITE_SEQUENCER_509 0x31FD +#define WM8995_WRITE_SEQUENCER_510 0x31FE +#define WM8995_WRITE_SEQUENCER_511 0x31FF + +#define WM8995_REGISTER_COUNT 725 +#define WM8995_MAX_REGISTER 0x31FF + +#define WM8995_MAX_CACHED_REGISTER WM8995_MAX_REGISTER + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8995_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM8995_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM8995_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8995_MICB2_ENA 0x0200 /* MICB2_ENA */ +#define WM8995_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */ +#define WM8995_MICB2_ENA_SHIFT 9 /* MICB2_ENA */ +#define WM8995_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ +#define WM8995_MICB1_ENA 0x0100 /* MICB1_ENA */ +#define WM8995_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */ +#define WM8995_MICB1_ENA_SHIFT 8 /* MICB1_ENA */ +#define WM8995_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ +#define WM8995_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */ +#define WM8995_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */ +#define WM8995_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */ +#define WM8995_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */ +#define WM8995_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */ +#define WM8995_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */ +#define WM8995_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */ +#define WM8995_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */ +#define WM8995_BG_ENA 0x0001 /* BG_ENA */ +#define WM8995_BG_ENA_MASK 0x0001 /* BG_ENA */ +#define WM8995_BG_ENA_SHIFT 0 /* BG_ENA */ +#define WM8995_BG_ENA_WIDTH 1 /* BG_ENA */ + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8995_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8995_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8995_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8995_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8995_IN1L_ENA 0x0020 /* IN1L_ENA */ +#define WM8995_IN1L_ENA_MASK 0x0020 /* IN1L_ENA */ +#define WM8995_IN1L_ENA_SHIFT 5 /* IN1L_ENA */ +#define WM8995_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define WM8995_IN1R_ENA 0x0010 /* IN1R_ENA */ +#define WM8995_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */ +#define WM8995_IN1R_ENA_SHIFT 4 /* IN1R_ENA */ +#define WM8995_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ +#define WM8995_LDO2_ENA 0x0002 /* LDO2_ENA */ +#define WM8995_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */ +#define WM8995_LDO2_ENA_SHIFT 1 /* LDO2_ENA */ +#define WM8995_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8995_AIF2ADCL_ENA 0x2000 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCL_ENA_MASK 0x2000 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCL_ENA_SHIFT 13 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCL_ENA_WIDTH 1 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCR_ENA 0x1000 /* AIF2ADCR_ENA */ +#define WM8995_AIF2ADCR_ENA_MASK 0x1000 /* AIF2ADCR_ENA */ +#define WM8995_AIF2ADCR_ENA_SHIFT 12 /* AIF2ADCR_ENA */ +#define WM8995_AIF2ADCR_ENA_WIDTH 1 /* AIF2ADCR_ENA */ +#define WM8995_AIF1ADC2L_ENA 0x0800 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2L_ENA_MASK 0x0800 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2L_ENA_SHIFT 11 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2L_ENA_WIDTH 1 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2R_ENA 0x0400 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC2R_ENA_MASK 0x0400 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC2R_ENA_SHIFT 10 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC2R_ENA_WIDTH 1 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC1L_ENA 0x0200 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1L_ENA_MASK 0x0200 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1L_ENA_SHIFT 9 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1L_ENA_WIDTH 1 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1R_ENA 0x0100 /* AIF1ADC1R_ENA */ +#define WM8995_AIF1ADC1R_ENA_MASK 0x0100 /* AIF1ADC1R_ENA */ +#define WM8995_AIF1ADC1R_ENA_SHIFT 8 /* AIF1ADC1R_ENA */ +#define WM8995_AIF1ADC1R_ENA_WIDTH 1 /* AIF1ADC1R_ENA */ +#define WM8995_DMIC3L_ENA 0x0080 /* DMIC3L_ENA */ +#define WM8995_DMIC3L_ENA_MASK 0x0080 /* DMIC3L_ENA */ +#define WM8995_DMIC3L_ENA_SHIFT 7 /* DMIC3L_ENA */ +#define WM8995_DMIC3L_ENA_WIDTH 1 /* DMIC3L_ENA */ +#define WM8995_DMIC3R_ENA 0x0040 /* DMIC3R_ENA */ +#define WM8995_DMIC3R_ENA_MASK 0x0040 /* DMIC3R_ENA */ +#define WM8995_DMIC3R_ENA_SHIFT 6 /* DMIC3R_ENA */ +#define WM8995_DMIC3R_ENA_WIDTH 1 /* DMIC3R_ENA */ +#define WM8995_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */ +#define WM8995_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */ +#define WM8995_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */ +#define WM8995_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */ +#define WM8995_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */ +#define WM8995_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */ +#define WM8995_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */ +#define WM8995_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */ +#define WM8995_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */ +#define WM8995_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */ +#define WM8995_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */ +#define WM8995_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */ +#define WM8995_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */ +#define WM8995_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */ +#define WM8995_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */ +#define WM8995_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */ +#define WM8995_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8995_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8995_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8995_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8995_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8995_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8995_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8995_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R4 (0x04) - Power Management (4) + */ +#define WM8995_AIF2DACL_ENA 0x2000 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACL_ENA_MASK 0x2000 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACL_ENA_SHIFT 13 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACL_ENA_WIDTH 1 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACR_ENA 0x1000 /* AIF2DACR_ENA */ +#define WM8995_AIF2DACR_ENA_MASK 0x1000 /* AIF2DACR_ENA */ +#define WM8995_AIF2DACR_ENA_SHIFT 12 /* AIF2DACR_ENA */ +#define WM8995_AIF2DACR_ENA_WIDTH 1 /* AIF2DACR_ENA */ +#define WM8995_AIF1DAC2L_ENA 0x0800 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2L_ENA_MASK 0x0800 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2L_ENA_SHIFT 11 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2L_ENA_WIDTH 1 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2R_ENA 0x0400 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC2R_ENA_MASK 0x0400 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC2R_ENA_SHIFT 10 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC2R_ENA_WIDTH 1 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC1L_ENA 0x0200 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1L_ENA_MASK 0x0200 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1L_ENA_SHIFT 9 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1L_ENA_WIDTH 1 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1R_ENA 0x0100 /* AIF1DAC1R_ENA */ +#define WM8995_AIF1DAC1R_ENA_MASK 0x0100 /* AIF1DAC1R_ENA */ +#define WM8995_AIF1DAC1R_ENA_SHIFT 8 /* AIF1DAC1R_ENA */ +#define WM8995_AIF1DAC1R_ENA_WIDTH 1 /* AIF1DAC1R_ENA */ +#define WM8995_DAC2L_ENA 0x0008 /* DAC2L_ENA */ +#define WM8995_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */ +#define WM8995_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */ +#define WM8995_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */ +#define WM8995_DAC2R_ENA 0x0004 /* DAC2R_ENA */ +#define WM8995_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */ +#define WM8995_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */ +#define WM8995_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */ +#define WM8995_DAC1L_ENA 0x0002 /* DAC1L_ENA */ +#define WM8995_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */ +#define WM8995_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */ +#define WM8995_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */ +#define WM8995_DAC1R_ENA 0x0001 /* DAC1R_ENA */ +#define WM8995_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */ +#define WM8995_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */ +#define WM8995_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */ + +/* + * R5 (0x05) - Power Management (5) + */ +#define WM8995_DMIC_SRC2_MASK 0x0300 /* DMIC_SRC2 - [9:8] */ +#define WM8995_DMIC_SRC2_SHIFT 8 /* DMIC_SRC2 - [9:8] */ +#define WM8995_DMIC_SRC2_WIDTH 2 /* DMIC_SRC2 - [9:8] */ +#define WM8995_DMIC_SRC1_MASK 0x00C0 /* DMIC_SRC1 - [7:6] */ +#define WM8995_DMIC_SRC1_SHIFT 6 /* DMIC_SRC1 - [7:6] */ +#define WM8995_DMIC_SRC1_WIDTH 2 /* DMIC_SRC1 - [7:6] */ +#define WM8995_AIF3_TRI 0x0020 /* AIF3_TRI */ +#define WM8995_AIF3_TRI_MASK 0x0020 /* AIF3_TRI */ +#define WM8995_AIF3_TRI_SHIFT 5 /* AIF3_TRI */ +#define WM8995_AIF3_TRI_WIDTH 1 /* AIF3_TRI */ +#define WM8995_AIF3_ADCDAT_SRC_MASK 0x0018 /* AIF3_ADCDAT_SRC - [4:3] */ +#define WM8995_AIF3_ADCDAT_SRC_SHIFT 3 /* AIF3_ADCDAT_SRC - [4:3] */ +#define WM8995_AIF3_ADCDAT_SRC_WIDTH 2 /* AIF3_ADCDAT_SRC - [4:3] */ +#define WM8995_AIF2_ADCDAT_SRC 0x0004 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_ADCDAT_SRC_MASK 0x0004 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_ADCDAT_SRC_SHIFT 2 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_ADCDAT_SRC_WIDTH 1 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC 0x0002 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC_MASK 0x0002 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC_SHIFT 1 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC_WIDTH 1 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC 0x0001 /* AIF1_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC_MASK 0x0001 /* AIF1_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC_SHIFT 0 /* AIF1_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC_WIDTH 1 /* AIF1_DACDAT_SRC */ + +/* + * R16 (0x10) - Left Line Input 1 Volume + */ +#define WM8995_IN1_VU 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_MASK 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_SHIFT 7 /* IN1_VU */ +#define WM8995_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8995_IN1L_ZC 0x0020 /* IN1L_ZC */ +#define WM8995_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */ +#define WM8995_IN1L_ZC_SHIFT 5 /* IN1L_ZC */ +#define WM8995_IN1L_ZC_WIDTH 1 /* IN1L_ZC */ +#define WM8995_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */ +#define WM8995_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */ +#define WM8995_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */ + +/* + * R17 (0x11) - Right Line Input 1 Volume + */ +#define WM8995_IN1_VU 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_MASK 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_SHIFT 7 /* IN1_VU */ +#define WM8995_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8995_IN1R_ZC 0x0020 /* IN1R_ZC */ +#define WM8995_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */ +#define WM8995_IN1R_ZC_SHIFT 5 /* IN1R_ZC */ +#define WM8995_IN1R_ZC_WIDTH 1 /* IN1R_ZC */ +#define WM8995_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */ +#define WM8995_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */ +#define WM8995_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */ + +/* + * R18 (0x12) - Left Line Input Control + */ +#define WM8995_IN1L_BOOST_MASK 0x0030 /* IN1L_BOOST - [5:4] */ +#define WM8995_IN1L_BOOST_SHIFT 4 /* IN1L_BOOST - [5:4] */ +#define WM8995_IN1L_BOOST_WIDTH 2 /* IN1L_BOOST - [5:4] */ +#define WM8995_IN1L_MODE_MASK 0x000C /* IN1L_MODE - [3:2] */ +#define WM8995_IN1L_MODE_SHIFT 2 /* IN1L_MODE - [3:2] */ +#define WM8995_IN1L_MODE_WIDTH 2 /* IN1L_MODE - [3:2] */ +#define WM8995_IN1R_MODE_MASK 0x0003 /* IN1R_MODE - [1:0] */ +#define WM8995_IN1R_MODE_SHIFT 0 /* IN1R_MODE - [1:0] */ +#define WM8995_IN1R_MODE_WIDTH 2 /* IN1R_MODE - [1:0] */ + +/* + * R24 (0x18) - DAC1 Left Volume + */ +#define WM8995_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */ +#define WM8995_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */ +#define WM8995_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */ +#define WM8995_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */ +#define WM8995_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8995_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8995_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */ +#define WM8995_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */ +#define WM8995_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */ + +/* + * R25 (0x19) - DAC1 Right Volume + */ +#define WM8995_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */ +#define WM8995_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */ +#define WM8995_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */ +#define WM8995_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */ +#define WM8995_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8995_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8995_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */ +#define WM8995_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */ +#define WM8995_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */ + +/* + * R26 (0x1A) - DAC2 Left Volume + */ +#define WM8995_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */ +#define WM8995_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */ +#define WM8995_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */ +#define WM8995_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */ +#define WM8995_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8995_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8995_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */ +#define WM8995_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */ +#define WM8995_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */ + +/* + * R27 (0x1B) - DAC2 Right Volume + */ +#define WM8995_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */ +#define WM8995_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */ +#define WM8995_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */ +#define WM8995_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */ +#define WM8995_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8995_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8995_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */ +#define WM8995_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */ +#define WM8995_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */ + +/* + * R28 (0x1C) - Output Volume ZC (1) + */ +#define WM8995_HPOUT2L_ZC 0x0008 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2L_ZC_MASK 0x0008 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2L_ZC_SHIFT 3 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2R_ZC 0x0004 /* HPOUT2R_ZC */ +#define WM8995_HPOUT2R_ZC_MASK 0x0004 /* HPOUT2R_ZC */ +#define WM8995_HPOUT2R_ZC_SHIFT 2 /* HPOUT2R_ZC */ +#define WM8995_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */ +#define WM8995_HPOUT1L_ZC 0x0002 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1L_ZC_MASK 0x0002 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1L_ZC_SHIFT 1 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1R_ZC 0x0001 /* HPOUT1R_ZC */ +#define WM8995_HPOUT1R_ZC_MASK 0x0001 /* HPOUT1R_ZC */ +#define WM8995_HPOUT1R_ZC_SHIFT 0 /* HPOUT1R_ZC */ +#define WM8995_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */ + +/* + * R32 (0x20) - MICBIAS (1) + */ +#define WM8995_MICB1_MODE 0x0008 /* MICB1_MODE */ +#define WM8995_MICB1_MODE_MASK 0x0008 /* MICB1_MODE */ +#define WM8995_MICB1_MODE_SHIFT 3 /* MICB1_MODE */ +#define WM8995_MICB1_MODE_WIDTH 1 /* MICB1_MODE */ +#define WM8995_MICB1_LVL_MASK 0x0006 /* MICB1_LVL - [2:1] */ +#define WM8995_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [2:1] */ +#define WM8995_MICB1_LVL_WIDTH 2 /* MICB1_LVL - [2:1] */ +#define WM8995_MICB1_DISCH 0x0001 /* MICB1_DISCH */ +#define WM8995_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */ +#define WM8995_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */ +#define WM8995_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ + +/* + * R33 (0x21) - MICBIAS (2) + */ +#define WM8995_MICB2_MODE 0x0008 /* MICB2_MODE */ +#define WM8995_MICB2_MODE_MASK 0x0008 /* MICB2_MODE */ +#define WM8995_MICB2_MODE_SHIFT 3 /* MICB2_MODE */ +#define WM8995_MICB2_MODE_WIDTH 1 /* MICB2_MODE */ +#define WM8995_MICB2_LVL_MASK 0x0006 /* MICB2_LVL - [2:1] */ +#define WM8995_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [2:1] */ +#define WM8995_MICB2_LVL_WIDTH 2 /* MICB2_LVL - [2:1] */ +#define WM8995_MICB2_DISCH 0x0001 /* MICB2_DISCH */ +#define WM8995_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */ +#define WM8995_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */ +#define WM8995_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ + +/* + * R40 (0x28) - LDO 1 + */ +#define WM8995_LDO1_MODE 0x0020 /* LDO1_MODE */ +#define WM8995_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */ +#define WM8995_LDO1_MODE_SHIFT 5 /* LDO1_MODE */ +#define WM8995_LDO1_MODE_WIDTH 1 /* LDO1_MODE */ +#define WM8995_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */ +#define WM8995_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */ +#define WM8995_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */ +#define WM8995_LDO1_DISCH 0x0001 /* LDO1_DISCH */ +#define WM8995_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */ +#define WM8995_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */ +#define WM8995_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */ + +/* + * R41 (0x29) - LDO 2 + */ +#define WM8995_LDO2_MODE 0x0020 /* LDO2_MODE */ +#define WM8995_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */ +#define WM8995_LDO2_MODE_SHIFT 5 /* LDO2_MODE */ +#define WM8995_LDO2_MODE_WIDTH 1 /* LDO2_MODE */ +#define WM8995_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */ +#define WM8995_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */ +#define WM8995_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */ +#define WM8995_LDO2_DISCH 0x0001 /* LDO2_DISCH */ +#define WM8995_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */ +#define WM8995_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */ +#define WM8995_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */ + +/* + * R48 (0x30) - Accessory Detect Mode1 + */ +#define WM8995_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */ +#define WM8995_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */ +#define WM8995_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */ + +/* + * R49 (0x31) - Accessory Detect Mode2 + */ +#define WM8995_VID_ENA 0x0001 /* VID_ENA */ +#define WM8995_VID_ENA_MASK 0x0001 /* VID_ENA */ +#define WM8995_VID_ENA_SHIFT 0 /* VID_ENA */ +#define WM8995_VID_ENA_WIDTH 1 /* VID_ENA */ + +/* + * R52 (0x34) - Headphone Detect1 + */ +#define WM8995_HP_RAMPRATE 0x0002 /* HP_RAMPRATE */ +#define WM8995_HP_RAMPRATE_MASK 0x0002 /* HP_RAMPRATE */ +#define WM8995_HP_RAMPRATE_SHIFT 1 /* HP_RAMPRATE */ +#define WM8995_HP_RAMPRATE_WIDTH 1 /* HP_RAMPRATE */ +#define WM8995_HP_POLL 0x0001 /* HP_POLL */ +#define WM8995_HP_POLL_MASK 0x0001 /* HP_POLL */ +#define WM8995_HP_POLL_SHIFT 0 /* HP_POLL */ +#define WM8995_HP_POLL_WIDTH 1 /* HP_POLL */ + +/* + * R53 (0x35) - Headphone Detect2 + */ +#define WM8995_HP_DONE 0x0080 /* HP_DONE */ +#define WM8995_HP_DONE_MASK 0x0080 /* HP_DONE */ +#define WM8995_HP_DONE_SHIFT 7 /* HP_DONE */ +#define WM8995_HP_DONE_WIDTH 1 /* HP_DONE */ +#define WM8995_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */ +#define WM8995_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ +#define WM8995_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ + +/* + * R56 (0x38) - Mic Detect (1) + */ +#define WM8995_MICD_RATE_MASK 0x7800 /* MICD_RATE - [14:11] */ +#define WM8995_MICD_RATE_SHIFT 11 /* MICD_RATE - [14:11] */ +#define WM8995_MICD_RATE_WIDTH 4 /* MICD_RATE - [14:11] */ +#define WM8995_MICD_LVL_SEL_MASK 0x01F8 /* MICD_LVL_SEL - [8:3] */ +#define WM8995_MICD_LVL_SEL_SHIFT 3 /* MICD_LVL_SEL - [8:3] */ +#define WM8995_MICD_LVL_SEL_WIDTH 6 /* MICD_LVL_SEL - [8:3] */ +#define WM8995_MICD_DBTIME 0x0002 /* MICD_DBTIME */ +#define WM8995_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */ +#define WM8995_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */ +#define WM8995_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */ +#define WM8995_MICD_ENA 0x0001 /* MICD_ENA */ +#define WM8995_MICD_ENA_MASK 0x0001 /* MICD_ENA */ +#define WM8995_MICD_ENA_SHIFT 0 /* MICD_ENA */ +#define WM8995_MICD_ENA_WIDTH 1 /* MICD_ENA */ + +/* + * R57 (0x39) - Mic Detect (2) + */ +#define WM8995_MICD_LVL_MASK 0x01FC /* MICD_LVL - [8:2] */ +#define WM8995_MICD_LVL_SHIFT 2 /* MICD_LVL - [8:2] */ +#define WM8995_MICD_LVL_WIDTH 7 /* MICD_LVL - [8:2] */ +#define WM8995_MICD_VALID 0x0002 /* MICD_VALID */ +#define WM8995_MICD_VALID_MASK 0x0002 /* MICD_VALID */ +#define WM8995_MICD_VALID_SHIFT 1 /* MICD_VALID */ +#define WM8995_MICD_VALID_WIDTH 1 /* MICD_VALID */ +#define WM8995_MICD_STS 0x0001 /* MICD_STS */ +#define WM8995_MICD_STS_MASK 0x0001 /* MICD_STS */ +#define WM8995_MICD_STS_SHIFT 0 /* MICD_STS */ +#define WM8995_MICD_STS_WIDTH 1 /* MICD_STS */ + +/* + * R64 (0x40) - Charge Pump (1) + */ +#define WM8995_CP_ENA 0x8000 /* CP_ENA */ +#define WM8995_CP_ENA_MASK 0x8000 /* CP_ENA */ +#define WM8995_CP_ENA_SHIFT 15 /* CP_ENA */ +#define WM8995_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R69 (0x45) - Class W (1) + */ +#define WM8995_CP_DYN_SRC_SEL_MASK 0x0300 /* CP_DYN_SRC_SEL - [9:8] */ +#define WM8995_CP_DYN_SRC_SEL_SHIFT 8 /* CP_DYN_SRC_SEL - [9:8] */ +#define WM8995_CP_DYN_SRC_SEL_WIDTH 2 /* CP_DYN_SRC_SEL - [9:8] */ +#define WM8995_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */ +#define WM8995_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */ +#define WM8995_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */ +#define WM8995_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */ + +/* + * R80 (0x50) - DC Servo (1) + */ +#define WM8995_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8995_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8995_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM8995_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R81 (0x51) - DC Servo (2) + */ +#define WM8995_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8995_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8995_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */ +#define WM8995_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ + +/* + * R82 (0x52) - DC Servo (3) + */ +#define WM8995_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8995_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8995_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8995_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8995_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8995_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R84 (0x54) - DC Servo (5) + */ +#define WM8995_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8995_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8995_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8995_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8995_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8995_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */ + +/* + * R85 (0x55) - DC Servo (6) + */ +#define WM8995_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */ + +/* + * R86 (0x56) - DC Servo (7) + */ +#define WM8995_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R87 (0x57) - DC Servo Readback 0 + */ +#define WM8995_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8995_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8995_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8995_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8995_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8995_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8995_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8995_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8995_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */ + +/* + * R96 (0x60) - Analogue HP (1) + */ +#define WM8995_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */ +#define WM8995_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */ +#define WM8995_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */ +#define WM8995_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */ + +/* + * R97 (0x61) - Analogue HP (2) + */ +#define WM8995_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */ +#define WM8995_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */ +#define WM8995_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */ +#define WM8995_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */ + +/* + * R256 (0x100) - Chip Revision + */ +#define WM8995_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */ +#define WM8995_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */ +#define WM8995_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */ + +/* + * R257 (0x101) - Control Interface (1) + */ +#define WM8995_REG_SYNC 0x8000 /* REG_SYNC */ +#define WM8995_REG_SYNC_MASK 0x8000 /* REG_SYNC */ +#define WM8995_REG_SYNC_SHIFT 15 /* REG_SYNC */ +#define WM8995_REG_SYNC_WIDTH 1 /* REG_SYNC */ +#define WM8995_SPI_CONTRD 0x0040 /* SPI_CONTRD */ +#define WM8995_SPI_CONTRD_MASK 0x0040 /* SPI_CONTRD */ +#define WM8995_SPI_CONTRD_SHIFT 6 /* SPI_CONTRD */ +#define WM8995_SPI_CONTRD_WIDTH 1 /* SPI_CONTRD */ +#define WM8995_SPI_4WIRE 0x0020 /* SPI_4WIRE */ +#define WM8995_SPI_4WIRE_MASK 0x0020 /* SPI_4WIRE */ +#define WM8995_SPI_4WIRE_SHIFT 5 /* SPI_4WIRE */ +#define WM8995_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */ +#define WM8995_SPI_CFG 0x0010 /* SPI_CFG */ +#define WM8995_SPI_CFG_MASK 0x0010 /* SPI_CFG */ +#define WM8995_SPI_CFG_SHIFT 4 /* SPI_CFG */ +#define WM8995_SPI_CFG_WIDTH 1 /* SPI_CFG */ +#define WM8995_AUTO_INC 0x0004 /* AUTO_INC */ +#define WM8995_AUTO_INC_MASK 0x0004 /* AUTO_INC */ +#define WM8995_AUTO_INC_SHIFT 2 /* AUTO_INC */ +#define WM8995_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +/* + * R258 (0x102) - Control Interface (2) + */ +#define WM8995_CTRL_IF_SRC 0x0001 /* CTRL_IF_SRC */ +#define WM8995_CTRL_IF_SRC_MASK 0x0001 /* CTRL_IF_SRC */ +#define WM8995_CTRL_IF_SRC_SHIFT 0 /* CTRL_IF_SRC */ +#define WM8995_CTRL_IF_SRC_WIDTH 1 /* CTRL_IF_SRC */ + +/* + * R272 (0x110) - Write Sequencer Ctrl (1) + */ +#define WM8995_WSEQ_ENA 0x8000 /* WSEQ_ENA */ +#define WM8995_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */ +#define WM8995_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */ +#define WM8995_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8995_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8995_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8995_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8995_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8995_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8995_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8995_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8995_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8995_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */ +#define WM8995_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */ +#define WM8995_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */ + +/* + * R273 (0x111) - Write Sequencer Ctrl (2) + */ +#define WM8995_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */ +#define WM8995_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */ +#define WM8995_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */ +#define WM8995_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ +#define WM8995_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */ +#define WM8995_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */ +#define WM8995_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */ + +/* + * R512 (0x200) - AIF1 Clocking (1) + */ +#define WM8995_AIF1CLK_SRC_MASK 0x0018 /* AIF1CLK_SRC - [4:3] */ +#define WM8995_AIF1CLK_SRC_SHIFT 3 /* AIF1CLK_SRC - [4:3] */ +#define WM8995_AIF1CLK_SRC_WIDTH 2 /* AIF1CLK_SRC - [4:3] */ +#define WM8995_AIF1CLK_INV 0x0004 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_INV_MASK 0x0004 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_INV_SHIFT 2 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_INV_WIDTH 1 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_DIV 0x0002 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_DIV_MASK 0x0002 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_DIV_SHIFT 1 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_DIV_WIDTH 1 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_ENA 0x0001 /* AIF1CLK_ENA */ +#define WM8995_AIF1CLK_ENA_MASK 0x0001 /* AIF1CLK_ENA */ +#define WM8995_AIF1CLK_ENA_SHIFT 0 /* AIF1CLK_ENA */ +#define WM8995_AIF1CLK_ENA_WIDTH 1 /* AIF1CLK_ENA */ + +/* + * R513 (0x201) - AIF1 Clocking (2) + */ +#define WM8995_AIF1DAC_DIV_MASK 0x0038 /* AIF1DAC_DIV - [5:3] */ +#define WM8995_AIF1DAC_DIV_SHIFT 3 /* AIF1DAC_DIV - [5:3] */ +#define WM8995_AIF1DAC_DIV_WIDTH 3 /* AIF1DAC_DIV - [5:3] */ +#define WM8995_AIF1ADC_DIV_MASK 0x0007 /* AIF1ADC_DIV - [2:0] */ +#define WM8995_AIF1ADC_DIV_SHIFT 0 /* AIF1ADC_DIV - [2:0] */ +#define WM8995_AIF1ADC_DIV_WIDTH 3 /* AIF1ADC_DIV - [2:0] */ + +/* + * R516 (0x204) - AIF2 Clocking (1) + */ +#define WM8995_AIF2CLK_SRC_MASK 0x0018 /* AIF2CLK_SRC - [4:3] */ +#define WM8995_AIF2CLK_SRC_SHIFT 3 /* AIF2CLK_SRC - [4:3] */ +#define WM8995_AIF2CLK_SRC_WIDTH 2 /* AIF2CLK_SRC - [4:3] */ +#define WM8995_AIF2CLK_INV 0x0004 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_INV_MASK 0x0004 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_INV_SHIFT 2 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_INV_WIDTH 1 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_DIV 0x0002 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_DIV_MASK 0x0002 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_DIV_SHIFT 1 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_DIV_WIDTH 1 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_ENA 0x0001 /* AIF2CLK_ENA */ +#define WM8995_AIF2CLK_ENA_MASK 0x0001 /* AIF2CLK_ENA */ +#define WM8995_AIF2CLK_ENA_SHIFT 0 /* AIF2CLK_ENA */ +#define WM8995_AIF2CLK_ENA_WIDTH 1 /* AIF2CLK_ENA */ + +/* + * R517 (0x205) - AIF2 Clocking (2) + */ +#define WM8995_AIF2DAC_DIV_MASK 0x0038 /* AIF2DAC_DIV - [5:3] */ +#define WM8995_AIF2DAC_DIV_SHIFT 3 /* AIF2DAC_DIV - [5:3] */ +#define WM8995_AIF2DAC_DIV_WIDTH 3 /* AIF2DAC_DIV - [5:3] */ +#define WM8995_AIF2ADC_DIV_MASK 0x0007 /* AIF2ADC_DIV - [2:0] */ +#define WM8995_AIF2ADC_DIV_SHIFT 0 /* AIF2ADC_DIV - [2:0] */ +#define WM8995_AIF2ADC_DIV_WIDTH 3 /* AIF2ADC_DIV - [2:0] */ + +/* + * R520 (0x208) - Clocking (1) + */ +#define WM8995_LFCLK_ENA 0x0020 /* LFCLK_ENA */ +#define WM8995_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */ +#define WM8995_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */ +#define WM8995_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */ +#define WM8995_TOCLK_ENA 0x0010 /* TOCLK_ENA */ +#define WM8995_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */ +#define WM8995_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */ +#define WM8995_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA 0x0008 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA_MASK 0x0008 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA_SHIFT 3 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA_WIDTH 1 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA 0x0004 /* AIF2DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA_MASK 0x0004 /* AIF2DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA_SHIFT 2 /* AIF2DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA_WIDTH 1 /* AIF2DSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */ +#define WM8995_SYSCLK_SRC 0x0001 /* SYSCLK_SRC */ +#define WM8995_SYSCLK_SRC_MASK 0x0001 /* SYSCLK_SRC */ +#define WM8995_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC */ +#define WM8995_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ + +/* + * R521 (0x209) - Clocking (2) + */ +#define WM8995_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */ +#define WM8995_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */ +#define WM8995_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */ +#define WM8995_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */ +#define WM8995_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */ +#define WM8995_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */ +#define WM8995_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */ +#define WM8995_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */ +#define WM8995_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */ + +/* + * R528 (0x210) - AIF1 Rate + */ +#define WM8995_AIF1_SR_MASK 0x00F0 /* AIF1_SR - [7:4] */ +#define WM8995_AIF1_SR_SHIFT 4 /* AIF1_SR - [7:4] */ +#define WM8995_AIF1_SR_WIDTH 4 /* AIF1_SR - [7:4] */ +#define WM8995_AIF1CLK_RATE_MASK 0x000F /* AIF1CLK_RATE - [3:0] */ +#define WM8995_AIF1CLK_RATE_SHIFT 0 /* AIF1CLK_RATE - [3:0] */ +#define WM8995_AIF1CLK_RATE_WIDTH 4 /* AIF1CLK_RATE - [3:0] */ + +/* + * R529 (0x211) - AIF2 Rate + */ +#define WM8995_AIF2_SR_MASK 0x00F0 /* AIF2_SR - [7:4] */ +#define WM8995_AIF2_SR_SHIFT 4 /* AIF2_SR - [7:4] */ +#define WM8995_AIF2_SR_WIDTH 4 /* AIF2_SR - [7:4] */ +#define WM8995_AIF2CLK_RATE_MASK 0x000F /* AIF2CLK_RATE - [3:0] */ +#define WM8995_AIF2CLK_RATE_SHIFT 0 /* AIF2CLK_RATE - [3:0] */ +#define WM8995_AIF2CLK_RATE_WIDTH 4 /* AIF2CLK_RATE - [3:0] */ + +/* + * R530 (0x212) - Rate Status + */ +#define WM8995_SR_ERROR_MASK 0x000F /* SR_ERROR - [3:0] */ +#define WM8995_SR_ERROR_SHIFT 0 /* SR_ERROR - [3:0] */ +#define WM8995_SR_ERROR_WIDTH 4 /* SR_ERROR - [3:0] */ + +/* + * R544 (0x220) - FLL1 Control (1) + */ +#define WM8995_FLL1_OSC_ENA 0x0002 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_OSC_ENA_MASK 0x0002 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_OSC_ENA_SHIFT 1 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_OSC_ENA_WIDTH 1 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_ENA 0x0001 /* FLL1_ENA */ +#define WM8995_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */ +#define WM8995_FLL1_ENA_SHIFT 0 /* FLL1_ENA */ +#define WM8995_FLL1_ENA_WIDTH 1 /* FLL1_ENA */ + +/* + * R545 (0x221) - FLL1 Control (2) + */ +#define WM8995_FLL1_OUTDIV_MASK 0x3F00 /* FLL1_OUTDIV - [13:8] */ +#define WM8995_FLL1_OUTDIV_SHIFT 8 /* FLL1_OUTDIV - [13:8] */ +#define WM8995_FLL1_OUTDIV_WIDTH 6 /* FLL1_OUTDIV - [13:8] */ +#define WM8995_FLL1_CTRL_RATE_MASK 0x0070 /* FLL1_CTRL_RATE - [6:4] */ +#define WM8995_FLL1_CTRL_RATE_SHIFT 4 /* FLL1_CTRL_RATE - [6:4] */ +#define WM8995_FLL1_CTRL_RATE_WIDTH 3 /* FLL1_CTRL_RATE - [6:4] */ +#define WM8995_FLL1_FRATIO_MASK 0x0007 /* FLL1_FRATIO - [2:0] */ +#define WM8995_FLL1_FRATIO_SHIFT 0 /* FLL1_FRATIO - [2:0] */ +#define WM8995_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [2:0] */ + +/* + * R546 (0x222) - FLL1 Control (3) + */ +#define WM8995_FLL1_K_MASK 0xFFFF /* FLL1_K - [15:0] */ +#define WM8995_FLL1_K_SHIFT 0 /* FLL1_K - [15:0] */ +#define WM8995_FLL1_K_WIDTH 16 /* FLL1_K - [15:0] */ + +/* + * R547 (0x223) - FLL1 Control (4) + */ +#define WM8995_FLL1_N_MASK 0x7FE0 /* FLL1_N - [14:5] */ +#define WM8995_FLL1_N_SHIFT 5 /* FLL1_N - [14:5] */ +#define WM8995_FLL1_N_WIDTH 10 /* FLL1_N - [14:5] */ +#define WM8995_FLL1_LOOP_GAIN_MASK 0x000F /* FLL1_LOOP_GAIN - [3:0] */ +#define WM8995_FLL1_LOOP_GAIN_SHIFT 0 /* FLL1_LOOP_GAIN - [3:0] */ +#define WM8995_FLL1_LOOP_GAIN_WIDTH 4 /* FLL1_LOOP_GAIN - [3:0] */ + +/* + * R548 (0x224) - FLL1 Control (5) + */ +#define WM8995_FLL1_FRC_NCO_VAL_MASK 0x1F80 /* FLL1_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL1_FRC_NCO_VAL_SHIFT 7 /* FLL1_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL1_FRC_NCO_VAL_WIDTH 6 /* FLL1_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL1_FRC_NCO 0x0040 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_FRC_NCO_MASK 0x0040 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_FRC_NCO_SHIFT 6 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_FRC_NCO_WIDTH 1 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_REFCLK_DIV_MASK 0x0018 /* FLL1_REFCLK_DIV - [4:3] */ +#define WM8995_FLL1_REFCLK_DIV_SHIFT 3 /* FLL1_REFCLK_DIV - [4:3] */ +#define WM8995_FLL1_REFCLK_DIV_WIDTH 2 /* FLL1_REFCLK_DIV - [4:3] */ +#define WM8995_FLL1_REFCLK_SRC_MASK 0x0003 /* FLL1_REFCLK_SRC - [1:0] */ +#define WM8995_FLL1_REFCLK_SRC_SHIFT 0 /* FLL1_REFCLK_SRC - [1:0] */ +#define WM8995_FLL1_REFCLK_SRC_WIDTH 2 /* FLL1_REFCLK_SRC - [1:0] */ + +/* + * R576 (0x240) - FLL2 Control (1) + */ +#define WM8995_FLL2_OSC_ENA 0x0002 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_OSC_ENA_MASK 0x0002 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_OSC_ENA_SHIFT 1 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_OSC_ENA_WIDTH 1 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_ENA 0x0001 /* FLL2_ENA */ +#define WM8995_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */ +#define WM8995_FLL2_ENA_SHIFT 0 /* FLL2_ENA */ +#define WM8995_FLL2_ENA_WIDTH 1 /* FLL2_ENA */ + +/* + * R577 (0x241) - FLL2 Control (2) + */ +#define WM8995_FLL2_OUTDIV_MASK 0x3F00 /* FLL2_OUTDIV - [13:8] */ +#define WM8995_FLL2_OUTDIV_SHIFT 8 /* FLL2_OUTDIV - [13:8] */ +#define WM8995_FLL2_OUTDIV_WIDTH 6 /* FLL2_OUTDIV - [13:8] */ +#define WM8995_FLL2_CTRL_RATE_MASK 0x0070 /* FLL2_CTRL_RATE - [6:4] */ +#define WM8995_FLL2_CTRL_RATE_SHIFT 4 /* FLL2_CTRL_RATE - [6:4] */ +#define WM8995_FLL2_CTRL_RATE_WIDTH 3 /* FLL2_CTRL_RATE - [6:4] */ +#define WM8995_FLL2_FRATIO_MASK 0x0007 /* FLL2_FRATIO - [2:0] */ +#define WM8995_FLL2_FRATIO_SHIFT 0 /* FLL2_FRATIO - [2:0] */ +#define WM8995_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [2:0] */ + +/* + * R578 (0x242) - FLL2 Control (3) + */ +#define WM8995_FLL2_K_MASK 0xFFFF /* FLL2_K - [15:0] */ +#define WM8995_FLL2_K_SHIFT 0 /* FLL2_K - [15:0] */ +#define WM8995_FLL2_K_WIDTH 16 /* FLL2_K - [15:0] */ + +/* + * R579 (0x243) - FLL2 Control (4) + */ +#define WM8995_FLL2_N_MASK 0x7FE0 /* FLL2_N - [14:5] */ +#define WM8995_FLL2_N_SHIFT 5 /* FLL2_N - [14:5] */ +#define WM8995_FLL2_N_WIDTH 10 /* FLL2_N - [14:5] */ +#define WM8995_FLL2_LOOP_GAIN_MASK 0x000F /* FLL2_LOOP_GAIN - [3:0] */ +#define WM8995_FLL2_LOOP_GAIN_SHIFT 0 /* FLL2_LOOP_GAIN - [3:0] */ +#define WM8995_FLL2_LOOP_GAIN_WIDTH 4 /* FLL2_LOOP_GAIN - [3:0] */ + +/* + * R580 (0x244) - FLL2 Control (5) + */ +#define WM8995_FLL2_FRC_NCO_VAL_MASK 0x1F80 /* FLL2_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL2_FRC_NCO_VAL_SHIFT 7 /* FLL2_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL2_FRC_NCO_VAL_WIDTH 6 /* FLL2_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL2_FRC_NCO 0x0040 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_FRC_NCO_MASK 0x0040 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_FRC_NCO_SHIFT 6 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_FRC_NCO_WIDTH 1 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_REFCLK_DIV_MASK 0x0018 /* FLL2_REFCLK_DIV - [4:3] */ +#define WM8995_FLL2_REFCLK_DIV_SHIFT 3 /* FLL2_REFCLK_DIV - [4:3] */ +#define WM8995_FLL2_REFCLK_DIV_WIDTH 2 /* FLL2_REFCLK_DIV - [4:3] */ +#define WM8995_FLL2_REFCLK_SRC_MASK 0x0003 /* FLL2_REFCLK_SRC - [1:0] */ +#define WM8995_FLL2_REFCLK_SRC_SHIFT 0 /* FLL2_REFCLK_SRC - [1:0] */ +#define WM8995_FLL2_REFCLK_SRC_WIDTH 2 /* FLL2_REFCLK_SRC - [1:0] */ + +/* + * R768 (0x300) - AIF1 Control (1) + */ +#define WM8995_AIF1ADCL_SRC 0x8000 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCL_SRC_MASK 0x8000 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCL_SRC_SHIFT 15 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCL_SRC_WIDTH 1 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCR_SRC 0x4000 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADCR_SRC_MASK 0x4000 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADCR_SRC_SHIFT 14 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADCR_SRC_WIDTH 1 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADC_TDM 0x2000 /* AIF1ADC_TDM */ +#define WM8995_AIF1ADC_TDM_MASK 0x2000 /* AIF1ADC_TDM */ +#define WM8995_AIF1ADC_TDM_SHIFT 13 /* AIF1ADC_TDM */ +#define WM8995_AIF1ADC_TDM_WIDTH 1 /* AIF1ADC_TDM */ +#define WM8995_AIF1_BCLK_INV 0x0100 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_BCLK_INV_MASK 0x0100 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_BCLK_INV_SHIFT 8 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_LRCLK_INV 0x0080 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_LRCLK_INV_MASK 0x0080 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_LRCLK_INV_SHIFT 7 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_WL_MASK 0x0060 /* AIF1_WL - [6:5] */ +#define WM8995_AIF1_WL_SHIFT 5 /* AIF1_WL - [6:5] */ +#define WM8995_AIF1_WL_WIDTH 2 /* AIF1_WL - [6:5] */ +#define WM8995_AIF1_FMT_MASK 0x0018 /* AIF1_FMT - [4:3] */ +#define WM8995_AIF1_FMT_SHIFT 3 /* AIF1_FMT - [4:3] */ +#define WM8995_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [4:3] */ + +/* + * R769 (0x301) - AIF1 Control (2) + */ +#define WM8995_AIF1DACL_SRC 0x8000 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACL_SRC_MASK 0x8000 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACL_SRC_SHIFT 15 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACL_SRC_WIDTH 1 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACR_SRC 0x4000 /* AIF1DACR_SRC */ +#define WM8995_AIF1DACR_SRC_MASK 0x4000 /* AIF1DACR_SRC */ +#define WM8995_AIF1DACR_SRC_SHIFT 14 /* AIF1DACR_SRC */ +#define WM8995_AIF1DACR_SRC_WIDTH 1 /* AIF1DACR_SRC */ +#define WM8995_AIF1DAC_BOOST_MASK 0x0C00 /* AIF1DAC_BOOST - [11:10] */ +#define WM8995_AIF1DAC_BOOST_SHIFT 10 /* AIF1DAC_BOOST - [11:10] */ +#define WM8995_AIF1DAC_BOOST_WIDTH 2 /* AIF1DAC_BOOST - [11:10] */ +#define WM8995_AIF1DAC_COMP 0x0010 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMP_MASK 0x0010 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMP_SHIFT 4 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMP_WIDTH 1 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMPMODE 0x0008 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1DAC_COMPMODE_MASK 0x0008 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1DAC_COMPMODE_SHIFT 3 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1DAC_COMPMODE_WIDTH 1 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1ADC_COMP 0x0004 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMP_MASK 0x0004 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMP_SHIFT 2 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMP_WIDTH 1 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMPMODE 0x0002 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1ADC_COMPMODE_MASK 0x0002 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1ADC_COMPMODE_SHIFT 1 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1ADC_COMPMODE_WIDTH 1 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1_LOOPBACK 0x0001 /* AIF1_LOOPBACK */ +#define WM8995_AIF1_LOOPBACK_MASK 0x0001 /* AIF1_LOOPBACK */ +#define WM8995_AIF1_LOOPBACK_SHIFT 0 /* AIF1_LOOPBACK */ +#define WM8995_AIF1_LOOPBACK_WIDTH 1 /* AIF1_LOOPBACK */ + +/* + * R770 (0x302) - AIF1 Master/Slave + */ +#define WM8995_AIF1_TRI 0x8000 /* AIF1_TRI */ +#define WM8995_AIF1_TRI_MASK 0x8000 /* AIF1_TRI */ +#define WM8995_AIF1_TRI_SHIFT 15 /* AIF1_TRI */ +#define WM8995_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ +#define WM8995_AIF1_MSTR 0x4000 /* AIF1_MSTR */ +#define WM8995_AIF1_MSTR_MASK 0x4000 /* AIF1_MSTR */ +#define WM8995_AIF1_MSTR_SHIFT 14 /* AIF1_MSTR */ +#define WM8995_AIF1_MSTR_WIDTH 1 /* AIF1_MSTR */ +#define WM8995_AIF1_CLK_FRC 0x2000 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_CLK_FRC_MASK 0x2000 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_CLK_FRC_SHIFT 13 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_CLK_FRC_WIDTH 1 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC 0x1000 /* AIF1_LRCLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC_MASK 0x1000 /* AIF1_LRCLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC_SHIFT 12 /* AIF1_LRCLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC_WIDTH 1 /* AIF1_LRCLK_FRC */ + +/* + * R771 (0x303) - AIF1 BCLK + */ +#define WM8995_AIF1_BCLK_DIV_MASK 0x00F0 /* AIF1_BCLK_DIV - [7:4] */ +#define WM8995_AIF1_BCLK_DIV_SHIFT 4 /* AIF1_BCLK_DIV - [7:4] */ +#define WM8995_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [7:4] */ + +/* + * R772 (0x304) - AIF1ADC LRCLK + */ +#define WM8995_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_LRCLK_DIR_WIDTH 1 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_RATE_MASK 0x07FF /* AIF1ADC_RATE - [10:0] */ +#define WM8995_AIF1ADC_RATE_SHIFT 0 /* AIF1ADC_RATE - [10:0] */ +#define WM8995_AIF1ADC_RATE_WIDTH 11 /* AIF1ADC_RATE - [10:0] */ + +/* + * R773 (0x305) - AIF1DAC LRCLK + */ +#define WM8995_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_LRCLK_DIR_WIDTH 1 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_RATE_MASK 0x07FF /* AIF1DAC_RATE - [10:0] */ +#define WM8995_AIF1DAC_RATE_SHIFT 0 /* AIF1DAC_RATE - [10:0] */ +#define WM8995_AIF1DAC_RATE_WIDTH 11 /* AIF1DAC_RATE - [10:0] */ + +/* + * R774 (0x306) - AIF1DAC Data + */ +#define WM8995_AIF1DACL_DAT_INV 0x0002 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACL_DAT_INV_MASK 0x0002 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACL_DAT_INV_SHIFT 1 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACL_DAT_INV_WIDTH 1 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV 0x0001 /* AIF1DACR_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV_MASK 0x0001 /* AIF1DACR_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV_SHIFT 0 /* AIF1DACR_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV_WIDTH 1 /* AIF1DACR_DAT_INV */ + +/* + * R775 (0x307) - AIF1ADC Data + */ +#define WM8995_AIF1ADCL_DAT_INV 0x0002 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCL_DAT_INV_MASK 0x0002 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCL_DAT_INV_SHIFT 1 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCL_DAT_INV_WIDTH 1 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV 0x0001 /* AIF1ADCR_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV_MASK 0x0001 /* AIF1ADCR_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV_SHIFT 0 /* AIF1ADCR_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV_WIDTH 1 /* AIF1ADCR_DAT_INV */ + +/* + * R784 (0x310) - AIF2 Control (1) + */ +#define WM8995_AIF2ADCL_SRC 0x8000 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCL_SRC_MASK 0x8000 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCL_SRC_SHIFT 15 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCL_SRC_WIDTH 1 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCR_SRC 0x4000 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADCR_SRC_MASK 0x4000 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADCR_SRC_SHIFT 14 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADCR_SRC_WIDTH 1 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADC_TDM 0x2000 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_MASK 0x2000 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_SHIFT 13 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_WIDTH 1 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_CHAN 0x1000 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2ADC_TDM_CHAN_MASK 0x1000 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2ADC_TDM_CHAN_SHIFT 12 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2ADC_TDM_CHAN_WIDTH 1 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2_BCLK_INV 0x0100 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_BCLK_INV_MASK 0x0100 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_BCLK_INV_SHIFT 8 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_LRCLK_INV 0x0080 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_LRCLK_INV_MASK 0x0080 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_LRCLK_INV_SHIFT 7 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_LRCLK_INV_WIDTH 1 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_WL_MASK 0x0060 /* AIF2_WL - [6:5] */ +#define WM8995_AIF2_WL_SHIFT 5 /* AIF2_WL - [6:5] */ +#define WM8995_AIF2_WL_WIDTH 2 /* AIF2_WL - [6:5] */ +#define WM8995_AIF2_FMT_MASK 0x0018 /* AIF2_FMT - [4:3] */ +#define WM8995_AIF2_FMT_SHIFT 3 /* AIF2_FMT - [4:3] */ +#define WM8995_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [4:3] */ + +/* + * R785 (0x311) - AIF2 Control (2) + */ +#define WM8995_AIF2DACL_SRC 0x8000 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACL_SRC_MASK 0x8000 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACL_SRC_SHIFT 15 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACL_SRC_WIDTH 1 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACR_SRC 0x4000 /* AIF2DACR_SRC */ +#define WM8995_AIF2DACR_SRC_MASK 0x4000 /* AIF2DACR_SRC */ +#define WM8995_AIF2DACR_SRC_SHIFT 14 /* AIF2DACR_SRC */ +#define WM8995_AIF2DACR_SRC_WIDTH 1 /* AIF2DACR_SRC */ +#define WM8995_AIF2DAC_TDM 0x2000 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_MASK 0x2000 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_SHIFT 13 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_WIDTH 1 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_CHAN 0x1000 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_TDM_CHAN_MASK 0x1000 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_TDM_CHAN_SHIFT 12 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_TDM_CHAN_WIDTH 1 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_BOOST_MASK 0x0C00 /* AIF2DAC_BOOST - [11:10] */ +#define WM8995_AIF2DAC_BOOST_SHIFT 10 /* AIF2DAC_BOOST - [11:10] */ +#define WM8995_AIF2DAC_BOOST_WIDTH 2 /* AIF2DAC_BOOST - [11:10] */ +#define WM8995_AIF2DAC_COMP 0x0010 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMP_MASK 0x0010 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMP_SHIFT 4 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMP_WIDTH 1 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMPMODE 0x0008 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2DAC_COMPMODE_MASK 0x0008 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2DAC_COMPMODE_SHIFT 3 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2DAC_COMPMODE_WIDTH 1 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2ADC_COMP 0x0004 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMP_MASK 0x0004 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMP_SHIFT 2 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMP_WIDTH 1 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMPMODE 0x0002 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2ADC_COMPMODE_MASK 0x0002 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2ADC_COMPMODE_SHIFT 1 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2ADC_COMPMODE_WIDTH 1 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2_LOOPBACK 0x0001 /* AIF2_LOOPBACK */ +#define WM8995_AIF2_LOOPBACK_MASK 0x0001 /* AIF2_LOOPBACK */ +#define WM8995_AIF2_LOOPBACK_SHIFT 0 /* AIF2_LOOPBACK */ +#define WM8995_AIF2_LOOPBACK_WIDTH 1 /* AIF2_LOOPBACK */ + +/* + * R786 (0x312) - AIF2 Master/Slave + */ +#define WM8995_AIF2_TRI 0x8000 /* AIF2_TRI */ +#define WM8995_AIF2_TRI_MASK 0x8000 /* AIF2_TRI */ +#define WM8995_AIF2_TRI_SHIFT 15 /* AIF2_TRI */ +#define WM8995_AIF2_TRI_WIDTH 1 /* AIF2_TRI */ +#define WM8995_AIF2_MSTR 0x4000 /* AIF2_MSTR */ +#define WM8995_AIF2_MSTR_MASK 0x4000 /* AIF2_MSTR */ +#define WM8995_AIF2_MSTR_SHIFT 14 /* AIF2_MSTR */ +#define WM8995_AIF2_MSTR_WIDTH 1 /* AIF2_MSTR */ +#define WM8995_AIF2_CLK_FRC 0x2000 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_CLK_FRC_MASK 0x2000 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_CLK_FRC_SHIFT 13 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_CLK_FRC_WIDTH 1 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC 0x1000 /* AIF2_LRCLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC_MASK 0x1000 /* AIF2_LRCLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC_SHIFT 12 /* AIF2_LRCLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC_WIDTH 1 /* AIF2_LRCLK_FRC */ + +/* + * R787 (0x313) - AIF2 BCLK + */ +#define WM8995_AIF2_BCLK_DIV_MASK 0x00F0 /* AIF2_BCLK_DIV - [7:4] */ +#define WM8995_AIF2_BCLK_DIV_SHIFT 4 /* AIF2_BCLK_DIV - [7:4] */ +#define WM8995_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [7:4] */ + +/* + * R788 (0x314) - AIF2ADC LRCLK + */ +#define WM8995_AIF2ADC_LRCLK_DIR 0x0800 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_LRCLK_DIR_MASK 0x0800 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_LRCLK_DIR_SHIFT 11 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_LRCLK_DIR_WIDTH 1 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_RATE_MASK 0x07FF /* AIF2ADC_RATE - [10:0] */ +#define WM8995_AIF2ADC_RATE_SHIFT 0 /* AIF2ADC_RATE - [10:0] */ +#define WM8995_AIF2ADC_RATE_WIDTH 11 /* AIF2ADC_RATE - [10:0] */ + +/* + * R789 (0x315) - AIF2DAC LRCLK + */ +#define WM8995_AIF2DAC_LRCLK_DIR 0x0800 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_LRCLK_DIR_MASK 0x0800 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_LRCLK_DIR_SHIFT 11 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_LRCLK_DIR_WIDTH 1 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_RATE_MASK 0x07FF /* AIF2DAC_RATE - [10:0] */ +#define WM8995_AIF2DAC_RATE_SHIFT 0 /* AIF2DAC_RATE - [10:0] */ +#define WM8995_AIF2DAC_RATE_WIDTH 11 /* AIF2DAC_RATE - [10:0] */ + +/* + * R790 (0x316) - AIF2DAC Data + */ +#define WM8995_AIF2DACL_DAT_INV 0x0002 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACL_DAT_INV_MASK 0x0002 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACL_DAT_INV_SHIFT 1 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACL_DAT_INV_WIDTH 1 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV 0x0001 /* AIF2DACR_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV_MASK 0x0001 /* AIF2DACR_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV_SHIFT 0 /* AIF2DACR_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV_WIDTH 1 /* AIF2DACR_DAT_INV */ + +/* + * R791 (0x317) - AIF2ADC Data + */ +#define WM8995_AIF2ADCL_DAT_INV 0x0002 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCL_DAT_INV_MASK 0x0002 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCL_DAT_INV_SHIFT 1 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCL_DAT_INV_WIDTH 1 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV 0x0001 /* AIF2ADCR_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV_MASK 0x0001 /* AIF2ADCR_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV_SHIFT 0 /* AIF2ADCR_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV_WIDTH 1 /* AIF2ADCR_DAT_INV */ + +/* + * R1024 (0x400) - AIF1 ADC1 Left Volume + */ +#define WM8995_AIF1ADC1_VU 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_MASK 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_SHIFT 8 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_WIDTH 1 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1L_VOL_MASK 0x00FF /* AIF1ADC1L_VOL - [7:0] */ +#define WM8995_AIF1ADC1L_VOL_SHIFT 0 /* AIF1ADC1L_VOL - [7:0] */ +#define WM8995_AIF1ADC1L_VOL_WIDTH 8 /* AIF1ADC1L_VOL - [7:0] */ + +/* + * R1025 (0x401) - AIF1 ADC1 Right Volume + */ +#define WM8995_AIF1ADC1_VU 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_MASK 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_SHIFT 8 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_WIDTH 1 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1R_VOL_MASK 0x00FF /* AIF1ADC1R_VOL - [7:0] */ +#define WM8995_AIF1ADC1R_VOL_SHIFT 0 /* AIF1ADC1R_VOL - [7:0] */ +#define WM8995_AIF1ADC1R_VOL_WIDTH 8 /* AIF1ADC1R_VOL - [7:0] */ + +/* + * R1026 (0x402) - AIF1 DAC1 Left Volume + */ +#define WM8995_AIF1DAC1_VU 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_MASK 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_SHIFT 8 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_WIDTH 1 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1L_VOL_MASK 0x00FF /* AIF1DAC1L_VOL - [7:0] */ +#define WM8995_AIF1DAC1L_VOL_SHIFT 0 /* AIF1DAC1L_VOL - [7:0] */ +#define WM8995_AIF1DAC1L_VOL_WIDTH 8 /* AIF1DAC1L_VOL - [7:0] */ + +/* + * R1027 (0x403) - AIF1 DAC1 Right Volume + */ +#define WM8995_AIF1DAC1_VU 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_MASK 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_SHIFT 8 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_WIDTH 1 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1R_VOL_MASK 0x00FF /* AIF1DAC1R_VOL - [7:0] */ +#define WM8995_AIF1DAC1R_VOL_SHIFT 0 /* AIF1DAC1R_VOL - [7:0] */ +#define WM8995_AIF1DAC1R_VOL_WIDTH 8 /* AIF1DAC1R_VOL - [7:0] */ + +/* + * R1028 (0x404) - AIF1 ADC2 Left Volume + */ +#define WM8995_AIF1ADC2_VU 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_MASK 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_SHIFT 8 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_WIDTH 1 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2L_VOL_MASK 0x00FF /* AIF1ADC2L_VOL - [7:0] */ +#define WM8995_AIF1ADC2L_VOL_SHIFT 0 /* AIF1ADC2L_VOL - [7:0] */ +#define WM8995_AIF1ADC2L_VOL_WIDTH 8 /* AIF1ADC2L_VOL - [7:0] */ + +/* + * R1029 (0x405) - AIF1 ADC2 Right Volume + */ +#define WM8995_AIF1ADC2_VU 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_MASK 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_SHIFT 8 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_WIDTH 1 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2R_VOL_MASK 0x00FF /* AIF1ADC2R_VOL - [7:0] */ +#define WM8995_AIF1ADC2R_VOL_SHIFT 0 /* AIF1ADC2R_VOL - [7:0] */ +#define WM8995_AIF1ADC2R_VOL_WIDTH 8 /* AIF1ADC2R_VOL - [7:0] */ + +/* + * R1030 (0x406) - AIF1 DAC2 Left Volume + */ +#define WM8995_AIF1DAC2_VU 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_MASK 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_SHIFT 8 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_WIDTH 1 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2L_VOL_MASK 0x00FF /* AIF1DAC2L_VOL - [7:0] */ +#define WM8995_AIF1DAC2L_VOL_SHIFT 0 /* AIF1DAC2L_VOL - [7:0] */ +#define WM8995_AIF1DAC2L_VOL_WIDTH 8 /* AIF1DAC2L_VOL - [7:0] */ + +/* + * R1031 (0x407) - AIF1 DAC2 Right Volume + */ +#define WM8995_AIF1DAC2_VU 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_MASK 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_SHIFT 8 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_WIDTH 1 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2R_VOL_MASK 0x00FF /* AIF1DAC2R_VOL - [7:0] */ +#define WM8995_AIF1DAC2R_VOL_SHIFT 0 /* AIF1DAC2R_VOL - [7:0] */ +#define WM8995_AIF1DAC2R_VOL_WIDTH 8 /* AIF1DAC2R_VOL - [7:0] */ + +/* + * R1040 (0x410) - AIF1 ADC1 Filters + */ +#define WM8995_AIF1ADC_4FS 0x8000 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC_4FS_MASK 0x8000 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC_4FS_SHIFT 15 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC_4FS_WIDTH 1 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC1L_HPF 0x1000 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1L_HPF_MASK 0x1000 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1L_HPF_SHIFT 12 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1L_HPF_WIDTH 1 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1R_HPF 0x0800 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1R_HPF_MASK 0x0800 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1R_HPF_SHIFT 11 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1R_HPF_WIDTH 1 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1_HPF_MODE 0x0008 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_MODE_MASK 0x0008 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_MODE_SHIFT 3 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_MODE_WIDTH 1 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_CUT_MASK 0x0007 /* AIF1ADC1_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC1_HPF_CUT_SHIFT 0 /* AIF1ADC1_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC1_HPF_CUT_WIDTH 3 /* AIF1ADC1_HPF_CUT - [2:0] */ + +/* + * R1041 (0x411) - AIF1 ADC2 Filters + */ +#define WM8995_AIF1ADC2L_HPF 0x1000 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2L_HPF_MASK 0x1000 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2L_HPF_SHIFT 12 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2L_HPF_WIDTH 1 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2R_HPF 0x0800 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2R_HPF_MASK 0x0800 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2R_HPF_SHIFT 11 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2R_HPF_WIDTH 1 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2_HPF_MODE 0x0008 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_MODE_MASK 0x0008 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_MODE_SHIFT 3 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_MODE_WIDTH 1 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_CUT_MASK 0x0007 /* AIF1ADC2_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC2_HPF_CUT_SHIFT 0 /* AIF1ADC2_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC2_HPF_CUT_WIDTH 3 /* AIF1ADC2_HPF_CUT - [2:0] */ + +/* + * R1056 (0x420) - AIF1 DAC1 Filters (1) + */ +#define WM8995_AIF1DAC1_MUTE 0x0200 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MUTE_MASK 0x0200 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MUTE_SHIFT 9 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MUTE_WIDTH 1 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MONO 0x0080 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MONO_MASK 0x0080 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MONO_SHIFT 7 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MONO_WIDTH 1 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MUTERATE 0x0020 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_MUTERATE_MASK 0x0020 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_MUTERATE_SHIFT 5 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_MUTERATE_WIDTH 1 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP 0x0010 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_DEEMP_MASK 0x0006 /* AIF1DAC1_DEEMP - [2:1] */ +#define WM8995_AIF1DAC1_DEEMP_SHIFT 1 /* AIF1DAC1_DEEMP - [2:1] */ +#define WM8995_AIF1DAC1_DEEMP_WIDTH 2 /* AIF1DAC1_DEEMP - [2:1] */ + +/* + * R1057 (0x421) - AIF1 DAC1 Filters (2) + */ +#define WM8995_AIF1DAC1_3D_GAIN_MASK 0x3E00 /* AIF1DAC1_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC1_3D_GAIN_SHIFT 9 /* AIF1DAC1_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC1_3D_GAIN_WIDTH 5 /* AIF1DAC1_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC1_3D_ENA 0x0100 /* AIF1DAC1_3D_ENA */ +#define WM8995_AIF1DAC1_3D_ENA_MASK 0x0100 /* AIF1DAC1_3D_ENA */ +#define WM8995_AIF1DAC1_3D_ENA_SHIFT 8 /* AIF1DAC1_3D_ENA */ +#define WM8995_AIF1DAC1_3D_ENA_WIDTH 1 /* AIF1DAC1_3D_ENA */ + +/* + * R1058 (0x422) - AIF1 DAC2 Filters (1) + */ +#define WM8995_AIF1DAC2_MUTE 0x0200 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MUTE_MASK 0x0200 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MUTE_SHIFT 9 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MUTE_WIDTH 1 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MONO 0x0080 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MONO_MASK 0x0080 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MONO_SHIFT 7 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MONO_WIDTH 1 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MUTERATE 0x0020 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_MUTERATE_MASK 0x0020 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_MUTERATE_SHIFT 5 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_MUTERATE_WIDTH 1 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP 0x0010 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_DEEMP_MASK 0x0006 /* AIF1DAC2_DEEMP - [2:1] */ +#define WM8995_AIF1DAC2_DEEMP_SHIFT 1 /* AIF1DAC2_DEEMP - [2:1] */ +#define WM8995_AIF1DAC2_DEEMP_WIDTH 2 /* AIF1DAC2_DEEMP - [2:1] */ + +/* + * R1059 (0x423) - AIF1 DAC2 Filters (2) + */ +#define WM8995_AIF1DAC2_3D_GAIN_MASK 0x3E00 /* AIF1DAC2_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC2_3D_GAIN_SHIFT 9 /* AIF1DAC2_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC2_3D_GAIN_WIDTH 5 /* AIF1DAC2_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC2_3D_ENA 0x0100 /* AIF1DAC2_3D_ENA */ +#define WM8995_AIF1DAC2_3D_ENA_MASK 0x0100 /* AIF1DAC2_3D_ENA */ +#define WM8995_AIF1DAC2_3D_ENA_SHIFT 8 /* AIF1DAC2_3D_ENA */ +#define WM8995_AIF1DAC2_3D_ENA_WIDTH 1 /* AIF1DAC2_3D_ENA */ + +/* + * R1088 (0x440) - AIF1 DRC1 (1) + */ +#define WM8995_AIF1DRC1_SIG_DET_RMS_MASK 0xF800 /* AIF1DRC1_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC1_SIG_DET_RMS_SHIFT 11 /* AIF1DRC1_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC1_SIG_DET_RMS_WIDTH 5 /* AIF1DRC1_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC1_SIG_DET_PK_MASK 0x0600 /* AIF1DRC1_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC1_SIG_DET_PK_SHIFT 9 /* AIF1DRC1_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC1_SIG_DET_PK_WIDTH 2 /* AIF1DRC1_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC1_NG_ENA 0x0100 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_NG_ENA_MASK 0x0100 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_NG_ENA_SHIFT 8 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_NG_ENA_WIDTH 1 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_SIG_DET_MODE 0x0080 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET_MODE_MASK 0x0080 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET_MODE_SHIFT 7 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET_MODE_WIDTH 1 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET 0x0040 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_SIG_DET_MASK 0x0040 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_SIG_DET_SHIFT 6 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_SIG_DET_WIDTH 1 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA 0x0020 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA_MASK 0x0020 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA_SHIFT 5 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA_WIDTH 1 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_QR 0x0010 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_QR_MASK 0x0010 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_QR_SHIFT 4 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_QR_WIDTH 1 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_ANTICLIP 0x0008 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DRC1_ANTICLIP_MASK 0x0008 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DRC1_ANTICLIP_SHIFT 3 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DRC1_ANTICLIP_WIDTH 1 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DAC1_DRC_ENA 0x0004 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1DAC1_DRC_ENA_MASK 0x0004 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1DAC1_DRC_ENA_SHIFT 2 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1DAC1_DRC_ENA_WIDTH 1 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA 0x0002 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA_MASK 0x0002 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA_SHIFT 1 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA_WIDTH 1 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA 0x0001 /* AIF1ADC1R_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA_MASK 0x0001 /* AIF1ADC1R_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA_SHIFT 0 /* AIF1ADC1R_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA_WIDTH 1 /* AIF1ADC1R_DRC_ENA */ + +/* + * R1089 (0x441) - AIF1 DRC1 (2) + */ +#define WM8995_AIF1DRC1_ATK_MASK 0x1E00 /* AIF1DRC1_ATK - [12:9] */ +#define WM8995_AIF1DRC1_ATK_SHIFT 9 /* AIF1DRC1_ATK - [12:9] */ +#define WM8995_AIF1DRC1_ATK_WIDTH 4 /* AIF1DRC1_ATK - [12:9] */ +#define WM8995_AIF1DRC1_DCY_MASK 0x01E0 /* AIF1DRC1_DCY - [8:5] */ +#define WM8995_AIF1DRC1_DCY_SHIFT 5 /* AIF1DRC1_DCY - [8:5] */ +#define WM8995_AIF1DRC1_DCY_WIDTH 4 /* AIF1DRC1_DCY - [8:5] */ +#define WM8995_AIF1DRC1_MINGAIN_MASK 0x001C /* AIF1DRC1_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC1_MINGAIN_SHIFT 2 /* AIF1DRC1_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC1_MINGAIN_WIDTH 3 /* AIF1DRC1_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC1_MAXGAIN_MASK 0x0003 /* AIF1DRC1_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC1_MAXGAIN_SHIFT 0 /* AIF1DRC1_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC1_MAXGAIN_WIDTH 2 /* AIF1DRC1_MAXGAIN - [1:0] */ + +/* + * R1090 (0x442) - AIF1 DRC1 (3) + */ +#define WM8995_AIF1DRC1_NG_MINGAIN_MASK 0xF000 /* AIF1DRC1_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC1_NG_MINGAIN_SHIFT 12 /* AIF1DRC1_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC1_NG_MINGAIN_WIDTH 4 /* AIF1DRC1_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC1_NG_EXP_MASK 0x0C00 /* AIF1DRC1_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC1_NG_EXP_SHIFT 10 /* AIF1DRC1_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC1_NG_EXP_WIDTH 2 /* AIF1DRC1_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC1_QR_THR_MASK 0x0300 /* AIF1DRC1_QR_THR - [9:8] */ +#define WM8995_AIF1DRC1_QR_THR_SHIFT 8 /* AIF1DRC1_QR_THR - [9:8] */ +#define WM8995_AIF1DRC1_QR_THR_WIDTH 2 /* AIF1DRC1_QR_THR - [9:8] */ +#define WM8995_AIF1DRC1_QR_DCY_MASK 0x00C0 /* AIF1DRC1_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC1_QR_DCY_SHIFT 6 /* AIF1DRC1_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC1_QR_DCY_WIDTH 2 /* AIF1DRC1_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC1_HI_COMP_MASK 0x0038 /* AIF1DRC1_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC1_HI_COMP_SHIFT 3 /* AIF1DRC1_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC1_HI_COMP_WIDTH 3 /* AIF1DRC1_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC1_LO_COMP_MASK 0x0007 /* AIF1DRC1_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC1_LO_COMP_SHIFT 0 /* AIF1DRC1_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC1_LO_COMP_WIDTH 3 /* AIF1DRC1_LO_COMP - [2:0] */ + +/* + * R1091 (0x443) - AIF1 DRC1 (4) + */ +#define WM8995_AIF1DRC1_KNEE_IP_MASK 0x07E0 /* AIF1DRC1_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC1_KNEE_IP_SHIFT 5 /* AIF1DRC1_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC1_KNEE_IP_WIDTH 6 /* AIF1DRC1_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC1_KNEE_OP_MASK 0x001F /* AIF1DRC1_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE_OP_SHIFT 0 /* AIF1DRC1_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE_OP_WIDTH 5 /* AIF1DRC1_KNEE_OP - [4:0] */ + +/* + * R1092 (0x444) - AIF1 DRC1 (5) + */ +#define WM8995_AIF1DRC1_KNEE2_IP_MASK 0x03E0 /* AIF1DRC1_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC1_KNEE2_IP_SHIFT 5 /* AIF1DRC1_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC1_KNEE2_IP_WIDTH 5 /* AIF1DRC1_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC1_KNEE2_OP_MASK 0x001F /* AIF1DRC1_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE2_OP_SHIFT 0 /* AIF1DRC1_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE2_OP_WIDTH 5 /* AIF1DRC1_KNEE2_OP - [4:0] */ + +/* + * R1104 (0x450) - AIF1 DRC2 (1) + */ +#define WM8995_AIF1DRC2_SIG_DET_RMS_MASK 0xF800 /* AIF1DRC2_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC2_SIG_DET_RMS_SHIFT 11 /* AIF1DRC2_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC2_SIG_DET_RMS_WIDTH 5 /* AIF1DRC2_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC2_SIG_DET_PK_MASK 0x0600 /* AIF1DRC2_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC2_SIG_DET_PK_SHIFT 9 /* AIF1DRC2_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC2_SIG_DET_PK_WIDTH 2 /* AIF1DRC2_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC2_NG_ENA 0x0100 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_NG_ENA_MASK 0x0100 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_NG_ENA_SHIFT 8 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_NG_ENA_WIDTH 1 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_SIG_DET_MODE 0x0080 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET_MODE_MASK 0x0080 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET_MODE_SHIFT 7 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET_MODE_WIDTH 1 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET 0x0040 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_SIG_DET_MASK 0x0040 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_SIG_DET_SHIFT 6 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_SIG_DET_WIDTH 1 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA 0x0020 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA_MASK 0x0020 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA_SHIFT 5 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA_WIDTH 1 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_QR 0x0010 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_QR_MASK 0x0010 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_QR_SHIFT 4 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_QR_WIDTH 1 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_ANTICLIP 0x0008 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DRC2_ANTICLIP_MASK 0x0008 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DRC2_ANTICLIP_SHIFT 3 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DRC2_ANTICLIP_WIDTH 1 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DAC2_DRC_ENA 0x0004 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1DAC2_DRC_ENA_MASK 0x0004 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1DAC2_DRC_ENA_SHIFT 2 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1DAC2_DRC_ENA_WIDTH 1 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA 0x0002 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA_MASK 0x0002 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA_SHIFT 1 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA_WIDTH 1 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA 0x0001 /* AIF1ADC2R_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA_MASK 0x0001 /* AIF1ADC2R_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA_SHIFT 0 /* AIF1ADC2R_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA_WIDTH 1 /* AIF1ADC2R_DRC_ENA */ + +/* + * R1105 (0x451) - AIF1 DRC2 (2) + */ +#define WM8995_AIF1DRC2_ATK_MASK 0x1E00 /* AIF1DRC2_ATK - [12:9] */ +#define WM8995_AIF1DRC2_ATK_SHIFT 9 /* AIF1DRC2_ATK - [12:9] */ +#define WM8995_AIF1DRC2_ATK_WIDTH 4 /* AIF1DRC2_ATK - [12:9] */ +#define WM8995_AIF1DRC2_DCY_MASK 0x01E0 /* AIF1DRC2_DCY - [8:5] */ +#define WM8995_AIF1DRC2_DCY_SHIFT 5 /* AIF1DRC2_DCY - [8:5] */ +#define WM8995_AIF1DRC2_DCY_WIDTH 4 /* AIF1DRC2_DCY - [8:5] */ +#define WM8995_AIF1DRC2_MINGAIN_MASK 0x001C /* AIF1DRC2_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC2_MINGAIN_SHIFT 2 /* AIF1DRC2_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC2_MINGAIN_WIDTH 3 /* AIF1DRC2_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC2_MAXGAIN_MASK 0x0003 /* AIF1DRC2_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC2_MAXGAIN_SHIFT 0 /* AIF1DRC2_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC2_MAXGAIN_WIDTH 2 /* AIF1DRC2_MAXGAIN - [1:0] */ + +/* + * R1106 (0x452) - AIF1 DRC2 (3) + */ +#define WM8995_AIF1DRC2_NG_MINGAIN_MASK 0xF000 /* AIF1DRC2_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC2_NG_MINGAIN_SHIFT 12 /* AIF1DRC2_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC2_NG_MINGAIN_WIDTH 4 /* AIF1DRC2_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC2_NG_EXP_MASK 0x0C00 /* AIF1DRC2_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC2_NG_EXP_SHIFT 10 /* AIF1DRC2_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC2_NG_EXP_WIDTH 2 /* AIF1DRC2_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC2_QR_THR_MASK 0x0300 /* AIF1DRC2_QR_THR - [9:8] */ +#define WM8995_AIF1DRC2_QR_THR_SHIFT 8 /* AIF1DRC2_QR_THR - [9:8] */ +#define WM8995_AIF1DRC2_QR_THR_WIDTH 2 /* AIF1DRC2_QR_THR - [9:8] */ +#define WM8995_AIF1DRC2_QR_DCY_MASK 0x00C0 /* AIF1DRC2_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC2_QR_DCY_SHIFT 6 /* AIF1DRC2_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC2_QR_DCY_WIDTH 2 /* AIF1DRC2_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC2_HI_COMP_MASK 0x0038 /* AIF1DRC2_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC2_HI_COMP_SHIFT 3 /* AIF1DRC2_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC2_HI_COMP_WIDTH 3 /* AIF1DRC2_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC2_LO_COMP_MASK 0x0007 /* AIF1DRC2_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC2_LO_COMP_SHIFT 0 /* AIF1DRC2_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC2_LO_COMP_WIDTH 3 /* AIF1DRC2_LO_COMP - [2:0] */ + +/* + * R1107 (0x453) - AIF1 DRC2 (4) + */ +#define WM8995_AIF1DRC2_KNEE_IP_MASK 0x07E0 /* AIF1DRC2_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC2_KNEE_IP_SHIFT 5 /* AIF1DRC2_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC2_KNEE_IP_WIDTH 6 /* AIF1DRC2_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC2_KNEE_OP_MASK 0x001F /* AIF1DRC2_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE_OP_SHIFT 0 /* AIF1DRC2_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE_OP_WIDTH 5 /* AIF1DRC2_KNEE_OP - [4:0] */ + +/* + * R1108 (0x454) - AIF1 DRC2 (5) + */ +#define WM8995_AIF1DRC2_KNEE2_IP_MASK 0x03E0 /* AIF1DRC2_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC2_KNEE2_IP_SHIFT 5 /* AIF1DRC2_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC2_KNEE2_IP_WIDTH 5 /* AIF1DRC2_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC2_KNEE2_OP_MASK 0x001F /* AIF1DRC2_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE2_OP_SHIFT 0 /* AIF1DRC2_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE2_OP_WIDTH 5 /* AIF1DRC2_KNEE2_OP - [4:0] */ + +/* + * R1152 (0x480) - AIF1 DAC1 EQ Gains (1) + */ +#define WM8995_AIF1DAC1_EQ_B1_GAIN_MASK 0xF800 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B1_GAIN_SHIFT 11 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B1_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B2_GAIN_MASK 0x07C0 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B2_GAIN_SHIFT 6 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B2_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B3_GAIN_MASK 0x003E /* AIF1DAC1_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC1_EQ_B3_GAIN_SHIFT 1 /* AIF1DAC1_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC1_EQ_B3_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC1_EQ_ENA 0x0001 /* AIF1DAC1_EQ_ENA */ +#define WM8995_AIF1DAC1_EQ_ENA_MASK 0x0001 /* AIF1DAC1_EQ_ENA */ +#define WM8995_AIF1DAC1_EQ_ENA_SHIFT 0 /* AIF1DAC1_EQ_ENA */ +#define WM8995_AIF1DAC1_EQ_ENA_WIDTH 1 /* AIF1DAC1_EQ_ENA */ + +/* + * R1153 (0x481) - AIF1 DAC1 EQ Gains (2) + */ +#define WM8995_AIF1DAC1_EQ_B4_GAIN_MASK 0xF800 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B4_GAIN_SHIFT 11 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B4_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B5_GAIN_MASK 0x07C0 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B5_GAIN_SHIFT 6 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B5_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */ + +/* + * R1154 (0x482) - AIF1 DAC1 EQ Band 1 A + */ +#define WM8995_AIF1DAC1_EQ_B1_A_MASK 0xFFFF /* AIF1DAC1_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_A_SHIFT 0 /* AIF1DAC1_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_A_WIDTH 16 /* AIF1DAC1_EQ_B1_A - [15:0] */ + +/* + * R1155 (0x483) - AIF1 DAC1 EQ Band 1 B + */ +#define WM8995_AIF1DAC1_EQ_B1_B_MASK 0xFFFF /* AIF1DAC1_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_B_SHIFT 0 /* AIF1DAC1_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_B_WIDTH 16 /* AIF1DAC1_EQ_B1_B - [15:0] */ + +/* + * R1156 (0x484) - AIF1 DAC1 EQ Band 1 PG + */ +#define WM8995_AIF1DAC1_EQ_B1_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_PG_SHIFT 0 /* AIF1DAC1_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_PG_WIDTH 16 /* AIF1DAC1_EQ_B1_PG - [15:0] */ + +/* + * R1157 (0x485) - AIF1 DAC1 EQ Band 2 A + */ +#define WM8995_AIF1DAC1_EQ_B2_A_MASK 0xFFFF /* AIF1DAC1_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_A_SHIFT 0 /* AIF1DAC1_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_A_WIDTH 16 /* AIF1DAC1_EQ_B2_A - [15:0] */ + +/* + * R1158 (0x486) - AIF1 DAC1 EQ Band 2 B + */ +#define WM8995_AIF1DAC1_EQ_B2_B_MASK 0xFFFF /* AIF1DAC1_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_B_SHIFT 0 /* AIF1DAC1_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_B_WIDTH 16 /* AIF1DAC1_EQ_B2_B - [15:0] */ + +/* + * R1159 (0x487) - AIF1 DAC1 EQ Band 2 C + */ +#define WM8995_AIF1DAC1_EQ_B2_C_MASK 0xFFFF /* AIF1DAC1_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_C_SHIFT 0 /* AIF1DAC1_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_C_WIDTH 16 /* AIF1DAC1_EQ_B2_C - [15:0] */ + +/* + * R1160 (0x488) - AIF1 DAC1 EQ Band 2 PG + */ +#define WM8995_AIF1DAC1_EQ_B2_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_PG_SHIFT 0 /* AIF1DAC1_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_PG_WIDTH 16 /* AIF1DAC1_EQ_B2_PG - [15:0] */ + +/* + * R1161 (0x489) - AIF1 DAC1 EQ Band 3 A + */ +#define WM8995_AIF1DAC1_EQ_B3_A_MASK 0xFFFF /* AIF1DAC1_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_A_SHIFT 0 /* AIF1DAC1_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_A_WIDTH 16 /* AIF1DAC1_EQ_B3_A - [15:0] */ + +/* + * R1162 (0x48A) - AIF1 DAC1 EQ Band 3 B + */ +#define WM8995_AIF1DAC1_EQ_B3_B_MASK 0xFFFF /* AIF1DAC1_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_B_SHIFT 0 /* AIF1DAC1_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_B_WIDTH 16 /* AIF1DAC1_EQ_B3_B - [15:0] */ + +/* + * R1163 (0x48B) - AIF1 DAC1 EQ Band 3 C + */ +#define WM8995_AIF1DAC1_EQ_B3_C_MASK 0xFFFF /* AIF1DAC1_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_C_SHIFT 0 /* AIF1DAC1_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_C_WIDTH 16 /* AIF1DAC1_EQ_B3_C - [15:0] */ + +/* + * R1164 (0x48C) - AIF1 DAC1 EQ Band 3 PG + */ +#define WM8995_AIF1DAC1_EQ_B3_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_PG_SHIFT 0 /* AIF1DAC1_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_PG_WIDTH 16 /* AIF1DAC1_EQ_B3_PG - [15:0] */ + +/* + * R1165 (0x48D) - AIF1 DAC1 EQ Band 4 A + */ +#define WM8995_AIF1DAC1_EQ_B4_A_MASK 0xFFFF /* AIF1DAC1_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_A_SHIFT 0 /* AIF1DAC1_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_A_WIDTH 16 /* AIF1DAC1_EQ_B4_A - [15:0] */ + +/* + * R1166 (0x48E) - AIF1 DAC1 EQ Band 4 B + */ +#define WM8995_AIF1DAC1_EQ_B4_B_MASK 0xFFFF /* AIF1DAC1_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_B_SHIFT 0 /* AIF1DAC1_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_B_WIDTH 16 /* AIF1DAC1_EQ_B4_B - [15:0] */ + +/* + * R1167 (0x48F) - AIF1 DAC1 EQ Band 4 C + */ +#define WM8995_AIF1DAC1_EQ_B4_C_MASK 0xFFFF /* AIF1DAC1_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_C_SHIFT 0 /* AIF1DAC1_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_C_WIDTH 16 /* AIF1DAC1_EQ_B4_C - [15:0] */ + +/* + * R1168 (0x490) - AIF1 DAC1 EQ Band 4 PG + */ +#define WM8995_AIF1DAC1_EQ_B4_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_PG_SHIFT 0 /* AIF1DAC1_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_PG_WIDTH 16 /* AIF1DAC1_EQ_B4_PG - [15:0] */ + +/* + * R1169 (0x491) - AIF1 DAC1 EQ Band 5 A + */ +#define WM8995_AIF1DAC1_EQ_B5_A_MASK 0xFFFF /* AIF1DAC1_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_A_SHIFT 0 /* AIF1DAC1_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_A_WIDTH 16 /* AIF1DAC1_EQ_B5_A - [15:0] */ + +/* + * R1170 (0x492) - AIF1 DAC1 EQ Band 5 B + */ +#define WM8995_AIF1DAC1_EQ_B5_B_MASK 0xFFFF /* AIF1DAC1_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_B_SHIFT 0 /* AIF1DAC1_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_B_WIDTH 16 /* AIF1DAC1_EQ_B5_B - [15:0] */ + +/* + * R1171 (0x493) - AIF1 DAC1 EQ Band 5 PG + */ +#define WM8995_AIF1DAC1_EQ_B5_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_PG_SHIFT 0 /* AIF1DAC1_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_PG_WIDTH 16 /* AIF1DAC1_EQ_B5_PG - [15:0] */ + +/* + * R1184 (0x4A0) - AIF1 DAC2 EQ Gains (1) + */ +#define WM8995_AIF1DAC2_EQ_B1_GAIN_MASK 0xF800 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B1_GAIN_SHIFT 11 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B1_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B2_GAIN_MASK 0x07C0 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B2_GAIN_SHIFT 6 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B2_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B3_GAIN_MASK 0x003E /* AIF1DAC2_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC2_EQ_B3_GAIN_SHIFT 1 /* AIF1DAC2_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC2_EQ_B3_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC2_EQ_ENA 0x0001 /* AIF1DAC2_EQ_ENA */ +#define WM8995_AIF1DAC2_EQ_ENA_MASK 0x0001 /* AIF1DAC2_EQ_ENA */ +#define WM8995_AIF1DAC2_EQ_ENA_SHIFT 0 /* AIF1DAC2_EQ_ENA */ +#define WM8995_AIF1DAC2_EQ_ENA_WIDTH 1 /* AIF1DAC2_EQ_ENA */ + +/* + * R1185 (0x4A1) - AIF1 DAC2 EQ Gains (2) + */ +#define WM8995_AIF1DAC2_EQ_B4_GAIN_MASK 0xF800 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B4_GAIN_SHIFT 11 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B4_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B5_GAIN_MASK 0x07C0 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B5_GAIN_SHIFT 6 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B5_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */ + +/* + * R1186 (0x4A2) - AIF1 DAC2 EQ Band 1 A + */ +#define WM8995_AIF1DAC2_EQ_B1_A_MASK 0xFFFF /* AIF1DAC2_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_A_SHIFT 0 /* AIF1DAC2_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_A_WIDTH 16 /* AIF1DAC2_EQ_B1_A - [15:0] */ + +/* + * R1187 (0x4A3) - AIF1 DAC2 EQ Band 1 B + */ +#define WM8995_AIF1DAC2_EQ_B1_B_MASK 0xFFFF /* AIF1DAC2_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_B_SHIFT 0 /* AIF1DAC2_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_B_WIDTH 16 /* AIF1DAC2_EQ_B1_B - [15:0] */ + +/* + * R1188 (0x4A4) - AIF1 DAC2 EQ Band 1 PG + */ +#define WM8995_AIF1DAC2_EQ_B1_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_PG_SHIFT 0 /* AIF1DAC2_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_PG_WIDTH 16 /* AIF1DAC2_EQ_B1_PG - [15:0] */ + +/* + * R1189 (0x4A5) - AIF1 DAC2 EQ Band 2 A + */ +#define WM8995_AIF1DAC2_EQ_B2_A_MASK 0xFFFF /* AIF1DAC2_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_A_SHIFT 0 /* AIF1DAC2_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_A_WIDTH 16 /* AIF1DAC2_EQ_B2_A - [15:0] */ + +/* + * R1190 (0x4A6) - AIF1 DAC2 EQ Band 2 B + */ +#define WM8995_AIF1DAC2_EQ_B2_B_MASK 0xFFFF /* AIF1DAC2_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_B_SHIFT 0 /* AIF1DAC2_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_B_WIDTH 16 /* AIF1DAC2_EQ_B2_B - [15:0] */ + +/* + * R1191 (0x4A7) - AIF1 DAC2 EQ Band 2 C + */ +#define WM8995_AIF1DAC2_EQ_B2_C_MASK 0xFFFF /* AIF1DAC2_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_C_SHIFT 0 /* AIF1DAC2_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_C_WIDTH 16 /* AIF1DAC2_EQ_B2_C - [15:0] */ + +/* + * R1192 (0x4A8) - AIF1 DAC2 EQ Band 2 PG + */ +#define WM8995_AIF1DAC2_EQ_B2_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_PG_SHIFT 0 /* AIF1DAC2_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_PG_WIDTH 16 /* AIF1DAC2_EQ_B2_PG - [15:0] */ + +/* + * R1193 (0x4A9) - AIF1 DAC2 EQ Band 3 A + */ +#define WM8995_AIF1DAC2_EQ_B3_A_MASK 0xFFFF /* AIF1DAC2_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_A_SHIFT 0 /* AIF1DAC2_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_A_WIDTH 16 /* AIF1DAC2_EQ_B3_A - [15:0] */ + +/* + * R1194 (0x4AA) - AIF1 DAC2 EQ Band 3 B + */ +#define WM8995_AIF1DAC2_EQ_B3_B_MASK 0xFFFF /* AIF1DAC2_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_B_SHIFT 0 /* AIF1DAC2_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_B_WIDTH 16 /* AIF1DAC2_EQ_B3_B - [15:0] */ + +/* + * R1195 (0x4AB) - AIF1 DAC2 EQ Band 3 C + */ +#define WM8995_AIF1DAC2_EQ_B3_C_MASK 0xFFFF /* AIF1DAC2_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_C_SHIFT 0 /* AIF1DAC2_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_C_WIDTH 16 /* AIF1DAC2_EQ_B3_C - [15:0] */ + +/* + * R1196 (0x4AC) - AIF1 DAC2 EQ Band 3 PG + */ +#define WM8995_AIF1DAC2_EQ_B3_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_PG_SHIFT 0 /* AIF1DAC2_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_PG_WIDTH 16 /* AIF1DAC2_EQ_B3_PG - [15:0] */ + +/* + * R1197 (0x4AD) - AIF1 DAC2 EQ Band 4 A + */ +#define WM8995_AIF1DAC2_EQ_B4_A_MASK 0xFFFF /* AIF1DAC2_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_A_SHIFT 0 /* AIF1DAC2_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_A_WIDTH 16 /* AIF1DAC2_EQ_B4_A - [15:0] */ + +/* + * R1198 (0x4AE) - AIF1 DAC2 EQ Band 4 B + */ +#define WM8995_AIF1DAC2_EQ_B4_B_MASK 0xFFFF /* AIF1DAC2_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_B_SHIFT 0 /* AIF1DAC2_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_B_WIDTH 16 /* AIF1DAC2_EQ_B4_B - [15:0] */ + +/* + * R1199 (0x4AF) - AIF1 DAC2 EQ Band 4 C + */ +#define WM8995_AIF1DAC2_EQ_B4_C_MASK 0xFFFF /* AIF1DAC2_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_C_SHIFT 0 /* AIF1DAC2_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_C_WIDTH 16 /* AIF1DAC2_EQ_B4_C - [15:0] */ + +/* + * R1200 (0x4B0) - AIF1 DAC2 EQ Band 4 PG + */ +#define WM8995_AIF1DAC2_EQ_B4_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_PG_SHIFT 0 /* AIF1DAC2_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_PG_WIDTH 16 /* AIF1DAC2_EQ_B4_PG - [15:0] */ + +/* + * R1201 (0x4B1) - AIF1 DAC2 EQ Band 5 A + */ +#define WM8995_AIF1DAC2_EQ_B5_A_MASK 0xFFFF /* AIF1DAC2_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_A_SHIFT 0 /* AIF1DAC2_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_A_WIDTH 16 /* AIF1DAC2_EQ_B5_A - [15:0] */ + +/* + * R1202 (0x4B2) - AIF1 DAC2 EQ Band 5 B + */ +#define WM8995_AIF1DAC2_EQ_B5_B_MASK 0xFFFF /* AIF1DAC2_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_B_SHIFT 0 /* AIF1DAC2_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_B_WIDTH 16 /* AIF1DAC2_EQ_B5_B - [15:0] */ + +/* + * R1203 (0x4B3) - AIF1 DAC2 EQ Band 5 PG + */ +#define WM8995_AIF1DAC2_EQ_B5_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_PG_SHIFT 0 /* AIF1DAC2_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_PG_WIDTH 16 /* AIF1DAC2_EQ_B5_PG - [15:0] */ + +/* + * R1280 (0x500) - AIF2 ADC Left Volume + */ +#define WM8995_AIF2ADC_VU 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_MASK 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_SHIFT 8 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_WIDTH 1 /* AIF2ADC_VU */ +#define WM8995_AIF2ADCL_VOL_MASK 0x00FF /* AIF2ADCL_VOL - [7:0] */ +#define WM8995_AIF2ADCL_VOL_SHIFT 0 /* AIF2ADCL_VOL - [7:0] */ +#define WM8995_AIF2ADCL_VOL_WIDTH 8 /* AIF2ADCL_VOL - [7:0] */ + +/* + * R1281 (0x501) - AIF2 ADC Right Volume + */ +#define WM8995_AIF2ADC_VU 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_MASK 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_SHIFT 8 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_WIDTH 1 /* AIF2ADC_VU */ +#define WM8995_AIF2ADCR_VOL_MASK 0x00FF /* AIF2ADCR_VOL - [7:0] */ +#define WM8995_AIF2ADCR_VOL_SHIFT 0 /* AIF2ADCR_VOL - [7:0] */ +#define WM8995_AIF2ADCR_VOL_WIDTH 8 /* AIF2ADCR_VOL - [7:0] */ + +/* + * R1282 (0x502) - AIF2 DAC Left Volume + */ +#define WM8995_AIF2DAC_VU 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_MASK 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_SHIFT 8 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_WIDTH 1 /* AIF2DAC_VU */ +#define WM8995_AIF2DACL_VOL_MASK 0x00FF /* AIF2DACL_VOL - [7:0] */ +#define WM8995_AIF2DACL_VOL_SHIFT 0 /* AIF2DACL_VOL - [7:0] */ +#define WM8995_AIF2DACL_VOL_WIDTH 8 /* AIF2DACL_VOL - [7:0] */ + +/* + * R1283 (0x503) - AIF2 DAC Right Volume + */ +#define WM8995_AIF2DAC_VU 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_MASK 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_SHIFT 8 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_WIDTH 1 /* AIF2DAC_VU */ +#define WM8995_AIF2DACR_VOL_MASK 0x00FF /* AIF2DACR_VOL - [7:0] */ +#define WM8995_AIF2DACR_VOL_SHIFT 0 /* AIF2DACR_VOL - [7:0] */ +#define WM8995_AIF2DACR_VOL_WIDTH 8 /* AIF2DACR_VOL - [7:0] */ + +/* + * R1296 (0x510) - AIF2 ADC Filters + */ +#define WM8995_AIF2ADC_4FS 0x8000 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADC_4FS_MASK 0x8000 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADC_4FS_SHIFT 15 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADC_4FS_WIDTH 1 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADCL_HPF 0x1000 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCL_HPF_MASK 0x1000 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCL_HPF_SHIFT 12 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCL_HPF_WIDTH 1 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCR_HPF 0x0800 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADCR_HPF_MASK 0x0800 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADCR_HPF_SHIFT 11 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADCR_HPF_WIDTH 1 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADC_HPF_MODE 0x0008 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_MODE_MASK 0x0008 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_MODE_SHIFT 3 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_MODE_WIDTH 1 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_CUT_MASK 0x0007 /* AIF2ADC_HPF_CUT - [2:0] */ +#define WM8995_AIF2ADC_HPF_CUT_SHIFT 0 /* AIF2ADC_HPF_CUT - [2:0] */ +#define WM8995_AIF2ADC_HPF_CUT_WIDTH 3 /* AIF2ADC_HPF_CUT - [2:0] */ + +/* + * R1312 (0x520) - AIF2 DAC Filters (1) + */ +#define WM8995_AIF2DAC_MUTE 0x0200 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MUTE_MASK 0x0200 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MUTE_SHIFT 9 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MUTE_WIDTH 1 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MONO 0x0080 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MONO_MASK 0x0080 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MONO_SHIFT 7 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MONO_WIDTH 1 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MUTERATE 0x0020 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_MUTERATE_MASK 0x0020 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_MUTERATE_SHIFT 5 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_MUTERATE_WIDTH 1 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_UNMUTE_RAMP 0x0010 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_UNMUTE_RAMP_MASK 0x0010 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_UNMUTE_RAMP_SHIFT 4 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_UNMUTE_RAMP_WIDTH 1 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_DEEMP_MASK 0x0006 /* AIF2DAC_DEEMP - [2:1] */ +#define WM8995_AIF2DAC_DEEMP_SHIFT 1 /* AIF2DAC_DEEMP - [2:1] */ +#define WM8995_AIF2DAC_DEEMP_WIDTH 2 /* AIF2DAC_DEEMP - [2:1] */ + +/* + * R1313 (0x521) - AIF2 DAC Filters (2) + */ +#define WM8995_AIF2DAC_3D_GAIN_MASK 0x3E00 /* AIF2DAC_3D_GAIN - [13:9] */ +#define WM8995_AIF2DAC_3D_GAIN_SHIFT 9 /* AIF2DAC_3D_GAIN - [13:9] */ +#define WM8995_AIF2DAC_3D_GAIN_WIDTH 5 /* AIF2DAC_3D_GAIN - [13:9] */ +#define WM8995_AIF2DAC_3D_ENA 0x0100 /* AIF2DAC_3D_ENA */ +#define WM8995_AIF2DAC_3D_ENA_MASK 0x0100 /* AIF2DAC_3D_ENA */ +#define WM8995_AIF2DAC_3D_ENA_SHIFT 8 /* AIF2DAC_3D_ENA */ +#define WM8995_AIF2DAC_3D_ENA_WIDTH 1 /* AIF2DAC_3D_ENA */ + +/* + * R1344 (0x540) - AIF2 DRC (1) + */ +#define WM8995_AIF2DRC_SIG_DET_RMS_MASK 0xF800 /* AIF2DRC_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF2DRC_SIG_DET_RMS_SHIFT 11 /* AIF2DRC_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF2DRC_SIG_DET_RMS_WIDTH 5 /* AIF2DRC_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF2DRC_SIG_DET_PK_MASK 0x0600 /* AIF2DRC_SIG_DET_PK - [10:9] */ +#define WM8995_AIF2DRC_SIG_DET_PK_SHIFT 9 /* AIF2DRC_SIG_DET_PK - [10:9] */ +#define WM8995_AIF2DRC_SIG_DET_PK_WIDTH 2 /* AIF2DRC_SIG_DET_PK - [10:9] */ +#define WM8995_AIF2DRC_NG_ENA 0x0100 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_NG_ENA_MASK 0x0100 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_NG_ENA_SHIFT 8 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_NG_ENA_WIDTH 1 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_SIG_DET_MODE 0x0080 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET_MODE_MASK 0x0080 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET_MODE_SHIFT 7 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET_MODE_WIDTH 1 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET 0x0040 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_SIG_DET_MASK 0x0040 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_SIG_DET_SHIFT 6 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_SIG_DET_WIDTH 1 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA 0x0020 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA_MASK 0x0020 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA_SHIFT 5 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA_WIDTH 1 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_QR 0x0010 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_QR_MASK 0x0010 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_QR_SHIFT 4 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_QR_WIDTH 1 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_ANTICLIP 0x0008 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DRC_ANTICLIP_MASK 0x0008 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DRC_ANTICLIP_SHIFT 3 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DRC_ANTICLIP_WIDTH 1 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DAC_DRC_ENA 0x0004 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2DAC_DRC_ENA_MASK 0x0004 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2DAC_DRC_ENA_SHIFT 2 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2DAC_DRC_ENA_WIDTH 1 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA 0x0002 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA_MASK 0x0002 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA_SHIFT 1 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA_WIDTH 1 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA 0x0001 /* AIF2ADCR_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA_MASK 0x0001 /* AIF2ADCR_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA_SHIFT 0 /* AIF2ADCR_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA_WIDTH 1 /* AIF2ADCR_DRC_ENA */ + +/* + * R1345 (0x541) - AIF2 DRC (2) + */ +#define WM8995_AIF2DRC_ATK_MASK 0x1E00 /* AIF2DRC_ATK - [12:9] */ +#define WM8995_AIF2DRC_ATK_SHIFT 9 /* AIF2DRC_ATK - [12:9] */ +#define WM8995_AIF2DRC_ATK_WIDTH 4 /* AIF2DRC_ATK - [12:9] */ +#define WM8995_AIF2DRC_DCY_MASK 0x01E0 /* AIF2DRC_DCY - [8:5] */ +#define WM8995_AIF2DRC_DCY_SHIFT 5 /* AIF2DRC_DCY - [8:5] */ +#define WM8995_AIF2DRC_DCY_WIDTH 4 /* AIF2DRC_DCY - [8:5] */ +#define WM8995_AIF2DRC_MINGAIN_MASK 0x001C /* AIF2DRC_MINGAIN - [4:2] */ +#define WM8995_AIF2DRC_MINGAIN_SHIFT 2 /* AIF2DRC_MINGAIN - [4:2] */ +#define WM8995_AIF2DRC_MINGAIN_WIDTH 3 /* AIF2DRC_MINGAIN - [4:2] */ +#define WM8995_AIF2DRC_MAXGAIN_MASK 0x0003 /* AIF2DRC_MAXGAIN - [1:0] */ +#define WM8995_AIF2DRC_MAXGAIN_SHIFT 0 /* AIF2DRC_MAXGAIN - [1:0] */ +#define WM8995_AIF2DRC_MAXGAIN_WIDTH 2 /* AIF2DRC_MAXGAIN - [1:0] */ + +/* + * R1346 (0x542) - AIF2 DRC (3) + */ +#define WM8995_AIF2DRC_NG_MINGAIN_MASK 0xF000 /* AIF2DRC_NG_MINGAIN - [15:12] */ +#define WM8995_AIF2DRC_NG_MINGAIN_SHIFT 12 /* AIF2DRC_NG_MINGAIN - [15:12] */ +#define WM8995_AIF2DRC_NG_MINGAIN_WIDTH 4 /* AIF2DRC_NG_MINGAIN - [15:12] */ +#define WM8995_AIF2DRC_NG_EXP_MASK 0x0C00 /* AIF2DRC_NG_EXP - [11:10] */ +#define WM8995_AIF2DRC_NG_EXP_SHIFT 10 /* AIF2DRC_NG_EXP - [11:10] */ +#define WM8995_AIF2DRC_NG_EXP_WIDTH 2 /* AIF2DRC_NG_EXP - [11:10] */ +#define WM8995_AIF2DRC_QR_THR_MASK 0x0300 /* AIF2DRC_QR_THR - [9:8] */ +#define WM8995_AIF2DRC_QR_THR_SHIFT 8 /* AIF2DRC_QR_THR - [9:8] */ +#define WM8995_AIF2DRC_QR_THR_WIDTH 2 /* AIF2DRC_QR_THR - [9:8] */ +#define WM8995_AIF2DRC_QR_DCY_MASK 0x00C0 /* AIF2DRC_QR_DCY - [7:6] */ +#define WM8995_AIF2DRC_QR_DCY_SHIFT 6 /* AIF2DRC_QR_DCY - [7:6] */ +#define WM8995_AIF2DRC_QR_DCY_WIDTH 2 /* AIF2DRC_QR_DCY - [7:6] */ +#define WM8995_AIF2DRC_HI_COMP_MASK 0x0038 /* AIF2DRC_HI_COMP - [5:3] */ +#define WM8995_AIF2DRC_HI_COMP_SHIFT 3 /* AIF2DRC_HI_COMP - [5:3] */ +#define WM8995_AIF2DRC_HI_COMP_WIDTH 3 /* AIF2DRC_HI_COMP - [5:3] */ +#define WM8995_AIF2DRC_LO_COMP_MASK 0x0007 /* AIF2DRC_LO_COMP - [2:0] */ +#define WM8995_AIF2DRC_LO_COMP_SHIFT 0 /* AIF2DRC_LO_COMP - [2:0] */ +#define WM8995_AIF2DRC_LO_COMP_WIDTH 3 /* AIF2DRC_LO_COMP - [2:0] */ + +/* + * R1347 (0x543) - AIF2 DRC (4) + */ +#define WM8995_AIF2DRC_KNEE_IP_MASK 0x07E0 /* AIF2DRC_KNEE_IP - [10:5] */ +#define WM8995_AIF2DRC_KNEE_IP_SHIFT 5 /* AIF2DRC_KNEE_IP - [10:5] */ +#define WM8995_AIF2DRC_KNEE_IP_WIDTH 6 /* AIF2DRC_KNEE_IP - [10:5] */ +#define WM8995_AIF2DRC_KNEE_OP_MASK 0x001F /* AIF2DRC_KNEE_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE_OP_SHIFT 0 /* AIF2DRC_KNEE_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE_OP_WIDTH 5 /* AIF2DRC_KNEE_OP - [4:0] */ + +/* + * R1348 (0x544) - AIF2 DRC (5) + */ +#define WM8995_AIF2DRC_KNEE2_IP_MASK 0x03E0 /* AIF2DRC_KNEE2_IP - [9:5] */ +#define WM8995_AIF2DRC_KNEE2_IP_SHIFT 5 /* AIF2DRC_KNEE2_IP - [9:5] */ +#define WM8995_AIF2DRC_KNEE2_IP_WIDTH 5 /* AIF2DRC_KNEE2_IP - [9:5] */ +#define WM8995_AIF2DRC_KNEE2_OP_MASK 0x001F /* AIF2DRC_KNEE2_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE2_OP_SHIFT 0 /* AIF2DRC_KNEE2_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE2_OP_WIDTH 5 /* AIF2DRC_KNEE2_OP - [4:0] */ + +/* + * R1408 (0x580) - AIF2 EQ Gains (1) + */ +#define WM8995_AIF2DAC_EQ_B1_GAIN_MASK 0xF800 /* AIF2DAC_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B1_GAIN_SHIFT 11 /* AIF2DAC_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B1_GAIN_WIDTH 5 /* AIF2DAC_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B2_GAIN_MASK 0x07C0 /* AIF2DAC_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B2_GAIN_SHIFT 6 /* AIF2DAC_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B2_GAIN_WIDTH 5 /* AIF2DAC_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B3_GAIN_MASK 0x003E /* AIF2DAC_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF2DAC_EQ_B3_GAIN_SHIFT 1 /* AIF2DAC_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF2DAC_EQ_B3_GAIN_WIDTH 5 /* AIF2DAC_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF2DAC_EQ_ENA 0x0001 /* AIF2DAC_EQ_ENA */ +#define WM8995_AIF2DAC_EQ_ENA_MASK 0x0001 /* AIF2DAC_EQ_ENA */ +#define WM8995_AIF2DAC_EQ_ENA_SHIFT 0 /* AIF2DAC_EQ_ENA */ +#define WM8995_AIF2DAC_EQ_ENA_WIDTH 1 /* AIF2DAC_EQ_ENA */ + +/* + * R1409 (0x581) - AIF2 EQ Gains (2) + */ +#define WM8995_AIF2DAC_EQ_B4_GAIN_MASK 0xF800 /* AIF2DAC_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B4_GAIN_SHIFT 11 /* AIF2DAC_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B4_GAIN_WIDTH 5 /* AIF2DAC_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B5_GAIN_MASK 0x07C0 /* AIF2DAC_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B5_GAIN_SHIFT 6 /* AIF2DAC_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B5_GAIN_WIDTH 5 /* AIF2DAC_EQ_B5_GAIN - [10:6] */ + +/* + * R1410 (0x582) - AIF2 EQ Band 1 A + */ +#define WM8995_AIF2DAC_EQ_B1_A_MASK 0xFFFF /* AIF2DAC_EQ_B1_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_A_SHIFT 0 /* AIF2DAC_EQ_B1_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_A_WIDTH 16 /* AIF2DAC_EQ_B1_A - [15:0] */ + +/* + * R1411 (0x583) - AIF2 EQ Band 1 B + */ +#define WM8995_AIF2DAC_EQ_B1_B_MASK 0xFFFF /* AIF2DAC_EQ_B1_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_B_SHIFT 0 /* AIF2DAC_EQ_B1_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_B_WIDTH 16 /* AIF2DAC_EQ_B1_B - [15:0] */ + +/* + * R1412 (0x584) - AIF2 EQ Band 1 PG + */ +#define WM8995_AIF2DAC_EQ_B1_PG_MASK 0xFFFF /* AIF2DAC_EQ_B1_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_PG_SHIFT 0 /* AIF2DAC_EQ_B1_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_PG_WIDTH 16 /* AIF2DAC_EQ_B1_PG - [15:0] */ + +/* + * R1413 (0x585) - AIF2 EQ Band 2 A + */ +#define WM8995_AIF2DAC_EQ_B2_A_MASK 0xFFFF /* AIF2DAC_EQ_B2_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_A_SHIFT 0 /* AIF2DAC_EQ_B2_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_A_WIDTH 16 /* AIF2DAC_EQ_B2_A - [15:0] */ + +/* + * R1414 (0x586) - AIF2 EQ Band 2 B + */ +#define WM8995_AIF2DAC_EQ_B2_B_MASK 0xFFFF /* AIF2DAC_EQ_B2_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_B_SHIFT 0 /* AIF2DAC_EQ_B2_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_B_WIDTH 16 /* AIF2DAC_EQ_B2_B - [15:0] */ + +/* + * R1415 (0x587) - AIF2 EQ Band 2 C + */ +#define WM8995_AIF2DAC_EQ_B2_C_MASK 0xFFFF /* AIF2DAC_EQ_B2_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_C_SHIFT 0 /* AIF2DAC_EQ_B2_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_C_WIDTH 16 /* AIF2DAC_EQ_B2_C - [15:0] */ + +/* + * R1416 (0x588) - AIF2 EQ Band 2 PG + */ +#define WM8995_AIF2DAC_EQ_B2_PG_MASK 0xFFFF /* AIF2DAC_EQ_B2_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_PG_SHIFT 0 /* AIF2DAC_EQ_B2_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_PG_WIDTH 16 /* AIF2DAC_EQ_B2_PG - [15:0] */ + +/* + * R1417 (0x589) - AIF2 EQ Band 3 A + */ +#define WM8995_AIF2DAC_EQ_B3_A_MASK 0xFFFF /* AIF2DAC_EQ_B3_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_A_SHIFT 0 /* AIF2DAC_EQ_B3_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_A_WIDTH 16 /* AIF2DAC_EQ_B3_A - [15:0] */ + +/* + * R1418 (0x58A) - AIF2 EQ Band 3 B + */ +#define WM8995_AIF2DAC_EQ_B3_B_MASK 0xFFFF /* AIF2DAC_EQ_B3_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_B_SHIFT 0 /* AIF2DAC_EQ_B3_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_B_WIDTH 16 /* AIF2DAC_EQ_B3_B - [15:0] */ + +/* + * R1419 (0x58B) - AIF2 EQ Band 3 C + */ +#define WM8995_AIF2DAC_EQ_B3_C_MASK 0xFFFF /* AIF2DAC_EQ_B3_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_C_SHIFT 0 /* AIF2DAC_EQ_B3_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_C_WIDTH 16 /* AIF2DAC_EQ_B3_C - [15:0] */ + +/* + * R1420 (0x58C) - AIF2 EQ Band 3 PG + */ +#define WM8995_AIF2DAC_EQ_B3_PG_MASK 0xFFFF /* AIF2DAC_EQ_B3_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_PG_SHIFT 0 /* AIF2DAC_EQ_B3_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_PG_WIDTH 16 /* AIF2DAC_EQ_B3_PG - [15:0] */ + +/* + * R1421 (0x58D) - AIF2 EQ Band 4 A + */ +#define WM8995_AIF2DAC_EQ_B4_A_MASK 0xFFFF /* AIF2DAC_EQ_B4_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_A_SHIFT 0 /* AIF2DAC_EQ_B4_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_A_WIDTH 16 /* AIF2DAC_EQ_B4_A - [15:0] */ + +/* + * R1422 (0x58E) - AIF2 EQ Band 4 B + */ +#define WM8995_AIF2DAC_EQ_B4_B_MASK 0xFFFF /* AIF2DAC_EQ_B4_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_B_SHIFT 0 /* AIF2DAC_EQ_B4_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_B_WIDTH 16 /* AIF2DAC_EQ_B4_B - [15:0] */ + +/* + * R1423 (0x58F) - AIF2 EQ Band 4 C + */ +#define WM8995_AIF2DAC_EQ_B4_C_MASK 0xFFFF /* AIF2DAC_EQ_B4_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_C_SHIFT 0 /* AIF2DAC_EQ_B4_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_C_WIDTH 16 /* AIF2DAC_EQ_B4_C - [15:0] */ + +/* + * R1424 (0x590) - AIF2 EQ Band 4 PG + */ +#define WM8995_AIF2DAC_EQ_B4_PG_MASK 0xFFFF /* AIF2DAC_EQ_B4_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_PG_SHIFT 0 /* AIF2DAC_EQ_B4_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_PG_WIDTH 16 /* AIF2DAC_EQ_B4_PG - [15:0] */ + +/* + * R1425 (0x591) - AIF2 EQ Band 5 A + */ +#define WM8995_AIF2DAC_EQ_B5_A_MASK 0xFFFF /* AIF2DAC_EQ_B5_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_A_SHIFT 0 /* AIF2DAC_EQ_B5_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_A_WIDTH 16 /* AIF2DAC_EQ_B5_A - [15:0] */ + +/* + * R1426 (0x592) - AIF2 EQ Band 5 B + */ +#define WM8995_AIF2DAC_EQ_B5_B_MASK 0xFFFF /* AIF2DAC_EQ_B5_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_B_SHIFT 0 /* AIF2DAC_EQ_B5_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_B_WIDTH 16 /* AIF2DAC_EQ_B5_B - [15:0] */ + +/* + * R1427 (0x593) - AIF2 EQ Band 5 PG + */ +#define WM8995_AIF2DAC_EQ_B5_PG_MASK 0xFFFF /* AIF2DAC_EQ_B5_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_PG_SHIFT 0 /* AIF2DAC_EQ_B5_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_PG_WIDTH 16 /* AIF2DAC_EQ_B5_PG - [15:0] */ + +/* + * R1536 (0x600) - DAC1 Mixer Volumes + */ +#define WM8995_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8995_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8995_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8995_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */ +#define WM8995_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */ +#define WM8995_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */ + +/* + * R1537 (0x601) - DAC1 Left Mixer Routing + */ +#define WM8995_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */ +#define WM8995_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */ +#define WM8995_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */ +#define WM8995_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L 0x0004 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L_MASK 0x0004 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L_SHIFT 2 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L_WIDTH 1 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L 0x0002 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L_MASK 0x0002 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L_SHIFT 1 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L_WIDTH 1 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L 0x0001 /* AIF1DAC1L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L_MASK 0x0001 /* AIF1DAC1L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L_SHIFT 0 /* AIF1DAC1L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L_WIDTH 1 /* AIF1DAC1L_TO_DAC1L */ + +/* + * R1538 (0x602) - DAC1 Right Mixer Routing + */ +#define WM8995_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */ +#define WM8995_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */ +#define WM8995_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */ +#define WM8995_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R 0x0004 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R_MASK 0x0004 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R_SHIFT 2 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R_WIDTH 1 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R 0x0002 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R_MASK 0x0002 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R_SHIFT 1 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R_WIDTH 1 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R 0x0001 /* AIF1DAC1R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R_MASK 0x0001 /* AIF1DAC1R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R_SHIFT 0 /* AIF1DAC1R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R_WIDTH 1 /* AIF1DAC1R_TO_DAC1R */ + +/* + * R1539 (0x603) - DAC2 Mixer Volumes + */ +#define WM8995_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8995_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8995_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8995_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */ +#define WM8995_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */ +#define WM8995_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */ + +/* + * R1540 (0x604) - DAC2 Left Mixer Routing + */ +#define WM8995_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */ +#define WM8995_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */ +#define WM8995_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */ +#define WM8995_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L 0x0004 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L_MASK 0x0004 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L_SHIFT 2 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L_WIDTH 1 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L 0x0002 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L_MASK 0x0002 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L_SHIFT 1 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L_WIDTH 1 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L 0x0001 /* AIF1DAC1L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L_MASK 0x0001 /* AIF1DAC1L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L_SHIFT 0 /* AIF1DAC1L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L_WIDTH 1 /* AIF1DAC1L_TO_DAC2L */ + +/* + * R1541 (0x605) - DAC2 Right Mixer Routing + */ +#define WM8995_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */ +#define WM8995_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */ +#define WM8995_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */ +#define WM8995_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R 0x0004 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R_MASK 0x0004 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R_SHIFT 2 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R_WIDTH 1 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R 0x0002 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R_MASK 0x0002 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R_SHIFT 1 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R_WIDTH 1 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R 0x0001 /* AIF1DAC1R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R_MASK 0x0001 /* AIF1DAC1R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R_SHIFT 0 /* AIF1DAC1R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R_WIDTH 1 /* AIF1DAC1R_TO_DAC2R */ + +/* + * R1542 (0x606) - AIF1 ADC1 Left Mixer Routing + */ +#define WM8995_ADC1L_TO_AIF1ADC1L 0x0002 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_ADC1L_TO_AIF1ADC1L_MASK 0x0002 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_ADC1L_TO_AIF1ADC1L_SHIFT 1 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_ADC1L_TO_AIF1ADC1L_WIDTH 1 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L 0x0001 /* AIF2DACL_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L_MASK 0x0001 /* AIF2DACL_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L_SHIFT 0 /* AIF2DACL_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L_WIDTH 1 /* AIF2DACL_TO_AIF1ADC1L */ + +/* + * R1543 (0x607) - AIF1 ADC1 Right Mixer Routing + */ +#define WM8995_ADC1R_TO_AIF1ADC1R 0x0002 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_ADC1R_TO_AIF1ADC1R_MASK 0x0002 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_ADC1R_TO_AIF1ADC1R_SHIFT 1 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_ADC1R_TO_AIF1ADC1R_WIDTH 1 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R 0x0001 /* AIF2DACR_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R_MASK 0x0001 /* AIF2DACR_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R_SHIFT 0 /* AIF2DACR_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R_WIDTH 1 /* AIF2DACR_TO_AIF1ADC1R */ + +/* + * R1544 (0x608) - AIF1 ADC2 Left Mixer Routing + */ +#define WM8995_ADC2L_TO_AIF1ADC2L 0x0002 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_ADC2L_TO_AIF1ADC2L_MASK 0x0002 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_ADC2L_TO_AIF1ADC2L_SHIFT 1 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_ADC2L_TO_AIF1ADC2L_WIDTH 1 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L 0x0001 /* AIF2DACL_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L_MASK 0x0001 /* AIF2DACL_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L_SHIFT 0 /* AIF2DACL_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L_WIDTH 1 /* AIF2DACL_TO_AIF1ADC2L */ + +/* + * R1545 (0x609) - AIF1 ADC2 Right mixer Routing + */ +#define WM8995_ADC2R_TO_AIF1ADC2R 0x0002 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_ADC2R_TO_AIF1ADC2R_MASK 0x0002 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_ADC2R_TO_AIF1ADC2R_SHIFT 1 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_ADC2R_TO_AIF1ADC2R_WIDTH 1 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R 0x0001 /* AIF2DACR_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R_MASK 0x0001 /* AIF2DACR_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R_SHIFT 0 /* AIF2DACR_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R_WIDTH 1 /* AIF2DACR_TO_AIF1ADC2R */ + +/* + * R1552 (0x610) - DAC Softmute + */ +#define WM8995_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */ +#define WM8995_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */ +#define WM8995_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */ +#define WM8995_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ + +/* + * R1568 (0x620) - Oversampling + */ +#define WM8995_ADC_OSR128 0x0002 /* ADC_OSR128 */ +#define WM8995_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */ +#define WM8995_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */ +#define WM8995_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ +#define WM8995_DAC_OSR128 0x0001 /* DAC_OSR128 */ +#define WM8995_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */ +#define WM8995_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */ +#define WM8995_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ + +/* + * R1569 (0x621) - Sidetone + */ +#define WM8995_ST_LPF 0x1000 /* ST_LPF */ +#define WM8995_ST_LPF_MASK 0x1000 /* ST_LPF */ +#define WM8995_ST_LPF_SHIFT 12 /* ST_LPF */ +#define WM8995_ST_LPF_WIDTH 1 /* ST_LPF */ +#define WM8995_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */ +#define WM8995_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */ +#define WM8995_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */ +#define WM8995_ST_HPF 0x0040 /* ST_HPF */ +#define WM8995_ST_HPF_MASK 0x0040 /* ST_HPF */ +#define WM8995_ST_HPF_SHIFT 6 /* ST_HPF */ +#define WM8995_ST_HPF_WIDTH 1 /* ST_HPF */ +#define WM8995_STR_SEL 0x0002 /* STR_SEL */ +#define WM8995_STR_SEL_MASK 0x0002 /* STR_SEL */ +#define WM8995_STR_SEL_SHIFT 1 /* STR_SEL */ +#define WM8995_STR_SEL_WIDTH 1 /* STR_SEL */ +#define WM8995_STL_SEL 0x0001 /* STL_SEL */ +#define WM8995_STL_SEL_MASK 0x0001 /* STL_SEL */ +#define WM8995_STL_SEL_SHIFT 0 /* STL_SEL */ +#define WM8995_STL_SEL_WIDTH 1 /* STL_SEL */ + +/* + * R1792 (0x700) - GPIO 1 + */ +#define WM8995_GP1_DIR 0x8000 /* GP1_DIR */ +#define WM8995_GP1_DIR_MASK 0x8000 /* GP1_DIR */ +#define WM8995_GP1_DIR_SHIFT 15 /* GP1_DIR */ +#define WM8995_GP1_DIR_WIDTH 1 /* GP1_DIR */ +#define WM8995_GP1_PU 0x4000 /* GP1_PU */ +#define WM8995_GP1_PU_MASK 0x4000 /* GP1_PU */ +#define WM8995_GP1_PU_SHIFT 14 /* GP1_PU */ +#define WM8995_GP1_PU_WIDTH 1 /* GP1_PU */ +#define WM8995_GP1_PD 0x2000 /* GP1_PD */ +#define WM8995_GP1_PD_MASK 0x2000 /* GP1_PD */ +#define WM8995_GP1_PD_SHIFT 13 /* GP1_PD */ +#define WM8995_GP1_PD_WIDTH 1 /* GP1_PD */ +#define WM8995_GP1_POL 0x0400 /* GP1_POL */ +#define WM8995_GP1_POL_MASK 0x0400 /* GP1_POL */ +#define WM8995_GP1_POL_SHIFT 10 /* GP1_POL */ +#define WM8995_GP1_POL_WIDTH 1 /* GP1_POL */ +#define WM8995_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */ +#define WM8995_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */ +#define WM8995_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */ +#define WM8995_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */ +#define WM8995_GP1_DB 0x0100 /* GP1_DB */ +#define WM8995_GP1_DB_MASK 0x0100 /* GP1_DB */ +#define WM8995_GP1_DB_SHIFT 8 /* GP1_DB */ +#define WM8995_GP1_DB_WIDTH 1 /* GP1_DB */ +#define WM8995_GP1_LVL 0x0040 /* GP1_LVL */ +#define WM8995_GP1_LVL_MASK 0x0040 /* GP1_LVL */ +#define WM8995_GP1_LVL_SHIFT 6 /* GP1_LVL */ +#define WM8995_GP1_LVL_WIDTH 1 /* GP1_LVL */ +#define WM8995_GP1_FN_MASK 0x001F /* GP1_FN - [4:0] */ +#define WM8995_GP1_FN_SHIFT 0 /* GP1_FN - [4:0] */ +#define WM8995_GP1_FN_WIDTH 5 /* GP1_FN - [4:0] */ + +/* + * R1793 (0x701) - GPIO 2 + */ +#define WM8995_GP2_DIR 0x8000 /* GP2_DIR */ +#define WM8995_GP2_DIR_MASK 0x8000 /* GP2_DIR */ +#define WM8995_GP2_DIR_SHIFT 15 /* GP2_DIR */ +#define WM8995_GP2_DIR_WIDTH 1 /* GP2_DIR */ +#define WM8995_GP2_PU 0x4000 /* GP2_PU */ +#define WM8995_GP2_PU_MASK 0x4000 /* GP2_PU */ +#define WM8995_GP2_PU_SHIFT 14 /* GP2_PU */ +#define WM8995_GP2_PU_WIDTH 1 /* GP2_PU */ +#define WM8995_GP2_PD 0x2000 /* GP2_PD */ +#define WM8995_GP2_PD_MASK 0x2000 /* GP2_PD */ +#define WM8995_GP2_PD_SHIFT 13 /* GP2_PD */ +#define WM8995_GP2_PD_WIDTH 1 /* GP2_PD */ +#define WM8995_GP2_POL 0x0400 /* GP2_POL */ +#define WM8995_GP2_POL_MASK 0x0400 /* GP2_POL */ +#define WM8995_GP2_POL_SHIFT 10 /* GP2_POL */ +#define WM8995_GP2_POL_WIDTH 1 /* GP2_POL */ +#define WM8995_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */ +#define WM8995_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */ +#define WM8995_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */ +#define WM8995_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */ +#define WM8995_GP2_DB 0x0100 /* GP2_DB */ +#define WM8995_GP2_DB_MASK 0x0100 /* GP2_DB */ +#define WM8995_GP2_DB_SHIFT 8 /* GP2_DB */ +#define WM8995_GP2_DB_WIDTH 1 /* GP2_DB */ +#define WM8995_GP2_LVL 0x0040 /* GP2_LVL */ +#define WM8995_GP2_LVL_MASK 0x0040 /* GP2_LVL */ +#define WM8995_GP2_LVL_SHIFT 6 /* GP2_LVL */ +#define WM8995_GP2_LVL_WIDTH 1 /* GP2_LVL */ +#define WM8995_GP2_FN_MASK 0x001F /* GP2_FN - [4:0] */ +#define WM8995_GP2_FN_SHIFT 0 /* GP2_FN - [4:0] */ +#define WM8995_GP2_FN_WIDTH 5 /* GP2_FN - [4:0] */ + +/* + * R1794 (0x702) - GPIO 3 + */ +#define WM8995_GP3_DIR 0x8000 /* GP3_DIR */ +#define WM8995_GP3_DIR_MASK 0x8000 /* GP3_DIR */ +#define WM8995_GP3_DIR_SHIFT 15 /* GP3_DIR */ +#define WM8995_GP3_DIR_WIDTH 1 /* GP3_DIR */ +#define WM8995_GP3_PU 0x4000 /* GP3_PU */ +#define WM8995_GP3_PU_MASK 0x4000 /* GP3_PU */ +#define WM8995_GP3_PU_SHIFT 14 /* GP3_PU */ +#define WM8995_GP3_PU_WIDTH 1 /* GP3_PU */ +#define WM8995_GP3_PD 0x2000 /* GP3_PD */ +#define WM8995_GP3_PD_MASK 0x2000 /* GP3_PD */ +#define WM8995_GP3_PD_SHIFT 13 /* GP3_PD */ +#define WM8995_GP3_PD_WIDTH 1 /* GP3_PD */ +#define WM8995_GP3_POL 0x0400 /* GP3_POL */ +#define WM8995_GP3_POL_MASK 0x0400 /* GP3_POL */ +#define WM8995_GP3_POL_SHIFT 10 /* GP3_POL */ +#define WM8995_GP3_POL_WIDTH 1 /* GP3_POL */ +#define WM8995_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */ +#define WM8995_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */ +#define WM8995_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */ +#define WM8995_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */ +#define WM8995_GP3_DB 0x0100 /* GP3_DB */ +#define WM8995_GP3_DB_MASK 0x0100 /* GP3_DB */ +#define WM8995_GP3_DB_SHIFT 8 /* GP3_DB */ +#define WM8995_GP3_DB_WIDTH 1 /* GP3_DB */ +#define WM8995_GP3_LVL 0x0040 /* GP3_LVL */ +#define WM8995_GP3_LVL_MASK 0x0040 /* GP3_LVL */ +#define WM8995_GP3_LVL_SHIFT 6 /* GP3_LVL */ +#define WM8995_GP3_LVL_WIDTH 1 /* GP3_LVL */ +#define WM8995_GP3_FN_MASK 0x001F /* GP3_FN - [4:0] */ +#define WM8995_GP3_FN_SHIFT 0 /* GP3_FN - [4:0] */ +#define WM8995_GP3_FN_WIDTH 5 /* GP3_FN - [4:0] */ + +/* + * R1795 (0x703) - GPIO 4 + */ +#define WM8995_GP4_DIR 0x8000 /* GP4_DIR */ +#define WM8995_GP4_DIR_MASK 0x8000 /* GP4_DIR */ +#define WM8995_GP4_DIR_SHIFT 15 /* GP4_DIR */ +#define WM8995_GP4_DIR_WIDTH 1 /* GP4_DIR */ +#define WM8995_GP4_PU 0x4000 /* GP4_PU */ +#define WM8995_GP4_PU_MASK 0x4000 /* GP4_PU */ +#define WM8995_GP4_PU_SHIFT 14 /* GP4_PU */ +#define WM8995_GP4_PU_WIDTH 1 /* GP4_PU */ +#define WM8995_GP4_PD 0x2000 /* GP4_PD */ +#define WM8995_GP4_PD_MASK 0x2000 /* GP4_PD */ +#define WM8995_GP4_PD_SHIFT 13 /* GP4_PD */ +#define WM8995_GP4_PD_WIDTH 1 /* GP4_PD */ +#define WM8995_GP4_POL 0x0400 /* GP4_POL */ +#define WM8995_GP4_POL_MASK 0x0400 /* GP4_POL */ +#define WM8995_GP4_POL_SHIFT 10 /* GP4_POL */ +#define WM8995_GP4_POL_WIDTH 1 /* GP4_POL */ +#define WM8995_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */ +#define WM8995_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */ +#define WM8995_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */ +#define WM8995_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */ +#define WM8995_GP4_DB 0x0100 /* GP4_DB */ +#define WM8995_GP4_DB_MASK 0x0100 /* GP4_DB */ +#define WM8995_GP4_DB_SHIFT 8 /* GP4_DB */ +#define WM8995_GP4_DB_WIDTH 1 /* GP4_DB */ +#define WM8995_GP4_LVL 0x0040 /* GP4_LVL */ +#define WM8995_GP4_LVL_MASK 0x0040 /* GP4_LVL */ +#define WM8995_GP4_LVL_SHIFT 6 /* GP4_LVL */ +#define WM8995_GP4_LVL_WIDTH 1 /* GP4_LVL */ +#define WM8995_GP4_FN_MASK 0x001F /* GP4_FN - [4:0] */ +#define WM8995_GP4_FN_SHIFT 0 /* GP4_FN - [4:0] */ +#define WM8995_GP4_FN_WIDTH 5 /* GP4_FN - [4:0] */ + +/* + * R1796 (0x704) - GPIO 5 + */ +#define WM8995_GP5_DIR 0x8000 /* GP5_DIR */ +#define WM8995_GP5_DIR_MASK 0x8000 /* GP5_DIR */ +#define WM8995_GP5_DIR_SHIFT 15 /* GP5_DIR */ +#define WM8995_GP5_DIR_WIDTH 1 /* GP5_DIR */ +#define WM8995_GP5_PU 0x4000 /* GP5_PU */ +#define WM8995_GP5_PU_MASK 0x4000 /* GP5_PU */ +#define WM8995_GP5_PU_SHIFT 14 /* GP5_PU */ +#define WM8995_GP5_PU_WIDTH 1 /* GP5_PU */ +#define WM8995_GP5_PD 0x2000 /* GP5_PD */ +#define WM8995_GP5_PD_MASK 0x2000 /* GP5_PD */ +#define WM8995_GP5_PD_SHIFT 13 /* GP5_PD */ +#define WM8995_GP5_PD_WIDTH 1 /* GP5_PD */ +#define WM8995_GP5_POL 0x0400 /* GP5_POL */ +#define WM8995_GP5_POL_MASK 0x0400 /* GP5_POL */ +#define WM8995_GP5_POL_SHIFT 10 /* GP5_POL */ +#define WM8995_GP5_POL_WIDTH 1 /* GP5_POL */ +#define WM8995_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */ +#define WM8995_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */ +#define WM8995_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */ +#define WM8995_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */ +#define WM8995_GP5_DB 0x0100 /* GP5_DB */ +#define WM8995_GP5_DB_MASK 0x0100 /* GP5_DB */ +#define WM8995_GP5_DB_SHIFT 8 /* GP5_DB */ +#define WM8995_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM8995_GP5_LVL 0x0040 /* GP5_LVL */ +#define WM8995_GP5_LVL_MASK 0x0040 /* GP5_LVL */ +#define WM8995_GP5_LVL_SHIFT 6 /* GP5_LVL */ +#define WM8995_GP5_LVL_WIDTH 1 /* GP5_LVL */ +#define WM8995_GP5_FN_MASK 0x001F /* GP5_FN - [4:0] */ +#define WM8995_GP5_FN_SHIFT 0 /* GP5_FN - [4:0] */ +#define WM8995_GP5_FN_WIDTH 5 /* GP5_FN - [4:0] */ + +/* + * R1797 (0x705) - GPIO 6 + */ +#define WM8995_GP6_DIR 0x8000 /* GP6_DIR */ +#define WM8995_GP6_DIR_MASK 0x8000 /* GP6_DIR */ +#define WM8995_GP6_DIR_SHIFT 15 /* GP6_DIR */ +#define WM8995_GP6_DIR_WIDTH 1 /* GP6_DIR */ +#define WM8995_GP6_PU 0x4000 /* GP6_PU */ +#define WM8995_GP6_PU_MASK 0x4000 /* GP6_PU */ +#define WM8995_GP6_PU_SHIFT 14 /* GP6_PU */ +#define WM8995_GP6_PU_WIDTH 1 /* GP6_PU */ +#define WM8995_GP6_PD 0x2000 /* GP6_PD */ +#define WM8995_GP6_PD_MASK 0x2000 /* GP6_PD */ +#define WM8995_GP6_PD_SHIFT 13 /* GP6_PD */ +#define WM8995_GP6_PD_WIDTH 1 /* GP6_PD */ +#define WM8995_GP6_POL 0x0400 /* GP6_POL */ +#define WM8995_GP6_POL_MASK 0x0400 /* GP6_POL */ +#define WM8995_GP6_POL_SHIFT 10 /* GP6_POL */ +#define WM8995_GP6_POL_WIDTH 1 /* GP6_POL */ +#define WM8995_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */ +#define WM8995_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */ +#define WM8995_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */ +#define WM8995_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */ +#define WM8995_GP6_DB 0x0100 /* GP6_DB */ +#define WM8995_GP6_DB_MASK 0x0100 /* GP6_DB */ +#define WM8995_GP6_DB_SHIFT 8 /* GP6_DB */ +#define WM8995_GP6_DB_WIDTH 1 /* GP6_DB */ +#define WM8995_GP6_LVL 0x0040 /* GP6_LVL */ +#define WM8995_GP6_LVL_MASK 0x0040 /* GP6_LVL */ +#define WM8995_GP6_LVL_SHIFT 6 /* GP6_LVL */ +#define WM8995_GP6_LVL_WIDTH 1 /* GP6_LVL */ +#define WM8995_GP6_FN_MASK 0x001F /* GP6_FN - [4:0] */ +#define WM8995_GP6_FN_SHIFT 0 /* GP6_FN - [4:0] */ +#define WM8995_GP6_FN_WIDTH 5 /* GP6_FN - [4:0] */ + +/* + * R1798 (0x706) - GPIO 7 + */ +#define WM8995_GP7_DIR 0x8000 /* GP7_DIR */ +#define WM8995_GP7_DIR_MASK 0x8000 /* GP7_DIR */ +#define WM8995_GP7_DIR_SHIFT 15 /* GP7_DIR */ +#define WM8995_GP7_DIR_WIDTH 1 /* GP7_DIR */ +#define WM8995_GP7_PU 0x4000 /* GP7_PU */ +#define WM8995_GP7_PU_MASK 0x4000 /* GP7_PU */ +#define WM8995_GP7_PU_SHIFT 14 /* GP7_PU */ +#define WM8995_GP7_PU_WIDTH 1 /* GP7_PU */ +#define WM8995_GP7_PD 0x2000 /* GP7_PD */ +#define WM8995_GP7_PD_MASK 0x2000 /* GP7_PD */ +#define WM8995_GP7_PD_SHIFT 13 /* GP7_PD */ +#define WM8995_GP7_PD_WIDTH 1 /* GP7_PD */ +#define WM8995_GP7_POL 0x0400 /* GP7_POL */ +#define WM8995_GP7_POL_MASK 0x0400 /* GP7_POL */ +#define WM8995_GP7_POL_SHIFT 10 /* GP7_POL */ +#define WM8995_GP7_POL_WIDTH 1 /* GP7_POL */ +#define WM8995_GP7_OP_CFG 0x0200 /* GP7_OP_CFG */ +#define WM8995_GP7_OP_CFG_MASK 0x0200 /* GP7_OP_CFG */ +#define WM8995_GP7_OP_CFG_SHIFT 9 /* GP7_OP_CFG */ +#define WM8995_GP7_OP_CFG_WIDTH 1 /* GP7_OP_CFG */ +#define WM8995_GP7_DB 0x0100 /* GP7_DB */ +#define WM8995_GP7_DB_MASK 0x0100 /* GP7_DB */ +#define WM8995_GP7_DB_SHIFT 8 /* GP7_DB */ +#define WM8995_GP7_DB_WIDTH 1 /* GP7_DB */ +#define WM8995_GP7_LVL 0x0040 /* GP7_LVL */ +#define WM8995_GP7_LVL_MASK 0x0040 /* GP7_LVL */ +#define WM8995_GP7_LVL_SHIFT 6 /* GP7_LVL */ +#define WM8995_GP7_LVL_WIDTH 1 /* GP7_LVL */ +#define WM8995_GP7_FN_MASK 0x001F /* GP7_FN - [4:0] */ +#define WM8995_GP7_FN_SHIFT 0 /* GP7_FN - [4:0] */ +#define WM8995_GP7_FN_WIDTH 5 /* GP7_FN - [4:0] */ + +/* + * R1799 (0x707) - GPIO 8 + */ +#define WM8995_GP8_DIR 0x8000 /* GP8_DIR */ +#define WM8995_GP8_DIR_MASK 0x8000 /* GP8_DIR */ +#define WM8995_GP8_DIR_SHIFT 15 /* GP8_DIR */ +#define WM8995_GP8_DIR_WIDTH 1 /* GP8_DIR */ +#define WM8995_GP8_PU 0x4000 /* GP8_PU */ +#define WM8995_GP8_PU_MASK 0x4000 /* GP8_PU */ +#define WM8995_GP8_PU_SHIFT 14 /* GP8_PU */ +#define WM8995_GP8_PU_WIDTH 1 /* GP8_PU */ +#define WM8995_GP8_PD 0x2000 /* GP8_PD */ +#define WM8995_GP8_PD_MASK 0x2000 /* GP8_PD */ +#define WM8995_GP8_PD_SHIFT 13 /* GP8_PD */ +#define WM8995_GP8_PD_WIDTH 1 /* GP8_PD */ +#define WM8995_GP8_POL 0x0400 /* GP8_POL */ +#define WM8995_GP8_POL_MASK 0x0400 /* GP8_POL */ +#define WM8995_GP8_POL_SHIFT 10 /* GP8_POL */ +#define WM8995_GP8_POL_WIDTH 1 /* GP8_POL */ +#define WM8995_GP8_OP_CFG 0x0200 /* GP8_OP_CFG */ +#define WM8995_GP8_OP_CFG_MASK 0x0200 /* GP8_OP_CFG */ +#define WM8995_GP8_OP_CFG_SHIFT 9 /* GP8_OP_CFG */ +#define WM8995_GP8_OP_CFG_WIDTH 1 /* GP8_OP_CFG */ +#define WM8995_GP8_DB 0x0100 /* GP8_DB */ +#define WM8995_GP8_DB_MASK 0x0100 /* GP8_DB */ +#define WM8995_GP8_DB_SHIFT 8 /* GP8_DB */ +#define WM8995_GP8_DB_WIDTH 1 /* GP8_DB */ +#define WM8995_GP8_LVL 0x0040 /* GP8_LVL */ +#define WM8995_GP8_LVL_MASK 0x0040 /* GP8_LVL */ +#define WM8995_GP8_LVL_SHIFT 6 /* GP8_LVL */ +#define WM8995_GP8_LVL_WIDTH 1 /* GP8_LVL */ +#define WM8995_GP8_FN_MASK 0x001F /* GP8_FN - [4:0] */ +#define WM8995_GP8_FN_SHIFT 0 /* GP8_FN - [4:0] */ +#define WM8995_GP8_FN_WIDTH 5 /* GP8_FN - [4:0] */ + +/* + * R1800 (0x708) - GPIO 9 + */ +#define WM8995_GP9_DIR 0x8000 /* GP9_DIR */ +#define WM8995_GP9_DIR_MASK 0x8000 /* GP9_DIR */ +#define WM8995_GP9_DIR_SHIFT 15 /* GP9_DIR */ +#define WM8995_GP9_DIR_WIDTH 1 /* GP9_DIR */ +#define WM8995_GP9_PU 0x4000 /* GP9_PU */ +#define WM8995_GP9_PU_MASK 0x4000 /* GP9_PU */ +#define WM8995_GP9_PU_SHIFT 14 /* GP9_PU */ +#define WM8995_GP9_PU_WIDTH 1 /* GP9_PU */ +#define WM8995_GP9_PD 0x2000 /* GP9_PD */ +#define WM8995_GP9_PD_MASK 0x2000 /* GP9_PD */ +#define WM8995_GP9_PD_SHIFT 13 /* GP9_PD */ +#define WM8995_GP9_PD_WIDTH 1 /* GP9_PD */ +#define WM8995_GP9_POL 0x0400 /* GP9_POL */ +#define WM8995_GP9_POL_MASK 0x0400 /* GP9_POL */ +#define WM8995_GP9_POL_SHIFT 10 /* GP9_POL */ +#define WM8995_GP9_POL_WIDTH 1 /* GP9_POL */ +#define WM8995_GP9_OP_CFG 0x0200 /* GP9_OP_CFG */ +#define WM8995_GP9_OP_CFG_MASK 0x0200 /* GP9_OP_CFG */ +#define WM8995_GP9_OP_CFG_SHIFT 9 /* GP9_OP_CFG */ +#define WM8995_GP9_OP_CFG_WIDTH 1 /* GP9_OP_CFG */ +#define WM8995_GP9_DB 0x0100 /* GP9_DB */ +#define WM8995_GP9_DB_MASK 0x0100 /* GP9_DB */ +#define WM8995_GP9_DB_SHIFT 8 /* GP9_DB */ +#define WM8995_GP9_DB_WIDTH 1 /* GP9_DB */ +#define WM8995_GP9_LVL 0x0040 /* GP9_LVL */ +#define WM8995_GP9_LVL_MASK 0x0040 /* GP9_LVL */ +#define WM8995_GP9_LVL_SHIFT 6 /* GP9_LVL */ +#define WM8995_GP9_LVL_WIDTH 1 /* GP9_LVL */ +#define WM8995_GP9_FN_MASK 0x001F /* GP9_FN - [4:0] */ +#define WM8995_GP9_FN_SHIFT 0 /* GP9_FN - [4:0] */ +#define WM8995_GP9_FN_WIDTH 5 /* GP9_FN - [4:0] */ + +/* + * R1801 (0x709) - GPIO 10 + */ +#define WM8995_GP10_DIR 0x8000 /* GP10_DIR */ +#define WM8995_GP10_DIR_MASK 0x8000 /* GP10_DIR */ +#define WM8995_GP10_DIR_SHIFT 15 /* GP10_DIR */ +#define WM8995_GP10_DIR_WIDTH 1 /* GP10_DIR */ +#define WM8995_GP10_PU 0x4000 /* GP10_PU */ +#define WM8995_GP10_PU_MASK 0x4000 /* GP10_PU */ +#define WM8995_GP10_PU_SHIFT 14 /* GP10_PU */ +#define WM8995_GP10_PU_WIDTH 1 /* GP10_PU */ +#define WM8995_GP10_PD 0x2000 /* GP10_PD */ +#define WM8995_GP10_PD_MASK 0x2000 /* GP10_PD */ +#define WM8995_GP10_PD_SHIFT 13 /* GP10_PD */ +#define WM8995_GP10_PD_WIDTH 1 /* GP10_PD */ +#define WM8995_GP10_POL 0x0400 /* GP10_POL */ +#define WM8995_GP10_POL_MASK 0x0400 /* GP10_POL */ +#define WM8995_GP10_POL_SHIFT 10 /* GP10_POL */ +#define WM8995_GP10_POL_WIDTH 1 /* GP10_POL */ +#define WM8995_GP10_OP_CFG 0x0200 /* GP10_OP_CFG */ +#define WM8995_GP10_OP_CFG_MASK 0x0200 /* GP10_OP_CFG */ +#define WM8995_GP10_OP_CFG_SHIFT 9 /* GP10_OP_CFG */ +#define WM8995_GP10_OP_CFG_WIDTH 1 /* GP10_OP_CFG */ +#define WM8995_GP10_DB 0x0100 /* GP10_DB */ +#define WM8995_GP10_DB_MASK 0x0100 /* GP10_DB */ +#define WM8995_GP10_DB_SHIFT 8 /* GP10_DB */ +#define WM8995_GP10_DB_WIDTH 1 /* GP10_DB */ +#define WM8995_GP10_LVL 0x0040 /* GP10_LVL */ +#define WM8995_GP10_LVL_MASK 0x0040 /* GP10_LVL */ +#define WM8995_GP10_LVL_SHIFT 6 /* GP10_LVL */ +#define WM8995_GP10_LVL_WIDTH 1 /* GP10_LVL */ +#define WM8995_GP10_FN_MASK 0x001F /* GP10_FN - [4:0] */ +#define WM8995_GP10_FN_SHIFT 0 /* GP10_FN - [4:0] */ +#define WM8995_GP10_FN_WIDTH 5 /* GP10_FN - [4:0] */ + +/* + * R1802 (0x70A) - GPIO 11 + */ +#define WM8995_GP11_DIR 0x8000 /* GP11_DIR */ +#define WM8995_GP11_DIR_MASK 0x8000 /* GP11_DIR */ +#define WM8995_GP11_DIR_SHIFT 15 /* GP11_DIR */ +#define WM8995_GP11_DIR_WIDTH 1 /* GP11_DIR */ +#define WM8995_GP11_PU 0x4000 /* GP11_PU */ +#define WM8995_GP11_PU_MASK 0x4000 /* GP11_PU */ +#define WM8995_GP11_PU_SHIFT 14 /* GP11_PU */ +#define WM8995_GP11_PU_WIDTH 1 /* GP11_PU */ +#define WM8995_GP11_PD 0x2000 /* GP11_PD */ +#define WM8995_GP11_PD_MASK 0x2000 /* GP11_PD */ +#define WM8995_GP11_PD_SHIFT 13 /* GP11_PD */ +#define WM8995_GP11_PD_WIDTH 1 /* GP11_PD */ +#define WM8995_GP11_POL 0x0400 /* GP11_POL */ +#define WM8995_GP11_POL_MASK 0x0400 /* GP11_POL */ +#define WM8995_GP11_POL_SHIFT 10 /* GP11_POL */ +#define WM8995_GP11_POL_WIDTH 1 /* GP11_POL */ +#define WM8995_GP11_OP_CFG 0x0200 /* GP11_OP_CFG */ +#define WM8995_GP11_OP_CFG_MASK 0x0200 /* GP11_OP_CFG */ +#define WM8995_GP11_OP_CFG_SHIFT 9 /* GP11_OP_CFG */ +#define WM8995_GP11_OP_CFG_WIDTH 1 /* GP11_OP_CFG */ +#define WM8995_GP11_DB 0x0100 /* GP11_DB */ +#define WM8995_GP11_DB_MASK 0x0100 /* GP11_DB */ +#define WM8995_GP11_DB_SHIFT 8 /* GP11_DB */ +#define WM8995_GP11_DB_WIDTH 1 /* GP11_DB */ +#define WM8995_GP11_LVL 0x0040 /* GP11_LVL */ +#define WM8995_GP11_LVL_MASK 0x0040 /* GP11_LVL */ +#define WM8995_GP11_LVL_SHIFT 6 /* GP11_LVL */ +#define WM8995_GP11_LVL_WIDTH 1 /* GP11_LVL */ +#define WM8995_GP11_FN_MASK 0x001F /* GP11_FN - [4:0] */ +#define WM8995_GP11_FN_SHIFT 0 /* GP11_FN - [4:0] */ +#define WM8995_GP11_FN_WIDTH 5 /* GP11_FN - [4:0] */ + +/* + * R1803 (0x70B) - GPIO 12 + */ +#define WM8995_GP12_DIR 0x8000 /* GP12_DIR */ +#define WM8995_GP12_DIR_MASK 0x8000 /* GP12_DIR */ +#define WM8995_GP12_DIR_SHIFT 15 /* GP12_DIR */ +#define WM8995_GP12_DIR_WIDTH 1 /* GP12_DIR */ +#define WM8995_GP12_PU 0x4000 /* GP12_PU */ +#define WM8995_GP12_PU_MASK 0x4000 /* GP12_PU */ +#define WM8995_GP12_PU_SHIFT 14 /* GP12_PU */ +#define WM8995_GP12_PU_WIDTH 1 /* GP12_PU */ +#define WM8995_GP12_PD 0x2000 /* GP12_PD */ +#define WM8995_GP12_PD_MASK 0x2000 /* GP12_PD */ +#define WM8995_GP12_PD_SHIFT 13 /* GP12_PD */ +#define WM8995_GP12_PD_WIDTH 1 /* GP12_PD */ +#define WM8995_GP12_POL 0x0400 /* GP12_POL */ +#define WM8995_GP12_POL_MASK 0x0400 /* GP12_POL */ +#define WM8995_GP12_POL_SHIFT 10 /* GP12_POL */ +#define WM8995_GP12_POL_WIDTH 1 /* GP12_POL */ +#define WM8995_GP12_OP_CFG 0x0200 /* GP12_OP_CFG */ +#define WM8995_GP12_OP_CFG_MASK 0x0200 /* GP12_OP_CFG */ +#define WM8995_GP12_OP_CFG_SHIFT 9 /* GP12_OP_CFG */ +#define WM8995_GP12_OP_CFG_WIDTH 1 /* GP12_OP_CFG */ +#define WM8995_GP12_DB 0x0100 /* GP12_DB */ +#define WM8995_GP12_DB_MASK 0x0100 /* GP12_DB */ +#define WM8995_GP12_DB_SHIFT 8 /* GP12_DB */ +#define WM8995_GP12_DB_WIDTH 1 /* GP12_DB */ +#define WM8995_GP12_LVL 0x0040 /* GP12_LVL */ +#define WM8995_GP12_LVL_MASK 0x0040 /* GP12_LVL */ +#define WM8995_GP12_LVL_SHIFT 6 /* GP12_LVL */ +#define WM8995_GP12_LVL_WIDTH 1 /* GP12_LVL */ +#define WM8995_GP12_FN_MASK 0x001F /* GP12_FN - [4:0] */ +#define WM8995_GP12_FN_SHIFT 0 /* GP12_FN - [4:0] */ +#define WM8995_GP12_FN_WIDTH 5 /* GP12_FN - [4:0] */ + +/* + * R1804 (0x70C) - GPIO 13 + */ +#define WM8995_GP13_DIR 0x8000 /* GP13_DIR */ +#define WM8995_GP13_DIR_MASK 0x8000 /* GP13_DIR */ +#define WM8995_GP13_DIR_SHIFT 15 /* GP13_DIR */ +#define WM8995_GP13_DIR_WIDTH 1 /* GP13_DIR */ +#define WM8995_GP13_PU 0x4000 /* GP13_PU */ +#define WM8995_GP13_PU_MASK 0x4000 /* GP13_PU */ +#define WM8995_GP13_PU_SHIFT 14 /* GP13_PU */ +#define WM8995_GP13_PU_WIDTH 1 /* GP13_PU */ +#define WM8995_GP13_PD 0x2000 /* GP13_PD */ +#define WM8995_GP13_PD_MASK 0x2000 /* GP13_PD */ +#define WM8995_GP13_PD_SHIFT 13 /* GP13_PD */ +#define WM8995_GP13_PD_WIDTH 1 /* GP13_PD */ +#define WM8995_GP13_POL 0x0400 /* GP13_POL */ +#define WM8995_GP13_POL_MASK 0x0400 /* GP13_POL */ +#define WM8995_GP13_POL_SHIFT 10 /* GP13_POL */ +#define WM8995_GP13_POL_WIDTH 1 /* GP13_POL */ +#define WM8995_GP13_OP_CFG 0x0200 /* GP13_OP_CFG */ +#define WM8995_GP13_OP_CFG_MASK 0x0200 /* GP13_OP_CFG */ +#define WM8995_GP13_OP_CFG_SHIFT 9 /* GP13_OP_CFG */ +#define WM8995_GP13_OP_CFG_WIDTH 1 /* GP13_OP_CFG */ +#define WM8995_GP13_DB 0x0100 /* GP13_DB */ +#define WM8995_GP13_DB_MASK 0x0100 /* GP13_DB */ +#define WM8995_GP13_DB_SHIFT 8 /* GP13_DB */ +#define WM8995_GP13_DB_WIDTH 1 /* GP13_DB */ +#define WM8995_GP13_LVL 0x0040 /* GP13_LVL */ +#define WM8995_GP13_LVL_MASK 0x0040 /* GP13_LVL */ +#define WM8995_GP13_LVL_SHIFT 6 /* GP13_LVL */ +#define WM8995_GP13_LVL_WIDTH 1 /* GP13_LVL */ +#define WM8995_GP13_FN_MASK 0x001F /* GP13_FN - [4:0] */ +#define WM8995_GP13_FN_SHIFT 0 /* GP13_FN - [4:0] */ +#define WM8995_GP13_FN_WIDTH 5 /* GP13_FN - [4:0] */ + +/* + * R1805 (0x70D) - GPIO 14 + */ +#define WM8995_GP14_DIR 0x8000 /* GP14_DIR */ +#define WM8995_GP14_DIR_MASK 0x8000 /* GP14_DIR */ +#define WM8995_GP14_DIR_SHIFT 15 /* GP14_DIR */ +#define WM8995_GP14_DIR_WIDTH 1 /* GP14_DIR */ +#define WM8995_GP14_PU 0x4000 /* GP14_PU */ +#define WM8995_GP14_PU_MASK 0x4000 /* GP14_PU */ +#define WM8995_GP14_PU_SHIFT 14 /* GP14_PU */ +#define WM8995_GP14_PU_WIDTH 1 /* GP14_PU */ +#define WM8995_GP14_PD 0x2000 /* GP14_PD */ +#define WM8995_GP14_PD_MASK 0x2000 /* GP14_PD */ +#define WM8995_GP14_PD_SHIFT 13 /* GP14_PD */ +#define WM8995_GP14_PD_WIDTH 1 /* GP14_PD */ +#define WM8995_GP14_POL 0x0400 /* GP14_POL */ +#define WM8995_GP14_POL_MASK 0x0400 /* GP14_POL */ +#define WM8995_GP14_POL_SHIFT 10 /* GP14_POL */ +#define WM8995_GP14_POL_WIDTH 1 /* GP14_POL */ +#define WM8995_GP14_OP_CFG 0x0200 /* GP14_OP_CFG */ +#define WM8995_GP14_OP_CFG_MASK 0x0200 /* GP14_OP_CFG */ +#define WM8995_GP14_OP_CFG_SHIFT 9 /* GP14_OP_CFG */ +#define WM8995_GP14_OP_CFG_WIDTH 1 /* GP14_OP_CFG */ +#define WM8995_GP14_DB 0x0100 /* GP14_DB */ +#define WM8995_GP14_DB_MASK 0x0100 /* GP14_DB */ +#define WM8995_GP14_DB_SHIFT 8 /* GP14_DB */ +#define WM8995_GP14_DB_WIDTH 1 /* GP14_DB */ +#define WM8995_GP14_LVL 0x0040 /* GP14_LVL */ +#define WM8995_GP14_LVL_MASK 0x0040 /* GP14_LVL */ +#define WM8995_GP14_LVL_SHIFT 6 /* GP14_LVL */ +#define WM8995_GP14_LVL_WIDTH 1 /* GP14_LVL */ +#define WM8995_GP14_FN_MASK 0x001F /* GP14_FN - [4:0] */ +#define WM8995_GP14_FN_SHIFT 0 /* GP14_FN - [4:0] */ +#define WM8995_GP14_FN_WIDTH 5 /* GP14_FN - [4:0] */ + +/* + * R1824 (0x720) - Pull Control (1) + */ +#define WM8995_DMICDAT3_PD 0x4000 /* DMICDAT3_PD */ +#define WM8995_DMICDAT3_PD_MASK 0x4000 /* DMICDAT3_PD */ +#define WM8995_DMICDAT3_PD_SHIFT 14 /* DMICDAT3_PD */ +#define WM8995_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */ +#define WM8995_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */ +#define WM8995_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */ +#define WM8995_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */ +#define WM8995_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define WM8995_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */ +#define WM8995_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */ +#define WM8995_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */ +#define WM8995_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ +#define WM8995_MCLK2_PU 0x0200 /* MCLK2_PU */ +#define WM8995_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */ +#define WM8995_MCLK2_PU_SHIFT 9 /* MCLK2_PU */ +#define WM8995_MCLK2_PU_WIDTH 1 /* MCLK2_PU */ +#define WM8995_MCLK2_PD 0x0100 /* MCLK2_PD */ +#define WM8995_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */ +#define WM8995_MCLK2_PD_SHIFT 8 /* MCLK2_PD */ +#define WM8995_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define WM8995_MCLK1_PU 0x0080 /* MCLK1_PU */ +#define WM8995_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */ +#define WM8995_MCLK1_PU_SHIFT 7 /* MCLK1_PU */ +#define WM8995_MCLK1_PU_WIDTH 1 /* MCLK1_PU */ +#define WM8995_MCLK1_PD 0x0040 /* MCLK1_PD */ +#define WM8995_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */ +#define WM8995_MCLK1_PD_SHIFT 6 /* MCLK1_PD */ +#define WM8995_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define WM8995_DACDAT1_PU 0x0020 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PD 0x0010 /* DACDAT1_PD */ +#define WM8995_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */ +#define WM8995_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */ +#define WM8995_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */ +#define WM8995_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */ +#define WM8995_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */ +#define WM8995_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */ +#define WM8995_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */ +#define WM8995_BCLK1_PU 0x0002 /* BCLK1_PU */ +#define WM8995_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */ +#define WM8995_BCLK1_PU_SHIFT 1 /* BCLK1_PU */ +#define WM8995_BCLK1_PU_WIDTH 1 /* BCLK1_PU */ +#define WM8995_BCLK1_PD 0x0001 /* BCLK1_PD */ +#define WM8995_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */ +#define WM8995_BCLK1_PD_SHIFT 0 /* BCLK1_PD */ +#define WM8995_BCLK1_PD_WIDTH 1 /* BCLK1_PD */ + +/* + * R1825 (0x721) - Pull Control (2) + */ +#define WM8995_LDO1ENA_PD 0x0010 /* LDO1ENA_PD */ +#define WM8995_LDO1ENA_PD_MASK 0x0010 /* LDO1ENA_PD */ +#define WM8995_LDO1ENA_PD_SHIFT 4 /* LDO1ENA_PD */ +#define WM8995_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define WM8995_MODE_PD 0x0004 /* MODE_PD */ +#define WM8995_MODE_PD_MASK 0x0004 /* MODE_PD */ +#define WM8995_MODE_PD_SHIFT 2 /* MODE_PD */ +#define WM8995_MODE_PD_WIDTH 1 /* MODE_PD */ +#define WM8995_CSNADDR_PD 0x0001 /* CSNADDR_PD */ +#define WM8995_CSNADDR_PD_MASK 0x0001 /* CSNADDR_PD */ +#define WM8995_CSNADDR_PD_SHIFT 0 /* CSNADDR_PD */ +#define WM8995_CSNADDR_PD_WIDTH 1 /* CSNADDR_PD */ + +/* + * R1840 (0x730) - Interrupt Status 1 + */ +#define WM8995_GP14_EINT 0x2000 /* GP14_EINT */ +#define WM8995_GP14_EINT_MASK 0x2000 /* GP14_EINT */ +#define WM8995_GP14_EINT_SHIFT 13 /* GP14_EINT */ +#define WM8995_GP14_EINT_WIDTH 1 /* GP14_EINT */ +#define WM8995_GP13_EINT 0x1000 /* GP13_EINT */ +#define WM8995_GP13_EINT_MASK 0x1000 /* GP13_EINT */ +#define WM8995_GP13_EINT_SHIFT 12 /* GP13_EINT */ +#define WM8995_GP13_EINT_WIDTH 1 /* GP13_EINT */ +#define WM8995_GP12_EINT 0x0800 /* GP12_EINT */ +#define WM8995_GP12_EINT_MASK 0x0800 /* GP12_EINT */ +#define WM8995_GP12_EINT_SHIFT 11 /* GP12_EINT */ +#define WM8995_GP12_EINT_WIDTH 1 /* GP12_EINT */ +#define WM8995_GP11_EINT 0x0400 /* GP11_EINT */ +#define WM8995_GP11_EINT_MASK 0x0400 /* GP11_EINT */ +#define WM8995_GP11_EINT_SHIFT 10 /* GP11_EINT */ +#define WM8995_GP11_EINT_WIDTH 1 /* GP11_EINT */ +#define WM8995_GP10_EINT 0x0200 /* GP10_EINT */ +#define WM8995_GP10_EINT_MASK 0x0200 /* GP10_EINT */ +#define WM8995_GP10_EINT_SHIFT 9 /* GP10_EINT */ +#define WM8995_GP10_EINT_WIDTH 1 /* GP10_EINT */ +#define WM8995_GP9_EINT 0x0100 /* GP9_EINT */ +#define WM8995_GP9_EINT_MASK 0x0100 /* GP9_EINT */ +#define WM8995_GP9_EINT_SHIFT 8 /* GP9_EINT */ +#define WM8995_GP9_EINT_WIDTH 1 /* GP9_EINT */ +#define WM8995_GP8_EINT 0x0080 /* GP8_EINT */ +#define WM8995_GP8_EINT_MASK 0x0080 /* GP8_EINT */ +#define WM8995_GP8_EINT_SHIFT 7 /* GP8_EINT */ +#define WM8995_GP8_EINT_WIDTH 1 /* GP8_EINT */ +#define WM8995_GP7_EINT 0x0040 /* GP7_EINT */ +#define WM8995_GP7_EINT_MASK 0x0040 /* GP7_EINT */ +#define WM8995_GP7_EINT_SHIFT 6 /* GP7_EINT */ +#define WM8995_GP7_EINT_WIDTH 1 /* GP7_EINT */ +#define WM8995_GP6_EINT 0x0020 /* GP6_EINT */ +#define WM8995_GP6_EINT_MASK 0x0020 /* GP6_EINT */ +#define WM8995_GP6_EINT_SHIFT 5 /* GP6_EINT */ +#define WM8995_GP6_EINT_WIDTH 1 /* GP6_EINT */ +#define WM8995_GP5_EINT 0x0010 /* GP5_EINT */ +#define WM8995_GP5_EINT_MASK 0x0010 /* GP5_EINT */ +#define WM8995_GP5_EINT_SHIFT 4 /* GP5_EINT */ +#define WM8995_GP5_EINT_WIDTH 1 /* GP5_EINT */ +#define WM8995_GP4_EINT 0x0008 /* GP4_EINT */ +#define WM8995_GP4_EINT_MASK 0x0008 /* GP4_EINT */ +#define WM8995_GP4_EINT_SHIFT 3 /* GP4_EINT */ +#define WM8995_GP4_EINT_WIDTH 1 /* GP4_EINT */ +#define WM8995_GP3_EINT 0x0004 /* GP3_EINT */ +#define WM8995_GP3_EINT_MASK 0x0004 /* GP3_EINT */ +#define WM8995_GP3_EINT_SHIFT 2 /* GP3_EINT */ +#define WM8995_GP3_EINT_WIDTH 1 /* GP3_EINT */ +#define WM8995_GP2_EINT 0x0002 /* GP2_EINT */ +#define WM8995_GP2_EINT_MASK 0x0002 /* GP2_EINT */ +#define WM8995_GP2_EINT_SHIFT 1 /* GP2_EINT */ +#define WM8995_GP2_EINT_WIDTH 1 /* GP2_EINT */ +#define WM8995_GP1_EINT 0x0001 /* GP1_EINT */ +#define WM8995_GP1_EINT_MASK 0x0001 /* GP1_EINT */ +#define WM8995_GP1_EINT_SHIFT 0 /* GP1_EINT */ +#define WM8995_GP1_EINT_WIDTH 1 /* GP1_EINT */ + +/* + * R1841 (0x731) - Interrupt Status 2 + */ +#define WM8995_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */ +#define WM8995_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */ +#define WM8995_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */ +#define WM8995_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */ +#define WM8995_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */ +#define WM8995_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */ +#define WM8995_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */ +#define WM8995_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */ +#define WM8995_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */ +#define WM8995_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */ +#define WM8995_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */ +#define WM8995_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT 0x0100 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT_MASK 0x0100 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT_SHIFT 8 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT_WIDTH 1 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT 0x0080 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT_MASK 0x0080 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT_SHIFT 7 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT_WIDTH 1 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT 0x0040 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT_MASK 0x0040 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT_SHIFT 6 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT_WIDTH 1 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_SRC2_LOCK_EINT 0x0020 /* SRC2_LOCK_EINT */ +#define WM8995_SRC2_LOCK_EINT_MASK 0x0020 /* SRC2_LOCK_EINT */ +#define WM8995_SRC2_LOCK_EINT_SHIFT 5 /* SRC2_LOCK_EINT */ +#define WM8995_SRC2_LOCK_EINT_WIDTH 1 /* SRC2_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT 0x0010 /* SRC1_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT_MASK 0x0010 /* SRC1_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT_SHIFT 4 /* SRC1_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT_WIDTH 1 /* SRC1_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT 0x0008 /* FLL2_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT_MASK 0x0008 /* FLL2_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT_SHIFT 3 /* FLL2_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT_WIDTH 1 /* FLL2_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT 0x0004 /* FLL1_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT_MASK 0x0004 /* FLL1_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT_SHIFT 2 /* FLL1_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT_WIDTH 1 /* FLL1_LOCK_EINT */ +#define WM8995_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */ +#define WM8995_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */ +#define WM8995_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */ +#define WM8995_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */ +#define WM8995_MICD_EINT 0x0001 /* MICD_EINT */ +#define WM8995_MICD_EINT_MASK 0x0001 /* MICD_EINT */ +#define WM8995_MICD_EINT_SHIFT 0 /* MICD_EINT */ +#define WM8995_MICD_EINT_WIDTH 1 /* MICD_EINT */ + +/* + * R1842 (0x732) - Interrupt Raw Status 2 + */ +#define WM8995_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */ +#define WM8995_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */ +#define WM8995_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */ +#define WM8995_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */ +#define WM8995_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */ +#define WM8995_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */ +#define WM8995_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */ +#define WM8995_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */ +#define WM8995_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */ +#define WM8995_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */ +#define WM8995_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */ +#define WM8995_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS 0x0100 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS_MASK 0x0100 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS_SHIFT 8 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS_WIDTH 1 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS 0x0080 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS_MASK 0x0080 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS_SHIFT 7 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS_WIDTH 1 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS 0x0040 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS_MASK 0x0040 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS_SHIFT 6 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS_WIDTH 1 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_SRC2_LOCK_STS 0x0020 /* SRC2_LOCK_STS */ +#define WM8995_SRC2_LOCK_STS_MASK 0x0020 /* SRC2_LOCK_STS */ +#define WM8995_SRC2_LOCK_STS_SHIFT 5 /* SRC2_LOCK_STS */ +#define WM8995_SRC2_LOCK_STS_WIDTH 1 /* SRC2_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS 0x0010 /* SRC1_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS_MASK 0x0010 /* SRC1_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS_SHIFT 4 /* SRC1_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS_WIDTH 1 /* SRC1_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */ + +/* + * R1848 (0x738) - Interrupt Status 1 Mask + */ +#define WM8995_IM_GP14_EINT 0x2000 /* IM_GP14_EINT */ +#define WM8995_IM_GP14_EINT_MASK 0x2000 /* IM_GP14_EINT */ +#define WM8995_IM_GP14_EINT_SHIFT 13 /* IM_GP14_EINT */ +#define WM8995_IM_GP14_EINT_WIDTH 1 /* IM_GP14_EINT */ +#define WM8995_IM_GP13_EINT 0x1000 /* IM_GP13_EINT */ +#define WM8995_IM_GP13_EINT_MASK 0x1000 /* IM_GP13_EINT */ +#define WM8995_IM_GP13_EINT_SHIFT 12 /* IM_GP13_EINT */ +#define WM8995_IM_GP13_EINT_WIDTH 1 /* IM_GP13_EINT */ +#define WM8995_IM_GP12_EINT 0x0800 /* IM_GP12_EINT */ +#define WM8995_IM_GP12_EINT_MASK 0x0800 /* IM_GP12_EINT */ +#define WM8995_IM_GP12_EINT_SHIFT 11 /* IM_GP12_EINT */ +#define WM8995_IM_GP12_EINT_WIDTH 1 /* IM_GP12_EINT */ +#define WM8995_IM_GP11_EINT 0x0400 /* IM_GP11_EINT */ +#define WM8995_IM_GP11_EINT_MASK 0x0400 /* IM_GP11_EINT */ +#define WM8995_IM_GP11_EINT_SHIFT 10 /* IM_GP11_EINT */ +#define WM8995_IM_GP11_EINT_WIDTH 1 /* IM_GP11_EINT */ +#define WM8995_IM_GP10_EINT 0x0200 /* IM_GP10_EINT */ +#define WM8995_IM_GP10_EINT_MASK 0x0200 /* IM_GP10_EINT */ +#define WM8995_IM_GP10_EINT_SHIFT 9 /* IM_GP10_EINT */ +#define WM8995_IM_GP10_EINT_WIDTH 1 /* IM_GP10_EINT */ +#define WM8995_IM_GP9_EINT 0x0100 /* IM_GP9_EINT */ +#define WM8995_IM_GP9_EINT_MASK 0x0100 /* IM_GP9_EINT */ +#define WM8995_IM_GP9_EINT_SHIFT 8 /* IM_GP9_EINT */ +#define WM8995_IM_GP9_EINT_WIDTH 1 /* IM_GP9_EINT */ +#define WM8995_IM_GP8_EINT 0x0080 /* IM_GP8_EINT */ +#define WM8995_IM_GP8_EINT_MASK 0x0080 /* IM_GP8_EINT */ +#define WM8995_IM_GP8_EINT_SHIFT 7 /* IM_GP8_EINT */ +#define WM8995_IM_GP8_EINT_WIDTH 1 /* IM_GP8_EINT */ +#define WM8995_IM_GP7_EINT 0x0040 /* IM_GP7_EINT */ +#define WM8995_IM_GP7_EINT_MASK 0x0040 /* IM_GP7_EINT */ +#define WM8995_IM_GP7_EINT_SHIFT 6 /* IM_GP7_EINT */ +#define WM8995_IM_GP7_EINT_WIDTH 1 /* IM_GP7_EINT */ +#define WM8995_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */ +#define WM8995_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */ +#define WM8995_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */ +#define WM8995_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */ +#define WM8995_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ +#define WM8995_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ +#define WM8995_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ +#define WM8995_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ +#define WM8995_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ +#define WM8995_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ +#define WM8995_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ +#define WM8995_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ +#define WM8995_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ +#define WM8995_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ +#define WM8995_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ +#define WM8995_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ +#define WM8995_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ +#define WM8995_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ +#define WM8995_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ +#define WM8995_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ +#define WM8995_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ +#define WM8995_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ +#define WM8995_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ +#define WM8995_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ + +/* + * R1849 (0x739) - Interrupt Status 2 Mask + */ +#define WM8995_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT 0x0100 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT_MASK 0x0100 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT_SHIFT 8 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT_WIDTH 1 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT 0x0080 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_MASK 0x0080 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_SHIFT 7 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_WIDTH 1 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT 0x0040 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_MASK 0x0040 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_SHIFT 6 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_WIDTH 1 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT 0x0020 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT_MASK 0x0020 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT_SHIFT 5 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT_WIDTH 1 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT 0x0010 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT_MASK 0x0010 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT_SHIFT 4 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT_WIDTH 1 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT 0x0008 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT_MASK 0x0008 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT_SHIFT 3 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT_WIDTH 1 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT 0x0004 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT_MASK 0x0004 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT_SHIFT 2 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT_WIDTH 1 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */ +#define WM8995_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */ +#define WM8995_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */ +#define WM8995_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */ +#define WM8995_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */ +#define WM8995_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */ +#define WM8995_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */ +#define WM8995_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */ + +/* + * R1856 (0x740) - Interrupt Control + */ +#define WM8995_IM_IRQ 0x0001 /* IM_IRQ */ +#define WM8995_IM_IRQ_MASK 0x0001 /* IM_IRQ */ +#define WM8995_IM_IRQ_SHIFT 0 /* IM_IRQ */ +#define WM8995_IM_IRQ_WIDTH 1 /* IM_IRQ */ + +/* + * R2048 (0x800) - Left PDM Speaker 1 + */ +#define WM8995_SPK1L_ENA 0x0010 /* SPK1L_ENA */ +#define WM8995_SPK1L_ENA_MASK 0x0010 /* SPK1L_ENA */ +#define WM8995_SPK1L_ENA_SHIFT 4 /* SPK1L_ENA */ +#define WM8995_SPK1L_ENA_WIDTH 1 /* SPK1L_ENA */ +#define WM8995_SPK1L_MUTE 0x0008 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_MASK 0x0008 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_SHIFT 3 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_ZC 0x0004 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_MUTE_ZC_MASK 0x0004 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_MUTE_ZC_SHIFT 2 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_MUTE_ZC_WIDTH 1 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_SRC_MASK 0x0003 /* SPK1L_SRC - [1:0] */ +#define WM8995_SPK1L_SRC_SHIFT 0 /* SPK1L_SRC - [1:0] */ +#define WM8995_SPK1L_SRC_WIDTH 2 /* SPK1L_SRC - [1:0] */ + +/* + * R2049 (0x801) - Right PDM Speaker 1 + */ +#define WM8995_SPK1R_ENA 0x0010 /* SPK1R_ENA */ +#define WM8995_SPK1R_ENA_MASK 0x0010 /* SPK1R_ENA */ +#define WM8995_SPK1R_ENA_SHIFT 4 /* SPK1R_ENA */ +#define WM8995_SPK1R_ENA_WIDTH 1 /* SPK1R_ENA */ +#define WM8995_SPK1R_MUTE 0x0008 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_MASK 0x0008 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_SHIFT 3 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_ZC 0x0004 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_MUTE_ZC_MASK 0x0004 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_MUTE_ZC_SHIFT 2 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_MUTE_ZC_WIDTH 1 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_SRC_MASK 0x0003 /* SPK1R_SRC - [1:0] */ +#define WM8995_SPK1R_SRC_SHIFT 0 /* SPK1R_SRC - [1:0] */ +#define WM8995_SPK1R_SRC_WIDTH 2 /* SPK1R_SRC - [1:0] */ + +/* + * R2050 (0x802) - PDM Speaker 1 Mute Sequence + */ +#define WM8995_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */ + +/* + * R2056 (0x808) - Left PDM Speaker 2 + */ +#define WM8995_SPK2L_ENA 0x0010 /* SPK2L_ENA */ +#define WM8995_SPK2L_ENA_MASK 0x0010 /* SPK2L_ENA */ +#define WM8995_SPK2L_ENA_SHIFT 4 /* SPK2L_ENA */ +#define WM8995_SPK2L_ENA_WIDTH 1 /* SPK2L_ENA */ +#define WM8995_SPK2L_MUTE 0x0008 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_MASK 0x0008 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_SHIFT 3 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_ZC 0x0004 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_MUTE_ZC_MASK 0x0004 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_MUTE_ZC_SHIFT 2 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_MUTE_ZC_WIDTH 1 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_SRC_MASK 0x0003 /* SPK2L_SRC - [1:0] */ +#define WM8995_SPK2L_SRC_SHIFT 0 /* SPK2L_SRC - [1:0] */ +#define WM8995_SPK2L_SRC_WIDTH 2 /* SPK2L_SRC - [1:0] */ + +/* + * R2057 (0x809) - Right PDM Speaker 2 + */ +#define WM8995_SPK2R_ENA 0x0010 /* SPK2R_ENA */ +#define WM8995_SPK2R_ENA_MASK 0x0010 /* SPK2R_ENA */ +#define WM8995_SPK2R_ENA_SHIFT 4 /* SPK2R_ENA */ +#define WM8995_SPK2R_ENA_WIDTH 1 /* SPK2R_ENA */ +#define WM8995_SPK2R_MUTE 0x0008 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_MASK 0x0008 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_SHIFT 3 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_ZC 0x0004 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_MUTE_ZC_MASK 0x0004 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_MUTE_ZC_SHIFT 2 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_MUTE_ZC_WIDTH 1 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_SRC_MASK 0x0003 /* SPK2R_SRC - [1:0] */ +#define WM8995_SPK2R_SRC_SHIFT 0 /* SPK2R_SRC - [1:0] */ +#define WM8995_SPK2R_SRC_WIDTH 2 /* SPK2R_SRC - [1:0] */ + +/* + * R2058 (0x80A) - PDM Speaker 2 Mute Sequence + */ +#define WM8995_SPK2_MUTE_SEQ1_MASK 0x00FF /* SPK2_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK2_MUTE_SEQ1_SHIFT 0 /* SPK2_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */ + +#define WM8995_CLASS_W_SWITCH(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = wm8995_put_class_w, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) \ +} + +struct wm8995_reg_access { + u16 read; + u16 write; + u16 vol; +}; + +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ +enum clk_src { + WM8995_SYSCLK_MCLK1 = 1, + WM8995_SYSCLK_MCLK2, + WM8995_SYSCLK_FLL1, + WM8995_SYSCLK_FLL2, + WM8995_SYSCLK_OPCLK +}; + +#define WM8995_FLL1 1 +#define WM8995_FLL2 2 + +#define WM8995_FLL_SRC_MCLK1 1 +#define WM8995_FLL_SRC_MCLK2 2 +#define WM8995_FLL_SRC_LRCLK 3 +#define WM8995_FLL_SRC_BCLK 4 + +#endif /* _WM8995_H */ diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index ecc7c37180c7..91c6b39de50c 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -15,6 +15,7 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> @@ -23,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -158,7 +158,6 @@ static struct { struct wm9081_priv { enum snd_soc_control_type control_type; void *control_data; - u16 reg_cache[WM9081_MAX_REGISTER + 1]; int sysclk_source; int mclk_rate; int sysclk_rate; @@ -168,10 +167,10 @@ struct wm9081_priv { int fll_fref; int fll_fout; int tdm_width; - struct wm9081_retune_mobile_config *retune; + struct wm9081_pdata pdata; }; -static int wm9081_volatile_register(unsigned int reg) +static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9081_SOFTWARE_RESET: @@ -306,7 +305,7 @@ static int speaker_mode_get(struct snd_kcontrol *kcontrol, /* * Stop any attempts to change speaker mode while the speaker is enabled. * - * We also have some special anti-pop controls dependant on speaker + * We also have some special anti-pop controls dependent on speaker * mode which must be changed along with the mode. */ static int speaker_mode_put(struct snd_kcontrol *kcontrol, @@ -390,27 +389,6 @@ SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0), }; -static int speaker_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - reg |= WM9081_SPK_ENA; - break; - - case SND_SOC_DAPM_PRE_PMD: - reg &= ~WM9081_SPK_ENA; - break; - } - - snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg); - - return 0; -} - struct _fll_div { u16 fll_fratio; u16 fll_outdiv; @@ -478,7 +456,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, pr_debug("Fvco=%dHz\n", target); - /* Find an appropraite FLL_FRATIO and factor it out of the target */ + /* Find an appropriate 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; @@ -591,6 +569,10 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id, reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT; snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5); + /* Set gain to the recommended value */ + snd_soc_update_bits(codec, WM9081_FLL_CONTROL_4, + WM9081_FLL_GAIN_MASK, 0); + /* Enable the FLL */ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA); @@ -744,9 +726,8 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0), -SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0, - speaker_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("LINEOUT"), SND_SOC_DAPM_OUTPUT("SPKN"), @@ -759,7 +740,7 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0), }; -static const struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route wm9081_audio_paths[] = { { "DAC", NULL, "CLK_SYS" }, { "DAC", NULL, "CLK_DSP" }, @@ -777,8 +758,10 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "Speaker PGA", NULL, "TOCLK" }, { "Speaker PGA", NULL, "CLK_SYS" }, - { "SPKN", NULL, "Speaker PGA" }, - { "SPKP", NULL, "Speaker PGA" }, + { "Speaker", NULL, "Speaker PGA" }, + + { "SPKN", NULL, "Speaker" }, + { "SPKP", NULL, "Speaker" }, }; static int wm9081_set_bias_level(struct snd_soc_codec *codec, @@ -805,7 +788,7 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: /* Initial cold start */ - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Disable LINEOUT discharge */ reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL); reg &= ~WM9081_LINEOUT_DISCH; @@ -865,7 +848,7 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1079,21 +1062,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream, aif4 |= wm9081->bclk / wm9081->fs; /* Apply a ReTune Mobile configuration if it's in use */ - if (wm9081->retune) { - struct wm9081_retune_mobile_config *retune = wm9081->retune; + if (wm9081->pdata.num_retune_configs) { + struct wm9081_pdata *pdata = &wm9081->pdata; struct wm9081_retune_mobile_setting *s; int eq1; best = 0; - best_val = abs(retune->configs[0].rate - wm9081->fs); - for (i = 0; i < retune->num_configs; i++) { - cur_val = abs(retune->configs[i].rate - wm9081->fs); + best_val = abs(pdata->retune_configs[0].rate - wm9081->fs); + for (i = 0; i < pdata->num_retune_configs; i++) { + cur_val = abs(pdata->retune_configs[i].rate - + wm9081->fs); if (cur_val < best_val) { best_val = cur_val; best = i; } } - s = &retune->configs[best]; + s = &pdata->retune_configs[best]; dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n", s->name, s->rate); @@ -1136,10 +1120,9 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } -static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai, +static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = codec_dai->codec; struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec); switch (clk_id) { @@ -1204,7 +1187,6 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai, static struct snd_soc_dai_ops wm9081_dai_ops = { .hw_params = wm9081_hw_params, - .set_sysclk = wm9081_set_sysclk, .set_fmt = wm9081_set_dai_fmt, .digital_mute = wm9081_digital_mute, .set_tdm_slot = wm9081_set_tdm_slot, @@ -1251,6 +1233,14 @@ static int wm9081_probe(struct snd_soc_codec *codec) return ret; } + reg = 0; + if (wm9081->pdata.irq_high) + reg |= WM9081_IRQ_POL; + if (!wm9081->pdata.irq_cmos) + reg |= WM9081_IRQ_OP_CTRL; + snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL, + WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); + wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Enable zero cross by default */ @@ -1262,17 +1252,13 @@ static int wm9081_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9081_snd_controls, ARRAY_SIZE(wm9081_snd_controls)); - if (!wm9081->retune) { + if (!wm9081->pdata.num_retune_configs) { dev_dbg(codec->dev, "No ReTune Mobile data, using normal EQ\n"); snd_soc_add_controls(codec, wm9081_eq_controls, ARRAY_SIZE(wm9081_eq_controls)); } - snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets, - ARRAY_SIZE(wm9081_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - return ret; } @@ -1316,11 +1302,19 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = { .remove = wm9081_remove, .suspend = wm9081_suspend, .resume = wm9081_resume, + + .set_sysclk = wm9081_set_sysclk, .set_bias_level = wm9081_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm9081_reg_defaults), .reg_word_size = sizeof(u16), .reg_cache_default = wm9081_reg_defaults, .volatile_register = wm9081_volatile_register, + + .dapm_widgets = wm9081_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets), + .dapm_routes = wm9081_audio_paths, + .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths), }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) @@ -1335,8 +1329,13 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, wm9081); + wm9081->control_type = SND_SOC_I2C; wm9081->control_data = i2c; + if (dev_get_platdata(&i2c->dev)) + memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm9081->pdata)); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm9081, &wm9081_dai, 1); if (ret < 0) @@ -1359,7 +1358,7 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id); static struct i2c_driver wm9081_i2c_driver = { .driver = { - .name = "wm9081-codec", + .name = "wm9081", .owner = THIS_MODULE, }, .probe = wm9081_i2c_probe, diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 99c046ba46bb..4de12203e611 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -28,7 +28,6 @@ #include <linux/slab.h> #include <sound/initval.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/wm9090.h> @@ -141,12 +140,11 @@ static const u16 wm9090_reg_defaults[] = { /* This struct is used to save the context */ struct wm9090_priv { struct mutex mutex; - u16 reg_cache[WM9090_MAX_REGISTER + 1]; struct wm9090_platform_data pdata; void *control_data; }; -static int wm9090_volatile(unsigned int reg) +static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9090_SOFTWARE_RESET: @@ -443,31 +441,32 @@ static const struct snd_soc_dapm_route audio_map_in2_diff[] = { static int wm9090_add_controls(struct snd_soc_codec *codec) { struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int i; - snd_soc_dapm_new_controls(codec, wm9090_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets, ARRAY_SIZE(wm9090_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); snd_soc_add_controls(codec, wm9090_controls, ARRAY_SIZE(wm9090_controls)); if (wm9090->pdata.lin1_diff) { - snd_soc_dapm_add_routes(codec, audio_map_in1_diff, + snd_soc_dapm_add_routes(dapm, audio_map_in1_diff, ARRAY_SIZE(audio_map_in1_diff)); } else { - snd_soc_dapm_add_routes(codec, audio_map_in1_se, + snd_soc_dapm_add_routes(dapm, audio_map_in1_se, ARRAY_SIZE(audio_map_in1_se)); snd_soc_add_controls(codec, wm9090_in1_se_controls, ARRAY_SIZE(wm9090_in1_se_controls)); } if (wm9090->pdata.lin2_diff) { - snd_soc_dapm_add_routes(codec, audio_map_in2_diff, + snd_soc_dapm_add_routes(dapm, audio_map_in2_diff, ARRAY_SIZE(audio_map_in2_diff)); } else { - snd_soc_dapm_add_routes(codec, audio_map_in2_se, + snd_soc_dapm_add_routes(dapm, audio_map_in2_se, ARRAY_SIZE(audio_map_in2_se)); snd_soc_add_controls(codec, wm9090_in2_se_controls, ARRAY_SIZE(wm9090_in2_se_controls)); @@ -514,12 +513,12 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Restore the register cache */ for (i = 1; i < codec->driver->reg_cache_size; i++) { if (reg_cache[i] == wm9090_reg_defaults[i]) continue; - if (wm9090_volatile(i)) + if (wm9090_volatile(codec, i)) continue; ret = snd_soc_write(codec, i, reg_cache[i]); @@ -544,7 +543,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -576,22 +575,30 @@ static int wm9090_probe(struct snd_soc_codec *codec) /* Configure some defaults; they will be written out when we * bring the bias up. */ - wm9090->reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU - | WM9090_IN1A_ZC; - wm9090->reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU - | WM9090_IN1B_ZC; - wm9090->reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU - | WM9090_IN2A_ZC; - wm9090->reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU - | WM9090_IN2B_ZC; - wm9090->reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |= - WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC; - wm9090->reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |= - WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC; - wm9090->reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |= - WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC; - - wm9090->reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA; + snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME, + WM9090_IN1_VU | WM9090_IN1A_ZC, + WM9090_IN1_VU | WM9090_IN1A_ZC); + snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME, + WM9090_IN1_VU | WM9090_IN1B_ZC, + WM9090_IN1_VU | WM9090_IN1B_ZC); + snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME, + WM9090_IN2_VU | WM9090_IN2A_ZC, + WM9090_IN2_VU | WM9090_IN2A_ZC); + snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME, + WM9090_IN2_VU | WM9090_IN2B_ZC, + WM9090_IN2_VU | WM9090_IN2B_ZC); + snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT, + WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC, + WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC); + snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME, + WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC, + WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC); + snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME, + WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC, + WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC); + + snd_soc_update_bits(codec, WM9090_CLOCKING_1, + WM9090_TOCLK_ENA, WM9090_TOCLK_ENA); wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index a144acda751c..47b357adabdd 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -19,7 +19,6 @@ #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "wm9705.h" @@ -203,9 +202,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm9705_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets, + 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(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index d2f224d62744..bf5d4ef1a2a6 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -20,7 +20,6 @@ #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "wm9712.h" #define WM9712_VERSION "0.4" @@ -432,10 +431,11 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm9712_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm9712_dapm_widgets, - ARRAY_SIZE(wm9712_dapm_widgets)); + struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + 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; } @@ -570,7 +570,7 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 7da13b07a53d..38ed98558718 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -26,7 +26,6 @@ #include <sound/pcm_params.h> #include <sound/tlv.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "wm9713.h" @@ -647,10 +646,12 @@ static const struct snd_soc_dapm_route audio_map[] = { static int wm9713_add_widgets(struct snd_soc_codec *codec) { - snd_soc_dapm_new_controls(codec, wm9713_dapm_widgets, + 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(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } @@ -1147,7 +1148,7 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec, ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 19ca782ac970..4005e9af5d61 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -22,7 +22,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -83,7 +82,8 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) } while (reg & op && count < 400); if (reg & op) - dev_err(codec->dev, "Timed out waiting for DC Servo\n"); + dev_err(codec->dev, "Timed out waiting for DC Servo %x\n", + op); } /* @@ -92,54 +92,73 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) static void calibrate_dc_servo(struct snd_soc_codec *codec) { struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + s8 offset; u16 reg, reg_l, reg_r, dcs_cfg; - /* Set for 32 series updates */ - snd_soc_update_bits(codec, WM8993_DC_SERVO_1, - WM8993_DCS_SERIES_NO_01_MASK, - 32 << WM8993_DCS_SERIES_NO_01_SHIFT); - wait_for_dc_servo(codec, - WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1); + /* If we're using a digital only path and have a previously + * callibrated DC servo offset stored then use that. */ + if (hubs->class_w && hubs->class_w_dcs) { + dev_dbg(codec->dev, "Using cached DC servo offset %x\n", + hubs->class_w_dcs); + snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs); + wait_for_dc_servo(codec, + WM8993_DCS_TRIG_DAC_WR_0 | + WM8993_DCS_TRIG_DAC_WR_1); + return; + } + + /* Devices not using a DCS code correction have startup mode */ + if (hubs->dcs_codes) { + /* Set for 32 series updates */ + snd_soc_update_bits(codec, WM8993_DC_SERVO_1, + WM8993_DCS_SERIES_NO_01_MASK, + 32 << WM8993_DCS_SERIES_NO_01_SHIFT); + wait_for_dc_servo(codec, + WM8993_DCS_TRIG_SERIES_0 | + WM8993_DCS_TRIG_SERIES_1); + } else { + wait_for_dc_servo(codec, + WM8993_DCS_TRIG_STARTUP_0 | + WM8993_DCS_TRIG_STARTUP_1); + } + + /* Different chips in the family support different readback + * methods. + */ + switch (hubs->dcs_readback_mode) { + case 0: + reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) + & WM8993_DCS_INTEG_CHAN_0_MASK; + reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) + & WM8993_DCS_INTEG_CHAN_1_MASK; + break; + case 1: + reg = snd_soc_read(codec, WM8993_DC_SERVO_3); + reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) + >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; + reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; + break; + default: + WARN(1, "Unknown DCS readback method\n"); + break; + } + + dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r); /* Apply correction to DC servo result */ if (hubs->dcs_codes) { dev_dbg(codec->dev, "Applying %d code DC servo correction\n", hubs->dcs_codes); - /* Different chips in the family support different - * readback methods. - */ - switch (hubs->dcs_readback_mode) { - case 0: - reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) - & WM8993_DCS_INTEG_CHAN_0_MASK;; - reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) - & WM8993_DCS_INTEG_CHAN_1_MASK; - break; - case 1: - reg = snd_soc_read(codec, WM8993_DC_SERVO_3); - reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) - >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; - reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; - break; - default: - WARN(1, "Unknown DCS readback method\n"); - break; - } - - dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r); - /* HPOUT1L */ - if (reg_l + hubs->dcs_codes > 0 && - reg_l + hubs->dcs_codes < 0xff) - reg_l += hubs->dcs_codes; - dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + offset = reg_l; + offset += hubs->dcs_codes; + dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT; /* HPOUT1R */ - if (reg_r + hubs->dcs_codes > 0 && - reg_r + hubs->dcs_codes < 0xff) - reg_r += hubs->dcs_codes; - dcs_cfg |= reg_r; + offset = reg_r; + offset += hubs->dcs_codes; + dcs_cfg |= (u8)offset; dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg); @@ -148,7 +167,15 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) wait_for_dc_servo(codec, WM8993_DCS_TRIG_DAC_WR_0 | WM8993_DCS_TRIG_DAC_WR_1); + } else { + dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + dcs_cfg |= reg_r; } + + /* Save the callibrated offset if we're in class W mode and + * therefore don't have any analogue signal mixed in. */ + if (hubs->class_w) + hubs->class_w_dcs = dcs_cfg; } /* @@ -163,6 +190,9 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + /* Updating the analogue gains invalidates the DC servo cache */ + hubs->class_w_dcs = 0; + /* If we're applying an offset correction then updating the * callibration would be likely to introduce further offsets. */ if (hubs->dcs_codes) @@ -293,7 +323,7 @@ SOC_DOUBLE_R("Speaker Switch", SOC_DOUBLE_R("Speaker ZC Switch", WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, 7, 1, 0), -SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 0, 3, 7, 0, +SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 3, 0, 7, 0, spkboost_tlv), SOC_ENUM("Speaker Reference", speaker_ref), SOC_ENUM("Speaker Mode", speaker_mode), @@ -645,6 +675,9 @@ SND_SOC_DAPM_OUTPUT("LINEOUT2N"), }; static const struct snd_soc_dapm_route analogue_routes[] = { + { "MICBIAS1", NULL, "CLK_SYS" }, + { "MICBIAS2", NULL, "CLK_SYS" }, + { "IN1L PGA", "IN1LP Switch", "IN1LP" }, { "IN1L PGA", "IN1LN Switch", "IN1LN" }, @@ -707,12 +740,12 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "SPKL", "Input Switch", "MIXINL" }, { "SPKL", "IN1LP Switch", "IN1LP" }, - { "SPKL", "Output Switch", "Left Output Mixer" }, + { "SPKL", "Output Switch", "Left Output PGA" }, { "SPKL", NULL, "TOCLK" }, { "SPKR", "Input Switch", "MIXINR" }, { "SPKR", "IN1RP Switch", "IN1RP" }, - { "SPKR", "Output Switch", "Right Output Mixer" }, + { "SPKR", "Output Switch", "Right Output PGA" }, { "SPKR", NULL, "TOCLK" }, { "SPKL Boost", "Direct Voice Switch", "Direct Voice" }, @@ -734,8 +767,8 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "SPKOUTRP", NULL, "SPKR Driver" }, { "SPKOUTRN", NULL, "SPKR Driver" }, - { "Left Headphone Mux", "Mixer", "Left Output Mixer" }, - { "Right Headphone Mux", "Mixer", "Right Output Mixer" }, + { "Left Headphone Mux", "Mixer", "Left Output PGA" }, + { "Right Headphone Mux", "Mixer", "Right Output PGA" }, { "Headphone PGA", NULL, "Left Headphone Mux" }, { "Headphone PGA", NULL, "Right Headphone Mux" }, @@ -791,6 +824,8 @@ static const struct snd_soc_dapm_route lineout2_se_routes[] = { int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + /* Latch volume update bits & default ZC on */ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME, WM8993_IN1_VU, WM8993_IN1_VU); @@ -819,7 +854,7 @@ int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec) snd_soc_add_controls(codec, analogue_snd_controls, ARRAY_SIZE(analogue_snd_controls)); - snd_soc_dapm_new_controls(codec, analogue_dapm_widgets, + snd_soc_dapm_new_controls(dapm, analogue_dapm_widgets, ARRAY_SIZE(analogue_dapm_widgets)); return 0; } @@ -828,24 +863,26 @@ EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls); int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec, int lineout1_diff, int lineout2_diff) { - snd_soc_dapm_add_routes(codec, analogue_routes, + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_add_routes(dapm, analogue_routes, ARRAY_SIZE(analogue_routes)); if (lineout1_diff) - snd_soc_dapm_add_routes(codec, + snd_soc_dapm_add_routes(dapm, lineout1_diff_routes, ARRAY_SIZE(lineout1_diff_routes)); else - snd_soc_dapm_add_routes(codec, + snd_soc_dapm_add_routes(dapm, lineout1_se_routes, ARRAY_SIZE(lineout1_se_routes)); if (lineout2_diff) - snd_soc_dapm_add_routes(codec, + snd_soc_dapm_add_routes(dapm, lineout2_diff_routes, ARRAY_SIZE(lineout2_diff_routes)); else - snd_soc_dapm_add_routes(codec, + snd_soc_dapm_add_routes(dapm, lineout2_se_routes, ARRAY_SIZE(lineout2_se_routes)); @@ -872,7 +909,7 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec, * VMID as an output and can disable it. */ if (lineout1_diff && lineout2_diff) - codec->idle_bias_off = 1; + codec->dapm.idle_bias_off = 1; if (lineout1fb) snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h index e51c16683589..f8a5e976b5e6 100644 --- a/sound/soc/codecs/wm_hubs.h +++ b/sound/soc/codecs/wm_hubs.h @@ -23,6 +23,9 @@ struct wm_hubs_data { int dcs_codes; int dcs_readback_mode; int hp_startup_mode; + + bool class_w; + u16 class_w_dcs; }; extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index bc9e6b0b3f6f..fe7984221eb9 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -18,7 +18,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/dma.h> #include <asm/mach-types.h> @@ -27,7 +26,6 @@ #include <mach/edma.h> #include <mach/mux.h> -#include "../codecs/tlv320aic3x.h" #include "davinci-pcm.h" #include "davinci-i2s.h" #include "davinci-mcasp.h" @@ -132,26 +130,27 @@ static const struct snd_soc_dapm_route audio_map[] = { static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; /* Add davinci-evm specific widgets */ - snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, + snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, ARRAY_SIZE(aic3x_dapm_widgets)); /* Set up davinci-evm specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* not connected */ - snd_soc_dapm_disable_pin(codec, "MONO_LOUT"); - snd_soc_dapm_disable_pin(codec, "HPLCOM"); - snd_soc_dapm_disable_pin(codec, "HPRCOM"); + snd_soc_dapm_disable_pin(dapm, "MONO_LOUT"); + snd_soc_dapm_disable_pin(dapm, "HPLCOM"); + snd_soc_dapm_disable_pin(dapm, "HPRCOM"); /* always connected */ - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Line Out"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_enable_pin(codec, "Line In"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Line Out"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Line In"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -219,12 +218,24 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = { .ops = &evm_spdif_ops, }, }; -static struct snd_soc_dai_link da8xx_evm_dai = { + +static struct snd_soc_dai_link da830_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcasp.1", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-0018", + .platform_name = "davinci-pcm-audio", + .init = evm_aic3x_init, + .ops = &evm_ops, +}; + +static struct snd_soc_dai_link da850_evm_dai = { .name = "TLV320AIC3X", .stream_name = "AIC3X", .cpu_dai_name= "davinci-mcasp.0", .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.0-001a", + .codec_name = "tlv320aic3x-codec.1-0018", .platform_name = "davinci-pcm-audio", .init = evm_aic3x_init, .ops = &evm_ops, @@ -260,13 +271,13 @@ static struct snd_soc_card dm6467_snd_soc_card_evm = { static struct snd_soc_card da830_snd_soc_card = { .name = "DA830/OMAP-L137 EVM", - .dai_link = &da8xx_evm_dai, + .dai_link = &da830_evm_dai, .num_links = 1, }; static struct snd_soc_card da850_snd_soc_card = { .name = "DA850/OMAP-L138 EVM", - .dai_link = &da8xx_evm_dai, + .dai_link = &da850_evm_dai, .num_links = 1, }; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 9e0e565e6ed9..d0d60b8a54d4 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -658,7 +658,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) return -ENODEV; } - ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "McBSP region already claimed\n"); @@ -694,20 +694,25 @@ static int davinci_i2s_probe(struct platform_device *pdev) } clk_enable(dev->clk); - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release_clk; + } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = - (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); + (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = - (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); + (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_iounmap; } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; @@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_iounmap; } dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; dev->dev = &pdev->dev; @@ -724,14 +729,19 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai); if (ret != 0) - goto err_free_mem; + goto err_iounmap; return 0; +err_iounmap: + iounmap(dev->base); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); err_free_mem: kfree(dev); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return ret; } @@ -747,7 +757,7 @@ static int davinci_i2s_remove(struct platform_device *pdev) dev->clk = NULL; kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return 0; } diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fb55d2c5d704..4ddc6d3b6678 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -434,17 +434,21 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26)); + mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | AHCLKX | AFSX); break; case SND_SOC_DAIFMT_CBM_CFS: /* codec is clock master and frame slave */ - mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26)); + mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | ACLKR); + mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, + AFSX | AFSR); break; case SND_SOC_DAIFMT_CBM_CFM: /* codec is clock and frame master */ @@ -454,7 +458,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26)); + mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, + ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); break; default: @@ -644,7 +649,7 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask); mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD); - if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) + if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXMOD(dev->tdm_slots), FSXMOD(0x1FF)); else @@ -660,7 +665,7 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) AHCLKRE); mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask); - if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32)) + if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRMOD(dev->tdm_slots), FSRMOD(0x1FF)); else @@ -868,7 +873,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) } ioarea = request_mem_region(mem->start, - (mem->end - mem->start) + 1, pdev->name); + resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "Audio region already claimed\n"); ret = -EBUSY; @@ -885,7 +890,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) clk_enable(dev->clk); dev->clk_active = 1; - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release_clk; + } + dev->op_mode = pdata->op_mode; dev->tdm_slots = pdata->tdm_slots; dev->num_serializer = pdata->num_serializer; @@ -899,14 +910,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + - io_v2p(dev->base)); + mem->start); /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_iounmap; } dma_data->channel = res->start; @@ -915,13 +926,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + - io_v2p(dev->base)); + mem->start); res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_iounmap; } dma_data->channel = res->start; @@ -929,11 +940,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]); if (ret != 0) - goto err_release_region; + goto err_iounmap; return 0; +err_iounmap: + iounmap(dev->base); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); err_release_data: kfree(dev); @@ -951,7 +967,7 @@ static int davinci_mcasp_remove(struct platform_device *pdev) dev->clk = NULL; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); kfree(dev); diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c index 6c6666a1f942..0fe558c65145 100644 --- a/sound/soc/davinci/davinci-sffsdr.c +++ b/sound/soc/davinci/davinci-sffsdr.c @@ -21,7 +21,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/dma.h> #include <asm/mach-types.h> diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9d2afccc3a2d..13e05a302a92 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -205,7 +205,7 @@ static struct snd_soc_dai_driver davinci_vcif_dai = { static int davinci_vcif_probe(struct platform_device *pdev) { - struct davinci_vc *davinci_vc = platform_get_drvdata(pdev); + struct davinci_vc *davinci_vc = mfd_get_data(pdev); struct davinci_vcif_dev *davinci_vcif_dev; int ret; diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig index 57429041189c..91a28de94109 100644 --- a/sound/soc/ep93xx/Kconfig +++ b/sound/soc/ep93xx/Kconfig @@ -30,3 +30,12 @@ config SND_EP93XX_SOC_SIMONE help Say Y or M here if you want to add support for AC97 audio on the Simplemachines Sim.One board. + +config SND_EP93XX_SOC_EDB93XX + tristate "SoC Audio support for Cirrus Logic EDB93xx boards" + depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A) + select SND_EP93XX_SOC_I2S + select SND_SOC_CS4271 + help + Say Y or M here if you want to add support for I2S audio on the + Cirrus Logic EDB93xx boards. diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile index 8e7977fb6b7d..5514146cbdf0 100644 --- a/sound/soc/ep93xx/Makefile +++ b/sound/soc/ep93xx/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o # EP93XX Machine Support snd-soc-snappercl15-objs := snappercl15.o snd-soc-simone-objs := simone.o +snd-soc-edb93xx-objs := edb93xx.o obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o +obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c new file mode 100644 index 000000000000..d3aa15119d26 --- /dev/null +++ b/sound/soc/ep93xx/edb93xx.c @@ -0,0 +1,142 @@ +/* + * SoC audio for EDB93xx + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * 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. + * + * 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. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include "ep93xx-pcm.h" + +#define edb93xx_has_audio() (machine_is_edb9301() || \ + machine_is_edb9302() || \ + machine_is_edb9302a() || \ + machine_is_edb9307a() || \ + machine_is_edb9315a()) + +static int edb93xx_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 err; + unsigned int mclk_rate; + unsigned int rate = params_rate(params); + + /* + * According to CS4271 datasheet we use MCLK/LRCK=256 for + * rates below 50kHz and 128 for higher sample rates + */ + if (rate < 50000) + mclk_rate = rate * 64 * 4; + else + mclk_rate = rate * 64 * 2; + + err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS); + if (err) + return err; + + err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS); + if (err) + return err; + + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, + SND_SOC_CLOCK_IN); + if (err) + return err; + + return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, + SND_SOC_CLOCK_OUT); +} + +static struct snd_soc_ops edb93xx_ops = { + .hw_params = edb93xx_hw_params, +}; + +static struct snd_soc_dai_link edb93xx_dai = { + .name = "CS4271", + .stream_name = "CS4271 HiFi", + .platform_name = "ep93xx-pcm-audio", + .cpu_dai_name = "ep93xx-i2s", + .codec_name = "spi0.0", + .codec_dai_name = "cs4271-hifi", + .ops = &edb93xx_ops, +}; + +static struct snd_soc_card snd_soc_edb93xx = { + .name = "EDB93XX", + .dai_link = &edb93xx_dai, + .num_links = 1, +}; + +static struct platform_device *edb93xx_snd_device; + +static int __init edb93xx_init(void) +{ + int ret; + + if (!edb93xx_has_audio()) + return -ENODEV; + + ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97, + EP93XX_SYSCON_I2SCLKDIV_ORIDE | + EP93XX_SYSCON_I2SCLKDIV_SPOL); + if (ret) + return ret; + + edb93xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!edb93xx_snd_device) { + ret = -ENOMEM; + goto free_i2s; + } + + platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx); + ret = platform_device_add(edb93xx_snd_device); + if (ret) + goto device_put; + + return 0; + +device_put: + platform_device_put(edb93xx_snd_device); +free_i2s: + ep93xx_i2s_release(); + return ret; +} +module_init(edb93xx_init); + +static void __exit edb93xx_exit(void) +{ + platform_device_unregister(edb93xx_snd_device); + ep93xx_i2s_release(); +} +module_exit(edb93xx_exit); + +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_DESCRIPTION("ALSA SoC EDB93xx"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c index 68a0bae1208a..104e95cda0ad 100644 --- a/sound/soc/ep93xx/ep93xx-ac97.c +++ b/sound/soc/ep93xx/ep93xx-ac97.c @@ -253,7 +253,6 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream, struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai); unsigned v = 0; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c index 4f4873359613..042f4e93746f 100644 --- a/sound/soc/ep93xx/ep93xx-i2s.c +++ b/sound/soc/ep93xx/ep93xx-i2s.c @@ -242,7 +242,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); unsigned word_len, div, sdiv, lrdiv; - int found = 0, err; + int err; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -267,21 +267,22 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len); /* - * Calculate the sdiv (bit clock) and lrdiv (left/right clock) values. - * If the lrclk is pulse length is larger than the word size, then the - * bit clock will be gated for the unused bits. + * EP93xx I2S module can be setup so SCLK / LRCLK value can be + * 32, 64, 128. MCLK / SCLK value can be 2 and 4. + * We set LRCLK equal to `rate' and minimum SCLK / LRCLK + * value is 64, because our sample size is 32 bit * 2 channels. + * I2S standard permits us to transmit more bits than + * the codec uses. */ - div = (clk_get_rate(info->mclk) / params_rate(params)) * - params_channels(params); - for (sdiv = 2; sdiv <= 4; sdiv += 2) - for (lrdiv = 32; lrdiv <= 128; lrdiv <<= 1) - if (sdiv * lrdiv == div) { - found = 1; - goto out; - } -out: - if (!found) - return -EINVAL; + div = clk_get_rate(info->mclk) / params_rate(params); + sdiv = 4; + if (div > (256 + 512) / 2) { + lrdiv = 128; + } else { + lrdiv = 64; + if (div < (128 + 256) / 2) + sdiv = 2; + } err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv); if (err) @@ -312,10 +313,12 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai) struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); if (!dai->active) - return; + return 0; ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK); ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE); + + return 0; } static int ep93xx_i2s_resume(struct snd_soc_dai *dai) @@ -323,10 +326,12 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai) struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); if (!dai->active) - return; + return 0; ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); + + return 0; } #else #define ep93xx_i2s_suspend NULL @@ -341,9 +346,7 @@ static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { .set_fmt = ep93xx_i2s_set_dai_fmt, }; -#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) +#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver ep93xx_i2s_dai = { .symmetric_rates= 1, @@ -352,13 +355,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = EP93XX_I2S_FORMATS, }, .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = EP93XX_I2S_FORMATS, }, .ops = &ep93xx_i2s_dai_ops, diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 2f121ddbe4bb..a456e491155f 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_8000_192000, .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_48000, + .rate_max = SNDRV_PCM_RATE_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | diff --git a/sound/soc/ep93xx/snappercl15.c b/sound/soc/ep93xx/snappercl15.c index 28ab5ff772ac..dfe1d7f74ea6 100644 --- a/sound/soc/ep93xx/snappercl15.c +++ b/sound/soc/ep93xx/snappercl15.c @@ -15,7 +15,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -79,11 +78,12 @@ static const struct snd_soc_dapm_route audio_map[] = { static int snappercl15_tlv320aic23_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_new_controls(codec, tlv320aic23_dapm_widgets, + snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets, ARRAY_SIZE(tlv320aic23_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 4cf98c03af22..15dac0f20cd8 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -896,8 +896,7 @@ static struct snd_pcm_ops fsl_dma_ops = { .pointer = fsl_dma_pointer, }; -static int __devinit fsl_soc_dma_probe(struct platform_device *pdev, - const struct of_device_id *match) +static int __devinit fsl_soc_dma_probe(struct platform_device *pdev) { struct dma_object *dma; struct device_node *np = pdev->dev.of_node; @@ -979,7 +978,7 @@ static const struct of_device_id fsl_soc_dma_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_soc_dma_ids); -static struct of_platform_driver fsl_soc_dma_driver = { +static struct platform_driver fsl_soc_dma_driver = { .driver = { .name = "fsl-pcm-audio", .owner = THIS_MODULE, @@ -993,12 +992,12 @@ static int __init fsl_soc_dma_init(void) { pr_info("Freescale Elo DMA ASoC PCM Driver\n"); - return of_register_platform_driver(&fsl_soc_dma_driver); + return platform_driver_register(&fsl_soc_dma_driver); } static void __exit fsl_soc_dma_exit(void) { - of_unregister_platform_driver(&fsl_soc_dma_driver); + platform_driver_unregister(&fsl_soc_dma_driver); } module_init(fsl_soc_dma_init); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 4cc167a7aeb8..313e0ccedd5b 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -624,8 +624,7 @@ static void make_lowercase(char *s) } } -static int __devinit fsl_ssi_probe(struct platform_device *pdev, - const struct of_device_id *match) +static int __devinit fsl_ssi_probe(struct platform_device *pdev) { struct fsl_ssi_private *ssi_private; int ret = 0; @@ -774,7 +773,7 @@ static const struct of_device_id fsl_ssi_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_ssi_ids); -static struct of_platform_driver fsl_ssi_driver = { +static struct platform_driver fsl_ssi_driver = { .driver = { .name = "fsl-ssi-dai", .owner = THIS_MODULE, @@ -788,12 +787,12 @@ static int __init fsl_ssi_init(void) { printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); - return of_register_platform_driver(&fsl_ssi_driver); + return platform_driver_register(&fsl_ssi_driver); } static void __exit fsl_ssi_exit(void) { - of_unregister_platform_driver(&fsl_ssi_driver); + platform_driver_unregister(&fsl_ssi_driver); } module_init(fsl_ssi_init); diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index f92dca07cd35..fff695ccdd3e 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -368,8 +368,7 @@ static struct snd_soc_platform_driver mpc5200_audio_dma_platform = { .pcm_free = &psc_dma_free, }; -static int mpc5200_hpcd_probe(struct of_device *op, - const struct of_device_id *match) +static int mpc5200_hpcd_probe(struct of_device *op) { phys_addr_t fifo; struct psc_dma *psc_dma; @@ -511,32 +510,31 @@ static int mpc5200_hpcd_remove(struct of_device *op) } static struct of_device_id mpc5200_hpcd_match[] = { - { - .compatible = "fsl,mpc5200-pcm", - }, + { .compatible = "fsl,mpc5200-pcm", }, {} }; MODULE_DEVICE_TABLE(of, mpc5200_hpcd_match); -static struct of_platform_driver mpc5200_hpcd_of_driver = { - .owner = THIS_MODULE, - .name = "mpc5200-pcm-audio", - .match_table = mpc5200_hpcd_match, +static struct platform_driver mpc5200_hpcd_of_driver = { .probe = mpc5200_hpcd_probe, .remove = mpc5200_hpcd_remove, + .dev = { + .owner = THIS_MODULE, + .name = "mpc5200-pcm-audio", + .of_match_table = mpc5200_hpcd_match, + } }; static int __init mpc5200_hpcd_init(void) { - return of_register_platform_driver(&mpc5200_hpcd_of_driver); + return platform_driver_register(&mpc5200_hpcd_of_driver); } +module_init(mpc5200_hpcd_init); static void __exit mpc5200_hpcd_exit(void) { - of_unregister_platform_driver(&mpc5200_hpcd_of_driver); + platform_driver_unregister(&mpc5200_hpcd_of_driver); } - -module_init(mpc5200_hpcd_init); module_exit(mpc5200_hpcd_exit); MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 40acc8e2b1ca..ad36b095bb79 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -272,8 +272,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = { * - Probe/remove operations * - OF device match table */ -static int __devinit psc_ac97_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit psc_ac97_of_probe(struct platform_device *op) { int rc; struct snd_ac97 ac97; @@ -316,7 +315,7 @@ static struct of_device_id psc_ac97_match[] __devinitdata = { }; MODULE_DEVICE_TABLE(of, psc_ac97_match); -static struct of_platform_driver psc_ac97_driver = { +static struct platform_driver psc_ac97_driver = { .probe = psc_ac97_of_probe, .remove = __devexit_p(psc_ac97_of_remove), .driver = { @@ -332,13 +331,13 @@ static struct of_platform_driver psc_ac97_driver = { */ static int __init psc_ac97_init(void) { - return of_register_platform_driver(&psc_ac97_driver); + return platform_driver_register(&psc_ac97_driver); } module_init(psc_ac97_init); static void __exit psc_ac97_exit(void) { - of_unregister_platform_driver(&psc_ac97_driver); + platform_driver_unregister(&psc_ac97_driver); } module_exit(psc_ac97_exit); diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9018fa5bf0db..87cf2a5c2b2c 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -150,8 +150,7 @@ static struct snd_soc_dai_driver psc_i2s_dai[] = {{ * - Probe/remove operations * - OF device match table */ -static int __devinit psc_i2s_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit psc_i2s_of_probe(struct platform_device *op) { int rc; struct psc_dma *psc_dma; @@ -213,7 +212,7 @@ static struct of_device_id psc_i2s_match[] __devinitdata = { }; MODULE_DEVICE_TABLE(of, psc_i2s_match); -static struct of_platform_driver psc_i2s_driver = { +static struct platform_driver psc_i2s_driver = { .probe = psc_i2s_of_probe, .remove = __devexit_p(psc_i2s_of_remove), .driver = { @@ -229,13 +228,13 @@ static struct of_platform_driver psc_i2s_driver = { */ static int __init psc_i2s_init(void) { - return of_register_platform_driver(&psc_i2s_driver); + return platform_driver_register(&psc_i2s_driver); } module_init(psc_i2s_init); static void __exit psc_i2s_exit(void) { - of_unregister_platform_driver(&psc_i2s_driver); + platform_driver_unregister(&psc_i2s_driver); } module_exit(psc_i2s_exit); diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 7d7847a1e66b..c16c6b2eff95 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -53,9 +53,8 @@ struct mpc8610_hpcd_data { * * Here we program the DMACR and PMUXCR registers. */ -static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; @@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 026b756961e0..66e0b68af147 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -85,9 +85,8 @@ struct machine_data { * * Here we program the DMACR and PMUXCR registers. */ -static int p1022_ds_machine_probe(struct platform_device *sound_device) +static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; @@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int p1022_ds_machine_remove(struct platform_device *sound_device) +static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 642270a635ea..d8f130d39dd9 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -30,6 +30,16 @@ config SND_MXC_SOC_WM1133_EV1 Enable support for audio on the i.MX31ADS with the WM1133-EV1 PMIC board with WM8835x fitted. +config SND_SOC_MX27VIS_AIC32X4 + tristate "SoC audio support for Visstrim M10 boards" + depends on MACH_IMX27_VISSTRIM_M10 + select SND_SOC_TVL320AIC32X4 + select SND_MXC_SOC_SSI + select SND_MXC_SOC_MX2 + help + Say Y if you want to add support for SoC audio on Visstrim SM10 + board with TLV320AIC32X4 codec. + config SND_SOC_PHYCORE_AC97 tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" depends on MACH_PCM043 || MACH_PCA100 @@ -44,7 +54,8 @@ config SND_SOC_EUKREA_TLV320 tristate "Eukrea TLV320" depends on MACH_EUKREA_MBIMX27_BASEBOARD \ || MACH_EUKREA_MBIMXSD25_BASEBOARD \ - || MACH_EUKREA_MBIMXSD35_BASEBOARD + || MACH_EUKREA_MBIMXSD35_BASEBOARD \ + || MACH_EUKREA_MBIMXSD51_BASEBOARD select SND_SOC_TLV320AIC23 select SND_MXC_SOC_SSI select SND_MXC_SOC_FIQ diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index b67fc02a4ecc..d6d609ba7e24 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -10,8 +10,10 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o +snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o +obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c index dd4fffdbd177..75fb4b83548b 100644 --- a/sound/soc/imx/eukrea-tlv320.c +++ b/sound/soc/imx/eukrea-tlv320.c @@ -22,7 +22,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include "../codecs/tlv320aic23.h" @@ -80,7 +79,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "imx-pcm-audio.0", + .platform_name = "imx-fiq-pcm-audio.0", .codec_name = "tlv320aic23-codec.0-001a", .cpu_dai_name = "imx-ssi.0", .ops = &eukrea_tlv320_snd_ops, @@ -99,7 +98,8 @@ static int __init eukrea_tlv320_init(void) int ret; if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd() - && !machine_is_eukrea_cpuimx35sd()) + && !machine_is_eukrea_cpuimx35sd() + && !machine_is_eukrea_cpuimx51sd()) /* return happy. We might run on a totally different machine */ return 0; diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 671ef8dd524c..aab7765f401a 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -110,12 +110,12 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, slave_config.direction = DMA_TO_DEVICE; slave_config.dst_addr = dma_params->dma_addr; slave_config.dst_addr_width = buswidth; - slave_config.dst_maxburst = dma_params->burstsize; + slave_config.dst_maxburst = dma_params->burstsize * buswidth; } else { slave_config.direction = DMA_FROM_DEVICE; slave_config.src_addr = dma_params->dma_addr; slave_config.src_addr_width = buswidth; - slave_config.src_maxburst = dma_params->burstsize; + slave_config.src_maxburst = dma_params->burstsize * buswidth; } ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config); @@ -303,6 +303,11 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = { static int __devinit imx_soc_platform_probe(struct platform_device *pdev) { + struct imx_ssi *ssi = platform_get_drvdata(pdev); + + ssi->dma_params_tx.burstsize = 6; + ssi->dma_params_rx.burstsize = 4; + return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); } diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 390b6ffc2658..ac2ded969253 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -16,7 +16,7 @@ * sane processor vendors have a FIFO per AC97 slot, the i.MX has only * one FIFO which combines all valid receive slots. We cannot even select * which slots we want to receive. The WM9712 with which this driver - * was developped with always sends GPIO status data in slot 12 which + * was developed with always sends GPIO status data in slot 12 which * we receive in our (PCM-) data stream. The only chance we have is to * manually skip this data in the FIQ handler. With sampling rates different * from 48000Hz not every frame has valid receive data, so the ratio @@ -108,7 +108,7 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TFSL; + strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ @@ -456,13 +456,13 @@ static int imx_ssi_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver imx_ssi_dai = { .probe = imx_ssi_dai_probe, .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, .formats = SNDRV_PCM_FMTBIT_S16_LE, @@ -656,6 +656,9 @@ static int imx_ssi_probe(struct platform_device *pdev) ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; + ssi->dma_params_tx.burstsize = 4; + ssi->dma_params_rx.burstsize = 4; + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); if (res) ssi->dma_params_tx.dma = res->start; diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h index a4406a134892..dc8a87530e3e 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/imx/imx-ssi.h @@ -234,7 +234,4 @@ void imx_pcm_free(struct snd_pcm *pcm); */ #define IMX_SSI_DMABUF_SIZE (64 * 1024) -#define DMA_RXFIFO_BURST 0x4 -#define DMA_TXFIFO_BURST 0x6 - #endif /* _IMX_SSI_H */ diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/imx/mx27vis-aic32x4.c new file mode 100644 index 000000000000..054110b91d42 --- /dev/null +++ b/sound/soc/imx/mx27vis-aic32x4.c @@ -0,0 +1,137 @@ +/* + * mx27vis-aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin <javier.martin@vista-silicon.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. + * + * 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 Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h> +#include <mach/audmux.h> + +#include "../codecs/tlv320aic32x4.h" +#include "imx-ssi.h" + +static int mx27vis_aic32x4_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; + u32 dai_format; + + dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + /* set codec DAI configuration */ + snd_soc_dai_set_fmt(codec_dai, dai_format); + + /* set cpu DAI configuration */ + snd_soc_dai_set_fmt(cpu_dai, dai_format); + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + 25000000, SND_SOC_CLOCK_OUT); + if (ret) { + pr_err("%s: failed setting codec sysclk\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops mx27vis_aic32x4_snd_ops = { + .hw_params = mx27vis_aic32x4_hw_params, +}; + +static struct snd_soc_dai_link mx27vis_aic32x4_dai = { + .name = "tlv320aic32x4", + .stream_name = "TLV320AIC32X4", + .codec_dai_name = "tlv320aic32x4-hifi", + .platform_name = "imx-pcm-audio.0", + .codec_name = "tlv320aic32x4.0-0018", + .cpu_dai_name = "imx-ssi.0", + .ops = &mx27vis_aic32x4_snd_ops, +}; + +static struct snd_soc_card mx27vis_aic32x4 = { + .name = "visstrim_m10-audio", + .dai_link = &mx27vis_aic32x4_dai, + .num_links = 1, +}; + +static struct platform_device *mx27vis_aic32x4_snd_device; + +static int __init mx27vis_aic32x4_init(void) +{ + int ret; + + mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1); + if (!mx27vis_aic32x4_snd_device) + return -ENOMEM; + + platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4); + ret = platform_device_add(mx27vis_aic32x4_snd_device); + + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + platform_device_put(mx27vis_aic32x4_snd_device); + } + + /* Connect SSI0 as clock slave to SSI1 external pins */ + mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + MXC_AUDMUX_V1_PCR_SYN | + MXC_AUDMUX_V1_PCR_TFSDIR | + MXC_AUDMUX_V1_PCR_TCLKDIR | + MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | + MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) + ); + mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, + MXC_AUDMUX_V1_PCR_SYN | + MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + ); + + return ret; +} + +static void __exit mx27vis_aic32x4_exit(void) +{ + platform_device_unregister(mx27vis_aic32x4_snd_device); +} + +module_init(mx27vis_aic32x4_init); +module_exit(mx27vis_aic32x4_exit); + +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/imx/phycore-ac97.c index 9eabc28667e6..a7deb5cb2433 100644 --- a/sound/soc/imx/phycore-ac97.c +++ b/sound/soc/imx/phycore-ac97.c @@ -17,7 +17,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> static struct snd_soc_card imx_phycore; diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/imx/wm1133-ev1.c index 30fdb15065be..75b4c72787e2 100644 --- a/sound/soc/imx/wm1133-ev1.c +++ b/sound/soc/imx/wm1133-ev1.c @@ -19,7 +19,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <mach/audmux.h> @@ -213,11 +212,12 @@ static struct snd_soc_jack_pin mic_jack_pins[] = { static int wm1133_ev1_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_new_controls(codec, wm1133_ev1_widgets, + snd_soc_dapm_new_controls(dapm, wm1133_ev1_widgets, ARRAY_SIZE(wm1133_ev1_widgets)); - snd_soc_dapm_add_routes(codec, wm1133_ev1_map, + snd_soc_dapm_add_routes(dapm, wm1133_ev1_map, ARRAY_SIZE(wm1133_ev1_map)); /* Headphone jack detection */ @@ -234,7 +234,7 @@ static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE, SND_JACK_BTN_0); - snd_soc_dapm_force_enable_pin(codec, "Mic Bias"); + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); return 0; } diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index f3cffd183401..419bf4f5534a 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -28,7 +28,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include "jz4740-i2s.h" diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c index ef1a99e6a3bd..49723e3e7e38 100644 --- a/sound/soc/jz4740/qi_lb60.c +++ b/sound/soc/jz4740/qi_lb60.c @@ -19,7 +19,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <linux/gpio.h> #define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29) @@ -59,10 +58,11 @@ static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - snd_soc_dapm_nc_pin(codec, "LIN"); - snd_soc_dapm_nc_pin(codec, "RIN"); + snd_soc_dapm_nc_pin(dapm, "LIN"); + snd_soc_dapm_nc_pin(dapm, "RIN"); ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT); if (ret < 0) { @@ -70,9 +70,11 @@ static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd) return ret; } - snd_soc_dapm_new_controls(codec, qi_lb60_widgets, ARRAY_SIZE(qi_lb60_widgets)); - snd_soc_dapm_add_routes(codec, qi_lb60_routes, ARRAY_SIZE(qi_lb60_routes)); - snd_soc_dapm_sync(codec); + 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; } diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index 16ec2a2dba4d..8f49e165f4d1 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -11,10 +11,19 @@ config SND_KIRKWOOD_SOC_I2S config SND_KIRKWOOD_SOC_OPENRD tristate "SoC Audio support for Kirkwood Openrd Client" - depends on SND_KIRKWOOD_SOC && MACH_OPENRD_CLIENT + depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE) select SND_KIRKWOOD_SOC_I2S select SND_SOC_CS42L51 help Say Y if you want to add support for SoC audio on Openrd Client. +config SND_KIRKWOOD_SOC_T5325 + tristate "SoC Audio support for HP t5325" + depends on SND_KIRKWOOD_SOC && MACH_T5325 + select SND_KIRKWOOD_SOC_I2S + select SND_SOC_ALC5623 + help + Say Y if you want to add support for SoC audio on + the HP t5325 thin client. + diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile index 33a16dcab5b5..3e62ae9e7bbe 100644 --- a/sound/soc/kirkwood/Makefile +++ b/sound/soc/kirkwood/Makefile @@ -5,5 +5,7 @@ obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o snd-soc-openrd-objs := kirkwood-openrd.o +snd-soc-t5325-objs := kirkwood-t5325.o obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o +obj-$(CONFIG_SND_KIRKWOOD_SOC_T5325) += snd-soc-t5325.o diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 0fd6a630db01..e13c6ce46328 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -132,7 +132,7 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) priv = snd_soc_dai_get_dma_data(cpu_dai, substream); snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw); - /* Ensure that all constraints linked to dma burst are fullfilled */ + /* Ensure that all constraints linked to dma burst are fulfilled */ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, priv->burst * 2, @@ -170,7 +170,7 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) /* * Enable Error interrupts. We're only ack'ing them but - * it's usefull for diagnostics + * it's useful for diagnostics */ writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK); } diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c index 9d7c81e921f1..d863afb3ee52 100644 --- a/sound/soc/kirkwood/kirkwood-openrd.c +++ b/sound/soc/kirkwood/kirkwood-openrd.c @@ -86,7 +86,7 @@ static int __init openrd_client_init(void) { int ret; - if (!machine_is_openrd_client()) + if (!machine_is_openrd_client() && !machine_is_openrd_ultimate()) return 0; openrd_client_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c new file mode 100644 index 000000000000..c8d21956ab52 --- /dev/null +++ b/sound/soc/kirkwood/kirkwood-t5325.c @@ -0,0 +1,141 @@ +/* + * kirkwood-t5325.c + * + * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> + * + * 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/interrupt.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <mach/kirkwood.h> +#include <plat/audio.h> +#include <asm/mach-types.h> +#include "../codecs/alc5623.h" + +static int t5325_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; + unsigned int freq, fmt; + + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) + return ret; + + freq = params_rate(params) * 256; + + return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN); + +} + +static struct snd_soc_ops t5325_ops = { + .hw_params = t5325_hw_params, +}; + +static const struct snd_soc_dapm_widget t5325_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_soc_dapm_route t5325_route[] = { + { "Headphone Jack", NULL, "HPL" }, + { "Headphone Jack", NULL, "HPR" }, + + {"Speaker", NULL, "SPKOUT"}, + {"Speaker", NULL, "SPKOUTN"}, + + { "MIC1", NULL, "Mic Jack" }, + { "MIC2", NULL, "Mic Jack" }, +}; + +static int t5325_dai_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_new_controls(dapm, t5325_dapm_widgets, + ARRAY_SIZE(t5325_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, t5325_route, ARRAY_SIZE(t5325_route)); + + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static struct snd_soc_dai_link t5325_dai[] = { +{ + .name = "ALC5621", + .stream_name = "ALC5621 HiFi", + .cpu_dai_name = "kirkwood-i2s", + .platform_name = "kirkwood-pcm-audio", + .codec_dai_name = "alc5621-hifi", + .codec_name = "alc562x-codec.0-001a", + .ops = &t5325_ops, + .init = t5325_dai_init, +}, +}; + + +static struct snd_soc_card t5325 = { + .name = "t5325", + .dai_link = t5325_dai, + .num_links = ARRAY_SIZE(t5325_dai), +}; + +static struct platform_device *t5325_snd_device; + +static int __init t5325_init(void) +{ + int ret; + + if (!machine_is_t5325()) + return 0; + + t5325_snd_device = platform_device_alloc("soc-audio", -1); + if (!t5325_snd_device) + return -ENOMEM; + + platform_set_drvdata(t5325_snd_device, + &t5325); + + ret = platform_device_add(t5325_snd_device); + if (ret) { + printk(KERN_ERR "%s: platform_device_add failed\n", __func__); + platform_device_put(t5325_snd_device); + } + + return ret; +} +module_init(t5325_init); + +static void __exit t5325_exit(void) +{ + platform_device_unregister(t5325_snd_device); +} +module_exit(t5325_exit); + +MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); +MODULE_DESCRIPTION("ALSA SoC t5325 audio client"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig new file mode 100644 index 000000000000..29350428f1c2 --- /dev/null +++ b/sound/soc/mid-x86/Kconfig @@ -0,0 +1,14 @@ +config SND_MFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Medfield MID platform" + depends on INTEL_SCU_IPC + depends on SND_INTEL_SST + select SND_SOC_SN95031 + select SND_SST_PLATFORM + help + This adds support for ASoC machine driver for Intel(R) MID Medfield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". + +config SND_SST_PLATFORM + tristate diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile new file mode 100644 index 000000000000..639883339465 --- /dev/null +++ b/sound/soc/mid-x86/Makefile @@ -0,0 +1,5 @@ +snd-soc-sst-platform-objs := sst_platform.o +snd-soc-mfld-machine-objs := mfld_machine.o + +obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o +obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c new file mode 100644 index 000000000000..429aa1be2cff --- /dev/null +++ b/sound/soc/mid-x86/mfld_machine.c @@ -0,0 +1,452 @@ +/* + * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/sn95031.h" + +#define MID_MONO 1 +#define MID_STEREO 2 +#define MID_MAX_CAP 5 +#define MFLD_JACK_INSERT 0x04 + +enum soc_mic_bias_zones { + MFLD_MV_START = 0, + /* mic bias volutage range for Headphones*/ + MFLD_MV_HP = 400, + /* mic bias volutage range for American Headset*/ + MFLD_MV_AM_HS = 650, + /* mic bias volutage range for Headset*/ + MFLD_MV_HS = 2000, + MFLD_MV_UNDEFINED, +}; + +static unsigned int hs_switch; +static unsigned int lo_dac; + +struct mfld_mc_private { + struct platform_device *socdev; + void __iomem *int_base; + struct snd_soc_codec *codec; + u8 interrupt_status; +}; + +struct snd_soc_jack mfld_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin mfld_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "AMIC1", + .mask = SND_JACK_MICROPHONE, + }, +}; + +/* jack detection voltage zones */ +static struct snd_soc_jack_zone mfld_zones[] = { + {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, + {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, +}; + +/* sound card controls */ +static const char *headset_switch_text[] = {"Earpiece", "Headset"}; + +static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; + +static const struct soc_enum headset_enum = + SOC_ENUM_SINGLE_EXT(2, headset_switch_text); + +static const struct soc_enum lo_enum = + SOC_ENUM_SINGLE_EXT(4, lo_text); + +static int headset_get_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hs_switch; + return 0; +} + +static int headset_set_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.integer.value[0] == hs_switch) + return 0; + + if (ucontrol->value.integer.value[0]) { + pr_debug("hs_set HS path\n"); + snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + } else { + pr_debug("hs_set EP path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); + } + snd_soc_dapm_sync(&codec->dapm); + hs_switch = ucontrol->value.integer.value[0]; + + return 0; +} + +static void lo_enable_out_pins(struct snd_soc_codec *codec) +{ + snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL"); + snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR"); + snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL"); + snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR"); + snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); + snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); + if (hs_switch) { + snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + } else { + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); + } +} + +static int lo_get_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lo_dac; + return 0; +} + +static int lo_set_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.integer.value[0] == lo_dac) + return 0; + + /* we dont want to work with last state of lineout so just enable all + * pins and then disable pins not required + */ + lo_enable_out_pins(codec); + switch (ucontrol->value.integer.value[0]) { + case 0: + pr_debug("set vibra path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT"); + snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0); + break; + + case 1: + pr_debug("set hs path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); + break; + + case 2: + pr_debug("set spkr path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL"); + snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44); + break; + + case 3: + pr_debug("set null path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL"); + snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66); + break; + } + snd_soc_dapm_sync(&codec->dapm); + lo_dac = ucontrol->value.integer.value[0]; + return 0; +} + +static const struct snd_kcontrol_new mfld_snd_controls[] = { + SOC_ENUM_EXT("Playback Switch", headset_enum, + headset_get_switch, headset_set_switch), + SOC_ENUM_EXT("Lineout Mux", lo_enum, + lo_get_switch, lo_set_switch), +}; + +static const struct snd_soc_dapm_widget mfld_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static const struct snd_soc_dapm_route mfld_map[] = { + {"Headphones", NULL, "HPOUTR"}, + {"Headphones", NULL, "HPOUTL"}, + {"Mic", NULL, "AMIC1"}, +}; + +static void mfld_jack_check(unsigned int intr_status) +{ + struct mfld_jack_data jack_data; + + jack_data.mfld_jack = &mfld_jack; + jack_data.intr_id = intr_status; + + sn95031_jack_detection(&jack_data); + /* TODO: add american headset detection post gpiolib support */ +} + +static int mfld_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret_val; + + /* Add jack sense widgets */ + snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets)); + + /* Set up the map */ + snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map)); + + /* always connected */ + snd_soc_dapm_enable_pin(dapm, "Headphones"); + snd_soc_dapm_enable_pin(dapm, "Mic"); + snd_soc_dapm_sync(dapm); + + ret_val = snd_soc_add_controls(codec, mfld_snd_controls, + ARRAY_SIZE(mfld_snd_controls)); + if (ret_val) { + pr_err("soc_add_controls failed %d", ret_val); + return ret_val; + } + /* default is earpiece pin, userspace sets it explcitly */ + snd_soc_dapm_disable_pin(dapm, "Headphones"); + /* default is lineout NC, userspace sets it explcitly */ + snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); + snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); + lo_dac = 3; + hs_switch = 0; + /* we dont use linein in this so set to NC */ + snd_soc_dapm_disable_pin(dapm, "LINEINL"); + snd_soc_dapm_disable_pin(dapm, "LINEINR"); + snd_soc_dapm_sync(dapm); + + /* Headset and button jack detection */ + ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1, &mfld_jack); + if (ret_val) { + pr_err("jack creation failed\n"); + return ret_val; + } + + ret_val = snd_soc_jack_add_pins(&mfld_jack, + ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); + if (ret_val) { + pr_err("adding jack pins failed\n"); + return ret_val; + } + ret_val = snd_soc_jack_add_zones(&mfld_jack, + ARRAY_SIZE(mfld_zones), mfld_zones); + if (ret_val) { + pr_err("adding jack zones failed\n"); + return ret_val; + } + + /* we want to check if anything is inserted at boot, + * so send a fake event to codec and it will read adc + * to find if anything is there or not */ + mfld_jack_check(MFLD_JACK_INSERT); + return ret_val; +} + +struct snd_soc_dai_link mfld_msic_dailink[] = { + { + .name = "Medfield Headset", + .stream_name = "Headset", + .cpu_dai_name = "Headset-cpu-dai", + .codec_dai_name = "SN95031 Headset", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = mfld_init, + }, + { + .name = "Medfield Speaker", + .stream_name = "Speaker", + .cpu_dai_name = "Speaker-cpu-dai", + .codec_dai_name = "SN95031 Speaker", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = NULL, + }, + { + .name = "Medfield Vibra", + .stream_name = "Vibra1", + .cpu_dai_name = "Vibra1-cpu-dai", + .codec_dai_name = "SN95031 Vibra1", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = NULL, + }, + { + .name = "Medfield Haptics", + .stream_name = "Vibra2", + .cpu_dai_name = "Vibra2-cpu-dai", + .codec_dai_name = "SN95031 Vibra2", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = NULL, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_mfld = { + .name = "medfield_audio", + .dai_link = mfld_msic_dailink, + .num_links = ARRAY_SIZE(mfld_msic_dailink), +}; + +static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) +{ + struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; + + memcpy_fromio(&mc_private->interrupt_status, + ((void *)(mc_private->int_base)), + sizeof(u8)); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t snd_mfld_jack_detection(int irq, void *data) +{ + struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; + + if (mfld_jack.codec == NULL) + return IRQ_HANDLED; + mfld_jack_check(mc_drv_ctx->interrupt_status); + + return IRQ_HANDLED; +} + +static int __devinit snd_mfld_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0, irq; + struct mfld_mc_private *mc_drv_ctx; + struct resource *irq_mem; + + pr_debug("snd_mfld_mc_probe called\n"); + + /* retrive the irq number */ + irq = platform_get_irq(pdev, 0); + + /* audio interrupt base of SRAM location where + * interrupts are stored by System FW */ + mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC); + if (!mc_drv_ctx) { + pr_err("allocation failed\n"); + return -ENOMEM; + } + + irq_mem = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "IRQ_BASE"); + if (!irq_mem) { + pr_err("no mem resource given\n"); + ret_val = -ENODEV; + goto unalloc; + } + mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start, + resource_size(irq_mem)); + if (!mc_drv_ctx->int_base) { + pr_err("Mapping of cache failed\n"); + ret_val = -ENOMEM; + goto unalloc; + } + /* register for interrupt */ + ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler, + snd_mfld_jack_detection, + IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); + if (ret_val) { + pr_err("cannot register IRQ\n"); + goto unalloc; + } + /* register the soc card */ + snd_soc_card_mfld.dev = &pdev->dev; + ret_val = snd_soc_register_card(&snd_soc_card_mfld); + if (ret_val) { + pr_debug("snd_soc_register_card failed %d\n", ret_val); + goto freeirq; + } + platform_set_drvdata(pdev, mc_drv_ctx); + pr_debug("successfully exited probe\n"); + return ret_val; + +freeirq: + free_irq(irq, mc_drv_ctx); +unalloc: + kfree(mc_drv_ctx); + return ret_val; +} + +static int __devexit snd_mfld_mc_remove(struct platform_device *pdev) +{ + struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev); + + pr_debug("snd_mfld_mc_remove called\n"); + free_irq(platform_get_irq(pdev, 0), mc_drv_ctx); + snd_soc_unregister_card(&snd_soc_card_mfld); + kfree(mc_drv_ctx); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_mfld_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "msic_audio", + }, + .probe = snd_mfld_mc_probe, + .remove = __devexit_p(snd_mfld_mc_remove), +}; + +static int __init snd_mfld_driver_init(void) +{ + pr_debug("snd_mfld_driver_init called\n"); + return platform_driver_register(&snd_mfld_mc_driver); +} +module_init(snd_mfld_driver_init); + +static void __exit snd_mfld_driver_exit(void) +{ + pr_debug("snd_mfld_driver_exit called\n"); + platform_driver_unregister(&snd_mfld_mc_driver); +} +module_exit(snd_mfld_driver_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:msic-audio"); diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c new file mode 100644 index 000000000000..d567c322a2fb --- /dev/null +++ b/sound/soc/mid-x86/sst_platform.c @@ -0,0 +1,476 @@ +/* + * sst_platform.c - Intel MID Platform driver + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <linux/io.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h" +#include "../../../drivers/staging/intel_sst/intel_sst.h" +#include "sst_platform.h" + +static struct snd_pcm_hardware sst_platform_pcm_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP| + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), + .rates = (SNDRV_PCM_RATE_8000| + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = SST_MIN_RATE, + .rate_max = SST_MAX_RATE, + .channels_min = SST_MIN_CHANNEL, + .channels_max = SST_MAX_CHANNEL, + .buffer_bytes_max = SST_MAX_BUFFER, + .period_bytes_min = SST_MIN_PERIOD_BYTES, + .period_bytes_max = SST_MAX_PERIOD_BYTES, + .periods_min = SST_MIN_PERIODS, + .periods_max = SST_MAX_PERIODS, + .fifo_size = SST_FIFO_SIZE, +}; + +/* MFLD - MSIC */ +struct snd_soc_dai_driver sst_platform_dai[] = { +{ + .name = "Headset-cpu-dai", + .id = 0, + .playback = { + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 5, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Speaker-cpu-dai", + .id = 1, + .playback = { + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Vibra1-cpu-dai", + .id = 2, + .playback = { + .channels_min = SST_MONO, + .channels_max = SST_MONO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Vibra2-cpu-dai", + .id = 3, + .playback = { + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +}; + +/* helper functions */ +static inline void sst_set_stream_status(struct sst_runtime_stream *stream, + int state) +{ + unsigned long flags; + spin_lock_irqsave(&stream->status_lock, flags); + stream->stream_status = state; + spin_unlock_irqrestore(&stream->status_lock, flags); +} + +static inline int sst_get_stream_status(struct sst_runtime_stream *stream) +{ + int state; + unsigned long flags; + + spin_lock_irqsave(&stream->status_lock, flags); + state = stream->stream_status; + spin_unlock_irqrestore(&stream->status_lock, flags); + return state; +} + +static void sst_fill_pcm_params(struct snd_pcm_substream *substream, + struct snd_sst_stream_params *param) +{ + + param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM; + param->uc.pcm_params.num_chan = (u8) substream->runtime->channels; + param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; + param->uc.pcm_params.reserved = 0; + param->uc.pcm_params.sfreq = substream->runtime->rate; + param->uc.pcm_params.ring_buffer_size = + snd_pcm_lib_buffer_bytes(substream); + param->uc.pcm_params.period_count = substream->runtime->period_size; + param->uc.pcm_params.ring_buffer_addr = + virt_to_phys(substream->dma_buffer.area); + pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count); + pr_debug("sfreq= %d, wd_sz = %d\n", + param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz); +} + +static int sst_platform_alloc_stream(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = + substream->runtime->private_data; + struct snd_sst_stream_params param = {{{0,},},}; + struct snd_sst_params str_params = {0}; + int ret_val; + + /* set codec params and inform SST driver the same */ + sst_fill_pcm_params(substream, ¶m); + substream->runtime->dma_area = substream->dma_buffer.area; + str_params.sparams = param; + str_params.codec = param.uc.pcm_params.codec; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + str_params.ops = STREAM_OPS_PLAYBACK; + str_params.device_type = substream->pcm->device + 1; + pr_debug("Playbck stream,Device %d\n", + substream->pcm->device); + } else { + str_params.ops = STREAM_OPS_CAPTURE; + str_params.device_type = SND_SST_DEVICE_CAPTURE; + pr_debug("Capture stream,Device %d\n", + substream->pcm->device); + } + ret_val = stream->sstdrv_ops->pcm_control->open(&str_params); + pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val); + if (ret_val < 0) + return ret_val; + + stream->stream_info.str_id = ret_val; + pr_debug("str id : %d\n", stream->stream_info.str_id); + return ret_val; +} + +static void sst_period_elapsed(void *mad_substream) +{ + struct snd_pcm_substream *substream = mad_substream; + struct sst_runtime_stream *stream; + int status; + + if (!substream || !substream->runtime) + return; + stream = substream->runtime->private_data; + if (!stream) + return; + status = sst_get_stream_status(stream); + if (status != SST_PLATFORM_RUNNING) + return; + snd_pcm_period_elapsed(substream); +} + +static int sst_platform_init_stream(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = + substream->runtime->private_data; + int ret_val; + + pr_debug("setting buffer ptr param\n"); + sst_set_stream_status(stream, SST_PLATFORM_INIT); + stream->stream_info.period_elapsed = sst_period_elapsed; + stream->stream_info.mad_substream = substream; + stream->stream_info.buffer_ptr = 0; + stream->stream_info.sfreq = substream->runtime->rate; + ret_val = stream->sstdrv_ops->pcm_control->device_control( + SST_SND_STREAM_INIT, &stream->stream_info); + if (ret_val) + pr_err("control_set ret error %d\n", ret_val); + return ret_val; + +} +/* end -- helper functions */ + +static int sst_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct sst_runtime_stream *stream; + int ret_val = 0; + + pr_debug("sst_platform_open called\n"); + runtime = substream->runtime; + runtime->hw = sst_platform_pcm_hw; + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + spin_lock_init(&stream->status_lock); + stream->stream_info.str_id = 0; + sst_set_stream_status(stream, SST_PLATFORM_INIT); + stream->stream_info.mad_substream = substream; + /* allocate memory for SST API set */ + stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops), + GFP_KERNEL); + if (!stream->sstdrv_ops) { + pr_err("sst: mem allocation for ops fail\n"); + kfree(stream); + return -ENOMEM; + } + stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID; + /* 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"); + return ret_val; + } + runtime->private_data = stream; + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +} + +static int sst_platform_close(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + pr_debug("sst_platform_close called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + if (str_id) + ret_val = stream->sstdrv_ops->pcm_control->close(str_id); + kfree(stream->sstdrv_ops); + kfree(stream); + return ret_val; +} + +static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + pr_debug("sst_platform_pcm_prepare called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + if (stream->stream_info.str_id) { + ret_val = stream->sstdrv_ops->pcm_control->device_control( + SST_SND_DROP, &str_id); + return ret_val; + } + + ret_val = sst_platform_alloc_stream(substream); + if (ret_val < 0) + return ret_val; + snprintf(substream->pcm->id, sizeof(substream->pcm->id), + "%d", stream->stream_info.str_id); + + ret_val = sst_platform_init_stream(substream); + if (ret_val) + return ret_val; + substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; + return ret_val; +} + +static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + int ret_val = 0, str_id; + struct sst_runtime_stream *stream; + int str_cmd, status; + + pr_debug("sst_platform_pcm_trigger called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("sst: Trigger Start\n"); + str_cmd = SST_SND_START; + status = SST_PLATFORM_RUNNING; + stream->stream_info.mad_substream = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("sst: in stop\n"); + str_cmd = SST_SND_DROP; + status = SST_PLATFORM_DROPPED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("sst: in pause\n"); + str_cmd = SST_SND_PAUSE; + status = SST_PLATFORM_PAUSED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("sst: in pause release\n"); + str_cmd = SST_SND_RESUME; + status = SST_PLATFORM_RUNNING; + break; + default: + return -EINVAL; + } + ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd, + &str_id); + if (!ret_val) + sst_set_stream_status(stream, status); + + return ret_val; +} + + +static snd_pcm_uframes_t sst_platform_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val, status; + struct pcm_stream_info *str_info; + + stream = substream->runtime->private_data; + status = sst_get_stream_status(stream); + if (status == SST_PLATFORM_INIT) + return 0; + str_info = &stream->stream_info; + ret_val = stream->sstdrv_ops->pcm_control->device_control( + SST_SND_BUFFER_POINTER, str_info); + if (ret_val) { + pr_err("sst: error code = %d\n", ret_val); + return ret_val; + } + return stream->stream_info.buffer_ptr; +} + +static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + + return 0; +} + +static struct snd_pcm_ops sst_platform_ops = { + .open = sst_platform_open, + .close = sst_platform_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = sst_platform_pcm_prepare, + .trigger = sst_platform_pcm_trigger, + .pointer = sst_platform_pcm_pointer, + .hw_params = sst_platform_pcm_hw_params, +}; + +static void sst_pcm_free(struct snd_pcm *pcm) +{ + pr_debug("sst_pcm_free called\n"); + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + int retval = 0; + + pr_debug("sst_pcm_new called\n"); + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + SST_MIN_BUFFER, SST_MAX_BUFFER); + if (retval) { + pr_err("dma buffer allocationf fail\n"); + return retval; + } + } + return retval; +} +struct snd_soc_platform_driver sst_soc_platform_drv = { + .ops = &sst_platform_ops, + .pcm_new = sst_pcm_new, + .pcm_free = sst_pcm_free, +}; + +static int sst_platform_probe(struct platform_device *pdev) +{ + int ret; + + pr_debug("sst_platform_probe called\n"); + ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); + if (ret) { + pr_err("registering soc platform failed\n"); + return ret; + } + + ret = snd_soc_register_dais(&pdev->dev, + sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); + if (ret) { + pr_err("registering cpu dais failed\n"); + snd_soc_unregister_platform(&pdev->dev); + } + return ret; +} + +static int sst_platform_remove(struct platform_device *pdev) +{ + + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai)); + snd_soc_unregister_platform(&pdev->dev); + pr_debug("sst_platform_remove success\n"); + return 0; +} + +static struct platform_driver sst_platform_driver = { + .driver = { + .name = "sst-platform", + .owner = THIS_MODULE, + }, + .probe = sst_platform_probe, + .remove = sst_platform_remove, +}; + +static int __init sst_soc_platform_init(void) +{ + pr_debug("sst_soc_platform_init called\n"); + return platform_driver_register(&sst_platform_driver); +} +module_init(sst_soc_platform_init); + +static void __exit sst_soc_platform_exit(void) +{ + platform_driver_unregister(&sst_platform_driver); + pr_debug("sst_soc_platform_exit success\n"); +} +module_exit(sst_soc_platform_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sst-platform"); diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/mid-x86/sst_platform.h new file mode 100644 index 000000000000..df370286694f --- /dev/null +++ b/sound/soc/mid-x86/sst_platform.h @@ -0,0 +1,63 @@ +/* + * sst_platform.h - Intel MID Platform driver header file + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ + +#ifndef __SST_PLATFORMDRV_H__ +#define __SST_PLATFORMDRV_H__ + +#define SST_MONO 1 +#define SST_STEREO 2 +#define SST_MAX_CAP 5 + +#define SST_MIN_RATE 8000 +#define SST_MAX_RATE 48000 +#define SST_MIN_CHANNEL 1 +#define SST_MAX_CHANNEL 5 +#define SST_MAX_BUFFER (800*1024) +#define SST_MIN_BUFFER (800*1024) +#define SST_MIN_PERIOD_BYTES 32 +#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER +#define SST_MIN_PERIODS 2 +#define SST_MAX_PERIODS (1024*2) +#define SST_FIFO_SIZE 0 +#define SST_CARD_NAMES "intel_mid_card" +#define MSIC_VENDOR_ID 3 + +struct sst_runtime_stream { + int stream_status; + struct pcm_stream_info stream_info; + struct intel_sst_card_ops *sstdrv_ops; + spinlock_t status_lock; +}; + +enum sst_drv_status { + SST_PLATFORM_INIT = 1, + SST_PLATFORM_STARTED, + SST_PLATFORM_RUNNING, + SST_PLATFORM_PAUSED, + SST_PLATFORM_DROPPED, +}; + +#endif diff --git a/sound/soc/nuc900/nuc900-audio.c b/sound/soc/nuc900/nuc900-audio.c index 161f5b667d7b..38a2d0d883b5 100644 --- a/sound/soc/nuc900/nuc900-audio.c +++ b/sound/soc/nuc900/nuc900-audio.c @@ -18,7 +18,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "nuc900-audio.h" diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index a088db6d5091..b5922984eac6 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -24,6 +24,7 @@ config SND_OMAP_SOC_RX51 select OMAP_MCBSP select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X + select SND_SOC_TPA6130A2 help Say Y if you want to add support for SoC audio on Nokia RX-51 hardware. This is also known as Nokia N900 product. diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c index 979dd508305f..73dde4a1adc3 100644 --- a/sound/soc/omap/am3517evm.c +++ b/sound/soc/omap/am3517evm.c @@ -22,7 +22,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -114,20 +113,21 @@ static const struct snd_soc_dapm_route audio_map[] = { static int am3517evm_aic23_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; /* Add am3517-evm specific widgets */ - snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets, ARRAY_SIZE(tlv320aic23_dapm_widgets)); /* Set up davinci-evm specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* always connected */ - snd_soc_dapm_enable_pin(codec, "Line Out"); - snd_soc_dapm_enable_pin(codec, "Line In"); - snd_soc_dapm_enable_pin(codec, "Mic In"); + snd_soc_dapm_enable_pin(dapm, "Line Out"); + snd_soc_dapm_enable_pin(dapm, "Line In"); + snd_soc_dapm_enable_pin(dapm, "Mic In"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -139,7 +139,7 @@ static struct snd_soc_dai_link am3517evm_dai = { .cpu_dai_name ="omap-mcbsp-dai.0", .codec_dai_name = "tlv320aic23-hifi", .platform_name = "omap-pcm-audio", - .codec_name = "tlv320aic23-codec", + .codec_name = "tlv320aic23-codec.2-001a", .init = am3517evm_aic23_init, .ops = &am3517evm_ops, }; diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 438146addbb8..462cbcbea74a 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -26,7 +26,7 @@ #include <linux/spinlock.h> #include <linux/tty.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include <sound/jack.h> #include <asm/mach-types.h> @@ -94,6 +94,7 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = &codec->dapm; struct soc_enum *control = (struct soc_enum *)kcontrol->private_value; unsigned short pins; int pin, changed = 0; @@ -112,48 +113,48 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, /* Setup pins after corresponding bits if changed */ pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE)); - if (pin != snd_soc_dapm_get_pin_status(codec, "Mouthpiece")) { + if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(codec, "Mouthpiece"); + snd_soc_dapm_enable_pin(dapm, "Mouthpiece"); else - snd_soc_dapm_disable_pin(codec, "Mouthpiece"); + snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); } pin = !!(pins & (1 << AMS_DELTA_EARPIECE)); - if (pin != snd_soc_dapm_get_pin_status(codec, "Earpiece")) { + if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(codec, "Earpiece"); + snd_soc_dapm_enable_pin(dapm, "Earpiece"); else - snd_soc_dapm_disable_pin(codec, "Earpiece"); + snd_soc_dapm_disable_pin(dapm, "Earpiece"); } pin = !!(pins & (1 << AMS_DELTA_MICROPHONE)); - if (pin != snd_soc_dapm_get_pin_status(codec, "Microphone")) { + if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(codec, "Microphone"); + snd_soc_dapm_enable_pin(dapm, "Microphone"); else - snd_soc_dapm_disable_pin(codec, "Microphone"); + snd_soc_dapm_disable_pin(dapm, "Microphone"); } pin = !!(pins & (1 << AMS_DELTA_SPEAKER)); - if (pin != snd_soc_dapm_get_pin_status(codec, "Speaker")) { + if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(codec, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(codec, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "Speaker"); } pin = !!(pins & (1 << AMS_DELTA_AGC)); if (pin != ams_delta_audio_agc) { ams_delta_audio_agc = pin; changed = 1; if (pin) - snd_soc_dapm_enable_pin(codec, "AGCIN"); + snd_soc_dapm_enable_pin(dapm, "AGCIN"); else - snd_soc_dapm_disable_pin(codec, "AGCIN"); + snd_soc_dapm_disable_pin(dapm, "AGCIN"); } if (changed) - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); mutex_unlock(&codec->mutex); @@ -164,19 +165,20 @@ static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned short pins, mode; - pins = ((snd_soc_dapm_get_pin_status(codec, "Mouthpiece") << + pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") << AMS_DELTA_MOUTHPIECE) | - (snd_soc_dapm_get_pin_status(codec, "Earpiece") << + (snd_soc_dapm_get_pin_status(dapm, "Earpiece") << AMS_DELTA_EARPIECE)); if (pins) - pins |= (snd_soc_dapm_get_pin_status(codec, "Microphone") << + pins |= (snd_soc_dapm_get_pin_status(dapm, "Microphone") << AMS_DELTA_MICROPHONE); else - pins = ((snd_soc_dapm_get_pin_status(codec, "Microphone") << + pins = ((snd_soc_dapm_get_pin_status(dapm, "Microphone") << AMS_DELTA_MICROPHONE) | - (snd_soc_dapm_get_pin_status(codec, "Speaker") << + (snd_soc_dapm_get_pin_status(dapm, "Speaker") << AMS_DELTA_SPEAKER) | (ams_delta_audio_agc << AMS_DELTA_AGC)); @@ -246,7 +248,7 @@ static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = { */ /* To actually apply any modem controlled configuration changes to the codec, - * we must connect codec DAI pins to the modem for a moment. Be carefull not + * we must connect codec DAI pins to the modem for a moment. Be careful not * to interfere with our digital mute function that shares the same hardware. */ static struct timer_list cx81801_timer; static bool cx81801_cmd_pending; @@ -300,6 +302,7 @@ static int cx81801_open(struct tty_struct *tty) static void cx81801_close(struct tty_struct *tty) { struct snd_soc_codec *codec = tty->disc_data; + struct snd_soc_dapm_context *dapm = &codec->dapm; del_timer_sync(&cx81801_timer); @@ -312,12 +315,12 @@ static void cx81801_close(struct tty_struct *tty) v253_ops.close(tty); /* Revert back to default audio input/output constellation */ - snd_soc_dapm_disable_pin(codec, "Mouthpiece"); - snd_soc_dapm_enable_pin(codec, "Earpiece"); - snd_soc_dapm_enable_pin(codec, "Microphone"); - snd_soc_dapm_disable_pin(codec, "Speaker"); - snd_soc_dapm_disable_pin(codec, "AGCIN"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); + snd_soc_dapm_enable_pin(dapm, "Earpiece"); + snd_soc_dapm_enable_pin(dapm, "Microphone"); + snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "AGCIN"); + snd_soc_dapm_sync(dapm); } /* Line discipline .hangup() */ @@ -399,9 +402,9 @@ static struct tty_ldisc_ops cx81801_ops = { /* - * Even if not very usefull, the sound card can still work without any of the + * Even if not very useful, the sound card can still work without any of the * above functonality activated. You can still control its audio input/output - * constellation and speakerphone gain from userspace by issueing AT commands + * constellation and speakerphone gain from userspace by issuing AT commands * over the modem port. */ @@ -432,16 +435,16 @@ static int ams_delta_set_bias_level(struct snd_soc_card *card, case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET, AMS_DELTA_LATCH2_MODEM_NRESET); break; case SND_SOC_BIAS_OFF: - if (codec->bias_level != SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level != SND_SOC_BIAS_OFF) ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET, 0); } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -492,6 +495,7 @@ static void ams_delta_shutdown(struct snd_pcm_substream *substream) static int ams_delta_cx20442_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_dai *codec_dai = rtd->codec_dai; struct snd_soc_card *card = rtd->card; int ret; @@ -503,8 +507,6 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) /* Set up digital mute if not provided by the codec */ if (!codec_dai->driver->ops) { codec_dai->driver->ops = &ams_delta_dai_ops; - } else if (!codec_dai->driver->ops->digital_mute) { - codec_dai->driver->ops->digital_mute = ams_delta_digital_mute; } else { ams_delta_ops.startup = ams_delta_startup; ams_delta_ops.shutdown = ams_delta_shutdown; @@ -541,7 +543,7 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) } /* Add board specific DAPM widgets and routes */ - ret = snd_soc_dapm_new_controls(codec, ams_delta_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, ams_delta_dapm_widgets, ARRAY_SIZE(ams_delta_dapm_widgets)); if (ret) { dev_warn(card->dev, @@ -550,7 +552,7 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) return 0; } - ret = snd_soc_dapm_add_routes(codec, ams_delta_audio_map, + ret = snd_soc_dapm_add_routes(dapm, ams_delta_audio_map, ARRAY_SIZE(ams_delta_audio_map)); if (ret) { dev_warn(card->dev, @@ -560,13 +562,13 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) } /* Set up initial pin constellation */ - snd_soc_dapm_disable_pin(codec, "Mouthpiece"); - snd_soc_dapm_enable_pin(codec, "Earpiece"); - snd_soc_dapm_enable_pin(codec, "Microphone"); - snd_soc_dapm_disable_pin(codec, "Speaker"); - snd_soc_dapm_disable_pin(codec, "AGCIN"); - snd_soc_dapm_disable_pin(codec, "AGCOUT"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); + snd_soc_dapm_enable_pin(dapm, "Earpiece"); + snd_soc_dapm_enable_pin(dapm, "Microphone"); + snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "AGCIN"); + snd_soc_dapm_disable_pin(dapm, "AGCOUT"); + snd_soc_dapm_sync(dapm); /* Add virtual switch */ ret = snd_soc_add_controls(codec, ams_delta_audio_controls, diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c index fd3a40f309c8..0ae34702995b 100644 --- a/sound/soc/omap/igep0020.c +++ b/sound/soc/omap/igep0020.c @@ -24,7 +24,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index a3b6d897ad84..83d213bfd3d1 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -27,7 +27,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -36,7 +35,6 @@ #include "omap-mcbsp.h" #include "omap-pcm.h" -#include "../codecs/tlv320aic3x.h" #define N810_HEADSET_AMP_GPIO 10 #define N810_SPEAKER_AMP_GPIO 101 @@ -58,6 +56,7 @@ static int n810_dmic_func; static void n810_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; int hp = 0, line1l = 0; switch (n810_jack_func) { @@ -72,25 +71,25 @@ static void n810_ext_control(struct snd_soc_codec *codec) } if (n810_spk_func) - snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Ext Spk"); if (hp) - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); else - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); if (line1l) - snd_soc_dapm_enable_pin(codec, "LINE1L"); + snd_soc_dapm_enable_pin(dapm, "LINE1L"); else - snd_soc_dapm_disable_pin(codec, "LINE1L"); + snd_soc_dapm_disable_pin(dapm, "LINE1L"); if (n810_dmic_func) - snd_soc_dapm_enable_pin(codec, "DMic"); + snd_soc_dapm_enable_pin(dapm, "DMic"); else - snd_soc_dapm_disable_pin(codec, "DMic"); + snd_soc_dapm_disable_pin(dapm, "DMic"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int n810_startup(struct snd_pcm_substream *substream) @@ -274,17 +273,18 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = { static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; /* Not connected */ - snd_soc_dapm_nc_pin(codec, "MONO_LOUT"); - snd_soc_dapm_nc_pin(codec, "HPLCOM"); - snd_soc_dapm_nc_pin(codec, "HPRCOM"); - snd_soc_dapm_nc_pin(codec, "MIC3L"); - snd_soc_dapm_nc_pin(codec, "MIC3R"); - snd_soc_dapm_nc_pin(codec, "LINE1R"); - snd_soc_dapm_nc_pin(codec, "LINE2L"); - snd_soc_dapm_nc_pin(codec, "LINE2R"); + snd_soc_dapm_nc_pin(dapm, "MONO_LOUT"); + snd_soc_dapm_nc_pin(dapm, "HPLCOM"); + snd_soc_dapm_nc_pin(dapm, "HPRCOM"); + snd_soc_dapm_nc_pin(dapm, "MIC3L"); + snd_soc_dapm_nc_pin(dapm, "MIC3R"); + snd_soc_dapm_nc_pin(dapm, "LINE1R"); + snd_soc_dapm_nc_pin(dapm, "LINE2L"); + snd_soc_dapm_nc_pin(dapm, "LINE2R"); /* Add N810 specific controls */ err = snd_soc_add_controls(codec, aic33_n810_controls, @@ -293,13 +293,13 @@ static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) return err; /* Add N810 specific widgets */ - snd_soc_dapm_new_controls(codec, aic33_dapm_widgets, + snd_soc_dapm_new_controls(dapm, aic33_dapm_widgets, ARRAY_SIZE(aic33_dapm_widgets)); /* Set up N810 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 7e84f24b9a88..2175f09e57b6 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -69,84 +69,6 @@ static struct omap_mcbsp_data mcbsp_data[NUM_LINKS]; */ static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2]; -#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) -static const int omap1_dma_reqs[][2] = { - { OMAP_DMA_MCBSP1_TX, OMAP_DMA_MCBSP1_RX }, - { OMAP_DMA_MCBSP2_TX, OMAP_DMA_MCBSP2_RX }, - { OMAP_DMA_MCBSP3_TX, OMAP_DMA_MCBSP3_RX }, -}; -static const unsigned long omap1_mcbsp_port[][2] = { - { OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, - OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, - { OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1, - OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, - { OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DXR1, - OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DRR1 }, -}; -#else -static const int omap1_dma_reqs[][2] = {}; -static const unsigned long omap1_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) -static const int omap24xx_dma_reqs[][2] = { - { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, - { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) - { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, - { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, - { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, -#endif -}; -#else -static const int omap24xx_dma_reqs[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP2420) -static const unsigned long omap2420_mcbsp_port[][2] = { - { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, - OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, - { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1, - OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, -}; -#else -static const unsigned long omap2420_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP2430) -static const unsigned long omap2430_mcbsp_port[][2] = { - { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, - OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, - OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, - OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, - OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, - OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, -}; -#else -static const unsigned long omap2430_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP3) -static const unsigned long omap34xx_mcbsp_port[][2] = { - { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, -}; -#else -static const unsigned long omap34xx_mcbsp_port[][2] = {}; -#endif - static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -224,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()) { + if (cpu_is_omap343x() || cpu_is_omap44xx()) { /* * Rule for the buffer size. We should not allow * smaller buffer than the FIFO size to avoid underruns @@ -320,21 +242,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, unsigned int format, div, framesize, master; dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; - if (cpu_class_is_omap1()) { - dma = omap1_dma_reqs[bus_id][substream->stream]; - port = omap1_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap2420()) { - dma = omap24xx_dma_reqs[bus_id][substream->stream]; - port = omap2420_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap2430()) { - dma = omap24xx_dma_reqs[bus_id][substream->stream]; - port = omap2430_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap343x()) { - dma = omap24xx_dma_reqs[bus_id][substream->stream]; - port = omap34xx_mcbsp_port[bus_id][substream->stream]; - } else { - return -ENODEV; - } + + dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream); + port = omap_mcbsp_dma_reg_params(bus_id, substream->stream); + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; @@ -498,11 +409,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, regs->spcr2 |= XINTM(3) | FREE; regs->spcr1 |= RINTM(3); /* RFIG and XFIG are not defined in 34xx */ - if (!cpu_is_omap34xx()) { + if (!cpu_is_omap34xx() && !cpu_is_omap44xx()) { regs->rcr2 |= RFIG; regs->xcr2 |= XFIG; } - if (cpu_is_omap2430() || cpu_is_omap34xx()) { + if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) { regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE; regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE; } diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index ffdcc5abb7b9..37dc7211ed3f 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -43,14 +43,18 @@ enum omap_mcbsp_div { OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ }; -#if defined(CONFIG_ARCH_OMAP2420) +#if defined(CONFIG_SOC_OMAP2420) #define NUM_LINKS 2 #endif #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) #undef NUM_LINKS #define NUM_LINKS 3 #endif -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) +#if defined(CONFIG_ARCH_OMAP4) +#undef NUM_LINKS +#define NUM_LINKS 4 +#endif +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_OMAP2430) #undef NUM_LINKS #define NUM_LINKS 5 #endif diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c index cf3fc8a675b5..29b60d6796e7 100644 --- a/sound/soc/omap/omap2evm.c +++ b/sound/soc/omap/omap2evm.c @@ -26,7 +26,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c index e56832b0c444..40db813c0795 100644 --- a/sound/soc/omap/omap3beagle.c +++ b/sound/soc/omap/omap3beagle.c @@ -24,7 +24,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c index 810f1e36da21..0daa04469836 100644 --- a/sound/soc/omap/omap3evm.c +++ b/sound/soc/omap/omap3evm.c @@ -22,7 +22,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index 4ee33ce2cb98..8047c521e318 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c @@ -28,7 +28,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <plat/mcbsp.h> @@ -170,51 +169,53 @@ static const struct snd_soc_dapm_route omap3pandora_in_map[] = { static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* All TWL4030 output pins are floating */ - snd_soc_dapm_nc_pin(codec, "EARPIECE"); - snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); - snd_soc_dapm_nc_pin(codec, "PREDRIVER"); - snd_soc_dapm_nc_pin(codec, "HSOL"); - snd_soc_dapm_nc_pin(codec, "HSOR"); - snd_soc_dapm_nc_pin(codec, "CARKITL"); - snd_soc_dapm_nc_pin(codec, "CARKITR"); - snd_soc_dapm_nc_pin(codec, "HFL"); - snd_soc_dapm_nc_pin(codec, "HFR"); - snd_soc_dapm_nc_pin(codec, "VIBRA"); - - ret = snd_soc_dapm_new_controls(codec, omap3pandora_out_dapm_widgets, + snd_soc_dapm_nc_pin(dapm, "EARPIECE"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); + snd_soc_dapm_nc_pin(dapm, "HSOL"); + snd_soc_dapm_nc_pin(dapm, "HSOR"); + snd_soc_dapm_nc_pin(dapm, "CARKITL"); + snd_soc_dapm_nc_pin(dapm, "CARKITR"); + snd_soc_dapm_nc_pin(dapm, "HFL"); + snd_soc_dapm_nc_pin(dapm, "HFR"); + snd_soc_dapm_nc_pin(dapm, "VIBRA"); + + ret = snd_soc_dapm_new_controls(dapm, omap3pandora_out_dapm_widgets, ARRAY_SIZE(omap3pandora_out_dapm_widgets)); if (ret < 0) return ret; - snd_soc_dapm_add_routes(codec, omap3pandora_out_map, + snd_soc_dapm_add_routes(dapm, omap3pandora_out_map, ARRAY_SIZE(omap3pandora_out_map)); - return snd_soc_dapm_sync(codec); + return snd_soc_dapm_sync(dapm); } static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* Not comnnected */ - snd_soc_dapm_nc_pin(codec, "HSMIC"); - snd_soc_dapm_nc_pin(codec, "CARKITMIC"); - snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); - snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); + snd_soc_dapm_nc_pin(dapm, "HSMIC"); + snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); - ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, omap3pandora_in_dapm_widgets, ARRAY_SIZE(omap3pandora_in_dapm_widgets)); if (ret < 0) return ret; - snd_soc_dapm_add_routes(codec, omap3pandora_in_map, + snd_soc_dapm_add_routes(dapm, omap3pandora_in_map, ARRAY_SIZE(omap3pandora_in_map)); - return snd_soc_dapm_sync(codec); + return snd_soc_dapm_sync(dapm); } static struct snd_soc_ops omap3pandora_ops = { diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c index 65ae00e976ef..7e75e775fb4a 100644 --- a/sound/soc/omap/osk5912.c +++ b/sound/soc/omap/osk5912.c @@ -26,7 +26,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -116,19 +115,20 @@ static const struct snd_soc_dapm_route audio_map[] = { static int osk_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; /* Add osk5912 specific widgets */ - snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets, ARRAY_SIZE(tlv320aic23_dapm_widgets)); /* Set up osk5912 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Line In"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Line In"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c index e95a607937de..bbcf380bfb56 100644 --- a/sound/soc/omap/overo.c +++ b/sound/soc/omap/overo.c @@ -24,7 +24,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 04b5723bf89b..d0986220eff9 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -30,17 +30,17 @@ #include <sound/jack.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <plat/mcbsp.h> +#include "../codecs/tpa6130a2.h" #include <asm/mach-types.h> #include "omap-mcbsp.h" #include "omap-pcm.h" -#include "../codecs/tlv320aic3x.h" #define RX51_TVOUT_SEL_GPIO 40 #define RX51_JACK_DETECT_GPIO 177 +#define RX51_ECI_SW_GPIO 182 /* * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c @@ -49,7 +49,9 @@ enum { RX51_JACK_DISABLED, - RX51_JACK_TVOUT, /* tv-out */ + RX51_JACK_TVOUT, /* tv-out with stereo output */ + RX51_JACK_HP, /* headphone: stereo output, no mic */ + RX51_JACK_HS, /* headset: stereo output with mic */ }; static int rx51_spk_func; @@ -58,19 +60,41 @@ static int rx51_jack_func; static void rx51_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + int hp = 0, hs = 0, tvout = 0; + + switch (rx51_jack_func) { + case RX51_JACK_TVOUT: + tvout = 1; + hp = 1; + break; + case RX51_JACK_HS: + hs = 1; + case RX51_JACK_HP: + hp = 1; + break; + } + if (rx51_spk_func) - snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Ext Spk"); if (rx51_dmic_func) - snd_soc_dapm_enable_pin(codec, "DMic"); + snd_soc_dapm_enable_pin(dapm, "DMic"); + else + snd_soc_dapm_disable_pin(dapm, "DMic"); + if (hp) + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); else - snd_soc_dapm_disable_pin(codec, "DMic"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + if (hs) + snd_soc_dapm_enable_pin(dapm, "HS Mic"); + else + snd_soc_dapm_disable_pin(dapm, "HS Mic"); - gpio_set_value(RX51_TVOUT_SEL_GPIO, - rx51_jack_func == RX51_JACK_TVOUT); + gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int rx51_startup(struct snd_pcm_substream *substream) @@ -153,6 +177,19 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w, return 0; } +static int rx51_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->dapm->codec; + + if (SND_SOC_DAPM_EVENT_ON(event)) + tpa6130a2_stereo_enable(codec, 1); + else + tpa6130a2_stereo_enable(codec, 0); + + return 0; +} + static int rx51_get_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -203,7 +240,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { { .gpio = RX51_JACK_DETECT_GPIO, .name = "avdet-gpio", - .report = SND_JACK_VIDEOOUT, + .report = SND_JACK_HEADSET, .invert = 1, .debounce_time = 200, }, @@ -212,19 +249,38 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), SND_SOC_DAPM_MIC("DMic", NULL), + SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event), + SND_SOC_DAPM_MIC("HS Mic", NULL), + SND_SOC_DAPM_LINE("FM Transmitter", NULL), +}; + +static const struct snd_soc_dapm_widget aic34_dapm_widgetsb[] = { + SND_SOC_DAPM_SPK("Earphone", NULL), }; static const struct snd_soc_dapm_route audio_map[] = { {"Ext Spk", NULL, "HPLOUT"}, {"Ext Spk", NULL, "HPROUT"}, + {"Headphone Jack", NULL, "LLOUT"}, + {"Headphone Jack", NULL, "RLOUT"}, + {"FM Transmitter", NULL, "LLOUT"}, + {"FM Transmitter", NULL, "RLOUT"}, {"DMic Rate 64", NULL, "Mic Bias 2V"}, {"Mic Bias 2V", NULL, "DMic"}, }; +static const struct snd_soc_dapm_route audio_mapb[] = { + {"b LINE2R", NULL, "MONO_LOUT"}, + {"Earphone", NULL, "b HPLOUT"}, + + {"LINE1L", NULL, "b Mic Bias 2.5V"}, + {"b Mic Bias 2.5V", NULL, "HS Mic"} +}; + static const char *spk_function[] = {"Off", "On"}; static const char *input_function[] = {"ADC", "Digital Mic"}; -static const char *jack_function[] = {"Off", "TV-OUT"}; +static const char *jack_function[] = {"Off", "TV-OUT", "Headphone", "Headset"}; static const struct soc_enum rx51_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), @@ -239,17 +295,23 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = { rx51_get_input, rx51_set_input), SOC_ENUM_EXT("Jack Function", rx51_enum[2], rx51_get_jack, rx51_set_jack), + SOC_DAPM_PIN_SWITCH("FM Transmitter"), +}; + +static const struct snd_kcontrol_new aic34_rx51_controlsb[] = { + SOC_DAPM_PIN_SWITCH("Earphone"), }; static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; /* Set up NC codec pins */ - snd_soc_dapm_nc_pin(codec, "MIC3L"); - snd_soc_dapm_nc_pin(codec, "MIC3R"); - snd_soc_dapm_nc_pin(codec, "LINE1R"); + snd_soc_dapm_nc_pin(dapm, "MIC3L"); + snd_soc_dapm_nc_pin(dapm, "MIC3R"); + snd_soc_dapm_nc_pin(dapm, "LINE1R"); /* Add RX-51 specific controls */ err = snd_soc_add_controls(codec, aic34_rx51_controls, @@ -258,17 +320,27 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) return err; /* Add RX-51 specific widgets */ - snd_soc_dapm_new_controls(codec, aic34_dapm_widgets, + snd_soc_dapm_new_controls(dapm, aic34_dapm_widgets, ARRAY_SIZE(aic34_dapm_widgets)); /* Set up RX-51 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + err = tpa6130a2_add_controls(codec); + if (err < 0) + return err; + snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42); + + err = omap_mcbsp_st_add_controls(codec, 1); + if (err < 0) + return err; + + snd_soc_dapm_sync(dapm); /* AV jack detection */ err = snd_soc_jack_new(codec, "AV Jack", - SND_JACK_VIDEOOUT, &rx51_av_jack); + SND_JACK_HEADSET | SND_JACK_VIDEOOUT, + &rx51_av_jack); if (err) return err; err = snd_soc_jack_add_gpios(&rx51_av_jack, @@ -278,6 +350,24 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) return err; } +static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm) +{ + int err; + + err = snd_soc_add_controls(dapm->codec, aic34_rx51_controlsb, + ARRAY_SIZE(aic34_rx51_controlsb)); + if (err < 0) + return err; + + err = snd_soc_dapm_new_controls(dapm, aic34_dapm_widgetsb, + ARRAY_SIZE(aic34_dapm_widgetsb)); + if (err < 0) + return 0; + + return snd_soc_dapm_add_routes(dapm, audio_mapb, + ARRAY_SIZE(audio_mapb)); +} + /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link rx51_dai[] = { { @@ -292,11 +382,30 @@ static struct snd_soc_dai_link rx51_dai[] = { }, }; +struct snd_soc_aux_dev rx51_aux_dev[] = { + { + .name = "TLV320AIC34b", + .codec_name = "tlv320aic3x-codec.2-0019", + .init = rx51_aic34b_init, + }, +}; + +static struct snd_soc_codec_conf rx51_codec_conf[] = { + { + .dev_name = "tlv320aic3x-codec.2-0019", + .name_prefix = "b", + }, +}; + /* Audio card */ static struct snd_soc_card rx51_sound_card = { .name = "RX-51", .dai_link = rx51_dai, .num_links = ARRAY_SIZE(rx51_dai), + .aux_dev = rx51_aux_dev, + .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), + .codec_conf = rx51_codec_conf, + .num_configs = ARRAY_SIZE(rx51_codec_conf), }; static struct platform_device *rx51_snd_device; @@ -308,10 +417,14 @@ static int __init rx51_soc_init(void) if (!machine_is_nokia_rx51()) return -ENODEV; - err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel"); + err = gpio_request_one(RX51_TVOUT_SEL_GPIO, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel"); if (err) goto err_gpio_tvout_sel; - gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0); + err = gpio_request_one(RX51_ECI_SW_GPIO, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw"); + if (err) + goto err_gpio_eci_sw; rx51_snd_device = platform_device_alloc("soc-audio", -1); if (!rx51_snd_device) { @@ -329,6 +442,8 @@ static int __init rx51_soc_init(void) err2: platform_device_put(rx51_snd_device); err1: + gpio_free(RX51_ECI_SW_GPIO); +err_gpio_eci_sw: gpio_free(RX51_TVOUT_SEL_GPIO); err_gpio_tvout_sel: @@ -341,6 +456,7 @@ static void __exit rx51_soc_exit(void) rx51_av_jack_gpios); platform_device_unregister(rx51_snd_device); + gpio_free(RX51_ECI_SW_GPIO); gpio_free(RX51_TVOUT_SEL_GPIO); } diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 07fbcf7d2411..3f72d17d1ef0 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -28,7 +28,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/jack.h> #include <asm/mach-types.h> @@ -191,39 +190,40 @@ static const struct snd_soc_dapm_route audio_map[] = { static int sdp3430_twl4030_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* Add SDP3430 specific widgets */ - ret = snd_soc_dapm_new_controls(codec, sdp3430_twl4030_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, sdp3430_twl4030_dapm_widgets, ARRAY_SIZE(sdp3430_twl4030_dapm_widgets)); if (ret) return ret; /* Set up SDP3430 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* SDP3430 connected pins */ - snd_soc_dapm_enable_pin(codec, "Ext Mic"); - snd_soc_dapm_enable_pin(codec, "Ext Spk"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Headset Stereophone"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic"); + snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); /* TWL4030 not connected pins */ - snd_soc_dapm_nc_pin(codec, "AUXL"); - snd_soc_dapm_nc_pin(codec, "AUXR"); - snd_soc_dapm_nc_pin(codec, "CARKITMIC"); - snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); - snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); - - snd_soc_dapm_nc_pin(codec, "OUTL"); - snd_soc_dapm_nc_pin(codec, "OUTR"); - snd_soc_dapm_nc_pin(codec, "EARPIECE"); - snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); - snd_soc_dapm_nc_pin(codec, "PREDRIVER"); - snd_soc_dapm_nc_pin(codec, "CARKITL"); - snd_soc_dapm_nc_pin(codec, "CARKITR"); - - ret = snd_soc_dapm_sync(codec); + snd_soc_dapm_nc_pin(dapm, "AUXL"); + snd_soc_dapm_nc_pin(dapm, "AUXR"); + snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); + + snd_soc_dapm_nc_pin(dapm, "OUTL"); + snd_soc_dapm_nc_pin(dapm, "OUTR"); + snd_soc_dapm_nc_pin(dapm, "EARPIECE"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); + snd_soc_dapm_nc_pin(dapm, "CARKITL"); + snd_soc_dapm_nc_pin(dapm, "CARKITR"); + + ret = snd_soc_dapm_sync(dapm); if (ret) return ret; diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index 4b4463db6ba0..189e03900637 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -24,7 +24,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> +#include <sound/jack.h> #include <asm/mach-types.h> #include <plat/hardware.h> @@ -66,6 +66,21 @@ static struct snd_soc_ops sdp4430_ops = { .hw_params = sdp4430_hw_params, }; +/* Headset jack */ +static struct snd_soc_jack hs_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -102,6 +117,7 @@ static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_HP("Headset Stereophone", NULL), SND_SOC_DAPM_SPK("Earphone Spk", NULL), + SND_SOC_DAPM_INPUT("Aux/FM Stereo In"), }; static const struct snd_soc_dapm_route audio_map[] = { @@ -124,11 +140,16 @@ static const struct snd_soc_dapm_route audio_map[] = { /* Earphone speaker */ {"Earphone Spk", NULL, "EP"}, + + /* Aux/FM Stereo In: AFML, AFMR */ + {"AFML", NULL, "Aux/FM Stereo In"}, + {"AFMR", NULL, "Aux/FM Stereo In"}, }; static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* Add SDP4430 specific controls */ @@ -138,25 +159,39 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) return ret; /* Add SDP4430 specific widgets */ - ret = snd_soc_dapm_new_controls(codec, sdp4430_twl6040_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); if (ret) return ret; /* Set up SDP4430 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* SDP4430 connected pins */ - snd_soc_dapm_enable_pin(codec, "Ext Mic"); - snd_soc_dapm_enable_pin(codec, "Ext Spk"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Headset Stereophone"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "AFML"); + snd_soc_dapm_enable_pin(dapm, "AFMR"); + snd_soc_dapm_enable_pin(dapm, "Headset Mic"); + snd_soc_dapm_enable_pin(dapm, "Headset Stereophone"); + + ret = snd_soc_dapm_sync(dapm); + if (ret) + return ret; + + /* Headset jack detection */ + ret = snd_soc_jack_new(codec, "Headset Jack", + SND_JACK_HEADSET, &hs_jack); + if (ret) + return ret; - /* TWL6040 not connected pins */ - snd_soc_dapm_nc_pin(codec, "AFML"); - snd_soc_dapm_nc_pin(codec, "AFMR"); + ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); - ret = snd_soc_dapm_sync(codec); + if (machine_is_omap_4430sdp()) + twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); + else + snd_soc_jack_report(&hs_jack, SND_JACK_HEADSET, SND_JACK_HEADSET); return ret; } diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 718031eeac34..01709940a43c 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -24,7 +24,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/hardware.h> @@ -162,35 +161,36 @@ static const struct snd_soc_dapm_route audio_map[] = { static int zoom2_twl4030_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* Add Zoom2 specific widgets */ - ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, zoom2_twl4030_dapm_widgets, ARRAY_SIZE(zoom2_twl4030_dapm_widgets)); if (ret) return ret; /* Set up Zoom2 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* Zoom2 connected pins */ - snd_soc_dapm_enable_pin(codec, "Ext Mic"); - snd_soc_dapm_enable_pin(codec, "Ext Spk"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Headset Stereophone"); - snd_soc_dapm_enable_pin(codec, "Aux In"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Headset Mic"); + snd_soc_dapm_enable_pin(dapm, "Headset Stereophone"); + snd_soc_dapm_enable_pin(dapm, "Aux In"); /* TWL4030 not connected pins */ - snd_soc_dapm_nc_pin(codec, "CARKITMIC"); - snd_soc_dapm_nc_pin(codec, "DIGIMIC0"); - snd_soc_dapm_nc_pin(codec, "DIGIMIC1"); - snd_soc_dapm_nc_pin(codec, "EARPIECE"); - snd_soc_dapm_nc_pin(codec, "PREDRIVEL"); - snd_soc_dapm_nc_pin(codec, "PREDRIVER"); - snd_soc_dapm_nc_pin(codec, "CARKITL"); - snd_soc_dapm_nc_pin(codec, "CARKITR"); - - ret = snd_soc_dapm_sync(codec); + snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); + snd_soc_dapm_nc_pin(dapm, "EARPIECE"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); + snd_soc_dapm_nc_pin(dapm, "CARKITL"); + snd_soc_dapm_nc_pin(dapm, "CARKITR"); + + ret = snd_soc_dapm_sync(dapm); return ret; } diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index f451acd4935b..9027da466cae 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -23,7 +23,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/corgi.h> @@ -48,51 +47,53 @@ static int corgi_spk_func; static void corgi_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + /* set up jack connection */ switch (corgi_jack_func) { case CORGI_HP: /* set = unmute headphone */ gpio_set_value(CORGI_GPIO_MUTE_L, 1); gpio_set_value(CORGI_GPIO_MUTE_R, 1); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); break; case CORGI_MIC: /* reset = mute headphone */ gpio_set_value(CORGI_GPIO_MUTE_L, 0); gpio_set_value(CORGI_GPIO_MUTE_R, 0); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); break; case CORGI_LINE: gpio_set_value(CORGI_GPIO_MUTE_L, 0); gpio_set_value(CORGI_GPIO_MUTE_R, 0); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_enable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Line Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); break; case CORGI_HEADSET: gpio_set_value(CORGI_GPIO_MUTE_L, 0); gpio_set_value(CORGI_GPIO_MUTE_R, 1); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Headset Jack"); break; } if (corgi_spk_func == CORGI_SPK_ON) - snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int corgi_startup(struct snd_pcm_substream *substream) @@ -279,10 +280,11 @@ static const struct snd_kcontrol_new wm8731_corgi_controls[] = { static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - snd_soc_dapm_nc_pin(codec, "LLINEIN"); - snd_soc_dapm_nc_pin(codec, "RLINEIN"); + snd_soc_dapm_nc_pin(dapm, "LLINEIN"); + snd_soc_dapm_nc_pin(dapm, "RLINEIN"); /* Add corgi specific controls */ err = snd_soc_add_controls(codec, wm8731_corgi_controls, @@ -291,13 +293,13 @@ static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) return err; /* Add corgi specific widgets */ - snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets, ARRAY_SIZE(wm8731_dapm_widgets)); /* Set up corgi specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -305,10 +307,10 @@ static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", .stream_name = "WM8731", - .cpu_dai_name = "pxa-is2-dai", + .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec-0.001a", + .codec_name = "wm8731-codec.0-001b", .init = corgi_wm8731_init, .ops = &corgi_ops, }; diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index c82cedb602fd..dc65650a6fa1 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -16,7 +16,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <mach/audio.h> #include <mach/eseries-gpio.h> @@ -92,23 +91,24 @@ static const struct snd_soc_dapm_route audio_map[] = { static int e740_ac97_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - - snd_soc_dapm_nc_pin(codec, "HPOUTL"); - snd_soc_dapm_nc_pin(codec, "HPOUTR"); - snd_soc_dapm_nc_pin(codec, "PHONE"); - snd_soc_dapm_nc_pin(codec, "LINEINL"); - snd_soc_dapm_nc_pin(codec, "LINEINR"); - snd_soc_dapm_nc_pin(codec, "CDINL"); - snd_soc_dapm_nc_pin(codec, "CDINR"); - snd_soc_dapm_nc_pin(codec, "PCBEEP"); - snd_soc_dapm_nc_pin(codec, "MIC2"); - - snd_soc_dapm_new_controls(codec, e740_dapm_widgets, + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_nc_pin(dapm, "HPOUTL"); + snd_soc_dapm_nc_pin(dapm, "HPOUTR"); + snd_soc_dapm_nc_pin(dapm, "PHONE"); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "CDINL"); + snd_soc_dapm_nc_pin(dapm, "CDINR"); + snd_soc_dapm_nc_pin(dapm, "PCBEEP"); + snd_soc_dapm_nc_pin(dapm, "MIC2"); + + snd_soc_dapm_new_controls(dapm, e740_dapm_widgets, ARRAY_SIZE(e740_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -117,7 +117,7 @@ static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", @@ -126,7 +126,7 @@ static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9705-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 4c143803a75e..51897fcd911b 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -16,7 +16,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <mach/audio.h> #include <mach/eseries-gpio.h> @@ -74,23 +73,24 @@ static const struct snd_soc_dapm_route audio_map[] = { static int e750_ac97_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - - snd_soc_dapm_nc_pin(codec, "LOUT"); - snd_soc_dapm_nc_pin(codec, "ROUT"); - snd_soc_dapm_nc_pin(codec, "PHONE"); - snd_soc_dapm_nc_pin(codec, "LINEINL"); - snd_soc_dapm_nc_pin(codec, "LINEINR"); - snd_soc_dapm_nc_pin(codec, "CDINL"); - snd_soc_dapm_nc_pin(codec, "CDINR"); - snd_soc_dapm_nc_pin(codec, "PCBEEP"); - snd_soc_dapm_nc_pin(codec, "MIC2"); - - snd_soc_dapm_new_controls(codec, e750_dapm_widgets, + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_nc_pin(dapm, "LOUT"); + snd_soc_dapm_nc_pin(dapm, "ROUT"); + snd_soc_dapm_nc_pin(dapm, "PHONE"); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "CDINL"); + snd_soc_dapm_nc_pin(dapm, "CDINR"); + snd_soc_dapm_nc_pin(dapm, "PCBEEP"); + snd_soc_dapm_nc_pin(dapm, "MIC2"); + + snd_soc_dapm_new_controls(dapm, e750_dapm_widgets, ARRAY_SIZE(e750_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -99,7 +99,7 @@ static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", @@ -109,7 +109,7 @@ static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9705-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index d42e5fe832c5..053ed208e59f 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -16,7 +16,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/audio.h> @@ -75,12 +74,13 @@ static const struct snd_soc_dapm_route audio_map[] = { static int e800_ac97_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_new_controls(codec, e800_dapm_widgets, + snd_soc_dapm_new_controls(dapm, e800_dapm_widgets, ARRAY_SIZE(e800_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_sync(dapm); return 0; } @@ -89,7 +89,7 @@ static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -98,7 +98,7 @@ static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index eadf9d351a04..b13a4252812d 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -26,7 +26,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/audio.h> @@ -38,7 +37,7 @@ static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -46,7 +45,7 @@ static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 5ef0526924b9..67dcc36cd621 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -26,7 +26,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/uda1380.h> #include <mach/magician.h> @@ -44,27 +43,29 @@ static int magician_in_sel = MAGICIAN_MIC; static void magician_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + if (magician_spk_switch) - snd_soc_dapm_enable_pin(codec, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(codec, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "Speaker"); if (magician_hp_switch) - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); else - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); switch (magician_in_sel) { case MAGICIAN_MIC: - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Call Mic"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic"); + snd_soc_dapm_enable_pin(dapm, "Call Mic"); break; case MAGICIAN_MIC_EXT: - snd_soc_dapm_disable_pin(codec, "Call Mic"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(dapm, "Call Mic"); + snd_soc_dapm_enable_pin(dapm, "Headset Mic"); break; } - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int magician_startup(struct snd_pcm_substream *substream) @@ -399,15 +400,16 @@ static const struct snd_kcontrol_new uda1380_magician_controls[] = { static int magician_uda1380_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 */ - snd_soc_dapm_nc_pin(codec, "VOUTLHP"); - snd_soc_dapm_nc_pin(codec, "VOUTRHP"); + snd_soc_dapm_nc_pin(dapm, "VOUTLHP"); + snd_soc_dapm_nc_pin(dapm, "VOUTRHP"); /* FIXME: is anything connected here? */ - snd_soc_dapm_nc_pin(codec, "VINL"); - snd_soc_dapm_nc_pin(codec, "VINR"); + snd_soc_dapm_nc_pin(dapm, "VINL"); + snd_soc_dapm_nc_pin(dapm, "VINR"); /* Add magician specific controls */ err = snd_soc_add_controls(codec, uda1380_magician_controls, @@ -416,13 +418,13 @@ static int magician_uda1380_init(struct snd_soc_pcm_runtime *rtd) return err; /* Add magician specific widgets */ - snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets, + snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets, ARRAY_SIZE(uda1380_dapm_widgets)); /* Set up magician specific audio path interconnects */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index f284cc54bc80..38ca6759907e 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -50,7 +50,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/ac97_codec.h> @@ -130,13 +129,14 @@ static const struct snd_soc_dapm_route audio_map[] = { static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned short reg; /* Add mioa701 specific widgets */ - snd_soc_dapm_new_controls(codec, ARRAY_AND_SIZE(mioa701_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, ARRAY_AND_SIZE(mioa701_dapm_widgets)); /* Set up mioa701 specific audio path audio_mapnects */ - snd_soc_dapm_add_routes(codec, ARRAY_AND_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, ARRAY_AND_SIZE(audio_map)); /* Prepare GPIO8 for rear speaker amplifier */ reg = codec->driver->read(codec, AC97_GPIO_CFG); @@ -146,12 +146,12 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) reg = codec->driver->read(codec, AC97_3D_CONTROL); codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000); - snd_soc_dapm_enable_pin(codec, "Front Speaker"); - snd_soc_dapm_enable_pin(codec, "Rear Speaker"); - snd_soc_dapm_enable_pin(codec, "Front Mic"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_enable_pin(dapm, "Front Speaker"); + snd_soc_dapm_enable_pin(dapm, "Rear Speaker"); + snd_soc_dapm_enable_pin(dapm, "Front Mic"); + snd_soc_dapm_enable_pin(dapm, "GSM Line In"); + snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); + snd_soc_dapm_sync(dapm); return 0; } @@ -162,7 +162,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9713-hifi", .codec_name = "wm9713-codec", .init = mioa701_wm9713_init, @@ -172,7 +172,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9713-aux", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 13f6d485d571..504e4004f004 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -21,7 +21,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/jack.h> #include <asm/mach-types.h> @@ -77,37 +76,38 @@ static struct snd_soc_card palm27x_asoc; static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; /* add palm27x specific widgets */ - err = snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, + err = snd_soc_dapm_new_controls(dapm, palm27x_dapm_widgets, ARRAY_SIZE(palm27x_dapm_widgets)); if (err) return err; /* set up palm27x specific audio path audio_map */ - err = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + err = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); if (err) return err; /* connected pins */ if (machine_is_palmld()) - snd_soc_dapm_enable_pin(codec, "MIC1"); - snd_soc_dapm_enable_pin(codec, "HPOUTL"); - snd_soc_dapm_enable_pin(codec, "HPOUTR"); - snd_soc_dapm_enable_pin(codec, "LOUT2"); - snd_soc_dapm_enable_pin(codec, "ROUT2"); + snd_soc_dapm_enable_pin(dapm, "MIC1"); + snd_soc_dapm_enable_pin(dapm, "HPOUTL"); + snd_soc_dapm_enable_pin(dapm, "HPOUTR"); + snd_soc_dapm_enable_pin(dapm, "LOUT2"); + snd_soc_dapm_enable_pin(dapm, "ROUT2"); /* not connected pins */ - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONOOUT"); - snd_soc_dapm_nc_pin(codec, "LINEINL"); - snd_soc_dapm_nc_pin(codec, "LINEINR"); - snd_soc_dapm_nc_pin(codec, "PCBEEP"); - snd_soc_dapm_nc_pin(codec, "PHONE"); - snd_soc_dapm_nc_pin(codec, "MIC2"); - - err = snd_soc_dapm_sync(codec); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONOOUT"); + snd_soc_dapm_nc_pin(dapm, "LINEINL"); + snd_soc_dapm_nc_pin(dapm, "LINEINR"); + snd_soc_dapm_nc_pin(dapm, "PCBEEP"); + snd_soc_dapm_nc_pin(dapm, "PHONE"); + snd_soc_dapm_nc_pin(dapm, "MIC2"); + + err = snd_soc_dapm_sync(dapm); if (err) return err; @@ -132,7 +132,7 @@ static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 HiFi", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .codec_name = "wm9712-codec", .platform_name = "pxa-pcm-audio", @@ -141,7 +141,7 @@ static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9712-aux", .codec_name = "wm9712-codec", .platform_name = "pxa-pcm-audio", diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 84edd0385a21..a7d4999f9b24 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -23,7 +23,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <asm/hardware/locomo.h> @@ -46,6 +45,8 @@ static int poodle_spk_func; static void poodle_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + /* set up jack connection */ if (poodle_jack_func == POODLE_HP) { /* set = unmute headphone */ @@ -53,23 +54,23 @@ static void poodle_ext_control(struct snd_soc_codec *codec) POODLE_LOCOMO_GPIO_MUTE_L, 1); locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_R, 1); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); } else { locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_L, 0); locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_R, 0); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); } /* set the enpoints to their new connetion states */ if (poodle_spk_func == POODLE_SPK_ON) - snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int poodle_startup(struct snd_pcm_substream *substream) @@ -244,11 +245,12 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = { static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - snd_soc_dapm_nc_pin(codec, "LLINEIN"); - snd_soc_dapm_nc_pin(codec, "RLINEIN"); - snd_soc_dapm_enable_pin(codec, "MICIN"); + snd_soc_dapm_nc_pin(dapm, "LLINEIN"); + snd_soc_dapm_nc_pin(dapm, "RLINEIN"); + snd_soc_dapm_enable_pin(dapm, "MICIN"); /* Add poodle specific controls */ err = snd_soc_add_controls(codec, wm8731_poodle_controls, @@ -257,13 +259,13 @@ static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) return err; /* Add poodle specific widgets */ - snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets, ARRAY_SIZE(wm8731_dapm_widgets)); /* Set up poodle specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -274,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-001a", + .codec_name = "wm8731-codec.0-001b", .init = poodle_wm8731_init, .ops = &poodle_ops, }; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index b439eee462cb..8ad93ee2e92b 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/pxa2xx_ssp.h> #include <asm/irq.h> @@ -33,7 +34,6 @@ #include <mach/hardware.h> #include <mach/dma.h> #include <mach/audio.h> -#include <plat/ssp.h> #include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 02fb66416ddc..2ce0b2d891d5 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -65,6 +65,7 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) if (prtd->dma_ch >= 0) { pxa_free_dma(prtd->dma_ch); prtd->dma_ch = -1; + prtd->params = NULL; } return 0; diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 2cda82bc5d2e..2afabaf59491 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -22,7 +22,6 @@ #include <linux/gpio.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> @@ -152,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = { .hw_params = raumfeld_cs4270_hw_params, }; -static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +static int raumfeld_line_suspend(struct snd_soc_card *card) { raumfeld_enable_audio(false); return 0; } -static int raumfeld_line_resume(struct platform_device *pdev) +static int raumfeld_line_resume(struct snd_soc_card *card) { raumfeld_enable_audio(true); return 0; @@ -230,19 +229,19 @@ static struct snd_soc_dai_link raumfeld_dai[] = { { .name = "ak4104", .stream_name = "Playback", - .cpu_dai_name = "pxa-ssp-dai.1", - .codec_dai_name = "ak4104-hifi", - .platform_name = "pxa-pcm-audio", + .cpu_dai_name = "pxa-ssp-dai.1", + .codec_dai_name = "ak4104-hifi", + .platform_name = "pxa-pcm-audio", .ops = &raumfeld_ak4104_ops, - .codec_name = "ak4104-codec.0", + .codec_name = "ak4104-codec.0", }, { .name = "CS4270", .stream_name = "CS4270", - .cpu_dai_name = "pxa-ssp-dai.0", - .platform_name = "pxa-pcm-audio", - .codec_dai_name = "cs4270-hifi", - .codec_name = "cs4270-codec.0-0048", + .cpu_dai_name = "pxa-ssp-dai.0", + .platform_name = "pxa-pcm-audio", + .codec_dai_name = "cs4270-hifi", + .codec_name = "cs4270-codec.0-0048", .ops = &raumfeld_cs4270_ops, },}; diff --git a/sound/soc/pxa/saarb.c b/sound/soc/pxa/saarb.c index d63cb474b4e1..9595189fc681 100644 --- a/sound/soc/pxa/saarb.c +++ b/sound/soc/pxa/saarb.c @@ -18,7 +18,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/jack.h> #include <asm/mach-types.h> @@ -133,20 +132,21 @@ static struct snd_soc_card snd_soc_card_saarb = { static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - snd_soc_dapm_new_controls(codec, saarb_dapm_widgets, + snd_soc_dapm_new_controls(dapm, saarb_dapm_widgets, ARRAY_SIZE(saarb_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* connected pins */ - snd_soc_dapm_enable_pin(codec, "Ext Speaker"); - snd_soc_dapm_enable_pin(codec, "Ext Mic 1"); - snd_soc_dapm_enable_pin(codec, "Ext Mic 3"); - snd_soc_dapm_disable_pin(codec, "Headset Mic 2"); - snd_soc_dapm_disable_pin(codec, "Headset Stereophone"); + snd_soc_dapm_enable_pin(dapm, "Ext Speaker"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic 1"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic 3"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic 2"); + snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); - ret = snd_soc_dapm_sync(codec); + ret = snd_soc_dapm_sync(dapm); if (ret) return ret; diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 0b30d7de24ec..8e1571350630 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -23,7 +23,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/spitz.h> @@ -46,61 +45,63 @@ static int spitz_spk_func; static void spitz_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + if (spitz_spk_func == SPITZ_SPK_ON) - snd_soc_dapm_enable_pin(codec, "Ext Spk"); + snd_soc_dapm_enable_pin(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(codec, "Ext Spk"); + snd_soc_dapm_disable_pin(dapm, "Ext Spk"); /* set up jack connection */ switch (spitz_jack_func) { case SPITZ_HP: /* enable and unmute hp jack, disable mic bias */ - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 1); gpio_set_value(SPITZ_GPIO_MUTE_R, 1); break; case SPITZ_MIC: /* enable mic jack and bias, mute hp */ - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 0); break; case SPITZ_LINE: /* enable line jack, disable mic bias and mute hp */ - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_enable_pin(codec, "Line Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Line Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 0); break; case SPITZ_HEADSET: /* enable and unmute headset jack enable mic bias, mute L hp */ - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_enable_pin(dapm, "Headset Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 1); break; case SPITZ_HP_OFF: /* jack removed, everything off */ - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); - snd_soc_dapm_disable_pin(codec, "Mic Jack"); - snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin(dapm, "Line Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 0); break; } - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int spitz_startup(struct snd_pcm_substream *substream) @@ -281,16 +282,17 @@ static const struct snd_kcontrol_new wm8750_spitz_controls[] = { static int spitz_wm8750_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 */ - snd_soc_dapm_nc_pin(codec, "RINPUT1"); - snd_soc_dapm_nc_pin(codec, "LINPUT2"); - snd_soc_dapm_nc_pin(codec, "RINPUT2"); - snd_soc_dapm_nc_pin(codec, "LINPUT3"); - snd_soc_dapm_nc_pin(codec, "RINPUT3"); - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONO1"); + snd_soc_dapm_nc_pin(dapm, "RINPUT1"); + snd_soc_dapm_nc_pin(dapm, "LINPUT2"); + snd_soc_dapm_nc_pin(dapm, "RINPUT2"); + snd_soc_dapm_nc_pin(dapm, "LINPUT3"); + snd_soc_dapm_nc_pin(dapm, "RINPUT3"); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONO1"); /* Add spitz specific controls */ err = snd_soc_add_controls(codec, wm8750_spitz_controls, @@ -299,13 +301,13 @@ static int spitz_wm8750_init(struct snd_soc_pcm_runtime *rtd) return err; /* Add spitz specific widgets */ - snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, ARRAY_SIZE(wm8750_dapm_widgets)); /* Set up spitz specific audio paths */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -313,10 +315,10 @@ static int spitz_wm8750_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link spitz_dai = { .name = "wm8750", .stream_name = "WM8750", - .cpu_dai_name = "pxa-is2", + .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8750-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8750-codec.0-001a", + .codec_name = "wm8750-codec.0-001b", .init = spitz_wm8750_init, .ops = &spitz_ops, }; diff --git a/sound/soc/pxa/tavorevb3.c b/sound/soc/pxa/tavorevb3.c index 248c283fc4df..f881f65ec172 100644 --- a/sound/soc/pxa/tavorevb3.c +++ b/sound/soc/pxa/tavorevb3.c @@ -18,7 +18,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/jack.h> #include <asm/mach-types.h> @@ -133,20 +132,21 @@ static struct snd_soc_card snd_soc_card_evb3 = { static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - snd_soc_dapm_new_controls(codec, evb3_dapm_widgets, + snd_soc_dapm_new_controls(dapm, evb3_dapm_widgets, ARRAY_SIZE(evb3_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* connected pins */ - snd_soc_dapm_enable_pin(codec, "Ext Speaker"); - snd_soc_dapm_enable_pin(codec, "Ext Mic 1"); - snd_soc_dapm_enable_pin(codec, "Ext Mic 3"); - snd_soc_dapm_disable_pin(codec, "Headset Mic 2"); - snd_soc_dapm_disable_pin(codec, "Headset Stereophone"); + snd_soc_dapm_enable_pin(dapm, "Ext Speaker"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic 1"); + snd_soc_dapm_enable_pin(dapm, "Ext Mic 3"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic 2"); + snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); - ret = snd_soc_dapm_sync(codec); + ret = snd_soc_dapm_sync(dapm); if (ret) return ret; diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 7b983f935454..9a2351366957 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -26,7 +26,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> #include <mach/tosa.h> @@ -49,31 +48,33 @@ static int tosa_spk_func; static void tosa_ext_control(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = &codec->dapm; + /* set up jack connection */ switch (tosa_jack_func) { case TOSA_HP: - snd_soc_dapm_disable_pin(codec, "Mic (Internal)"); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic (Internal)"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); break; case TOSA_MIC_INT: - snd_soc_dapm_enable_pin(codec, "Mic (Internal)"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_enable_pin(dapm, "Mic (Internal)"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin(dapm, "Headset Jack"); break; case TOSA_HEADSET: - snd_soc_dapm_disable_pin(codec, "Mic (Internal)"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(dapm, "Mic (Internal)"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Headset Jack"); break; } if (tosa_spk_func == TOSA_SPK_ON) - snd_soc_dapm_enable_pin(codec, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(codec, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "Speaker"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); } static int tosa_startup(struct snd_pcm_substream *substream) @@ -191,10 +192,11 @@ static const struct snd_kcontrol_new tosa_controls[] = { static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONOOUT"); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONOOUT"); /* add tosa specific controls */ err = snd_soc_add_controls(codec, tosa_controls, @@ -203,13 +205,13 @@ static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd) return err; /* add tosa specific widgets */ - snd_soc_dapm_new_controls(codec, tosa_dapm_widgets, + snd_soc_dapm_new_controls(dapm, tosa_dapm_widgets, ARRAY_SIZE(tosa_dapm_widgets)); /* set up tosa specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -217,7 +219,7 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -227,7 +229,7 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -235,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = { }, }; -static int tosa_probe(struct platform_device *dev) +static int tosa_probe(struct snd_soc_card *card) { int ret; @@ -249,7 +251,7 @@ static int tosa_probe(struct platform_device *dev) return ret; } -static int tosa_remove(struct platform_device *dev) +static int tosa_remove(struct snd_soc_card *card) { gpio_free(TOSA_GPIO_L_MUTE); return 0; diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 4cc841b44182..d69d9fc32233 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -21,7 +21,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/jack.h> #include <asm/mach-types.h> @@ -96,6 +95,11 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, }, + { + .pin = "Ext Spk", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, }; /* Headset jack detection gpios */ @@ -105,6 +109,7 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { .name = "hsdet-gpio", .report = SND_JACK_HEADSET, .debounce_time = 200, + .invert = 1, }, }; @@ -140,22 +145,23 @@ static const struct snd_soc_dapm_route audio_map[] = { static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* NC codec pins */ - snd_soc_dapm_disable_pin(codec, "LINPUT3"); - snd_soc_dapm_disable_pin(codec, "RINPUT3"); - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "MONO"); + snd_soc_dapm_disable_pin(dapm, "LINPUT3"); + snd_soc_dapm_disable_pin(dapm, "RINPUT3"); + snd_soc_dapm_disable_pin(dapm, "OUT3"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Add z2 specific widgets */ - snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, ARRAY_SIZE(wm8750_dapm_widgets)); /* Set up z2 specific audio paths */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - ret = snd_soc_dapm_sync(codec); + ret = snd_soc_dapm_sync(dapm); if (ret) goto err; @@ -192,7 +198,7 @@ static struct snd_soc_dai_link z2_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8750-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8750-codec.0-001a", + .codec_name = "wm8750-codec.0-001b", .init = z2_wm8750_init, .ops = &z2_ops, }; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index d27e05af7759..b6445757fc54 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -20,7 +20,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "../codecs/wm9713.h" #include "pxa2xx-ac97.h" @@ -73,21 +72,22 @@ static const struct snd_soc_dapm_route audio_map[] = { static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; if (clk_pout) snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, clk_get_rate(pout), 0); - snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets, + snd_soc_dapm_new_controls(dapm, zylonite_dapm_widgets, ARRAY_SIZE(zylonite_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* Static setup for now */ - snd_soc_dapm_enable_pin(codec, "Headphone"); - snd_soc_dapm_enable_pin(codec, "Headset Earpiece"); + snd_soc_dapm_enable_pin(dapm, "Headphone"); + snd_soc_dapm_enable_pin(dapm, "Headset Earpiece"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -166,8 +166,8 @@ static struct snd_soc_dai_link zylonite_dai[] = { .stream_name = "AC97 HiFi", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", - .cpu_dai_name = "pxa-ac97.0", - .codec_name = "wm9713-hifi", + .cpu_dai_name = "pxa2xx-ac97", + .codec_dai_name = "wm9713-hifi", .init = zylonite_wm9713_init, }, { @@ -175,8 +175,8 @@ static struct snd_soc_dai_link zylonite_dai[] = { .stream_name = "AC97 Aux", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", - .cpu_dai_name = "pxa-ac97.1", - .codec_name = "wm9713-aux", + .cpu_dai_name = "pxa2xx-ac97-aux", + .codec_dai_name = "wm9713-aux", }, { .name = "WM9713 Voice", @@ -184,12 +184,12 @@ static struct snd_soc_dai_link zylonite_dai[] = { .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", .cpu_dai_name = "pxa-ssp-dai.2", - .codec_name = "wm9713-voice", + .codec_dai_name = "wm9713-voice", .ops = &zylonite_voice_ops, }, }; -static int zylonite_probe(struct platform_device *pdev) +static int zylonite_probe(struct snd_soc_card *card) { int ret; @@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev) return 0; } -static int zylonite_remove(struct platform_device *pdev) +static int zylonite_remove(struct snd_soc_card *card) { if (clk_pout) { clk_disable(pout); @@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev) return 0; } -static int zylonite_suspend_post(struct platform_device *pdev, - pm_message_t state) +static int zylonite_suspend_post(struct snd_soc_card *card) { if (clk_pout) clk_disable(pout); @@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev, return 0; } -static int zylonite_resume_pre(struct platform_device *pdev) +static int zylonite_resume_pre(struct snd_soc_card *card) { int ret = 0; diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig deleted file mode 100644 index d85bf8a0abb2..000000000000 --- a/sound/soc/s3c24xx/Kconfig +++ /dev/null @@ -1,171 +0,0 @@ -config SND_S3C24XX_SOC - tristate "SoC Audio for the Samsung S3CXXXX chips" - depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 - select S3C64XX_DMA if ARCH_S3C64XX - select S3C2410_DMA if ARCH_S3C2410 - help - Say Y or M if you want to add support for codecs attached to - the S3C24XX AC97 or I2S interfaces. You will also need to - select the audio interfaces to support below. - -config SND_S3C24XX_SOC_I2S - tristate - select S3C2410_DMA - -config SND_S3C_I2SV2_SOC - tristate - -config SND_S3C2412_SOC_I2S - tristate - select SND_S3C_I2SV2_SOC - select S3C2410_DMA - -config SND_S3C64XX_SOC_I2S - tristate - select SND_S3C_I2SV2_SOC - select S3C64XX_DMA - -config SND_S3C64XX_SOC_I2S_V4 - tristate - select SND_S3C_I2SV2_SOC - select S3C64XX_DMA - -config SND_S3C_SOC_PCM - tristate - -config SND_S3C_SOC_AC97 - tristate - select SND_SOC_AC97_BUS - -config SND_S5P_SOC_SPDIF - tristate - select SND_SOC_SPDIF - -config SND_S3C24XX_SOC_NEO1973_WM8753 - tristate "SoC I2S Audio support for NEO1973 - WM8753" - depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01 - select SND_S3C24XX_SOC_I2S - select SND_SOC_WM8753 - help - Say Y if you want to add support for SoC audio on smdk2440 - with the WM8753. - -config SND_S3C24XX_SOC_NEO1973_GTA02_WM8753 - tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)" - depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA02 - select SND_S3C24XX_SOC_I2S - select SND_SOC_WM8753 - help - This driver provides audio support for the Openmoko Neo FreeRunner - smartphone. - -config SND_S3C24XX_SOC_JIVE_WM8750 - tristate "SoC I2S Audio support for Jive" - depends on SND_S3C24XX_SOC && MACH_JIVE - select SND_SOC_WM8750 - select SND_S3C2412_SOC_I2S - help - Sat Y if you want to add support for SoC audio on the Jive. - -config SND_S3C64XX_SOC_WM8580 - tristate "SoC I2S Audio support for WM8580 on SMDK64XX" - depends on SND_S3C24XX_SOC && MACH_SMDK6410 - select SND_SOC_WM8580 - select SND_S3C64XX_SOC_I2S_V4 - help - Say Y if you want to add support for SoC audio on the SMDK6410. - -config SND_S3C24XX_SOC_SMDK2443_WM9710 - tristate "SoC AC97 Audio support for SMDK2443 - WM9710" - depends on SND_S3C24XX_SOC && MACH_SMDK2443 - select S3C2410_DMA - select AC97_BUS - select SND_SOC_AC97_CODEC - select SND_S3C_SOC_AC97 - help - Say Y if you want to add support for SoC audio on smdk2443 - with the WM9710. - -config SND_S3C24XX_SOC_LN2440SBC_ALC650 - tristate "SoC AC97 Audio support for LN2440SBC - ALC650" - depends on SND_S3C24XX_SOC && ARCH_S3C2410 - select S3C2410_DMA - select AC97_BUS - select SND_SOC_AC97_CODEC - select SND_S3C_SOC_AC97 - help - Say Y if you want to add support for SoC audio on ln2440sbc - with the ALC650. - -config SND_S3C24XX_SOC_S3C24XX_UDA134X - tristate "SoC I2S Audio support UDA134X wired to a S3C24XX" - depends on SND_S3C24XX_SOC && ARCH_S3C2410 - select SND_S3C24XX_SOC_I2S - select SND_SOC_L3 - select SND_SOC_UDA134X - -config SND_S3C24XX_SOC_SIMTEC - tristate - help - Internal node for common S3C24XX/Simtec suppor - -config SND_S3C24XX_SOC_SIMTEC_TLV320AIC23 - tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards" - depends on SND_S3C24XX_SOC && ARCH_S3C2410 - select SND_S3C24XX_SOC_I2S - select SND_SOC_TLV320AIC23 - select SND_S3C24XX_SOC_SIMTEC - -config SND_S3C24XX_SOC_SIMTEC_HERMES - tristate "SoC I2S Audio support for Simtec Hermes board" - depends on SND_S3C24XX_SOC && ARCH_S3C2410 - select SND_S3C24XX_SOC_I2S - select SND_SOC_TLV320AIC3X - select SND_S3C24XX_SOC_SIMTEC - -config SND_S3C24XX_SOC_RX1950_UDA1380 - tristate "Audio support for the HP iPAQ RX1950" - depends on SND_S3C24XX_SOC && MACH_RX1950 - select SND_S3C24XX_SOC_I2S - select SND_SOC_UDA1380 - help - This driver provides audio support for HP iPAQ RX1950 PDA. - -config SND_SOC_SMDK_WM9713 - tristate "SoC AC97 Audio support for SMDK with WM9713" - depends on SND_S3C24XX_SOC && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110) - select SND_SOC_WM9713 - select SND_S3C_SOC_AC97 - help - Sat Y if you want to add support for SoC audio on the SMDK. - -config SND_S3C64XX_SOC_SMARTQ - tristate "SoC I2S Audio support for SmartQ board" - depends on SND_S3C24XX_SOC && MACH_SMARTQ - select SND_S3C64XX_SOC_I2S - select SND_SOC_WM8750 - -config SND_S5PC110_SOC_AQUILA_WM8994 - tristate "SoC I2S Audio support for AQUILA - WM8994" - depends on SND_S3C24XX_SOC && MACH_AQUILA - select SND_S3C64XX_SOC_I2S_V4 - select SND_SOC_WM8994 - help - Say Y if you want to add support for SoC audio on aquila - with the WM8994. - -config SND_S5PV210_SOC_GONI_WM8994 - tristate "SoC I2S Audio support for GONI - WM8994" - depends on SND_S3C24XX_SOC && MACH_GONI - select SND_S3C64XX_SOC_I2S_V4 - select SND_SOC_WM8994 - help - Say Y if you want to add support for SoC audio on goni - with the WM8994. - -config SND_SOC_SMDK_SPDIF - tristate "SoC S/PDIF Audio support for SMDK" - depends on SND_S3C24XX_SOC && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210) - select SND_S5P_SOC_SPDIF - help - Say Y if you want to add support for SoC S/PDIF audio on the SMDK. diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile deleted file mode 100644 index ee8f41d6df99..000000000000 --- a/sound/soc/s3c24xx/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -# S3c24XX Platform Support -snd-soc-s3c24xx-objs := s3c-dma.o -snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o -snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o -snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o -snd-soc-s3c-ac97-objs := s3c-ac97.o -snd-soc-s3c64xx-i2s-v4-objs := s3c64xx-i2s-v4.o -snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o -snd-soc-s3c-pcm-objs := s3c-pcm.o -snd-soc-samsung-spdif-objs := spdif.o - -obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o -obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o -obj-$(CONFIG_SND_S3C_SOC_AC97) += snd-soc-s3c-ac97.o -obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o -obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o -obj-$(CONFIG_SND_S3C64XX_SOC_I2S_V4) += snd-soc-s3c64xx-i2s-v4.o -obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o -obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o -obj-$(CONFIG_SND_S5P_SOC_SPDIF) += snd-soc-samsung-spdif.o - -# S3C24XX Machine Support -snd-soc-jive-wm8750-objs := jive_wm8750.o -snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o -snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o -snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o -snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o -snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o -snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o -snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o -snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o -snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o -snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o -snd-soc-smdk-wm9713-objs := smdk_wm9713.o -snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o -snd-soc-aquila-wm8994-objs := aquila_wm8994.o -snd-soc-goni-wm8994-objs := goni_wm8994.o -snd-soc-smdk-spdif-objs := smdk_spdif.o - -obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o -obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o -obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o -obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o -obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o -obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o -obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o -obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o -obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o -obj-$(CONFIG_SND_S3C24XX_SOC_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o -obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o -obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o -obj-$(CONFIG_SND_S3C64XX_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o -obj-$(CONFIG_SND_S5PC110_SOC_AQUILA_WM8994) += snd-soc-aquila-wm8994.o -obj-$(CONFIG_SND_S5PV210_SOC_GONI_WM8994) += snd-soc-goni-wm8994.o -obj-$(CONFIG_SND_SOC_SMDK_SPDIF) += snd-soc-smdk-spdif.o diff --git a/sound/soc/s3c24xx/aquila_wm8994.c b/sound/soc/s3c24xx/aquila_wm8994.c deleted file mode 100644 index 235d1973f7d0..000000000000 --- a/sound/soc/s3c24xx/aquila_wm8994.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * aquila_wm8994.c - * - * Copyright (C) 2010 Samsung Electronics Co.Ltd - * Author: Chanwoo Choi <cw00.choi@samsung.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/io.h> -#include <linux/platform_device.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/jack.h> -#include <asm/mach-types.h> -#include <mach/gpio.h> -#include <mach/regs-clock.h> - -#include <linux/mfd/wm8994/core.h> -#include <linux/mfd/wm8994/registers.h> -#include "../codecs/wm8994.h" -#include "s3c-dma.h" -#include "s3c64xx-i2s.h" - -static struct snd_soc_card aquila; -static struct platform_device *aquila_snd_device; - -/* 3.5 pie jack */ -static struct snd_soc_jack jack; - -/* 3.5 pie jack detection DAPM pins */ -static struct snd_soc_jack_pin jack_pins[] = { - { - .pin = "Headset Mic", - .mask = SND_JACK_MICROPHONE, - }, { - .pin = "Headset Stereophone", - .mask = SND_JACK_HEADPHONE | SND_JACK_MECHANICAL | - SND_JACK_AVOUT, - }, -}; - -/* 3.5 pie jack detection gpios */ -static struct snd_soc_jack_gpio jack_gpios[] = { - { - .gpio = S5PV210_GPH0(6), - .name = "DET_3.5", - .report = SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_AVOUT, - .debounce_time = 200, - }, -}; - -static const struct snd_soc_dapm_widget aquila_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Ext Spk", NULL), - SND_SOC_DAPM_SPK("Ext Rcv", NULL), - SND_SOC_DAPM_HP("Headset Stereophone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Main Mic", NULL), - SND_SOC_DAPM_MIC("2nd Mic", NULL), - SND_SOC_DAPM_LINE("Radio In", NULL), -}; - -static const struct snd_soc_dapm_route aquila_dapm_routes[] = { - {"Ext Spk", NULL, "SPKOUTLP"}, - {"Ext Spk", NULL, "SPKOUTLN"}, - - {"Ext Rcv", NULL, "HPOUT2N"}, - {"Ext Rcv", NULL, "HPOUT2P"}, - - {"Headset Stereophone", NULL, "HPOUT1L"}, - {"Headset Stereophone", NULL, "HPOUT1R"}, - - {"IN1RN", NULL, "Headset Mic"}, - {"IN1RP", NULL, "Headset Mic"}, - - {"IN1RN", NULL, "2nd Mic"}, - {"IN1RP", NULL, "2nd Mic"}, - - {"IN1LN", NULL, "Main Mic"}, - {"IN1LP", NULL, "Main Mic"}, - - {"IN2LN", NULL, "Radio In"}, - {"IN2RN", NULL, "Radio In"}, -}; - -static int aquila_wm8994_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - int ret; - - /* add aquila specific widgets */ - snd_soc_dapm_new_controls(codec, aquila_dapm_widgets, - ARRAY_SIZE(aquila_dapm_widgets)); - - /* set up aquila specific audio routes */ - snd_soc_dapm_add_routes(codec, aquila_dapm_routes, - ARRAY_SIZE(aquila_dapm_routes)); - - /* set endpoints to not connected */ - snd_soc_dapm_nc_pin(codec, "IN2LP:VXRN"); - snd_soc_dapm_nc_pin(codec, "IN2RP:VXRP"); - snd_soc_dapm_nc_pin(codec, "LINEOUT1N"); - snd_soc_dapm_nc_pin(codec, "LINEOUT1P"); - snd_soc_dapm_nc_pin(codec, "LINEOUT2N"); - snd_soc_dapm_nc_pin(codec, "LINEOUT2P"); - snd_soc_dapm_nc_pin(codec, "SPKOUTRN"); - snd_soc_dapm_nc_pin(codec, "SPKOUTRP"); - - snd_soc_dapm_sync(codec); - - /* Headset jack detection */ - ret = snd_soc_jack_new(&aquila, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT, - &jack); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins); - if (ret) - return ret; - - ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios); - if (ret) - return ret; - - return 0; -} - -static int aquila_hifi_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; - unsigned int pll_out = 24000000; - int ret = 0; - - /* set the cpu DAI configuration */ - 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; - - /* set the cpu system clock */ - ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, - 0, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set codec DAI configuration */ - 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; - - /* set the codec FLL */ - ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 0, pll_out, - params_rate(params) * 256); - if (ret < 0) - return ret; - - /* set the codec system clock */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, - params_rate(params) * 256, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops aquila_hifi_ops = { - .hw_params = aquila_hifi_hw_params, -}; - -static int aquila_voice_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; - unsigned int pll_out = 24000000; - int ret = 0; - - if (params_rate(params) != 8000) - return -EINVAL; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set the codec FLL */ - ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, pll_out, - params_rate(params) * 256); - if (ret < 0) - return ret; - - /* set the codec system clock */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2, - params_rate(params) * 256, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_dai_driver voice_dai = { - .name = "aquila-voice-dai", - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -}; - -static struct snd_soc_ops aquila_voice_ops = { - .hw_params = aquila_voice_hw_params, -}; - -static struct snd_soc_dai_link aquila_dai[] = { -{ - .name = "WM8994", - .stream_name = "WM8994 HiFi", - .cpu_dai_name = "s3c64xx-i2s-v4", - .codec_dai_name = "wm8994-hifi", - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8994-codec.0-0x1a", - .init = aquila_wm8994_init, - .ops = &aquila_hifi_ops, -}, { - .name = "WM8994 Voice", - .stream_name = "Voice", - .cpu_dai_name = "aquila-voice-dai", - .codec_dai_name = "wm8994-voice", - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8994-codec.0-0x1a", - .ops = &aquila_voice_ops, -}, -}; - -static struct snd_soc_card aquila = { - .name = "aquila", - .dai_link = aquila_dai, - .num_links = ARRAY_SIZE(aquila_dai), -}; - -static int __init aquila_init(void) -{ - int ret; - - if (!machine_is_aquila()) - return -ENODEV; - - aquila_snd_device = platform_device_alloc("soc-audio", -1); - if (!aquila_snd_device) - return -ENOMEM; - - /* register voice DAI here */ - ret = snd_soc_register_dai(&aquila_snd_device->dev, &voice_dai); - if (ret) - return ret; - - platform_set_drvdata(aquila_snd_device, &aquila); - ret = platform_device_add(aquila_snd_device); - - if (ret) - platform_device_put(aquila_snd_device); - - return ret; -} - -static void __exit aquila_exit(void) -{ - platform_device_unregister(aquila_snd_device); -} - -module_init(aquila_init); -module_exit(aquila_exit); - -/* Module information */ -MODULE_DESCRIPTION("ALSA SoC WM8994 Aquila(S5PC110)"); -MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/lm4857.h b/sound/soc/s3c24xx/lm4857.h deleted file mode 100644 index 0cf5b7011d6f..000000000000 --- a/sound/soc/s3c24xx/lm4857.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * lm4857.h -- ALSA Soc Audio Layer - * - * 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 as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * Revision history - * 18th Jun 2007 Initial version. - */ - -#ifndef LM4857_H_ -#define LM4857_H_ - -/* The register offsets in the cache array */ -#define LM4857_MVOL 0 -#define LM4857_LVOL 1 -#define LM4857_RVOL 2 -#define LM4857_CTRL 3 - -/* the shifts required to set these bits */ -#define LM4857_3D 5 -#define LM4857_WAKEUP 5 -#define LM4857_EPGAIN 4 - -#endif /*LM4857_H_*/ - diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c deleted file mode 100644 index e97bdf150a03..000000000000 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * neo1973_gta02_wm8753.c -- SoC audio for Openmoko Freerunner(GTA02) - * - * Copyright 2007 Openmoko Inc - * Author: Graeme Gregory <graeme@openmoko.org> - * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory <linux@wolfsonmicro.com> - * Copyright 2009 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 <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include <asm/mach-types.h> - -#include <plat/regs-iis.h> - -#include <mach/regs-clock.h> -#include <asm/io.h> -#include <mach/gta02.h> -#include "../codecs/wm8753.h" -#include "s3c-dma.h" -#include "s3c24xx-i2s.h" - -static struct snd_soc_card neo1973_gta02; - -static int neo1973_gta02_hifi_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; - unsigned int pll_out = 0, bclk = 0; - int ret = 0; - unsigned long iis_clkrate; - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - switch (params_rate(params)) { - case 8000: - case 16000: - pll_out = 12288000; - break; - case 48000: - bclk = WM8753_BCLK_DIV_4; - pll_out = 12288000; - break; - case 96000: - bclk = WM8753_BCLK_DIV_2; - pll_out = 12288000; - break; - case 11025: - bclk = WM8753_BCLK_DIV_16; - pll_out = 11289600; - break; - case 22050: - bclk = WM8753_BCLK_DIV_8; - pll_out = 11289600; - break; - case 44100: - bclk = WM8753_BCLK_DIV_4; - pll_out = 11289600; - break; - case 88200: - bclk = WM8753_BCLK_DIV_2; - pll_out = 11289600; - break; - } - - /* set codec DAI configuration */ - 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; - - /* set cpu DAI configuration */ - 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; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, - SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set MCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, - S3C2410_IISMOD_32FS); - if (ret < 0) - return ret; - - /* set codec BCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, - WM8753_BCLKDIV, bclk); - if (ret < 0) - return ret; - - /* set prescaler division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, - S3C24XX_PRESCALE(4, 4)); - if (ret < 0) - return ret; - - /* codec PLL input is PCLK/4 */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, - iis_clkrate / 4, pll_out); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); -} - -/* - * Neo1973 WM8753 HiFi DAI opserations. - */ -static struct snd_soc_ops neo1973_gta02_hifi_ops = { - .hw_params = neo1973_gta02_hifi_hw_params, - .hw_free = neo1973_gta02_hifi_hw_free, -}; - -static int neo1973_gta02_voice_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; - unsigned int pcmdiv = 0; - int ret = 0; - unsigned long iis_clkrate; - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - if (params_rate(params) != 8000) - return -EINVAL; - if (params_channels(params) != 1) - return -EINVAL; - - pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ - - /* todo: gg check mode (DSP_B) against CSR datasheet */ - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, - 12288000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set codec PCM division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, - pcmdiv); - if (ret < 0) - return ret; - - /* configure and enable PLL for 12.288MHz output */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, - iis_clkrate / 4, 12288000); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); -} - -static struct snd_soc_ops neo1973_gta02_voice_ops = { - .hw_params = neo1973_gta02_voice_hw_params, - .hw_free = neo1973_gta02_voice_hw_free, -}; - -#define LM4853_AMP 1 -#define LM4853_SPK 2 - -static u8 lm4853_state; - -/* This has no effect, it exists only to maintain compatibility with - * existing ALSA state files. - */ -static int lm4853_set_state(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int val = ucontrol->value.integer.value[0]; - - if (val) - lm4853_state |= LM4853_AMP; - else - lm4853_state &= ~LM4853_AMP; - - return 0; -} - -static int lm4853_get_state(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP; - - return 0; -} - -static int lm4853_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int val = ucontrol->value.integer.value[0]; - - if (val) { - lm4853_state |= LM4853_SPK; - gpio_set_value(GTA02_GPIO_HP_IN, 0); - } else { - lm4853_state &= ~LM4853_SPK; - gpio_set_value(GTA02_GPIO_HP_IN, 1); - } - - return 0; -} - -static int lm4853_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1; - - return 0; -} - -static int lm4853_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, - int event) -{ - gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event)); - - return 0; -} - -static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Handset Mic", NULL), - SND_SOC_DAPM_SPK("Handset Spk", NULL), -}; - - -/* example machine audio_mapnections */ -static const struct snd_soc_dapm_route audio_map[] = { - - /* Connections to the lm4853 amp */ - {"Stereo Out", NULL, "LOUT1"}, - {"Stereo Out", NULL, "ROUT1"}, - - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, - - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, - - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Handset Mic"}, - - /* Call Speaker */ - {"Handset Spk", NULL, "LOUT2"}, - {"Handset Spk", NULL, "ROUT2"}, - - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; - -static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = { - SOC_DAPM_PIN_SWITCH("Stereo Out"), - SOC_DAPM_PIN_SWITCH("GSM Line Out"), - SOC_DAPM_PIN_SWITCH("GSM Line In"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Spk"), - - /* This has no effect, it exists only to maintain compatibility with - * existing ALSA state files. - */ - SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0, - lm4853_get_state, - lm4853_set_state), - SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0, - lm4853_get_spk, - lm4853_set_spk), -}; - -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 GTA02. - */ -static int neo1973_gta02_wm8753_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - int err; - - /* set up NC codec pins */ - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "OUT4"); - snd_soc_dapm_nc_pin(codec, "LINE1"); - snd_soc_dapm_nc_pin(codec, "LINE2"); - - /* Add neo1973 gta02 specific widgets */ - snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); - - /* add neo1973 gta02 specific controls */ - err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls, - ARRAY_SIZE(wm8753_neo1973_gta02_controls)); - - if (err < 0) - return err; - - /* set up neo1973 gta02 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - /* set endpoints to default off mode */ - snd_soc_dapm_disable_pin(codec, "Stereo Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Handset Mic"); - snd_soc_dapm_disable_pin(codec, "Handset Spk"); - - /* allow audio paths from the GSM modem to run during suspend */ - snd_soc_dapm_ignore_suspend(codec, "Stereo Out"); - snd_soc_dapm_ignore_suspend(codec, "GSM Line Out"); - snd_soc_dapm_ignore_suspend(codec, "GSM Line In"); - snd_soc_dapm_ignore_suspend(codec, "Headset Mic"); - snd_soc_dapm_ignore_suspend(codec, "Handset Mic"); - snd_soc_dapm_ignore_suspend(codec, "Handset Spk"); - - snd_soc_dapm_sync(codec); - - return 0; -} - -/* - * BT Codec DAI - */ -static struct snd_soc_dai_driver bt_dai = { - .name = "bluetooth-dai", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -}; - -static struct snd_soc_dai_link neo1973_gta02_dai[] = { -{ /* Hifi Playback - for similatious use with voice below */ - .name = "WM8753", - .stream_name = "WM8753 HiFi", - .cpu_dai_name = "s3c24xx-i2s", - .codec_dai_name = "wm8753-hifi", - .init = neo1973_gta02_wm8753_init, - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8753-codec.0-0x1a", - .ops = &neo1973_gta02_hifi_ops, -}, -{ /* Voice via BT */ - .name = "Bluetooth", - .stream_name = "Voice", - .cpu_dai_name = "bluetooth-dai", - .codec_dai_name = "wm8753-voice", - .ops = &neo1973_gta02_voice_ops, - .codec_name = "wm8753-codec.0-0x1a", - .platform_name = "s3c24xx-pcm-audio", -}, -}; - -static struct snd_soc_card neo1973_gta02 = { - .name = "neo1973-gta02", - .dai_link = neo1973_gta02_dai, - .num_links = ARRAY_SIZE(neo1973_gta02_dai), -}; - -static struct platform_device *neo1973_gta02_snd_device; - -static int __init neo1973_gta02_init(void) -{ - int ret; - - if (!machine_is_neo1973_gta02()) { - printk(KERN_INFO - "Only GTA02 is supported by this ASoC driver\n"); - return -ENODEV; - } - - neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_gta02_snd_device) - return -ENOMEM; - - /* register bluetooth DAI here */ - ret = snd_soc_register_dai(&neo1973_gta02_snd_device->dev, -1, &bt_dai); - if (ret) { - platform_device_put(neo1973_gta02_snd_device); - return ret; - } - - platform_set_drvdata(neo1973_gta02_snd_device, &neo1973_gta02); - ret = platform_device_add(neo1973_gta02_snd_device); - - if (ret) { - platform_device_put(neo1973_gta02_snd_device); - return ret; - } - - /* Initialise GPIOs used by amp */ - ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN"); - if (ret) { - pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN); - goto err_unregister_device; - } - - ret = gpio_direction_output(GTA02_GPIO_HP_IN, 1); - if (ret) { - pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN); - goto err_free_gpio_hp_in; - } - - ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT"); - if (ret) { - pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT); - goto err_free_gpio_hp_in; - } - - ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1); - if (ret) { - pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT); - goto err_free_gpio_amp_shut; - } - - return 0; - -err_free_gpio_amp_shut: - gpio_free(GTA02_GPIO_AMP_SHUT); -err_free_gpio_hp_in: - gpio_free(GTA02_GPIO_HP_IN); -err_unregister_device: - platform_device_unregister(neo1973_gta02_snd_device); - return ret; -} -module_init(neo1973_gta02_init); - -static void __exit neo1973_gta02_exit(void) -{ - snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev, -1); - platform_device_unregister(neo1973_gta02_snd_device); - gpio_free(GTA02_GPIO_HP_IN); - gpio_free(GTA02_GPIO_AMP_SHUT); -} -module_exit(neo1973_gta02_exit); - -/* Module information */ -MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c deleted file mode 100644 index f4f2ee731f01..000000000000 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - * neo1973_wm8753.c -- SoC audio for Neo1973 - * - * 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 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/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/tlv.h> - -#include <asm/mach-types.h> -#include <asm/hardware/scoop.h> -#include <mach/regs-clock.h> -#include <mach/regs-gpio.h> -#include <mach/hardware.h> -#include <linux/io.h> -#include <mach/spi-gpio.h> - -#include <plat/regs-iis.h> - -#include "../codecs/wm8753.h" -#include "lm4857.h" -#include "s3c-dma.h" -#include "s3c24xx-i2s.h" - -/* define the scenarios */ -#define NEO_AUDIO_OFF 0 -#define NEO_GSM_CALL_AUDIO_HANDSET 1 -#define NEO_GSM_CALL_AUDIO_HEADSET 2 -#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3 -#define NEO_STEREO_TO_SPEAKERS 4 -#define NEO_STEREO_TO_HEADPHONES 5 -#define NEO_CAPTURE_HANDSET 6 -#define NEO_CAPTURE_HEADSET 7 -#define NEO_CAPTURE_BLUETOOTH 8 - -static struct snd_soc_card neo1973; -static struct i2c_client *i2c; - -static int neo1973_hifi_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; - unsigned int pll_out = 0, bclk = 0; - int ret = 0; - unsigned long iis_clkrate; - - pr_debug("Entered %s\n", __func__); - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - switch (params_rate(params)) { - case 8000: - case 16000: - pll_out = 12288000; - break; - case 48000: - bclk = WM8753_BCLK_DIV_4; - pll_out = 12288000; - break; - case 96000: - bclk = WM8753_BCLK_DIV_2; - pll_out = 12288000; - break; - case 11025: - bclk = WM8753_BCLK_DIV_16; - pll_out = 11289600; - break; - case 22050: - bclk = WM8753_BCLK_DIV_8; - pll_out = 11289600; - break; - case 44100: - bclk = WM8753_BCLK_DIV_4; - pll_out = 11289600; - break; - case 88200: - bclk = WM8753_BCLK_DIV_2; - pll_out = 11289600; - break; - } - - /* set codec DAI configuration */ - 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; - - /* set cpu DAI configuration */ - 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; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, - SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set MCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, - S3C2410_IISMOD_32FS); - if (ret < 0) - return ret; - - /* set codec BCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk); - if (ret < 0) - return ret; - - /* set prescaler division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, - S3C24XX_PRESCALE(4, 4)); - if (ret < 0) - return ret; - - /* codec PLL input is PCLK/4 */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, - iis_clkrate / 4, pll_out); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - pr_debug("Entered %s\n", __func__); - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); -} - -/* - * Neo1973 WM8753 HiFi DAI opserations. - */ -static struct snd_soc_ops neo1973_hifi_ops = { - .hw_params = neo1973_hifi_hw_params, - .hw_free = neo1973_hifi_hw_free, -}; - -static int neo1973_voice_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; - unsigned int pcmdiv = 0; - int ret = 0; - unsigned long iis_clkrate; - - pr_debug("Entered %s\n", __func__); - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - if (params_rate(params) != 8000) - return -EINVAL; - if (params_channels(params) != 1) - return -EINVAL; - - pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ - - /* todo: gg check mode (DSP_B) against CSR datasheet */ - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000, - SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set codec PCM division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv); - if (ret < 0) - return ret; - - /* configure and enable PLL for 12.288MHz output */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, - iis_clkrate / 4, 12288000); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - pr_debug("Entered %s\n", __func__); - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); -} - -static struct snd_soc_ops neo1973_voice_ops = { - .hw_params = neo1973_voice_hw_params, - .hw_free = neo1973_voice_hw_free, -}; - -static int neo1973_scenario; - -static int neo1973_get_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = neo1973_scenario; - return 0; -} - -static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) -{ - pr_debug("Entered %s\n", __func__); - - switch (neo1973_scenario) { - case NEO_AUDIO_OFF: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HANDSET: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HEADSET: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_BLUETOOTH: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line Out"); - snd_soc_dapm_enable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_STEREO_TO_SPEAKERS: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_STEREO_TO_HEADPHONES: - snd_soc_dapm_enable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_CAPTURE_HANDSET: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_enable_pin(codec, "Call Mic"); - break; - case NEO_CAPTURE_HEADSET: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_enable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - case NEO_CAPTURE_BLUETOOTH: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - break; - default: - snd_soc_dapm_disable_pin(codec, "Audio Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line Out"); - snd_soc_dapm_disable_pin(codec, "GSM Line In"); - snd_soc_dapm_disable_pin(codec, "Headset Mic"); - snd_soc_dapm_disable_pin(codec, "Call Mic"); - } - - snd_soc_dapm_sync(codec); - - return 0; -} - -static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - - pr_debug("Entered %s\n", __func__); - - if (neo1973_scenario == ucontrol->value.integer.value[0]) - return 0; - - neo1973_scenario = ucontrol->value.integer.value[0]; - set_scenario_endpoints(codec, neo1973_scenario); - return 1; -} - -static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; - -static void lm4857_write_regs(void) -{ - pr_debug("Entered %s\n", __func__); - - if (i2c_master_send(i2c, lm4857_regs, 4) != 4) - printk(KERN_ERR "lm4857: i2c write failed\n"); -} - -static int lm4857_get_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; - - pr_debug("Entered %s\n", __func__); - - ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; - return 0; -} - -static int lm4857_set_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; - - if (((lm4857_regs[reg] >> shift) & mask) == - ucontrol->value.integer.value[0]) - return 0; - - lm4857_regs[reg] &= ~(mask << shift); - lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift; - lm4857_write_regs(); - return 1; -} - -static int lm4857_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; - - pr_debug("Entered %s\n", __func__); - - if (value) - value -= 5; - - ucontrol->value.integer.value[0] = value; - return 0; -} - -static int lm4857_set_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = ucontrol->value.integer.value[0]; - - pr_debug("Entered %s\n", __func__); - - if (value) - value += 5; - - if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value) - return 0; - - lm4857_regs[LM4857_CTRL] &= 0xF0; - lm4857_regs[LM4857_CTRL] |= value; - lm4857_write_regs(); - return 1; -} - -static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_LINE("Audio Out", NULL), - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Call Mic", NULL), -}; - - -static const struct snd_soc_dapm_route dapm_routes[] = { - - /* Connections to the lm4857 amp */ - {"Audio Out", NULL, "LOUT1"}, - {"Audio Out", NULL, "ROUT1"}, - - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, - - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, - - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Call Mic"}, - - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; - -static const char *lm4857_mode[] = { - "Off", - "Call Speaker", - "Stereo Speakers", - "Stereo Speakers + Headphones", - "Headphones" -}; - -static const struct soc_enum lm4857_mode_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode), -}; - -static const char *neo_scenarios[] = { - "Off", - "GSM Handset", - "GSM Headset", - "GSM Bluetooth", - "Speakers", - "Headphones", - "Capture Handset", - "Capture Headset", - "Capture Bluetooth" -}; - -static const struct soc_enum neo_scenario_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios), -}; - -static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); -static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); - -static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { - SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, stereo_tlv), - SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, stereo_tlv), - SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, mono_tlv), - SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0], - lm4857_get_mode, lm4857_set_mode), - SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0], - neo1973_get_scenario, neo1973_set_scenario), - SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0, - lm4857_get_reg, lm4857_set_reg), -}; - -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 II. It is missing logic to detect hp/mic insertions and logic - * to re-route the audio in such an event. - */ -static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - int err; - - pr_debug("Entered %s\n", __func__); - - /* set up NC codec pins */ - snd_soc_dapm_nc_pin(codec, "LOUT2"); - snd_soc_dapm_nc_pin(codec, "ROUT2"); - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "OUT4"); - snd_soc_dapm_nc_pin(codec, "LINE1"); - snd_soc_dapm_nc_pin(codec, "LINE2"); - - /* Add neo1973 specific widgets */ - snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); - - /* set endpoints to default mode */ - set_scenario_endpoints(codec, NEO_AUDIO_OFF); - - /* add neo1973 specific controls */ - err = snd_soc_add_controls(codec, wm8753_neo1973_controls, - ARRAY_SIZE(8753_neo1973_controls)); - if (err < 0) - return err; - - /* set up neo1973 specific audio routes */ - err = snd_soc_dapm_add_routes(codec, dapm_routes, - ARRAY_SIZE(dapm_routes)); - - snd_soc_dapm_sync(codec); - return 0; -} - -/* - * BT Codec DAI - */ -static struct snd_soc_dai bt_dai = { - .name = "bluetooth-dai", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -}; - -static struct snd_soc_dai_link neo1973_dai[] = { -{ /* Hifi Playback - for similatious use with voice below */ - .name = "WM8753", - .stream_name = "WM8753 HiFi", - .platform_name = "s3c24xx-pcm-audio", - .cpu_dai_name = "s3c24xx-i2s", - .codec_dai_name = "wm8753-hifi", - .codec_name = "wm8753-codec.0-0x1a", - .init = neo1973_wm8753_init, - .ops = &neo1973_hifi_ops, -}, -{ /* Voice via BT */ - .name = "Bluetooth", - .stream_name = "Voice", - .platform_name = "s3c24xx-pcm-audio", - .cpu_dai_name = "bluetooth-dai", - .codec_dai_name = "wm8753-voice", - .codec_name = "wm8753-codec.0-0x1a", - .ops = &neo1973_voice_ops, -}, -}; - -static struct snd_soc_card neo1973 = { - .name = "neo1973", - .dai_link = neo1973_dai, - .num_links = ARRAY_SIZE(neo1973_dai), -}; - -static int lm4857_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - pr_debug("Entered %s\n", __func__); - - i2c = client; - - lm4857_write_regs(); - return 0; -} - -static int lm4857_i2c_remove(struct i2c_client *client) -{ - pr_debug("Entered %s\n", __func__); - - i2c = NULL; - - return 0; -} - -static u8 lm4857_state; - -static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) -{ - pr_debug("Entered %s\n", __func__); - - dev_dbg(&dev->dev, "lm4857_suspend\n"); - lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); - } - return 0; -} - -static int lm4857_resume(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); - lm4857_write_regs(); - } - return 0; -} - -static void lm4857_shutdown(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - - dev_dbg(&dev->dev, "lm4857_shutdown\n"); - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); -} - -static const struct i2c_device_id lm4857_i2c_id[] = { - { "neo1973_lm4857", 0 }, - { } -}; - -static struct i2c_driver lm4857_i2c_driver = { - .driver = { - .name = "LM4857 I2C Amp", - .owner = THIS_MODULE, - }, - .suspend = lm4857_suspend, - .resume = lm4857_resume, - .shutdown = lm4857_shutdown, - .probe = lm4857_i2c_probe, - .remove = lm4857_i2c_remove, - .id_table = lm4857_i2c_id, -}; - -static struct platform_device *neo1973_snd_device; - -static int __init neo1973_init(void) -{ - int ret; - - pr_debug("Entered %s\n", __func__); - - if (!machine_is_neo1973_gta01()) { - printk(KERN_INFO - "Only GTA01 hardware supported by ASoC driver\n"); - return -ENODEV; - } - - neo1973_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_snd_device) - return -ENOMEM; - - platform_set_drvdata(neo1973_snd_device, &neo1973); - ret = platform_device_add(neo1973_snd_device); - - if (ret) { - platform_device_put(neo1973_snd_device); - return ret; - } - - ret = i2c_add_driver(&lm4857_i2c_driver); - - if (ret != 0) - platform_device_unregister(neo1973_snd_device); - - return ret; -} - -static void __exit neo1973_exit(void) -{ - pr_debug("Entered %s\n", __func__); - - i2c_del_driver(&lm4857_i2c_driver); - platform_device_unregister(neo1973_snd_device); -} - -module_init(neo1973_init); -module_exit(neo1973_exit); - -/* Module information */ -MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h deleted file mode 100644 index 5dcedd07fdbb..000000000000 --- a/sound/soc/s3c24xx/s3c-ac97.h +++ /dev/null @@ -1,21 +0,0 @@ -/* sound/soc/s3c24xx/s3c-ac97.h - * - * ALSA SoC Audio Layer - S3C AC97 Controller driver - * Evolved from s3c2443-ac97.h - * - * Copyright (c) 2010 Samsung Electronics Co. Ltd - * Author: Jaswinder Singh <jassi.brar@samsung.com> - * Credits: Graeme Gregory, Sean Choi - * - * 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 __S3C_AC97_H_ -#define __S3C_AC97_H_ - -#define S3C_AC97_DAI_PCM 0 -#define S3C_AC97_DAI_MIC 1 - -#endif /* __S3C_AC97_H_ */ diff --git a/sound/soc/s3c24xx/s3c-pcm.h b/sound/soc/s3c24xx/s3c-pcm.h deleted file mode 100644 index f60baa19387d..000000000000 --- a/sound/soc/s3c24xx/s3c-pcm.h +++ /dev/null @@ -1,124 +0,0 @@ -/* sound/soc/s3c24xx/s3c-pcm.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 __S3C_PCM_H -#define __S3C_PCM_H __FILE__ - -/*Register Offsets */ -#define S3C_PCM_CTL (0x00) -#define S3C_PCM_CLKCTL (0x04) -#define S3C_PCM_TXFIFO (0x08) -#define S3C_PCM_RXFIFO (0x0C) -#define S3C_PCM_IRQCTL (0x10) -#define S3C_PCM_IRQSTAT (0x14) -#define S3C_PCM_FIFOSTAT (0x18) -#define S3C_PCM_CLRINT (0x20) - -/* PCM_CTL Bit-Fields */ -#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f) -#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13) -#define S3C_PCM_CTL_RXDIPSTICK_MASK (0x3f) -#define S3C_PCM_CTL_RXDIPSTICK_SHIFT (7) -#define S3C_PCM_CTL_TXDMA_EN (0x1<<6) -#define S3C_PCM_CTL_RXDMA_EN (0x1<<5) -#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4) -#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3) -#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2) -#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1) -#define S3C_PCM_CTL_ENABLE (0x1<<0) - -/* PCM_CLKCTL Bit-Fields */ -#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19) -#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18) -#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff) -#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff) -#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9) -#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0) - -/* PCM_TXFIFO Bit-Fields */ -#define S3C_PCM_TXFIFO_DVALID (0x1<<16) -#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0) - -/* PCM_RXFIFO Bit-Fields */ -#define S3C_PCM_RXFIFO_DVALID (0x1<<16) -#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0) - -/* PCM_IRQCTL Bit-Fields */ -#define S3C_PCM_IRQCTL_IRQEN (0x1<<14) -#define S3C_PCM_IRQCTL_WRDEN (0x1<<12) -#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11) -#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10) -#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9) -#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8) -#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7) -#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6) -#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5) -#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4) -#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3) -#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2) -#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1) -#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0) - -/* PCM_IRQSTAT Bit-Fields */ -#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13) -#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12) -#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11) -#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10) -#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9) -#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8) -#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7) -#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6) -#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5) -#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4) -#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3) -#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2) -#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1) -#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0) - -/* PCM_FIFOSTAT Bit-Fields */ -#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14) -#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13) -#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12) -#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11) -#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10) -#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4) -#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3) -#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2) -#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1) -#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0) - -#define S3C_PCM_CLKSRC_PCLK 0 -#define S3C_PCM_CLKSRC_MUX 1 - -#define S3C_PCM_SCLK_PER_FS 0 - -/** - * struct s3c_pcm_info - S3C PCM Controller information - * @dev: The parent device passed to use from the probe. - * @regs: The pointer to the device register block. - * @dma_playback: DMA information for playback channel. - * @dma_capture: DMA information for capture channel. - */ -struct s3c_pcm_info { - spinlock_t lock; - struct device *dev; - void __iomem *regs; - - unsigned int sclk_per_fs; - - /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */ - unsigned int idleclk; - - struct clk *pclk; - struct clk *cclk; - - struct s3c_dma_params *dma_playback; - struct s3c_dma_params *dma_capture; -}; - -#endif /* __S3C_PCM_H */ diff --git a/sound/soc/s3c24xx/s3c64xx-i2s-v4.c b/sound/soc/s3c24xx/s3c64xx-i2s-v4.c deleted file mode 100644 index a9628472ebfe..000000000000 --- a/sound/soc/s3c24xx/s3c64xx-i2s-v4.c +++ /dev/null @@ -1,230 +0,0 @@ -/* sound/soc/s3c24xx/s3c64xx-i2s-v4.c - * - * ALSA SoC Audio Layer - S3C64XX I2Sv4 driver - * Copyright (c) 2010 Samsung Electronics Co. Ltd - * Author: Jaswinder Singh <jassi.brar@samsung.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/clk.h> -#include <linux/gpio.h> -#include <linux/io.h> - -#include <sound/soc.h> -#include <sound/pcm_params.h> - -#include <plat/audio.h> - -#include <mach/map.h> -#include <mach/dma.h> - -#include "s3c-dma.h" -#include "regs-i2s-v2.h" -#include "s3c64xx-i2s.h" - -static struct s3c2410_dma_client s3c64xx_dma_client_out = { - .name = "I2Sv4 PCM Stereo out" -}; - -static struct s3c2410_dma_client s3c64xx_dma_client_in = { - .name = "I2Sv4 PCM Stereo in" -}; - -static struct s3c_dma_params s3c64xx_i2sv4_pcm_stereo_out; -static struct s3c_dma_params s3c64xx_i2sv4_pcm_stereo_in; -static struct s3c_i2sv2_info s3c64xx_i2sv4; - -static int s3c64xx_i2sv4_probe(struct snd_soc_dai *dai) -{ - struct s3c_i2sv2_info *i2s = &s3c64xx_i2sv4; - int ret = 0; - - snd_soc_dai_set_drvdata(dai, i2s); - - ret = s3c_i2sv2_probe(dai, i2s, i2s->base); - - return ret; -} - -static int s3c_i2sv4_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) -{ - struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(cpu_dai); - struct s3c_dma_params *dma_data; - u32 iismod; - - dev_dbg(cpu_dai->dev, "Entered %s\n", __func__); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = i2s->dma_playback; - else - dma_data = i2s->dma_capture; - - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - - iismod = readl(i2s->regs + S3C2412_IISMOD); - dev_dbg(cpu_dai->dev, "%s: r: IISMOD: %x\n", __func__, iismod); - - iismod &= ~S3C64XX_IISMOD_BLC_MASK; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - iismod |= S3C64XX_IISMOD_BLC_8BIT; - break; - case SNDRV_PCM_FORMAT_S16_LE: - break; - case SNDRV_PCM_FORMAT_S24_LE: - iismod |= S3C64XX_IISMOD_BLC_24BIT; - break; - } - - writel(iismod, i2s->regs + S3C2412_IISMOD); - dev_dbg(cpu_dai->dev, "%s: w: IISMOD: %x\n", __func__, iismod); - - return 0; -} - -static struct snd_soc_dai_ops s3c64xx_i2sv4_dai_ops = { - .hw_params = s3c_i2sv4_hw_params, -}; - -static struct snd_soc_dai_driver s3c64xx_i2s_v4_dai = { - .symmetric_rates = 1, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS, - }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS, - }, - .probe = s3c64xx_i2sv4_probe, - .ops = &s3c64xx_i2sv4_dai_ops, -}; - -static __devinit int s3c64xx_i2sv4_dev_probe(struct platform_device *pdev) -{ - struct s3c_audio_pdata *i2s_pdata; - struct s3c_i2sv2_info *i2s; - struct resource *res; - int ret; - - i2s = &s3c64xx_i2sv4; - - i2s->feature |= S3C_FEATURE_CDCLKCON; - - i2s->dma_capture = &s3c64xx_i2sv4_pcm_stereo_in; - i2s->dma_playback = &s3c64xx_i2sv4_pcm_stereo_out; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); - return -ENXIO; - } - i2s->dma_playback->channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); - return -ENXIO; - } - i2s->dma_capture->channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S SFR address\n"); - return -ENXIO; - } - - if (!request_mem_region(res->start, resource_size(res), - "s3c64xx-i2s-v4")) { - dev_err(&pdev->dev, "Unable to request SFR region\n"); - return -EBUSY; - } - i2s->dma_capture->dma_addr = res->start + S3C2412_IISRXD; - i2s->dma_playback->dma_addr = res->start + S3C2412_IISTXD; - - i2s->dma_capture->client = &s3c64xx_dma_client_in; - i2s->dma_capture->dma_size = 4; - i2s->dma_playback->client = &s3c64xx_dma_client_out; - i2s->dma_playback->dma_size = 4; - - i2s->base = res->start; - - i2s_pdata = pdev->dev.platform_data; - if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - return -EINVAL; - } - - i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus"); - if (IS_ERR(i2s->iis_cclk)) { - dev_err(&pdev->dev, "failed to get audio-bus\n"); - ret = PTR_ERR(i2s->iis_cclk); - goto err; - } - - clk_enable(i2s->iis_cclk); - - ret = s3c_i2sv2_register_dai(&pdev->dev, pdev->id, &s3c64xx_i2s_v4_dai); - if (ret != 0) - goto err_i2sv2; - - return 0; - -err_i2sv2: - clk_put(i2s->iis_cclk); -err: - return ret; -} - -static __devexit int s3c64xx_i2sv4_dev_remove(struct platform_device *pdev) -{ - struct s3c_i2sv2_info *i2s = &s3c64xx_i2sv4; - struct resource *res; - - snd_soc_unregister_dai(&pdev->dev); - clk_put(i2s->iis_cclk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); - else - dev_warn(&pdev->dev, "Unable to get I2S SFR address\n"); - - return 0; -} - -static struct platform_driver s3c64xx_i2sv4_driver = { - .probe = s3c64xx_i2sv4_dev_probe, - .remove = s3c64xx_i2sv4_dev_remove, - .driver = { - .name = "s3c64xx-iis-v4", - .owner = THIS_MODULE, - }, -}; - -static int __init s3c64xx_i2sv4_init(void) -{ - return platform_driver_register(&s3c64xx_i2sv4_driver); -} -module_init(s3c64xx_i2sv4_init); - -static void __exit s3c64xx_i2sv4_exit(void) -{ - platform_driver_unregister(&s3c64xx_i2sv4_driver); -} -module_exit(s3c64xx_i2sv4_exit); - -/* Module information */ -MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>"); -MODULE_DESCRIPTION("S3C64XX I2Sv4 SoC Interface"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c64xx-iis-v4"); diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c deleted file mode 100644 index ae7acb6c4f1d..000000000000 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ /dev/null @@ -1,242 +0,0 @@ -/* sound/soc/s3c24xx/s3c64xx-i2s.c - * - * ALSA SoC Audio Layer - S3C64XX I2S driver - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * http://armlinux.simtec.co.uk/ - * - * 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/clk.h> -#include <linux/gpio.h> -#include <linux/io.h> -#include <linux/slab.h> - -#include <sound/soc.h> - -#include <plat/audio.h> - -#include <mach/map.h> -#include <mach/dma.h> - -#include "s3c-dma.h" -#include "regs-i2s-v2.h" -#include "s3c64xx-i2s.h" - -/* The value should be set to maximum of the total number - * of I2Sv3 controllers that any supported SoC has. - */ -#define MAX_I2SV3 2 - -static struct s3c2410_dma_client s3c64xx_dma_client_out = { - .name = "I2S PCM Stereo out" -}; - -static struct s3c2410_dma_client s3c64xx_dma_client_in = { - .name = "I2S PCM Stereo in" -}; - -static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[MAX_I2SV3]; -static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[MAX_I2SV3]; -static struct s3c_i2sv2_info s3c64xx_i2s[MAX_I2SV3]; - -struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai) -{ - struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(dai); - u32 iismod = readl(i2s->regs + S3C2412_IISMOD); - - if (iismod & S3C2412_IISMOD_IMS_SYSMUX) - return i2s->iis_cclk; - else - return i2s->iis_pclk; -} -EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock); - -static int s3c64xx_i2s_probe(struct snd_soc_dai *dai) -{ - struct s3c_i2sv2_info *i2s; - int ret; - - if (dai->id >= MAX_I2SV3) { - dev_err(dai->dev, "id %d out of range\n", dai->id); - return -EINVAL; - } - - i2s = &s3c64xx_i2s[dai->id]; - snd_soc_dai_set_drvdata(dai, i2s); - - i2s->iis_cclk = clk_get(dai->dev, "audio-bus"); - if (IS_ERR(i2s->iis_cclk)) { - dev_err(dai->dev, "failed to get audio-bus\n"); - ret = PTR_ERR(i2s->iis_cclk); - goto err; - } - - clk_enable(i2s->iis_cclk); - - ret = s3c_i2sv2_probe(dai, i2s, i2s->base); - if (ret) - goto err_clk; - - return 0; - -err_clk: - clk_disable(i2s->iis_cclk); - clk_put(i2s->iis_cclk); -err: - kfree(i2s); - return ret; -} - -static int s3c64xx_i2s_remove(struct snd_soc_dai *dai) -{ - struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(dai); - - clk_disable(i2s->iis_cclk); - clk_put(i2s->iis_cclk); - kfree(i2s); - return 0; -} - -static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops; - -static struct snd_soc_dai_driver s3c64xx_i2s_dai[MAX_I2SV3] = { -{ - .name = "s3c64xx-i2s-0", - .probe = s3c64xx_i2s_probe, - .remove = s3c64xx_i2s_remove, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS,}, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS,}, - .ops = &s3c64xx_i2s_dai_ops, - .symmetric_rates = 1, -}, { - .name = "s3c64xx-i2s-1", - .probe = s3c64xx_i2s_probe, - .remove = s3c64xx_i2s_remove, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS,}, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = S3C64XX_I2S_RATES, - .formats = S3C64XX_I2S_FMTS,}, - .ops = &s3c64xx_i2s_dai_ops, - .symmetric_rates = 1, -},}; - -static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) -{ - struct s3c_audio_pdata *i2s_pdata; - struct s3c_i2sv2_info *i2s; - struct resource *res; - int i, ret; - - if (pdev->id >= MAX_I2SV3) { - dev_err(&pdev->dev, "id %d out of range\n", pdev->id); - return -EINVAL; - } - - i2s = &s3c64xx_i2s[pdev->id]; - - i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; - i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); - return -ENXIO; - } - i2s->dma_playback->channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); - return -ENXIO; - } - i2s->dma_capture->channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S SFR address\n"); - return -ENXIO; - } - - if (!request_mem_region(res->start, resource_size(res), - "s3c64xx-i2s")) { - dev_err(&pdev->dev, "Unable to request SFR region\n"); - return -EBUSY; - } - i2s->base = res->start; - - i2s_pdata = pdev->dev.platform_data; - if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - return -EINVAL; - } - i2s->dma_capture->dma_addr = res->start + S3C2412_IISRXD; - i2s->dma_playback->dma_addr = res->start + S3C2412_IISTXD; - - i2s->dma_capture->client = &s3c64xx_dma_client_in; - i2s->dma_capture->dma_size = 4; - i2s->dma_playback->client = &s3c64xx_dma_client_out; - i2s->dma_playback->dma_size = 4; - - for (i = 0; i < ARRAY_SIZE(s3c64xx_i2s_dai); i++) { - ret = s3c_i2sv2_register_dai(&pdev->dev, i, - &s3c64xx_i2s_dai[i]); - if (ret != 0) - return ret; - } - - return 0; -} - -static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev) -{ - snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(s3c64xx_i2s_dai)); - return 0; -} - -static struct platform_driver s3c64xx_iis_driver = { - .probe = s3c64xx_iis_dev_probe, - .remove = s3c64xx_iis_dev_remove, - .driver = { - .name = "s3c64xx-iis", - .owner = THIS_MODULE, - }, -}; - -static int __init s3c64xx_i2s_init(void) -{ - return platform_driver_register(&s3c64xx_iis_driver); -} -module_init(s3c64xx_i2s_init); - -static void __exit s3c64xx_i2s_exit(void) -{ - platform_driver_unregister(&s3c64xx_iis_driver); -} -module_exit(s3c64xx_i2s_exit); - -/* Module information */ -MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); -MODULE_DESCRIPTION("S3C64XX I2S SoC Interface"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c64xx-iis"); diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h deleted file mode 100644 index de4075d26f0c..000000000000 --- a/sound/soc/s3c24xx/s3c64xx-i2s.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sound/soc/s3c24xx/s3c64xx-i2s.h - * - * ALSA SoC Audio Layer - S3C64XX I2S driver - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * http://armlinux.simtec.co.uk/ - * - * 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 __SND_SOC_S3C24XX_S3C64XX_I2S_H -#define __SND_SOC_S3C24XX_S3C64XX_I2S_H __FILE__ - -struct clk; - -#include "s3c-i2s-v2.h" - -#define S3C64XX_DIV_BCLK S3C_I2SV2_DIV_BCLK -#define S3C64XX_DIV_RCLK S3C_I2SV2_DIV_RCLK -#define S3C64XX_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER - -#define S3C64XX_CLKSRC_PCLK S3C_I2SV2_CLKSRC_PCLK -#define S3C64XX_CLKSRC_MUX S3C_I2SV2_CLKSRC_AUDIOBUS -#define S3C64XX_CLKSRC_CDCLK S3C_I2SV2_CLKSRC_CDCLK - -#define S3C64XX_I2S_RATES \ - (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) - -#define S3C64XX_I2S_FMTS \ - (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) - -struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai); - -#endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */ diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c index c1244c5bc730..5890e431852f 100644 --- a/sound/soc/s6000/s6105-ipcam.c +++ b/sound/soc/s6000/s6105-ipcam.c @@ -18,11 +18,9 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <variant/dmac.h> -#include "../codecs/tlv320aic3x.h" #include "s6000-pcm.h" #include "s6000-i2s.h" @@ -107,6 +105,7 @@ static int output_type_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = kcontrol->private_data; + struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned int val = (ucontrol->value.enumerated.item[0] != 0); char *differential = "Audio Out Differential"; char *stereo = "Audio Out Stereo"; @@ -114,10 +113,10 @@ static int output_type_put(struct snd_kcontrol *kcontrol, if (kcontrol->private_value == val) return 0; kcontrol->private_value = val; - snd_soc_dapm_disable_pin(codec, val ? differential : stereo); - snd_soc_dapm_sync(codec); - snd_soc_dapm_enable_pin(codec, val ? stereo : differential); - snd_soc_dapm_sync(codec); + snd_soc_dapm_disable_pin(dapm, val ? differential : stereo); + snd_soc_dapm_sync(dapm); + snd_soc_dapm_enable_pin(dapm, val ? stereo : differential); + snd_soc_dapm_sync(dapm); return 1; } @@ -137,35 +136,36 @@ static const struct snd_kcontrol_new audio_out_mux = { static int s6105_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; /* Add s6105 specific widgets */ - snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, + snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, ARRAY_SIZE(aic3x_dapm_widgets)); /* Set up s6105 specific audio path audio_map */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* not present */ - snd_soc_dapm_nc_pin(codec, "MONO_LOUT"); - snd_soc_dapm_nc_pin(codec, "LINE2L"); - snd_soc_dapm_nc_pin(codec, "LINE2R"); + snd_soc_dapm_nc_pin(dapm, "MONO_LOUT"); + snd_soc_dapm_nc_pin(dapm, "LINE2L"); + snd_soc_dapm_nc_pin(dapm, "LINE2R"); /* not connected */ - snd_soc_dapm_nc_pin(codec, "MIC3L"); /* LINE2L on this chip */ - snd_soc_dapm_nc_pin(codec, "MIC3R"); /* LINE2R on this chip */ - snd_soc_dapm_nc_pin(codec, "LLOUT"); - snd_soc_dapm_nc_pin(codec, "RLOUT"); - snd_soc_dapm_nc_pin(codec, "HPRCOM"); + snd_soc_dapm_nc_pin(dapm, "MIC3L"); /* LINE2L on this chip */ + snd_soc_dapm_nc_pin(dapm, "MIC3R"); /* LINE2R on this chip */ + snd_soc_dapm_nc_pin(dapm, "LLOUT"); + snd_soc_dapm_nc_pin(dapm, "RLOUT"); + snd_soc_dapm_nc_pin(dapm, "HPRCOM"); /* always connected */ - snd_soc_dapm_enable_pin(codec, "Audio In"); + snd_soc_dapm_enable_pin(dapm, "Audio In"); /* must correspond to audio_out_mux.private_value initializer */ - snd_soc_dapm_disable_pin(codec, "Audio Out Differential"); - snd_soc_dapm_sync(codec); - snd_soc_dapm_enable_pin(codec, "Audio Out Stereo"); + snd_soc_dapm_disable_pin(dapm, "Audio Out Differential"); + snd_soc_dapm_sync(dapm); + snd_soc_dapm_enable_pin(dapm, "Audio Out Stereo"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); snd_ctl_add(codec->card->snd_card, snd_ctl_new1(&audio_out_mux, codec)); diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig new file mode 100644 index 000000000000..a3fdfb631469 --- /dev/null +++ b/sound/soc/samsung/Kconfig @@ -0,0 +1,164 @@ +config SND_SOC_SAMSUNG + tristate "ASoC support for Samsung" + depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_EXYNOS4 + select S3C64XX_DMA if ARCH_S3C64XX + select S3C2410_DMA if ARCH_S3C2410 + help + Say Y or M if you want to add support for codecs attached to + the Samsung SoCs' Audio interfaces. You will also need to + select the audio interfaces to support below. + +config SND_S3C24XX_I2S + tristate + select S3C2410_DMA + +config SND_S3C_I2SV2_SOC + tristate + +config SND_S3C2412_SOC_I2S + tristate + select SND_S3C_I2SV2_SOC + select S3C2410_DMA + +config SND_SAMSUNG_PCM + tristate + +config SND_SAMSUNG_AC97 + tristate + select SND_SOC_AC97_BUS + +config SND_SAMSUNG_SPDIF + tristate + select SND_SOC_SPDIF + +config SND_SAMSUNG_I2S + tristate + +config SND_SOC_SAMSUNG_NEO1973_WM8753 + tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)" + depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02) + select SND_S3C24XX_I2S + select SND_SOC_WM8753 + select SND_SOC_LM4857 if MACH_NEO1973_GTA01 + select SND_SOC_DFBMCS320 + help + Say Y here to enable audio support for the Openmoko Neo1973 + Smartphones. + +config SND_SOC_SAMSUNG_JIVE_WM8750 + tristate "SoC I2S Audio support for Jive" + depends on SND_SOC_SAMSUNG && MACH_JIVE + select SND_SOC_WM8750 + select SND_S3C2412_SOC_I2S + help + Sat Y if you want to add support for SoC audio on the Jive. + +config SND_SOC_SAMSUNG_SMDK_WM8580 + tristate "SoC I2S Audio support for WM8580 on SMDK" + depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDK6440 || MACH_SMDK6450 || MACH_SMDK6442 || MACH_SMDKV210 || MACH_SMDKC110) + select SND_SOC_WM8580 + select SND_SAMSUNG_I2S + help + Say Y if you want to add support for SoC audio on the SMDKs. + +config SND_SOC_SAMSUNG_SMDK_WM8994 + tristate "SoC I2S Audio support for WM8994 on SMDK" + depends on SND_SOC_SAMSUNG && (MACH_SMDKV310 || MACH_SMDKC210) + select SND_SOC_WM8994 + select SND_SAMSUNG_I2S + help + Say Y if you want to add support for SoC audio on the SMDKs. + +config SND_SOC_SAMSUNG_SMDK2443_WM9710 + tristate "SoC AC97 Audio support for SMDK2443 - WM9710" + depends on SND_SOC_SAMSUNG && MACH_SMDK2443 + select S3C2410_DMA + select AC97_BUS + select SND_SOC_AC97_CODEC + select SND_SAMSUNG_AC97 + help + Say Y if you want to add support for SoC audio on smdk2443 + with the WM9710. + +config SND_SOC_SAMSUNG_LN2440SBC_ALC650 + tristate "SoC AC97 Audio support for LN2440SBC - ALC650" + depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + select S3C2410_DMA + select AC97_BUS + select SND_SOC_AC97_CODEC + select SND_SAMSUNG_AC97 + help + Say Y if you want to add support for SoC audio on ln2440sbc + with the ALC650. + +config SND_SOC_SAMSUNG_S3C24XX_UDA134X + tristate "SoC I2S Audio support UDA134X wired to a S3C24XX" + depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + select SND_S3C24XX_I2S + select SND_SOC_L3 + select SND_SOC_UDA134X + +config SND_SOC_SAMSUNG_SIMTEC + tristate + help + Internal node for common S3C24XX/Simtec suppor + +config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23 + tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards" + depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + select SND_S3C24XX_I2S + select SND_SOC_TLV320AIC23 + select SND_SOC_SAMSUNG_SIMTEC + +config SND_SOC_SAMSUNG_SIMTEC_HERMES + tristate "SoC I2S Audio support for Simtec Hermes board" + depends on SND_SOC_SAMSUNG && ARCH_S3C2410 + select SND_S3C24XX_I2S + select SND_SOC_TLV320AIC3X + select SND_SOC_SAMSUNG_SIMTEC + +config SND_SOC_SAMSUNG_H1940_UDA1380 + tristate "Audio support for the HP iPAQ H1940" + depends on SND_SOC_SAMSUNG && ARCH_H1940 + select SND_S3C24XX_I2S + select SND_SOC_UDA1380 + help + This driver provides audio support for HP iPAQ h1940 PDA. + +config SND_SOC_SAMSUNG_RX1950_UDA1380 + tristate "Audio support for the HP iPAQ RX1950" + depends on SND_SOC_SAMSUNG && MACH_RX1950 + select SND_S3C24XX_I2S + select SND_SOC_UDA1380 + help + This driver provides audio support for HP iPAQ RX1950 PDA. + +config SND_SOC_SAMSUNG_SMDK_WM9713 + tristate "SoC AC97 Audio support for SMDK with WM9713" + depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110 || MACH_SMDKV310 || MACH_SMDKC210) + select SND_SOC_WM9713 + select SND_SAMSUNG_AC97 + help + Sat Y if you want to add support for SoC audio on the SMDK. + +config SND_SOC_SMARTQ + tristate "SoC I2S Audio support for SmartQ board" + depends on SND_SOC_SAMSUNG && MACH_SMARTQ + select SND_SAMSUNG_I2S + select SND_SOC_WM8750 + +config SND_SOC_GONI_AQUILA_WM8994 + tristate "SoC I2S Audio support for AQUILA/GONI - WM8994" + depends on SND_SOC_SAMSUNG && (MACH_GONI || MACH_AQUILA) + select SND_SAMSUNG_I2S + select SND_SOC_WM8994 + help + Say Y if you want to add support for SoC audio on goni or aquila + with the WM8994. + +config SND_SOC_SAMSUNG_SMDK_SPDIF + tristate "SoC S/PDIF Audio support for SMDK" + depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210) + select SND_SAMSUNG_SPDIF + help + Say Y if you want to add support for SoC S/PDIF audio on the SMDK. diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile new file mode 100644 index 000000000000..294dec05c26d --- /dev/null +++ b/sound/soc/samsung/Makefile @@ -0,0 +1,53 @@ +# S3c24XX Platform Support +snd-soc-s3c24xx-objs := dma.o +snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o +snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o +snd-soc-ac97-objs := ac97.o +snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o +snd-soc-samsung-spdif-objs := spdif.o +snd-soc-pcm-objs := pcm.o +snd-soc-i2s-objs := i2s.o + +obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c24xx.o +obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o +obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o +obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o +obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o +obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o +obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o +obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o + +# S3C24XX Machine Support +snd-soc-jive-wm8750-objs := jive_wm8750.o +snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o +snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o +snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o +snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o +snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o +snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o +snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o +snd-soc-h1940-uda1380-objs := h1940_uda1380.o +snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o +snd-soc-smdk-wm8580-objs := smdk_wm8580.o +snd-soc-smdk-wm8994-objs := smdk_wm8994.o +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 + +obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o +obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o +obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o +obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o +obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o +obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o +obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o +obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o +obj-$(CONFIG_SND_SOC_SAMSUNG_H1940_UDA1380) += snd-soc-h1940-uda1380.o +obj-$(CONFIG_SND_SOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o +obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o +obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994) += snd-soc-smdk-wm8994.o +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 diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/samsung/ac97.c index f891eb79b575..f97110e72e85 100644 --- a/sound/soc/s3c24xx/s3c-ac97.c +++ b/sound/soc/samsung/ac97.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c-ac97.c +/* sound/soc/samsung/ac97.c * * ALSA SoC Audio Layer - S3C AC97 Controller driver * Evolved from s3c2443-ac97.c @@ -12,24 +12,24 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> -#include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/clk.h> #include <sound/soc.h> -#include <plat/regs-ac97.h> #include <mach/dma.h> +#include <plat/regs-ac97.h> #include <plat/audio.h> -#include "s3c-dma.h" -#include "s3c-ac97.h" +#include "dma.h" #define AC_CMD_ADDR(x) (x << 16) #define AC_CMD_DATA(x) (x & 0xffff) +#define S3C_AC97_DAI_PCM 0 +#define S3C_AC97_DAI_MIC 1 + struct s3c_ac97_info { struct clk *ac97_clk; void __iomem *regs; @@ -122,7 +122,7 @@ static unsigned short s3c_ac97_read(struct snd_ac97 *ac97, data = (stat & 0xffff); if (addr != reg) - pr_err("s3c-ac97: req addr = %02x, rep addr = %02x\n", + pr_err("ac97: req addr = %02x, rep addr = %02x\n", reg, addr); mutex_unlock(&s3c_ac97.lock); @@ -334,7 +334,7 @@ static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = { static struct snd_soc_dai_driver s3c_ac97_dai[] = { [S3C_AC97_DAI_PCM] = { - .name = "s3c-ac97", + .name = "samsung-ac97", .ac97_control = 1, .playback = { .stream_name = "AC97 Playback", @@ -351,7 +351,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = { .ops = &s3c_ac97_dai_ops, }, [S3C_AC97_DAI_MIC] = { - .name = "s3c-ac97-mic", + .name = "samsung-ac97-mic", .ac97_control = 1, .capture = { .stream_name = "AC97 Mic Capture", @@ -407,7 +407,7 @@ static __devinit int s3c_ac97_probe(struct platform_device *pdev) } if (!request_mem_region(mem_res->start, - resource_size(mem_res), "s3c-ac97")) { + resource_size(mem_res), "ac97")) { dev_err(&pdev->dev, "Unable to request register region\n"); return -EBUSY; } @@ -431,7 +431,7 @@ static __devinit int s3c_ac97_probe(struct platform_device *pdev) s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97"); if (IS_ERR(s3c_ac97.ac97_clk)) { - dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n"); + dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n"); ret = -ENODEV; goto err2; } @@ -446,7 +446,7 @@ static __devinit int s3c_ac97_probe(struct platform_device *pdev) ret = request_irq(irq_res->start, s3c_ac97_irq, IRQF_DISABLED, "AC97", NULL); if (ret < 0) { - dev_err(&pdev->dev, "s3c-ac97: interrupt request failed.\n"); + dev_err(&pdev->dev, "ac97: interrupt request failed.\n"); goto err4; } @@ -497,7 +497,7 @@ static struct platform_driver s3c_ac97_driver = { .probe = s3c_ac97_probe, .remove = s3c_ac97_remove, .driver = { - .name = "s3c-ac97", + .name = "samsung-ac97", .owner = THIS_MODULE, }, }; @@ -517,4 +517,4 @@ module_exit(s3c_ac97_exit); MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>"); MODULE_DESCRIPTION("AC97 driver for the Samsung SoC"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c-ac97"); +MODULE_ALIAS("platform:samsung-ac97"); diff --git a/sound/soc/s3c24xx/s3c-dma.c b/sound/soc/samsung/dma.c index 243f79bf43bb..5cb3b880f0d5 100644 --- a/sound/soc/s3c24xx/s3c-dma.c +++ b/sound/soc/samsung/dma.c @@ -1,5 +1,5 @@ /* - * s3c-dma.c -- ALSA Soc Audio Layer + * dma.c -- ALSA Soc Audio Layer * * (c) 2006 Wolfson Microelectronics PLC. * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com @@ -14,25 +14,22 @@ * option) any later version. */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dma-mapping.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <asm/dma.h> #include <mach/hardware.h> #include <mach/dma.h> -#include "s3c-dma.h" +#include "dma.h" + +#define ST_RUNNING (1<<0) +#define ST_OPENED (1<<1) -static const struct snd_pcm_hardware s3c_dma_hardware = { +static const struct snd_pcm_hardware dma_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -53,7 +50,7 @@ static const struct snd_pcm_hardware s3c_dma_hardware = { .fifo_size = 32, }; -struct s3c24xx_runtime_data { +struct runtime_data { spinlock_t lock; int state; unsigned int dma_loaded; @@ -65,14 +62,14 @@ struct s3c24xx_runtime_data { struct s3c_dma_params *params; }; -/* s3c_dma_enqueue +/* dma_enqueue * * place a dma buffer onto the queue for the dma system * to handle. */ -static void s3c_dma_enqueue(struct snd_pcm_substream *substream) +static void dma_enqueue(struct snd_pcm_substream *substream) { - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + struct runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; unsigned int limit; int ret; @@ -112,12 +109,12 @@ static void s3c_dma_enqueue(struct snd_pcm_substream *substream) prtd->dma_pos = pos; } -static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, +static void audio_buffdone(struct s3c2410_dma_chan *channel, void *dev_id, int size, enum s3c2410_dma_buffresult result) { struct snd_pcm_substream *substream = dev_id; - struct s3c24xx_runtime_data *prtd; + struct runtime_data *prtd; pr_debug("Entered %s\n", __func__); @@ -132,17 +129,17 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, spin_lock(&prtd->lock); if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { prtd->dma_loaded--; - s3c_dma_enqueue(substream); + dma_enqueue(substream); } spin_unlock(&prtd->lock); } -static int s3c_dma_hw_params(struct snd_pcm_substream *substream, +static int dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd = runtime->private_data; + struct runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned long totbytes = params_buffer_bytes(params); struct s3c_dma_params *dma = @@ -181,7 +178,7 @@ static int s3c_dma_hw_params(struct snd_pcm_substream *substream, } s3c2410_dma_set_buffdone_fn(prtd->params->channel, - s3c24xx_audio_buffdone); + audio_buffdone); snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); @@ -199,9 +196,9 @@ static int s3c_dma_hw_params(struct snd_pcm_substream *substream, return 0; } -static int s3c_dma_hw_free(struct snd_pcm_substream *substream) +static int dma_hw_free(struct snd_pcm_substream *substream) { - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + struct runtime_data *prtd = substream->runtime->private_data; pr_debug("Entered %s\n", __func__); @@ -216,9 +213,9 @@ static int s3c_dma_hw_free(struct snd_pcm_substream *substream) return 0; } -static int s3c_dma_prepare(struct snd_pcm_substream *substream) +static int dma_prepare(struct snd_pcm_substream *substream) { - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + struct runtime_data *prtd = substream->runtime->private_data; int ret = 0; pr_debug("Entered %s\n", __func__); @@ -249,14 +246,14 @@ static int s3c_dma_prepare(struct snd_pcm_substream *substream) prtd->dma_pos = prtd->dma_start; /* enqueue dma buffers */ - s3c_dma_enqueue(substream); + dma_enqueue(substream); return ret; } -static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) +static int dma_trigger(struct snd_pcm_substream *substream, int cmd) { - struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; + struct runtime_data *prtd = substream->runtime->private_data; int ret = 0; pr_debug("Entered %s\n", __func__); @@ -289,10 +286,10 @@ static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) } static snd_pcm_uframes_t -s3c_dma_pointer(struct snd_pcm_substream *substream) +dma_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd = runtime->private_data; + struct runtime_data *prtd = runtime->private_data; unsigned long res; dma_addr_t src, dst; @@ -313,7 +310,7 @@ s3c_dma_pointer(struct snd_pcm_substream *substream) /* we seem to be getting the odd error from the pcm library due * to out-of-bounds pointers. this is maybe due to the dma engine * not having loaded the new values for the channel before being - * callled... (todo - fix ) + * called... (todo - fix ) */ if (res >= snd_pcm_lib_buffer_bytes(substream)) { @@ -324,17 +321,17 @@ s3c_dma_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, res); } -static int s3c_dma_open(struct snd_pcm_substream *substream) +static int dma_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd; + struct runtime_data *prtd; pr_debug("Entered %s\n", __func__); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware); + snd_soc_set_runtime_hwparams(substream, &dma_hardware); - prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); + prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL); if (prtd == NULL) return -ENOMEM; @@ -344,22 +341,22 @@ static int s3c_dma_open(struct snd_pcm_substream *substream) return 0; } -static int s3c_dma_close(struct snd_pcm_substream *substream) +static int dma_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct s3c24xx_runtime_data *prtd = runtime->private_data; + struct runtime_data *prtd = runtime->private_data; pr_debug("Entered %s\n", __func__); if (!prtd) - pr_debug("s3c_dma_close called with prtd == NULL\n"); + pr_debug("dma_close called with prtd == NULL\n"); kfree(prtd); return 0; } -static int s3c_dma_mmap(struct snd_pcm_substream *substream, +static int dma_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -372,23 +369,23 @@ static int s3c_dma_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); } -static struct snd_pcm_ops s3c_dma_ops = { - .open = s3c_dma_open, - .close = s3c_dma_close, +static struct snd_pcm_ops dma_ops = { + .open = dma_open, + .close = dma_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = s3c_dma_hw_params, - .hw_free = s3c_dma_hw_free, - .prepare = s3c_dma_prepare, - .trigger = s3c_dma_trigger, - .pointer = s3c_dma_pointer, - .mmap = s3c_dma_mmap, + .hw_params = dma_hw_params, + .hw_free = dma_hw_free, + .prepare = dma_prepare, + .trigger = dma_trigger, + .pointer = dma_pointer, + .mmap = dma_mmap, }; -static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = s3c_dma_hardware.buffer_bytes_max; + size_t size = dma_hardware.buffer_bytes_max; pr_debug("Entered %s\n", __func__); @@ -403,7 +400,7 @@ static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) return 0; } -static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) +static void dma_free_dma_buffers(struct snd_pcm *pcm) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -426,9 +423,9 @@ static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) } } -static u64 s3c_dma_mask = DMA_BIT_MASK(32); +static u64 dma_mask = DMA_BIT_MASK(32); -static int s3c_dma_new(struct snd_card *card, +static int dma_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { int ret = 0; @@ -436,67 +433,67 @@ static int s3c_dma_new(struct snd_card *card, pr_debug("Entered %s\n", __func__); if (!card->dev->dma_mask) - card->dev->dma_mask = &s3c_dma_mask; + card->dev->dma_mask = &dma_mask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff; if (dai->driver->playback.channels_min) { - ret = s3c_preallocate_dma_buffer(pcm, + ret = preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } if (dai->driver->capture.channels_min) { - ret = s3c_preallocate_dma_buffer(pcm, + ret = preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) goto out; } - out: +out: return ret; } -static struct snd_soc_platform_driver s3c24xx_soc_platform = { - .ops = &s3c_dma_ops, - .pcm_new = s3c_dma_new, - .pcm_free = s3c_dma_free_dma_buffers, +static struct snd_soc_platform_driver samsung_asoc_platform = { + .ops = &dma_ops, + .pcm_new = dma_new, + .pcm_free = dma_free_dma_buffers, }; -static int __devinit s3c24xx_soc_platform_probe(struct platform_device *pdev) +static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &s3c24xx_soc_platform); + return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform); } -static int __devexit s3c24xx_soc_platform_remove(struct platform_device *pdev) +static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev) { snd_soc_unregister_platform(&pdev->dev); return 0; } -static struct platform_driver s3c24xx_pcm_driver = { +static struct platform_driver asoc_dma_driver = { .driver = { - .name = "s3c24xx-pcm-audio", + .name = "samsung-audio", .owner = THIS_MODULE, }, - .probe = s3c24xx_soc_platform_probe, - .remove = __devexit_p(s3c24xx_soc_platform_remove), + .probe = samsung_asoc_platform_probe, + .remove = __devexit_p(samsung_asoc_platform_remove), }; -static int __init snd_s3c24xx_pcm_init(void) +static int __init samsung_asoc_init(void) { - return platform_driver_register(&s3c24xx_pcm_driver); + return platform_driver_register(&asoc_dma_driver); } -module_init(snd_s3c24xx_pcm_init); +module_init(samsung_asoc_init); -static void __exit snd_s3c24xx_pcm_exit(void) +static void __exit samsung_asoc_exit(void) { - platform_driver_unregister(&s3c24xx_pcm_driver); + platform_driver_unregister(&asoc_dma_driver); } -module_exit(snd_s3c24xx_pcm_exit); +module_exit(samsung_asoc_exit); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); -MODULE_DESCRIPTION("Samsung S3C Audio DMA module"); +MODULE_DESCRIPTION("Samsung ASoC DMA Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c24xx-pcm-audio"); +MODULE_ALIAS("platform:samsung-audio"); diff --git a/sound/soc/s3c24xx/s3c-dma.h b/sound/soc/samsung/dma.h index 748c07d7c075..c50659269a40 100644 --- a/sound/soc/s3c24xx/s3c-dma.h +++ b/sound/soc/samsung/dma.h @@ -1,5 +1,5 @@ /* - * s3c-dma.h -- + * dma.h -- * * 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 @@ -12,9 +12,6 @@ #ifndef _S3C_AUDIO_H #define _S3C_AUDIO_H -#define ST_RUNNING (1<<0) -#define ST_OPENED (1<<1) - struct s3c_dma_params { struct s3c2410_dma_client *client; /* stream identifier */ int channel; /* Channel ID */ @@ -22,9 +19,4 @@ struct s3c_dma_params { int dma_size; /* Size of the DMA transfer */ }; -#define S3C24XX_DAI_I2S 0 - -/* platform data */ -extern struct snd_ac97_bus_ops s3c24xx_ac97_ops; - #endif diff --git a/sound/soc/s3c24xx/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c index 694f702cc8e2..0e80daee8b6f 100644 --- a/sound/soc/s3c24xx/goni_wm8994.c +++ b/sound/soc/samsung/goni_wm8994.c @@ -11,22 +11,21 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/io.h> -#include <linux/platform_device.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/jack.h> + #include <asm/mach-types.h> #include <mach/gpio.h> -#include <mach/regs-clock.h> -#include <linux/mfd/wm8994/core.h> -#include <linux/mfd/wm8994/registers.h> #include "../codecs/wm8994.h" -#include "s3c-dma.h" -#include "s3c64xx-i2s.h" + +#define MACHINE_NAME 0 +#define CPU_VOICE_DAI 1 + +static const char *aquila_str[] = { + [MACHINE_NAME] = "aquila", + [CPU_VOICE_DAI] = "aquila-voice-dai", +}; static struct snd_soc_card goni; static struct platform_device *goni_snd_device; @@ -97,28 +96,34 @@ static const struct snd_soc_dapm_route goni_dapm_routes[] = { static int goni_wm8994_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; /* add goni specific widgets */ - snd_soc_dapm_new_controls(codec, goni_dapm_widgets, + snd_soc_dapm_new_controls(dapm, goni_dapm_widgets, ARRAY_SIZE(goni_dapm_widgets)); /* set up goni specific audio routes */ - snd_soc_dapm_add_routes(codec, goni_dapm_routes, + snd_soc_dapm_add_routes(dapm, goni_dapm_routes, ARRAY_SIZE(goni_dapm_routes)); /* set endpoints to not connected */ - snd_soc_dapm_nc_pin(codec, "IN2LP:VXRN"); - snd_soc_dapm_nc_pin(codec, "IN2RP:VXRP"); - snd_soc_dapm_nc_pin(codec, "LINEOUT1N"); - snd_soc_dapm_nc_pin(codec, "LINEOUT1P"); - snd_soc_dapm_nc_pin(codec, "LINEOUT2N"); - snd_soc_dapm_nc_pin(codec, "LINEOUT2P"); + snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN"); + snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); + + if (machine_is_aquila()) { + snd_soc_dapm_nc_pin(dapm, "SPKOUTRN"); + snd_soc_dapm_nc_pin(dapm, "SPKOUTRP"); + } - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); /* Headset jack detection */ - ret = snd_soc_jack_new(&goni, "Headset Jack", + ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT, &jack); if (ret) @@ -150,12 +155,6 @@ static int goni_hifi_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* set the cpu system clock */ - ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, - 0, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); @@ -236,19 +235,19 @@ static struct snd_soc_dai_link goni_dai[] = { { .name = "WM8994", .stream_name = "WM8994 HiFi", - .cpu_dai_name = "s3c64xx-i2s-v4", - .codec_dai_name = "wm8994-hifi", - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8994-codec.0-0x1a", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "wm8994-aif1", + .platform_name = "samsung-audio", + .codec_name = "wm8994-codec.0-001a", .init = goni_wm8994_init, .ops = &goni_hifi_ops, }, { .name = "WM8994 Voice", .stream_name = "Voice", .cpu_dai_name = "goni-voice-dai", - .codec_dai_name = "wm8994-voice", - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8994-codec.0-0x1a", + .codec_dai_name = "wm8994-aif2", + .platform_name = "samsung-audio", + .codec_name = "wm8994-codec.0-001a", .ops = &goni_voice_ops, }, }; @@ -263,7 +262,11 @@ static int __init goni_init(void) { int ret; - if (!machine_is_goni()) + if (machine_is_aquila()) { + voice_dai.name = aquila_str[CPU_VOICE_DAI]; + goni_dai[1].cpu_dai_name = aquila_str[CPU_VOICE_DAI]; + goni.name = aquila_str[MACHINE_NAME]; + } else if (!machine_is_goni()) return -ENODEV; goni_snd_device = platform_device_alloc("soc-audio", -1); @@ -272,20 +275,25 @@ static int __init goni_init(void) /* register voice DAI here */ ret = snd_soc_register_dai(&goni_snd_device->dev, &voice_dai); - if (ret) + if (ret) { + platform_device_put(goni_snd_device); return ret; + } platform_set_drvdata(goni_snd_device, &goni); ret = platform_device_add(goni_snd_device); - if (ret) + if (ret) { + snd_soc_unregister_dai(&goni_snd_device->dev); platform_device_put(goni_snd_device); + } return ret; } static void __exit goni_exit(void) { + snd_soc_unregister_dai(&goni_snd_device->dev); platform_device_unregister(goni_snd_device); } diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c new file mode 100644 index 000000000000..241f55d00660 --- /dev/null +++ b/sound/soc/samsung/h1940_uda1380.c @@ -0,0 +1,287 @@ +/* + * h1940-uda1380.c -- ALSA Soc Audio Layer + * + * Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> + * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com> + * + * Based on version from Arnaud Patard <arnaud.patard@rtp-net.org> + * + * 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/gpio.h> + +#include <sound/soc.h> +#include <sound/jack.h> + +#include <plat/regs-iis.h> +#include <mach/h1940-latch.h> +#include <asm/mach-types.h> + +#include "s3c24xx-i2s.h" + +static unsigned int rates[] = { + 11025, + 22050, + 44100, +}; + +static struct snd_pcm_hw_constraint_list hw_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static struct snd_soc_jack hp_jack; + +static struct snd_soc_jack_pin hp_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1, + }, +}; + +static struct snd_soc_jack_gpio hp_jack_gpios[] = { + { + .gpio = S3C2410_GPG(4), + .name = "hp-gpio", + .report = SND_JACK_HEADPHONE, + .invert = 1, + .debounce_time = 200, + }, +}; + +static int h1940_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.rate_min = hw_rates.list[0]; + runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1]; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + + return snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_rates); +} + +static int h1940_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 div; + int ret; + unsigned int rate = params_rate(params); + + switch (rate) { + case 11025: + case 22050: + case 44100: + div = s3c24xx_i2s_get_clockrate() / (384 * rate); + if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate)) + div++; + break; + default: + dev_err(&rtd->dev, "%s: rate %d is not supported\n", + __func__, rate); + return -EINVAL; + } + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + 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_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* select clock source */ + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + /* set MCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, + S3C2410_IISMOD_384FS); + if (ret < 0) + return ret; + + /* set BCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, + S3C2410_IISMOD_32FS); + if (ret < 0) + return ret; + + /* set prescaler division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, + S3C24XX_PRESCALE(div, div)); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops h1940_ops = { + .startup = h1940_startup, + .hw_params = h1940_hw_params, +}; + +static int h1940_spk_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + gpio_set_value(H1940_LATCH_AUDIO_POWER, 1); + else + gpio_set_value(H1940_LATCH_AUDIO_POWER, 0); + + return 0; +} + +/* h1940 machine dapm widgets */ +static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", h1940_spk_power), +}; + +/* h1940 machine audio_map */ +static const struct snd_soc_dapm_route audio_map[] = { + /* headphone connected to VOUTLHP, VOUTRHP */ + {"Headphone Jack", NULL, "VOUTLHP"}, + {"Headphone Jack", NULL, "VOUTRHP"}, + + /* ext speaker connected to VOUTL, VOUTR */ + {"Speaker", NULL, "VOUTL"}, + {"Speaker", NULL, "VOUTR"}, + + /* mic is connected to VINM */ + {"VINM", NULL, "Mic Jack"}, +}; + +static struct platform_device *s3c24xx_snd_device; + +static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int err; + + /* Add h1940 specific widgets */ + err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets, + ARRAY_SIZE(uda1380_dapm_widgets)); + if (err) + return err; + + /* Set up h1940 specific audio path audio_mapnects */ + err = snd_soc_dapm_add_routes(dapm, audio_map, + ARRAY_SIZE(audio_map)); + if (err) + return err; + + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + + snd_soc_dapm_sync(dapm); + + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack); + + snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), + hp_jack_pins); + + snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); + + return 0; +} + +/* s3c24xx digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link h1940_uda1380_dai[] = { + { + .name = "uda1380", + .stream_name = "UDA1380 Duplex", + .cpu_dai_name = "s3c24xx-iis", + .codec_dai_name = "uda1380-hifi", + .init = h1940_uda1380_init, + .platform_name = "samsung-audio", + .codec_name = "uda1380-codec.0-001a", + .ops = &h1940_ops, + }, +}; + +static struct snd_soc_card h1940_asoc = { + .name = "h1940", + .dai_link = h1940_uda1380_dai, + .num_links = ARRAY_SIZE(h1940_uda1380_dai), +}; + +static int __init h1940_init(void) +{ + int ret; + + if (!machine_is_h1940()) + return -ENODEV; + + /* configure some gpios */ + ret = gpio_request(H1940_LATCH_AUDIO_POWER, "speaker-power"); + if (ret) + goto err_out; + + ret = gpio_direction_output(H1940_LATCH_AUDIO_POWER, 0); + if (ret) + goto err_gpio; + + s3c24xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!s3c24xx_snd_device) { + ret = -ENOMEM; + goto err_gpio; + } + + platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc); + ret = platform_device_add(s3c24xx_snd_device); + + if (ret) + goto err_plat; + + return 0; + +err_plat: + platform_device_put(s3c24xx_snd_device); +err_gpio: + gpio_free(H1940_LATCH_AUDIO_POWER); + +err_out: + return ret; +} + +static void __exit h1940_exit(void) +{ + platform_device_unregister(s3c24xx_snd_device); + snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); + gpio_free(H1940_LATCH_AUDIO_POWER); +} + +module_init(h1940_init); +module_exit(h1940_exit); + +/* Module information */ +MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick"); +MODULE_DESCRIPTION("ALSA SoC H1940"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c new file mode 100644 index 000000000000..ffa09b3b2caa --- /dev/null +++ b/sound/soc/samsung/i2s.c @@ -0,0 +1,1257 @@ +/* sound/soc/samsung/i2s.c + * + * ALSA SoC Audio Layer - Samsung I2S Controller driver + * + * Copyright (c) 2010 Samsung Electronics Co. Ltd. + * Jaswinder Singh <jassi.brar@samsung.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/delay.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include <plat/audio.h> + +#include "dma.h" +#include "i2s.h" + +#define I2SCON 0x0 +#define I2SMOD 0x4 +#define I2SFIC 0x8 +#define I2SPSR 0xc +#define I2STXD 0x10 +#define I2SRXD 0x14 +#define I2SFICS 0x18 +#define I2STXDS 0x1c + +#define CON_RSTCLR (1 << 31) +#define CON_FRXOFSTATUS (1 << 26) +#define CON_FRXORINTEN (1 << 25) +#define CON_FTXSURSTAT (1 << 24) +#define CON_FTXSURINTEN (1 << 23) +#define CON_TXSDMA_PAUSE (1 << 20) +#define CON_TXSDMA_ACTIVE (1 << 18) + +#define CON_FTXURSTATUS (1 << 17) +#define CON_FTXURINTEN (1 << 16) +#define CON_TXFIFO2_EMPTY (1 << 15) +#define CON_TXFIFO1_EMPTY (1 << 14) +#define CON_TXFIFO2_FULL (1 << 13) +#define CON_TXFIFO1_FULL (1 << 12) + +#define CON_LRINDEX (1 << 11) +#define CON_TXFIFO_EMPTY (1 << 10) +#define CON_RXFIFO_EMPTY (1 << 9) +#define CON_TXFIFO_FULL (1 << 8) +#define CON_RXFIFO_FULL (1 << 7) +#define CON_TXDMA_PAUSE (1 << 6) +#define CON_RXDMA_PAUSE (1 << 5) +#define CON_TXCH_PAUSE (1 << 4) +#define CON_RXCH_PAUSE (1 << 3) +#define CON_TXDMA_ACTIVE (1 << 2) +#define CON_RXDMA_ACTIVE (1 << 1) +#define CON_ACTIVE (1 << 0) + +#define MOD_OPCLK_CDCLK_OUT (0 << 30) +#define MOD_OPCLK_CDCLK_IN (1 << 30) +#define MOD_OPCLK_BCLK_OUT (2 << 30) +#define MOD_OPCLK_PCLK (3 << 30) +#define MOD_OPCLK_MASK (3 << 30) +#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ + +#define MOD_BLCS_SHIFT 26 +#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT) +#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT) +#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT) +#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT) +#define MOD_BLCP_SHIFT 24 +#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT) +#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT) +#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT) +#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT) + +#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define MOD_C1DD_HHALF (1 << 19) +#define MOD_C1DD_LHALF (1 << 18) +#define MOD_DC2_EN (1 << 17) +#define MOD_DC1_EN (1 << 16) +#define MOD_BLC_16BIT (0 << 13) +#define MOD_BLC_8BIT (1 << 13) +#define MOD_BLC_24BIT (2 << 13) +#define MOD_BLC_MASK (3 << 13) + +#define MOD_IMS_SYSMUX (1 << 10) +#define MOD_SLAVE (1 << 11) +#define MOD_TXONLY (0 << 8) +#define MOD_RXONLY (1 << 8) +#define MOD_TXRX (2 << 8) +#define MOD_MASK (3 << 8) +#define MOD_LR_LLOW (0 << 7) +#define MOD_LR_RLOW (1 << 7) +#define MOD_SDF_IIS (0 << 5) +#define MOD_SDF_MSB (1 << 5) +#define MOD_SDF_LSB (2 << 5) +#define MOD_SDF_MASK (3 << 5) +#define MOD_RCLK_256FS (0 << 3) +#define MOD_RCLK_512FS (1 << 3) +#define MOD_RCLK_384FS (2 << 3) +#define MOD_RCLK_768FS (3 << 3) +#define MOD_RCLK_MASK (3 << 3) +#define MOD_BCLK_32FS (0 << 1) +#define MOD_BCLK_48FS (1 << 1) +#define MOD_BCLK_16FS (2 << 1) +#define MOD_BCLK_24FS (3 << 1) +#define MOD_BCLK_MASK (3 << 1) +#define MOD_8BIT (1 << 0) + +#define MOD_CDCLKCON (1 << 12) + +#define PSR_PSREN (1 << 15) + +#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) + +#define FIC_TXFLUSH (1 << 15) +#define FIC_RXFLUSH (1 << 7) +#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) +#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) +#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) + +#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) + +struct i2s_dai { + /* Platform device for this DAI */ + struct platform_device *pdev; + /* IOREMAP'd SFRs */ + void __iomem *addr; + /* Physical base address of SFRs */ + u32 base; + /* Rate of RCLK source clock */ + unsigned long rclk_srcrate; + /* Frame Clock */ + unsigned frmclk; + /* + * Specifically requested RCLK,BCLK by MACHINE Driver. + * 0 indicates CPU driver is free to choose any value. + */ + unsigned rfs, bfs; + /* I2S Controller's core clock */ + struct clk *clk; + /* Clock for generating I2S signals */ + struct clk *op_clk; + /* Array of clock names for op_clk */ + const char **src_clk; + /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ + struct i2s_dai *pri_dai; + /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ + struct i2s_dai *sec_dai; +#define DAI_OPENED (1 << 0) /* Dai is opened */ +#define DAI_MANAGER (1 << 1) /* Dai is the manager */ + unsigned mode; + /* Driver for this DAI */ + struct snd_soc_dai_driver i2s_dai_drv; + /* DMA parameters */ + struct s3c_dma_params dma_playback; + struct s3c_dma_params dma_capture; + u32 quirks; + u32 suspend_i2smod; + u32 suspend_i2scon; + u32 suspend_i2spsr; +}; + +/* Lock for cross i/f checks */ +static DEFINE_SPINLOCK(lock); + +/* If this is the 'overlay' stereo DAI */ +static inline bool is_secondary(struct i2s_dai *i2s) +{ + return i2s->pri_dai ? true : false; +} + +/* If operating in SoC-Slave mode */ +static inline bool is_slave(struct i2s_dai *i2s) +{ + return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false; +} + +/* If this interface of the controller is transmitting data */ +static inline bool tx_active(struct i2s_dai *i2s) +{ + u32 active; + + if (!i2s) + return false; + + active = readl(i2s->addr + I2SMOD); + + if (is_secondary(i2s)) + active &= CON_TXSDMA_ACTIVE; + else + active &= CON_TXDMA_ACTIVE; + + return active ? true : false; +} + +/* If the other interface of the controller is transmitting data */ +static inline bool other_tx_active(struct i2s_dai *i2s) +{ + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + + return tx_active(other); +} + +/* If any interface of the controller is transmitting data */ +static inline bool any_tx_active(struct i2s_dai *i2s) +{ + return tx_active(i2s) || other_tx_active(i2s); +} + +/* If this interface of the controller is receiving data */ +static inline bool rx_active(struct i2s_dai *i2s) +{ + u32 active; + + if (!i2s) + return false; + + active = readl(i2s->addr + I2SMOD) & CON_RXDMA_ACTIVE; + + return active ? true : false; +} + +/* If the other interface of the controller is receiving data */ +static inline bool other_rx_active(struct i2s_dai *i2s) +{ + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + + return rx_active(other); +} + +/* If any interface of the controller is receiving data */ +static inline bool any_rx_active(struct i2s_dai *i2s) +{ + return rx_active(i2s) || other_rx_active(i2s); +} + +/* If the other DAI is transmitting or receiving data */ +static inline bool other_active(struct i2s_dai *i2s) +{ + return other_rx_active(i2s) || other_tx_active(i2s); +} + +/* If this DAI is transmitting or receiving data */ +static inline bool this_active(struct i2s_dai *i2s) +{ + return tx_active(i2s) || rx_active(i2s); +} + +/* If the controller is active anyway */ +static inline bool any_active(struct i2s_dai *i2s) +{ + return this_active(i2s) || other_active(i2s); +} + +static inline struct i2s_dai *to_info(struct snd_soc_dai *dai) +{ + return snd_soc_dai_get_drvdata(dai); +} + +static inline bool is_opened(struct i2s_dai *i2s) +{ + if (i2s && (i2s->mode & DAI_OPENED)) + return true; + else + return false; +} + +static inline bool is_manager(struct i2s_dai *i2s) +{ + if (is_opened(i2s) && (i2s->mode & DAI_MANAGER)) + return true; + else + return false; +} + +/* Read RCLK of I2S (in multiples of LRCLK) */ +static inline unsigned get_rfs(struct i2s_dai *i2s) +{ + u32 rfs = (readl(i2s->addr + I2SMOD) >> 3) & 0x3; + + switch (rfs) { + case 3: return 768; + case 2: return 384; + case 1: return 512; + default: return 256; + } +} + +/* Write RCLK of I2S (in multiples of LRCLK) */ +static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) +{ + u32 mod = readl(i2s->addr + I2SMOD); + + mod &= ~MOD_RCLK_MASK; + + switch (rfs) { + case 768: + mod |= MOD_RCLK_768FS; + break; + case 512: + mod |= MOD_RCLK_512FS; + break; + case 384: + mod |= MOD_RCLK_384FS; + break; + default: + mod |= MOD_RCLK_256FS; + break; + } + + writel(mod, i2s->addr + I2SMOD); +} + +/* Read Bit-Clock of I2S (in multiples of LRCLK) */ +static inline unsigned get_bfs(struct i2s_dai *i2s) +{ + u32 bfs = (readl(i2s->addr + I2SMOD) >> 1) & 0x3; + + switch (bfs) { + case 3: return 24; + case 2: return 16; + case 1: return 48; + default: return 32; + } +} + +/* Write Bit-Clock of I2S (in multiples of LRCLK) */ +static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) +{ + u32 mod = readl(i2s->addr + I2SMOD); + + mod &= ~MOD_BCLK_MASK; + + switch (bfs) { + case 48: + mod |= MOD_BCLK_48FS; + break; + case 32: + mod |= MOD_BCLK_32FS; + break; + case 24: + mod |= MOD_BCLK_24FS; + break; + case 16: + mod |= MOD_BCLK_16FS; + break; + default: + dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); + return; + } + + writel(mod, i2s->addr + I2SMOD); +} + +/* Sample-Size */ +static inline int get_blc(struct i2s_dai *i2s) +{ + int blc = readl(i2s->addr + I2SMOD); + + blc = (blc >> 13) & 0x3; + + switch (blc) { + case 2: return 24; + case 1: return 8; + default: return 16; + } +} + +/* TX Channel Control */ +static void i2s_txctrl(struct i2s_dai *i2s, int on) +{ + void __iomem *addr = i2s->addr; + u32 con = readl(addr + I2SCON); + u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; + + if (on) { + con |= CON_ACTIVE; + con &= ~CON_TXCH_PAUSE; + + if (is_secondary(i2s)) { + con |= CON_TXSDMA_ACTIVE; + con &= ~CON_TXSDMA_PAUSE; + } else { + con |= CON_TXDMA_ACTIVE; + con &= ~CON_TXDMA_PAUSE; + } + + if (any_rx_active(i2s)) + mod |= MOD_TXRX; + else + mod |= MOD_TXONLY; + } else { + if (is_secondary(i2s)) { + con |= CON_TXSDMA_PAUSE; + con &= ~CON_TXSDMA_ACTIVE; + } else { + con |= CON_TXDMA_PAUSE; + con &= ~CON_TXDMA_ACTIVE; + } + + if (other_tx_active(i2s)) { + writel(con, addr + I2SCON); + return; + } + + con |= CON_TXCH_PAUSE; + + if (any_rx_active(i2s)) + mod |= MOD_RXONLY; + else + con &= ~CON_ACTIVE; + } + + writel(mod, addr + I2SMOD); + writel(con, addr + I2SCON); +} + +/* RX Channel Control */ +static void i2s_rxctrl(struct i2s_dai *i2s, int on) +{ + void __iomem *addr = i2s->addr; + u32 con = readl(addr + I2SCON); + u32 mod = readl(addr + I2SMOD) & ~MOD_MASK; + + if (on) { + con |= CON_RXDMA_ACTIVE | CON_ACTIVE; + con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE); + + if (any_tx_active(i2s)) + mod |= MOD_TXRX; + else + mod |= MOD_RXONLY; + } else { + con |= CON_RXDMA_PAUSE | CON_RXCH_PAUSE; + con &= ~CON_RXDMA_ACTIVE; + + if (any_tx_active(i2s)) + mod |= MOD_TXONLY; + else + con &= ~CON_ACTIVE; + } + + writel(mod, addr + I2SMOD); + writel(con, addr + I2SCON); +} + +/* Flush FIFO of an interface */ +static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush) +{ + void __iomem *fic; + u32 val; + + if (!i2s) + return; + + if (is_secondary(i2s)) + fic = i2s->addr + I2SFICS; + else + fic = i2s->addr + I2SFIC; + + /* Flush the FIFO */ + writel(readl(fic) | flush, fic); + + /* Be patient */ + val = msecs_to_loops(1) / 1000; /* 1 usec */ + while (--val) + cpu_relax(); + + writel(readl(fic) & ~flush, fic); +} + +static int i2s_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int rfs, int dir) +{ + struct i2s_dai *i2s = to_info(dai); + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + u32 mod = readl(i2s->addr + I2SMOD); + + switch (clk_id) { + case SAMSUNG_I2S_CDCLK: + /* Shouldn't matter in GATING(CLOCK_IN) mode */ + if (dir == SND_SOC_CLOCK_IN) + rfs = 0; + + if ((rfs && other->rfs && (other->rfs != rfs)) || + (any_active(i2s) && + (((dir == SND_SOC_CLOCK_IN) + && !(mod & MOD_CDCLKCON)) || + ((dir == SND_SOC_CLOCK_OUT) + && (mod & MOD_CDCLKCON))))) { + dev_err(&i2s->pdev->dev, + "%s:%d Other DAI busy\n", __func__, __LINE__); + return -EAGAIN; + } + + if (dir == SND_SOC_CLOCK_IN) + mod |= MOD_CDCLKCON; + else + mod &= ~MOD_CDCLKCON; + + i2s->rfs = rfs; + break; + + case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ + case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ + if ((i2s->quirks & QUIRK_NO_MUXPSR) + || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) + clk_id = 0; + else + clk_id = 1; + + if (!any_active(i2s)) { + if (i2s->op_clk) { + if ((clk_id && !(mod & MOD_IMS_SYSMUX)) || + (!clk_id && (mod & MOD_IMS_SYSMUX))) { + clk_disable(i2s->op_clk); + clk_put(i2s->op_clk); + } else { + i2s->rclk_srcrate = + clk_get_rate(i2s->op_clk); + return 0; + } + } + + i2s->op_clk = clk_get(&i2s->pdev->dev, + i2s->src_clk[clk_id]); + clk_enable(i2s->op_clk); + i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); + + /* Over-ride the other's */ + if (other) { + other->op_clk = i2s->op_clk; + other->rclk_srcrate = i2s->rclk_srcrate; + } + } else if ((!clk_id && (mod & MOD_IMS_SYSMUX)) + || (clk_id && !(mod & MOD_IMS_SYSMUX))) { + dev_err(&i2s->pdev->dev, + "%s:%d Other DAI busy\n", __func__, __LINE__); + return -EAGAIN; + } else { + /* Call can't be on the active DAI */ + i2s->op_clk = other->op_clk; + i2s->rclk_srcrate = other->rclk_srcrate; + return 0; + } + + if (clk_id == 0) + mod &= ~MOD_IMS_SYSMUX; + else + mod |= MOD_IMS_SYSMUX; + break; + + default: + dev_err(&i2s->pdev->dev, "We don't serve that!\n"); + return -EINVAL; + } + + writel(mod, i2s->addr + I2SMOD); + + return 0; +} + +static int i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct i2s_dai *i2s = to_info(dai); + u32 mod = readl(i2s->addr + I2SMOD); + u32 tmp = 0; + + /* Format is priority */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + tmp |= MOD_LR_RLOW; + tmp |= MOD_SDF_MSB; + break; + case SND_SOC_DAIFMT_LEFT_J: + tmp |= MOD_LR_RLOW; + tmp |= MOD_SDF_LSB; + break; + case SND_SOC_DAIFMT_I2S: + tmp |= MOD_SDF_IIS; + break; + default: + dev_err(&i2s->pdev->dev, "Format not supported\n"); + return -EINVAL; + } + + /* + * INV flag is relative to the FORMAT flag - if set it simply + * flips the polarity specified by the Standard + */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + if (tmp & MOD_LR_RLOW) + tmp &= ~MOD_LR_RLOW; + else + tmp |= MOD_LR_RLOW; + break; + default: + dev_err(&i2s->pdev->dev, "Polarity not supported\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + tmp |= MOD_SLAVE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + /* Set default source clock in Master mode */ + if (i2s->rclk_srcrate == 0) + i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, + 0, SND_SOC_CLOCK_IN); + break; + default: + dev_err(&i2s->pdev->dev, "master/slave format not supported\n"); + return -EINVAL; + } + + if (any_active(i2s) && + ((mod & (MOD_SDF_MASK | MOD_LR_RLOW + | MOD_SLAVE)) != tmp)) { + dev_err(&i2s->pdev->dev, + "%s:%d Other DAI busy\n", __func__, __LINE__); + return -EAGAIN; + } + + mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); + mod |= tmp; + writel(mod, i2s->addr + I2SMOD); + + return 0; +} + +static int i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + u32 mod = readl(i2s->addr + I2SMOD); + + if (!is_secondary(i2s)) + mod &= ~(MOD_DC2_EN | MOD_DC1_EN); + + switch (params_channels(params)) { + case 6: + mod |= MOD_DC2_EN; + case 4: + mod |= MOD_DC1_EN; + break; + case 2: + break; + default: + dev_err(&i2s->pdev->dev, "%d channels not supported\n", + params_channels(params)); + return -EINVAL; + } + + if (is_secondary(i2s)) + mod &= ~MOD_BLCS_MASK; + else + mod &= ~MOD_BLCP_MASK; + + if (is_manager(i2s)) + mod &= ~MOD_BLC_MASK; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + if (is_secondary(i2s)) + mod |= MOD_BLCS_8BIT; + else + mod |= MOD_BLCP_8BIT; + if (is_manager(i2s)) + mod |= MOD_BLC_8BIT; + break; + case SNDRV_PCM_FORMAT_S16_LE: + if (is_secondary(i2s)) + mod |= MOD_BLCS_16BIT; + else + mod |= MOD_BLCP_16BIT; + if (is_manager(i2s)) + mod |= MOD_BLC_16BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + if (is_secondary(i2s)) + mod |= MOD_BLCS_24BIT; + else + mod |= MOD_BLCP_24BIT; + if (is_manager(i2s)) + mod |= MOD_BLC_24BIT; + break; + default: + dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", + params_format(params)); + return -EINVAL; + } + writel(mod, i2s->addr + I2SMOD); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_set_dma_data(dai, substream, + (void *)&i2s->dma_playback); + else + snd_soc_dai_set_dma_data(dai, substream, + (void *)&i2s->dma_capture); + + i2s->frmclk = params_rate(params); + + return 0; +} + +/* We set constraints on the substream acc to the version of I2S */ +static int i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + + i2s->mode |= DAI_OPENED; + + if (is_manager(other)) + i2s->mode &= ~DAI_MANAGER; + else + i2s->mode |= DAI_MANAGER; + + /* Enforce set_sysclk in Master mode */ + i2s->rclk_srcrate = 0; + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +static void i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + + i2s->mode &= ~DAI_OPENED; + i2s->mode &= ~DAI_MANAGER; + + if (is_opened(other)) + other->mode |= DAI_MANAGER; + + /* Reset any constraint on RFS and BFS */ + i2s->rfs = 0; + i2s->bfs = 0; + + spin_unlock_irqrestore(&lock, flags); + + /* Gate CDCLK by default */ + if (!is_opened(other)) + i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, + 0, SND_SOC_CLOCK_IN); +} + +static int config_setup(struct i2s_dai *i2s) +{ + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + unsigned rfs, bfs, blc; + u32 psr; + + blc = get_blc(i2s); + + bfs = i2s->bfs; + + if (!bfs && other) + bfs = other->bfs; + + /* Select least possible multiple(2) if no constraint set */ + if (!bfs) + bfs = blc * 2; + + rfs = i2s->rfs; + + if (!rfs && other) + rfs = other->rfs; + + if ((rfs == 256 || rfs == 512) && (blc == 24)) { + dev_err(&i2s->pdev->dev, + "%d-RFS not supported for 24-blc\n", rfs); + return -EINVAL; + } + + if (!rfs) { + if (bfs == 16 || bfs == 32) + rfs = 256; + else + rfs = 384; + } + + /* If already setup and running */ + if (any_active(i2s) && (get_rfs(i2s) != rfs || get_bfs(i2s) != bfs)) { + dev_err(&i2s->pdev->dev, + "%s:%d Other DAI busy\n", __func__, __LINE__); + return -EAGAIN; + } + + /* Don't bother RFS, BFS & PSR in Slave mode */ + if (is_slave(i2s)) + return 0; + + set_bfs(i2s, bfs); + set_rfs(i2s, rfs); + + if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + psr = i2s->rclk_srcrate / i2s->frmclk / rfs; + writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR); + dev_dbg(&i2s->pdev->dev, + "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n", + i2s->rclk_srcrate, psr, rfs, bfs); + } + + return 0; +} + +static int i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct i2s_dai *i2s = to_info(rtd->cpu_dai); + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + local_irq_save(flags); + + if (config_setup(i2s)) { + local_irq_restore(flags); + return -EINVAL; + } + + if (capture) + i2s_rxctrl(i2s, 1); + else + i2s_txctrl(i2s, 1); + + local_irq_restore(flags); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + local_irq_save(flags); + + if (capture) + i2s_rxctrl(i2s, 0); + else + i2s_txctrl(i2s, 0); + + if (capture) + i2s_fifo(i2s, FIC_RXFLUSH); + else + i2s_fifo(i2s, FIC_TXFLUSH); + + local_irq_restore(flags); + break; + } + + return 0; +} + +static int i2s_set_clkdiv(struct snd_soc_dai *dai, + int div_id, int div) +{ + struct i2s_dai *i2s = to_info(dai); + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + + switch (div_id) { + case SAMSUNG_I2S_DIV_BCLK: + if ((any_active(i2s) && div && (get_bfs(i2s) != div)) + || (other && other->bfs && (other->bfs != div))) { + dev_err(&i2s->pdev->dev, + "%s:%d Other DAI busy\n", __func__, __LINE__); + return -EAGAIN; + } + i2s->bfs = div; + break; + default: + dev_err(&i2s->pdev->dev, + "Invalid clock divider(%d)\n", div_id); + return -EINVAL; + } + + return 0; +} + +static snd_pcm_sframes_t +i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + u32 reg = readl(i2s->addr + I2SFIC); + snd_pcm_sframes_t delay; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + delay = FIC_RXCOUNT(reg); + else if (is_secondary(i2s)) + delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS)); + else + delay = FIC_TXCOUNT(reg); + + return delay; +} + +#ifdef CONFIG_PM +static int i2s_suspend(struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + + if (dai->active) { + i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); + i2s->suspend_i2scon = readl(i2s->addr + I2SCON); + i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); + } + + return 0; +} + +static int i2s_resume(struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + + if (dai->active) { + writel(i2s->suspend_i2scon, i2s->addr + I2SCON); + writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); + writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); + } + + return 0; +} +#else +#define i2s_suspend NULL +#define i2s_resume NULL +#endif + +static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = to_info(dai); + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + + if (other && other->clk) /* If this is probe on secondary */ + goto probe_exit; + + i2s->addr = ioremap(i2s->base, 0x100); + if (i2s->addr == NULL) { + dev_err(&i2s->pdev->dev, "cannot ioremap registers\n"); + return -ENXIO; + } + + i2s->clk = clk_get(&i2s->pdev->dev, "iis"); + if (IS_ERR(i2s->clk)) { + dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n"); + iounmap(i2s->addr); + return -ENOENT; + } + clk_enable(i2s->clk); + + if (other) { + other->addr = i2s->addr; + other->clk = i2s->clk; + } + + if (i2s->quirks & QUIRK_NEED_RSTCLR) + writel(CON_RSTCLR, i2s->addr + I2SCON); + +probe_exit: + /* Reset any constraint on RFS and BFS */ + i2s->rfs = 0; + i2s->bfs = 0; + i2s_txctrl(i2s, 0); + i2s_rxctrl(i2s, 0); + i2s_fifo(i2s, FIC_TXFLUSH); + i2s_fifo(other, FIC_TXFLUSH); + i2s_fifo(i2s, FIC_RXFLUSH); + + /* Gate CDCLK by default */ + if (!is_opened(other)) + i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, + 0, SND_SOC_CLOCK_IN); + + return 0; +} + +static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) +{ + struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); + struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + + if (!other || !other->clk) { + + if (i2s->quirks & QUIRK_NEED_RSTCLR) + writel(0, i2s->addr + I2SCON); + + clk_disable(i2s->clk); + clk_put(i2s->clk); + + iounmap(i2s->addr); + } + + i2s->clk = NULL; + + return 0; +} + +static struct snd_soc_dai_ops samsung_i2s_dai_ops = { + .trigger = i2s_trigger, + .hw_params = i2s_hw_params, + .set_fmt = i2s_set_fmt, + .set_clkdiv = i2s_set_clkdiv, + .set_sysclk = i2s_set_sysclk, + .startup = i2s_startup, + .shutdown = i2s_shutdown, + .delay = i2s_delay, +}; + +#define SAMSUNG_I2S_RATES SNDRV_PCM_RATE_8000_96000 + +#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static __devinit +struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) +{ + struct i2s_dai *i2s; + + i2s = kzalloc(sizeof(struct i2s_dai), GFP_KERNEL); + if (i2s == NULL) + return NULL; + + i2s->pdev = pdev; + i2s->pri_dai = NULL; + i2s->sec_dai = NULL; + i2s->i2s_dai_drv.symmetric_rates = 1; + i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe; + i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove; + i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops; + i2s->i2s_dai_drv.suspend = i2s_suspend; + i2s->i2s_dai_drv.resume = i2s_resume; + i2s->i2s_dai_drv.playback.channels_min = 2; + i2s->i2s_dai_drv.playback.channels_max = 2; + i2s->i2s_dai_drv.playback.rates = SAMSUNG_I2S_RATES; + i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS; + + if (!sec) { + i2s->i2s_dai_drv.capture.channels_min = 2; + i2s->i2s_dai_drv.capture.channels_max = 2; + i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES; + i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; + } else { /* Create a new platform_device for Secondary */ + i2s->pdev = platform_device_register_resndata(NULL, + pdev->name, pdev->id + SAMSUNG_I2S_SECOFF, + NULL, 0, NULL, 0); + if (IS_ERR(i2s->pdev)) { + kfree(i2s); + return NULL; + } + } + + /* Pre-assign snd_soc_dai_set_drvdata */ + dev_set_drvdata(&i2s->pdev->dev, i2s); + + return i2s; +} + +static __devinit int samsung_i2s_probe(struct platform_device *pdev) +{ + u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan; + struct i2s_dai *pri_dai, *sec_dai = NULL; + struct s3c_audio_pdata *i2s_pdata; + struct samsung_i2s *i2s_cfg; + struct resource *res; + u32 regs_base, quirks; + int ret = 0; + + /* Call during Seconday interface registration */ + if (pdev->id >= SAMSUNG_I2S_SECOFF) { + sec_dai = dev_get_drvdata(&pdev->dev); + snd_soc_register_dai(&sec_dai->pdev->dev, + &sec_dai->i2s_dai_drv); + return 0; + } + + i2s_pdata = pdev->dev.platform_data; + if (i2s_pdata == NULL) { + dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); + return -ENXIO; + } + dma_pl_chan = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); + return -ENXIO; + } + dma_cp_chan = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (res) + dma_pl_sec_chan = res->start; + else + dma_pl_sec_chan = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get I2S SFR address\n"); + return -ENXIO; + } + + if (!request_mem_region(res->start, resource_size(res), + "samsung-i2s")) { + dev_err(&pdev->dev, "Unable to request SFR region\n"); + return -EBUSY; + } + regs_base = res->start; + + i2s_cfg = &i2s_pdata->type.i2s; + quirks = i2s_cfg->quirks; + + pri_dai = i2s_alloc_dai(pdev, false); + if (!pri_dai) { + dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); + ret = -ENOMEM; + goto err1; + } + + pri_dai->dma_playback.dma_addr = regs_base + I2STXD; + pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; + pri_dai->dma_playback.client = + (struct s3c2410_dma_client *)&pri_dai->dma_playback; + pri_dai->dma_capture.client = + (struct s3c2410_dma_client *)&pri_dai->dma_capture; + pri_dai->dma_playback.channel = dma_pl_chan; + pri_dai->dma_capture.channel = dma_cp_chan; + pri_dai->src_clk = i2s_cfg->src_clk; + pri_dai->dma_playback.dma_size = 4; + pri_dai->dma_capture.dma_size = 4; + pri_dai->base = regs_base; + pri_dai->quirks = quirks; + + if (quirks & QUIRK_PRI_6CHAN) + pri_dai->i2s_dai_drv.playback.channels_max = 6; + + if (quirks & QUIRK_SEC_DAI) { + sec_dai = i2s_alloc_dai(pdev, true); + if (!sec_dai) { + dev_err(&pdev->dev, "Unable to alloc I2S_sec\n"); + ret = -ENOMEM; + goto err2; + } + sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; + sec_dai->dma_playback.client = + (struct s3c2410_dma_client *)&sec_dai->dma_playback; + /* Use iDMA always if SysDMA not provided */ + sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1; + sec_dai->src_clk = i2s_cfg->src_clk; + sec_dai->dma_playback.dma_size = 4; + sec_dai->base = regs_base; + sec_dai->quirks = quirks; + sec_dai->pri_dai = pri_dai; + pri_dai->sec_dai = sec_dai; + } + + if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err3; + } + + snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv); + + return 0; +err3: + kfree(sec_dai); +err2: + kfree(pri_dai); +err1: + release_mem_region(regs_base, resource_size(res)); + + return ret; +} + +static __devexit int samsung_i2s_remove(struct platform_device *pdev) +{ + struct i2s_dai *i2s, *other; + + i2s = dev_get_drvdata(&pdev->dev); + other = i2s->pri_dai ? : i2s->sec_dai; + + if (other) { + other->pri_dai = NULL; + other->sec_dai = NULL; + } else { + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); + } + + i2s->pri_dai = NULL; + i2s->sec_dai = NULL; + + kfree(i2s); + + snd_soc_unregister_dai(&pdev->dev); + + return 0; +} + +static struct platform_driver samsung_i2s_driver = { + .probe = samsung_i2s_probe, + .remove = samsung_i2s_remove, + .driver = { + .name = "samsung-i2s", + .owner = THIS_MODULE, + }, +}; + +static int __init samsung_i2s_init(void) +{ + return platform_driver_register(&samsung_i2s_driver); +} +module_init(samsung_i2s_init); + +static void __exit samsung_i2s_exit(void) +{ + platform_driver_unregister(&samsung_i2s_driver); +} +module_exit(samsung_i2s_exit); + +/* Module information */ +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>"); +MODULE_DESCRIPTION("Samsung I2S Interface"); +MODULE_ALIAS("platform:samsung-i2s"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h new file mode 100644 index 000000000000..8e15f6a616d1 --- /dev/null +++ b/sound/soc/samsung/i2s.h @@ -0,0 +1,29 @@ +/* sound/soc/samsung/i2s.h + * + * ALSA SoC Audio Layer - Samsung I2S Controller driver + * + * Copyright (c) 2010 Samsung Electronics Co. Ltd. + * Jaswinder Singh <jassi.brar@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SND_SOC_SAMSUNG_I2S_H +#define __SND_SOC_SAMSUNG_I2S_H + +/* + * Maximum number of I2S blocks that any SoC can have. + * The secondary interface of a CPU dai(if there exists any), + * is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF] + */ +#define SAMSUNG_I2S_SECOFF 4 + +#define SAMSUNG_I2S_DIV_BCLK 1 + +#define SAMSUNG_I2S_RCLKSRC_0 0 +#define SAMSUNG_I2S_RCLKSRC_1 1 +#define SAMSUNG_I2S_CDCLK 2 + +#endif /* __SND_SOC_SAMSUNG_I2S_H */ diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c index 49605cd83947..3b53ad54bc33 100644 --- a/sound/soc/s3c24xx/jive_wm8750.c +++ b/sound/soc/samsung/jive_wm8750.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/jive_wm8750.c +/* sound/soc/samsung/jive_wm8750.c * * Copyright 2007,2008 Simtec Electronics * @@ -11,23 +11,11 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/clk.h> - -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/mach-types.h> -#include "s3c-dma.h" #include "s3c2412-i2s.h" - #include "../codecs/wm8750.h" static const struct snd_soc_dapm_route audio_map[] = { @@ -111,18 +99,19 @@ static struct snd_soc_ops jive_ops = { static int jive_wm8750_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; /* These endpoints are not being used. */ - snd_soc_dapm_nc_pin(codec, "LINPUT2"); - snd_soc_dapm_nc_pin(codec, "RINPUT2"); - snd_soc_dapm_nc_pin(codec, "LINPUT3"); - snd_soc_dapm_nc_pin(codec, "RINPUT3"); - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "MONO"); + snd_soc_dapm_nc_pin(dapm, "LINPUT2"); + snd_soc_dapm_nc_pin(dapm, "RINPUT2"); + snd_soc_dapm_nc_pin(dapm, "LINPUT3"); + snd_soc_dapm_nc_pin(dapm, "RINPUT3"); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "MONO"); /* Add jive specific widgets */ - err = snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, + err = snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, ARRAY_SIZE(wm8750_dapm_widgets)); if (err) { printk(KERN_ERR "%s: failed to add widgets (%d)\n", @@ -130,8 +119,8 @@ static int jive_wm8750_init(struct snd_soc_pcm_runtime *rtd) return err; } - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync(codec); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_sync(dapm); return 0; } @@ -141,7 +130,7 @@ static struct snd_soc_dai_link jive_dai = { .stream_name = "WM8750", .cpu_dai_name = "s3c2412-i2s", .codec_dai_name = "wm8750-hifi", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", .codec_name = "wm8750-codec.0-0x1a", .init = jive_wm8750_init, .ops = &jive_ops, diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c index abe64abe8c84..bd91c19a6c08 100644 --- a/sound/soc/s3c24xx/ln2440sbc_alc650.c +++ b/sound/soc/samsung/ln2440sbc_alc650.c @@ -16,15 +16,7 @@ * */ -#include <linux/module.h> -#include <linux/device.h> -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include "s3c-dma.h" -#include "s3c-ac97.h" static struct snd_soc_card ln2440sbc; @@ -32,10 +24,10 @@ static struct snd_soc_dai_link ln2440sbc_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "s3c-ac97", + .cpu_dai_name = "samsung-ac97", .codec_dai_name = "ac97-hifi", .codec_name = "ac97-codec", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", }, }; diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c new file mode 100644 index 000000000000..452230975632 --- /dev/null +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -0,0 +1,539 @@ +/* + * neo1973_wm8753.c -- SoC audio for Openmoko Neo1973 and Freerunner devices + * + * Copyright 2007 Openmoko Inc + * Author: Graeme Gregory <graeme@openmoko.org> + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Copyright 2009 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 <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> + +#include <sound/soc.h> + +#include <asm/mach-types.h> +#include <plat/regs-iis.h> +#include <mach/gta02.h> + +#include "../codecs/wm8753.h" +#include "s3c24xx-i2s.h" + +static int neo1973_hifi_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; + unsigned int pll_out = 0, bclk = 0; + int ret = 0; + unsigned long iis_clkrate; + + iis_clkrate = s3c24xx_i2s_get_clockrate(); + + switch (params_rate(params)) { + case 8000: + case 16000: + pll_out = 12288000; + break; + case 48000: + bclk = WM8753_BCLK_DIV_4; + pll_out = 12288000; + break; + case 96000: + bclk = WM8753_BCLK_DIV_2; + pll_out = 12288000; + break; + case 11025: + bclk = WM8753_BCLK_DIV_16; + pll_out = 11289600; + break; + case 22050: + bclk = WM8753_BCLK_DIV_8; + pll_out = 11289600; + break; + case 44100: + bclk = WM8753_BCLK_DIV_4; + pll_out = 11289600; + break; + case 88200: + bclk = WM8753_BCLK_DIV_2; + pll_out = 11289600; + break; + } + + /* set codec DAI configuration */ + 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; + + /* set cpu DAI configuration */ + 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; + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set MCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, + S3C2410_IISMOD_32FS); + if (ret < 0) + return ret; + + /* set codec BCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk); + if (ret < 0) + return ret; + + /* set prescaler division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, + S3C24XX_PRESCALE(4, 4)); + if (ret < 0) + return ret; + + /* codec PLL input is PCLK/4 */ + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, + iis_clkrate / 4, pll_out); + if (ret < 0) + return ret; + + return 0; +} + +static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* disable the PLL */ + return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); +} + +/* + * Neo1973 WM8753 HiFi DAI opserations. + */ +static struct snd_soc_ops neo1973_hifi_ops = { + .hw_params = neo1973_hifi_hw_params, + .hw_free = neo1973_hifi_hw_free, +}; + +static int neo1973_voice_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; + unsigned int pcmdiv = 0; + int ret = 0; + unsigned long iis_clkrate; + + iis_clkrate = s3c24xx_i2s_get_clockrate(); + + if (params_rate(params) != 8000) + return -EINVAL; + if (params_channels(params) != 1) + return -EINVAL; + + pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ + + /* todo: gg check mode (DSP_B) against CSR datasheet */ + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set codec PCM division for sample rate */ + ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv); + if (ret < 0) + return ret; + + /* configure and enable PLL for 12.288MHz output */ + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, + iis_clkrate / 4, 12288000); + if (ret < 0) + return ret; + + return 0; +} + +static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* disable the PLL */ + return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); +} + +static struct snd_soc_ops neo1973_voice_ops = { + .hw_params = neo1973_voice_hw_params, + .hw_free = neo1973_voice_hw_free, +}; + +/* Shared routes and controls */ + +static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_LINE("GSM Line Out", NULL), + SND_SOC_DAPM_LINE("GSM Line In", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), +}; + +static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = { + /* Connections to the GSM Module */ + {"GSM Line Out", NULL, "MONO1"}, + {"GSM Line Out", NULL, "MONO2"}, + {"RXP", NULL, "GSM Line In"}, + {"RXN", NULL, "GSM Line In"}, + + /* Connections to Headset */ + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset Mic"}, + + /* Call Mic */ + {"MIC2", NULL, "Mic Bias"}, + {"MIC2N", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Handset Mic"}, + + /* Connect the ALC pins */ + {"ACIN", NULL, "ACOP"}, +}; + +static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("GSM Line Out"), + SOC_DAPM_PIN_SWITCH("GSM Line In"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Handset Mic"), +}; + +/* GTA02 specific routes and controls */ + +#ifdef CONFIG_MACH_NEO1973_GTA02 + +static int gta02_speaker_enabled; + +static int lm4853_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + gta02_speaker_enabled = ucontrol->value.integer.value[0]; + + gpio_set_value(GTA02_GPIO_HP_IN, !gta02_speaker_enabled); + + return 0; +} + +static int lm4853_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = gta02_speaker_enabled; + return 0; +} + +static int lm4853_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event)); + + return 0; +} + +static const struct snd_soc_dapm_route neo1973_gta02_routes[] = { + /* Connections to the amp */ + {"Stereo Out", NULL, "LOUT1"}, + {"Stereo Out", NULL, "ROUT1"}, + + /* Call Speaker */ + {"Handset Spk", NULL, "LOUT2"}, + {"Handset Spk", NULL, "ROUT2"}, +}; + +static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("Handset Spk"), + SOC_DAPM_PIN_SWITCH("Stereo Out"), + + SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0, + lm4853_get_spk, + lm4853_set_spk), +}; + +static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), +}; + +static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets, + ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes, + ARRAY_SIZE(neo1973_gta02_routes)); + if (ret) + return ret; + + ret = snd_soc_add_controls(codec, neo1973_gta02_wm8753_controls, + ARRAY_SIZE(neo1973_gta02_wm8753_controls)); + if (ret) + return ret; + + snd_soc_dapm_disable_pin(dapm, "Stereo Out"); + snd_soc_dapm_disable_pin(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); + + return 0; +} + +#else +static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; } +#endif + +static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + /* set up NC codec pins */ + if (machine_is_neo1973_gta01()) { + snd_soc_dapm_nc_pin(dapm, "LOUT2"); + snd_soc_dapm_nc_pin(dapm, "ROUT2"); + } + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "OUT4"); + snd_soc_dapm_nc_pin(dapm, "LINE1"); + snd_soc_dapm_nc_pin(dapm, "LINE2"); + + /* Add neo1973 specific widgets */ + ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets, + ARRAY_SIZE(neo1973_wm8753_dapm_widgets)); + if (ret) + return ret; + + /* add neo1973 specific controls */ + ret = snd_soc_add_controls(codec, neo1973_wm8753_controls, + ARRAY_SIZE(neo1973_wm8753_controls)); + if (ret) + return ret; + + /* set up neo1973 specific audio routes */ + ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes, + ARRAY_SIZE(neo1973_wm8753_routes)); + if (ret) + return ret; + + /* set endpoints to default off mode */ + snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); + snd_soc_dapm_disable_pin(dapm, "GSM Line In"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic"); + snd_soc_dapm_disable_pin(dapm, "Handset Mic"); + + /* allow audio paths from the GSM modem to run during suspend */ + snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out"); + snd_soc_dapm_ignore_suspend(dapm, "GSM Line In"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + + if (machine_is_neo1973_gta02()) { + ret = neo1973_gta02_wm8753_init(codec); + if (ret) + return ret; + } + + snd_soc_dapm_sync(dapm); + + return 0; +} + +/* GTA01 specific controls */ + +#ifdef CONFIG_MACH_NEO1973_GTA01 + +static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = { + {"Amp IN", NULL, "ROUT1"}, + {"Amp IN", NULL, "LOUT1"}, + + {"Handset Spk", NULL, "Amp EP"}, + {"Stereo Out", NULL, "Amp LS"}, + {"Headphone", NULL, "Amp HP"}, +}; + +static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), +}; + +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) +{ + int ret; + + ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets, + ARRAY_SIZE(neo1973_lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes, + ARRAY_SIZE(neo1973_lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Headphone"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +#else +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; }; +#endif + +static struct snd_soc_dai_link neo1973_dai[] = { +{ /* Hifi Playback - for similatious use with voice below */ + .name = "WM8753", + .stream_name = "WM8753 HiFi", + .platform_name = "samsung-audio", + .cpu_dai_name = "s3c24xx-iis", + .codec_dai_name = "wm8753-hifi", + .codec_name = "wm8753-codec.0-001a", + .init = neo1973_wm8753_init, + .ops = &neo1973_hifi_ops, +}, +{ /* 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", + .ops = &neo1973_voice_ops, +}, +}; + +static struct snd_soc_aux_dev neo1973_aux_devs[] = { + { + .name = "dfbmcs320", + .codec_name = "dfbmcs320.0", + }, + { + .name = "lm4857", + .codec_name = "lm4857.0-007c", + .init = neo1973_lm4857_init, + }, +}; + +static struct snd_soc_codec_conf neo1973_codec_conf[] = { + { + .dev_name = "lm4857.0-007c", + .name_prefix = "Amp", + }, +}; + +#ifdef CONFIG_MACH_NEO1973_GTA02 +static const struct gpio neo1973_gta02_gpios[] = { + { GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" }, + { GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" }, +}; +#else +static const struct gpio neo1973_gta02_gpios[] = {}; +#endif + +static struct snd_soc_card neo1973 = { + .name = "neo1973", + .dai_link = neo1973_dai, + .num_links = ARRAY_SIZE(neo1973_dai), + .aux_dev = neo1973_aux_devs, + .num_aux_devs = ARRAY_SIZE(neo1973_aux_devs), + .codec_conf = neo1973_codec_conf, + .num_configs = ARRAY_SIZE(neo1973_codec_conf), +}; + +static struct platform_device *neo1973_snd_device; + +static int __init neo1973_init(void) +{ + int ret; + + if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02()) + return -ENODEV; + + if (machine_is_neo1973_gta02()) { + neo1973.name = "neo1973gta02"; + neo1973.num_aux_devs = 1; + + ret = gpio_request_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + if (ret) + return ret; + } + + neo1973_snd_device = platform_device_alloc("soc-audio", -1); + if (!neo1973_snd_device) { + ret = -ENOMEM; + goto err_gpio_free; + } + + platform_set_drvdata(neo1973_snd_device, &neo1973); + ret = platform_device_add(neo1973_snd_device); + + if (ret) + goto err_put_device; + + return 0; + +err_put_device: + platform_device_put(neo1973_snd_device); +err_gpio_free: + if (machine_is_neo1973_gta02()) { + gpio_free_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + } + return ret; +} +module_init(neo1973_init); + +static void __exit neo1973_exit(void) +{ + platform_device_unregister(neo1973_snd_device); + + if (machine_is_neo1973_gta02()) { + gpio_free_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + } +} +module_exit(neo1973_exit); + +/* Module information */ +MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); +MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/samsung/pcm.c index 2e020e1b4eab..9c7e8b48aed6 100644 --- a/sound/soc/s3c24xx/s3c-pcm.c +++ b/sound/soc/samsung/pcm.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c-pcm.c +/* sound/soc/samsung/pcm.c * * ALSA SoC Audio Layer - S3C PCM-Controller driver * @@ -11,26 +11,124 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/delay.h> #include <linux/clk.h> -#include <linux/kernel.h> -#include <linux/gpio.h> #include <linux/io.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <plat/audio.h> #include <plat/dma.h> -#include "s3c-dma.h" -#include "s3c-pcm.h" +#include "dma.h" +#include "pcm.h" + +/*Register Offsets */ +#define S3C_PCM_CTL 0x00 +#define S3C_PCM_CLKCTL 0x04 +#define S3C_PCM_TXFIFO 0x08 +#define S3C_PCM_RXFIFO 0x0C +#define S3C_PCM_IRQCTL 0x10 +#define S3C_PCM_IRQSTAT 0x14 +#define S3C_PCM_FIFOSTAT 0x18 +#define S3C_PCM_CLRINT 0x20 + +/* PCM_CTL Bit-Fields */ +#define S3C_PCM_CTL_TXDIPSTICK_MASK 0x3f +#define S3C_PCM_CTL_TXDIPSTICK_SHIFT 13 +#define S3C_PCM_CTL_RXDIPSTICK_MASK 0x3f +#define S3C_PCM_CTL_RXDIPSTICK_SHIFT 7 +#define S3C_PCM_CTL_TXDMA_EN (0x1 << 6) +#define S3C_PCM_CTL_RXDMA_EN (0x1 << 5) +#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1 << 4) +#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1 << 3) +#define S3C_PCM_CTL_TXFIFO_EN (0x1 << 2) +#define S3C_PCM_CTL_RXFIFO_EN (0x1 << 1) +#define S3C_PCM_CTL_ENABLE (0x1 << 0) + +/* PCM_CLKCTL Bit-Fields */ +#define S3C_PCM_CLKCTL_SERCLK_EN (0x1 << 19) +#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1 << 18) +#define S3C_PCM_CLKCTL_SCLKDIV_MASK 0x1ff +#define S3C_PCM_CLKCTL_SYNCDIV_MASK 0x1ff +#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT 9 +#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT 0 + +/* PCM_TXFIFO Bit-Fields */ +#define S3C_PCM_TXFIFO_DVALID (0x1 << 16) +#define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0) + +/* PCM_RXFIFO Bit-Fields */ +#define S3C_PCM_RXFIFO_DVALID (0x1 << 16) +#define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0) + +/* PCM_IRQCTL Bit-Fields */ +#define S3C_PCM_IRQCTL_IRQEN (0x1 << 14) +#define S3C_PCM_IRQCTL_WRDEN (0x1 << 12) +#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1 << 11) +#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1 << 10) +#define S3C_PCM_IRQCTL_TXFULLEN (0x1 << 9) +#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1 << 8) +#define S3C_PCM_IRQCTL_TXSTARVEN (0x1 << 7) +#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1 << 6) +#define S3C_PCM_IRQCTL_RXEMPTEN (0x1 << 5) +#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1 << 4) +#define S3C_PCM_IRQCTL_RXFULLEN (0x1 << 3) +#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1 << 2) +#define S3C_PCM_IRQCTL_RXSTARVEN (0x1 << 1) +#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1 << 0) + +/* PCM_IRQSTAT Bit-Fields */ +#define S3C_PCM_IRQSTAT_IRQPND (0x1 << 13) +#define S3C_PCM_IRQSTAT_WRD_XFER (0x1 << 12) +#define S3C_PCM_IRQSTAT_TXEMPTY (0x1 << 11) +#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1 << 10) +#define S3C_PCM_IRQSTAT_TXFULL (0x1 << 9) +#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1 << 8) +#define S3C_PCM_IRQSTAT_TXSTARV (0x1 << 7) +#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1 << 6) +#define S3C_PCM_IRQSTAT_RXEMPT (0x1 << 5) +#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1 << 4) +#define S3C_PCM_IRQSTAT_RXFULL (0x1 << 3) +#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1 << 2) +#define S3C_PCM_IRQSTAT_RXSTARV (0x1 << 1) +#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1 << 0) + +/* PCM_FIFOSTAT Bit-Fields */ +#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f << 14) +#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1 << 13) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1 << 12) +#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1 << 11) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1 << 10) +#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f << 4) +#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1 << 3) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1 << 2) +#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1 << 1) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1 << 0) + +/** + * struct s3c_pcm_info - S3C PCM Controller information + * @dev: The parent device passed to use from the probe. + * @regs: The pointer to the device register block. + * @dma_playback: DMA information for playback channel. + * @dma_capture: DMA information for capture channel. + */ +struct s3c_pcm_info { + spinlock_t lock; + struct device *dev; + void __iomem *regs; + + unsigned int sclk_per_fs; + + /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */ + unsigned int idleclk; + + struct clk *pclk; + struct clk *cclk; + + struct s3c_dma_params *dma_playback; + struct s3c_dma_params *dma_capture; +}; static struct s3c2410_dma_client s3c_pcm_dma_client_out = { .name = "PCM Stereo out" @@ -252,8 +350,8 @@ static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai, ctl = readl(regs + S3C_PCM_CTL); switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - /* Nothing to do, NB_NF by default */ + case SND_SOC_DAIFMT_IB_NF: + /* Nothing to do, IB_NF by default */ break; default: dev_err(pcm->dev, "Unsupported clock inversion!\n"); diff --git a/sound/soc/samsung/pcm.h b/sound/soc/samsung/pcm.h new file mode 100644 index 000000000000..726baf814613 --- /dev/null +++ b/sound/soc/samsung/pcm.h @@ -0,0 +1,17 @@ +/* sound/soc/samsung/pcm.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 __S3C_PCM_H +#define __S3C_PCM_H __FILE__ + +#define S3C_PCM_CLKSRC_PCLK 0 +#define S3C_PCM_CLKSRC_MUX 1 + +#define S3C_PCM_SCLK_PER_FS 0 + +#endif /* __S3C_PCM_H */ diff --git a/sound/soc/s3c24xx/regs-i2s-v2.h b/sound/soc/samsung/regs-i2s-v2.h index 5e5e5680580b..5e5e5680580b 100644 --- a/sound/soc/s3c24xx/regs-i2s-v2.h +++ b/sound/soc/samsung/regs-i2s-v2.h diff --git a/sound/soc/s3c24xx/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 468cc11fdf47..1e574a5d440d 100644 --- a/sound/soc/s3c24xx/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -17,27 +17,15 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> #include <linux/gpio.h> -#include <linux/clk.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/uda1380.h> #include <sound/jack.h> #include <plat/regs-iis.h> - -#include <mach/regs-clock.h> - #include <asm/mach-types.h> -#include "s3c-dma.h" #include "s3c24xx-i2s.h" -#include "../codecs/uda1380.h" static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd); static int rx1950_startup(struct snd_pcm_substream *substream); @@ -95,7 +83,7 @@ static struct snd_soc_dai_link rx1950_uda1380_dai[] = { .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "uda1380-hifi", .init = rx1950_uda1380_init, - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", .codec_name = "uda1380-codec.0-001a", .ops = &rx1950_ops, }, @@ -228,26 +216,28 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err; /* Add rx1950 specific widgets */ - err = snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets, + err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets, ARRAY_SIZE(uda1380_dapm_widgets)); if (err) return err; /* Set up rx1950 specific audio path audio_mapnects */ - err = snd_soc_dapm_add_routes(codec, audio_map, + err = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); if (err) return err; - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &hp_jack); diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index b3866d5b19e9..52074a2b0696 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c-i2c-v2.c +/* sound/soc/samsung/s3c-i2c-v2.c * * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs. * @@ -20,15 +20,14 @@ #include <linux/clk.h> #include <linux/io.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <mach/dma.h> #include "regs-i2s-v2.h" #include "s3c-i2s-v2.h" -#include "s3c-dma.h" +#include "dma.h" #undef S3C_IIS_V2_SUPPORTED diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h index d45830151484..f8297d9bb8a3 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.h +++ b/sound/soc/samsung/s3c-i2s-v2.h @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c-i2s-v2.h +/* sound/soc/samsung/s3c-i2s-v2.h * * ALSA Soc Audio Layer - S3C_I2SV2 I2S driver * diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 4a861cfa52c5..841ab14c1100 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c2412-i2s.c +/* sound/soc/samsung/s3c2412-i2s.c * * ALSA Soc Audio Layer - S3C2412 I2S driver * @@ -16,31 +16,21 @@ * option) any later version. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> #include <linux/delay.h> #include <linux/gpio.h> #include <linux/clk.h> -#include <linux/kernel.h> #include <linux/io.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> -#include <mach/hardware.h> +#include <sound/pcm_params.h> #include <mach/regs-gpio.h> #include <mach/dma.h> -#include "s3c-dma.h" +#include "dma.h" #include "regs-i2s-v2.h" #include "s3c2412-i2s.h" -#define S3C2412_I2S_DEBUG 0 - static struct s3c2410_dma_client s3c2412_dma_client_out = { .name = "I2S PCM Stereo out" }; diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/samsung/s3c2412-i2s.h index 01a0471ac65c..02ad5794c0a9 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.h +++ b/sound/soc/samsung/s3c2412-i2s.h @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c2412-i2s.c +/* sound/soc/samsung/s3c2412-i2s.c * * ALSA Soc Audio Layer - S3C2412 I2S driver * diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index e060daaa458f..63d8849d80bd 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -14,31 +14,19 @@ * option) any later version. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> #include <linux/delay.h> #include <linux/clk.h> -#include <linux/jiffies.h> #include <linux/io.h> #include <linux/gpio.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> +#include <sound/pcm_params.h> -#include <mach/hardware.h> #include <mach/regs-gpio.h> -#include <mach/regs-clock.h> - -#include <asm/dma.h> #include <mach/dma.h> - #include <plat/regs-iis.h> -#include "s3c-dma.h" +#include "dma.h" #include "s3c24xx-i2s.h" static struct s3c2410_dma_client s3c24xx_dma_client_out = { diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/samsung/s3c24xx-i2s.h index f9ca04edacb7..f9ca04edacb7 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.h +++ b/sound/soc/samsung/s3c24xx-i2s.h diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c index c4c111442010..349566f0686b 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec.c +++ b/sound/soc/samsung/s3c24xx_simtec.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c24xx_simtec.c +/* sound/soc/samsung/s3c24xx_simtec.c * * Copyright 2009 Simtec Electronics * @@ -7,21 +7,13 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/clk.h> -#include <linux/i2c.h> -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <plat/audio-simtec.h> -#include "s3c-dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.h b/sound/soc/samsung/s3c24xx_simtec.h index e63d5ff9c41f..8270748a2c41 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec.h +++ b/sound/soc/samsung/s3c24xx_simtec.h @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c24xx_simtec.h +/* sound/soc/samsung/s3c24xx_simtec.h * * Copyright 2009 Simtec Electronics * diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c index f88453735ae2..ce6aef604179 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c +++ b/sound/soc/samsung/s3c24xx_simtec_hermes.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c24xx_simtec_hermes.c +/* sound/soc/samsung/s3c24xx_simtec_hermes.c * * Copyright 2009 Simtec Electronics * @@ -7,23 +7,10 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/platform_device.h> - -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include <plat/audio-simtec.h> -#include "s3c-dma.h" -#include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" -#include "../codecs/tlv320aic3x.h" - static const struct snd_soc_dapm_widget dapm_widgets[] = { SND_SOC_DAPM_LINE("GSM Out", NULL), SND_SOC_DAPM_LINE("GSM In", NULL), @@ -76,19 +63,20 @@ static const struct snd_soc_dapm_route base_map[] = { static int simtec_hermes_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_new_controls(codec, dapm_widgets, + snd_soc_dapm_new_controls(dapm, dapm_widgets, ARRAY_SIZE(dapm_widgets)); - snd_soc_dapm_add_routes(codec, base_map, ARRAY_SIZE(base_map)); + snd_soc_dapm_add_routes(dapm, base_map, ARRAY_SIZE(base_map)); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Line In"); - snd_soc_dapm_enable_pin(codec, "Line Out"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Line In"); + snd_soc_dapm_enable_pin(dapm, "Line Out"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); simtec_audio_init(rtd); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -96,10 +84,10 @@ static int simtec_hermes_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link simtec_dai_aic33 = { .name = "tlv320aic33", .stream_name = "TLV320AIC33", - .codec_name = "tlv320aic3x-codec.0-0x1a", - .cpu_dai_name = "s3c24xx-i2s", + .codec_name = "tlv320aic3x-codec.0-001a", + .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "tlv320aic3x-hifi", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", .init = simtec_hermes_init, }; diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c index c0967593510d..a7ef7db54687 100644 --- a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c +++ b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c +/* sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c * * Copyright 2009 Simtec Electronics * @@ -7,23 +7,10 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/platform_device.h> - -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include <plat/audio-simtec.h> -#include "s3c-dma.h" -#include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" -#include "../codecs/tlv320aic23.h" - /* supported machines: * * Machine Connections AMP @@ -65,19 +52,20 @@ static const struct snd_soc_dapm_route base_map[] = { static int simtec_tlv320aic23_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_new_controls(codec, dapm_widgets, + snd_soc_dapm_new_controls(dapm, dapm_widgets, ARRAY_SIZE(dapm_widgets)); - snd_soc_dapm_add_routes(codec, base_map, ARRAY_SIZE(base_map)); + snd_soc_dapm_add_routes(dapm, base_map, ARRAY_SIZE(base_map)); - snd_soc_dapm_enable_pin(codec, "Headphone Jack"); - snd_soc_dapm_enable_pin(codec, "Line In"); - snd_soc_dapm_enable_pin(codec, "Line Out"); - snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Line In"); + snd_soc_dapm_enable_pin(dapm, "Line Out"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); simtec_audio_init(rtd); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } @@ -85,10 +73,10 @@ static int simtec_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link simtec_dai_aic23 = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", - .codec_name = "tlv320aic3x-codec.0-0x1a", - .cpu_dai_name = "s3c24xx-i2s", + .codec_name = "tlv320aic3x-codec.0-001a", + .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "tlv320aic3x-hifi", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", .init = simtec_tlv320aic23_init, }; diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index bd48ffbde880..dc9d551f6788 100644 --- a/sound/soc/s3c24xx/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -11,23 +11,15 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> #include <linux/clk.h> -#include <linux/mutex.h> #include <linux/gpio.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> + #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/s3c24xx_uda134x.h> -#include <sound/uda134x.h> #include <plat/regs-iis.h> -#include "s3c-dma.h" #include "s3c24xx-i2s.h" -#include "../codecs/uda134x.h" - /* #define ENFORCE_RATES 1 */ /* @@ -227,11 +219,11 @@ static struct snd_soc_ops s3c24xx_uda134x_ops = { static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", - .codec_name = "uda134x-hifi", + .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", - .cpu_dai_name = "s3c24xx-i2s", + .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", }; static struct snd_soc_card snd_soc_s3c24xx_uda134x = { @@ -322,6 +314,7 @@ static int s3c24xx_uda134x_probe(struct platform_device *pdev) platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x); + platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x)); ret = platform_device_add(s3c24xx_uda134x_snd_device); if (ret) { printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n"); diff --git a/sound/soc/s3c24xx/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index dd20ca7f4681..0a2c4f223038 100644 --- a/sound/soc/s3c24xx/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/smartq_wm8987.c +/* sound/soc/samsung/smartq_wm8987.c * * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com> * @@ -13,20 +13,14 @@ * */ -#include <linux/module.h> -#include <linux/platform_device.h> #include <linux/gpio.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include <sound/jack.h> #include <asm/mach-types.h> -#include "s3c-dma.h" -#include "s3c64xx-i2s.h" - +#include "i2s.h" #include "../codecs/wm8750.h" /* @@ -39,15 +33,11 @@ static int smartq_hifi_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->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct s3c_i2sv2_rate_calc div; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; unsigned int clk = 0; int ret; - s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params), - s3c_i2sv2_get_clock(cpu_dai)); - switch (params_rate(params)) { case 8000: case 16000: @@ -78,20 +68,21 @@ static int smartq_hifi_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, WM8750_SYSCLK, clk, - SND_SOC_CLOCK_IN); + /* Use PCLK for I2S signal generation */ + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, + 0, SND_SOC_CLOCK_IN); if (ret < 0) return ret; - /* set MCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, div.fs_div); + /* Gate the RCLK output on PAD */ + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, + 0, SND_SOC_CLOCK_IN); if (ret < 0) return ret; - /* set prescaler division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_PRESCALER, - div.clk_div - 1); + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, + SND_SOC_CLOCK_IN); if (ret < 0) return ret; @@ -156,12 +147,14 @@ static const struct snd_soc_dapm_route audio_map[] = { {"LINPUT2", NULL, "Mic Bias"}, }; -static int smartq_wm8987_init(struct snd_soc_codec *codec) +static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; int err = 0; /* Add SmartQ specific widgets */ - snd_soc_dapm_new_controls(codec, wm8987_dapm_widgets, + snd_soc_dapm_new_controls(dapm, wm8987_dapm_widgets, ARRAY_SIZE(wm8987_dapm_widgets)); /* add SmartQ specific controls */ @@ -172,25 +165,25 @@ static int smartq_wm8987_init(struct snd_soc_codec *codec) return err; /* setup SmartQ specific audio path */ - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* set endpoints to not connected */ - snd_soc_dapm_nc_pin(codec, "LINPUT1"); - snd_soc_dapm_nc_pin(codec, "RINPUT1"); - snd_soc_dapm_nc_pin(codec, "OUT3"); - snd_soc_dapm_nc_pin(codec, "ROUT1"); + snd_soc_dapm_nc_pin(dapm, "LINPUT1"); + snd_soc_dapm_nc_pin(dapm, "RINPUT1"); + snd_soc_dapm_nc_pin(dapm, "OUT3"); + snd_soc_dapm_nc_pin(dapm, "ROUT1"); /* set endpoints to default off mode */ - snd_soc_dapm_enable_pin(codec, "Internal Speaker"); - snd_soc_dapm_enable_pin(codec, "Internal Mic"); - snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Internal Speaker"); + snd_soc_dapm_enable_pin(dapm, "Internal Mic"); + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - err = snd_soc_dapm_sync(codec); + err = snd_soc_dapm_sync(dapm); if (err) return err; /* Headphone jack detection */ - err = snd_soc_jack_new(&snd_soc_smartq, "Headphone Jack", + err = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &smartq_jack); if (err) return err; @@ -211,9 +204,9 @@ static struct snd_soc_dai_link smartq_dai[] = { { .name = "wm8987", .stream_name = "SmartQ Hi-Fi", - .cpu_dai_name = "s3c64xx-i2s.0", + .cpu_dai_name = "samsung-i2s.0", .codec_dai_name = "wm8750-hifi", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", .codec_name = "wm8750-codec.0-0x1a", .init = smartq_wm8987_init, .ops = &smartq_hifi_ops, @@ -275,6 +268,7 @@ err_unregister_device: static void __exit smartq_exit(void) { + gpio_free(S3C64XX_GPK(12)); snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios), smartq_jack_gpios); diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c index 4613288c2772..3a0dbfc793f0 100644 --- a/sound/soc/s3c24xx/smdk2443_wm9710.c +++ b/sound/soc/samsung/smdk2443_wm9710.c @@ -12,15 +12,7 @@ * */ -#include <linux/module.h> -#include <linux/device.h> -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include "s3c-dma.h" -#include "s3c-ac97.h" static struct snd_soc_card smdk2443; @@ -28,10 +20,10 @@ static struct snd_soc_dai_link smdk2443_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "s3c-ac97", + .cpu_dai_name = "samsung-ac97", .codec_dai_name = "ac97-hifi", .codec_name = "ac97-codec", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", }, }; diff --git a/sound/soc/s3c24xx/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index c8bd90488a87..e8ac961c6ba1 100644 --- a/sound/soc/s3c24xx/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -10,15 +10,10 @@ * */ -#include <linux/module.h> -#include <linux/device.h> #include <linux/clk.h> -#include <plat/devs.h> - #include <sound/soc.h> -#include "s3c-dma.h" #include "spdif.h" /* Audio clock settings are belonged to board specific part. Every @@ -28,7 +23,7 @@ static int set_audio_clock_heirachy(struct platform_device *pdev) { struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif; - int ret; + int ret = 0; fout_epll = clk_get(NULL, "fout_epll"); if (IS_ERR(fout_epll)) { @@ -61,7 +56,7 @@ static int set_audio_clock_heirachy(struct platform_device *pdev) goto out3; } - /* Set audio clock heirachy for S/PDIF */ + /* Set audio clock hierarchy for S/PDIF */ clk_set_parent(mout_epll, fout_epll); clk_set_parent(sclk_audio0, mout_epll); clk_set_parent(sclk_spdif, sclk_audio0); @@ -79,7 +74,7 @@ out1: /* We should haved to set clock directly on this part because of clock * scheme of Samsudng SoCs did not support to set rates from abstrct - * clock of it's heirachy. + * clock of it's hierarchy. */ static int set_audio_clock_rate(unsigned long epll_rate, unsigned long audio_rate) @@ -152,12 +147,10 @@ static struct snd_soc_ops smdk_spdif_ops = { .hw_params = smdk_hw_params, }; -static struct snd_soc_card smdk; - static struct snd_soc_dai_link smdk_dai = { .name = "S/PDIF", .stream_name = "S/PDIF PCM Playback", - .platform_name = "s3c24xx-pcm-audio", + .platform_name = "samsung-audio", .cpu_dai_name = "samsung-spdif", .codec_dai_name = "dit-hifi", .codec_name = "spdif-dit", @@ -183,7 +176,7 @@ static int __init smdk_init(void) ret = platform_device_add(smdk_snd_spdif_dit_device); if (ret) - goto err2; + goto err1; smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1); if (!smdk_snd_spdif_device) { @@ -195,17 +188,21 @@ static int __init smdk_init(void) ret = platform_device_add(smdk_snd_spdif_device); if (ret) - goto err1; + goto err3; - /* Set audio clock heirachy manually */ + /* Set audio clock hierarchy manually */ ret = set_audio_clock_heirachy(smdk_snd_spdif_device); if (ret) - goto err1; + goto err4; return 0; -err1: +err4: + platform_device_del(smdk_snd_spdif_device); +err3: platform_device_put(smdk_snd_spdif_device); err2: + platform_device_del(smdk_snd_spdif_dit_device); +err1: platform_device_put(smdk_snd_spdif_dit_device); return ret; } @@ -213,6 +210,7 @@ err2: static void __exit smdk_exit(void) { platform_device_unregister(smdk_snd_spdif_device); + platform_device_unregister(smdk_snd_spdif_dit_device); } module_init(smdk_init); diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index 052e499b68d1..8aacf23d6f3a 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -1,5 +1,5 @@ /* - * smdk64xx_wm8580.c + * smdk_wm8580.c * * Copyright (c) 2009 Samsung Electronics Co. Ltd * Author: Jaswinder Singh <jassi.brar@samsung.com> @@ -10,17 +10,13 @@ * option) any later version. */ -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> +#include <sound/pcm_params.h> + +#include <asm/mach-types.h> #include "../codecs/wm8580.h" -#include "s3c-dma.h" -#include "s3c64xx-i2s.h" +#include "i2s.h" /* * Default CFG switch settings to use this driver: @@ -28,10 +24,10 @@ * SMDK6410: Set CFG1 1-3 Off, CFG2 1-4 On */ -/* SMDK64XX has a 12MHZ crystal attached to WM8580 */ -#define SMDK64XX_WM8580_FREQ 12000000 +/* SMDK has a 12MHZ crystal attached to WM8580 */ +#define SMDK_WM8580_FREQ 12000000 -static int smdk64xx_hw_params(struct snd_pcm_substream *substream, +static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -96,17 +92,6 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK, - 0, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* We use PCLK for basic ops in SoC-Slave mode */ - ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, - 0, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - /* Set WM8580 to drive MCLK from its PLLA */ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA); @@ -114,7 +99,7 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, return ret; ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, - SMDK64XX_WM8580_FREQ, pll_out); + SMDK_WM8580_FREQ, pll_out); if (ret < 0) return ret; @@ -123,32 +108,24 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs); - if (ret < 0) - return ret; - return 0; } /* - * SMDK64XX WM8580 DAI operations. + * SMDK WM8580 DAI operations. */ -static struct snd_soc_ops smdk64xx_ops = { - .hw_params = smdk64xx_hw_params, +static struct snd_soc_ops smdk_ops = { + .hw_params = smdk_hw_params, }; -/* SMDK64xx Playback widgets */ +/* SMDK Playback widgets */ static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = { SND_SOC_DAPM_HP("Front", NULL), SND_SOC_DAPM_HP("Center+Sub", NULL), SND_SOC_DAPM_HP("Rear", NULL), }; -/* SMDK64xx Capture widgets */ +/* SMDK Capture widgets */ static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = { SND_SOC_DAPM_MIC("MicIn", NULL), SND_SOC_DAPM_LINE("LineIn", NULL), @@ -179,94 +156,132 @@ static const struct snd_soc_dapm_route audio_map_rx[] = { {"Rear", NULL, "VOUT3R"}, }; -static int smdk64xx_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd) +static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; - /* Add smdk64xx specific Capture widgets */ - snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt, + /* Add smdk specific Capture widgets */ + snd_soc_dapm_new_controls(dapm, wm8580_dapm_widgets_cpt, ARRAY_SIZE(wm8580_dapm_widgets_cpt)); /* Set up PAIFTX audio path */ - snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx)); + snd_soc_dapm_add_routes(dapm, audio_map_tx, ARRAY_SIZE(audio_map_tx)); /* Enabling the microphone requires the fitting of a 0R * resistor to connect the line from the microphone jack. */ - snd_soc_dapm_disable_pin(codec, "MicIn"); + snd_soc_dapm_disable_pin(dapm, "MicIn"); /* signal a DAPM event */ - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } -static int smdk64xx_wm8580_init_paifrx(struct snd_soc_pcm_runtime *rtd) +static int smdk_wm8580_init_paifrx(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; - /* Add smdk64xx specific Playback widgets */ - snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk, + /* Add smdk specific Playback widgets */ + snd_soc_dapm_new_controls(dapm, wm8580_dapm_widgets_pbk, ARRAY_SIZE(wm8580_dapm_widgets_pbk)); /* Set up PAIFRX audio path */ - snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx)); + snd_soc_dapm_add_routes(dapm, audio_map_rx, ARRAY_SIZE(audio_map_rx)); /* signal a DAPM event */ - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); return 0; } -static struct snd_soc_dai_link smdk64xx_dai[] = { -{ /* Primary Playback i/f */ - .name = "WM8580 PAIF RX", - .stream_name = "Playback", - .cpu_dai_name = "s3c64xx-iis-v4", - .codec_dai_name = "wm8580-hifi-playback", - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8580-codec.0-001b", - .init = smdk64xx_wm8580_init_paifrx, - .ops = &smdk64xx_ops, -}, -{ /* Primary Capture i/f */ - .name = "WM8580 PAIF TX", - .stream_name = "Capture", - .cpu_dai_name = "s3c64xx-iis-v4", - .codec_dai_name = "wm8580-hifi-capture", - .platform_name = "s3c24xx-pcm-audio", - .codec_name = "wm8580-codec.0-001b", - .init = smdk64xx_wm8580_init_paiftx, - .ops = &smdk64xx_ops, -}, +enum { + PRI_PLAYBACK = 0, + PRI_CAPTURE, + SEC_PLAYBACK, +}; + +static struct snd_soc_dai_link smdk_dai[] = { + [PRI_PLAYBACK] = { /* Primary Playback i/f */ + .name = "WM8580 PAIF RX", + .stream_name = "Playback", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "wm8580-hifi-playback", + .platform_name = "samsung-audio", + .codec_name = "wm8580-codec.0-001b", + .init = smdk_wm8580_init_paifrx, + .ops = &smdk_ops, + }, + [PRI_CAPTURE] = { /* Primary Capture i/f */ + .name = "WM8580 PAIF TX", + .stream_name = "Capture", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "wm8580-hifi-capture", + .platform_name = "samsung-audio", + .codec_name = "wm8580-codec.0-001b", + .init = smdk_wm8580_init_paiftx, + .ops = &smdk_ops, + }, + [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */ + .name = "Sec_FIFO TX", + .stream_name = "Playback", + .cpu_dai_name = "samsung-i2s.x", + .codec_dai_name = "wm8580-hifi-playback", + .platform_name = "samsung-audio", + .codec_name = "wm8580-codec.0-001b", + .init = smdk_wm8580_init_paifrx, + .ops = &smdk_ops, + }, }; -static struct snd_soc_card smdk64xx = { - .name = "SMDK64xx 5.1", - .dai_link = smdk64xx_dai, - .num_links = ARRAY_SIZE(smdk64xx_dai), +static struct snd_soc_card smdk = { + .name = "SMDK-I2S", + .dai_link = smdk_dai, + .num_links = 2, }; -static struct platform_device *smdk64xx_snd_device; +static struct platform_device *smdk_snd_device; -static int __init smdk64xx_audio_init(void) +static int __init smdk_audio_init(void) { int ret; + char *str; + + if (machine_is_smdkc100() || machine_is_smdk6442() + || machine_is_smdkv210() || machine_is_smdkc110()) { + smdk.num_links = 3; + /* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */ + str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name; + str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF; + } else if (machine_is_smdk6410()) { + str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name; + str[strlen(str) - 1] = '2'; + str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name; + str[strlen(str) - 1] = '2'; + } - smdk64xx_snd_device = platform_device_alloc("soc-audio", -1); - if (!smdk64xx_snd_device) + smdk_snd_device = platform_device_alloc("soc-audio", -1); + if (!smdk_snd_device) return -ENOMEM; - platform_set_drvdata(smdk64xx_snd_device, &smdk64xx); - ret = platform_device_add(smdk64xx_snd_device); + platform_set_drvdata(smdk_snd_device, &smdk); + ret = platform_device_add(smdk_snd_device); if (ret) - platform_device_put(smdk64xx_snd_device); + platform_device_put(smdk_snd_device); return ret; } -module_init(smdk64xx_audio_init); +module_init(smdk_audio_init); + +static void __exit smdk_audio_exit(void) +{ + platform_device_unregister(smdk_snd_device); +} +module_exit(smdk_audio_exit); MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com"); -MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580"); +MODULE_DESCRIPTION("ALSA SoC SMDK WM8580"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c new file mode 100644 index 000000000000..e7c1009a1e1d --- /dev/null +++ b/sound/soc/samsung/smdk_wm8994.c @@ -0,0 +1,176 @@ +/* + * smdk_wm8994.c + * + * 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 "../codecs/wm8994.h" + + /* + * Default CFG switch settings to use this driver: + * SMDKV310: CFG5-1000, CFG7-111111 + */ + + /* + * Configure audio route as :- + * $ amixer sset 'DAC1' on,on + * $ amixer sset 'Right Headphone Mux' 'DAC' + * $ amixer sset 'Left Headphone Mux' 'DAC' + * $ amixer sset 'DAC1R Mixer AIF1.1' on + * $ amixer sset 'DAC1L Mixer AIF1.1' on + * $ amixer sset 'IN2L' on + * $ amixer sset 'IN2L PGA IN2LN' on + * $ amixer sset 'MIXINL IN2L' on + * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on + * $ amixer sset 'IN2R' on + * $ amixer sset 'IN2R PGA IN2RN' on + * $ amixer sset 'MIXINR IN2R' on + * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on + */ + +/* SMDK has a 16.934MHZ crystal attached to WM8994 */ +#define SMDK_WM8994_FREQ 16934000 + +static int smdk_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 pll_out; + int ret; + + /* AIF1CLK should be >=3MHz for optimal performance */ + if (params_rate(params) == 8000 || params_rate(params) == 11025) + pll_out = params_rate(params) * 512; + else + pll_out = params_rate(params) * 256; + + 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, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + SMDK_WM8994_FREQ, pll_out); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1, + pll_out, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return 0; +} + +/* + * SMDK WM8994 DAI operations. + */ +static struct snd_soc_ops smdk_ops = { + .hw_params = smdk_hw_params, +}; + +static int smdk_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + /* HeadPhone */ + snd_soc_dapm_enable_pin(dapm, "HPOUT1R"); + snd_soc_dapm_enable_pin(dapm, "HPOUT1L"); + + /* MicIn */ + snd_soc_dapm_enable_pin(dapm, "IN1LN"); + snd_soc_dapm_enable_pin(dapm, "IN1RN"); + + /* LineIn */ + snd_soc_dapm_enable_pin(dapm, "IN2LN"); + snd_soc_dapm_enable_pin(dapm, "IN2RN"); + + /* Other pins NC */ + snd_soc_dapm_nc_pin(dapm, "HPOUT2P"); + snd_soc_dapm_nc_pin(dapm, "HPOUT2N"); + snd_soc_dapm_nc_pin(dapm, "SPKOUTLN"); + snd_soc_dapm_nc_pin(dapm, "SPKOUTLP"); + snd_soc_dapm_nc_pin(dapm, "SPKOUTRP"); + snd_soc_dapm_nc_pin(dapm, "SPKOUTRN"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); + snd_soc_dapm_nc_pin(dapm, "IN1LP"); + snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN"); + snd_soc_dapm_nc_pin(dapm, "IN1RP"); + snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static struct snd_soc_dai_link smdk_dai[] = { + { /* Primary DAI i/f */ + .name = "WM8994 AIF1", + .stream_name = "Pri_Dai", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "wm8994-aif1", + .platform_name = "samsung-audio", + .codec_name = "wm8994-codec", + .init = smdk_wm8994_init_paiftx, + .ops = &smdk_ops, + }, { /* Sec_Fifo Playback i/f */ + .name = "Sec_FIFO TX", + .stream_name = "Sec_Dai", + .cpu_dai_name = "samsung-i2s.4", + .codec_dai_name = "wm8994-aif1", + .platform_name = "samsung-audio", + .codec_name = "wm8994-codec", + .ops = &smdk_ops, + }, +}; + +static struct snd_soc_card smdk = { + .name = "SMDK-I2S", + .dai_link = smdk_dai, + .num_links = ARRAY_SIZE(smdk_dai), +}; + +static struct platform_device *smdk_snd_device; + +static int __init smdk_audio_init(void) +{ + int ret; + + smdk_snd_device = platform_device_alloc("soc-audio", -1); + if (!smdk_snd_device) + return -ENOMEM; + + platform_set_drvdata(smdk_snd_device, &smdk); + + ret = platform_device_add(smdk_snd_device); + if (ret) + platform_device_put(smdk_snd_device); + + return ret; +} +module_init(smdk_audio_init); + +static void __exit smdk_audio_exit(void) +{ + platform_device_unregister(smdk_snd_device); +} +module_exit(smdk_audio_exit); + +MODULE_DESCRIPTION("ALSA SoC SMDK WM8994"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c index 33ba8fdbcf07..fffe3c1dd1bd 100644 --- a/sound/soc/s3c24xx/smdk_wm9713.c +++ b/sound/soc/samsung/smdk_wm9713.c @@ -11,13 +11,8 @@ * */ -#include <linux/module.h> -#include <linux/device.h> #include <sound/soc.h> -#include "s3c-dma.h" -#include "s3c-ac97.h" - static struct snd_soc_card smdk; /* @@ -27,6 +22,7 @@ static struct snd_soc_card smdk; * SMDKC100: Set CFG6 1-3 On, CFG7 1 On * SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On * SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On + * SMDKV310: Set CFG2 1-2 Off, CFG4 All On, CFG7 All Off, CFG8 1-On */ /* @@ -45,8 +41,8 @@ static struct snd_soc_card smdk; static struct snd_soc_dai_link smdk_dai = { .name = "AC97", .stream_name = "AC97 PCM", - .platform_name = "s3c24xx-pcm-audio", - .cpu_dai_name = "s3c-ac97", + .platform_name = "samsung-audio", + .cpu_dai_name = "samsung-ac97", .codec_dai_name = "wm9713-hifi", .codec_name = "wm9713-codec", }; @@ -70,24 +66,27 @@ static int __init smdk_init(void) ret = platform_device_add(smdk_snd_wm9713_device); if (ret) - goto err; + goto err1; smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1); if (!smdk_snd_ac97_device) { ret = -ENOMEM; - goto err; + goto err2; } platform_set_drvdata(smdk_snd_ac97_device, &smdk); ret = platform_device_add(smdk_snd_ac97_device); - if (ret) { - platform_device_put(smdk_snd_ac97_device); - goto err; - } + if (ret) + goto err3; return 0; -err: + +err3: + platform_device_put(smdk_snd_ac97_device); +err2: + platform_device_del(smdk_snd_wm9713_device); +err1: platform_device_put(smdk_snd_wm9713_device); return ret; } diff --git a/sound/soc/s3c24xx/spdif.c b/sound/soc/samsung/spdif.c index ce554e9cabcc..28c491dacf7a 100644 --- a/sound/soc/s3c24xx/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/spdif.c +/* sound/soc/samsung/spdif.c * * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver * @@ -13,14 +13,13 @@ #include <linux/clk.h> #include <linux/io.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <plat/audio.h> #include <mach/dma.h> -#include "s3c-dma.h" +#include "dma.h" #include "spdif.h" /* Registers */ diff --git a/sound/soc/s3c24xx/spdif.h b/sound/soc/samsung/spdif.h index 3ed55592710f..4f72cb446dbf 100644 --- a/sound/soc/s3c24xx/spdif.h +++ b/sound/soc/samsung/spdif.h @@ -1,4 +1,4 @@ -/* sound/soc/s3c24xx/spdif.h +/* sound/soc/samsung/spdif.h * * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver * diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 7f0a496e07ce..d8e06a607a22 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -48,7 +48,7 @@ config SND_SH7760_AC97 config SND_FSI_AK4642 tristate "FSI-AK4642 sound support" - depends on SND_SOC_SH4_FSI && I2C_SH_MOBILE + depends on SND_SOC_SH4_FSI && I2C select SND_SOC_AK4642 help This option enables generic sound support for the @@ -56,7 +56,7 @@ config SND_FSI_AK4642 config SND_FSI_DA7210 tristate "FSI-DA7210 sound support" - depends on SND_SOC_SH4_FSI && I2C_SH_MOBILE + depends on SND_SOC_SH4_FSI && I2C select SND_SOC_DA7210 help This option enables generic sound support for the diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index d96602de71de..d6f4703b3c07 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -12,52 +12,73 @@ #include <linux/platform_device.h> #include <sound/sh_fsi.h> +struct fsi_ak4642_data { + const char *name; + const char *card; + const char *cpu_dai; + const char *codec; + const char *platform; + int id; +}; + static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0); + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBS_CFS); return ret; } static struct snd_soc_dai_link fsi_dai_link = { - .name = "AK4642", - .stream_name = "AK4642", - .cpu_dai_name = "fsia-dai", /* fsi A */ .codec_dai_name = "ak4642-hifi", -#ifdef CONFIG_MACH_AP4EVB - .platform_name = "sh_fsi2", - .codec_name = "ak4642-codec.0-0013", -#else - .platform_name = "sh_fsi.0", - .codec_name = "ak4642-codec.0-0012", -#endif .init = fsi_ak4642_dai_init, - .ops = NULL, }; static struct snd_soc_card fsi_soc_card = { - .name = "FSI (AK4642)", .dai_link = &fsi_dai_link, .num_links = 1, }; static struct platform_device *fsi_snd_device; -static int __init fsi_ak4642_init(void) +static int fsi_ak4642_probe(struct platform_device *pdev) { int ret = -ENOMEM; + const struct platform_device_id *id_entry; + struct fsi_ak4642_data *pdata; + + id_entry = pdev->id_entry; + if (!id_entry) { + dev_err(&pdev->dev, "unknown fsi ak4642\n"); + return -ENODEV; + } + + pdata = (struct fsi_ak4642_data *)id_entry->driver_data; - fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A); + fsi_snd_device = platform_device_alloc("soc-audio", pdata->id); if (!fsi_snd_device) goto out; + fsi_dai_link.name = pdata->name; + fsi_dai_link.stream_name = pdata->name; + fsi_dai_link.cpu_dai_name = pdata->cpu_dai; + fsi_dai_link.platform_name = pdata->platform; + fsi_dai_link.codec_name = pdata->codec; + fsi_soc_card.name = pdata->card; + platform_set_drvdata(fsi_snd_device, &fsi_soc_card); ret = platform_device_add(fsi_snd_device); @@ -68,9 +89,116 @@ out: return ret; } -static void __exit fsi_ak4642_exit(void) +static int fsi_ak4642_remove(struct platform_device *pdev) { platform_device_unregister(fsi_snd_device); + return 0; +} + +static struct fsi_ak4642_data fsi_a_ak4642 = { + .name = "AK4642", + .card = "FSIA (AK4642)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi.0", + .id = FSI_PORT_A, +}; + +static struct fsi_ak4642_data fsi_b_ak4642 = { + .name = "AK4642", + .card = "FSIB (AK4642)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi.0", + .id = FSI_PORT_B, +}; + +static struct fsi_ak4642_data fsi_a_ak4643 = { + .name = "AK4643", + .card = "FSIA (AK4643)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi.0", + .id = FSI_PORT_A, +}; + +static struct fsi_ak4642_data fsi_b_ak4643 = { + .name = "AK4643", + .card = "FSIB (AK4643)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi.0", + .id = FSI_PORT_B, +}; + +static struct fsi_ak4642_data fsi2_a_ak4642 = { + .name = "AK4642", + .card = "FSI2A (AK4642)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi2", + .id = FSI_PORT_A, +}; + +static struct fsi_ak4642_data fsi2_b_ak4642 = { + .name = "AK4642", + .card = "FSI2B (AK4642)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi2", + .id = FSI_PORT_B, +}; + +static struct fsi_ak4642_data fsi2_a_ak4643 = { + .name = "AK4643", + .card = "FSI2A (AK4643)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi2", + .id = FSI_PORT_A, +}; + +static struct fsi_ak4642_data fsi2_b_ak4643 = { + .name = "AK4643", + .card = "FSI2B (AK4643)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi2", + .id = FSI_PORT_B, +}; + +static struct platform_device_id fsi_id_table[] = { + /* FSI */ + { "sh_fsi_a_ak4642", (kernel_ulong_t)&fsi_a_ak4642 }, + { "sh_fsi_b_ak4642", (kernel_ulong_t)&fsi_b_ak4642 }, + { "sh_fsi_a_ak4643", (kernel_ulong_t)&fsi_a_ak4643 }, + { "sh_fsi_b_ak4643", (kernel_ulong_t)&fsi_b_ak4643 }, + + /* FSI 2 */ + { "sh_fsi2_a_ak4642", (kernel_ulong_t)&fsi2_a_ak4642 }, + { "sh_fsi2_b_ak4642", (kernel_ulong_t)&fsi2_b_ak4642 }, + { "sh_fsi2_a_ak4643", (kernel_ulong_t)&fsi2_a_ak4643 }, + { "sh_fsi2_b_ak4643", (kernel_ulong_t)&fsi2_b_ak4643 }, + {}, +}; + +static struct platform_driver fsi_ak4642 = { + .driver = { + .name = "fsi-ak4642-audio", + }, + .probe = fsi_ak4642_probe, + .remove = fsi_ak4642_remove, + .id_table = fsi_id_table, +}; + +static int __init fsi_ak4642_init(void) +{ + return platform_driver_register(&fsi_ak4642); +} + +static void __exit fsi_ak4642_exit(void) +{ + platform_driver_unregister(&fsi_ak4642); } module_init(fsi_ak4642_init); diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index a6adb6e43250..dbafd7ac5590 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -15,11 +15,20 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; - return snd_soc_dai_set_fmt(dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + ret = snd_soc_dai_set_fmt(codec, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS); + + return ret; } static struct snd_soc_dai_link fsi_da7210_dai = { diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c index a52dd8ec71d3..9719985eb82d 100644 --- a/sound/soc/sh/fsi-hdmi.c +++ b/sound/soc/sh/fsi-hdmi.c @@ -12,31 +12,59 @@ #include <linux/platform_device.h> #include <sound/sh_fsi.h> +struct fsi_hdmi_data { + const char *cpu_dai; + const char *card; + int id; +}; + +static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM); + + return ret; +} + static struct snd_soc_dai_link fsi_dai_link = { .name = "HDMI", .stream_name = "HDMI", - .cpu_dai_name = "fsib-dai", /* fsi B */ .codec_dai_name = "sh_mobile_hdmi-hifi", .platform_name = "sh_fsi2", .codec_name = "sh-mobile-hdmi", + .init = fsi_hdmi_dai_init, }; static struct snd_soc_card fsi_soc_card = { - .name = "FSI (SH MOBILE HDMI)", .dai_link = &fsi_dai_link, .num_links = 1, }; static struct platform_device *fsi_snd_device; -static int __init fsi_hdmi_init(void) +static int fsi_hdmi_probe(struct platform_device *pdev) { int ret = -ENOMEM; + const struct platform_device_id *id_entry; + struct fsi_hdmi_data *pdata; + + id_entry = pdev->id_entry; + if (!id_entry) { + dev_err(&pdev->dev, "unknown fsi hdmi\n"); + return -ENODEV; + } - fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B); + pdata = (struct fsi_hdmi_data *)id_entry->driver_data; + + fsi_snd_device = platform_device_alloc("soc-audio", pdata->id); if (!fsi_snd_device) goto out; + fsi_dai_link.cpu_dai_name = pdata->cpu_dai; + fsi_soc_card.name = pdata->card; + platform_set_drvdata(fsi_snd_device, &fsi_soc_card); ret = platform_device_add(fsi_snd_device); @@ -47,9 +75,48 @@ out: return ret; } -static void __exit fsi_hdmi_exit(void) +static int fsi_hdmi_remove(struct platform_device *pdev) { platform_device_unregister(fsi_snd_device); + return 0; +} + +static struct fsi_hdmi_data fsi2_a_hdmi = { + .cpu_dai = "fsia-dai", + .card = "FSI2A (SH MOBILE HDMI)", + .id = FSI_PORT_A, +}; + +static struct fsi_hdmi_data fsi2_b_hdmi = { + .cpu_dai = "fsib-dai", + .card = "FSI2B (SH MOBILE HDMI)", + .id = FSI_PORT_B, +}; + +static struct platform_device_id fsi_id_table[] = { + /* FSI 2 */ + { "sh_fsi2_a_hdmi", (kernel_ulong_t)&fsi2_a_hdmi }, + { "sh_fsi2_b_hdmi", (kernel_ulong_t)&fsi2_b_hdmi }, + {}, +}; + +static struct platform_driver fsi_hdmi = { + .driver = { + .name = "fsi-hdmi-audio", + }, + .probe = fsi_hdmi_probe, + .remove = fsi_hdmi_remove, + .id_table = fsi_id_table, +}; + +static int __init fsi_hdmi_init(void) +{ + return platform_driver_register(&fsi_hdmi); +} + +static void __exit fsi_hdmi_exit(void) +{ + platform_driver_unregister(&fsi_hdmi); } module_init(fsi_hdmi_init); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 4c2404b1b862..23c0e83d4c19 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -19,20 +19,26 @@ #include <sound/soc.h> #include <sound/sh_fsi.h> -#define DO_FMT 0x0000 -#define DOFF_CTL 0x0004 -#define DOFF_ST 0x0008 -#define DI_FMT 0x000C -#define DIFF_CTL 0x0010 -#define DIFF_ST 0x0014 -#define CKG1 0x0018 -#define CKG2 0x001C -#define DIDT 0x0020 -#define DODT 0x0024 -#define MUTE_ST 0x0028 -#define OUT_SEL 0x0030 -#define REG_END OUT_SEL - +/* PortA/PortB register */ +#define REG_DO_FMT 0x0000 +#define REG_DOFF_CTL 0x0004 +#define REG_DOFF_ST 0x0008 +#define REG_DI_FMT 0x000C +#define REG_DIFF_CTL 0x0010 +#define REG_DIFF_ST 0x0014 +#define REG_CKG1 0x0018 +#define REG_CKG2 0x001C +#define REG_DIDT 0x0020 +#define REG_DODT 0x0024 +#define REG_MUTE_ST 0x0028 +#define REG_OUT_SEL 0x0030 + +/* master register */ +#define MST_CLK_RST 0x0210 +#define MST_SOFT_RST 0x0214 +#define MST_FIFO_SZ 0x0218 + +/* core register (depend on FSI version) */ #define A_MST_CTLR 0x0180 #define B_MST_CTLR 0x01A0 #define CPU_INT_ST 0x01F4 @@ -41,22 +47,23 @@ #define INT_ST 0x0200 #define IEMSK 0x0204 #define IMSK 0x0208 -#define MUTE 0x020C -#define CLK_RST 0x0210 -#define SOFT_RST 0x0214 -#define FIFO_SZ 0x0218 -#define MREG_START A_MST_CTLR -#define MREG_END FIFO_SZ /* DO_FMT */ /* DI_FMT */ +#define CR_BWS_24 (0x0 << 20) /* FSI2 */ +#define CR_BWS_16 (0x1 << 20) /* FSI2 */ +#define CR_BWS_20 (0x2 << 20) /* FSI2 */ + +#define CR_DTMD_PCM (0x0 << 8) /* FSI2 */ +#define CR_DTMD_SPDIF_PCM (0x1 << 8) /* FSI2 */ +#define CR_DTMD_SPDIF_STREAM (0x2 << 8) /* FSI2 */ + #define CR_MONO (0x0 << 4) #define CR_MONO_D (0x1 << 4) #define CR_PCM (0x2 << 4) #define CR_I2S (0x3 << 4) #define CR_TDM (0x4 << 4) #define CR_TDM_D (0x5 << 4) -#define CR_SPDIF 0x00100120 /* DOFF_CTL */ /* DIFF_CTL */ @@ -71,6 +78,8 @@ /* CKG1 */ #define ACKMD_MASK 0x00007000 #define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) /* A/B MST_CTLR */ #define BP (1 << 4) /* Fix the signal of Biphase output */ @@ -93,6 +102,10 @@ #define IR (1 << 4) /* Interrupt Reset */ #define FSISR (1 << 0) /* Software Reset */ +/* OUT_SEL (FSI2) */ +#define DMMD (1 << 4) /* SPDIF output timing 0: Biphase only */ + /* 1: Biphase and serial */ + /* FIFO_SZ */ #define FIFO_SZ_MASK 0x7 @@ -100,6 +113,8 @@ #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) +typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int enable); + /* * FSI driver use below type name for variable * @@ -117,24 +132,25 @@ struct fsi_stream { struct snd_pcm_substream *substream; int fifo_max_num; - int chan_num; int buff_offset; int buff_len; int period_len; int period_num; + + int uerr_num; + int oerr_num; }; struct fsi_priv { void __iomem *base; struct fsi_master *master; + int chan_num; struct fsi_stream playback; struct fsi_stream capture; long rate; - - u32 mst_ctrl; }; struct fsi_core { @@ -143,6 +159,8 @@ struct fsi_core { u32 int_st; u32 iemsk; u32 imsk; + u32 a_mclk; + u32 b_mclk; }; struct fsi_master { @@ -182,62 +200,22 @@ static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) __fsi_reg_write(reg, val); } -static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) -{ - if (reg > REG_END) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } +#define fsi_reg_write(p, r, d)\ + __fsi_reg_write((u32)(p->base + REG_##r), d) - __fsi_reg_write((u32)(fsi->base + reg), data); -} +#define fsi_reg_read(p, r)\ + __fsi_reg_read((u32)(p->base + REG_##r)) -static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) -{ - if (reg > REG_END) { - pr_err("fsi: register access err (%s)\n", __func__); - return 0; - } +#define fsi_reg_mask_set(p, r, m, d)\ + __fsi_reg_mask_set((u32)(p->base + REG_##r), m, d) - return __fsi_reg_read((u32)(fsi->base + reg)); -} - -static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) -{ - if (reg > REG_END) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - - __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); -} - -static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data) -{ - unsigned long flags; - - if ((reg < MREG_START) || - (reg > MREG_END)) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - - spin_lock_irqsave(&master->lock, flags); - __fsi_reg_write((u32)(master->base + reg), data); - spin_unlock_irqrestore(&master->lock, flags); -} - -static u32 fsi_master_read(struct fsi_master *master, u32 reg) +#define fsi_master_read(p, r) _fsi_master_read(p, MST_##r) +#define fsi_core_read(p, r) _fsi_master_read(p, p->core->r) +static u32 _fsi_master_read(struct fsi_master *master, u32 reg) { u32 ret; unsigned long flags; - if ((reg < MREG_START) || - (reg > MREG_END)) { - pr_err("fsi: register access err (%s)\n", __func__); - return 0; - } - spin_lock_irqsave(&master->lock, flags); ret = __fsi_reg_read((u32)(master->base + reg)); spin_unlock_irqrestore(&master->lock, flags); @@ -245,17 +223,13 @@ static u32 fsi_master_read(struct fsi_master *master, u32 reg) return ret; } -static void fsi_master_mask_set(struct fsi_master *master, +#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d) +#define fsi_core_mask_set(p, r, m, d) _fsi_master_mask_set(p, p->core->r, m, d) +static void _fsi_master_mask_set(struct fsi_master *master, u32 reg, u32 mask, u32 data) { unsigned long flags; - if ((reg < MREG_START) || - (reg > MREG_END)) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - spin_lock_irqsave(&master->lock, flags); __fsi_reg_mask_set((u32)(master->base + reg), mask, data); spin_unlock_irqrestore(&master->lock, flags); @@ -282,9 +256,8 @@ static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) return rtd->cpu_dai; } -static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) { - struct snd_soc_dai *dai = fsi_get_dai(substream); struct fsi_master *master = snd_soc_dai_get_drvdata(dai); if (dai->id == 0) @@ -293,11 +266,27 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return &master->fsib; } +static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +{ + return fsi_get_priv_frm_dai(fsi_get_dai(substream)); +} + +static set_rate_func fsi_get_info_set_rate(struct fsi_master *master) +{ + if (!master->info) + return NULL; + + return master->info->set_rate; +} + static u32 fsi_get_info_flags(struct fsi_priv *fsi) { int is_porta = fsi_is_port_a(fsi); struct fsi_master *master = fsi_get_master(fsi); + if (!master->info) + return 0; + return is_porta ? master->info->porta_flags : master->info->portb_flags; } @@ -318,21 +307,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi, return is_play ? &fsi->playback : &fsi->capture; } -static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) -{ - u32 mode; - u32 flags = fsi_get_info_flags(fsi); - - mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; - - /* return - * 1 : master mode - * 0 : slave mode - */ - - return (mode & flags) != mode; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) { int is_porta = fsi_is_port_a(fsi); @@ -359,29 +333,42 @@ static void fsi_stream_push(struct fsi_priv *fsi, io->buff_offset = 0; io->period_len = period_len; io->period_num = 0; + io->oerr_num = -1; /* ignore 1st err */ + io->uerr_num = -1; /* ignore 1st err */ } static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) { struct fsi_stream *io = fsi_get_stream(fsi, is_play); + struct snd_soc_dai *dai = fsi_get_dai(io->substream); + + + if (io->oerr_num > 0) + dev_err(dai->dev, "over_run = %d\n", io->oerr_num); + + if (io->uerr_num > 0) + dev_err(dai->dev, "under_run = %d\n", io->uerr_num); io->substream = NULL; io->buff_len = 0; io->buff_offset = 0; io->period_len = 0; io->period_num = 0; + io->oerr_num = 0; + io->uerr_num = 0; } static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) { u32 status; - u32 reg = is_play ? DOFF_ST : DIFF_ST; - struct fsi_stream *io = fsi_get_stream(fsi, is_play); int data_num; - status = fsi_reg_read(fsi, reg); + status = is_play ? + fsi_reg_read(fsi, DOFF_ST) : + fsi_reg_read(fsi, DIFF_ST); + data_num = 0x1ff & (status >> 8); - data_num *= io->chan_num; + data_num *= fsi->chan_num; return data_num; } @@ -403,7 +390,28 @@ static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) struct snd_pcm_substream *substream = io->substream; struct snd_pcm_runtime *runtime = substream->runtime; - return frames_to_bytes(runtime, 1) / io->chan_num; + return frames_to_bytes(runtime, 1) / fsi->chan_num; +} + +static void fsi_count_fifo_err(struct fsi_priv *fsi) +{ + u32 ostatus = fsi_reg_read(fsi, DOFF_ST); + u32 istatus = fsi_reg_read(fsi, DIFF_ST); + + if (ostatus & ERR_OVER) + fsi->playback.oerr_num++; + + if (ostatus & ERR_UNDER) + fsi->playback.uerr_num++; + + if (istatus & ERR_OVER) + fsi->capture.oerr_num++; + + if (istatus & ERR_UNDER) + fsi->capture.uerr_num++; + + fsi_reg_write(fsi, DOFF_ST, 0); + fsi_reg_write(fsi, DIFF_ST, 0); } /* @@ -473,8 +481,8 @@ static void fsi_irq_enable(struct fsi_priv *fsi, int is_play) u32 data = AB_IO(1, fsi_get_port_shift(fsi, is_play)); struct fsi_master *master = fsi_get_master(fsi); - fsi_master_mask_set(master, master->core->imsk, data, data); - fsi_master_mask_set(master, master->core->iemsk, data, data); + fsi_core_mask_set(master, imsk, data, data); + fsi_core_mask_set(master, iemsk, data, data); } static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) @@ -482,18 +490,13 @@ static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) u32 data = AB_IO(1, fsi_get_port_shift(fsi, is_play)); struct fsi_master *master = fsi_get_master(fsi); - fsi_master_mask_set(master, master->core->imsk, data, 0); - fsi_master_mask_set(master, master->core->iemsk, data, 0); + fsi_core_mask_set(master, imsk, data, 0); + fsi_core_mask_set(master, iemsk, data, 0); } static u32 fsi_irq_get_status(struct fsi_master *master) { - return fsi_master_read(master, master->core->int_st); -} - -static void fsi_irq_clear_all_status(struct fsi_master *master) -{ - fsi_master_write(master, master->core->int_st, 0); + return fsi_core_read(master, int_st); } static void fsi_irq_clear_status(struct fsi_priv *fsi) @@ -505,7 +508,7 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi) data |= AB_IO(1, fsi_get_port_shift(fsi, 1)); /* clear interrupt factor */ - fsi_master_mask_set(master, master->core->int_st, data, 0); + fsi_core_mask_set(master, int_st, data, 0); } /* @@ -516,17 +519,19 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi) static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) { struct fsi_master *master = fsi_get_master(fsi); - u32 val = BP | SE; + u32 mask, val; if (master->core->ver < 2) { pr_err("fsi: register access err (%s)\n", __func__); return; } - if (enable) - fsi_master_mask_set(master, fsi->mst_ctrl, val, val); - else - fsi_master_mask_set(master, fsi->mst_ctrl, val, 0); + mask = BP | SE; + val = enable ? mask : 0; + + fsi_is_port_a(fsi) ? + fsi_core_mask_set(master, a_mclk, mask, val) : + fsi_core_mask_set(master, b_mclk, mask, val); } /* @@ -550,7 +555,7 @@ static void fsi_fifo_init(struct fsi_priv *fsi, { struct fsi_master *master = fsi_get_master(fsi); struct fsi_stream *io = fsi_get_stream(fsi, is_play); - u32 ctrl, shift, i; + u32 shift, i; /* get on-chip RAM capacity */ shift = fsi_master_read(master, FIFO_SZ); @@ -578,18 +583,22 @@ static void fsi_fifo_init(struct fsi_priv *fsi, * 7 channels: 32 ( 32 x 7 = 224) * 8 channels: 32 ( 32 x 8 = 256) */ - for (i = 1; i < io->chan_num; i <<= 1) + for (i = 1; i < fsi->chan_num; i <<= 1) io->fifo_max_num >>= 1; dev_dbg(dai->dev, "%d channel %d store\n", - io->chan_num, io->fifo_max_num); + fsi->chan_num, io->fifo_max_num); - ctrl = is_play ? DOFF_CTL : DIFF_CTL; - - /* set interrupt generation factor */ - fsi_reg_write(fsi, ctrl, IRQ_HALF); - - /* clear FIFO */ - fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR); + /* + * set interrupt generation factor + * clear FIFO + */ + if (is_play) { + fsi_reg_write(fsi, DOFF_CTL, IRQ_HALF); + fsi_reg_mask_set(fsi, DOFF_CTL, FIFO_CLR, FIFO_CLR); + } else { + fsi_reg_write(fsi, DIFF_CTL, IRQ_HALF); + fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); + } } static void fsi_soft_all_reset(struct fsi_master *master) @@ -604,13 +613,12 @@ static void fsi_soft_all_reset(struct fsi_master *master) mdelay(10); } -static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) +static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) { struct snd_pcm_runtime *runtime; struct snd_pcm_substream *substream = NULL; int is_play = fsi_stream_is_play(stream); struct fsi_stream *io = fsi_get_stream(fsi, is_play); - u32 status_reg = is_play ? DOFF_ST : DIFF_ST; int data_residue_num; int data_num; int data_num_max; @@ -654,7 +662,7 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) * data_num_max : number of FSI fifo free space * data_num : number of ALSA residue data */ - data_num_max = io->fifo_max_num * io->chan_num; + data_num_max = io->fifo_max_num * fsi->chan_num; data_num_max -= fsi_get_fifo_data_num(fsi, is_play); data_num = data_residue_num; @@ -698,35 +706,20 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) /* update buff_offset */ io->buff_offset += fsi_num2offset(data_num, ch_width); - /* check fifo status */ - if (!startup) { - struct snd_soc_dai *dai = fsi_get_dai(substream); - u32 status = fsi_reg_read(fsi, status_reg); - - if (status & ERR_OVER) - dev_err(dai->dev, "over run\n"); - if (status & ERR_UNDER) - dev_err(dai->dev, "under run\n"); - } - fsi_reg_write(fsi, status_reg, 0); - - /* re-enable irq */ - fsi_irq_enable(fsi, is_play); - if (over_period) snd_pcm_period_elapsed(substream); return 0; } -static int fsi_data_pop(struct fsi_priv *fsi, int startup) +static int fsi_data_pop(struct fsi_priv *fsi) { - return fsi_fifo_data_ctrl(fsi, startup, SNDRV_PCM_STREAM_CAPTURE); + return fsi_fifo_data_ctrl(fsi, SNDRV_PCM_STREAM_CAPTURE); } -static int fsi_data_push(struct fsi_priv *fsi, int startup) +static int fsi_data_push(struct fsi_priv *fsi) { - return fsi_fifo_data_ctrl(fsi, startup, SNDRV_PCM_STREAM_PLAYBACK); + return fsi_fifo_data_ctrl(fsi, SNDRV_PCM_STREAM_PLAYBACK); } static irqreturn_t fsi_interrupt(int irq, void *data) @@ -739,15 +732,19 @@ static irqreturn_t fsi_interrupt(int irq, void *data) fsi_master_mask_set(master, SOFT_RST, IR, IR); if (int_st & AB_IO(1, AO_SHIFT)) - fsi_data_push(&master->fsia, 0); + fsi_data_push(&master->fsia); if (int_st & AB_IO(1, BO_SHIFT)) - fsi_data_push(&master->fsib, 0); + fsi_data_push(&master->fsib); if (int_st & AB_IO(1, AI_SHIFT)) - fsi_data_pop(&master->fsia, 0); + fsi_data_pop(&master->fsia); if (int_st & AB_IO(1, BI_SHIFT)) - fsi_data_pop(&master->fsib, 0); + fsi_data_pop(&master->fsib); - fsi_irq_clear_all_status(master); + fsi_count_fifo_err(&master->fsia); + fsi_count_fifo_err(&master->fsib); + + fsi_irq_clear_status(&master->fsia); + fsi_irq_clear_status(&master->fsib); return IRQ_HANDLED; } @@ -760,26 +757,12 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); - struct fsi_stream *io; u32 flags = fsi_get_info_flags(fsi); - u32 fmt; - u32 reg; u32 data; int is_play = fsi_is_play(substream); - int is_master; - - io = fsi_get_stream(fsi, is_play); pm_runtime_get_sync(dai->dev); - /* CKG1 */ - data = is_play ? (1 << 0) : (1 << 4); - is_master = fsi_is_master_mode(fsi, is_play); - if (is_master) - fsi_reg_mask_set(fsi, CKG1, data, data); - else - fsi_reg_mask_set(fsi, CKG1, data, 0); /* clock inversion (CKG2) */ data = 0; @@ -794,53 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); - /* do fmt, di fmt */ - data = 0; - reg = is_play ? DO_FMT : DI_FMT; - fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); - switch (fmt) { - case SH_FSI_FMT_MONO: - data = CR_MONO; - io->chan_num = 1; - break; - case SH_FSI_FMT_MONO_DELAY: - data = CR_MONO_D; - io->chan_num = 1; - break; - case SH_FSI_FMT_PCM: - data = CR_PCM; - io->chan_num = 2; - break; - case SH_FSI_FMT_I2S: - data = CR_I2S; - io->chan_num = 2; - break; - case SH_FSI_FMT_TDM: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM | (io->chan_num - 1); - break; - case SH_FSI_FMT_TDM_DELAY: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM_D | (io->chan_num - 1); - break; - case SH_FSI_FMT_SPDIF: - if (master->core->ver < 2) { - dev_err(dai->dev, "This FSI can not use SPDIF\n"); - return -EINVAL; - } - data = CR_SPDIF; - io->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, 0x0010, 0x0010); - break; - default: - dev_err(dai->dev, "unknown format.\n"); - return -EINVAL; - } - fsi_reg_write(fsi, reg, data); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -857,12 +793,12 @@ 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); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; fsi_irq_disable(fsi, is_play); fsi_clk_ctrl(fsi, 0); - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (set_rate && fsi->rate) set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); fsi->rate = 0; @@ -883,7 +819,8 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, fsi_stream_push(fsi, is_play, substream, frames_to_bytes(runtime, runtime->buffer_size), frames_to_bytes(runtime, runtime->period_size)); - ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1); + ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); + fsi_irq_enable(fsi, is_play); break; case SNDRV_PCM_TRIGGER_STOP: fsi_irq_disable(fsi, is_play); @@ -894,18 +831,100 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) +{ + u32 data = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + data = CR_I2S; + fsi->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + data = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_set_fmt_spdif(struct fsi_priv *fsi) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 data = 0; + + if (master->core->ver < 2) + return -EINVAL; + + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 flags = fsi_get_info_flags(fsi); + u32 data = 0; + int ret; + + pm_runtime_get_sync(dai->dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + data = DIMD | DOMD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + ret = -EINVAL; + goto set_fmt_exit; + } + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + + /* set format */ + switch (flags & SH_FSI_FMT_MASK) { + case SH_FSI_FMT_DAI: + ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + break; + case SH_FSI_FMT_SPDIF: + ret = fsi_set_fmt_spdif(fsi); + break; + default: + ret = -EINVAL; + } + +set_fmt_exit: + pm_runtime_put_sync(dai->dev); + + return ret; +} + static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; int fsi_ver = master->core->ver; long rate = params_rate(params); int ret; - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (!set_rate) return 0; @@ -980,6 +999,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, .hw_params = fsi_dai_hw_params, }; @@ -1174,18 +1194,17 @@ static int fsi_probe(struct platform_device *pdev) /* FSI A setting */ master->fsia.base = master->base; master->fsia.master = master; - master->fsia.mst_ctrl = A_MST_CTLR; /* FSI B setting */ master->fsib.base = master->base + 0x40; master->fsib.master = master; - master->fsib.mst_ctrl = B_MST_CTLR; pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&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); ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, id_entry->name, master); @@ -1200,8 +1219,17 @@ static int fsi_probe(struct platform_device *pdev) goto exit_free_irq; } - return snd_soc_register_dais(&pdev->dev, fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); + ret = snd_soc_register_dais(&pdev->dev, fsi_soc_dai, + ARRAY_SIZE(fsi_soc_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "cannot snd dai register\n"); + goto exit_snd_soc; + } + + return ret; +exit_snd_soc: + snd_soc_unregister_platform(&pdev->dev); exit_free_irq: free_irq(irq, master); exit_iounmap: @@ -1220,12 +1248,11 @@ static int fsi_remove(struct platform_device *pdev) master = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(fsi_soc_dai)); - snd_soc_unregister_platform(&pdev->dev); - + free_irq(master->irq, master); pm_runtime_disable(&pdev->dev); - free_irq(master->irq, master); + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(fsi_soc_dai)); + snd_soc_unregister_platform(&pdev->dev); iounmap(master->base); kfree(master); @@ -1266,6 +1293,8 @@ static struct fsi_core fsi2_core = { .int_st = CPU_INT_ST, .iemsk = CPU_IEMSK, .imsk = CPU_IMSK, + .a_mclk = A_MST_CTLR, + .b_mclk = B_MST_CTLR, }; static struct platform_device_id fsi_id_table[] = { @@ -1301,3 +1330,4 @@ module_exit(fsi_mobile_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SuperH onchip FSI audio driver"); MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); +MODULE_ALIAS("platform:fsi-pcm-audio"); diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index ac6c49ce6fdf..6088a6a3238a 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -8,11 +8,11 @@ * published by the Free Software Foundation. */ +#include <linux/clkdev.h> #include <linux/device.h> #include <linux/firmware.h> #include <linux/module.h> -#include <asm/clkdev.h> #include <asm/clock.h> #include <cpu/sh7722.h> @@ -20,7 +20,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include "../codecs/wm8978.h" #include "siu.h" @@ -140,11 +139,12 @@ static const struct snd_soc_dapm_route audio_map[] = { static int migor_dai_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_new_controls(codec, migor_dapm_widgets, + snd_soc_dapm_new_controls(dapm, migor_dapm_widgets, ARRAY_SIZE(migor_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; } diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c index f8e0ab82ef59..917d3ceadc9d 100644 --- a/sound/soc/sh/sh7760-ac97.c +++ b/sound/soc/sh/sh7760-ac97.c @@ -12,7 +12,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <asm/io.h> #define IPSEL 0xFE400034 @@ -23,7 +22,7 @@ extern struct snd_soc_platform_driver sh7760_soc_platform; static int machine_init(struct snd_soc_pcm_runtime *rtd) { - snd_soc_dapm_sync(rtd->codec); + snd_soc_dapm_sync(&rtd->codec->dapm); return 0; } diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h index 9f4dcb921ff0..83c3430ad797 100644 --- a/sound/soc/sh/siu.h +++ b/sound/soc/sh/siu.h @@ -75,7 +75,7 @@ struct siu_firmware { #include <sound/core.h> #include <sound/pcm.h> -#include <sound/soc-dai.h> +#include <sound/soc.h> #define SIU_PERIOD_BYTES_MAX 8192 /* DMA transfer/period size */ #define SIU_PERIOD_BYTES_MIN 256 /* DMA transfer/period size */ diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index af53b64d8af2..4973c2939d79 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -28,7 +28,7 @@ #include <asm/siu.h> #include <sound/control.h> -#include <sound/soc-dai.h> +#include <sound/soc.h> #include "siu.h" diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index ed29c9e1ed4e..a423babcf145 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -29,7 +29,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> -#include <sound/soc-dai.h> +#include <sound/soc.h> #include <asm/siu.h> diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index d214f02cbb65..5d76da43b14c 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -14,27 +14,37 @@ #include <linux/i2c.h> #include <linux/spi/spi.h> #include <sound/soc.h> +#include <linux/lzo.h> +#include <linux/bitmap.h> +#include <linux/rbtree.h> + +#include <trace/events/asoc.h> static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int reg) { - u16 *cache = codec->reg_cache; + int ret; + unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + 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); } - return cache[reg]; + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u16 *cache = codec->reg_cache; u8 data[2]; int ret; @@ -42,16 +52,18 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; + 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; } - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - ret = codec->hw_write(codec->control_data, data, 2); if (ret == 2) return 0; @@ -77,7 +89,7 @@ static int snd_soc_4_12_spi_write(void *control_data, const char *data, msg[1] = data[0]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -94,23 +106,28 @@ static int snd_soc_4_12_spi_write(void *control_data, const char *data, static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, unsigned int reg) { - u16 *cache = codec->reg_cache; + int ret; + unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + 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); } - return cache[reg]; + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u16 *cache = codec->reg_cache; u8 data[2]; int ret; @@ -118,16 +135,18 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; + 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; } - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - ret = codec->hw_write(codec->control_data, data, 2); if (ret == 2) return 0; @@ -153,7 +172,7 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data, msg[1] = data[1]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -170,24 +189,26 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data, static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u8 *cache = codec->reg_cache; 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) - cache[reg] = value; + 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; } - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) return 0; else @@ -197,18 +218,24 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, unsigned int reg) { - u8 *cache = codec->reg_cache; + int ret; + unsigned int val; reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + 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); } - return cache[reg]; + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } #if defined(CONFIG_SPI_MASTER) @@ -227,7 +254,7 @@ static int snd_soc_8_8_spi_write(void *control_data, const char *data, msg[1] = data[1]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -244,24 +271,26 @@ static int snd_soc_8_8_spi_write(void *control_data, const char *data, static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u16 *reg_cache = codec->reg_cache; 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) - reg_cache[reg] = value; + 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; } - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - if (codec->hw_write(codec->control_data, data, 3) == 3) return 0; else @@ -271,17 +300,23 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int reg) { - u16 *cache = codec->reg_cache; + int ret; + unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + 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); - } else { - return cache[reg]; } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } #if defined(CONFIG_SPI_MASTER) @@ -301,7 +336,7 @@ static int snd_soc_8_16_spi_write(void *control_data, const char *data, msg[2] = data[2]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -420,24 +455,29 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, unsigned int reg) { - u8 *cache = codec->reg_cache; + int ret; + unsigned int val; reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + 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); } - return cache[reg]; + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u8 *cache = codec->reg_cache; u8 data[3]; int ret; @@ -447,16 +487,18 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, reg &= 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; + 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; } - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - ret = codec->hw_write(codec->control_data, data, 3); if (ret == 3) return 0; @@ -483,7 +525,7 @@ static int snd_soc_16_8_spi_write(void *control_data, const char *data, msg[2] = data[2]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -534,23 +576,29 @@ 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) { - u16 *cache = codec->reg_cache; + int ret; + unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + 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); } - return cache[reg]; + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + + return val; } static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - u16 *cache = codec->reg_cache; u8 data[4]; int ret; @@ -560,16 +608,18 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, data[3] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) - cache[reg] = value; + 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; } - dev_dbg(codec->dev, "0x%x = 0x%x\n", reg, value); - ret = codec->hw_write(codec->control_data, data, 4); if (ret == 4) return 0; @@ -597,7 +647,7 @@ static int snd_soc_16_16_spi_write(void *control_data, const char *data, msg[3] = data[3]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -692,8 +742,8 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, return -EINVAL; } - codec->driver->write = io_types[i].write; - codec->driver->read = io_types[i].read; + codec->write = io_types[i].write; + codec->read = io_types[i].read; switch (control) { case SND_SOC_CUSTOM: @@ -724,3 +774,906 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, return 0; } EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); + +static bool snd_soc_set_cache_val(void *base, unsigned int idx, + unsigned int val, unsigned int word_size) +{ + switch (word_size) { + case 1: { + u8 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } + case 2: { + u16 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } + default: + BUG(); + } + return false; +} + +static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, + unsigned int word_size) +{ + switch (word_size) { + case 1: { + const u8 *cache = base; + return cache[idx]; + } + case 2: { + const u16 *cache = base; + return cache[idx]; + } + default: + BUG(); + } + /* unreachable */ + return -1; +} + +struct snd_soc_rbtree_node { + struct rb_node node; + unsigned int reg; + unsigned int value; + unsigned int defval; +} __attribute__ ((packed)); + +struct snd_soc_rbtree_ctx { + struct rb_root root; +}; + +static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup( + struct rb_root *root, unsigned int reg) +{ + struct rb_node *node; + struct snd_soc_rbtree_node *rbnode; + + node = root->rb_node; + while (node) { + rbnode = container_of(node, struct snd_soc_rbtree_node, node); + if (rbnode->reg < reg) + node = node->rb_left; + else if (rbnode->reg > reg) + node = node->rb_right; + else + return rbnode; + } + + return NULL; +} + +static int snd_soc_rbtree_insert(struct rb_root *root, + struct snd_soc_rbtree_node *rbnode) +{ + struct rb_node **new, *parent; + struct snd_soc_rbtree_node *rbnode_tmp; + + parent = NULL; + new = &root->rb_node; + while (*new) { + rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node, + node); + parent = *new; + if (rbnode_tmp->reg < rbnode->reg) + new = &((*new)->rb_left); + else if (rbnode_tmp->reg > rbnode->reg) + new = &((*new)->rb_right); + else + return 0; + } + + /* insert the node into the rbtree */ + rb_link_node(&rbnode->node, parent, new); + rb_insert_color(&rbnode->node, root); + + return 1; +} + +static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) +{ + struct snd_soc_rbtree_ctx *rbtree_ctx; + struct rb_node *node; + struct snd_soc_rbtree_node *rbnode; + unsigned int val; + int ret; + + rbtree_ctx = codec->reg_cache; + for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { + rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); + if (rbnode->value == rbnode->defval) + continue; + ret = snd_soc_cache_read(codec, rbnode->reg, &val); + if (ret) + return ret; + codec->cache_bypass = 1; + ret = snd_soc_write(codec, rbnode->reg, val); + codec->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", + rbnode->reg, val); + } + + return 0; +} + +static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + struct snd_soc_rbtree_ctx *rbtree_ctx; + struct snd_soc_rbtree_node *rbnode; + + rbtree_ctx = codec->reg_cache; + rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); + if (rbnode) { + if (rbnode->value == value) + return 0; + rbnode->value = value; + } else { + /* bail out early, no need to create the rbnode yet */ + if (!value) + return 0; + /* + * for uninitialized registers whose value is changed + * from the default zero, create an rbnode and insert + * it into the tree. + */ + rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); + if (!rbnode) + return -ENOMEM; + rbnode->reg = reg; + rbnode->value = value; + snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode); + } + + return 0; +} + +static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec, + unsigned int reg, unsigned int *value) +{ + struct snd_soc_rbtree_ctx *rbtree_ctx; + struct snd_soc_rbtree_node *rbnode; + + rbtree_ctx = codec->reg_cache; + rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg); + if (rbnode) { + *value = rbnode->value; + } else { + /* uninitialized registers default to 0 */ + *value = 0; + } + + return 0; +} + +static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) +{ + struct rb_node *next; + struct snd_soc_rbtree_ctx *rbtree_ctx; + struct snd_soc_rbtree_node *rbtree_node; + + /* if we've already been called then just return */ + rbtree_ctx = codec->reg_cache; + if (!rbtree_ctx) + return 0; + + /* free up the rbtree */ + next = rb_first(&rbtree_ctx->root); + while (next) { + rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node); + next = rb_next(&rbtree_node->node); + rb_erase(&rbtree_node->node, &rbtree_ctx->root); + kfree(rbtree_node); + } + + /* release the resources */ + kfree(codec->reg_cache); + codec->reg_cache = NULL; + + return 0; +} + +static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) +{ + struct snd_soc_rbtree_node *rbtree_node; + struct snd_soc_rbtree_ctx *rbtree_ctx; + unsigned int val; + unsigned int word_size; + int i; + int ret; + + codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL); + if (!codec->reg_cache) + return -ENOMEM; + + rbtree_ctx = codec->reg_cache; + rbtree_ctx->root = RB_ROOT; + + if (!codec->reg_def_copy) + return 0; + + /* + * populate the rbtree with the initialized registers. All other + * registers will be inserted when they are first modified. + */ + word_size = codec->driver->reg_word_size; + for (i = 0; i < codec->driver->reg_cache_size; ++i) { + val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size); + if (!val) + continue; + rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); + if (!rbtree_node) { + ret = -ENOMEM; + snd_soc_cache_exit(codec); + break; + } + rbtree_node->reg = i; + rbtree_node->value = val; + rbtree_node->defval = val; + snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node); + } + + return 0; +} + +#ifdef CONFIG_SND_SOC_CACHE_LZO +struct snd_soc_lzo_ctx { + void *wmem; + void *dst; + const void *src; + size_t src_len; + size_t dst_len; + size_t decompressed_size; + unsigned long *sync_bmp; + int sync_bmp_nbits; +}; + +#define LZO_BLOCK_NUM 8 +static int snd_soc_lzo_block_count(void) +{ + return LZO_BLOCK_NUM; +} + +static int snd_soc_lzo_prepare(struct snd_soc_lzo_ctx *lzo_ctx) +{ + lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + if (!lzo_ctx->wmem) + return -ENOMEM; + return 0; +} + +static int snd_soc_lzo_compress(struct snd_soc_lzo_ctx *lzo_ctx) +{ + size_t compress_size; + int ret; + + ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len, + lzo_ctx->dst, &compress_size, lzo_ctx->wmem); + if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len) + return -EINVAL; + lzo_ctx->dst_len = compress_size; + return 0; +} + +static int snd_soc_lzo_decompress(struct snd_soc_lzo_ctx *lzo_ctx) +{ + size_t dst_len; + int ret; + + dst_len = lzo_ctx->dst_len; + ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len, + lzo_ctx->dst, &dst_len); + if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len) + return -EINVAL; + return 0; +} + +static int snd_soc_lzo_compress_cache_block(struct snd_soc_codec *codec, + struct snd_soc_lzo_ctx *lzo_ctx) +{ + int ret; + + lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE); + lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL); + if (!lzo_ctx->dst) { + lzo_ctx->dst_len = 0; + return -ENOMEM; + } + + ret = snd_soc_lzo_compress(lzo_ctx); + if (ret < 0) + return ret; + return 0; +} + +static int snd_soc_lzo_decompress_cache_block(struct snd_soc_codec *codec, + struct snd_soc_lzo_ctx *lzo_ctx) +{ + int ret; + + lzo_ctx->dst_len = lzo_ctx->decompressed_size; + lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL); + if (!lzo_ctx->dst) { + lzo_ctx->dst_len = 0; + return -ENOMEM; + } + + ret = snd_soc_lzo_decompress(lzo_ctx); + if (ret < 0) + return ret; + return 0; +} + +static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, + unsigned int reg) +{ + const struct snd_soc_codec_driver *codec_drv; + + codec_drv = codec->driver; + return (reg * codec_drv->reg_word_size) / + DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); +} + +static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, + unsigned int reg) +{ + const struct snd_soc_codec_driver *codec_drv; + + codec_drv = codec->driver; + return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) / + codec_drv->reg_word_size); +} + +static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec) +{ + const struct snd_soc_codec_driver *codec_drv; + + codec_drv = codec->driver; + return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); +} + +static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) +{ + struct snd_soc_lzo_ctx **lzo_blocks; + unsigned int val; + int i; + int ret; + + lzo_blocks = codec->reg_cache; + for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { + ret = snd_soc_cache_read(codec, i, &val); + if (ret) + return ret; + codec->cache_bypass = 1; + ret = snd_soc_write(codec, i, val); + codec->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", + i, val); + } + + return 0; +} + +static int snd_soc_lzo_cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + struct snd_soc_lzo_ctx *lzo_block, **lzo_blocks; + int ret, blkindex, blkpos; + size_t blksize, tmp_dst_len; + void *tmp_dst; + + /* index of the compressed lzo block */ + blkindex = snd_soc_lzo_get_blkindex(codec, reg); + /* register index within the decompressed block */ + blkpos = snd_soc_lzo_get_blkpos(codec, reg); + /* size of the compressed block */ + blksize = snd_soc_lzo_get_blksize(codec); + lzo_blocks = codec->reg_cache; + lzo_block = lzo_blocks[blkindex]; + + /* save the pointer and length of the compressed block */ + tmp_dst = lzo_block->dst; + tmp_dst_len = lzo_block->dst_len; + + /* prepare the source to be the compressed block */ + lzo_block->src = lzo_block->dst; + lzo_block->src_len = lzo_block->dst_len; + + /* decompress the block */ + ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block); + if (ret < 0) { + kfree(lzo_block->dst); + goto out; + } + + /* write the new value to the cache */ + if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value, + codec->driver->reg_word_size)) { + kfree(lzo_block->dst); + goto out; + } + + /* prepare the source to be the decompressed block */ + lzo_block->src = lzo_block->dst; + lzo_block->src_len = lzo_block->dst_len; + + /* compress the block */ + ret = snd_soc_lzo_compress_cache_block(codec, lzo_block); + if (ret < 0) { + kfree(lzo_block->dst); + kfree(lzo_block->src); + goto out; + } + + /* set the bit so we know we have to sync this register */ + set_bit(reg, lzo_block->sync_bmp); + kfree(tmp_dst); + kfree(lzo_block->src); + return 0; +out: + lzo_block->dst = tmp_dst; + lzo_block->dst_len = tmp_dst_len; + return ret; +} + +static int snd_soc_lzo_cache_read(struct snd_soc_codec *codec, + unsigned int reg, unsigned int *value) +{ + struct snd_soc_lzo_ctx *lzo_block, **lzo_blocks; + int ret, blkindex, blkpos; + size_t blksize, tmp_dst_len; + void *tmp_dst; + + *value = 0; + /* index of the compressed lzo block */ + blkindex = snd_soc_lzo_get_blkindex(codec, reg); + /* register index within the decompressed block */ + blkpos = snd_soc_lzo_get_blkpos(codec, reg); + /* size of the compressed block */ + blksize = snd_soc_lzo_get_blksize(codec); + lzo_blocks = codec->reg_cache; + lzo_block = lzo_blocks[blkindex]; + + /* save the pointer and length of the compressed block */ + tmp_dst = lzo_block->dst; + tmp_dst_len = lzo_block->dst_len; + + /* prepare the source to be the compressed block */ + lzo_block->src = lzo_block->dst; + lzo_block->src_len = lzo_block->dst_len; + + /* decompress the block */ + ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block); + if (ret >= 0) + /* fetch the value from the cache */ + *value = snd_soc_get_cache_val(lzo_block->dst, blkpos, + codec->driver->reg_word_size); + + kfree(lzo_block->dst); + /* restore the pointer and length of the compressed block */ + lzo_block->dst = tmp_dst; + lzo_block->dst_len = tmp_dst_len; + return 0; +} + +static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec) +{ + struct snd_soc_lzo_ctx **lzo_blocks; + int i, blkcount; + + lzo_blocks = codec->reg_cache; + if (!lzo_blocks) + return 0; + + blkcount = snd_soc_lzo_block_count(); + /* + * the pointer to the bitmap used for syncing the cache + * is shared amongst all lzo_blocks. Ensure it is freed + * only once. + */ + if (lzo_blocks[0]) + kfree(lzo_blocks[0]->sync_bmp); + for (i = 0; i < blkcount; ++i) { + if (lzo_blocks[i]) { + kfree(lzo_blocks[i]->wmem); + kfree(lzo_blocks[i]->dst); + } + /* each lzo_block is a pointer returned by kmalloc or NULL */ + kfree(lzo_blocks[i]); + } + kfree(lzo_blocks); + codec->reg_cache = NULL; + return 0; +} + +static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) +{ + struct snd_soc_lzo_ctx **lzo_blocks; + size_t bmp_size; + const struct snd_soc_codec_driver *codec_drv; + int ret, tofree, i, blksize, blkcount; + const char *p, *end; + unsigned long *sync_bmp; + + ret = 0; + codec_drv = codec->driver; + + /* + * If we have not been given a default register cache + * then allocate a dummy zero-ed out region, compress it + * and remember to free it afterwards. + */ + tofree = 0; + if (!codec->reg_def_copy) + tofree = 1; + + if (!codec->reg_def_copy) { + codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL); + if (!codec->reg_def_copy) + return -ENOMEM; + } + + blkcount = snd_soc_lzo_block_count(); + codec->reg_cache = kzalloc(blkcount * sizeof *lzo_blocks, + GFP_KERNEL); + if (!codec->reg_cache) { + ret = -ENOMEM; + goto err_tofree; + } + lzo_blocks = codec->reg_cache; + + /* + * allocate a bitmap to be used when syncing the cache with + * the hardware. Each time a register is modified, the corresponding + * bit is set in the bitmap, so we know that we have to sync + * that register. + */ + bmp_size = codec_drv->reg_cache_size; + sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long), + GFP_KERNEL); + if (!sync_bmp) { + ret = -ENOMEM; + goto err; + } + bitmap_zero(sync_bmp, bmp_size); + + /* allocate the lzo blocks and initialize them */ + for (i = 0; i < blkcount; ++i) { + lzo_blocks[i] = kzalloc(sizeof **lzo_blocks, + GFP_KERNEL); + if (!lzo_blocks[i]) { + kfree(sync_bmp); + ret = -ENOMEM; + goto err; + } + lzo_blocks[i]->sync_bmp = sync_bmp; + lzo_blocks[i]->sync_bmp_nbits = bmp_size; + /* alloc the working space for the compressed block */ + ret = snd_soc_lzo_prepare(lzo_blocks[i]); + if (ret < 0) + goto err; + } + + blksize = snd_soc_lzo_get_blksize(codec); + p = codec->reg_def_copy; + end = codec->reg_def_copy + codec->reg_size; + /* compress the register map and fill the lzo blocks */ + for (i = 0; i < blkcount; ++i, p += blksize) { + lzo_blocks[i]->src = p; + if (p + blksize > end) + lzo_blocks[i]->src_len = end - p; + else + lzo_blocks[i]->src_len = blksize; + ret = snd_soc_lzo_compress_cache_block(codec, + lzo_blocks[i]); + if (ret < 0) + goto err; + lzo_blocks[i]->decompressed_size = + lzo_blocks[i]->src_len; + } + + if (tofree) { + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; + } + return 0; +err: + snd_soc_cache_exit(codec); +err_tofree: + if (tofree) { + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; + } + return ret; +} +#endif + +static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) +{ + int i; + int ret; + const struct snd_soc_codec_driver *codec_drv; + unsigned int val; + + codec_drv = codec->driver; + for (i = 0; i < codec_drv->reg_cache_size; ++i) { + ret = snd_soc_cache_read(codec, i, &val); + if (ret) + return ret; + if (codec->reg_def_copy) + if (snd_soc_get_cache_val(codec->reg_def_copy, + i, codec_drv->reg_word_size) == val) + continue; + ret = snd_soc_write(codec, i, val); + if (ret) + return ret; + dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", + i, val); + } + return 0; +} + +static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + snd_soc_set_cache_val(codec->reg_cache, reg, value, + codec->driver->reg_word_size); + return 0; +} + +static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, + unsigned int reg, unsigned int *value) +{ + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); + return 0; +} + +static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) +{ + if (!codec->reg_cache) + return 0; + kfree(codec->reg_cache); + codec->reg_cache = NULL; + return 0; +} + +static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) +{ + const struct snd_soc_codec_driver *codec_drv; + + codec_drv = codec->driver; + + if (codec->reg_def_copy) + codec->reg_cache = kmemdup(codec->reg_def_copy, + codec->reg_size, GFP_KERNEL); + else + codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); + if (!codec->reg_cache) + return -ENOMEM; + + return 0; +} + +/* an array of all supported compression types */ +static const struct snd_soc_cache_ops cache_types[] = { + /* Flat *must* be the first entry for fallback */ + { + .id = SND_SOC_FLAT_COMPRESSION, + .name = "flat", + .init = snd_soc_flat_cache_init, + .exit = snd_soc_flat_cache_exit, + .read = snd_soc_flat_cache_read, + .write = snd_soc_flat_cache_write, + .sync = snd_soc_flat_cache_sync + }, +#ifdef CONFIG_SND_SOC_CACHE_LZO + { + .id = SND_SOC_LZO_COMPRESSION, + .name = "LZO", + .init = snd_soc_lzo_cache_init, + .exit = snd_soc_lzo_cache_exit, + .read = snd_soc_lzo_cache_read, + .write = snd_soc_lzo_cache_write, + .sync = snd_soc_lzo_cache_sync + }, +#endif + { + .id = SND_SOC_RBTREE_COMPRESSION, + .name = "rbtree", + .init = snd_soc_rbtree_cache_init, + .exit = snd_soc_rbtree_cache_exit, + .read = snd_soc_rbtree_cache_read, + .write = snd_soc_rbtree_cache_write, + .sync = snd_soc_rbtree_cache_sync + } +}; + +int snd_soc_cache_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cache_types); ++i) + if (cache_types[i].id == codec->compress_type) + break; + + /* Fall back to flat compression */ + if (i == ARRAY_SIZE(cache_types)) { + dev_warn(codec->dev, "Could not match compress type: %d\n", + codec->compress_type); + i = 0; + } + + mutex_init(&codec->cache_rw_mutex); + codec->cache_ops = &cache_types[i]; + + if (codec->cache_ops->init) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Initializing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + return codec->cache_ops->init(codec); + } + return -EINVAL; +} + +/* + * NOTE: keep in mind that this function might be called + * multiple times. + */ +int snd_soc_cache_exit(struct snd_soc_codec *codec) +{ + if (codec->cache_ops && codec->cache_ops->exit) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Destroying %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + return codec->cache_ops->exit(codec); + } + return -EINVAL; +} + +/** + * snd_soc_cache_read: Fetch the value of a given register from the cache. + * + * @codec: CODEC to configure. + * @reg: The register index. + * @value: The value to be returned. + */ +int snd_soc_cache_read(struct snd_soc_codec *codec, + unsigned int reg, unsigned int *value) +{ + int ret; + + mutex_lock(&codec->cache_rw_mutex); + + if (value && codec->cache_ops && codec->cache_ops->read) { + ret = codec->cache_ops->read(codec, reg, value); + mutex_unlock(&codec->cache_rw_mutex); + return ret; + } + + mutex_unlock(&codec->cache_rw_mutex); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_cache_read); + +/** + * snd_soc_cache_write: Set the value of a given register in the cache. + * + * @codec: CODEC to configure. + * @reg: The register index. + * @value: The new register value. + */ +int snd_soc_cache_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int ret; + + mutex_lock(&codec->cache_rw_mutex); + + if (codec->cache_ops && codec->cache_ops->write) { + ret = codec->cache_ops->write(codec, reg, value); + mutex_unlock(&codec->cache_rw_mutex); + return ret; + } + + mutex_unlock(&codec->cache_rw_mutex); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_cache_write); + +/** + * snd_soc_cache_sync: Sync the register cache with the hardware. + * + * @codec: CODEC to configure. + * + * Any registers that should not be synced should be marked as + * volatile. In general drivers can choose not to use the provided + * syncing functionality if they so require. + */ +int snd_soc_cache_sync(struct snd_soc_codec *codec) +{ + int ret; + const char *name; + + if (!codec->cache_sync) { + return 0; + } + + if (!codec->cache_ops || !codec->cache_ops->sync) + return -EINVAL; + + if (codec->cache_ops->name) + name = codec->cache_ops->name; + else + name = "unknown"; + + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); + ret = codec->cache_ops->sync(codec); + if (!ret) + codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_cache_sync); + +static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, + unsigned int reg) +{ + const struct snd_soc_codec_driver *codec_drv; + unsigned int min, max, index; + + codec_drv = codec->driver; + min = 0; + max = codec_drv->reg_access_size - 1; + do { + index = (min + max) / 2; + if (codec_drv->reg_access_default[index].reg == reg) + return index; + if (codec_drv->reg_access_default[index].reg < reg) + min = index + 1; + else + max = index; + } while (min <= max); + return -1; +} + +int snd_soc_default_volatile_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].vol; +} +EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); + +int snd_soc_default_readable_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].read; +} +EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 441285ade024..d8562ce4de7a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -33,19 +33,23 @@ #include <linux/slab.h> #include <sound/ac97_codec.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> +#define CREATE_TRACE_POINTS +#include <trace/events/asoc.h> + #define NAME_SIZE 32 static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS -static struct dentry *debugfs_root; +struct dentry *snd_soc_debugfs_root; +EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); @@ -54,8 +58,6 @@ static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); -static int snd_soc_register_card(struct snd_soc_card *card); -static int snd_soc_unregister_card(struct snd_soc_card *card); static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); /* @@ -67,29 +69,73 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); -/* - * This function forces any delayed work to be queued and run. - */ -static int run_delayed_work(struct delayed_work *dwork) +/* returns the minimum number of bytes needed to represent + * a particular given value */ +static int min_bytes_needed(unsigned long val) +{ + int c = 0; + int i; + + for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) + if (val & (1UL << i)) + break; + c = (sizeof val * 8) - c; + if (!c || (c % 8)) + c = (c + 8) / 8; + else + c /= 8; + return c; +} + +/* fill buf which is 'len' bytes with a formatted + * string of the form 'reg: value\n' */ +static int format_register_str(struct snd_soc_codec *codec, + unsigned int reg, char *buf, size_t len) { + int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + int regsize = codec->driver->reg_word_size * 2; int ret; + char tmpbuf[len + 1]; + char regbuf[regsize + 1]; + + /* since tmpbuf is allocated on the stack, warn the callers if they + * try to abuse this function */ + WARN_ON(len > 63); - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(dwork); + /* +2 for ': ' and + 1 for '\n' */ + if (wordsize + regsize + 2 + 1 != len) + return -EINVAL; - /* if there was any work waiting then we run it now and - * wait for it's completion */ - if (ret) { - schedule_delayed_work(dwork, 0); - flush_scheduled_work(); + ret = snd_soc_read(codec , reg); + if (ret < 0) { + memset(regbuf, 'X', regsize); + regbuf[regsize] = '\0'; + } else { + snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); } - return ret; + + /* prepare the buffer */ + snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); + /* copy it back to the caller without the '\0' */ + memcpy(buf, tmpbuf, len); + + return 0; } /* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, + size_t count, loff_t pos) { - int ret, i, step = 1, count = 0; + int i, step = 1; + int wordsize, regsize; + int len; + size_t total = 0; + loff_t p = 0; + + wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + regsize = codec->driver->reg_word_size * 2; + + len = wordsize + regsize + 2 + 1; if (!codec->driver->reg_cache_size) return 0; @@ -97,55 +143,37 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) if (codec->driver->reg_cache_step) step = codec->driver->reg_cache_step; - count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->driver->readable_register && !codec->driver->readable_register(i)) + if (codec->readable_register && !codec->readable_register(codec, i)) continue; - - count += sprintf(buf + count, "%2x: ", i); - if (count >= PAGE_SIZE - 1) - break; - if (codec->driver->display_register) { count += codec->driver->display_register(codec, buf + count, PAGE_SIZE - count, i); } else { - /* If the read fails it's almost certainly due to - * the register being volatile and the device being - * powered off. - */ - ret = codec->driver->read(codec, i); - if (ret >= 0) - count += snprintf(buf + count, - PAGE_SIZE - count, - "%4x", ret); - else - count += snprintf(buf + count, - PAGE_SIZE - count, - "<no data: %d>", ret); + /* only support larger than PAGE_SIZE bytes debugfs + * entries for the default case */ + if (p >= pos) { + if (total + len >= count - 1) + break; + format_register_str(codec, i, buf + total, len); + total += len; + } + p += len; } - - if (count >= PAGE_SIZE - 1) - break; - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE - 1) - break; } - /* Truncate count; min() would cause a warning */ - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; + total = min(total, count - 1); - return count; + return total; } + static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = container_of(dev, struct snd_soc_pcm_runtime, dev); - return soc_codec_reg_show(rtd->codec, buf); + return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); } static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); @@ -184,16 +212,28 @@ static int codec_reg_open_file(struct inode *inode, struct file *file) } static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { ssize_t ret; struct snd_soc_codec *codec = file->private_data; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *buf; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = soc_codec_reg_show(codec, buf); - if (ret >= 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + ret = soc_codec_reg_show(codec, buf, count, *ppos); + if (ret >= 0) { + if (copy_to_user(user_buf, buf, ret)) { + kfree(buf); + return -EFAULT; + } + *ppos += ret; + } + kfree(buf); return ret; } @@ -219,13 +259,15 @@ static ssize_t codec_reg_write_file(struct file *file, while (*start == ' ') start++; reg = simple_strtoul(start, &start, 16); - if ((reg >= codec->driver->reg_cache_size) || (reg % step)) - return -EINVAL; while (*start == ' ') start++; if (strict_strtoul(start, 16, &value)) return -EINVAL; - codec->driver->write(codec, reg, value); + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER); + + snd_soc_write(codec, reg, value); return buf_size; } @@ -238,14 +280,21 @@ static const struct file_operations codec_reg_fops = { static void soc_init_codec_debugfs(struct snd_soc_codec *codec) { - codec->debugfs_codec_root = debugfs_create_dir(codec->name , - debugfs_root); + struct dentry *debugfs_card_root = codec->card->debugfs_card_root; + + codec->debugfs_codec_root = debugfs_create_dir(codec->name, + debugfs_card_root); if (!codec->debugfs_codec_root) { printk(KERN_WARNING "ASoC: Failed to create codec debugfs directory\n"); return; } + debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + &codec->cache_sync); + debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, + &codec->cache_only); + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->debugfs_codec_root, codec, &codec_reg_fops); @@ -253,20 +302,13 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) printk(KERN_WARNING "ASoC: Failed to create codec register debugfs file\n"); - codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644, - codec->debugfs_codec_root, - &codec->pop_time); - if (!codec->debugfs_pop_time) - printk(KERN_WARNING - "Failed to create pop time debugfs file\n"); - - codec->debugfs_dapm = debugfs_create_dir("dapm", + codec->dapm.debugfs_dapm = debugfs_create_dir("dapm", codec->debugfs_codec_root); - if (!codec->debugfs_dapm) + if (!codec->dapm.debugfs_dapm) printk(KERN_WARNING "Failed to create DAPM debugfs directory\n"); - snd_soc_dapm_debugfs_init(codec); + snd_soc_dapm_debugfs_init(&codec->dapm); } static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) @@ -374,6 +416,29 @@ static const struct file_operations platform_list_fops = { .llseek = default_llseek,/* read accesses f_pos */ }; +static void soc_init_card_debugfs(struct snd_soc_card *card) +{ + card->debugfs_card_root = debugfs_create_dir(card->name, + snd_soc_debugfs_root); + if (!card->debugfs_card_root) { + dev_warn(card->dev, + "ASoC: Failed to create codec debugfs directory\n"); + return; + } + + card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644, + card->debugfs_card_root, + &card->pop_time); + if (!card->debugfs_pop_time) + dev_warn(card->dev, + "Failed to create pop time debugfs file\n"); +} + +static void soc_cleanup_card_debugfs(struct snd_soc_card *card) +{ + debugfs_remove_recursive(card->debugfs_card_root); +} + #else static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) @@ -383,6 +448,14 @@ static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec) static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) { } + +static inline void soc_init_card_debugfs(struct snd_soc_card *card) +{ +} + +static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) +{ +} #endif #ifdef CONFIG_SND_SOC_AC97_BUS @@ -425,20 +498,30 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - if (codec_dai->driver->symmetric_rates || cpu_dai->driver->symmetric_rates || - rtd->dai_link->symmetric_rates) { - dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", - rtd->rate); + if (!codec_dai->driver->symmetric_rates && + !cpu_dai->driver->symmetric_rates && + !rtd->dai_link->symmetric_rates) + return 0; - ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - rtd->rate, - rtd->rate); - if (ret < 0) { - dev_err(&rtd->dev, - "Unable to apply rate symmetry constraint: %d\n", ret); - return ret; - } + /* This can happen if multiple streams are starting simultaneously - + * the second can need to get its constraints before the first has + * picked a rate. Complain and allow the application to carry on. + */ + if (!rtd->rate) { + dev_warn(&rtd->dev, + "Not enforcing symmetric_rates due to race\n"); + return 0; + } + + dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); + + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + rtd->rate, rtd->rate); + if (ret < 0) { + dev_err(&rtd->dev, + "Unable to apply rate symmetry constraint: %d\n", ret); + return ret; } return 0; @@ -497,7 +580,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - /* Check that the codec and cpu DAI's are compatible */ + /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = max(codec_dai_drv->playback.rate_min, @@ -546,6 +629,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rates |= codec_dai_drv->capture.rates; } + ret = -EINVAL; snd_pcm_limit_hw_rates(runtime); if (!runtime->hw.rates) { printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", @@ -557,7 +641,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) codec_dai->name, cpu_dai->name); goto config_err; } - if (!runtime->hw.channels_min || !runtime->hw.channels_max) { + if (!runtime->hw.channels_min || !runtime->hw.channels_max || + runtime->hw.channels_min > runtime->hw.channels_max) { printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", codec_dai->name, cpu_dai->name); goto config_err; @@ -846,7 +931,7 @@ codec_err: } /* - * Free's resources allocated by hw_params, can be called multiple times + * Frees resources allocated by hw_params, can be called multiple times */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { @@ -870,7 +955,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) if (platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); - /* now free hw params for the DAI's */ + /* now free hw params for the DAIs */ if (codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); @@ -952,12 +1037,12 @@ static struct snd_pcm_ops soc_pcm_ops = { .pointer = soc_pcm_pointer, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ -static int soc_suspend(struct device *dev) +int snd_soc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); + struct snd_soc_codec *codec; int i; /* If the initialization of this soc device failed, there is no codec @@ -976,7 +1061,7 @@ static int soc_suspend(struct device *dev) /* we're going to block userspace touching us until resume completes */ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); - /* mute any active DAC's */ + /* mute any active DACs */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *dai = card->rtd[i].codec_dai; struct snd_soc_dai_driver *drv = dai->driver; @@ -997,7 +1082,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_pre) - card->suspend_pre(pdev, PMSG_SUSPEND); + card->suspend_pre(card); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; @@ -1016,8 +1101,8 @@ static int soc_suspend(struct device *dev) /* close any waiting streams and save state */ for (i = 0; i < card->num_rtd; i++) { - run_delayed_work(&card->rtd[i].delayed_work); - card->rtd[i].codec->suspend_bias_level = card->rtd[i].codec->bias_level; + flush_delayed_work_sync(&card->rtd[i].delayed_work); + card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level; } for (i = 0; i < card->num_rtd; i++) { @@ -1036,12 +1121,11 @@ static int soc_suspend(struct device *dev) } /* suspend all CODECs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_codec *codec = card->rtd[i].codec; + list_for_each_entry(codec, &card->codec_dev_list, card_list) { /* If there are paths active then the CODEC will be held with * bias _ON and should not be suspended. */ if (!codec->suspended && codec->driver->suspend) { - switch (codec->bias_level) { + switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_OFF: codec->driver->suspend(codec, PMSG_SUSPEND); @@ -1065,10 +1149,11 @@ static int soc_suspend(struct device *dev) } if (card->suspend_post) - card->suspend_post(pdev, PMSG_SUSPEND); + card->suspend_post(card); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_suspend); /* deferred resume work, so resume can complete before we finished * setting our codec back up, which can be very slow on I2C @@ -1077,7 +1162,7 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); - struct platform_device *pdev = to_platform_device(card->dev); + struct snd_soc_codec *codec; int i; /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, @@ -1090,7 +1175,7 @@ static void soc_resume_deferred(struct work_struct *work) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); if (card->resume_pre) - card->resume_pre(pdev); + card->resume_pre(card); /* resume AC97 DAIs */ for (i = 0; i < card->num_rtd; i++) { @@ -1103,14 +1188,13 @@ static void soc_resume_deferred(struct work_struct *work) cpu_dai->driver->resume(cpu_dai); } - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_codec *codec = card->rtd[i].codec; + list_for_each_entry(codec, &card->codec_dev_list, card_list) { /* If the CODEC was idle over suspend then it will have been * left with bias OFF or STANDBY and suspended so we must now * resume. Otherwise the suspend was suppressed. */ if (codec->driver->resume && codec->suspended) { - switch (codec->bias_level) { + switch (codec->dapm.bias_level) { case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_OFF: codec->driver->resume(codec); @@ -1166,7 +1250,7 @@ static void soc_resume_deferred(struct work_struct *work) } if (card->resume_post) - card->resume_post(pdev); + card->resume_post(card); dev_dbg(card->dev, "resume work completed\n"); @@ -1175,10 +1259,9 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct device *dev) +int snd_soc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; /* AC97 devices might have other drivers hanging off them so @@ -1200,9 +1283,10 @@ static int soc_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_resume); #else -#define soc_suspend NULL -#define soc_resume NULL +#define snd_soc_suspend NULL +#define snd_soc_resume NULL #endif static struct snd_soc_dai_ops null_dai_ops = { @@ -1249,9 +1333,6 @@ find_codec: if (!strcmp(codec->name, dai_link->codec_name)) { rtd->codec = codec; - if (!try_module_get(codec->dev->driver->owner)) - return -ENODEV; - /* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/ list_for_each_entry(codec_dai, &dai_list, list) { if (codec->dev == codec_dai->dev && @@ -1277,10 +1358,6 @@ find_platform: /* no, then find CPU DAI from registered DAIs*/ list_for_each_entry(platform, &platform_list, list) { if (!strcmp(platform->name, dai_link->platform_name)) { - - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - rtd->platform = platform; goto out; } @@ -1299,6 +1376,27 @@ out: return 1; } +static void soc_remove_codec(struct snd_soc_codec *codec) +{ + int err; + + if (codec->driver->remove) { + err = codec->driver->remove(codec); + if (err < 0) + dev_err(codec->dev, + "asoc: failed to remove %s: %d\n", + codec->name, err); + } + + /* Make sure all DAPM widgets are freed */ + snd_soc_dapm_free(&codec->dapm); + + soc_cleanup_codec_debugfs(codec); + codec->probed = 0; + list_del(&codec->card_list); + module_put(codec->dev->driver->owner); +} + static void soc_remove_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; @@ -1310,6 +1408,7 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) /* unregister the rtd device */ if (rtd->dev_registered) { device_remove_file(&rtd->dev, &dev_attr_pmdown_time); + device_remove_file(&rtd->dev, &dev_attr_codec_reg); device_unregister(&rtd->dev); rtd->dev_registered = 0; } @@ -1338,22 +1437,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } /* remove the CODEC */ - if (codec && codec->probed) { - if (codec->driver->remove) { - err = codec->driver->remove(codec); - if (err < 0) - printk(KERN_ERR "asoc: failed to remove %s\n", codec->name); - } - - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(codec); - - soc_cleanup_codec_debugfs(codec); - device_remove_file(&rtd->dev, &dev_attr_codec_reg); - codec->probed = 0; - list_del(&codec->card_list); - module_put(codec->dev->driver->owner); - } + if (codec && codec->probed) + soc_remove_codec(codec); /* remove the cpu_dai */ if (cpu_dai && cpu_dai->probed) { @@ -1368,8 +1453,138 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } } +static void soc_set_name_prefix(struct snd_soc_card *card, + struct snd_soc_codec *codec) +{ + int i; + + if (card->codec_conf == NULL) + return; + + for (i = 0; i < card->num_configs; i++) { + struct snd_soc_codec_conf *map = &card->codec_conf[i]; + if (map->dev_name && !strcmp(codec->name, map->dev_name)) { + codec->name_prefix = map->name_prefix; + break; + } + } +} + +static int soc_probe_codec(struct snd_soc_card *card, + struct snd_soc_codec *codec) +{ + int ret = 0; + const struct snd_soc_codec_driver *driver = codec->driver; + + codec->card = card; + codec->dapm.card = card; + soc_set_name_prefix(card, codec); + + if (!try_module_get(codec->dev->driver->owner)) + return -ENODEV; + + if (driver->probe) { + ret = driver->probe(codec); + if (ret < 0) { + dev_err(codec->dev, + "asoc: failed to probe CODEC %s: %d\n", + codec->name, ret); + goto err_probe; + } + } + + if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, + driver->num_dapm_widgets); + 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); + list_add(&codec->dapm.list, &card->dapm_list); + + return 0; + +err_probe: + module_put(codec->dev->driver->owner); + + return ret; +} + static void rtd_release(struct device *dev) {} +static int soc_post_component_init(struct snd_soc_card *card, + struct snd_soc_codec *codec, + int num, int dailess) +{ + struct snd_soc_dai_link *dai_link = NULL; + struct snd_soc_aux_dev *aux_dev = NULL; + struct snd_soc_pcm_runtime *rtd; + const char *temp, *name; + int ret = 0; + + if (!dailess) { + dai_link = &card->dai_link[num]; + rtd = &card->rtd[num]; + name = dai_link->name; + } else { + aux_dev = &card->aux_dev[num]; + rtd = &card->rtd_aux[num]; + name = aux_dev->name; + } + rtd->card = card; + + /* machine controls, routes and widgets are not prefixed */ + temp = codec->name_prefix; + codec->name_prefix = NULL; + + /* do machine specific initialization */ + if (!dailess && dai_link->init) + ret = dai_link->init(rtd); + else if (dailess && aux_dev->init) + ret = aux_dev->init(&codec->dapm); + if (ret < 0) { + dev_err(card->dev, "asoc: failed to init %s: %d\n", name, ret); + return ret; + } + codec->name_prefix = temp; + + /* Make sure all DAPM widgets are instantiated */ + snd_soc_dapm_new_widgets(&codec->dapm); + + /* register the rtd device */ + rtd->codec = codec; + rtd->dev.parent = card->dev; + rtd->dev.release = rtd_release; + rtd->dev.init_name = name; + ret = device_register(&rtd->dev); + if (ret < 0) { + dev_err(card->dev, + "asoc: failed to register runtime device: %d\n", ret); + return ret; + } + rtd->dev_registered = 1; + + /* add DAPM sysfs entries for this codec */ + ret = snd_soc_dapm_sys_add(&rtd->dev); + if (ret < 0) + dev_err(codec->dev, + "asoc: failed to add codec dapm sysfs entries: %d\n", + ret); + + /* add codec sysfs entries */ + ret = device_create_file(&rtd->dev, &dev_attr_codec_reg); + if (ret < 0) + dev_err(codec->dev, + "asoc: failed to add codec sysfs files: %d\n", ret); + + return 0; +} + static int soc_probe_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; @@ -1383,10 +1598,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* config components */ codec_dai->codec = codec; - codec->card = card; cpu_dai->platform = platform; - rtd->card = card; - rtd->dev.parent = card->dev; codec_dai->card = card; cpu_dai->card = card; @@ -1410,29 +1622,22 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* probe the CODEC */ if (!codec->probed) { - if (codec->driver->probe) { - ret = codec->driver->probe(codec); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to probe CODEC %s\n", - codec->name); - return ret; - } - } - - 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); + ret = soc_probe_codec(card, codec); + if (ret < 0) + return ret; } /* probe the platform */ if (!platform->probed) { + if (!try_module_get(platform->dev->driver->owner)) + return -ENODEV; + if (platform->driver->probe) { ret = platform->driver->probe(platform); if (ret < 0) { printk(KERN_ERR "asoc: failed to probe platform %s\n", platform->name); + module_put(platform->dev->driver->owner); return ret; } } @@ -1460,43 +1665,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); - /* now that all clients have probed, initialise the DAI link */ - if (dai_link->init) { - ret = dai_link->init(rtd); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to init %s\n", dai_link->stream_name); - return ret; - } - } - - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(codec); - snd_soc_dapm_sync(codec); - - /* register the rtd device */ - rtd->dev.release = rtd_release; - rtd->dev.init_name = dai_link->name; - ret = device_register(&rtd->dev); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to register DAI runtime device %d\n", ret); + ret = soc_post_component_init(card, codec, num, 0); + if (ret) return ret; - } - rtd->dev_registered = 1; ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time); if (ret < 0) printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n"); - /* add DAPM sysfs entries for this codec */ - ret = snd_soc_dapm_sys_add(&rtd->dev); - if (ret < 0) - printk(KERN_WARNING "asoc: failed to add codec dapm sysfs entries\n"); - - /* add codec sysfs entries */ - ret = device_create_file(&rtd->dev, &dev_attr_codec_reg); - if (ret < 0) - printk(KERN_WARNING "asoc: failed to add codec sysfs files\n"); - /* create the pcm */ ret = soc_new_pcm(rtd, num); if (ret < 0) { @@ -1551,9 +1727,81 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec) } #endif +static int soc_probe_aux_dev(struct snd_soc_card *card, int num) +{ + struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; + struct snd_soc_codec *codec; + int ret = -ENODEV; + + /* find CODEC from registered CODECs*/ + list_for_each_entry(codec, &codec_list, list) { + if (!strcmp(codec->name, aux_dev->codec_name)) { + if (codec->probed) { + dev_err(codec->dev, + "asoc: codec already probed"); + ret = -EBUSY; + goto out; + } + goto found; + } + } + /* codec not found */ + dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name); + goto out; + +found: + ret = soc_probe_codec(card, codec); + if (ret < 0) + return ret; + + ret = soc_post_component_init(card, codec, num, 1); + +out: + return ret; +} + +static void soc_remove_aux_dev(struct snd_soc_card *card, int num) +{ + struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; + struct snd_soc_codec *codec = rtd->codec; + + /* unregister the rtd device */ + if (rtd->dev_registered) { + device_remove_file(&rtd->dev, &dev_attr_codec_reg); + device_unregister(&rtd->dev); + rtd->dev_registered = 0; + } + + if (codec && codec->probed) + soc_remove_codec(codec); +} + +static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, + enum snd_soc_compress_type compress_type) +{ + int ret; + + if (codec->cache_init) + return 0; + + /* override the compress_type if necessary */ + if (compress_type && codec->compress_type != compress_type) + codec->compress_type = compress_type; + ret = snd_soc_cache_init(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache compression type: %d\n", + ret); + return ret; + } + codec->cache_init = 1; + return 0; +} + static void snd_soc_instantiate_card(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); + struct snd_soc_codec *codec; + struct snd_soc_codec_conf *codec_conf; + enum snd_soc_compress_type compress_type; int ret, i; mutex_lock(&card->mutex); @@ -1573,6 +1821,29 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) return; } + /* initialize the register cache for each available codec */ + list_for_each_entry(codec, &codec_list, list) { + if (codec->cache_init) + continue; + /* by default we don't override the compress_type */ + compress_type = 0; + /* check to see if we need to override the compress_type */ + for (i = 0; i < card->num_configs; ++i) { + codec_conf = &card->codec_conf[i]; + if (!strcmp(codec->name, codec_conf->dev_name)) { + compress_type = codec_conf->compress_type; + if (compress_type && compress_type + != codec->compress_type) + break; + } + } + ret = snd_soc_init_codec_cache(codec, compress_type); + if (ret < 0) { + mutex_unlock(&card->mutex); + return; + } + } + /* card bind complete so register a sound card */ ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); @@ -1584,14 +1855,19 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } card->snd_card->dev = card->dev; -#ifdef CONFIG_PM + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + +#ifdef CONFIG_PM_SLEEP /* deferred resume work */ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); #endif /* initialise the sound card only once */ if (card->probe) { - ret = card->probe(pdev); + ret = card->probe(card); if (ret < 0) goto card_probe_error; } @@ -1605,39 +1881,80 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + for (i = 0; i < card->num_aux_devs; i++) { + ret = soc_probe_aux_dev(card, i); + if (ret < 0) { + pr_err("asoc: failed to add auxiliary devices %s: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + + if (card->dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, + card->num_dapm_widgets); + 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); + if (card->late_probe) { + ret = card->late_probe(card); + if (ret < 0) { + dev_err(card->dev, "%s late_probe() failed: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + ret = snd_card_register(card->snd_card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name); - goto probe_dai_err; + goto probe_aux_dev_err; } #ifdef CONFIG_SND_SOC_AC97_BUS /* register any AC97 codecs */ for (i = 0; i < card->num_rtd; i++) { - ret = soc_register_ac97_dai_link(&card->rtd[i]); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name); - goto probe_dai_err; - } + ret = soc_register_ac97_dai_link(&card->rtd[i]); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name); + while (--i >= 0) + soc_unregister_ac97_dai_link(card->rtd[i].codec); + goto probe_aux_dev_err; } + } #endif card->instantiated = 1; mutex_unlock(&card->mutex); return; +probe_aux_dev_err: + for (i = 0; i < card->num_aux_devs; i++) + soc_remove_aux_dev(card, i); + probe_dai_err: for (i = 0; i < card->num_links; i++) soc_remove_dai_link(card, i); card_probe_error: if (card->remove) - card->remove(pdev); + card->remove(card); snd_card_free(card->snd_card); @@ -1661,11 +1978,15 @@ static int soc_probe(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); int ret = 0; + /* + * no card, so machine driver should be registering card + * we should not be here in that case so ret error + */ + if (!card) + return -EINVAL; + /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; - INIT_LIST_HEAD(&card->dai_dev_list); - INIT_LIST_HEAD(&card->codec_dev_list); - INIT_LIST_HEAD(&card->platform_dev_list); ret = snd_soc_register_card(card); if (ret != 0) { @@ -1676,39 +1997,48 @@ static int soc_probe(struct platform_device *pdev) return 0; } -/* removes a socdev */ -static int soc_remove(struct platform_device *pdev) +static int soc_cleanup_card_resources(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(pdev); int i; - if (card->instantiated) { + /* make sure any delayed work runs */ + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + flush_delayed_work_sync(&rtd->delayed_work); + } - /* make sure any delayed work runs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - run_delayed_work(&rtd->delayed_work); - } + /* remove auxiliary devices */ + for (i = 0; i < card->num_aux_devs; i++) + 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); + /* remove and free each DAI */ + for (i = 0; i < card->num_rtd; i++) + soc_remove_dai_link(card, i); - /* remove the card */ - if (card->remove) - card->remove(pdev); + soc_cleanup_card_debugfs(card); - kfree(card->rtd); - snd_card_free(card->snd_card); - } - snd_soc_unregister_card(card); + /* remove the card */ + if (card->remove) + card->remove(card); + + kfree(card->rtd); + snd_card_free(card->snd_card); return 0; + } -static int soc_poweroff(struct device *dev) +/* removes a socdev */ +static int soc_remove(struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(dev); struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +int snd_soc_poweroff(struct device *dev) +{ + struct snd_soc_card *card = dev_get_drvdata(dev); int i; if (!card->instantiated) @@ -1718,26 +2048,28 @@ static int soc_poweroff(struct device *dev) * now, we're shutting down so no imminent restart. */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - run_delayed_work(&rtd->delayed_work); + flush_delayed_work_sync(&rtd->delayed_work); } snd_soc_dapm_shutdown(card); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_poweroff); -static const struct dev_pm_ops soc_pm_ops = { - .suspend = soc_suspend, - .resume = soc_resume, - .poweroff = soc_poweroff, +const struct dev_pm_ops snd_soc_pm_ops = { + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, + .poweroff = snd_soc_poweroff, }; +EXPORT_SYMBOL_GPL(snd_soc_pm_ops); /* ASoC platform driver */ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, - .pm = &soc_pm_ops, + .pm = &snd_soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, @@ -1807,10 +2139,11 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * * Boolean function indiciating if a CODEC register is volatile. */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) { - if (codec->driver->volatile_register) - return codec->driver->volatile_register(reg); + if (codec->volatile_register) + return codec->volatile_register(codec, reg); else return 0; } @@ -1877,6 +2210,27 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); +unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int ret; + + ret = codec->read(codec, reg); + dev_dbg(codec->dev, "read %x => %x\n", reg, ret); + trace_snd_soc_reg_read(codec, reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_read); + +unsigned int snd_soc_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val) +{ + dev_dbg(codec->dev, "write %x = %x\n", reg, val); + trace_snd_soc_reg_write(codec, reg, val); + return codec->write(codec, reg, val); +} +EXPORT_SYMBOL_GPL(snd_soc_write); + /** * snd_soc_update_bits - update codec register bits * @codec: audio codec @@ -1886,19 +2240,27 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec); * * Writes new register value. * - * Returns 1 for change else 0. + * Returns 1 for change, 0 for no change, or negative error code. */ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, unsigned int mask, unsigned int value) { int change; unsigned int old, new; + int ret; - old = snd_soc_read(codec, reg); + ret = snd_soc_read(codec, reg); + if (ret < 0) + return ret; + + old = ret; new = (old & ~mask) | value; change = old != new; - if (change) - snd_soc_write(codec, reg, new); + if (change) { + ret = snd_soc_write(codec, reg, new); + if (ret < 0) + return ret; + } return change; } @@ -1983,22 +2345,45 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); * @_template: control template * @data: control private data * @long_name: control long name + * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name) + void *data, char *long_name, + const char *prefix) { struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + char *name = NULL; + int name_len; memcpy(&template, _template, sizeof(template)); - if (long_name) - template.name = long_name; template.index = 0; - return snd_ctl_new1(&template, data); + if (!long_name) + long_name = template.name; + + if (prefix) { + name_len = strlen(long_name) + strlen(prefix) + 2; + name = kmalloc(name_len, GFP_ATOMIC); + if (!name) + return NULL; + + snprintf(name, name_len, "%s %s", prefix, long_name); + + template.name = name; + } else { + template.name = long_name; + } + + kcontrol = snd_ctl_new1(&template, data); + + kfree(name); + + return kcontrol; } EXPORT_SYMBOL_GPL(snd_soc_cnew); @@ -2021,7 +2406,9 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; - err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL)); + err = snd_ctl_add(card, snd_soc_cnew(control, codec, + control->name, + codec->name_prefix)); if (err < 0) { dev_err(codec->dev, "%s: Failed to add %s: %d\n", codec->name, control->name, err); @@ -2705,12 +3092,34 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, { if (dai->driver && dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + else if (dai->codec && dai->codec->driver->set_sysclk) + return dai->codec->driver->set_sysclk(dai->codec, clk_id, + freq, dir); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); /** + * snd_soc_codec_set_sysclk - configure CODEC system or master clock. + * @codec: CODEC + * @clk_id: DAI specific clock ID + * @freq: new clock frequency in Hz + * @dir: new clock direction - input/output. + * + * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. + */ +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + if (codec->driver->set_sysclk) + return codec->driver->set_sysclk(codec, clk_id, freq, dir); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); + +/** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI * @div_id: DAI specific clock divider ID @@ -2746,11 +3155,35 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, if (dai->driver && dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); + else if (dai->codec && dai->codec->driver->set_pll) + return dai->codec->driver->set_pll(dai->codec, pll_id, source, + freq_in, freq_out); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); +/* + * snd_soc_codec_set_pll - configure codec PLL. + * @codec: CODEC + * @pll_id: DAI specific PLL ID + * @source: DAI specific source for the PLL + * @freq_in: PLL input clock frequency in Hz + * @freq_out: requested PLL output clock frequency in Hz + * + * Configures and enables PLL to generate output clock based on input clock. + */ +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + if (codec->driver->set_pll) + return codec->driver->set_pll(codec, pll_id, source, + freq_in, freq_out); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI @@ -2850,21 +3283,24 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); * * @card: Card to register * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_register_card(struct snd_soc_card *card) +int snd_soc_register_card(struct snd_soc_card *card) { int i; if (!card->name || !card->dev) return -EINVAL; - card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links, - GFP_KERNEL); + snd_soc_initialize_card_lists(card); + + soc_init_card_debugfs(card); + + card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * + (card->num_links + card->num_aux_devs), + GFP_KERNEL); if (card->rtd == NULL) return -ENOMEM; + card->rtd_aux = &card->rtd[card->num_links]; for (i = 0; i < card->num_links; i++) card->rtd[i].dai_link = &card->dai_link[i]; @@ -2882,18 +3318,18 @@ static int snd_soc_register_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_register_card); /** * snd_soc_unregister_card - Unregister a card with the ASoC core * * @card: Card to unregister * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_unregister_card(struct snd_soc_card *card) +int snd_soc_unregister_card(struct snd_soc_card *card) { + if (card->instantiated) + soc_cleanup_card_resources(card); mutex_lock(&client_mutex); list_del(&card->list); mutex_unlock(&client_mutex); @@ -2901,12 +3337,13 @@ static int snd_soc_unregister_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_unregister_card); /* * Simplify DAI link configuration by removing ".-1" from device names * and sanitizing names. */ -static inline char *fmt_single_name(struct device *dev, int *id) +static char *fmt_single_name(struct device *dev, int *id) { char *found, name[NAME_SIZE]; int id1, id2; @@ -2914,7 +3351,7 @@ static inline char *fmt_single_name(struct device *dev, int *id) if (dev_name(dev) == NULL) return NULL; - strncpy(name, dev_name(dev), NAME_SIZE); + strlcpy(name, dev_name(dev), NAME_SIZE); /* are we a "%s.%d" name (platform and SPI components) */ found = strstr(name, dev->driver->name); @@ -2937,7 +3374,7 @@ static inline char *fmt_single_name(struct device *dev, int *id) /* sanitize component name for DAI link creation */ snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name); - strncpy(name, tmp, NAME_SIZE); + strlcpy(name, tmp, NAME_SIZE); } else *id = 0; } @@ -3072,7 +3509,9 @@ int snd_soc_register_dais(struct device *dev, pr_debug("Registered DAI '%s'\n", dai->name); } + mutex_lock(&client_mutex); snd_soc_instantiate_cards(); + mutex_unlock(&client_mutex); return 0; err: @@ -3200,9 +3639,11 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) * @codec: codec to register */ int snd_soc_register_codec(struct device *dev, - struct snd_soc_codec_driver *codec_drv, - struct snd_soc_dai_driver *dai_drv, int num_dai) + const struct snd_soc_codec_driver *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai) { + size_t reg_size; struct snd_soc_codec *codec; int ret, i; @@ -3219,30 +3660,50 @@ int snd_soc_register_codec(struct device *dev, return -ENOMEM; } + if (codec_drv->compress_type) + codec->compress_type = codec_drv->compress_type; + else + codec->compress_type = SND_SOC_FLAT_COMPRESSION; + + codec->write = codec_drv->write; + codec->read = codec_drv->read; + codec->volatile_register = codec_drv->volatile_register; + codec->readable_register = codec_drv->readable_register; + codec->dapm.bias_level = SND_SOC_BIAS_OFF; + codec->dapm.dev = dev; + codec->dapm.codec = codec; + codec->dapm.seq_notifier = codec_drv->seq_notifier; + codec->dev = dev; + codec->driver = codec_drv; + codec->num_dai = num_dai; + mutex_init(&codec->mutex); + /* allocate CODEC register cache */ if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { - - if (codec_drv->reg_cache_default) - codec->reg_cache = kmemdup(codec_drv->reg_cache_default, - codec_drv->reg_cache_size * codec_drv->reg_word_size, GFP_KERNEL); - else - codec->reg_cache = kzalloc(codec_drv->reg_cache_size * - codec_drv->reg_word_size, GFP_KERNEL); - - if (codec->reg_cache == NULL) { - kfree(codec->name); - kfree(codec); - return -ENOMEM; + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + codec->reg_size = reg_size; + /* it is necessary to make a copy of the default register cache + * because in the case of using a compression type that requires + * the default register cache to be marked as __devinitconst the + * kernel might have freed the array by the time we initialize + * the cache. + */ + if (codec_drv->reg_cache_default) { + codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, + reg_size, GFP_KERNEL); + if (!codec->reg_def_copy) { + ret = -ENOMEM; + goto fail; + } } } - codec->dev = dev; - codec->driver = codec_drv; - codec->bias_level = SND_SOC_BIAS_OFF; - codec->num_dai = num_dai; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); + if (codec_drv->reg_access_size && codec_drv->reg_access_default) { + if (!codec->volatile_register) + codec->volatile_register = snd_soc_default_volatile_register; + if (!codec->readable_register) + codec->readable_register = snd_soc_default_readable_register; + } for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); @@ -3253,7 +3714,7 @@ int snd_soc_register_codec(struct device *dev, if (num_dai) { ret = snd_soc_register_dais(dev, dai_drv, num_dai); if (ret < 0) - goto error; + goto fail; } mutex_lock(&client_mutex); @@ -3264,9 +3725,9 @@ int snd_soc_register_codec(struct device *dev, pr_debug("Registered codec '%s'\n", codec->name); return 0; -error: - if (codec->reg_cache) - kfree(codec->reg_cache); +fail: + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; kfree(codec->name); kfree(codec); return ret; @@ -3300,8 +3761,8 @@ found: pr_debug("Unregistered codec '%s'\n", codec->name); - if (codec->reg_cache) - kfree(codec->reg_cache); + snd_soc_cache_exit(codec); + kfree(codec->reg_def_copy); kfree(codec->name); kfree(codec); } @@ -3310,22 +3771,22 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS - debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(debugfs_root) || !debugfs_root) { + snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { printk(KERN_WARNING "ASoC: Failed to create debugfs directory\n"); - debugfs_root = NULL; + snd_soc_debugfs_root = NULL; } - if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL, + if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, debugfs_root, NULL, + if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL, + if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); #endif @@ -3337,7 +3798,7 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(debugfs_root); + debugfs_remove_recursive(snd_soc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 75ed6491222d..81c4052c127c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/async.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/bitops.h> @@ -42,9 +43,11 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> -#include <sound/soc-dapm.h> +#include <sound/soc.h> #include <sound/initval.h> +#include <trace/events/asoc.h> + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -54,12 +57,14 @@ static int dapm_up_seq[] = { [snd_soc_dapm_aif_out] = 3, [snd_soc_dapm_mic] = 4, [snd_soc_dapm_mux] = 5, + [snd_soc_dapm_virt_mux] = 5, [snd_soc_dapm_value_mux] = 5, [snd_soc_dapm_dac] = 6, [snd_soc_dapm_mixer] = 7, [snd_soc_dapm_mixer_named_ctl] = 7, [snd_soc_dapm_pga] = 8, [snd_soc_dapm_adc] = 9, + [snd_soc_dapm_out_drv] = 10, [snd_soc_dapm_hp] = 10, [snd_soc_dapm_spk] = 10, [snd_soc_dapm_post] = 11, @@ -70,6 +75,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_adc] = 1, [snd_soc_dapm_hp] = 2, [snd_soc_dapm_spk] = 2, + [snd_soc_dapm_out_drv] = 2, [snd_soc_dapm_pga] = 4, [snd_soc_dapm_mixer_named_ctl] = 5, [snd_soc_dapm_mixer] = 5, @@ -77,6 +83,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_mic] = 7, [snd_soc_dapm_micbias] = 8, [snd_soc_dapm_mux] = 9, + [snd_soc_dapm_virt_mux] = 9, [snd_soc_dapm_value_mux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, @@ -90,17 +97,24 @@ static void pop_wait(u32 pop_time) schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time)); } -static void pop_dbg(u32 pop_time, const char *fmt, ...) +static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) { va_list args; + char *buf; - va_start(args, fmt); + if (!pop_time) + return; - if (pop_time) { - vprintk(fmt, args); - } + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return; + va_start(args, fmt); + vsnprintf(buf, PAGE_SIZE, fmt, args); + dev_info(dev, "%s", buf); va_end(args); + + kfree(buf); } /* create a new dapm widget */ @@ -112,44 +126,53 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( /** * snd_soc_dapm_set_bias_level - set the bias level for the system - * @card: audio device + * @dapm: DAPM context * @level: level to configure * * Configure the bias (power) levels for the SoC audio device. * * Returns 0 for success else error. */ -static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card, - struct snd_soc_codec *codec, enum snd_soc_bias_level level) +static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) { + struct snd_soc_card *card = dapm->card; int ret = 0; switch (level) { case SND_SOC_BIAS_ON: - dev_dbg(codec->dev, "Setting full bias\n"); + dev_dbg(dapm->dev, "Setting full bias\n"); break; case SND_SOC_BIAS_PREPARE: - dev_dbg(codec->dev, "Setting bias prepare\n"); + dev_dbg(dapm->dev, "Setting bias prepare\n"); break; case SND_SOC_BIAS_STANDBY: - dev_dbg(codec->dev, "Setting standby bias\n"); + dev_dbg(dapm->dev, "Setting standby bias\n"); break; case SND_SOC_BIAS_OFF: - dev_dbg(codec->dev, "Setting bias off\n"); + dev_dbg(dapm->dev, "Setting bias off\n"); break; default: - dev_err(codec->dev, "Setting invalid bias %d\n", level); + dev_err(dapm->dev, "Setting invalid bias %d\n", level); return -EINVAL; } + trace_snd_soc_bias_level_start(card, level); + if (card && card->set_bias_level) ret = card->set_bias_level(card, level); if (ret == 0) { - if (codec->driver->set_bias_level) - ret = codec->driver->set_bias_level(codec, level); + if (dapm->codec && dapm->codec->driver->set_bias_level) + ret = dapm->codec->driver->set_bias_level(dapm->codec, level); else - codec->bias_level = level; + dapm->bias_level = level; } + if (ret == 0) { + if (card && card->set_bias_level_post) + ret = card->set_bias_level_post(card, level); + } + + trace_snd_soc_bias_level_done(card, level); return ret; } @@ -196,6 +219,20 @@ 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; + + p->connect = 0; + /* since a virtual mux has no backing registers to + * decide which path to connect, it will try to match + * with the first enumeration. This is to ensure + * that the default mux choice (the first) will be + * correctly powered up during initialization. + */ + if (!strcmp(p->name, e->texts[0])) + p->connect = 1; + } + break; case snd_soc_dapm_value_mux: { struct soc_enum *e = (struct soc_enum *) w->kcontrols[i].private_value; @@ -217,6 +254,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, break; /* does not effect routing - always connected */ case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: case snd_soc_dapm_output: case snd_soc_dapm_adc: case snd_soc_dapm_input: @@ -241,7 +279,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } /* connect mux widget to its interconnecting audio paths */ -static int dapm_connect_mux(struct snd_soc_codec *codec, +static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name, const struct snd_kcontrol_new *kcontrol) @@ -251,7 +289,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, for (i = 0; i < e->max; i++) { if (!(strcmp(control_name, e->texts[i]))) { - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = (char*)e->texts[i]; @@ -264,7 +302,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec, } /* connect mixer widget to its interconnecting audio paths */ -static int dapm_connect_mixer(struct snd_soc_codec *codec, +static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, struct snd_soc_dapm_path *path, const char *control_name) { @@ -273,7 +311,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec, /* search for mixer kcontrol */ for (i = 0; i < dest->num_kcontrols; i++) { if (!strcmp(control_name, dest->kcontrols[i].name)) { - list_add(&path->list, &codec->dapm_paths); + 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; @@ -290,6 +328,8 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget) 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 || @@ -309,24 +349,37 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget) change = old != new; if (change) { - pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n", + pop_dbg(dapm->dev, card->pop_time, + "pop test %s : %s in %d ms\n", widget->name, widget->power ? "on" : "off", - codec->pop_time); - pop_wait(codec->pop_time); + card->pop_time); + pop_wait(card->pop_time); snd_soc_write(codec, widget->reg, new); } - pr_debug("reg %x old %x new %x change %d\n", widget->reg, - old, new, change); + dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg, + old, new, change); return change; } /* create new dapm mixer control */ -static int dapm_new_mixer(struct snd_soc_codec *codec, +static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) { int i, ret = 0; - size_t name_len; + size_t name_len, prefix_len; struct snd_soc_dapm_path *path; + struct snd_card *card = dapm->card->snd_card; + const char *prefix; + + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + if (prefix) + prefix_len = strlen(prefix) + 1; + else + prefix_len = 0; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -355,8 +408,15 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, switch (w->id) { default: + /* 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. + */ snprintf(path->long_name, name_len, "%s %s", - w->name, w->kcontrols[i].name); + w->name + prefix_len, + w->kcontrols[i].name); break; case snd_soc_dapm_mixer_named_ctl: snprintf(path->long_name, name_len, "%s", @@ -367,12 +427,12 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, path->long_name[name_len - 1] = '\0'; path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, - path->long_name); - ret = snd_ctl_add(codec->card->snd_card, path->kcontrol); + path->long_name, prefix); + ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { - printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n", - path->long_name, - ret); + dev_err(dapm->dev, + "asoc: failed to add dapm kcontrol %s: %d\n", + path->long_name, ret); kfree(path->long_name); path->long_name = NULL; return ret; @@ -383,20 +443,39 @@ static int dapm_new_mixer(struct snd_soc_codec *codec, } /* create new dapm mux control */ -static int dapm_new_mux(struct snd_soc_codec *codec, +static int dapm_new_mux(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *path = NULL; struct snd_kcontrol *kcontrol; + struct snd_card *card = dapm->card->snd_card; + const char *prefix; + size_t prefix_len; int ret = 0; if (!w->num_kcontrols) { - printk(KERN_ERR "asoc: mux %s has no controls\n", w->name); + dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name); return -EINVAL; } - kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); - ret = snd_ctl_add(codec->card->snd_card, kcontrol); + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + 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); + if (ret < 0) goto err; @@ -406,26 +485,27 @@ static int dapm_new_mux(struct snd_soc_codec *codec, return ret; err: - printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name); + dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name); return ret; } /* create new dapm volume control */ -static int dapm_new_pga(struct snd_soc_codec *codec, +static int dapm_new_pga(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) { if (w->num_kcontrols) - pr_err("asoc: PGA controls not supported: '%s'\n", w->name); + dev_err(w->dapm->dev, + "asoc: PGA controls not supported: '%s'\n", w->name); return 0; } /* reset 'walked' bit for each dapm path */ -static inline void dapm_clear_walk(struct snd_soc_codec *codec) +static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_path *p; - list_for_each_entry(p, &codec->dapm_paths, list) + list_for_each_entry(p, &dapm->card->paths, list) p->walked = 0; } @@ -435,13 +515,14 @@ static inline void dapm_clear_walk(struct snd_soc_codec *codec) */ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) { - int level = snd_power_get_state(widget->codec->card->snd_card); + int level = snd_power_get_state(widget->dapm->card->snd_card); switch (level) { case SNDRV_CTL_POWER_D3hot: case SNDRV_CTL_POWER_D3cold: if (widget->ignore_suspend) - pr_debug("%s ignoring suspend\n", widget->name); + dev_dbg(widget->dapm->dev, "%s ignoring suspend\n", + widget->name); return widget->ignore_suspend; default: return 1; @@ -572,7 +653,7 @@ static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w) /* call any power change event handlers */ if (w->event) - pr_debug("power %s event for %s flags %x\n", + dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n", w->power ? "on" : "off", w->name, w->event_flags); @@ -621,9 +702,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) int in, out; in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); return out != 0 && in != 0; } @@ -634,7 +715,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) if (w->active) { in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); return in != 0; } else { return dapm_generic_check_power(w); @@ -648,7 +729,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) if (w->active) { out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); return out != 0; } else { return dapm_generic_check_power(w); @@ -667,28 +748,49 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) !path->connected(path->source, path->sink)) continue; - if (path->sink && path->sink->power_check && + if (!path->sink) + continue; + + if (path->sink->force) { + power = 1; + break; + } + + if (path->sink->power_check && path->sink->power_check(path->sink)) { power = 1; break; } } - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); return power; } static int dapm_seq_compare(struct snd_soc_dapm_widget *a, struct snd_soc_dapm_widget *b, - int sort[]) + bool power_up) { + int *sort; + + if (power_up) + sort = dapm_up_seq; + else + sort = dapm_down_seq; + if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; + if (a->subseq != b->subseq) { + if (power_up) + return a->subseq - b->subseq; + else + return b->subseq - a->subseq; + } if (a->reg != b->reg) return a->reg - b->reg; - if (a->codec != b->codec) - return (unsigned long)a->codec - (unsigned long)b->codec; + if (a->dapm != b->dapm) + return (unsigned long)a->dapm - (unsigned long)b->dapm; return 0; } @@ -696,12 +798,12 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, /* Insert a widget in order into a DAPM power sequence. */ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, struct list_head *list, - int sort[]) + bool power_up) { struct snd_soc_dapm_widget *w; list_for_each_entry(w, list, power_list) - if (dapm_seq_compare(new_widget, w, sort) < 0) { + if (dapm_seq_compare(new_widget, w, power_up) < 0) { list_add_tail(&new_widget->power_list, &w->power_list); return; } @@ -709,12 +811,57 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, list_add_tail(&new_widget->power_list, list); } +static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *w, int event) +{ + struct snd_soc_card *card = dapm->card; + const char *ev_name; + int power, ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ev_name = "PRE_PMU"; + power = 1; + break; + case SND_SOC_DAPM_POST_PMU: + ev_name = "POST_PMU"; + power = 1; + break; + case SND_SOC_DAPM_PRE_PMD: + ev_name = "PRE_PMD"; + power = 0; + break; + case SND_SOC_DAPM_POST_PMD: + ev_name = "POST_PMD"; + power = 0; + break; + default: + BUG(); + return; + } + + if (w->power != power) + return; + + if (w->event && (w->event_flags & event)) { + pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n", + w->name, ev_name); + trace_snd_soc_dapm_widget_event_start(w, event); + ret = w->event(w, NULL, event); + trace_snd_soc_dapm_widget_event_done(w, event); + if (ret < 0) + pr_err("%s: %s event failed: %d\n", + ev_name, w->name, ret); + } +} + /* Apply the coalesced changes from a DAPM sequence */ -static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, +static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, struct list_head *pending) { + struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; - int reg, power, ret; + int reg, power; unsigned int value = 0; unsigned int mask = 0; unsigned int cur_mask; @@ -735,64 +882,26 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, if (power) value |= cur_mask; - pop_dbg(codec->pop_time, + pop_dbg(dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", w->name, reg, value, mask); - /* power up pre event */ - if (w->power && w->event && - (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { - pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n", - w->name); - ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); - if (ret < 0) - pr_err("%s: pre event failed: %d\n", - w->name, ret); - } - - /* power down pre event */ - if (!w->power && w->event && - (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { - pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n", - w->name); - ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); - if (ret < 0) - pr_err("%s: pre event failed: %d\n", - w->name, ret); - } + /* Check for events */ + dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU); + dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD); } if (reg >= 0) { - pop_dbg(codec->pop_time, + pop_dbg(dapm->dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", - value, mask, reg, codec->pop_time); - pop_wait(codec->pop_time); - snd_soc_update_bits(codec, reg, mask, value); + value, mask, reg, card->pop_time); + pop_wait(card->pop_time); + snd_soc_update_bits(dapm->codec, reg, mask, value); } list_for_each_entry(w, pending, power_list) { - /* power up post event */ - if (w->power && w->event && - (w->event_flags & SND_SOC_DAPM_POST_PMU)) { - pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n", - w->name); - ret = w->event(w, - NULL, SND_SOC_DAPM_POST_PMU); - if (ret < 0) - pr_err("%s: post event failed: %d\n", - w->name, ret); - } - - /* power down post event */ - if (!w->power && w->event && - (w->event_flags & SND_SOC_DAPM_POST_PMD)) { - pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n", - w->name); - ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); - if (ret < 0) - pr_err("%s: post event failed: %d\n", - w->name, ret); - } + dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU); + dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD); } } @@ -804,26 +913,45 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, * Currently anything that requires more than a single write is not * handled. */ -static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, - int event, int sort[]) +static void dapm_seq_run(struct snd_soc_dapm_context *dapm, + struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); int cur_sort = -1; + int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; - int ret; + struct snd_soc_dapm_context *cur_dapm = NULL; + int ret, i; + int *sort; + + if (power_up) + sort = dapm_up_seq; + else + sort = dapm_down_seq; list_for_each_entry_safe(w, n, list, power_list) { ret = 0; /* Do we need to apply any queued changes? */ - if (sort[w->id] != cur_sort || w->reg != cur_reg) { + if (sort[w->id] != cur_sort || w->reg != cur_reg || + w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) - dapm_seq_run_coalesced(codec, &pending); + dapm_seq_run_coalesced(cur_dapm, &pending); + + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i, + cur_subseq); + } INIT_LIST_HEAD(&pending); cur_sort = -1; + cur_subseq = -1; cur_reg = SND_SOC_NOPM; + cur_dapm = NULL; } switch (w->id) { @@ -866,18 +994,117 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, default: /* Queue it up for application */ cur_sort = sort[w->id]; + cur_subseq = w->subseq; cur_reg = w->reg; + cur_dapm = w->dapm; list_move(&w->power_list, &pending); break; } if (ret < 0) - pr_err("Failed to apply widget power: %d\n", - ret); + dev_err(w->dapm->dev, + "Failed to apply widget power: %d\n", ret); } if (!list_empty(&pending)) - dapm_seq_run_coalesced(codec, &pending); + dapm_seq_run_coalesced(cur_dapm, &pending); + + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i, cur_subseq); + } +} + +static void dapm_widget_update(struct snd_soc_dapm_context *dapm) +{ + struct snd_soc_dapm_update *update = dapm->update; + struct snd_soc_dapm_widget *w; + int ret; + + if (!update) + return; + + w = update->widget; + + if (w->event && + (w->event_flags & SND_SOC_DAPM_PRE_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret != 0) + pr_err("%s DAPM pre-event failed: %d\n", + w->name, ret); + } + + ret = snd_soc_update_bits(w->codec, update->reg, update->mask, + update->val); + if (ret < 0) + pr_err("%s DAPM update failed: %d\n", w->name, ret); + + if (w->event && + (w->event_flags & SND_SOC_DAPM_POST_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); + if (ret != 0) + pr_err("%s DAPM post-event failed: %d\n", + w->name, ret); + } +} + +/* Async callback run prior to DAPM sequences - brings to _PREPARE if + * they're changing state. + */ +static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) +{ + struct snd_soc_dapm_context *d = data; + int ret; + + if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to turn on bias: %d\n", ret); + } + + /* If we're changing to all on or all off then prepare */ + if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); + if (ret != 0) + dev_err(d->dev, + "Failed to prepare bias: %d\n", ret); + } +} + +/* Async callback run prior to DAPM sequences - brings to their final + * state. + */ +static void dapm_post_sequence_async(void *data, async_cookie_t cookie) +{ + struct snd_soc_dapm_context *d = data; + int ret; + + /* If we just powered the last thing off drop to standby bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, "Failed to apply standby bias: %d\n", + ret); + } + + /* If we're in standby and can support bias off then do that */ + if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); + if (ret != 0) + dev_err(d->dev, "Failed to turn off bias: %d\n", ret); + } + + /* If we just powered up then move to active bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); + if (ret != 0) + dev_err(d->dev, "Failed to apply active bias: %d\n", + ret); + } } /* @@ -889,26 +1116,32 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ -static int dapm_power_widgets(struct snd_soc_codec *codec, int event) +static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) { - struct snd_soc_card *card = codec->card; + struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); - int ret = 0; + LIST_HEAD(async_domain); int power; - int sys_power = 0; + + trace_snd_soc_dapm_start(card); + + list_for_each_entry(d, &card->dapm_list, list) + if (d->n_widgets) + d->dev_power = 0; /* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */ - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &card->widgets, list) { switch (w->id) { case snd_soc_dapm_pre: - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); break; case snd_soc_dapm_post: - dapm_seq_insert(w, &up_list, dapm_up_seq); + dapm_seq_insert(w, &up_list, true); break; default: @@ -920,15 +1153,17 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) else power = 1; if (power) - sys_power = 1; + w->dapm->dev_power = 1; if (w->power == power) continue; + trace_snd_soc_dapm_widget_power(w, power); + if (power) - dapm_seq_insert(w, &up_list, dapm_up_seq); + dapm_seq_insert(w, &up_list, true); else - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); w->power = power; break; @@ -938,23 +1173,26 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) /* If there are no DAPM widgets then try to figure out power from the * event type. */ - if (list_empty(&codec->dapm_widgets)) { + if (!dapm->n_widgets) { switch (event) { case SND_SOC_DAPM_STREAM_START: case SND_SOC_DAPM_STREAM_RESUME: - sys_power = 1; + dapm->dev_power = 1; + break; + case SND_SOC_DAPM_STREAM_STOP: + dapm->dev_power = !!dapm->codec->active; break; case SND_SOC_DAPM_STREAM_SUSPEND: - sys_power = 0; + dapm->dev_power = 0; break; case SND_SOC_DAPM_STREAM_NOP: - switch (codec->bias_level) { + switch (dapm->bias_level) { case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_OFF: - sys_power = 0; + dapm->dev_power = 0; break; default: - sys_power = 1; + dapm->dev_power = 1; break; } break; @@ -963,52 +1201,31 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) } } - if (sys_power && codec->bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(card, codec, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - pr_err("Failed to turn on bias: %d\n", ret); - } - - /* If we're changing to all on or all off then prepare */ - if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) || - (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_PREPARE); - if (ret != 0) - pr_err("Failed to prepare bias: %d\n", ret); - } + /* 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, + &async_domain); + async_synchronize_full_domain(&async_domain); /* Power down widgets first; try to avoid amplifying pops. */ - dapm_seq_run(codec, &down_list, event, dapm_down_seq); + dapm_seq_run(dapm, &down_list, event, false); - /* Now power up. */ - dapm_seq_run(codec, &up_list, event, dapm_up_seq); + dapm_widget_update(dapm); - /* If we just powered the last thing off drop to standby bias */ - if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { - ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_STANDBY); - if (ret != 0) - pr_err("Failed to apply standby bias: %d\n", ret); - } + /* Now power up. */ + dapm_seq_run(dapm, &up_list, event, true); - /* If we're in standby and can support bias off then do that */ - if (codec->bias_level == SND_SOC_BIAS_STANDBY && - codec->idle_bias_off) { - ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF); - if (ret != 0) - pr_err("Failed to turn off bias: %d\n", ret); - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_post_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); - /* If we just powered up then move to active bias */ - if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) { - ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_ON); - if (ret != 0) - pr_err("Failed to apply active bias: %d\n", ret); - } + pop_dbg(dapm->dev, card->pop_time, + "DAPM sequencing finished, waiting %dms\n", card->pop_time); + pop_wait(card->pop_time); - pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n", - codec->pop_time); - pop_wait(codec->pop_time); + trace_snd_soc_dapm_done(card); return 0; } @@ -1035,9 +1252,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file, return -ENOMEM; in = is_connected_input_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); out = is_connected_output_ep(w); - dapm_clear_walk(w->codec); + dapm_clear_walk(w->dapm); ret = snprintf(buf, PAGE_SIZE, "%s: %s in %d out %d", w->name, w->power ? "On" : "Off", in, out); @@ -1060,7 +1277,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " in %s %s\n", + " in \"%s\" \"%s\"\n", p->name ? p->name : "static", p->source->name); } @@ -1070,7 +1287,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " out %s %s\n", + " out \"%s\" \"%s\"\n", p->name ? p->name : "static", p->sink->name); } @@ -1087,29 +1304,29 @@ static const struct file_operations dapm_widget_power_fops = { .llseek = default_llseek, }; -void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec) +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; struct dentry *d; - if (!codec->debugfs_dapm) + if (!dapm->debugfs_dapm) return; - list_for_each_entry(w, &codec->dapm_widgets, list) { - if (!w->name) + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!w->name || w->dapm != dapm) continue; d = debugfs_create_file(w->name, 0444, - codec->debugfs_dapm, w, + dapm->debugfs_dapm, w, &dapm_widget_power_fops); if (!d) - printk(KERN_WARNING - "ASoC: Failed to create %s debugfs file\n", - w->name); + dev_warn(w->dapm->dev, + "ASoC: Failed to create %s debugfs file\n", + w->name); } } #else -void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec) +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) { } #endif @@ -1123,6 +1340,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, int found = 0; if (widget->id != snd_soc_dapm_mux && + widget->id != snd_soc_dapm_virt_mux && widget->id != snd_soc_dapm_value_mux) return -ENODEV; @@ -1130,7 +1348,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->codec->dapm_paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1146,7 +1364,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, } if (found) - dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); return 0; } @@ -1164,7 +1382,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, return -ENODEV; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->codec->dapm_paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1175,7 +1393,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, } if (found) - dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); return 0; } @@ -1191,7 +1409,9 @@ static ssize_t dapm_widget_show(struct device *dev, int count = 0; char *state = "not set"; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != &codec->dapm) + continue; /* only display widgets that burnm power */ switch (w->id) { @@ -1203,6 +1423,7 @@ static ssize_t dapm_widget_show(struct device *dev, case snd_soc_dapm_dac: case snd_soc_dapm_adc: case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: @@ -1215,7 +1436,7 @@ static ssize_t dapm_widget_show(struct device *dev, } } - switch (codec->bias_level) { + switch (codec->dapm.bias_level) { case SND_SOC_BIAS_ON: state = "On"; break; @@ -1247,31 +1468,50 @@ static void snd_soc_dapm_sys_remove(struct device *dev) } /* free all dapm widgets and resources */ -static void dapm_free_widgets(struct snd_soc_codec *codec) +static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w, *next_w; struct snd_soc_dapm_path *p, *next_p; - list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) { + list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; list_del(&w->list); + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } + list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } + kfree(w->name); kfree(w); } - - list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) { - list_del(&p->list); - kfree(p->long_name); - kfree(p); - } } -static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec, +static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, const char *pin, int status) { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { - pr_debug("dapm: %s: pin %s\n", codec->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) @@ -1280,46 +1520,72 @@ static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec, } } - pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin); + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); return -EINVAL; } /** * snd_soc_dapm_sync - scan and power dapm paths - * @codec: audio codec + * @dapm: DAPM context * * Walks all dapm audio paths and powers widgets according to their * stream or path usage. * * Returns 0 for success. */ -int snd_soc_dapm_sync(struct snd_soc_codec *codec) +int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { - return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); + return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); -static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, +static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; - const char *sink = route->sink; + struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; + const char *sink; const char *control = route->control; - const char *source = route->source; + const char *source; + char prefixed_sink[80]; + char prefixed_source[80]; int ret = 0; - /* find src and dest widgets */ - list_for_each_entry(w, &codec->dapm_widgets, list) { + if (dapm->codec && dapm->codec->name_prefix) { + snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", + dapm->codec->name_prefix, route->sink); + sink = prefixed_sink; + snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", + dapm->codec->name_prefix, route->source); + source = prefixed_source; + } else { + sink = route->sink; + source = route->source; + } + /* + * find src and dest widgets over all widgets but favor a widget from + * current DAPM context + */ + list_for_each_entry(w, &dapm->card->widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { - wsink = w; + wtsink = w; + if (w->dapm == dapm) + wsink = w; continue; } if (!wsource && !(strcmp(w->name, source))) { - wsource = w; + wtsource = w; + if (w->dapm == dapm) + wsource = w; } } + /* use widget from another DAPM context if not found from this */ + if (!wsink) + wsink = wtsink; + if (!wsource) + wsource = wtsource; if (wsource == NULL || wsink == NULL) return -ENODEV; @@ -1353,7 +1619,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, /* connect static paths */ if (control == NULL) { - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; @@ -1365,6 +1631,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, case snd_soc_dapm_adc: case snd_soc_dapm_dac: case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: case snd_soc_dapm_input: case snd_soc_dapm_output: case snd_soc_dapm_micbias: @@ -1374,14 +1641,15 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, case snd_soc_dapm_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; return 0; case snd_soc_dapm_mux: + case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: - ret = dapm_connect_mux(codec, wsource, wsink, path, control, + ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrols[0]); if (ret != 0) goto err; @@ -1389,7 +1657,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: - ret = dapm_connect_mixer(codec, wsource, wsink, path, control); + ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); if (ret != 0) goto err; break; @@ -1397,7 +1665,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_spk: - list_add(&path->list, &codec->dapm_paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 0; @@ -1406,15 +1674,15 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, return 0; err: - printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source, - control, sink); + dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s\n", + source, control, sink); kfree(path); return ret; } /** * snd_soc_dapm_add_routes - Add routes between DAPM widgets - * @codec: codec + * @dapm: DAPM context * @route: audio routes * @num: number of routes * @@ -1425,17 +1693,16 @@ err: * Returns 0 for success else error. On error all resources can be freed * with a call to snd_soc_card_free(). */ -int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, +int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { int i, ret; for (i = 0; i < num; i++) { - ret = snd_soc_dapm_add_route(codec, route); + ret = snd_soc_dapm_add_route(dapm, route); if (ret < 0) { - printk(KERN_ERR "Failed to add route %s->%s\n", - route->source, - route->sink); + dev_err(dapm->dev, "Failed to add route %s->%s\n", + route->source, route->sink); return ret; } route++; @@ -1447,17 +1714,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); /** * snd_soc_dapm_new_widgets - add new dapm widgets - * @codec: audio codec + * @dapm: DAPM context * * Checks the codec for any new dapm widgets and creates them if found. * * Returns 0 for success. */ -int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) +int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; + unsigned int val; - list_for_each_entry(w, &codec->dapm_widgets, list) + list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) continue; @@ -1467,12 +1735,13 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: w->power_check = dapm_generic_check_power; - dapm_new_mixer(codec, w); + dapm_new_mixer(dapm, w); break; case snd_soc_dapm_mux: + case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: w->power_check = dapm_generic_check_power; - dapm_new_mux(codec, w); + dapm_new_mux(dapm, w); break; case snd_soc_dapm_adc: case snd_soc_dapm_aif_out: @@ -1483,8 +1752,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) w->power_check = dapm_dac_check_power; break; case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: w->power_check = dapm_generic_check_power; - dapm_new_pga(codec, w); + dapm_new_pga(dapm, w); break; case snd_soc_dapm_input: case snd_soc_dapm_output: @@ -1502,10 +1772,22 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) case snd_soc_dapm_post: break; } + + /* Read the initial power state from the device */ + if (w->reg >= 0) { + val = snd_soc_read(w->codec, w->reg); + val &= 1 << w->shift; + if (w->invert) + val = !val; + + if (val) + w->power = 1; + } + w->new = 1; } - dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -1566,32 +1848,25 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned int val, val2, val_mask; - int connect; - int ret; + unsigned int val; + int connect, change; + struct snd_soc_dapm_update update; val = (ucontrol->value.integer.value[0] & mask); if (invert) val = max - val; - val_mask = mask << shift; + mask = mask << shift; val = val << shift; - if (shift != rshift) { - val2 = (ucontrol->value.integer.value[1] & mask); - if (invert) - val2 = max - val2; - val_mask |= mask << rshift; - val |= val2 << rshift; - } mutex_lock(&widget->codec->mutex); widget->value = val; - if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { + change = snd_soc_test_bits(widget->codec, reg, mask, val); + if (change) { if (val) /* new connection */ connect = invert ? 0:1; @@ -1599,28 +1874,20 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, /* old connection must be powered down */ connect = invert ? 1:0; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; + dapm_mixer_update_power(widget, kcontrol, connect); + + widget->dapm->update = NULL; } - if (widget->event) { - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, kcontrol, - SND_SOC_DAPM_PRE_REG); - if (ret < 0) { - ret = 1; - goto out; - } - } - ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, kcontrol, - SND_SOC_DAPM_POST_REG); - } else - ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); - -out: mutex_unlock(&widget->codec->mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -1668,7 +1935,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; - int ret = 0; + struct snd_soc_dapm_update update; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -1687,24 +1954,20 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; change = snd_soc_test_bits(widget->codec, e->reg, mask, val); - dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret < 0) - goto out; - } + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_POST_REG); + widget->dapm->update = NULL; -out: mutex_unlock(&widget->codec->mutex); - return ret; + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -1816,7 +2079,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; - int ret = 0; + struct snd_soc_dapm_update update; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -1833,24 +2096,20 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock(&widget->codec->mutex); widget->value = val; change = snd_soc_test_bits(widget->codec, e->reg, mask, val); - dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret < 0) - goto out; - } + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); + dapm_mux_update_power(widget, kcontrol, change, mux, e); - if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, - kcontrol, SND_SOC_DAPM_POST_REG); + widget->dapm->update = NULL; -out: mutex_unlock(&widget->codec->mutex); - return ret; + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -1889,7 +2148,7 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, mutex_lock(&codec->mutex); ucontrol->value.integer.value[0] = - snd_soc_dapm_get_pin_status(codec, pin); + snd_soc_dapm_get_pin_status(&codec->dapm, pin); mutex_unlock(&codec->mutex); @@ -1912,11 +2171,11 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, mutex_lock(&codec->mutex); if (ucontrol->value.integer.value[0]) - snd_soc_dapm_enable_pin(codec, pin); + snd_soc_dapm_enable_pin(&codec->dapm, pin); else - snd_soc_dapm_disable_pin(codec, pin); + snd_soc_dapm_disable_pin(&codec->dapm, pin); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(&codec->dapm); mutex_unlock(&codec->mutex); @@ -1926,26 +2185,43 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); /** * snd_soc_dapm_new_control - create new dapm control - * @codec: audio codec + * @dapm: DAPM context * @widget: widget template * * Creates a new dapm control based upon the template. * * Returns 0 for success else error. */ -int snd_soc_dapm_new_control(struct snd_soc_codec *codec, +int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; + size_t name_len; if ((w = dapm_cnew_widget(widget)) == NULL) return -ENOMEM; - w->codec = codec; + name_len = strlen(widget->name) + 1; + if (dapm->codec && dapm->codec->name_prefix) + name_len += 1 + strlen(dapm->codec->name_prefix); + w->name = kmalloc(name_len, GFP_KERNEL); + if (w->name == NULL) { + kfree(w); + return -ENOMEM; + } + if (dapm->codec && dapm->codec->name_prefix) + snprintf(w->name, name_len, "%s %s", + dapm->codec->name_prefix, widget->name); + else + snprintf(w->name, name_len, "%s", widget->name); + + dapm->n_widgets++; + w->dapm = dapm; + w->codec = dapm->codec; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); - list_add(&w->list, &codec->dapm_widgets); + list_add(&w->list, &dapm->card->widgets); /* machine layer set ups unconnected pins and insertions */ w->connected = 1; @@ -1955,7 +2231,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); /** * snd_soc_dapm_new_controls - create new dapm controls - * @codec: audio codec + * @dapm: DAPM context * @widget: widget array * @num: number of widgets * @@ -1963,18 +2239,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); * * Returns 0 for success else error. */ -int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, +int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget, int num) { int i, ret; for (i = 0; i < num; i++) { - ret = snd_soc_dapm_new_control(codec, widget); + ret = snd_soc_dapm_new_control(dapm, widget); if (ret < 0) { - printk(KERN_ERR - "ASoC: Failed to create DAPM control %s: %d\n", - widget->name, ret); + dev_err(dapm->dev, + "ASoC: Failed to create DAPM control %s: %d\n", + widget->name, ret); return ret; } widget++; @@ -1983,34 +2259,17 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); - -/** - * snd_soc_dapm_stream_event - send a stream event to the dapm core - * @codec: audio codec - * @stream: stream name - * @event: stream event - * - * Sends a stream event to the dapm core. The core then makes any - * necessary widget power changes. - * - * Returns 0 for success else error. - */ -int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, +static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, const char *stream, int event) { - struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_widget *w; - if (stream == NULL) - return 0; - - mutex_lock(&codec->mutex); - list_for_each_entry(w, &codec->dapm_widgets, list) + list_for_each_entry(w, &dapm->card->widgets, list) { - if (!w->sname) + if (!w->sname || w->dapm != dapm) continue; - pr_debug("widget %s\n %s stream %s event %d\n", - w->name, w->sname, stream, event); + dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n", + w->name, w->sname, stream, event); if (strstr(w->sname, stream)) { switch(event) { case SND_SOC_DAPM_STREAM_START: @@ -2028,15 +2287,37 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, } } - dapm_power_widgets(codec, event); + dapm_power_widgets(dapm, event); +} + +/** + * snd_soc_dapm_stream_event - send a stream event to the dapm core + * @rtd: PCM runtime data + * @stream: stream name + * @event: stream event + * + * Sends a stream event to the dapm core. The core then makes any + * necessary widget power changes. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, + const char *stream, int event) +{ + struct snd_soc_codec *codec = rtd->codec; + + if (stream == NULL) + return 0; + + mutex_lock(&codec->mutex); + soc_dapm_stream_event(&codec->dapm, stream, event); mutex_unlock(&codec->mutex); return 0; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); /** * snd_soc_dapm_enable_pin - enable pin. - * @codec: SoC codec + * @dapm: DAPM context * @pin: pin name * * Enables input/output pin and its parents or children widgets iff there is @@ -2044,15 +2325,15 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ -int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin) +int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(codec, pin, 1); + return snd_soc_dapm_set_pin(dapm, pin, 1); } EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); /** * snd_soc_dapm_force_enable_pin - force a pin to be enabled - * @codec: SoC codec + * @dapm: DAPM context * @pin: pin name * * Enables input/output pin regardless of any other state. This is @@ -2062,42 +2343,47 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ -int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec, const char *pin) +int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, + const char *pin) { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { - pr_debug("dapm: %s: pin %s\n", codec->name, pin); + dev_dbg(w->dapm->dev, + "dapm: force enable pin %s\n", pin); w->connected = 1; w->force = 1; return 0; } } - pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin); + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); /** * snd_soc_dapm_disable_pin - disable pin. - * @codec: SoC codec + * @dapm: DAPM context * @pin: pin name * * Disables input/output pin and its parents or children widgets. * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ -int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin) +int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, + const char *pin) { - return snd_soc_dapm_set_pin(codec, pin, 0); + return snd_soc_dapm_set_pin(dapm, pin, 0); } EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); /** * snd_soc_dapm_nc_pin - permanently disable pin. - * @codec: SoC codec + * @dapm: DAPM context * @pin: pin name * * Marks the specified pin as being not connected, disabling it along @@ -2109,26 +2395,29 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ -int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin) +int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(codec, pin, 0); + return snd_soc_dapm_set_pin(dapm, pin, 0); } EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); /** * snd_soc_dapm_get_pin_status - get audio pin status - * @codec: audio codec + * @dapm: DAPM context * @pin: audio signal pin endpoint (or start point) * * Get audio pin status - connected or disconnected. * * Returns 1 for connected otherwise 0. */ -int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin) +int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, + const char *pin) { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) return w->connected; } @@ -2139,7 +2428,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); /** * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint - * @codec: audio codec + * @dapm: DAPM context * @pin: audio signal pin endpoint (or start point) * * Mark the given endpoint or pin as ignoring suspend. When the @@ -2148,18 +2437,21 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); * normal means at suspend time, it will not be turned on if it was not * already enabled. */ -int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin) +int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, + const char *pin) { struct snd_soc_dapm_widget *w; - list_for_each_entry(w, &codec->dapm_widgets, list) { + 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; } } - pr_err("Unknown DAPM pin: %s\n", pin); + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); @@ -2170,22 +2462,25 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); * * Free all dapm widgets and resources. */ -void snd_soc_dapm_free(struct snd_soc_codec *codec) +void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) { - snd_soc_dapm_sys_remove(codec->dev); - dapm_free_widgets(codec); + snd_soc_dapm_sys_remove(dapm->dev); + dapm_free_widgets(dapm); + list_del(&dapm->list); } EXPORT_SYMBOL_GPL(snd_soc_dapm_free); -static void soc_dapm_shutdown_codec(struct snd_soc_codec *codec) +static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; LIST_HEAD(down_list); int powerdown = 0; - list_for_each_entry(w, &codec->dapm_widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (w->power) { - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); w->power = 0; powerdown = 1; } @@ -2195,9 +2490,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_codec *codec) * standby. */ if (powerdown) { - snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_PREPARE); - dapm_seq_run(codec, &down_list, 0, dapm_down_seq); - snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_STANDBY); + snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); + dapm_seq_run(dapm, &down_list, 0, false); + snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); } } @@ -2208,10 +2503,10 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) { struct snd_soc_codec *codec; - list_for_each_entry(codec, &card->codec_dev_list, list) - soc_dapm_shutdown_codec(codec); - - snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF); + list_for_each_entry(codec, &card->codec_dev_list, list) { + soc_dapm_shutdown_codec(&codec->dapm); + snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF); + } } /* Module information */ diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 8a0a9205b1e7..fc017c0a7b5d 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -13,11 +13,11 @@ #include <sound/jack.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/delay.h> +#include <trace/events/asoc.h> /** * snd_soc_jack_new - Create a new jack @@ -37,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, { jack->codec = codec; INIT_LIST_HEAD(&jack->pins); + INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); @@ -60,14 +61,18 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new); void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) { struct snd_soc_codec *codec; + struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; int enable; int oldstatus; + trace_snd_soc_jack_report(jack, mask, status); + if (!jack) return; codec = jack->codec; + dapm = &codec->dapm; mutex_lock(&codec->mutex); @@ -81,6 +86,8 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) if (mask && (jack->status == oldstatus)) goto out; + trace_snd_soc_jack_notify(jack, status); + list_for_each_entry(pin, &jack->pins, list) { enable = pin->mask & jack->status; @@ -88,15 +95,15 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) enable = !enable; if (enable) - snd_soc_dapm_enable_pin(codec, pin->pin); + snd_soc_dapm_enable_pin(dapm, pin->pin); else - snd_soc_dapm_disable_pin(codec, pin->pin); + snd_soc_dapm_disable_pin(dapm, pin->pin); } /* Report before the DAPM sync to help users updating micbias status */ - blocking_notifier_call_chain(&jack->notifier, status, NULL); + blocking_notifier_call_chain(&jack->notifier, status, jack); - snd_soc_dapm_sync(codec); + snd_soc_dapm_sync(dapm); snd_jack_report(jack->jack, status); @@ -106,6 +113,51 @@ out: EXPORT_SYMBOL_GPL(snd_soc_jack_report); /** + * snd_soc_jack_add_zones - Associate voltage zones with jack + * + * @jack: ASoC jack + * @count: Number of zones + * @zone: Array of zones + * + * After this function has been called the zones specified in the + * array will be associated with the jack. + */ +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones) +{ + int i; + + for (i = 0; i < count; i++) { + INIT_LIST_HEAD(&zones[i].list); + list_add(&(zones[i].list), &jack->jack_zones); + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); + +/** + * snd_soc_jack_get_type - Based on the mic bias value, this function returns + * the type of jack from the zones delcared in the jack type + * + * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in + * + * Based on the mic bias value passed, this function helps identify + * the type of jack from the already delcared jack zones + */ +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) +{ + struct snd_soc_jack_zone *zone; + + list_for_each_entry(zone, &jack->jack_zones, list) { + if (micbias_voltage >= zone->min_mv && + micbias_voltage < zone->max_mv) + return zone->jack_type; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); + +/** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * * @jack: ASoC jack @@ -188,7 +240,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) int enable; int report; - enable = gpio_get_value(gpio->gpio); + enable = gpio_get_value_cansleep(gpio->gpio); if (gpio->invert) enable = !enable; @@ -207,6 +259,12 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) static irqreturn_t gpio_handler(int irq, void *data) { struct snd_soc_jack_gpio *gpio = data; + struct device *dev = gpio->jack->codec->card->dev; + + trace_snd_soc_jack_irq(gpio->name); + + if (device_may_wakeup(dev)) + pm_wakeup_event(dev, gpio->debounce_time + 50); schedule_delayed_work(&gpio->work, msecs_to_jiffies(gpio->debounce_time)); @@ -263,14 +321,23 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, INIT_DELAYED_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; - ret = request_irq(gpio_to_irq(gpios[i].gpio), - gpio_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - jack->codec->dev->driver->name, - &gpios[i]); + ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), + gpio_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + jack->codec->dev->driver->name, + &gpios[i]); if (ret) goto err; + if (gpios[i].wake) { + ret = irq_set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + if (ret != 0) + printk(KERN_ERR + "Failed to mark GPIO %d as wake source: %d\n", + gpios[i].gpio, ret); + } + #ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ gpio_export(gpios[i].gpio, false); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 1d07b931f3d8..3f45e6a439bf 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -28,26 +28,9 @@ int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) { int sample_size; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - sample_size = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - sample_size = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - sample_size = 24; - break; - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - sample_size = 32; - break; - default: - return -ENOTSUPP; - } + sample_size = snd_pcm_format_width(params_format(params)); + if (sample_size < 0) + return sample_size; return snd_soc_calc_frame_size(sample_size, params_channels(params), 1); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig new file mode 100644 index 000000000000..66b504f06c23 --- /dev/null +++ b/sound/soc/tegra/Kconfig @@ -0,0 +1,26 @@ +config SND_TEGRA_SOC + 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 + tristate + depends on SND_TEGRA_SOC + default m + 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 + 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. + diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile new file mode 100644 index 000000000000..fd183d3ab4f1 --- /dev/null +++ b/sound/soc/tegra/Makefile @@ -0,0 +1,15 @@ +# Tegra platform Support +snd-soc-tegra-das-objs := tegra_das.o +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 + +# Tegra machine Support +snd-soc-tegra-harmony-objs := harmony.o + +obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c new file mode 100644 index 000000000000..556a57133925 --- /dev/null +++ b/sound/soc/tegra/harmony.c @@ -0,0 +1,394 @@ +/* + * 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 new file mode 100644 index 000000000000..52f0a3f9ce40 --- /dev/null +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -0,0 +1,155 @@ +/* + * tegra_asoc_utils.c - Harmony machine ASoC driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - 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 <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> + +#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 new_baseclock; + int err; + + switch (srate) { + case 11025: + case 22050: + case 44100: + case 88200: + new_baseclock = 56448000; + break; + case 8000: + case 16000: + case 32000: + case 48000: + case 64000: + case 96000: + new_baseclock = 73728000; + break; + default: + return -EINVAL; + } + + *mclk_change = ((new_baseclock != data->set_baseclock) || + (mclk != data->set_mclk)); + if (!*mclk_change) + return 0; + + data->set_baseclock = 0; + data->set_mclk = 0; + + clk_disable(data->clk_cdev1); + clk_disable(data->clk_pll_a_out0); + clk_disable(data->clk_pll_a); + + err = clk_set_rate(data->clk_pll_a, new_baseclock); + if (err) { + dev_err(data->dev, "Can't set pll_a rate: %d\n", err); + return err; + } + + err = clk_set_rate(data->clk_pll_a_out0, mclk); + if (err) { + dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); + return err; + } + + /* Don't set cdev1 rate; its locked to pll_a_out0 */ + + err = clk_enable(data->clk_pll_a); + if (err) { + dev_err(data->dev, "Can't enable pll_a: %d\n", err); + return err; + } + + err = clk_enable(data->clk_pll_a_out0); + if (err) { + dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); + return err; + } + + err = clk_enable(data->clk_cdev1); + if (err) { + dev_err(data->dev, "Can't enable cdev1: %d\n", err); + return err; + } + + data->set_baseclock = new_baseclock; + data->set_mclk = mclk; + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); + +int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + struct device *dev) +{ + int ret; + + data->dev = dev; + + data->clk_pll_a = clk_get_sys(NULL, "pll_a"); + if (IS_ERR(data->clk_pll_a)) { + dev_err(data->dev, "Can't retrieve clk pll_a\n"); + ret = PTR_ERR(data->clk_pll_a); + goto err; + } + + data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); + if (IS_ERR(data->clk_pll_a_out0)) { + dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); + ret = PTR_ERR(data->clk_pll_a_out0); + goto err_put_pll_a; + } + + data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + if (IS_ERR(data->clk_cdev1)) { + dev_err(data->dev, "Can't retrieve clk cdev1\n"); + ret = PTR_ERR(data->clk_cdev1); + goto err_put_pll_a_out0; + } + + return 0; + +err_put_pll_a_out0: + clk_put(data->clk_pll_a_out0); +err_put_pll_a: + clk_put(data->clk_pll_a); +err: + return ret; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); + +void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) +{ + clk_put(data->clk_cdev1); + clk_put(data->clk_pll_a_out0); + clk_put(data->clk_pll_a); +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra ASoC utility code"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h new file mode 100644 index 000000000000..bbba7afdfc2c --- /dev/null +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -0,0 +1,45 @@ +/* + * tegra_asoc_utils.h - Definitions for Tegra DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - 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 + * + */ + +#ifndef __TEGRA_ASOC_UTILS_H__ +#define __TEGRA_ASOC_UTILS_H_ + +struct clk; +struct device; + +struct tegra_asoc_utils_data { + struct device *dev; + struct clk *clk_pll_a; + struct clk *clk_pll_a_out0; + struct clk *clk_cdev1; + int set_baseclock; + int set_mclk; +}; + +int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, + int mclk, int *mclk_change); +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); + +#endif + diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c new file mode 100644 index 000000000000..9f24ef73f2cb --- /dev/null +++ b/sound/soc/tegra/tegra_das.c @@ -0,0 +1,265 @@ +/* + * tegra_das.c - Tegra DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - 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 <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <mach/iomap.h> +#include <sound/soc.h> +#include "tegra_das.h" + +#define DRV_NAME "tegra-das" + +static struct tegra_das *das; + +static inline void tegra_das_write(u32 reg, u32 val) +{ + __raw_writel(val, das->regs + reg); +} + +static inline u32 tegra_das_read(u32 reg) +{ + return __raw_readl(das->regs + reg); +} + +int tegra_das_connect_dap_to_dac(int dap, int dac) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA_DAS_DAP_CTRL_SEL + + (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); + reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; + + tegra_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac); + +int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master, + int sdata1rx, int sdata2rx) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA_DAS_DAP_CTRL_SEL + + (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); + reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P | + !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P | + !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P | + !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P; + + tegra_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap); + +int tegra_das_connect_dac_to_dap(int dac, int dap) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + + (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | + dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | + dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; + + tegra_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap); + +#ifdef CONFIG_DEBUG_FS +static int tegra_das_show(struct seq_file *s, void *unused) +{ + int i; + u32 addr; + u32 reg; + + for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) { + addr = TEGRA_DAS_DAP_CTRL_SEL + + (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); + reg = tegra_das_read(addr); + seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg); + } + + for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) { + addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + + (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = tegra_das_read(addr); + seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n", + i, reg); + } + + return 0; +} + +static int tegra_das_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra_das_show, inode->i_private); +} + +static const struct file_operations tegra_das_debug_fops = { + .open = tegra_das_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra_das_debug_add(struct tegra_das *das) +{ + das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, + snd_soc_debugfs_root, das, + &tegra_das_debug_fops); +} + +static void tegra_das_debug_remove(struct tegra_das *das) +{ + if (das->debug) + debugfs_remove(das->debug); +} +#else +static inline void tegra_das_debug_add(struct tegra_das *das) +{ +} + +static inline void tegra_das_debug_remove(struct tegra_das *das) +{ +} +#endif + +static int __devinit tegra_das_probe(struct platform_device *pdev) +{ + struct resource *res, *region; + int ret = 0; + + if (das) + return -ENODEV; + + das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL); + if (!das) { + dev_err(&pdev->dev, "Can't allocate tegra_das\n"); + ret = -ENOMEM; + goto exit; + } + das->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_free; + } + + region = request_mem_region(res->start, resource_size(res), + pdev->name); + if (!region) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_free; + } + + das->regs = ioremap(res->start, resource_size(res)); + if (!das->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release; + } + + tegra_das_debug_add(das); + + platform_set_drvdata(pdev, das); + + return 0; + +err_release: + release_mem_region(res->start, resource_size(res)); +err_free: + kfree(das); + das = 0; +exit: + return ret; +} + +static int __devexit tegra_das_remove(struct platform_device *pdev) +{ + struct resource *res; + + if (!das) + return -ENODEV; + + platform_set_drvdata(pdev, NULL); + + tegra_das_debug_remove(das); + + iounmap(das->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(das); + das = 0; + + return 0; +} + +static struct platform_driver tegra_das_driver = { + .probe = tegra_das_probe, + .remove = __devexit_p(tegra_das_remove), + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init tegra_das_modinit(void) +{ + return platform_driver_register(&tegra_das_driver); +} +module_init(tegra_das_modinit); + +static void __exit tegra_das_modexit(void) +{ + platform_driver_unregister(&tegra_das_driver); +} +module_exit(tegra_das_modexit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra DAS driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h new file mode 100644 index 000000000000..2c96c7b3c459 --- /dev/null +++ b/sound/soc/tegra/tegra_das.h @@ -0,0 +1,135 @@ +/* + * tegra_das.h - Definitions for Tegra DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - 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 + * + */ + +#ifndef __TEGRA_DAS_H__ +#define __TEGRA_DAS_H__ + +/* Register TEGRA_DAS_DAP_CTRL_SEL */ +#define TEGRA_DAS_DAP_CTRL_SEL 0x00 +#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5 +#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5 + +/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */ +#define TEGRA_DAS_DAP_SEL_DAC1 0 +#define TEGRA_DAS_DAP_SEL_DAC2 1 +#define TEGRA_DAS_DAP_SEL_DAC3 2 +#define TEGRA_DAS_DAP_SEL_DAP1 16 +#define TEGRA_DAS_DAP_SEL_DAP2 17 +#define TEGRA_DAS_DAP_SEL_DAP3 18 +#define TEGRA_DAS_DAP_SEL_DAP4 19 +#define TEGRA_DAS_DAP_SEL_DAP5 20 + +/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */ +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4 + +/* + * Values for: + * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL + * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL + * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL + */ +#define TEGRA_DAS_DAC_SEL_DAP1 0 +#define TEGRA_DAS_DAC_SEL_DAP2 1 +#define TEGRA_DAS_DAC_SEL_DAP3 2 +#define TEGRA_DAS_DAC_SEL_DAP4 3 +#define TEGRA_DAS_DAC_SEL_DAP5 4 + +/* + * Names/IDs of the DACs/DAPs. + */ + +#define TEGRA_DAS_DAP_ID_1 0 +#define TEGRA_DAS_DAP_ID_2 1 +#define TEGRA_DAS_DAP_ID_3 2 +#define TEGRA_DAS_DAP_ID_4 3 +#define TEGRA_DAS_DAP_ID_5 4 + +#define TEGRA_DAS_DAC_ID_1 0 +#define TEGRA_DAS_DAC_ID_2 1 +#define TEGRA_DAS_DAC_ID_3 2 + +struct tegra_das { + struct device *dev; + void __iomem *regs; + struct dentry *debug; +}; + +/* + * Terminology: + * DAS: Digital audio switch (HW module controlled by this driver) + * DAP: Digital audio port (port/pins on Tegra device) + * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere) + * + * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific + * DAC, or another DAP. When DAPs are connected, one must be the master and + * one the slave. Each DAC allows selection of a specific DAP for input, to + * cater for the case where N DAPs are connected to 1 DAC for broadcast + * output. + * + * This driver is dumb; no attempt is made to ensure that a valid routing + * configuration is programmed. + */ + +/* + * Connect a DAP to to a DAC + * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* + * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC* + */ +extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel); + +/* + * Connect a DAP to to another DAP + * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* + * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP* + * master: Is this DAP the master (1) or slave (0) + * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0) + * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0) + */ +extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel, + int master, int sdata1rx, + int sdata2rx); + +/* + * Connect a DAC's input to a DAP + * (DAC outputs are selected by the DAP) + * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_* + * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP* + */ +extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel); + +#endif diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c new file mode 100644 index 000000000000..4f5e2c90b020 --- /dev/null +++ b/sound/soc/tegra/tegra_i2s.c @@ -0,0 +1,503 @@ +/* + * tegra_i2s.c - Tegra I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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 <linux/clk.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <mach/iomap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_das.h" +#include "tegra_i2s.h" + +#define DRV_NAME "tegra-i2s" + +static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val) +{ + __raw_writel(val, i2s->regs + reg); +} + +static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg) +{ + return __raw_readl(i2s->regs + reg); +} + +#ifdef CONFIG_DEBUG_FS +static int tegra_i2s_show(struct seq_file *s, void *unused) +{ +#define REG(r) { r, #r } + static const struct { + int offset; + const char *name; + } regs[] = { + REG(TEGRA_I2S_CTRL), + REG(TEGRA_I2S_STATUS), + REG(TEGRA_I2S_TIMING), + REG(TEGRA_I2S_FIFO_SCR), + REG(TEGRA_I2S_PCM_CTRL), + REG(TEGRA_I2S_NW_CTRL), + REG(TEGRA_I2S_TDM_CTRL), + REG(TEGRA_I2S_TDM_TX_RX_CTRL), + }; +#undef REG + + struct tegra_i2s *i2s = s->private; + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + u32 val = tegra_i2s_read(i2s, regs[i].offset); + seq_printf(s, "%s = %08x\n", regs[i].name, val); + } + + return 0; +} + +static int tegra_i2s_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra_i2s_show, inode->i_private); +} + +static const struct file_operations tegra_i2s_debug_fops = { + .open = tegra_i2s_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) +{ + char name[] = DRV_NAME ".0"; + + snprintf(name, sizeof(name), DRV_NAME".%1d", id); + i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, + i2s, &tegra_i2s_debug_fops); +} + +static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) +{ + if (i2s->debug) + debugfs_remove(i2s->debug); +} +#else +static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s) +{ +} + +static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s) +{ +} +#endif + +static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | + TEGRA_I2S_CTRL_LRCK_MASK); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_DSP_B: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW; + break; + case SND_SOC_DAIFMT_I2S: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 reg; + int ret, sample_size, srate, i2sclock, bitcnt; + + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16; + sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24; + sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32; + sample_size = 32; + break; + default: + return -EINVAL; + } + + srate = params_rate(params); + + /* Final "* 2" required by Tegra hardware */ + i2sclock = srate * params_channels(params) * sample_size * 2; + + ret = clk_set_rate(i2s->clk_i2s, i2sclock); + if (ret) { + dev_err(dev, "Can't set I2S clock rate: %d\n", ret); + return ret; + } + + bitcnt = (i2sclock / (2 * srate)) - 1; + if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) + return -EINVAL; + reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; + + if (i2sclock % (2 * srate)) + reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE; + + tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg); + + tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR, + TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | + TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); + + return 0; +} + +static void tegra_i2s_start_playback(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra_i2s_stop_playback(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra_i2s_start_capture(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra_i2s_stop_capture(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (!i2s->clk_refs) + clk_enable(i2s->clk_i2s); + i2s->clk_refs++; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra_i2s_start_playback(i2s); + else + tegra_i2s_start_capture(i2s); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra_i2s_stop_playback(i2s); + else + tegra_i2s_stop_capture(i2s); + i2s->clk_refs--; + if (!i2s->clk_refs) + clk_disable(i2s->clk_i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra_i2s_probe(struct snd_soc_dai *dai) +{ + struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &i2s->capture_dma_data; + dai->playback_dma_data = &i2s->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_ops tegra_i2s_dai_ops = { + .set_fmt = tegra_i2s_set_fmt, + .hw_params = tegra_i2s_hw_params, + .trigger = tegra_i2s_trigger, +}; + +struct snd_soc_dai_driver tegra_i2s_dai[] = { + { + .name = DRV_NAME ".0", + .probe = tegra_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra_i2s_dai_ops, + .symmetric_rates = 1, + }, + { + .name = DRV_NAME ".1", + .probe = tegra_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra_i2s_dai_ops, + .symmetric_rates = 1, + }, +}; + +static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) +{ + struct tegra_i2s * i2s; + char clk_name[12]; /* tegra-i2s.0 */ + struct resource *mem, *memregion, *dmareq; + int ret; + + if ((pdev->id < 0) || + (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) { + dev_err(&pdev->dev, "ID %d out of range\n", pdev->id); + return -EINVAL; + } + + /* + * FIXME: Until a codec driver exists for the tegra DAS, hard-code a + * 1:1 mapping between audio controllers and audio ports. + */ + ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id, + TEGRA_DAS_DAP_SEL_DAC1 + pdev->id); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAP connection\n"); + return ret; + } + ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id, + TEGRA_DAS_DAC_SEL_DAP1 + pdev->id); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAC connection\n"); + return ret; + } + + i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL); + if (!i2s) { + dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); + ret = -ENOMEM; + goto exit; + } + dev_set_drvdata(&pdev->dev, i2s); + + snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id); + i2s->clk_i2s = clk_get_sys(clk_name, NULL); + if (IS_ERR(i2s->clk_i2s)) { + dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); + ret = PTR_ERR(i2s->clk_i2s); + goto err_free; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmareq) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = request_mem_region(mem->start, resource_size(mem), + DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + i2s->regs = ioremap(mem->start, resource_size(mem)); + if (!i2s->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release; + } + + i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; + i2s->capture_dma_data.wrap = 4; + i2s->capture_dma_data.width = 32; + i2s->capture_dma_data.req_sel = dmareq->start; + + i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; + i2s->playback_dma_data.wrap = 4; + i2s->playback_dma_data.width = 32; + i2s->playback_dma_data.req_sel = dmareq->start; + + i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; + + ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_unmap; + } + + tegra_i2s_debug_add(i2s, pdev->id); + + return 0; + +err_unmap: + iounmap(i2s->regs); +err_release: + release_mem_region(mem->start, resource_size(mem)); +err_clk_put: + clk_put(i2s->clk_i2s); +err_free: + kfree(i2s); +exit: + return ret; +} + +static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) +{ + struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); + struct resource *res; + + snd_soc_unregister_dai(&pdev->dev); + + tegra_i2s_debug_remove(i2s); + + iounmap(i2s->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_put(i2s->clk_i2s); + + kfree(i2s); + + return 0; +} + +static struct platform_driver tegra_i2s_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_i2s_platform_probe, + .remove = __devexit_p(tegra_i2s_platform_remove), +}; + +static int __init snd_tegra_i2s_init(void) +{ + return platform_driver_register(&tegra_i2s_driver); +} +module_init(snd_tegra_i2s_init); + +static void __exit snd_tegra_i2s_exit(void) +{ + platform_driver_unregister(&tegra_i2s_driver); +} +module_exit(snd_tegra_i2s_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra I2S ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h new file mode 100644 index 000000000000..2b38a096f46c --- /dev/null +++ b/sound/soc/tegra/tegra_i2s.h @@ -0,0 +1,165 @@ +/* + * tegra_i2s.h - Definitions for Tegra I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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 + * + */ + +#ifndef __TEGRA_I2S_H__ +#define __TEGRA_I2S_H__ + +#include "tegra_pcm.h" + +/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */ + +#define TEGRA_I2S_CTRL 0x00 +#define TEGRA_I2S_STATUS 0x04 +#define TEGRA_I2S_TIMING 0x08 +#define TEGRA_I2S_FIFO_SCR 0x0c +#define TEGRA_I2S_PCM_CTRL 0x10 +#define TEGRA_I2S_NW_CTRL 0x14 +#define TEGRA_I2S_TDM_CTRL 0x20 +#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24 +#define TEGRA_I2S_FIFO1 0x40 +#define TEGRA_I2S_FIFO2 0x80 + +/* Fields in TEGRA_I2S_CTRL */ + +#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30) +#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29) +#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28) +#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27) +#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26) +#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25) + +#define TEGRA_I2S_LRCK_LEFT_LOW 0 +#define TEGRA_I2S_LRCK_RIGHT_LOW 1 + +#define TEGRA_I2S_CTRL_LRCK_SHIFT 24 +#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT) +#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) +#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) + +#define TEGRA_I2S_BIT_FORMAT_I2S 0 +#define TEGRA_I2S_BIT_FORMAT_RJM 1 +#define TEGRA_I2S_BIT_FORMAT_LJM 2 +#define TEGRA_I2S_BIT_FORMAT_DSP 3 + +#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10 +#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) + +#define TEGRA_I2S_BIT_SIZE_16 0 +#define TEGRA_I2S_BIT_SIZE_20 1 +#define TEGRA_I2S_BIT_SIZE_24 2 +#define TEGRA_I2S_BIT_SIZE_32 3 + +#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8 +#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) + +#define TEGRA_I2S_FIFO_16_LSB 0 +#define TEGRA_I2S_FIFO_20_LSB 1 +#define TEGRA_I2S_FIFO_24_LSB 2 +#define TEGRA_I2S_FIFO_32 3 +#define TEGRA_I2S_FIFO_PACKED 7 + +#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4 +#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) + +#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3) +#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2) +#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1) +#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0) + +/* Fields in TEGRA_I2S_STATUS */ + +#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31) +#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30) +#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29) +#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28) +#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3) +#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2) +#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1) +#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0) + +/* Fields in TEGRA_I2S_TIMING */ + +#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12) +#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 +#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff +#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) + +/* Fields in TEGRA_I2S_FIFO_SCR */ + +#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24 +#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16 +#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f + +#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12) +#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8) + +#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0 +#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1 +#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2 +#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3 + +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4 +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) + +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0 +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) + +struct tegra_i2s { + struct clk *clk_i2s; + int clk_refs; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + void __iomem *regs; + struct dentry *debug; + u32 reg_ctrl; +}; + +#endif diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c new file mode 100644 index 000000000000..3c271f953582 --- /dev/null +++ b/sound/soc/tegra/tegra_pcm.c @@ -0,0 +1,404 @@ +/* + * tegra_pcm.c - Tegra PCM driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * Vijay Mali <vmali@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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 <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_pcm.h" + +#define DRV_NAME "tegra-pcm-audio" + +static const struct snd_pcm_hardware tegra_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 1024, + .period_bytes_max = PAGE_SIZE, + .periods_min = 2, + .periods_max = 8, + .buffer_bytes_max = PAGE_SIZE * 8, + .fifo_size = 4, +}; + +static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd) +{ + struct snd_pcm_substream *substream = prtd->substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + struct tegra_dma_req *dma_req; + unsigned long addr; + + dma_req = &prtd->dma_req[prtd->dma_req_idx]; + prtd->dma_req_idx = 1 - prtd->dma_req_idx; + + addr = buf->addr + prtd->dma_pos; + prtd->dma_pos += dma_req->size; + if (prtd->dma_pos >= prtd->dma_pos_end) + prtd->dma_pos = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_req->source_addr = addr; + else + dma_req->dest_addr = addr; + + tegra_dma_enqueue_req(prtd->dma_chan, dma_req); +} + +static void dma_complete_callback(struct tegra_dma_req *req) +{ + struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev; + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + spin_lock(&prtd->lock); + + if (!prtd->running) { + spin_unlock(&prtd->lock); + return; + } + + if (++prtd->period_index >= runtime->periods) + prtd->period_index = 0; + + tegra_pcm_queue_dma(prtd); + + spin_unlock(&prtd->lock); + + snd_pcm_period_elapsed(substream); +} + +static void setup_dma_tx_request(struct tegra_dma_req *req, + struct tegra_pcm_dma_params * dmap) +{ + req->complete = dma_complete_callback; + req->to_memory = false; + req->dest_addr = dmap->addr; + req->dest_wrap = dmap->wrap; + req->source_bus_width = 32; + req->source_wrap = 0; + req->dest_bus_width = dmap->width; + req->req_sel = dmap->req_sel; +} + +static void setup_dma_rx_request(struct tegra_dma_req *req, + struct tegra_pcm_dma_params * dmap) +{ + req->complete = dma_complete_callback; + req->to_memory = true; + req->source_addr = dmap->addr; + req->dest_wrap = 0; + req->source_bus_width = dmap->width; + req->source_wrap = dmap->wrap; + req->dest_bus_width = 32; + req->req_sel = dmap->req_sel; +} + +static int tegra_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct tegra_pcm_dma_params * dmap; + int ret = 0; + + prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + runtime->private_data = prtd; + prtd->substream = substream; + + spin_lock_init(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + setup_dma_tx_request(&prtd->dma_req[0], dmap); + setup_dma_tx_request(&prtd->dma_req[1], dmap); + } else { + dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + setup_dma_rx_request(&prtd->dma_req[0], dmap); + setup_dma_rx_request(&prtd->dma_req[1], dmap); + } + + prtd->dma_req[0].dev = prtd; + prtd->dma_req[1].dev = prtd; + + prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (prtd->dma_chan == NULL) { + ret = -ENOMEM; + goto err; + } + + /* Set HW params now that initialization is complete */ + snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); + + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto err; + + return 0; + +err: + if (prtd->dma_chan) { + tegra_dma_free_channel(prtd->dma_chan); + } + + kfree(prtd); + + return ret; +} + +static int tegra_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + + tegra_dma_free_channel(prtd->dma_chan); + + kfree(prtd); + + return 0; +} + +static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + prtd->dma_req[0].size = params_period_bytes(params); + prtd->dma_req[1].size = prtd->dma_req[0].size; + + return 0; +} + +static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + + return 0; +} + +static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->dma_pos = 0; + prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size); + prtd->period_index = 0; + prtd->dma_req_idx = 0; + /* Fall-through */ + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&prtd->lock, flags); + prtd->running = 1; + spin_unlock_irqrestore(&prtd->lock, flags); + tegra_pcm_queue_dma(prtd); + tegra_pcm_queue_dma(prtd); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&prtd->lock, flags); + prtd->running = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]); + tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + + return prtd->period_index * runtime->period_size; +} + + +static int tegra_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops tegra_pcm_ops = { + .open = tegra_pcm_open, + .close = tegra_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = tegra_pcm_hw_params, + .hw_free = tegra_pcm_hw_free, + .trigger = tegra_pcm_trigger, + .pointer = tegra_pcm_pointer, + .mmap = tegra_pcm_mmap, +}; + +static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = tegra_pcm_hardware.buffer_bytes_max; + + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->bytes = size; + + return 0; +} + +static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + + if (!buf->area) + return; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; +} + +static u64 tegra_dma_mask = DMA_BIT_MASK(32); + +static int tegra_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &tegra_dma_mask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->driver->playback.channels_min) { + ret = tegra_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto err; + } + + if (dai->driver->capture.channels_min) { + ret = tegra_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto err_free_play; + } + + return 0; + +err_free_play: + tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); +err: + return ret; +} + +static void tegra_pcm_free(struct snd_pcm *pcm) +{ + tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); + tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); +} + +struct snd_soc_platform_driver tegra_pcm_platform = { + .ops = &tegra_pcm_ops, + .pcm_new = tegra_pcm_new, + .pcm_free = tegra_pcm_free, +}; + +static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform); +} + +static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver tegra_pcm_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_pcm_platform_probe, + .remove = __devexit_p(tegra_pcm_platform_remove), +}; + +static int __init snd_tegra_pcm_init(void) +{ + return platform_driver_register(&tegra_pcm_driver); +} +module_init(snd_tegra_pcm_init); + +static void __exit snd_tegra_pcm_exit(void) +{ + platform_driver_unregister(&tegra_pcm_driver); +} +module_exit(snd_tegra_pcm_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra PCM ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h new file mode 100644 index 000000000000..dbb90339fe0d --- /dev/null +++ b/sound/soc/tegra/tegra_pcm.h @@ -0,0 +1,55 @@ +/* + * tegra_pcm.h - Definitions for Tegra PCM driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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 + * + */ + +#ifndef __TEGRA_PCM_H__ +#define __TEGRA_PCM_H__ + +#include <mach/dma.h> + +struct tegra_pcm_dma_params { + unsigned long addr; + unsigned long wrap; + unsigned long width; + unsigned long req_sel; +}; + +struct tegra_runtime_data { + struct snd_pcm_substream *substream; + spinlock_t lock; + int running; + int dma_pos; + int dma_pos_end; + int period_index; + int dma_req_idx; + struct tegra_dma_req dma_req[2]; + struct tegra_dma_channel *dma_chan; +}; + +#endif |