diff options
Diffstat (limited to 'sound/soc/codecs')
51 files changed, 11030 insertions, 826 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1e1613a438dd..9f8e8594aeb9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -12,6 +12,7 @@ config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" select SND_SOC_88PM860X if MFD_88PM860X select SND_SOC_L3 + select SND_SOC_AB8500_CODEC if ABX500_CORE select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI @@ -35,7 +36,9 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C + select SND_SOC_DA732X if I2C select SND_SOC_DFBMCS320 + select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C select SND_SOC_LM49453 if I2C @@ -54,6 +57,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SPDIF select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI select SND_SOC_STA32X if I2C + select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -70,6 +74,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM2000 if I2C select SND_SOC_WM2200 if I2C select SND_SOC_WM5100 if I2C + select SND_SOC_WM5102 if MFD_WM5102 + select SND_SOC_WM5110 if MFD_WM5110 select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8400 if MFD_WM8400 select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI @@ -126,11 +132,21 @@ config SND_SOC_ALL_CODECS config SND_SOC_88PM860X tristate +config SND_SOC_ARIZONA + tristate + default y if SND_SOC_WM5102=y + default y if SND_SOC_WM5110=y + default m if SND_SOC_WM5102=m + default m if SND_SOC_WM5110=m + config SND_SOC_WM_HUBS tristate default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m +config SND_SOC_AB8500_CODEC + tristate + config SND_SOC_AC97_CODEC tristate select SND_AC97_CODEC @@ -219,12 +235,18 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_DA732X + tristate + config SND_SOC_DFBMCS320 tristate config SND_SOC_DMIC tristate +config SND_SOC_ISABELLE + tristate + config SND_SOC_LM49453 tristate @@ -266,6 +288,9 @@ config SND_SOC_SSM2602 config SND_SOC_STA32X tristate +config SND_SOC_STA529 + tristate + config SND_SOC_STAC9766 tristate @@ -313,6 +338,12 @@ config SND_SOC_WM2200 config SND_SOC_WM5100 tristate +config SND_SOC_WM5102 + tristate + +config SND_SOC_WM5110 + tristate + config SND_SOC_WM8350 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fc27fec39487..34148bb59c68 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,4 +1,5 @@ snd-soc-88pm860x-objs := 88pm860x-codec.o +snd-soc-ab8500-codec-objs := ab8500-codec.o snd-soc-ac97-objs := ac97.o snd-soc-ad1836-objs := ad1836.o snd-soc-ad193x-objs := ad193x.o @@ -13,6 +14,7 @@ snd-soc-ak4535-objs := ak4535.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o +snd-soc-arizona-objs := arizona.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l52-objs := cs42l52.o @@ -21,8 +23,10 @@ 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-da732x-objs := da732x.o snd-soc-dfbmcs320-objs := dfbmcs320.o snd-soc-dmic-objs := dmic.o +snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o @@ -41,9 +45,11 @@ snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o snd-soc-sigmadsp-objs := sigmadsp.o snd-soc-sn95031-objs := sn95031.o -snd-soc-spdif-objs := spdif_transciever.o +snd-soc-spdif-tx-objs := spdif_transciever.o +snd-soc-spdif-rx-objs := spdif_receiver.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-sta32x-objs := sta32x.o +snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o @@ -59,6 +65,8 @@ snd-soc-wm1250-ev1-objs := wm1250-ev1.o snd-soc-wm2000-objs := wm2000.o snd-soc-wm2200-objs := wm2200.o snd-soc-wm5100-objs := wm5100.o wm5100-tables.o +snd-soc-wm5102-objs := wm5102.o +snd-soc-wm5110-objs := wm5110.o snd-soc-wm8350-objs := wm8350.o snd-soc-wm8400-objs := wm8400.o snd-soc-wm8510-objs := wm8510.o @@ -108,6 +116,7 @@ snd-soc-max9877-objs := max9877.o snd-soc-tpa6130a2-objs := tpa6130a2.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o +obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o @@ -124,6 +133,7 @@ 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_ALC5632) += snd-soc-alc5632.o +obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o @@ -132,8 +142,10 @@ 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_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o +obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o @@ -150,9 +162,10 @@ obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o -obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o +obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o +obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.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 @@ -168,6 +181,8 @@ obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o obj-$(CONFIG_SND_SOC_WM5100) += snd-soc-wm5100.o +obj-$(CONFIG_SND_SOC_WM5102) += snd-soc-wm5102.o +obj-$(CONFIG_SND_SOC_WM5110) += snd-soc-wm5110.o obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c new file mode 100644 index 000000000000..3c795921c5f6 --- /dev/null +++ b/sound/soc/codecs/ab8500-codec.c @@ -0,0 +1,2522 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja <ola.o.lilja@stericsson.com>, + * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>, + * Roger Nilsson <roger.xr.nilsson@stericsson.com>, + * for ST-Ericsson. + * + * Based on the early work done by: + * Mikko J. Lehto <mikko.lehto@symbio.com>, + * Mikko Sarmanne <mikko.sarmanne@symbio.com>, + * Jarmo K. Kuronen <jarmo.kuronen@symbio.com>, + * for ST-Ericsson. + * + * License terms: + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/mfd/abx500/ab8500.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab8500-sysctrl.h> +#include <linux/mfd/abx500/ab8500-codec.h> +#include <linux/regulator/consumer.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "ab8500-codec.h" + +/* Macrocell value definitions */ +#define CLK_32K_OUT2_DISABLE 0x01 +#define INACTIVE_RESET_AUDIO 0x02 +#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK 0x10 +#define ENABLE_VINTCORE12_SUPPLY 0x04 +#define GPIO27_DIR_OUTPUT 0x04 +#define GPIO29_DIR_OUTPUT 0x10 +#define GPIO31_DIR_OUTPUT 0x40 + +/* Macrocell register definitions */ +#define AB8500_CTRL3_REG 0x0200 +#define AB8500_GPIO_DIR4_REG 0x1013 + +/* Nr of FIR/IIR-coeff banks in ANC-block */ +#define AB8500_NR_OF_ANC_COEFF_BANKS 2 + +/* Minimum duration to keep ANC IIR Init bit high or +low before proceeding with the configuration sequence */ +#define AB8500_ANC_SM_DELAY 2000 + +#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = filter_control_info, \ + .get = filter_control_get, .put = filter_control_put, \ + .private_value = (unsigned long)&(struct filter_control) \ + {.count = xcount, .min = xmin, .max = xmax} } + +struct filter_control { + long min, max; + unsigned int count; + long value[128]; +}; + +/* Sidetone states */ +static const char * const enum_sid_state[] = { + "Unconfigured", + "Apply FIR", + "FIR is configured", +}; +enum sid_state { + SID_UNCONFIGURED = 0, + SID_APPLY_FIR = 1, + SID_FIR_CONFIGURED = 2, +}; + +static const char * const enum_anc_state[] = { + "Unconfigured", + "Apply FIR and IIR", + "FIR and IIR are configured", + "Apply FIR", + "FIR is configured", + "Apply IIR", + "IIR is configured" +}; +enum anc_state { + ANC_UNCONFIGURED = 0, + ANC_APPLY_FIR_IIR = 1, + ANC_FIR_IIR_CONFIGURED = 2, + ANC_APPLY_FIR = 3, + ANC_FIR_CONFIGURED = 4, + ANC_APPLY_IIR = 5, + ANC_IIR_CONFIGURED = 6 +}; + +/* Analog microphones */ +enum amic_idx { + AMIC_IDX_1A, + AMIC_IDX_1B, + AMIC_IDX_2 +}; + +struct ab8500_codec_drvdata_dbg { + struct regulator *vaud; + struct regulator *vamic1; + struct regulator *vamic2; + struct regulator *vdmic; +}; + +/* Private data for AB8500 device-driver */ +struct ab8500_codec_drvdata { + /* Sidetone */ + long *sid_fir_values; + enum sid_state sid_status; + + /* ANC */ + struct mutex anc_lock; + long *anc_fir_values; + long *anc_iir_values; + enum anc_state anc_status; +}; + +static inline const char *amic_micbias_str(enum amic_micbias micbias) +{ + switch (micbias) { + case AMIC_MICBIAS_VAMIC1: + return "VAMIC1"; + case AMIC_MICBIAS_VAMIC2: + return "VAMIC2"; + default: + return "Unknown"; + } +} + +static inline const char *amic_type_str(enum amic_type type) +{ + switch (type) { + case AMIC_TYPE_DIFFERENTIAL: + return "DIFFERENTIAL"; + case AMIC_TYPE_SINGLE_ENDED: + return "SINGLE ENDED"; + default: + return "Unknown"; + } +} + +/* + * Read'n'write functions + */ + +/* Read a register from the audio-bank of AB8500 */ +static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec, + unsigned int reg) +{ + int status; + unsigned int value = 0; + + u8 value8; + status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO, + reg, &value8); + if (status < 0) { + dev_err(codec->dev, + "%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n", + __func__, (u8)AB8500_AUDIO, (u8)reg, status); + } else { + dev_dbg(codec->dev, + "%s: Read 0x%02x from register 0x%02x:0x%02x\n", + __func__, value8, (u8)AB8500_AUDIO, (u8)reg); + value = (unsigned int)value8; + } + + return value; +} + +/* Write to a register in the audio-bank of AB8500 */ +static int ab8500_codec_write_reg(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int status; + + status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO, + reg, value); + if (status < 0) + dev_err(codec->dev, + "%s: ERROR: Register (%02x:%02x) write failed (%d).\n", + __func__, (u8)AB8500_AUDIO, (u8)reg, status); + else + dev_dbg(codec->dev, + "%s: Wrote 0x%02x into register %02x:%02x\n", + __func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg); + + return status; +} + +/* + * Controls - DAPM + */ + +/* Earpiece */ + +/* Earpiece source selector */ +static const char * const enum_ear_lineout_source[] = {"Headset Left", + "Speaker Left"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ear_lineout_source, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DA3TOEAR, enum_ear_lineout_source); +static const struct snd_kcontrol_new dapm_ear_lineout_source = + SOC_DAPM_ENUM("Earpiece or LineOut Mono Source", + dapm_enum_ear_lineout_source); + +/* LineOut */ + +/* LineOut source selector */ +static const char * const enum_lineout_source[] = {"Mono Path", "Stereo Path"}; +static SOC_ENUM_DOUBLE_DECL(dapm_enum_lineout_source, AB8500_ANACONF5, + AB8500_ANACONF5_HSLDACTOLOL, + AB8500_ANACONF5_HSRDACTOLOR, enum_lineout_source); +static const struct snd_kcontrol_new dapm_lineout_source[] = { + SOC_DAPM_ENUM("LineOut Source", dapm_enum_lineout_source), +}; + +/* Handsfree */ + +/* Speaker Left - ANC selector */ +static const char * const enum_HFx_sel[] = {"Audio Path", "ANC"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_HFl_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_HFLSEL, enum_HFx_sel); +static const struct snd_kcontrol_new dapm_HFl_select[] = { + SOC_DAPM_ENUM("Speaker Left Source", dapm_enum_HFl_sel), +}; + +/* Speaker Right - ANC selector */ +static SOC_ENUM_SINGLE_DECL(dapm_enum_HFr_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_HFRSEL, enum_HFx_sel); +static const struct snd_kcontrol_new dapm_HFr_select[] = { + SOC_DAPM_ENUM("Speaker Right Source", dapm_enum_HFr_sel), +}; + +/* Mic 1 */ + +/* Mic 1 - Mic 1a or 1b selector */ +static const char * const enum_mic1ab_sel[] = {"Mic 1b", "Mic 1a"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_mic1ab_sel, AB8500_ANACONF3, + AB8500_ANACONF3_MIC1SEL, enum_mic1ab_sel); +static const struct snd_kcontrol_new dapm_mic1ab_mux[] = { + SOC_DAPM_ENUM("Mic 1a or 1b Select", dapm_enum_mic1ab_sel), +}; + +/* Mic 1 - AD3 - Mic 1 or DMic 3 selector */ +static const char * const enum_ad3_sel[] = {"Mic 1", "DMic 3"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad3_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD3SEL, enum_ad3_sel); +static const struct snd_kcontrol_new dapm_ad3_select[] = { + SOC_DAPM_ENUM("AD3 Source Select", dapm_enum_ad3_sel), +}; + +/* Mic 1 - AD6 - Mic 1 or DMic 6 selector */ +static const char * const enum_ad6_sel[] = {"Mic 1", "DMic 6"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad6_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD6SEL, enum_ad6_sel); +static const struct snd_kcontrol_new dapm_ad6_select[] = { + SOC_DAPM_ENUM("AD6 Source Select", dapm_enum_ad6_sel), +}; + +/* Mic 2 */ + +/* Mic 2 - AD5 - Mic 2 or DMic 5 selector */ +static const char * const enum_ad5_sel[] = {"Mic 2", "DMic 5"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad5_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD5SEL, enum_ad5_sel); +static const struct snd_kcontrol_new dapm_ad5_select[] = { + SOC_DAPM_ENUM("AD5 Source Select", dapm_enum_ad5_sel), +}; + +/* LineIn */ + +/* LineIn left - AD1 - LineIn Left or DMic 1 selector */ +static const char * const enum_ad1_sel[] = {"LineIn Left", "DMic 1"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad1_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD1SEL, enum_ad1_sel); +static const struct snd_kcontrol_new dapm_ad1_select[] = { + SOC_DAPM_ENUM("AD1 Source Select", dapm_enum_ad1_sel), +}; + +/* LineIn right - Mic 2 or LineIn Right selector */ +static const char * const enum_mic2lr_sel[] = {"Mic 2", "LineIn Right"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_mic2lr_sel, AB8500_ANACONF3, + AB8500_ANACONF3_LINRSEL, enum_mic2lr_sel); +static const struct snd_kcontrol_new dapm_mic2lr_select[] = { + SOC_DAPM_ENUM("Mic 2 or LINR Select", dapm_enum_mic2lr_sel), +}; + +/* LineIn right - AD2 - LineIn Right or DMic2 selector */ +static const char * const enum_ad2_sel[] = {"LineIn Right", "DMic 2"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad2_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD2SEL, enum_ad2_sel); +static const struct snd_kcontrol_new dapm_ad2_select[] = { + SOC_DAPM_ENUM("AD2 Source Select", dapm_enum_ad2_sel), +}; + + +/* ANC */ + +static const char * const enum_anc_in_sel[] = {"Mic 1 / DMic 6", + "Mic 2 / DMic 5"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_anc_in_sel, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_ANCINSEL, enum_anc_in_sel); +static const struct snd_kcontrol_new dapm_anc_in_select[] = { + SOC_DAPM_ENUM("ANC Source", dapm_enum_anc_in_sel), +}; + +/* ANC - Enable/Disable */ +static const struct snd_kcontrol_new dapm_anc_enable[] = { + SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1, + AB8500_ANCCONF1_ENANC, 0, 0), +}; + +/* ANC to Earpiece - Mute */ +static const struct snd_kcontrol_new dapm_anc_ear_mute[] = { + SOC_DAPM_SINGLE("Switch", AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_ANCSEL, 1, 0), +}; + + + +/* Sidetone left */ + +/* Sidetone left - Input selector */ +static const char * const enum_stfir1_in_sel[] = { + "LineIn Left", "LineIn Right", "Mic 1", "Headset Left" +}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir1_in_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_FIRSID1SEL, enum_stfir1_in_sel); +static const struct snd_kcontrol_new dapm_stfir1_in_select[] = { + SOC_DAPM_ENUM("Sidetone Left Source", dapm_enum_stfir1_in_sel), +}; + +/* Sidetone right path */ + +/* Sidetone right - Input selector */ +static const char * const enum_stfir2_in_sel[] = { + "LineIn Right", "Mic 1", "DMic 4", "Headset Right" +}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir2_in_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_FIRSID2SEL, enum_stfir2_in_sel); +static const struct snd_kcontrol_new dapm_stfir2_in_select[] = { + SOC_DAPM_ENUM("Sidetone Right Source", dapm_enum_stfir2_in_sel), +}; + +/* Vibra */ + +static const char * const enum_pwm2vibx[] = {"Audio Path", "PWM Generator"}; + +static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib1, AB8500_PWMGENCONF1, + AB8500_PWMGENCONF1_PWMTOVIB1, enum_pwm2vibx); + +static const struct snd_kcontrol_new dapm_pwm2vib1[] = { + SOC_DAPM_ENUM("Vibra 1 Controller", dapm_enum_pwm2vib1), +}; + +static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib2, AB8500_PWMGENCONF1, + AB8500_PWMGENCONF1_PWMTOVIB2, enum_pwm2vibx); + +static const struct snd_kcontrol_new dapm_pwm2vib2[] = { + SOC_DAPM_ENUM("Vibra 2 Controller", dapm_enum_pwm2vib2), +}; + +/* + * DAPM-widgets + */ + +static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { + + /* Clocks */ + SND_SOC_DAPM_CLOCK_SUPPLY("audioclk"), + + /* Regulators */ + SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0), + + /* Power */ + SND_SOC_DAPM_SUPPLY("Audio Power", + AB8500_POWERUP, AB8500_POWERUP_POWERUP, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Audio Analog Power", + AB8500_POWERUP, AB8500_POWERUP_ENANA, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Main supply node */ + SND_SOC_DAPM_SUPPLY("Main Supply", SND_SOC_NOPM, 0, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DA/AD */ + + SND_SOC_DAPM_INPUT("ADC Input"), + SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN5", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN6", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT57", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT68", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Headset path */ + + SND_SOC_DAPM_SUPPLY("Charge Pump", AB8500_ANACONF5, + AB8500_ANACONF5_ENCPHS, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DA1 Enable", "ab8500_0p", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA1, 0), + SND_SOC_DAPM_DAC("DA2 Enable", "ab8500_0p", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA2, 0), + + SND_SOC_DAPM_PGA("HSL Digital Volume", SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_PGA("HSR Digital Volume", SND_SOC_NOPM, 0, 0, + NULL, 0), + + SND_SOC_DAPM_DAC("HSL DAC", "ab8500_0p", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSL, 0), + SND_SOC_DAPM_DAC("HSR DAC", "ab8500_0p", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSR, 0), + SND_SOC_DAPM_MIXER("HSL DAC Mute", AB8500_MUTECONF, + AB8500_MUTECONF_MUTDACHSL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("HSR DAC Mute", AB8500_MUTECONF, + AB8500_MUTECONF_MUTDACHSR, 1, + NULL, 0), + SND_SOC_DAPM_DAC("HSL DAC Driver", "ab8500_0p", + AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSL, 0), + SND_SOC_DAPM_DAC("HSR DAC Driver", "ab8500_0p", + AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSR, 0), + + SND_SOC_DAPM_MIXER("HSL Mute", + AB8500_MUTECONF, AB8500_MUTECONF_MUTHSL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("HSR Mute", + AB8500_MUTECONF, AB8500_MUTECONF_MUTHSR, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("HSL Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHSL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HSR Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHSR, 0, + NULL, 0), + SND_SOC_DAPM_PGA("HSL Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_PGA("HSR Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Headset Left"), + SND_SOC_DAPM_OUTPUT("Headset Right"), + + /* LineOut path */ + + SND_SOC_DAPM_MUX("LineOut Source", + SND_SOC_NOPM, 0, 0, dapm_lineout_source), + + SND_SOC_DAPM_MIXER("LOL Disable HFL", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("LOR Disable HFR", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 1, + NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Enable", + AB8500_ANACONF5, AB8500_ANACONF5_ENLOL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LOR Enable", + AB8500_ANACONF5, AB8500_ANACONF5_ENLOR, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("LineOut Left"), + SND_SOC_DAPM_OUTPUT("LineOut Right"), + + /* Earpiece path */ + + SND_SOC_DAPM_MUX("Earpiece or LineOut Mono Source", + SND_SOC_NOPM, 0, 0, &dapm_ear_lineout_source), + SND_SOC_DAPM_MIXER("EAR DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACEAR, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("EAR Mute", + AB8500_MUTECONF, AB8500_MUTECONF_MUTEAR, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("EAR Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENEAR, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Earpiece"), + + /* Handsfree path */ + + SND_SOC_DAPM_MIXER("DA3 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA3, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA4 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA4, 0, + NULL, 0), + SND_SOC_DAPM_MUX("Speaker Left Source", + SND_SOC_NOPM, 0, 0, dapm_HFl_select), + SND_SOC_DAPM_MUX("Speaker Right Source", + SND_SOC_NOPM, 0, 0, dapm_HFr_select), + SND_SOC_DAPM_MIXER("HFL DAC", AB8500_DAPATHCONF, + AB8500_DAPATHCONF_ENDACHFL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HFR DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHFR, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA4 or ANC path to HfR", + AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFREN, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA3 or ANC path to HfL", + AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFLEN, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HFL Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HFR Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Speaker Left"), + SND_SOC_DAPM_OUTPUT("Speaker Right"), + + /* Vibrator path */ + + SND_SOC_DAPM_INPUT("PWMGEN1"), + SND_SOC_DAPM_INPUT("PWMGEN2"), + + SND_SOC_DAPM_MIXER("DA5 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA5, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA6 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA6, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("VIB1 DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("VIB2 DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB2, 0, + NULL, 0), + SND_SOC_DAPM_MUX("Vibra 1 Controller", + SND_SOC_NOPM, 0, 0, dapm_pwm2vib1), + SND_SOC_DAPM_MUX("Vibra 2 Controller", + SND_SOC_NOPM, 0, 0, dapm_pwm2vib2), + SND_SOC_DAPM_MIXER("VIB1 Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENVIB1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("VIB2 Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENVIB2, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Vibra 1"), + SND_SOC_DAPM_OUTPUT("Vibra 2"), + + /* Mic 1 */ + + SND_SOC_DAPM_INPUT("Mic 1"), + + SND_SOC_DAPM_MUX("Mic 1a or 1b Select", + SND_SOC_NOPM, 0, 0, dapm_mic1ab_mux), + SND_SOC_DAPM_MIXER("MIC1 Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC1, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC1A V-AMICx Enable", + AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC1B V-AMICx Enable", + AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC1 ADC", + AB8500_ANACONF3, AB8500_ANACONF3_ENADCMIC, 0, + NULL, 0), + SND_SOC_DAPM_MUX("AD3 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad3_select), + SND_SOC_DAPM_MIXER("AD3 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD3 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0, + NULL, 0), + + /* Mic 2 */ + + SND_SOC_DAPM_INPUT("Mic 2"), + + SND_SOC_DAPM_MIXER("MIC2 Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC2, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC2 V-AMICx Enable", AB8500_ANACONF2, + AB8500_ANACONF2_ENMIC2, 0, + NULL, 0), + + /* LineIn */ + + SND_SOC_DAPM_INPUT("LineIn Left"), + SND_SOC_DAPM_INPUT("LineIn Right"), + + SND_SOC_DAPM_MIXER("LINL Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTLINL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTLINR, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("LINL Enable", AB8500_ANACONF2, + AB8500_ANACONF2_ENLINL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR Enable", AB8500_ANACONF2, + AB8500_ANACONF2_ENLINR, 0, + NULL, 0), + + /* LineIn Bypass path */ + SND_SOC_DAPM_MIXER("LINL to HSL Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR to HSR Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + + /* LineIn, Mic 2 */ + SND_SOC_DAPM_MUX("Mic 2 or LINR Select", + SND_SOC_NOPM, 0, 0, dapm_mic2lr_select), + SND_SOC_DAPM_MIXER("LINL ADC", AB8500_ANACONF3, + AB8500_ANACONF3_ENADCLINL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR ADC", AB8500_ANACONF3, + AB8500_ANACONF3_ENADCLINR, 0, + NULL, 0), + SND_SOC_DAPM_MUX("AD1 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad1_select), + SND_SOC_DAPM_MUX("AD2 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad2_select), + SND_SOC_DAPM_MIXER("AD1 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD2 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + + SND_SOC_DAPM_MIXER("AD12 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0, + NULL, 0), + + /* HD Capture path */ + + SND_SOC_DAPM_MUX("AD5 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad5_select), + SND_SOC_DAPM_MUX("AD6 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad6_select), + SND_SOC_DAPM_MIXER("AD5 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD6 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD57 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD68 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0, + NULL, 0), + + /* Digital Microphone path */ + + SND_SOC_DAPM_INPUT("DMic 1"), + SND_SOC_DAPM_INPUT("DMic 2"), + SND_SOC_DAPM_INPUT("DMic 3"), + SND_SOC_DAPM_INPUT("DMic 4"), + SND_SOC_DAPM_INPUT("DMic 5"), + SND_SOC_DAPM_INPUT("DMic 6"), + + SND_SOC_DAPM_MIXER("DMIC1", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC2", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC3", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC4", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC5", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC6", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD4 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD4 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, + 0, NULL, 0), + + /* Acoustical Noise Cancellation path */ + + SND_SOC_DAPM_INPUT("ANC Configure Input"), + SND_SOC_DAPM_OUTPUT("ANC Configure Output"), + + SND_SOC_DAPM_MUX("ANC Source", + SND_SOC_NOPM, 0, 0, + dapm_anc_in_select), + SND_SOC_DAPM_SWITCH("ANC", + SND_SOC_NOPM, 0, 0, + dapm_anc_enable), + SND_SOC_DAPM_SWITCH("ANC to Earpiece", + SND_SOC_NOPM, 0, 0, + dapm_anc_ear_mute), + + /* Sidetone Filter path */ + + SND_SOC_DAPM_MUX("Sidetone Left Source", + SND_SOC_NOPM, 0, 0, + dapm_stfir1_in_select), + SND_SOC_DAPM_MUX("Sidetone Right Source", + SND_SOC_NOPM, 0, 0, + dapm_stfir2_in_select), + SND_SOC_DAPM_MIXER("STFIR1 Control", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("STFIR2 Control", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("STFIR1 Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("STFIR2 Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), +}; + +/* + * DAPM-routes + */ +static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { + /* Power AB8500 audio-block when AD/DA is active */ + {"Main Supply", NULL, "V-AUD"}, + {"Main Supply", NULL, "audioclk"}, + {"Main Supply", NULL, "Audio Power"}, + {"Main Supply", NULL, "Audio Analog Power"}, + + {"DAC", NULL, "ab8500_0p"}, + {"DAC", NULL, "Main Supply"}, + {"ADC", NULL, "ab8500_0c"}, + {"ADC", NULL, "Main Supply"}, + + /* ANC Configure */ + {"ANC Configure Input", NULL, "Main Supply"}, + {"ANC Configure Output", NULL, "ANC Configure Input"}, + + /* AD/DA */ + {"ADC", NULL, "ADC Input"}, + {"DAC Output", NULL, "DAC"}, + + /* Powerup charge pump if DA1/2 is in use */ + + {"DA_IN1", NULL, "ab8500_0p"}, + {"DA_IN1", NULL, "Charge Pump"}, + {"DA_IN2", NULL, "ab8500_0p"}, + {"DA_IN2", NULL, "Charge Pump"}, + + /* Headset path */ + + {"DA1 Enable", NULL, "DA_IN1"}, + {"DA2 Enable", NULL, "DA_IN2"}, + + {"HSL Digital Volume", NULL, "DA1 Enable"}, + {"HSR Digital Volume", NULL, "DA2 Enable"}, + + {"HSL DAC", NULL, "HSL Digital Volume"}, + {"HSR DAC", NULL, "HSR Digital Volume"}, + + {"HSL DAC Mute", NULL, "HSL DAC"}, + {"HSR DAC Mute", NULL, "HSR DAC"}, + + {"HSL DAC Driver", NULL, "HSL DAC Mute"}, + {"HSR DAC Driver", NULL, "HSR DAC Mute"}, + + {"HSL Mute", NULL, "HSL DAC Driver"}, + {"HSR Mute", NULL, "HSR DAC Driver"}, + + {"HSL Enable", NULL, "HSL Mute"}, + {"HSR Enable", NULL, "HSR Mute"}, + + {"HSL Volume", NULL, "HSL Enable"}, + {"HSR Volume", NULL, "HSR Enable"}, + + {"Headset Left", NULL, "HSL Volume"}, + {"Headset Right", NULL, "HSR Volume"}, + + /* HF or LineOut path */ + + {"DA_IN3", NULL, "ab8500_0p"}, + {"DA3 Channel Volume", NULL, "DA_IN3"}, + {"DA_IN4", NULL, "ab8500_0p"}, + {"DA4 Channel Volume", NULL, "DA_IN4"}, + + {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"}, + {"Speaker Right Source", "Audio Path", "DA4 Channel Volume"}, + + {"DA3 or ANC path to HfL", NULL, "Speaker Left Source"}, + {"DA4 or ANC path to HfR", NULL, "Speaker Right Source"}, + + /* HF path */ + + {"HFL DAC", NULL, "DA3 or ANC path to HfL"}, + {"HFR DAC", NULL, "DA4 or ANC path to HfR"}, + + {"HFL Enable", NULL, "HFL DAC"}, + {"HFR Enable", NULL, "HFR DAC"}, + + {"Speaker Left", NULL, "HFL Enable"}, + {"Speaker Right", NULL, "HFR Enable"}, + + /* Earpiece path */ + + {"Earpiece or LineOut Mono Source", "Headset Left", + "HSL Digital Volume"}, + {"Earpiece or LineOut Mono Source", "Speaker Left", + "DA3 or ANC path to HfL"}, + + {"EAR DAC", NULL, "Earpiece or LineOut Mono Source"}, + + {"EAR Mute", NULL, "EAR DAC"}, + + {"EAR Enable", NULL, "EAR Mute"}, + + {"Earpiece", NULL, "EAR Enable"}, + + /* LineOut path stereo */ + + {"LineOut Source", "Stereo Path", "HSL DAC Driver"}, + {"LineOut Source", "Stereo Path", "HSR DAC Driver"}, + + /* LineOut path mono */ + + {"LineOut Source", "Mono Path", "EAR DAC"}, + + /* LineOut path */ + + {"LOL Disable HFL", NULL, "LineOut Source"}, + {"LOR Disable HFR", NULL, "LineOut Source"}, + + {"LOL Enable", NULL, "LOL Disable HFL"}, + {"LOR Enable", NULL, "LOR Disable HFR"}, + + {"LineOut Left", NULL, "LOL Enable"}, + {"LineOut Right", NULL, "LOR Enable"}, + + /* Vibrator path */ + + {"DA_IN5", NULL, "ab8500_0p"}, + {"DA5 Channel Volume", NULL, "DA_IN5"}, + {"DA_IN6", NULL, "ab8500_0p"}, + {"DA6 Channel Volume", NULL, "DA_IN6"}, + + {"VIB1 DAC", NULL, "DA5 Channel Volume"}, + {"VIB2 DAC", NULL, "DA6 Channel Volume"}, + + {"Vibra 1 Controller", "Audio Path", "VIB1 DAC"}, + {"Vibra 2 Controller", "Audio Path", "VIB2 DAC"}, + {"Vibra 1 Controller", "PWM Generator", "PWMGEN1"}, + {"Vibra 2 Controller", "PWM Generator", "PWMGEN2"}, + + {"VIB1 Enable", NULL, "Vibra 1 Controller"}, + {"VIB2 Enable", NULL, "Vibra 2 Controller"}, + + {"Vibra 1", NULL, "VIB1 Enable"}, + {"Vibra 2", NULL, "VIB2 Enable"}, + + + /* Mic 2 */ + + {"MIC2 V-AMICx Enable", NULL, "Mic 2"}, + + /* LineIn */ + {"LINL Mute", NULL, "LineIn Left"}, + {"LINR Mute", NULL, "LineIn Right"}, + + {"LINL Enable", NULL, "LINL Mute"}, + {"LINR Enable", NULL, "LINR Mute"}, + + /* LineIn, Mic 2 */ + {"Mic 2 or LINR Select", "LineIn Right", "LINR Enable"}, + {"Mic 2 or LINR Select", "Mic 2", "MIC2 V-AMICx Enable"}, + + {"LINL ADC", NULL, "LINL Enable"}, + {"LINR ADC", NULL, "Mic 2 or LINR Select"}, + + {"AD1 Source Select", "LineIn Left", "LINL ADC"}, + {"AD2 Source Select", "LineIn Right", "LINR ADC"}, + + {"AD1 Channel Volume", NULL, "AD1 Source Select"}, + {"AD2 Channel Volume", NULL, "AD2 Source Select"}, + + {"AD12 Enable", NULL, "AD1 Channel Volume"}, + {"AD12 Enable", NULL, "AD2 Channel Volume"}, + + {"AD_OUT1", NULL, "ab8500_0c"}, + {"AD_OUT1", NULL, "AD12 Enable"}, + {"AD_OUT2", NULL, "ab8500_0c"}, + {"AD_OUT2", NULL, "AD12 Enable"}, + + /* Mic 1 */ + + {"MIC1 Mute", NULL, "Mic 1"}, + + {"MIC1A V-AMICx Enable", NULL, "MIC1 Mute"}, + {"MIC1B V-AMICx Enable", NULL, "MIC1 Mute"}, + + {"Mic 1a or 1b Select", "Mic 1a", "MIC1A V-AMICx Enable"}, + {"Mic 1a or 1b Select", "Mic 1b", "MIC1B V-AMICx Enable"}, + + {"MIC1 ADC", NULL, "Mic 1a or 1b Select"}, + + {"AD3 Source Select", "Mic 1", "MIC1 ADC"}, + + {"AD3 Channel Volume", NULL, "AD3 Source Select"}, + + {"AD3 Enable", NULL, "AD3 Channel Volume"}, + + {"AD_OUT3", NULL, "ab8500_0c"}, + {"AD_OUT3", NULL, "AD3 Enable"}, + + /* HD Capture path */ + + {"AD5 Source Select", "Mic 2", "LINR ADC"}, + {"AD6 Source Select", "Mic 1", "MIC1 ADC"}, + + {"AD5 Channel Volume", NULL, "AD5 Source Select"}, + {"AD6 Channel Volume", NULL, "AD6 Source Select"}, + + {"AD57 Enable", NULL, "AD5 Channel Volume"}, + {"AD68 Enable", NULL, "AD6 Channel Volume"}, + + {"AD_OUT57", NULL, "ab8500_0c"}, + {"AD_OUT57", NULL, "AD57 Enable"}, + {"AD_OUT68", NULL, "ab8500_0c"}, + {"AD_OUT68", NULL, "AD68 Enable"}, + + /* Digital Microphone path */ + + {"DMic 1", NULL, "V-DMIC"}, + {"DMic 2", NULL, "V-DMIC"}, + {"DMic 3", NULL, "V-DMIC"}, + {"DMic 4", NULL, "V-DMIC"}, + {"DMic 5", NULL, "V-DMIC"}, + {"DMic 6", NULL, "V-DMIC"}, + + {"AD1 Source Select", NULL, "DMic 1"}, + {"AD2 Source Select", NULL, "DMic 2"}, + {"AD3 Source Select", NULL, "DMic 3"}, + {"AD5 Source Select", NULL, "DMic 5"}, + {"AD6 Source Select", NULL, "DMic 6"}, + + {"AD4 Channel Volume", NULL, "DMic 4"}, + {"AD4 Enable", NULL, "AD4 Channel Volume"}, + + {"AD_OUT4", NULL, "ab8500_0c"}, + {"AD_OUT4", NULL, "AD4 Enable"}, + + /* LineIn Bypass path */ + + {"LINL to HSL Volume", NULL, "LINL Enable"}, + {"LINR to HSR Volume", NULL, "LINR Enable"}, + + {"HSL DAC Driver", NULL, "LINL to HSL Volume"}, + {"HSR DAC Driver", NULL, "LINR to HSR Volume"}, + + /* ANC path (Acoustic Noise Cancellation) */ + + {"ANC Source", "Mic 2 / DMic 5", "AD5 Channel Volume"}, + {"ANC Source", "Mic 1 / DMic 6", "AD6 Channel Volume"}, + + {"ANC", "Switch", "ANC Source"}, + + {"Speaker Left Source", "ANC", "ANC"}, + {"Speaker Right Source", "ANC", "ANC"}, + {"ANC to Earpiece", "Switch", "ANC"}, + + {"HSL Digital Volume", NULL, "ANC to Earpiece"}, + + /* Sidetone Filter path */ + + {"Sidetone Left Source", "LineIn Left", "AD12 Enable"}, + {"Sidetone Left Source", "LineIn Right", "AD12 Enable"}, + {"Sidetone Left Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Left Source", "Headset Left", "DA_IN1"}, + {"Sidetone Right Source", "LineIn Right", "AD12 Enable"}, + {"Sidetone Right Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Right Source", "DMic 4", "AD4 Enable"}, + {"Sidetone Right Source", "Headset Right", "DA_IN2"}, + + {"STFIR1 Control", NULL, "Sidetone Left Source"}, + {"STFIR2 Control", NULL, "Sidetone Right Source"}, + + {"STFIR1 Volume", NULL, "STFIR1 Control"}, + {"STFIR2 Volume", NULL, "STFIR2 Control"}, + + {"DA1 Enable", NULL, "STFIR1 Volume"}, + {"DA2 Enable", NULL, "STFIR2 Volume"}, +}; + +static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1a_vamicx[] = { + {"MIC1A V-AMICx Enable", NULL, "V-AMIC1"}, + {"MIC1A V-AMICx Enable", NULL, "V-AMIC2"}, +}; + +static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1b_vamicx[] = { + {"MIC1B V-AMICx Enable", NULL, "V-AMIC1"}, + {"MIC1B V-AMICx Enable", NULL, "V-AMIC2"}, +}; + +static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { + {"MIC2 V-AMICx Enable", NULL, "V-AMIC1"}, + {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, +}; + +/* ANC FIR-coefficients configuration sequence */ +static void anc_fir(struct snd_soc_codec *codec, + unsigned int bnk, unsigned int par, unsigned int val) +{ + if (par == 0 && bnk == 0) + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCFIRUPDATE), + BIT(AB8500_ANCCONF1_ANCFIRUPDATE)); + + snd_soc_write(codec, AB8500_ANCCONF5, val >> 8 & 0xff); + snd_soc_write(codec, AB8500_ANCCONF6, val & 0xff); + + if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1) + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0); +} + +/* ANC IIR-coefficients configuration sequence */ +static void anc_iir(struct snd_soc_codec *codec, unsigned int bnk, + unsigned int par, unsigned int val) +{ + if (par == 0) { + if (bnk == 0) { + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRINIT), + BIT(AB8500_ANCCONF1_ANCIIRINIT)); + usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY); + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRINIT), 0); + usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY); + } else { + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRUPDATE), + BIT(AB8500_ANCCONF1_ANCIIRUPDATE)); + } + } else if (par > 3) { + snd_soc_write(codec, AB8500_ANCCONF7, 0); + snd_soc_write(codec, AB8500_ANCCONF8, val >> 16 & 0xff); + } + + snd_soc_write(codec, AB8500_ANCCONF7, val >> 8 & 0xff); + snd_soc_write(codec, AB8500_ANCCONF8, val & 0xff); + + if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1) + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0); +} + +/* ANC IIR-/FIR-coefficients configuration sequence */ +static void anc_configure(struct snd_soc_codec *codec, + bool apply_fir, bool apply_iir) +{ + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); + unsigned int bnk, par, val; + + dev_dbg(codec->dev, "%s: Enter.\n", __func__); + + if (apply_fir) + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ENANC), 0); + + snd_soc_update_bits(codec, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC)); + + if (apply_fir) + for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++) + for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) { + val = snd_soc_read(codec, + drvdata->anc_fir_values[par]); + anc_fir(codec, bnk, par, val); + } + + if (apply_iir) + for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++) + for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) { + val = snd_soc_read(codec, + drvdata->anc_iir_values[par]); + anc_iir(codec, bnk, par, val); + } + + dev_dbg(codec->dev, "%s: Exit.\n", __func__); +} + +/* + * Control-events + */ + +static int sid_status_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); + + mutex_lock(&codec->mutex); + ucontrol->value.integer.value[0] = drvdata->sid_status; + mutex_unlock(&codec->mutex); + + return 0; +} + +/* Write sidetone FIR-coefficients configuration sequence */ +static int sid_status_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); + unsigned int param, sidconf, val; + int status = 1; + + dev_dbg(codec->dev, "%s: Enter\n", __func__); + + if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) { + dev_err(codec->dev, + "%s: ERROR: This control supports '%s' only!\n", + __func__, enum_sid_state[SID_APPLY_FIR]); + return -EIO; + } + + mutex_lock(&codec->mutex); + + sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF); + if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { + if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { + dev_err(codec->dev, "%s: Sidetone busy while off!\n", + __func__); + status = -EPERM; + } else { + status = -EBUSY; + } + goto out; + } + + snd_soc_write(codec, AB8500_SIDFIRADR, 0); + + for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { + val = snd_soc_read(codec, drvdata->sid_fir_values[param]); + snd_soc_write(codec, AB8500_SIDFIRCOEF1, val >> 8 & 0xff); + snd_soc_write(codec, AB8500_SIDFIRCOEF2, val & 0xff); + } + + snd_soc_update_bits(codec, AB8500_SIDFIRADR, + BIT(AB8500_SIDFIRADR_FIRSIDSET), + BIT(AB8500_SIDFIRADR_FIRSIDSET)); + snd_soc_update_bits(codec, AB8500_SIDFIRADR, + BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); + + drvdata->sid_status = SID_FIR_CONFIGURED; + +out: + mutex_unlock(&codec->mutex); + + dev_dbg(codec->dev, "%s: Exit\n", __func__); + + return status; +} + +static int anc_status_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); + + mutex_lock(&codec->mutex); + ucontrol->value.integer.value[0] = drvdata->anc_status; + mutex_unlock(&codec->mutex); + + return 0; +} + +static int anc_status_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); + struct device *dev = codec->dev; + bool apply_fir, apply_iir; + int req, status; + + dev_dbg(dev, "%s: Enter.\n", __func__); + + mutex_lock(&drvdata->anc_lock); + + req = ucontrol->value.integer.value[0]; + if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR && + req != ANC_APPLY_IIR) { + dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n", + __func__, enum_anc_state[req]); + status = -EINVAL; + goto cleanup; + } + apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR; + apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR; + + status = snd_soc_dapm_force_enable_pin(&codec->dapm, + "ANC Configure Input"); + if (status < 0) { + dev_err(dev, + "%s: ERROR: Failed to enable power (status = %d)!\n", + __func__, status); + goto cleanup; + } + snd_soc_dapm_sync(&codec->dapm); + + mutex_lock(&codec->mutex); + anc_configure(codec, apply_fir, apply_iir); + mutex_unlock(&codec->mutex); + + if (apply_fir) { + if (drvdata->anc_status == ANC_IIR_CONFIGURED) + drvdata->anc_status = ANC_FIR_IIR_CONFIGURED; + else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED) + drvdata->anc_status = ANC_FIR_CONFIGURED; + } + if (apply_iir) { + if (drvdata->anc_status == ANC_FIR_CONFIGURED) + drvdata->anc_status = ANC_FIR_IIR_CONFIGURED; + else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED) + drvdata->anc_status = ANC_IIR_CONFIGURED; + } + + status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); + snd_soc_dapm_sync(&codec->dapm); + +cleanup: + mutex_unlock(&drvdata->anc_lock); + + if (status < 0) + dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n", + __func__, status); + + dev_dbg(dev, "%s: Exit.\n", __func__); + + return (status < 0) ? status : 1; +} + +static int filter_control_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct filter_control *fc = + (struct filter_control *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = fc->count; + uinfo->value.integer.min = fc->min; + uinfo->value.integer.max = fc->max; + + return 0; +} + +static int filter_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct filter_control *fc = + (struct filter_control *)kcontrol->private_value; + unsigned int i; + + mutex_lock(&codec->mutex); + for (i = 0; i < fc->count; i++) + ucontrol->value.integer.value[i] = fc->value[i]; + mutex_unlock(&codec->mutex); + + return 0; +} + +static int filter_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct filter_control *fc = + (struct filter_control *)kcontrol->private_value; + unsigned int i; + + mutex_lock(&codec->mutex); + for (i = 0; i < fc->count; i++) + fc->value[i] = ucontrol->value.integer.value[i]; + mutex_unlock(&codec->mutex); + + return 0; +} + +/* + * Controls - Non-DAPM ASoC + */ + +static DECLARE_TLV_DB_SCALE(adx_dig_gain_tlv, -3200, 100, 1); +/* -32dB = Mute */ + +static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1); +/* -63dB = Mute */ + +static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1); +/* -1dB = Mute */ + +static const unsigned int hs_gain_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0), + 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0), +}; + +static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0); + +static DECLARE_TLV_DB_SCALE(lin_gain_tlv, -1000, 200, 0); + +static DECLARE_TLV_DB_SCALE(lin2hs_gain_tlv, -3800, 200, 1); +/* -38dB = Mute */ + +static const char * const enum_hsfadspeed[] = {"2ms", "0.5ms", "10.6ms", + "5ms"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_hsfadspeed, + AB8500_DIGMICCONF, AB8500_DIGMICCONF_HSFADSPEED, enum_hsfadspeed); + +static const char * const enum_envdetthre[] = { + "250mV", "300mV", "350mV", "400mV", + "450mV", "500mV", "550mV", "600mV", + "650mV", "700mV", "750mV", "800mV", + "850mV", "900mV", "950mV", "1.00V" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_envdeththre, + AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETHTHRE, enum_envdetthre); +static SOC_ENUM_SINGLE_DECL(soc_enum_envdetlthre, + AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETLTHRE, enum_envdetthre); +static const char * const enum_envdettime[] = { + "26.6us", "53.2us", "106us", "213us", + "426us", "851us", "1.70ms", "3.40ms", + "6.81ms", "13.6ms", "27.2ms", "54.5ms", + "109ms", "218ms", "436ms", "872ms" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_envdettime, + AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETTIME, enum_envdettime); + +static const char * const enum_sinc31[] = {"Sinc 3", "Sinc 1"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_hsesinc, AB8500_HSLEARDIGGAIN, + AB8500_HSLEARDIGGAIN_HSSINC1, enum_sinc31); + +static const char * const enum_fadespeed[] = {"1ms", "4ms", "8ms", "16ms"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_fadespeed, AB8500_HSRDIGGAIN, + AB8500_HSRDIGGAIN_FADESPEED, enum_fadespeed); + +/* Earpiece */ + +static const char * const enum_lowpow[] = {"Normal", "Low Power"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_eardaclowpow, AB8500_ANACONF1, + AB8500_ANACONF1_EARDACLOWPOW, enum_lowpow); +static SOC_ENUM_SINGLE_DECL(soc_enum_eardrvlowpow, AB8500_ANACONF1, + AB8500_ANACONF1_EARDRVLOWPOW, enum_lowpow); + +static const char * const enum_av_mode[] = {"Audio", "Voice"}; +static SOC_ENUM_DOUBLE_DECL(soc_enum_ad12voice, AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD1VOICE, AB8500_ADFILTCONF_AD2VOICE, enum_av_mode); +static SOC_ENUM_DOUBLE_DECL(soc_enum_ad34voice, AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD3VOICE, AB8500_ADFILTCONF_AD4VOICE, enum_av_mode); + +/* DA */ + +static SOC_ENUM_SINGLE_DECL(soc_enum_da12voice, + AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DA12VOICE, + enum_av_mode); +static SOC_ENUM_SINGLE_DECL(soc_enum_da34voice, + AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DA34VOICE, + enum_av_mode); +static SOC_ENUM_SINGLE_DECL(soc_enum_da56voice, + AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DA56VOICE, + enum_av_mode); + +static const char * const enum_da2hslr[] = {"Sidetone", "Audio Path"}; +static SOC_ENUM_DOUBLE_DECL(soc_enum_da2hslr, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_DATOHSLEN, + AB8500_DIGMULTCONF1_DATOHSREN, enum_da2hslr); + +static const char * const enum_sinc53[] = {"Sinc 5", "Sinc 3"}; +static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic12sinc, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DMIC1SINC3, + AB8500_DMICFILTCONF_DMIC2SINC3, enum_sinc53); +static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic34sinc, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DMIC3SINC3, + AB8500_DMICFILTCONF_DMIC4SINC3, enum_sinc53); +static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic56sinc, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DMIC5SINC3, + AB8500_DMICFILTCONF_DMIC6SINC3, enum_sinc53); + +/* Digital interface - DA from slot mapping */ +static const char * const enum_da_from_slot_map[] = {"SLOT0", + "SLOT1", + "SLOT2", + "SLOT3", + "SLOT4", + "SLOT5", + "SLOT6", + "SLOT7", + "SLOT8", + "SLOT9", + "SLOT10", + "SLOT11", + "SLOT12", + "SLOT13", + "SLOT14", + "SLOT15", + "SLOT16", + "SLOT17", + "SLOT18", + "SLOT19", + "SLOT20", + "SLOT21", + "SLOT22", + "SLOT23", + "SLOT24", + "SLOT25", + "SLOT26", + "SLOT27", + "SLOT28", + "SLOT29", + "SLOT30", + "SLOT31"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_da1slotmap, + AB8500_DASLOTCONF1, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da2slotmap, + AB8500_DASLOTCONF2, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da3slotmap, + AB8500_DASLOTCONF3, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da4slotmap, + AB8500_DASLOTCONF4, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da5slotmap, + AB8500_DASLOTCONF5, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da6slotmap, + AB8500_DASLOTCONF6, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da7slotmap, + AB8500_DASLOTCONF7, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da8slotmap, + AB8500_DASLOTCONF8, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); + +/* Digital interface - AD to slot mapping */ +static const char * const enum_ad_to_slot_map[] = {"AD_OUT1", + "AD_OUT2", + "AD_OUT3", + "AD_OUT4", + "AD_OUT5", + "AD_OUT6", + "AD_OUT7", + "AD_OUT8", + "zeroes", + "tristate"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map, + AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot1map, + AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot2map, + AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot3map, + AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot4map, + AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot5map, + AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot6map, + AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot7map, + AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot8map, + AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot9map, + AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot10map, + AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot11map, + AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot12map, + AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot13map, + AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot14map, + AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot15map, + AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot16map, + AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot17map, + AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot18map, + AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot19map, + AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot20map, + AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot21map, + AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot22map, + AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot23map, + AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot24map, + AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot25map, + AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot26map, + AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot27map, + AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot28map, + AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot29map, + AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot30map, + AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot31map, + AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); + +/* Digital interface - Burst mode */ +static const char * const enum_mask[] = {"Unmasked", "Masked"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomask, + AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOMASK, + enum_mask); +static const char * const enum_bitclk0[] = {"19_2_MHz", "38_4_MHz"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bfifo19m2, + AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFO19M2, + enum_bitclk0); +static const char * const enum_slavemaster[] = {"Slave", "Master"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, + enum_slavemaster); + +/* Sidetone */ +static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); + +/* ANC */ +static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state); + +static struct snd_kcontrol_new ab8500_ctrls[] = { + /* Charge pump */ + SOC_ENUM("Charge Pump High Threshold For Low Voltage", + soc_enum_envdeththre), + SOC_ENUM("Charge Pump Low Threshold For Low Voltage", + soc_enum_envdetlthre), + SOC_SINGLE("Charge Pump Envelope Detection Switch", + AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETCPEN, + 1, 0), + SOC_ENUM("Charge Pump Envelope Detection Decay Time", + soc_enum_envdettime), + + /* Headset */ + SOC_ENUM("Headset Mode", soc_enum_da12voice), + SOC_SINGLE("Headset High Pass Switch", + AB8500_ANACONF1, AB8500_ANACONF1_HSHPEN, + 1, 0), + SOC_SINGLE("Headset Low Power Switch", + AB8500_ANACONF1, AB8500_ANACONF1_HSLOWPOW, + 1, 0), + SOC_SINGLE("Headset DAC Low Power Switch", + AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW1, + 1, 0), + SOC_SINGLE("Headset DAC Drv Low Power Switch", + AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW0, + 1, 0), + SOC_ENUM("Headset Fade Speed", soc_enum_hsfadspeed), + SOC_ENUM("Headset Source", soc_enum_da2hslr), + SOC_ENUM("Headset Filter", soc_enum_hsesinc), + SOC_DOUBLE_R_TLV("Headset Master Volume", + AB8500_DADIGGAIN1, AB8500_DADIGGAIN2, + 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv), + SOC_DOUBLE_R_TLV("Headset Digital Volume", + AB8500_HSLEARDIGGAIN, AB8500_HSRDIGGAIN, + 0, AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX, 1, hs_ear_dig_gain_tlv), + SOC_DOUBLE_TLV("Headset Volume", + AB8500_ANAGAIN3, + AB8500_ANAGAIN3_HSLGAIN, AB8500_ANAGAIN3_HSRGAIN, + AB8500_ANAGAIN3_HSXGAIN_MAX, 1, hs_gain_tlv), + + /* Earpiece */ + SOC_ENUM("Earpiece DAC Mode", + soc_enum_eardaclowpow), + SOC_ENUM("Earpiece DAC Drv Mode", + soc_enum_eardrvlowpow), + + /* HandsFree */ + SOC_ENUM("HF Mode", soc_enum_da34voice), + SOC_SINGLE("HF and Headset Swap Switch", + AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_SWAPDA12_34, + 1, 0), + SOC_DOUBLE("HF Low EMI Mode Switch", + AB8500_CLASSDCONF1, + AB8500_CLASSDCONF1_HFLSWAPEN, AB8500_CLASSDCONF1_HFRSWAPEN, + 1, 0), + SOC_DOUBLE("HF FIR Bypass Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_FIRBYP0, AB8500_CLASSDCONF2_FIRBYP1, + 1, 0), + SOC_DOUBLE("HF High Volume Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_HIGHVOLEN0, AB8500_CLASSDCONF2_HIGHVOLEN1, + 1, 0), + SOC_SINGLE("HF L and R Bridge Switch", + AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLHF, + 1, 0), + SOC_DOUBLE_R_TLV("HF Master Volume", + AB8500_DADIGGAIN3, AB8500_DADIGGAIN4, + 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv), + + /* Vibra */ + SOC_DOUBLE("Vibra High Volume Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_HIGHVOLEN2, AB8500_CLASSDCONF2_HIGHVOLEN3, + 1, 0), + SOC_DOUBLE("Vibra Low EMI Mode Switch", + AB8500_CLASSDCONF1, + AB8500_CLASSDCONF1_VIB1SWAPEN, AB8500_CLASSDCONF1_VIB2SWAPEN, + 1, 0), + SOC_DOUBLE("Vibra FIR Bypass Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_FIRBYP2, AB8500_CLASSDCONF2_FIRBYP3, + 1, 0), + SOC_ENUM("Vibra Mode", soc_enum_da56voice), + SOC_DOUBLE_R("Vibra PWM Duty Cycle N", + AB8500_PWMGENCONF3, AB8500_PWMGENCONF5, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0), + SOC_DOUBLE_R("Vibra PWM Duty Cycle P", + AB8500_PWMGENCONF2, AB8500_PWMGENCONF4, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0), + SOC_SINGLE("Vibra 1 and 2 Bridge Switch", + AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLVIB, + 1, 0), + SOC_DOUBLE_R_TLV("Vibra Master Volume", + AB8500_DADIGGAIN5, AB8500_DADIGGAIN6, + 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv), + + /* HandsFree, Vibra */ + SOC_SINGLE("ClassD High Pass Volume", + AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHHPGAIN, + AB8500_CLASSDCONF3_DITHHPGAIN_MAX, 0), + SOC_SINGLE("ClassD White Volume", + AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHWGAIN, + AB8500_CLASSDCONF3_DITHWGAIN_MAX, 0), + + /* Mic 1, Mic 2, LineIn */ + SOC_DOUBLE_R_TLV("Mic Master Volume", + AB8500_ADDIGGAIN3, AB8500_ADDIGGAIN4, + 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv), + + /* Mic 1 */ + SOC_SINGLE_TLV("Mic 1", + AB8500_ANAGAIN1, + AB8500_ANAGAINX_MICXGAIN, + AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv), + SOC_SINGLE("Mic 1 Low Power Switch", + AB8500_ANAGAIN1, AB8500_ANAGAINX_LOWPOWMICX, + 1, 0), + + /* Mic 2 */ + SOC_DOUBLE("Mic High Pass Switch", + AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD3NH, AB8500_ADFILTCONF_AD4NH, + 1, 1), + SOC_ENUM("Mic Mode", soc_enum_ad34voice), + SOC_ENUM("Mic Filter", soc_enum_dmic34sinc), + SOC_SINGLE_TLV("Mic 2", + AB8500_ANAGAIN2, + AB8500_ANAGAINX_MICXGAIN, + AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv), + SOC_SINGLE("Mic 2 Low Power Switch", + AB8500_ANAGAIN2, AB8500_ANAGAINX_LOWPOWMICX, + 1, 0), + + /* LineIn */ + SOC_DOUBLE("LineIn High Pass Switch", + AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD1NH, AB8500_ADFILTCONF_AD2NH, + 1, 1), + SOC_ENUM("LineIn Filter", soc_enum_dmic12sinc), + SOC_ENUM("LineIn Mode", soc_enum_ad12voice), + SOC_DOUBLE_R_TLV("LineIn Master Volume", + AB8500_ADDIGGAIN1, AB8500_ADDIGGAIN2, + 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv), + SOC_DOUBLE_TLV("LineIn", + AB8500_ANAGAIN4, + AB8500_ANAGAIN4_LINLGAIN, AB8500_ANAGAIN4_LINRGAIN, + AB8500_ANAGAIN4_LINXGAIN_MAX, 0, lin_gain_tlv), + SOC_DOUBLE_R_TLV("LineIn to Headset Volume", + AB8500_DIGLINHSLGAIN, AB8500_DIGLINHSRGAIN, + AB8500_DIGLINHSXGAIN_LINTOHSXGAIN, + AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX, + 1, lin2hs_gain_tlv), + + /* DMic */ + SOC_ENUM("DMic Filter", soc_enum_dmic56sinc), + SOC_DOUBLE_R_TLV("DMic Master Volume", + AB8500_ADDIGGAIN5, AB8500_ADDIGGAIN6, + 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv), + + /* Digital gains */ + SOC_ENUM("Digital Gain Fade Speed", soc_enum_fadespeed), + + /* Analog loopback */ + SOC_DOUBLE_R_TLV("Analog Loopback Volume", + AB8500_ADDIGLOOPGAIN1, AB8500_ADDIGLOOPGAIN2, + 0, AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX, 1, dax_dig_gain_tlv), + + /* Digital interface - DA from slot mapping */ + SOC_ENUM("Digital Interface DA 1 From Slot Map", soc_enum_da1slotmap), + SOC_ENUM("Digital Interface DA 2 From Slot Map", soc_enum_da2slotmap), + SOC_ENUM("Digital Interface DA 3 From Slot Map", soc_enum_da3slotmap), + SOC_ENUM("Digital Interface DA 4 From Slot Map", soc_enum_da4slotmap), + SOC_ENUM("Digital Interface DA 5 From Slot Map", soc_enum_da5slotmap), + SOC_ENUM("Digital Interface DA 6 From Slot Map", soc_enum_da6slotmap), + SOC_ENUM("Digital Interface DA 7 From Slot Map", soc_enum_da7slotmap), + SOC_ENUM("Digital Interface DA 8 From Slot Map", soc_enum_da8slotmap), + + /* Digital interface - AD to slot mapping */ + SOC_ENUM("Digital Interface AD To Slot 0 Map", soc_enum_adslot0map), + SOC_ENUM("Digital Interface AD To Slot 1 Map", soc_enum_adslot1map), + SOC_ENUM("Digital Interface AD To Slot 2 Map", soc_enum_adslot2map), + SOC_ENUM("Digital Interface AD To Slot 3 Map", soc_enum_adslot3map), + SOC_ENUM("Digital Interface AD To Slot 4 Map", soc_enum_adslot4map), + SOC_ENUM("Digital Interface AD To Slot 5 Map", soc_enum_adslot5map), + SOC_ENUM("Digital Interface AD To Slot 6 Map", soc_enum_adslot6map), + SOC_ENUM("Digital Interface AD To Slot 7 Map", soc_enum_adslot7map), + SOC_ENUM("Digital Interface AD To Slot 8 Map", soc_enum_adslot8map), + SOC_ENUM("Digital Interface AD To Slot 9 Map", soc_enum_adslot9map), + SOC_ENUM("Digital Interface AD To Slot 10 Map", soc_enum_adslot10map), + SOC_ENUM("Digital Interface AD To Slot 11 Map", soc_enum_adslot11map), + SOC_ENUM("Digital Interface AD To Slot 12 Map", soc_enum_adslot12map), + SOC_ENUM("Digital Interface AD To Slot 13 Map", soc_enum_adslot13map), + SOC_ENUM("Digital Interface AD To Slot 14 Map", soc_enum_adslot14map), + SOC_ENUM("Digital Interface AD To Slot 15 Map", soc_enum_adslot15map), + SOC_ENUM("Digital Interface AD To Slot 16 Map", soc_enum_adslot16map), + SOC_ENUM("Digital Interface AD To Slot 17 Map", soc_enum_adslot17map), + SOC_ENUM("Digital Interface AD To Slot 18 Map", soc_enum_adslot18map), + SOC_ENUM("Digital Interface AD To Slot 19 Map", soc_enum_adslot19map), + SOC_ENUM("Digital Interface AD To Slot 20 Map", soc_enum_adslot20map), + SOC_ENUM("Digital Interface AD To Slot 21 Map", soc_enum_adslot21map), + SOC_ENUM("Digital Interface AD To Slot 22 Map", soc_enum_adslot22map), + SOC_ENUM("Digital Interface AD To Slot 23 Map", soc_enum_adslot23map), + SOC_ENUM("Digital Interface AD To Slot 24 Map", soc_enum_adslot24map), + SOC_ENUM("Digital Interface AD To Slot 25 Map", soc_enum_adslot25map), + SOC_ENUM("Digital Interface AD To Slot 26 Map", soc_enum_adslot26map), + SOC_ENUM("Digital Interface AD To Slot 27 Map", soc_enum_adslot27map), + SOC_ENUM("Digital Interface AD To Slot 28 Map", soc_enum_adslot28map), + SOC_ENUM("Digital Interface AD To Slot 29 Map", soc_enum_adslot29map), + SOC_ENUM("Digital Interface AD To Slot 30 Map", soc_enum_adslot30map), + SOC_ENUM("Digital Interface AD To Slot 31 Map", soc_enum_adslot31map), + + /* Digital interface - Loopback */ + SOC_SINGLE("Digital Interface AD 1 Loopback Switch", + AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DAI7TOADO1, + 1, 0), + SOC_SINGLE("Digital Interface AD 2 Loopback Switch", + AB8500_DASLOTCONF2, AB8500_DASLOTCONF2_DAI8TOADO2, + 1, 0), + SOC_SINGLE("Digital Interface AD 3 Loopback Switch", + AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DAI7TOADO3, + 1, 0), + SOC_SINGLE("Digital Interface AD 4 Loopback Switch", + AB8500_DASLOTCONF4, AB8500_DASLOTCONF4_DAI8TOADO4, + 1, 0), + SOC_SINGLE("Digital Interface AD 5 Loopback Switch", + AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DAI7TOADO5, + 1, 0), + SOC_SINGLE("Digital Interface AD 6 Loopback Switch", + AB8500_DASLOTCONF6, AB8500_DASLOTCONF6_DAI8TOADO6, + 1, 0), + SOC_SINGLE("Digital Interface AD 7 Loopback Switch", + AB8500_DASLOTCONF7, AB8500_DASLOTCONF7_DAI8TOADO7, + 1, 0), + SOC_SINGLE("Digital Interface AD 8 Loopback Switch", + AB8500_DASLOTCONF8, AB8500_DASLOTCONF8_DAI7TOADO8, + 1, 0), + + /* Digital interface - Burst FIFO */ + SOC_SINGLE("Digital Interface 0 FIFO Enable Switch", + AB8500_DIGIFCONF3, AB8500_DIGIFCONF3_IF0BFIFOEN, + 1, 0), + SOC_ENUM("Burst FIFO Mask", soc_enum_bfifomask), + SOC_ENUM("Burst FIFO Bit-clock Frequency", soc_enum_bfifo19m2), + SOC_SINGLE("Burst FIFO Threshold", + AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOINT_SHIFT, + AB8500_FIFOCONF1_BFIFOINT_MAX, 0), + SOC_SINGLE("Burst FIFO Length", + AB8500_FIFOCONF2, AB8500_FIFOCONF2_BFIFOTX_SHIFT, + AB8500_FIFOCONF2_BFIFOTX_MAX, 0), + SOC_SINGLE("Burst FIFO EOS Extra Slots", + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOEXSL_SHIFT, + AB8500_FIFOCONF3_BFIFOEXSL_MAX, 0), + SOC_SINGLE("Burst FIFO FS Extra Bit-clocks", + AB8500_FIFOCONF3, AB8500_FIFOCONF3_PREBITCLK0_SHIFT, + AB8500_FIFOCONF3_PREBITCLK0_MAX, 0), + SOC_ENUM("Burst FIFO Interface Mode", soc_enum_bfifomast), + + SOC_SINGLE("Burst FIFO Interface Switch", + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFORUN_SHIFT, + 1, 0), + SOC_SINGLE("Burst FIFO Switch Frame Number", + AB8500_FIFOCONF4, AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT, + AB8500_FIFOCONF4_BFIFOFRAMSW_MAX, 0), + SOC_SINGLE("Burst FIFO Wake Up Delay", + AB8500_FIFOCONF5, AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT, + AB8500_FIFOCONF5_BFIFOWAKEUP_MAX, 0), + SOC_SINGLE("Burst FIFO Samples In FIFO", + AB8500_FIFOCONF6, AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT, + AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0), + + /* ANC */ + SOC_ENUM_EXT("ANC Status", soc_enum_ancstate, + anc_status_control_get, anc_status_control_put), + SOC_SINGLE_XR_SX("ANC Warp Delay Shift", + AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT, + AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0), + SOC_SINGLE_XR_SX("ANC FIR Output Shift", + AB8500_ANCCONF3, 1, AB8500_ANCCONF3_SHIFT, + AB8500_ANCCONF3_MIN, AB8500_ANCCONF3_MAX, 0), + SOC_SINGLE_XR_SX("ANC IIR Output Shift", + AB8500_ANCCONF4, 1, AB8500_ANCCONF4_SHIFT, + AB8500_ANCCONF4_MIN, AB8500_ANCCONF4_MAX, 0), + SOC_SINGLE_XR_SX("ANC Warp Delay", + AB8500_ANCCONF9, 2, AB8500_ANC_WARP_DELAY_SHIFT, + AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), + + /* Sidetone */ + SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, + sid_status_control_get, sid_status_control_put), + SOC_SINGLE_STROBE("Sidetone Reset", + AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), +}; + +static struct snd_kcontrol_new ab8500_filter_controls[] = { + AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS, + AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX), + AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS, + AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX), + AB8500_FILTER_CONTROL("Sidetone FIR Coefficients", + AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN, + AB8500_SID_FIR_COEFF_MAX) +}; +enum ab8500_filter { + AB8500_FILTER_ANC_FIR = 0, + AB8500_FILTER_ANC_IIR = 1, + AB8500_FILTER_SID_FIR = 2, +}; + +/* + * Extended interface for codec-driver + */ + +static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec) +{ + int status; + + dev_dbg(codec->dev, "%s: Enter.\n", __func__); + + /* Reset audio-registers and disable 32kHz-clock output 2 */ + status = ab8500_sysctrl_write(AB8500_STW4500CTRL3, + AB8500_STW4500CTRL3_CLK32KOUT2DIS | + AB8500_STW4500CTRL3_RESETAUDN, + AB8500_STW4500CTRL3_RESETAUDN); + if (status < 0) + return status; + + return 0; +} + +static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, + struct amic_settings *amics) +{ + u8 value8; + unsigned int value; + int status; + const struct snd_soc_dapm_route *route; + + dev_dbg(codec->dev, "%s: Enter.\n", __func__); + + /* Set DMic-clocks to outputs */ + status = abx500_get_register_interruptible(codec->dev, (u8)AB8500_MISC, + (u8)AB8500_GPIO_DIR4_REG, + &value8); + if (status < 0) + return status; + value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | + GPIO31_DIR_OUTPUT; + status = abx500_set_register_interruptible(codec->dev, + (u8)AB8500_MISC, + (u8)AB8500_GPIO_DIR4_REG, + value); + if (status < 0) + return status; + + /* Attach regulators to AMic DAPM-paths */ + dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__, + amic_micbias_str(amics->mic1a_micbias)); + route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias]; + status = snd_soc_dapm_add_routes(&codec->dapm, route, 1); + dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__, + amic_micbias_str(amics->mic1b_micbias)); + route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias]; + status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1); + dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__, + amic_micbias_str(amics->mic2_micbias)); + route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias]; + status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1); + if (status < 0) { + dev_err(codec->dev, + "%s: Failed to add AMic-regulator DAPM-routes (%d).\n", + __func__, status); + return status; + } + + /* Set AMic-configuration */ + dev_dbg(codec->dev, "%s: Mic 1 mic-type: %s\n", __func__, + amic_type_str(amics->mic1_type)); + snd_soc_update_bits(codec, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX, + amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ? + 0 : AB8500_ANAGAINX_ENSEMICX); + dev_dbg(codec->dev, "%s: Mic 2 mic-type: %s\n", __func__, + amic_type_str(amics->mic2_type)); + snd_soc_update_bits(codec, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX, + amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ? + 0 : AB8500_ANAGAINX_ENSEMICX); + + return 0; +} +EXPORT_SYMBOL_GPL(ab8500_audio_setup_mics); + +static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec, + enum ear_cm_voltage ear_cmv) +{ + char *cmv_str; + + switch (ear_cmv) { + case EAR_CMV_0_95V: + cmv_str = "0.95V"; + break; + case EAR_CMV_1_10V: + cmv_str = "1.10V"; + break; + case EAR_CMV_1_27V: + cmv_str = "1.27V"; + break; + case EAR_CMV_1_58V: + cmv_str = "1.58V"; + break; + default: + dev_err(codec->dev, + "%s: Unknown earpiece CM-voltage (%d)!\n", + __func__, (int)ear_cmv); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: Earpiece CM-voltage: %s\n", __func__, + cmv_str); + snd_soc_update_bits(codec, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM, + ear_cmv); + + return 0; +} +EXPORT_SYMBOL_GPL(ab8500_audio_set_ear_cmv); + +static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai, + unsigned int delay) +{ + unsigned int mask, val; + struct snd_soc_codec *codec = dai->codec; + + mask = BIT(AB8500_DIGIFCONF2_IF0DEL); + val = 0; + + switch (delay) { + case 0: + break; + case 1: + val |= BIT(AB8500_DIGIFCONF2_IF0DEL); + break; + default: + dev_err(dai->codec->dev, + "%s: ERROR: Unsupported bit-delay (0x%x)!\n", + __func__, delay); + return -EINVAL; + } + + dev_dbg(dai->codec->dev, "%s: IF0 Bit-delay: %d bits.\n", + __func__, delay); + snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val); + + return 0; +} + +/* Gates clocking according format mask */ +static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec, + unsigned int fmt) +{ + unsigned int mask; + unsigned int val; + + mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) | + BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); + + val = BIT(AB8500_DIGIFCONF1_ENMASTGEN); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: /* continuous clock */ + dev_dbg(codec->dev, "%s: IF0 Clock is continuous.\n", + __func__); + val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); + break; + case SND_SOC_DAIFMT_GATED: /* clock is gated */ + dev_dbg(codec->dev, "%s: IF0 Clock is gated.\n", + __func__); + break; + default: + dev_err(codec->dev, + "%s: ERROR: Unsupported clock mask (0x%x)!\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK); + return -EINVAL; + } + + snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val); + + return 0; +} + +static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + unsigned int mask; + unsigned int val; + struct snd_soc_codec *codec = dai->codec; + int status; + + dev_dbg(codec->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt); + + mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) | + BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) | + BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) | + BIT(AB8500_DIGIFCONF3_IF0MASTER); + val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */ + dev_dbg(dai->codec->dev, + "%s: IF0 Master-mode: AB8500 master.\n", __func__); + val |= BIT(AB8500_DIGIFCONF3_IF0MASTER); + break; + case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & FRM slave */ + dev_dbg(dai->codec->dev, + "%s: IF0 Master-mode: AB8500 slave.\n", __func__); + break; + case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */ + case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ + dev_err(dai->codec->dev, + "%s: ERROR: The device is either a master or a slave.\n", + __func__); + default: + dev_err(dai->codec->dev, + "%s: ERROR: Unsupporter master mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + break; + } + + snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val); + + /* Set clock gating */ + status = ab8500_codec_set_dai_clock_gate(codec, fmt); + if (status) { + dev_err(dai->codec->dev, + "%s: ERRROR: Failed to set clock gate (%d).\n", + __func__, status); + return status; + } + + /* Setting data transfer format */ + + mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) | + BIT(AB8500_DIGIFCONF2_IF0FORMAT1) | + BIT(AB8500_DIGIFCONF2_FSYNC0P) | + BIT(AB8500_DIGIFCONF2_BITCLK0P); + val = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: /* I2S mode */ + dev_dbg(dai->codec->dev, "%s: IF0 Protocol: I2S\n", __func__); + val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1); + ab8500_audio_set_bit_delay(dai, 0); + break; + + case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ + dev_dbg(dai->codec->dev, + "%s: IF0 Protocol: DSP A (TDM)\n", __func__); + val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); + ab8500_audio_set_bit_delay(dai, 1); + break; + + case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ + dev_dbg(dai->codec->dev, + "%s: IF0 Protocol: DSP B (TDM)\n", __func__); + val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); + ab8500_audio_set_bit_delay(dai, 0); + break; + + default: + dev_err(dai->codec->dev, + "%s: ERROR: Unsupported format (0x%x)!\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ + dev_dbg(dai->codec->dev, + "%s: IF0: Normal bit clock, normal frame\n", + __func__); + break; + case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */ + dev_dbg(dai->codec->dev, + "%s: IF0: Normal bit clock, inverted frame\n", + __func__); + val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); + break; + case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */ + dev_dbg(dai->codec->dev, + "%s: IF0: Inverted bit clock, normal frame\n", + __func__); + val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); + break; + case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */ + dev_dbg(dai->codec->dev, + "%s: IF0: Inverted bit clock, inverted frame\n", + __func__); + val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); + val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); + break; + default: + dev_err(dai->codec->dev, + "%s: ERROR: Unsupported INV mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val); + + return 0; +} + +static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val, mask, slots_active; + + mask = BIT(AB8500_DIGIFCONF2_IF0WL0) | + BIT(AB8500_DIGIFCONF2_IF0WL1); + val = 0; + + switch (slot_width) { + case 16: + break; + case 20: + val |= BIT(AB8500_DIGIFCONF2_IF0WL0); + break; + case 24: + val |= BIT(AB8500_DIGIFCONF2_IF0WL1); + break; + case 32: + val |= BIT(AB8500_DIGIFCONF2_IF0WL1) | + BIT(AB8500_DIGIFCONF2_IF0WL0); + break; + default: + dev_err(dai->codec->dev, "%s: Unsupported slot-width 0x%x\n", + __func__, slot_width); + return -EINVAL; + } + + dev_dbg(dai->codec->dev, "%s: IF0 slot-width: %d bits.\n", + __func__, slot_width); + snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val); + + /* Setup TDM clocking according to slot count */ + dev_dbg(dai->codec->dev, "%s: Slots, total: %d\n", __func__, slots); + mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | + BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + switch (slots) { + case 2: + val = AB8500_MASK_NONE; + break; + case 4: + val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0); + break; + case 8: + val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + break; + case 16: + val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | + BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + break; + default: + dev_err(dai->codec->dev, + "%s: ERROR: Unsupported number of slots (%d)!\n", + __func__, slots); + return -EINVAL; + } + snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val); + + /* Setup TDM DA according to active tx slots */ + mask = AB8500_DASLOTCONFX_SLTODAX_MASK; + slots_active = hweight32(tx_mask); + dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__, + slots_active); + switch (slots_active) { + case 0: + break; + case 1: + /* Slot 9 -> DA_IN1 & DA_IN3 */ + snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 11); + snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 11); + snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11); + snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11); + break; + case 2: + /* Slot 9 -> DA_IN1 & DA_IN3, Slot 11 -> DA_IN2 & DA_IN4 */ + snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 9); + snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 9); + snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11); + snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11); + + break; + case 8: + dev_dbg(dai->codec->dev, + "%s: In 8-channel mode DA-from-slot mapping is set manually.", + __func__); + break; + default: + dev_err(dai->codec->dev, + "%s: Unsupported number of active TX-slots (%d)!\n", + __func__, slots_active); + return -EINVAL; + } + + /* Setup TDM AD according to active RX-slots */ + slots_active = hweight32(rx_mask); + dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__, + slots_active); + switch (slots_active) { + case 0: + break; + case 1: + /* AD_OUT3 -> slot 0 & 1 */ + snd_soc_update_bits(codec, AB8500_ADSLOTSEL1, AB8500_MASK_ALL, + AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN | + AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD); + break; + case 2: + /* AD_OUT3 -> slot 0, AD_OUT2 -> slot 1 */ + snd_soc_update_bits(codec, + AB8500_ADSLOTSEL1, + AB8500_MASK_ALL, + AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN | + AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD); + break; + case 8: + dev_dbg(dai->codec->dev, + "%s: In 8-channel mode AD-to-slot mapping is set manually.", + __func__); + break; + default: + dev_err(dai->codec->dev, + "%s: Unsupported number of active RX-slots (%d)!\n", + __func__, slots_active); + return -EINVAL; + } + + return 0; +} + +struct snd_soc_dai_driver ab8500_codec_dai[] = { + { + .name = "ab8500-codec-dai.0", + .id = 0, + .playback = { + .stream_name = "ab8500_0p", + .channels_min = 1, + .channels_max = 8, + .rates = AB8500_SUPPORTED_RATE, + .formats = AB8500_SUPPORTED_FMT, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .set_tdm_slot = ab8500_codec_set_dai_tdm_slot, + .set_fmt = ab8500_codec_set_dai_fmt, + } + }, + .symmetric_rates = 1 + }, + { + .name = "ab8500-codec-dai.1", + .id = 1, + .capture = { + .stream_name = "ab8500_0c", + .channels_min = 1, + .channels_max = 8, + .rates = AB8500_SUPPORTED_RATE, + .formats = AB8500_SUPPORTED_FMT, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .set_tdm_slot = ab8500_codec_set_dai_tdm_slot, + .set_fmt = ab8500_codec_set_dai_fmt, + } + }, + .symmetric_rates = 1 + } +}; + +static int ab8500_codec_probe(struct snd_soc_codec *codec) +{ + struct device *dev = codec->dev; + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); + struct ab8500_platform_data *pdata; + struct filter_control *fc; + int status; + + dev_dbg(dev, "%s: Enter.\n", __func__); + + /* Setup AB8500 according to board-settings */ + pdata = (struct ab8500_platform_data *)dev_get_platdata(dev->parent); + status = ab8500_audio_setup_mics(codec, &pdata->codec->amics); + if (status < 0) { + pr_err("%s: Failed to setup mics (%d)!\n", __func__, status); + return status; + } + status = ab8500_audio_set_ear_cmv(codec, pdata->codec->ear_cmv); + if (status < 0) { + pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n", + __func__, status); + return status; + } + + status = ab8500_audio_init_audioblock(codec); + if (status < 0) { + dev_err(dev, "%s: failed to init audio-block (%d)!\n", + __func__, status); + return status; + } + + /* Override HW-defaults */ + ab8500_codec_write_reg(codec, + AB8500_ANACONF5, + BIT(AB8500_ANACONF5_HSAUTOEN)); + ab8500_codec_write_reg(codec, + AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + + /* Add filter controls */ + status = snd_soc_add_codec_controls(codec, ab8500_filter_controls, + ARRAY_SIZE(ab8500_filter_controls)); + if (status < 0) { + dev_err(dev, + "%s: failed to add ab8500 filter controls (%d).\n", + __func__, status); + return status; + } + fc = (struct filter_control *) + &ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value; + drvdata->anc_fir_values = (long *)fc->value; + fc = (struct filter_control *) + &ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value; + drvdata->anc_iir_values = (long *)fc->value; + fc = (struct filter_control *) + &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value; + drvdata->sid_fir_values = (long *)fc->value; + + (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); + + mutex_init(&drvdata->anc_lock); + + return status; +} + +static struct snd_soc_codec_driver ab8500_codec_driver = { + .probe = ab8500_codec_probe, + .read = ab8500_codec_read_reg, + .write = ab8500_codec_write_reg, + .reg_word_size = sizeof(u8), + .controls = ab8500_ctrls, + .num_controls = ARRAY_SIZE(ab8500_ctrls), + .dapm_widgets = ab8500_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets), + .dapm_routes = ab8500_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes), +}; + +static int __devinit ab8500_codec_driver_probe(struct platform_device *pdev) +{ + int status; + struct ab8500_codec_drvdata *drvdata; + + dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); + + /* Create driver private-data struct */ + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_codec_drvdata), + GFP_KERNEL); + drvdata->sid_status = SID_UNCONFIGURED; + drvdata->anc_status = ANC_UNCONFIGURED; + dev_set_drvdata(&pdev->dev, drvdata); + + dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__); + status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver, + ab8500_codec_dai, + ARRAY_SIZE(ab8500_codec_dai)); + if (status < 0) + dev_err(&pdev->dev, + "%s: Error: Failed to register codec (%d).\n", + __func__, status); + + return status; +} + +static int __devexit ab8500_codec_driver_remove(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s Enter.\n", __func__); + + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver ab8500_codec_platform_driver = { + .driver = { + .name = "ab8500-codec", + .owner = THIS_MODULE, + }, + .probe = ab8500_codec_driver_probe, + .remove = __devexit_p(ab8500_codec_driver_remove), + .suspend = NULL, + .resume = NULL, +}; +module_platform_driver(ab8500_codec_platform_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h new file mode 100644 index 000000000000..114f69a0c629 --- /dev/null +++ b/sound/soc/codecs/ab8500-codec.h @@ -0,0 +1,590 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja <ola.o.lilja@stericsson.com>, + * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>, + * Roger Nilsson <roger.xr.nilsson@stericsson.com>, + * for ST-Ericsson. + * + * Based on the early work done by: + * Mikko J. Lehto <mikko.lehto@symbio.com>, + * Mikko Sarmanne <mikko.sarmanne@symbio.com>, + * for ST-Ericsson. + * + * License terms: + * + * 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 AB8500_CODEC_REGISTERS_H +#define AB8500_CODEC_REGISTERS_H + +#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000) +#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE) + +/* AB8500 audio bank (0x0d) register definitions */ + +#define AB8500_POWERUP 0x00 +#define AB8500_AUDSWRESET 0x01 +#define AB8500_ADPATHENA 0x02 +#define AB8500_DAPATHENA 0x03 +#define AB8500_ANACONF1 0x04 +#define AB8500_ANACONF2 0x05 +#define AB8500_DIGMICCONF 0x06 +#define AB8500_ANACONF3 0x07 +#define AB8500_ANACONF4 0x08 +#define AB8500_DAPATHCONF 0x09 +#define AB8500_MUTECONF 0x0A +#define AB8500_SHORTCIRCONF 0x0B +#define AB8500_ANACONF5 0x0C +#define AB8500_ENVCPCONF 0x0D +#define AB8500_SIGENVCONF 0x0E +#define AB8500_PWMGENCONF1 0x0F +#define AB8500_PWMGENCONF2 0x10 +#define AB8500_PWMGENCONF3 0x11 +#define AB8500_PWMGENCONF4 0x12 +#define AB8500_PWMGENCONF5 0x13 +#define AB8500_ANAGAIN1 0x14 +#define AB8500_ANAGAIN2 0x15 +#define AB8500_ANAGAIN3 0x16 +#define AB8500_ANAGAIN4 0x17 +#define AB8500_DIGLINHSLGAIN 0x18 +#define AB8500_DIGLINHSRGAIN 0x19 +#define AB8500_ADFILTCONF 0x1A +#define AB8500_DIGIFCONF1 0x1B +#define AB8500_DIGIFCONF2 0x1C +#define AB8500_DIGIFCONF3 0x1D +#define AB8500_DIGIFCONF4 0x1E +#define AB8500_ADSLOTSEL1 0x1F +#define AB8500_ADSLOTSEL2 0x20 +#define AB8500_ADSLOTSEL3 0x21 +#define AB8500_ADSLOTSEL4 0x22 +#define AB8500_ADSLOTSEL5 0x23 +#define AB8500_ADSLOTSEL6 0x24 +#define AB8500_ADSLOTSEL7 0x25 +#define AB8500_ADSLOTSEL8 0x26 +#define AB8500_ADSLOTSEL9 0x27 +#define AB8500_ADSLOTSEL10 0x28 +#define AB8500_ADSLOTSEL11 0x29 +#define AB8500_ADSLOTSEL12 0x2A +#define AB8500_ADSLOTSEL13 0x2B +#define AB8500_ADSLOTSEL14 0x2C +#define AB8500_ADSLOTSEL15 0x2D +#define AB8500_ADSLOTSEL16 0x2E +#define AB8500_ADSLOTHIZCTRL1 0x2F +#define AB8500_ADSLOTHIZCTRL2 0x30 +#define AB8500_ADSLOTHIZCTRL3 0x31 +#define AB8500_ADSLOTHIZCTRL4 0x32 +#define AB8500_DASLOTCONF1 0x33 +#define AB8500_DASLOTCONF2 0x34 +#define AB8500_DASLOTCONF3 0x35 +#define AB8500_DASLOTCONF4 0x36 +#define AB8500_DASLOTCONF5 0x37 +#define AB8500_DASLOTCONF6 0x38 +#define AB8500_DASLOTCONF7 0x39 +#define AB8500_DASLOTCONF8 0x3A +#define AB8500_CLASSDCONF1 0x3B +#define AB8500_CLASSDCONF2 0x3C +#define AB8500_CLASSDCONF3 0x3D +#define AB8500_DMICFILTCONF 0x3E +#define AB8500_DIGMULTCONF1 0x3F +#define AB8500_DIGMULTCONF2 0x40 +#define AB8500_ADDIGGAIN1 0x41 +#define AB8500_ADDIGGAIN2 0x42 +#define AB8500_ADDIGGAIN3 0x43 +#define AB8500_ADDIGGAIN4 0x44 +#define AB8500_ADDIGGAIN5 0x45 +#define AB8500_ADDIGGAIN6 0x46 +#define AB8500_DADIGGAIN1 0x47 +#define AB8500_DADIGGAIN2 0x48 +#define AB8500_DADIGGAIN3 0x49 +#define AB8500_DADIGGAIN4 0x4A +#define AB8500_DADIGGAIN5 0x4B +#define AB8500_DADIGGAIN6 0x4C +#define AB8500_ADDIGLOOPGAIN1 0x4D +#define AB8500_ADDIGLOOPGAIN2 0x4E +#define AB8500_HSLEARDIGGAIN 0x4F +#define AB8500_HSRDIGGAIN 0x50 +#define AB8500_SIDFIRGAIN1 0x51 +#define AB8500_SIDFIRGAIN2 0x52 +#define AB8500_ANCCONF1 0x53 +#define AB8500_ANCCONF2 0x54 +#define AB8500_ANCCONF3 0x55 +#define AB8500_ANCCONF4 0x56 +#define AB8500_ANCCONF5 0x57 +#define AB8500_ANCCONF6 0x58 +#define AB8500_ANCCONF7 0x59 +#define AB8500_ANCCONF8 0x5A +#define AB8500_ANCCONF9 0x5B +#define AB8500_ANCCONF10 0x5C +#define AB8500_ANCCONF11 0x5D +#define AB8500_ANCCONF12 0x5E +#define AB8500_ANCCONF13 0x5F +#define AB8500_ANCCONF14 0x60 +#define AB8500_SIDFIRADR 0x61 +#define AB8500_SIDFIRCOEF1 0x62 +#define AB8500_SIDFIRCOEF2 0x63 +#define AB8500_SIDFIRCONF 0x64 +#define AB8500_AUDINTMASK1 0x65 +#define AB8500_AUDINTSOURCE1 0x66 +#define AB8500_AUDINTMASK2 0x67 +#define AB8500_AUDINTSOURCE2 0x68 +#define AB8500_FIFOCONF1 0x69 +#define AB8500_FIFOCONF2 0x6A +#define AB8500_FIFOCONF3 0x6B +#define AB8500_FIFOCONF4 0x6C +#define AB8500_FIFOCONF5 0x6D +#define AB8500_FIFOCONF6 0x6E +#define AB8500_AUDREV 0x6F + +#define AB8500_FIRST_REG AB8500_POWERUP +#define AB8500_LAST_REG AB8500_AUDREV +#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1) + +#define AB8500_MASK_ALL 0xFF +#define AB8500_MASK_NONE 0x00 + +/* AB8500_POWERUP */ +#define AB8500_POWERUP_POWERUP 7 +#define AB8500_POWERUP_ENANA 3 + +/* AB8500_AUDSWRESET */ +#define AB8500_AUDSWRESET_SWRESET 7 + +/* AB8500_ADPATHENA */ +#define AB8500_ADPATHENA_ENAD12 7 +#define AB8500_ADPATHENA_ENAD34 5 +#define AB8500_ADPATHENA_ENAD5768 3 + +/* AB8500_DAPATHENA */ +#define AB8500_DAPATHENA_ENDA1 7 +#define AB8500_DAPATHENA_ENDA2 6 +#define AB8500_DAPATHENA_ENDA3 5 +#define AB8500_DAPATHENA_ENDA4 4 +#define AB8500_DAPATHENA_ENDA5 3 +#define AB8500_DAPATHENA_ENDA6 2 + +/* AB8500_ANACONF1 */ +#define AB8500_ANACONF1_HSLOWPOW 7 +#define AB8500_ANACONF1_DACLOWPOW1 6 +#define AB8500_ANACONF1_DACLOWPOW0 5 +#define AB8500_ANACONF1_EARDACLOWPOW 4 +#define AB8500_ANACONF1_EARSELCM 2 +#define AB8500_ANACONF1_HSHPEN 1 +#define AB8500_ANACONF1_EARDRVLOWPOW 0 + +/* AB8500_ANACONF2 */ +#define AB8500_ANACONF2_ENMIC1 7 +#define AB8500_ANACONF2_ENMIC2 6 +#define AB8500_ANACONF2_ENLINL 5 +#define AB8500_ANACONF2_ENLINR 4 +#define AB8500_ANACONF2_MUTMIC1 3 +#define AB8500_ANACONF2_MUTMIC2 2 +#define AB8500_ANACONF2_MUTLINL 1 +#define AB8500_ANACONF2_MUTLINR 0 + +/* AB8500_DIGMICCONF */ +#define AB8500_DIGMICCONF_ENDMIC1 7 +#define AB8500_DIGMICCONF_ENDMIC2 6 +#define AB8500_DIGMICCONF_ENDMIC3 5 +#define AB8500_DIGMICCONF_ENDMIC4 4 +#define AB8500_DIGMICCONF_ENDMIC5 3 +#define AB8500_DIGMICCONF_ENDMIC6 2 +#define AB8500_DIGMICCONF_HSFADSPEED 0 + +/* AB8500_ANACONF3 */ +#define AB8500_ANACONF3_MIC1SEL 7 +#define AB8500_ANACONF3_LINRSEL 6 +#define AB8500_ANACONF3_ENDRVHSL 5 +#define AB8500_ANACONF3_ENDRVHSR 4 +#define AB8500_ANACONF3_ENADCMIC 2 +#define AB8500_ANACONF3_ENADCLINL 1 +#define AB8500_ANACONF3_ENADCLINR 0 + +/* AB8500_ANACONF4 */ +#define AB8500_ANACONF4_DISPDVSS 7 +#define AB8500_ANACONF4_ENEAR 6 +#define AB8500_ANACONF4_ENHSL 5 +#define AB8500_ANACONF4_ENHSR 4 +#define AB8500_ANACONF4_ENHFL 3 +#define AB8500_ANACONF4_ENHFR 2 +#define AB8500_ANACONF4_ENVIB1 1 +#define AB8500_ANACONF4_ENVIB2 0 + +/* AB8500_DAPATHCONF */ +#define AB8500_DAPATHCONF_ENDACEAR 6 +#define AB8500_DAPATHCONF_ENDACHSL 5 +#define AB8500_DAPATHCONF_ENDACHSR 4 +#define AB8500_DAPATHCONF_ENDACHFL 3 +#define AB8500_DAPATHCONF_ENDACHFR 2 +#define AB8500_DAPATHCONF_ENDACVIB1 1 +#define AB8500_DAPATHCONF_ENDACVIB2 0 + +/* AB8500_MUTECONF */ +#define AB8500_MUTECONF_MUTEAR 6 +#define AB8500_MUTECONF_MUTHSL 5 +#define AB8500_MUTECONF_MUTHSR 4 +#define AB8500_MUTECONF_MUTDACEAR 2 +#define AB8500_MUTECONF_MUTDACHSL 1 +#define AB8500_MUTECONF_MUTDACHSR 0 + +/* AB8500_SHORTCIRCONF */ +#define AB8500_SHORTCIRCONF_ENSHORTPWD 7 +#define AB8500_SHORTCIRCONF_EARSHORTDIS 6 +#define AB8500_SHORTCIRCONF_HSSHORTDIS 5 +#define AB8500_SHORTCIRCONF_HSPULLDEN 4 +#define AB8500_SHORTCIRCONF_HSOSCEN 2 +#define AB8500_SHORTCIRCONF_HSFADDIS 1 +#define AB8500_SHORTCIRCONF_HSZCDDIS 0 +/* Zero cross should be disabled */ + +/* AB8500_ANACONF5 */ +#define AB8500_ANACONF5_ENCPHS 7 +#define AB8500_ANACONF5_HSLDACTOLOL 5 +#define AB8500_ANACONF5_HSRDACTOLOR 4 +#define AB8500_ANACONF5_ENLOL 3 +#define AB8500_ANACONF5_ENLOR 2 +#define AB8500_ANACONF5_HSAUTOEN 0 + +/* AB8500_ENVCPCONF */ +#define AB8500_ENVCPCONF_ENVDETHTHRE 4 +#define AB8500_ENVCPCONF_ENVDETLTHRE 0 +#define AB8500_ENVCPCONF_ENVDETHTHRE_MAX 0x0F +#define AB8500_ENVCPCONF_ENVDETLTHRE_MAX 0x0F + +/* AB8500_SIGENVCONF */ +#define AB8500_SIGENVCONF_CPLVEN 5 +#define AB8500_SIGENVCONF_ENVDETCPEN 4 +#define AB8500_SIGENVCONF_ENVDETTIME 0 +#define AB8500_SIGENVCONF_ENVDETTIME_MAX 0x0F + +/* AB8500_PWMGENCONF1 */ +#define AB8500_PWMGENCONF1_PWMTOVIB1 7 +#define AB8500_PWMGENCONF1_PWMTOVIB2 6 +#define AB8500_PWMGENCONF1_PWM1CTRL 5 +#define AB8500_PWMGENCONF1_PWM2CTRL 4 +#define AB8500_PWMGENCONF1_PWM1NCTRL 3 +#define AB8500_PWMGENCONF1_PWM1PCTRL 2 +#define AB8500_PWMGENCONF1_PWM2NCTRL 1 +#define AB8500_PWMGENCONF1_PWM2PCTRL 0 + +/* AB8500_PWMGENCONF2 */ +/* AB8500_PWMGENCONF3 */ +/* AB8500_PWMGENCONF4 */ +/* AB8500_PWMGENCONF5 */ +#define AB8500_PWMGENCONFX_PWMVIBXPOL 7 +#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC 0 +#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX 0x64 + +/* AB8500_ANAGAIN1 */ +/* AB8500_ANAGAIN2 */ +#define AB8500_ANAGAINX_ENSEMICX 7 +#define AB8500_ANAGAINX_LOWPOWMICX 6 +#define AB8500_ANAGAINX_MICXGAIN 0 +#define AB8500_ANAGAINX_MICXGAIN_MAX 0x1F + +/* AB8500_ANAGAIN3 */ +#define AB8500_ANAGAIN3_HSLGAIN 4 +#define AB8500_ANAGAIN3_HSRGAIN 0 +#define AB8500_ANAGAIN3_HSXGAIN_MAX 0x0F + +/* AB8500_ANAGAIN4 */ +#define AB8500_ANAGAIN4_LINLGAIN 4 +#define AB8500_ANAGAIN4_LINRGAIN 0 +#define AB8500_ANAGAIN4_LINXGAIN_MAX 0x0F + +/* AB8500_DIGLINHSLGAIN */ +/* AB8500_DIGLINHSRGAIN */ +#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN 0 +#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX 0x13 + +/* AB8500_ADFILTCONF */ +#define AB8500_ADFILTCONF_AD1NH 7 +#define AB8500_ADFILTCONF_AD2NH 6 +#define AB8500_ADFILTCONF_AD3NH 5 +#define AB8500_ADFILTCONF_AD4NH 4 +#define AB8500_ADFILTCONF_AD1VOICE 3 +#define AB8500_ADFILTCONF_AD2VOICE 2 +#define AB8500_ADFILTCONF_AD3VOICE 1 +#define AB8500_ADFILTCONF_AD4VOICE 0 + +/* AB8500_DIGIFCONF1 */ +#define AB8500_DIGIFCONF1_ENMASTGEN 7 +#define AB8500_DIGIFCONF1_IF1BITCLKOS1 6 +#define AB8500_DIGIFCONF1_IF1BITCLKOS0 5 +#define AB8500_DIGIFCONF1_ENFSBITCLK1 4 +#define AB8500_DIGIFCONF1_IF0BITCLKOS1 2 +#define AB8500_DIGIFCONF1_IF0BITCLKOS0 1 +#define AB8500_DIGIFCONF1_ENFSBITCLK0 0 + +/* AB8500_DIGIFCONF2 */ +#define AB8500_DIGIFCONF2_FSYNC0P 6 +#define AB8500_DIGIFCONF2_BITCLK0P 5 +#define AB8500_DIGIFCONF2_IF0DEL 4 +#define AB8500_DIGIFCONF2_IF0FORMAT1 3 +#define AB8500_DIGIFCONF2_IF0FORMAT0 2 +#define AB8500_DIGIFCONF2_IF0WL1 1 +#define AB8500_DIGIFCONF2_IF0WL0 0 + +/* AB8500_DIGIFCONF3 */ +#define AB8500_DIGIFCONF3_IF0DATOIF1AD 7 +#define AB8500_DIGIFCONF3_IF0CLKTOIF1CLK 6 +#define AB8500_DIGIFCONF3_IF1MASTER 5 +#define AB8500_DIGIFCONF3_IF1DATOIF0AD 3 +#define AB8500_DIGIFCONF3_IF1CLKTOIF0CLK 2 +#define AB8500_DIGIFCONF3_IF0MASTER 1 +#define AB8500_DIGIFCONF3_IF0BFIFOEN 0 + +/* AB8500_DIGIFCONF4 */ +#define AB8500_DIGIFCONF4_FSYNC1P 6 +#define AB8500_DIGIFCONF4_BITCLK1P 5 +#define AB8500_DIGIFCONF4_IF1DEL 4 +#define AB8500_DIGIFCONF4_IF1FORMAT1 3 +#define AB8500_DIGIFCONF4_IF1FORMAT0 2 +#define AB8500_DIGIFCONF4_IF1WL1 1 +#define AB8500_DIGIFCONF4_IF1WL0 0 + +/* AB8500_ADSLOTSELX */ +#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_ODD 0x00 +#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD 0x01 +#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD 0x02 +#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_ODD 0x03 +#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_ODD 0x04 +#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_ODD 0x05 +#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_ODD 0x06 +#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_ODD 0x07 +#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_ODD 0x08 +#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_ODD 0x0F +#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_EVEN 0x00 +#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_EVEN 0x10 +#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN 0x20 +#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_EVEN 0x30 +#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_EVEN 0x40 +#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_EVEN 0x50 +#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_EVEN 0x60 +#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_EVEN 0x70 +#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_EVEN 0x80 +#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_EVEN 0xF0 +#define AB8500_ADSLOTSELX_EVEN_SHIFT 0 +#define AB8500_ADSLOTSELX_ODD_SHIFT 4 + +/* AB8500_ADSLOTHIZCTRL1 */ +/* AB8500_ADSLOTHIZCTRL2 */ +/* AB8500_ADSLOTHIZCTRL3 */ +/* AB8500_ADSLOTHIZCTRL4 */ +/* AB8500_DASLOTCONF1 */ +#define AB8500_DASLOTCONF1_DA12VOICE 7 +#define AB8500_DASLOTCONF1_SWAPDA12_34 6 +#define AB8500_DASLOTCONF1_DAI7TOADO1 5 + +/* AB8500_DASLOTCONF2 */ +#define AB8500_DASLOTCONF2_DAI8TOADO2 5 + +/* AB8500_DASLOTCONF3 */ +#define AB8500_DASLOTCONF3_DA34VOICE 7 +#define AB8500_DASLOTCONF3_DAI7TOADO3 5 + +/* AB8500_DASLOTCONF4 */ +#define AB8500_DASLOTCONF4_DAI8TOADO4 5 + +/* AB8500_DASLOTCONF5 */ +#define AB8500_DASLOTCONF5_DA56VOICE 7 +#define AB8500_DASLOTCONF5_DAI7TOADO5 5 + +/* AB8500_DASLOTCONF6 */ +#define AB8500_DASLOTCONF6_DAI8TOADO6 5 + +/* AB8500_DASLOTCONF7 */ +#define AB8500_DASLOTCONF7_DAI8TOADO7 5 + +/* AB8500_DASLOTCONF8 */ +#define AB8500_DASLOTCONF8_DAI7TOADO8 5 + +#define AB8500_DASLOTCONFX_SLTODAX_SHIFT 0 +#define AB8500_DASLOTCONFX_SLTODAX_MASK 0x1F + +/* AB8500_CLASSDCONF1 */ +#define AB8500_CLASSDCONF1_PARLHF 7 +#define AB8500_CLASSDCONF1_PARLVIB 6 +#define AB8500_CLASSDCONF1_VIB1SWAPEN 3 +#define AB8500_CLASSDCONF1_VIB2SWAPEN 2 +#define AB8500_CLASSDCONF1_HFLSWAPEN 1 +#define AB8500_CLASSDCONF1_HFRSWAPEN 0 + +/* AB8500_CLASSDCONF2 */ +#define AB8500_CLASSDCONF2_FIRBYP3 7 +#define AB8500_CLASSDCONF2_FIRBYP2 6 +#define AB8500_CLASSDCONF2_FIRBYP1 5 +#define AB8500_CLASSDCONF2_FIRBYP0 4 +#define AB8500_CLASSDCONF2_HIGHVOLEN3 3 +#define AB8500_CLASSDCONF2_HIGHVOLEN2 2 +#define AB8500_CLASSDCONF2_HIGHVOLEN1 1 +#define AB8500_CLASSDCONF2_HIGHVOLEN0 0 + +/* AB8500_CLASSDCONF3 */ +#define AB8500_CLASSDCONF3_DITHHPGAIN 4 +#define AB8500_CLASSDCONF3_DITHHPGAIN_MAX 0x0A +#define AB8500_CLASSDCONF3_DITHWGAIN 0 +#define AB8500_CLASSDCONF3_DITHWGAIN_MAX 0x0A + +/* AB8500_DMICFILTCONF */ +#define AB8500_DMICFILTCONF_ANCINSEL 7 +#define AB8500_DMICFILTCONF_DA3TOEAR 6 +#define AB8500_DMICFILTCONF_DMIC1SINC3 5 +#define AB8500_DMICFILTCONF_DMIC2SINC3 4 +#define AB8500_DMICFILTCONF_DMIC3SINC3 3 +#define AB8500_DMICFILTCONF_DMIC4SINC3 2 +#define AB8500_DMICFILTCONF_DMIC5SINC3 1 +#define AB8500_DMICFILTCONF_DMIC6SINC3 0 + +/* AB8500_DIGMULTCONF1 */ +#define AB8500_DIGMULTCONF1_DATOHSLEN 7 +#define AB8500_DIGMULTCONF1_DATOHSREN 6 +#define AB8500_DIGMULTCONF1_AD1SEL 5 +#define AB8500_DIGMULTCONF1_AD2SEL 4 +#define AB8500_DIGMULTCONF1_AD3SEL 3 +#define AB8500_DIGMULTCONF1_AD5SEL 2 +#define AB8500_DIGMULTCONF1_AD6SEL 1 +#define AB8500_DIGMULTCONF1_ANCSEL 0 + +/* AB8500_DIGMULTCONF2 */ +#define AB8500_DIGMULTCONF2_DATOHFREN 7 +#define AB8500_DIGMULTCONF2_DATOHFLEN 6 +#define AB8500_DIGMULTCONF2_HFRSEL 5 +#define AB8500_DIGMULTCONF2_HFLSEL 4 +#define AB8500_DIGMULTCONF2_FIRSID1SEL 2 +#define AB8500_DIGMULTCONF2_FIRSID2SEL 0 + +/* AB8500_ADDIGGAIN1 */ +/* AB8500_ADDIGGAIN2 */ +/* AB8500_ADDIGGAIN3 */ +/* AB8500_ADDIGGAIN4 */ +/* AB8500_ADDIGGAIN5 */ +/* AB8500_ADDIGGAIN6 */ +#define AB8500_ADDIGGAINX_FADEDISADX 6 +#define AB8500_ADDIGGAINX_ADXGAIN_MAX 0x3F + +/* AB8500_DADIGGAIN1 */ +/* AB8500_DADIGGAIN2 */ +/* AB8500_DADIGGAIN3 */ +/* AB8500_DADIGGAIN4 */ +/* AB8500_DADIGGAIN5 */ +/* AB8500_DADIGGAIN6 */ +#define AB8500_DADIGGAINX_FADEDISDAX 6 +#define AB8500_DADIGGAINX_DAXGAIN_MAX 0x3F + +/* AB8500_ADDIGLOOPGAIN1 */ +/* AB8500_ADDIGLOOPGAIN2 */ +#define AB8500_ADDIGLOOPGAINX_FADEDISADXL 6 +#define AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX 0x3F + +/* AB8500_HSLEARDIGGAIN */ +#define AB8500_HSLEARDIGGAIN_HSSINC1 7 +#define AB8500_HSLEARDIGGAIN_FADEDISHSL 4 +#define AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX 0x09 + +/* AB8500_HSRDIGGAIN */ +#define AB8500_HSRDIGGAIN_FADESPEED 6 +#define AB8500_HSRDIGGAIN_FADEDISHSR 4 +#define AB8500_HSRDIGGAIN_HSRDGAIN_MAX 0x09 + +/* AB8500_SIDFIRGAIN1 */ +/* AB8500_SIDFIRGAIN2 */ +#define AB8500_SIDFIRGAINX_FIRSIDXGAIN_MAX 0x1F + +/* AB8500_ANCCONF1 */ +#define AB8500_ANCCONF1_ANCIIRUPDATE 3 +#define AB8500_ANCCONF1_ENANC 2 +#define AB8500_ANCCONF1_ANCIIRINIT 1 +#define AB8500_ANCCONF1_ANCFIRUPDATE 0 + +/* AB8500_ANCCONF2 */ +#define AB8500_ANCCONF2_SHIFT 5 +#define AB8500_ANCCONF2_MIN -0x10 +#define AB8500_ANCCONF2_MAX 0xF + +/* AB8500_ANCCONF3 */ +#define AB8500_ANCCONF3_SHIFT 5 +#define AB8500_ANCCONF3_MIN -0x10 +#define AB8500_ANCCONF3_MAX 0xF + +/* AB8500_ANCCONF4 */ +#define AB8500_ANCCONF4_SHIFT 5 +#define AB8500_ANCCONF4_MIN -0x10 +#define AB8500_ANCCONF4_MAX 0xF + +/* AB8500_ANC_FIR_COEFFS */ +#define AB8500_ANC_FIR_COEFF_MIN -0x8000 +#define AB8500_ANC_FIR_COEFF_MAX 0x7FFF +#define AB8500_ANC_FIR_COEFFS 15 + +/* AB8500_ANC_IIR_COEFFS */ +#define AB8500_ANC_IIR_COEFF_MIN -0x800000 +#define AB8500_ANC_IIR_COEFF_MAX 0x7FFFFF +#define AB8500_ANC_IIR_COEFFS 24 +/* AB8500_ANC_WARP_DELAY */ +#define AB8500_ANC_WARP_DELAY_SHIFT 16 +#define AB8500_ANC_WARP_DELAY_MIN 0x0000 +#define AB8500_ANC_WARP_DELAY_MAX 0xFFFF + +/* AB8500_ANCCONF11 */ +/* AB8500_ANCCONF12 */ +/* AB8500_ANCCONF13 */ +/* AB8500_ANCCONF14 */ + +/* AB8500_SIDFIRADR */ +#define AB8500_SIDFIRADR_FIRSIDSET 7 +#define AB8500_SIDFIRADR_ADDRESS_SHIFT 0 +#define AB8500_SIDFIRADR_ADDRESS_MAX 0x7F + +/* AB8500_SIDFIRCOEF1 */ +/* AB8500_SIDFIRCOEF2 */ +#define AB8500_SID_FIR_COEFF_MIN 0 +#define AB8500_SID_FIR_COEFF_MAX 0xFFFF +#define AB8500_SID_FIR_COEFFS 128 + +/* AB8500_SIDFIRCONF */ +#define AB8500_SIDFIRCONF_ENFIRSIDS 2 +#define AB8500_SIDFIRCONF_FIRSIDSTOIF1 1 +#define AB8500_SIDFIRCONF_FIRSIDBUSY 0 + +/* AB8500_AUDINTMASK1 */ +/* AB8500_AUDINTSOURCE1 */ +/* AB8500_AUDINTMASK2 */ +/* AB8500_AUDINTSOURCE2 */ + +/* AB8500_FIFOCONF1 */ +#define AB8500_FIFOCONF1_BFIFOMASK 0x80 +#define AB8500_FIFOCONF1_BFIFO19M2 0x40 +#define AB8500_FIFOCONF1_BFIFOINT_SHIFT 0 +#define AB8500_FIFOCONF1_BFIFOINT_MAX 0x3F + +/* AB8500_FIFOCONF2 */ +#define AB8500_FIFOCONF2_BFIFOTX_SHIFT 0 +#define AB8500_FIFOCONF2_BFIFOTX_MAX 0xFF + +/* AB8500_FIFOCONF3 */ +#define AB8500_FIFOCONF3_BFIFOEXSL_SHIFT 5 +#define AB8500_FIFOCONF3_BFIFOEXSL_MAX 0x5 +#define AB8500_FIFOCONF3_PREBITCLK0_SHIFT 2 +#define AB8500_FIFOCONF3_PREBITCLK0_MAX 0x7 +#define AB8500_FIFOCONF3_BFIFOMAST_SHIFT 1 +#define AB8500_FIFOCONF3_BFIFORUN_SHIFT 0 + +/* AB8500_FIFOCONF4 */ +#define AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT 0 +#define AB8500_FIFOCONF4_BFIFOFRAMSW_MAX 0xFF + +/* AB8500_FIFOCONF5 */ +#define AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT 0 +#define AB8500_FIFOCONF5_BFIFOWAKEUP_MAX 0xFF + +/* AB8500_FIFOCONF6 */ +#define AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT 0 +#define AB8500_FIFOCONF6_BFIFOSAMPLE_MAX 0xFF + +/* AB8500_AUDREV */ + +#endif diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 2023c749f232..ea06b834a7de 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -91,11 +91,6 @@ static int ac97_soc_probe(struct snd_soc_codec *codec) return 0; } -static int ac97_soc_remove(struct snd_soc_codec *codec) -{ - return 0; -} - #ifdef CONFIG_PM static int ac97_soc_suspend(struct snd_soc_codec *codec) { @@ -119,7 +114,6 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = { .write = ac97_write, .read = ac97_read, .probe = ac97_soc_probe, - .remove = ac97_soc_remove, .suspend = ac97_soc_suspend, .resume = ac97_soc_resume, }; diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c new file mode 100644 index 000000000000..5c9cacaf2d52 --- /dev/null +++ b/sound/soc/codecs/arizona.c @@ -0,0 +1,937 @@ +/* + * arizona.c - Wolfson Arizona class device shared support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/gcd.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" + +#define ARIZONA_AIF_BCLK_CTRL 0x00 +#define ARIZONA_AIF_TX_PIN_CTRL 0x01 +#define ARIZONA_AIF_RX_PIN_CTRL 0x02 +#define ARIZONA_AIF_RATE_CTRL 0x03 +#define ARIZONA_AIF_FORMAT 0x04 +#define ARIZONA_AIF_TX_BCLK_RATE 0x05 +#define ARIZONA_AIF_RX_BCLK_RATE 0x06 +#define ARIZONA_AIF_FRAME_CTRL_1 0x07 +#define ARIZONA_AIF_FRAME_CTRL_2 0x08 +#define ARIZONA_AIF_FRAME_CTRL_3 0x09 +#define ARIZONA_AIF_FRAME_CTRL_4 0x0A +#define ARIZONA_AIF_FRAME_CTRL_5 0x0B +#define ARIZONA_AIF_FRAME_CTRL_6 0x0C +#define ARIZONA_AIF_FRAME_CTRL_7 0x0D +#define ARIZONA_AIF_FRAME_CTRL_8 0x0E +#define ARIZONA_AIF_FRAME_CTRL_9 0x0F +#define ARIZONA_AIF_FRAME_CTRL_10 0x10 +#define ARIZONA_AIF_FRAME_CTRL_11 0x11 +#define ARIZONA_AIF_FRAME_CTRL_12 0x12 +#define ARIZONA_AIF_FRAME_CTRL_13 0x13 +#define ARIZONA_AIF_FRAME_CTRL_14 0x14 +#define ARIZONA_AIF_FRAME_CTRL_15 0x15 +#define ARIZONA_AIF_FRAME_CTRL_16 0x16 +#define ARIZONA_AIF_FRAME_CTRL_17 0x17 +#define ARIZONA_AIF_FRAME_CTRL_18 0x18 +#define ARIZONA_AIF_TX_ENABLES 0x19 +#define ARIZONA_AIF_RX_ENABLES 0x1A +#define ARIZONA_AIF_FORCE_WRITE 0x1B + +#define arizona_fll_err(_fll, fmt, ...) \ + dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define arizona_fll_warn(_fll, fmt, ...) \ + dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define arizona_fll_dbg(_fll, fmt, ...) \ + dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + +#define arizona_aif_err(_dai, fmt, ...) \ + dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) +#define arizona_aif_warn(_dai, fmt, ...) \ + dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) +#define arizona_aif_dbg(_dai, fmt, ...) \ + dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + +const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { + "None", + "Tone Generator 1", + "Tone Generator 2", + "Haptics", + "AEC", + "Mic Mute Mixer", + "Noise Generator", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "IN3L", + "IN3R", + "IN4L", + "IN4R", + "AIF1RX1", + "AIF1RX2", + "AIF1RX3", + "AIF1RX4", + "AIF1RX5", + "AIF1RX6", + "AIF1RX7", + "AIF1RX8", + "AIF2RX1", + "AIF2RX2", + "AIF3RX1", + "AIF3RX2", + "SLIMRX1", + "SLIMRX2", + "SLIMRX3", + "SLIMRX4", + "SLIMRX5", + "SLIMRX6", + "SLIMRX7", + "SLIMRX8", + "EQ1", + "EQ2", + "EQ3", + "EQ4", + "DRC1L", + "DRC1R", + "DRC2L", + "DRC2R", + "LHPF1", + "LHPF2", + "LHPF3", + "LHPF4", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "ASRC1L", + "ASRC1R", + "ASRC2L", + "ASRC2R", +}; +EXPORT_SYMBOL_GPL(arizona_mixer_texts); + +int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { + 0x00, /* None */ + 0x04, /* Tone */ + 0x05, + 0x06, /* Haptics */ + 0x08, /* AEC */ + 0x0c, /* Noise mixer */ + 0x0d, /* Comfort noise */ + 0x10, /* IN1L */ + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x20, /* AIF1RX1 */ + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, /* AIF2RX1 */ + 0x29, + 0x30, /* AIF3RX1 */ + 0x31, + 0x38, /* SLIMRX1 */ + 0x39, + 0x3a, + 0x3b, + 0x3c, + 0x3d, + 0x3e, + 0x3f, + 0x50, /* EQ1 */ + 0x51, + 0x52, + 0x53, + 0x58, /* DRC1L */ + 0x59, + 0x5a, + 0x5b, + 0x60, /* LHPF1 */ + 0x61, + 0x62, + 0x63, + 0x68, /* DSP1.1 */ + 0x69, + 0x6a, + 0x6b, + 0x6c, + 0x6d, + 0x90, /* ASRC1L */ + 0x91, + 0x92, + 0x93, +}; +EXPORT_SYMBOL_GPL(arizona_mixer_values); + +const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); +EXPORT_SYMBOL_GPL(arizona_mixer_tlv); + +static const char *arizona_lhpf_mode_text[] = { + "Low-pass", "High-pass" +}; + +const struct soc_enum arizona_lhpf1_mode = + SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf1_mode); + +const struct soc_enum arizona_lhpf2_mode = + SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf2_mode); + +const struct soc_enum arizona_lhpf3_mode = + SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf3_mode); + +const struct soc_enum arizona_lhpf4_mode = + SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); + +int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + return 0; +} +EXPORT_SYMBOL_GPL(arizona_in_ev); + +int arizona_out_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return 0; +} +EXPORT_SYMBOL_GPL(arizona_out_ev); + +int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + char *name; + unsigned int reg; + unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK; + unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT; + unsigned int *clk; + + switch (clk_id) { + case ARIZONA_CLK_SYSCLK: + name = "SYSCLK"; + reg = ARIZONA_SYSTEM_CLOCK_1; + clk = &priv->sysclk; + mask |= ARIZONA_SYSCLK_FRAC; + break; + case ARIZONA_CLK_ASYNCCLK: + name = "ASYNCCLK"; + reg = ARIZONA_ASYNC_CLOCK_1; + clk = &priv->asyncclk; + break; + default: + return -EINVAL; + } + + switch (freq) { + case 5644800: + case 6144000: + break; + case 11289600: + case 12288000: + val |= 1 << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 22579200: + case 24576000: + val |= 2 << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 45158400: + case 49152000: + val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + default: + return -EINVAL; + } + + *clk = freq; + + if (freq % 6144000) + val |= ARIZONA_SYSCLK_FRAC; + + dev_dbg(arizona->dev, "%s set to %uHz", name, freq); + + return regmap_update_bits(arizona->regmap, reg, mask, val); +} +EXPORT_SYMBOL_GPL(arizona_set_sysclk); + +static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + int lrclk, bclk, mode, base; + + base = dai->driver->base; + + lrclk = 0; + bclk = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + mode = 0; + break; + case SND_SOC_DAIFMT_DSP_B: + mode = 1; + break; + case SND_SOC_DAIFMT_I2S: + mode = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode = 3; + break; + default: + arizona_aif_err(dai, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + bclk |= ARIZONA_AIF1_BCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + bclk |= ARIZONA_AIF1_BCLK_MSTR; + lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; + break; + default: + arizona_aif_err(dai, "Unsupported master mode %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bclk |= ARIZONA_AIF1_BCLK_INV; + lrclk |= ARIZONA_AIF1TX_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk |= ARIZONA_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk |= ARIZONA_AIF1TX_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR, + bclk); + snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL, + ARIZONA_AIF1TX_LRCLK_INV | + ARIZONA_AIF1TX_LRCLK_MSTR, lrclk); + snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL, + ARIZONA_AIF1RX_LRCLK_INV | + ARIZONA_AIF1RX_LRCLK_MSTR, lrclk); + snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT, + ARIZONA_AIF1_FMT_MASK, mode); + + return 0; +} + +static const int arizona_48k_bclk_rates[] = { + -1, + 48000, + 64000, + 96000, + 128000, + 192000, + 256000, + 384000, + 512000, + 768000, + 1024000, + 1536000, + 2048000, + 3072000, + 4096000, + 6144000, + 8192000, + 12288000, + 24576000, +}; + +static const unsigned int arizona_48k_rates[] = { + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = { + .count = ARRAY_SIZE(arizona_48k_rates), + .list = arizona_48k_rates, +}; + +static const int arizona_44k1_bclk_rates[] = { + -1, + 44100, + 58800, + 88200, + 117600, + 177640, + 235200, + 352800, + 470400, + 705600, + 940800, + 1411200, + 1881600, + 2882400, + 3763200, + 5644800, + 7526400, + 11289600, + 22579200, +}; + +static const unsigned int arizona_44k1_rates[] = { + 11025, + 22050, + 44100, + 88200, + 176400, + 352800, + 705600, +}; + +static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = { + .count = ARRAY_SIZE(arizona_44k1_rates), + .list = arizona_44k1_rates, +}; + +static int arizona_sr_vals[] = { + 0, + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 0, + 11025, + 22050, + 44100, + 88200, + 176400, + 352800, + 705600, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +static int arizona_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + const struct snd_pcm_hw_constraint_list *constraint; + unsigned int base_rate; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + base_rate = priv->sysclk; + break; + case ARIZONA_CLK_ASYNCCLK: + base_rate = priv->asyncclk; + break; + default: + return 0; + } + + if (base_rate % 8000) + constraint = &arizona_44k1_constraint; + else + constraint = &arizona_48k_constraint; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + constraint); +} + +static int arizona_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + const int *rates; + int i; + int bclk, lrclk, wl, frame, sr_val; + + if (params_rate(params) % 8000) + rates = &arizona_44k1_bclk_rates[0]; + else + rates = &arizona_48k_bclk_rates[0]; + + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { + if (rates[i] >= snd_soc_params_to_bclk(params) && + rates[i] % params_rate(params) == 0) { + bclk = i; + break; + } + } + if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + lrclk = snd_soc_params_to_bclk(params) / params_rate(params); + + arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", + rates[bclk], rates[bclk] / lrclk); + + wl = snd_pcm_format_width(params_format(params)); + frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, sr_val); + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 8); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); + snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, + ARIZONA_AIF1TX_BCPF_MASK, lrclk); + snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, + ARIZONA_AIF1RX_BCPF_MASK, lrclk); + snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, + ARIZONA_AIF1TX_WL_MASK | + ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); + snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, + ARIZONA_AIF1RX_WL_MASK | + ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); + + return 0; +} + +static const char *arizona_dai_clk_str(int clk_id) +{ + switch (clk_id) { + case ARIZONA_CLK_SYSCLK: + return "SYSCLK"; + case ARIZONA_CLK_ASYNCCLK: + return "ASYNCCLK"; + default: + return "Unknown clock"; + } +} + +static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct snd_soc_dapm_route routes[2]; + + switch (clk_id) { + case ARIZONA_CLK_SYSCLK: + case ARIZONA_CLK_ASYNCCLK: + break; + default: + return -EINVAL; + } + + if (clk_id == dai_priv->clk) + return 0; + + if (dai->active) { + dev_err(codec->dev, "Can't change clock on active DAI %d\n", + dai->id); + return -EBUSY; + } + + memset(&routes, 0, sizeof(routes)); + routes[0].sink = dai->driver->capture.stream_name; + routes[1].sink = dai->driver->playback.stream_name; + + routes[0].source = arizona_dai_clk_str(dai_priv->clk); + routes[1].source = arizona_dai_clk_str(dai_priv->clk); + snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); + + routes[0].source = arizona_dai_clk_str(clk_id); + routes[1].source = arizona_dai_clk_str(clk_id); + snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); + + return snd_soc_dapm_sync(&codec->dapm); +} + +const struct snd_soc_dai_ops arizona_dai_ops = { + .startup = arizona_startup, + .set_fmt = arizona_set_fmt, + .hw_params = arizona_hw_params, + .set_sysclk = arizona_dai_set_sysclk, +}; +EXPORT_SYMBOL_GPL(arizona_dai_ops); + +int arizona_init_dai(struct arizona_priv *priv, int id) +{ + struct arizona_dai_priv *dai_priv = &priv->dai[id]; + + dai_priv->clk = ARIZONA_CLK_SYSCLK; + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_dai); + +static irqreturn_t arizona_fll_lock(int irq, void *data) +{ + struct arizona_fll *fll = data; + + arizona_fll_dbg(fll, "Locked\n"); + + complete(&fll->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_fll_clock_ok(int irq, void *data) +{ + struct arizona_fll *fll = data; + + arizona_fll_dbg(fll, "clock OK\n"); + + complete(&fll->ok); + + return IRQ_HANDLED; +} + +static struct { + unsigned int min; + unsigned int max; + u16 fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +struct arizona_fll_cfg { + int n; + int theta; + int lambda; + int refdiv; + int outdiv; + int fratio; +}; + +static int arizona_calc_fll(struct arizona_fll *fll, + struct arizona_fll_cfg *cfg, + unsigned int Fref, + unsigned int Fout) +{ + unsigned int target, div, gcd_fll; + int i, ratio; + + arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout); + + /* Fref must be <=13.5MHz */ + div = 1; + cfg->refdiv = 0; + while ((Fref / div) > 13500000) { + div *= 2; + cfg->refdiv++; + + if (div > 8) { + arizona_fll_err(fll, + "Can't scale %dMHz in to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be over the targt; don't check the upper bound */ + div = 1; + while (Fout * div < 90000000 * fll->vco_mult) { + div++; + if (div > 7) { + arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * div / fll->vco_mult; + cfg->outdiv = div; + + arizona_fll_dbg(fll, "Fvco=%dHz\n", target); + + /* Find an appropraite FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + cfg->fratio = fll_fratios[i].fratio; + ratio = fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", + Fref); + return -EINVAL; + } + + cfg->n = target / (ratio * Fref); + + if (target % Fref) { + gcd_fll = gcd(target, ratio * Fref); + arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); + + cfg->theta = (target - (cfg->n * ratio * Fref)) + / gcd_fll; + cfg->lambda = (ratio * Fref) / gcd_fll; + } else { + cfg->theta = 0; + cfg->lambda = 0; + } + + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", + cfg->n, cfg->theta, cfg->lambda); + arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", + cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv); + + return 0; + +} + +static void arizona_apply_fll(struct arizona *arizona, unsigned int base, + struct arizona_fll_cfg *cfg, int source) +{ + regmap_update_bits(arizona->regmap, base + 3, + ARIZONA_FLL1_THETA_MASK, cfg->theta); + regmap_update_bits(arizona->regmap, base + 4, + ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda); + regmap_update_bits(arizona->regmap, base + 5, + ARIZONA_FLL1_FRATIO_MASK, + cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT); + regmap_update_bits(arizona->regmap, base + 6, + ARIZONA_FLL1_CLK_REF_DIV_MASK | + ARIZONA_FLL1_CLK_REF_SRC_MASK, + cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | + source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); + + regmap_update_bits(arizona->regmap, base + 2, + ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, + ARIZONA_FLL1_CTRL_UPD | cfg->n); +} + +int arizona_set_fll(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout) +{ + struct arizona *arizona = fll->arizona; + struct arizona_fll_cfg cfg, sync; + unsigned int reg, val; + int syncsrc; + bool ena; + int ret; + + ret = regmap_read(arizona->regmap, fll->base + 1, ®); + if (ret != 0) { + arizona_fll_err(fll, "Failed to read current state: %d\n", + ret); + return ret; + } + ena = reg & ARIZONA_FLL1_ENA; + + if (Fout) { + /* Do we have a 32kHz reference? */ + regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val); + switch (val & ARIZONA_CLK_32K_SRC_MASK) { + case ARIZONA_CLK_SRC_MCLK1: + case ARIZONA_CLK_SRC_MCLK2: + syncsrc = val & ARIZONA_CLK_32K_SRC_MASK; + break; + default: + syncsrc = -1; + } + + if (source == syncsrc) + syncsrc = -1; + + if (syncsrc >= 0) { + ret = arizona_calc_fll(fll, &sync, Fref, Fout); + if (ret != 0) + return ret; + + ret = arizona_calc_fll(fll, &cfg, 32768, Fout); + if (ret != 0) + return ret; + } else { + ret = arizona_calc_fll(fll, &cfg, Fref, Fout); + if (ret != 0) + return ret; + } + } else { + regmap_update_bits(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, 0); + regmap_update_bits(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, 0); + + if (ena) + pm_runtime_put_autosuspend(arizona->dev); + + return 0; + } + + regmap_update_bits(arizona->regmap, fll->base + 5, + ARIZONA_FLL1_OUTDIV_MASK, + cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); + + if (syncsrc >= 0) { + arizona_apply_fll(arizona, fll->base, &cfg, syncsrc); + arizona_apply_fll(arizona, fll->base + 0x10, &sync, source); + } else { + arizona_apply_fll(arizona, fll->base, &cfg, source); + } + + if (!ena) + pm_runtime_get(arizona->dev); + + /* Clear any pending completions */ + try_wait_for_completion(&fll->ok); + + regmap_update_bits(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); + if (syncsrc >= 0) + regmap_update_bits(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, + ARIZONA_FLL1_SYNC_ENA); + + ret = wait_for_completion_timeout(&fll->ok, + msecs_to_jiffies(25)); + if (ret == 0) + arizona_fll_warn(fll, "Timed out waiting for lock\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_set_fll); + +int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, + int ok_irq, struct arizona_fll *fll) +{ + int ret; + + init_completion(&fll->lock); + init_completion(&fll->ok); + + fll->id = id; + fll->base = base; + fll->arizona = arizona; + + snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id); + snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), + "FLL%d clock OK", id); + + ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, + arizona_fll_lock, fll); + if (ret != 0) { + dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", + id, ret); + } + + ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, + arizona_fll_clock_ok, fll); + if (ret != 0) { + dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n", + id, ret); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_fll); + +MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h new file mode 100644 index 000000000000..59caca8865e8 --- /dev/null +++ b/sound/soc/codecs/arizona.h @@ -0,0 +1,159 @@ +/* + * arizona.h - Wolfson Arizona class device shared support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ASOC_ARIZONA_H +#define _ASOC_ARIZONA_H + +#include <linux/completion.h> + +#include <sound/soc.h> + +#define ARIZONA_CLK_SYSCLK 1 +#define ARIZONA_CLK_ASYNCCLK 2 + +#define ARIZONA_CLK_SRC_MCLK1 0x0 +#define ARIZONA_CLK_SRC_MCLK2 0x1 +#define ARIZONA_CLK_SRC_FLL1 0x4 +#define ARIZONA_CLK_SRC_FLL2 0x5 +#define ARIZONA_CLK_SRC_AIF1BCLK 0x8 +#define ARIZONA_CLK_SRC_AIF2BCLK 0x9 +#define ARIZONA_CLK_SRC_AIF3BCLK 0xa + +#define ARIZONA_FLL_SRC_MCLK1 0 +#define ARIZONA_FLL_SRC_MCLK2 1 +#define ARIZONA_FLL_SRC_SLIMCLK 2 +#define ARIZONA_FLL_SRC_FLL1 3 +#define ARIZONA_FLL_SRC_FLL2 4 +#define ARIZONA_FLL_SRC_AIF1BCLK 5 +#define ARIZONA_FLL_SRC_AIF2BCLK 6 +#define ARIZONA_FLL_SRC_AIF3BCLK 7 +#define ARIZONA_FLL_SRC_AIF1LRCLK 8 +#define ARIZONA_FLL_SRC_AIF2LRCLK 9 +#define ARIZONA_FLL_SRC_AIF3LRCLK 10 + +#define ARIZONA_MIXER_VOL_MASK 0x00FE +#define ARIZONA_MIXER_VOL_SHIFT 1 +#define ARIZONA_MIXER_VOL_WIDTH 7 + +#define ARIZONA_MAX_DAI 3 + +struct arizona; + +struct arizona_dai_priv { + int clk; +}; + +struct arizona_priv { + struct arizona *arizona; + int sysclk; + int asyncclk; + struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; +}; + +#define ARIZONA_NUM_MIXER_INPUTS 57 + +extern const unsigned int arizona_mixer_tlv[]; +extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; +extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; + +#define ARIZONA_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv) + +#define ARIZONA_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \ + arizona_mixer_texts, arizona_mixer_values) + +#define ARIZONA_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_VALUE_ENUM("Route", name##_enum) + +#define ARIZONA_MIXER_ENUMS(name, base_reg) \ + static ARIZONA_MUX_ENUM_DECL(name##_in1_enum, base_reg); \ + static ARIZONA_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \ + static ARIZONA_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \ + static ARIZONA_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \ + static ARIZONA_MUX_CTL_DECL(name##_in1); \ + static ARIZONA_MUX_CTL_DECL(name##_in2); \ + static ARIZONA_MUX_CTL_DECL(name##_in3); \ + static ARIZONA_MUX_CTL_DECL(name##_in4) + +#define ARIZONA_MUX(name, ctrl) \ + SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define ARIZONA_MIXER_WIDGETS(name, name_str) \ + ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \ + ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \ + ARIZONA_MUX(name_str " Input 3", &name##_in3_mux), \ + ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define ARIZONA_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 4") + +extern const struct soc_enum arizona_lhpf1_mode; +extern const struct soc_enum arizona_lhpf2_mode; +extern const struct soc_enum arizona_lhpf3_mode; +extern const struct soc_enum arizona_lhpf4_mode; + +extern int arizona_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); +extern int arizona_out_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); + +extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir); + +extern const struct snd_soc_dai_ops arizona_dai_ops; + +#define ARIZONA_FLL_NAME_LEN 20 + +struct arizona_fll { + struct arizona *arizona; + int id; + unsigned int base; + unsigned int vco_mult; + struct completion lock; + struct completion ok; + + char lock_name[ARIZONA_FLL_NAME_LEN]; + char clock_ok_name[ARIZONA_FLL_NAME_LEN]; +}; + +extern int arizona_init_fll(struct arizona *arizona, int id, int base, + int lock_irq, int ok_irq, struct arizona_fll *fll); +extern int arizona_set_fll(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout); + +extern int arizona_init_dai(struct arizona_priv *priv, int dai); + +#endif diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index a7109413aef1..628daf6a1d97 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> @@ -1217,11 +1216,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, return -ENOMEM; cs42l52->dev = &i2c_client->dev; - cs42l52->regmap = regmap_init_i2c(i2c_client, &cs42l52_regmap); + cs42l52->regmap = devm_regmap_init_i2c(i2c_client, &cs42l52_regmap); if (IS_ERR(cs42l52->regmap)) { ret = PTR_ERR(cs42l52->regmap); dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); - goto err; + return ret; } i2c_set_clientdata(i2c_client, cs42l52); @@ -1243,7 +1242,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, dev_err(&i2c_client->dev, "CS42L52 Device ID (%X). Expected %X\n", devid, CS42L52_CHIP_ID); - goto err_regmap; + return ret; } regcache_cache_only(cs42l52->regmap, true); @@ -1251,23 +1250,13 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_cs42l52, &cs42l52_dai, 1); if (ret < 0) - goto err_regmap; + return ret; return 0; - -err_regmap: - regmap_exit(cs42l52->regmap); - -err: - return ret; } static int cs42l52_i2c_remove(struct i2c_client *client) { - struct cs42l52_private *cs42l52 = i2c_get_clientdata(client); - snd_soc_unregister_codec(&client->dev); - regmap_exit(cs42l52->regmap); - return 0; } diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index e0d45fdaa750..2c08c4cb465a 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1362,11 +1362,11 @@ static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client, i2c_set_clientdata(i2c_client, cs42l73); - cs42l73->regmap = regmap_init_i2c(i2c_client, &cs42l73_regmap); + cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap); if (IS_ERR(cs42l73->regmap)) { ret = PTR_ERR(cs42l73->regmap); dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); - goto err; + return ret; } /* initialize codec */ ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, ®); @@ -1384,13 +1384,13 @@ static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client, dev_err(&i2c_client->dev, "CS42L73 Device ID (%X). Expected %X\n", devid, CS42L73_DEVID); - goto err_regmap; + return ret; } ret = regmap_read(cs42l73->regmap, CS42L73_REVID, ®); if (ret < 0) { dev_err(&i2c_client->dev, "Get Revision ID failed\n"); - goto err_regmap; + return ret;; } dev_info(&i2c_client->dev, @@ -1402,23 +1402,13 @@ static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client, &soc_codec_dev_cs42l73, cs42l73_dai, ARRAY_SIZE(cs42l73_dai)); if (ret < 0) - goto err_regmap; + return ret; return 0; - -err_regmap: - regmap_exit(cs42l73->regmap); - -err: - return ret; } static __devexit int cs42l73_i2c_remove(struct i2c_client *client) { - struct cs42l73_private *cs42l73 = i2c_get_clientdata(client); - snd_soc_unregister_codec(&client->dev); - regmap_exit(cs42l73->regmap); - return 0; } diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c new file mode 100644 index 000000000000..01be2a320e21 --- /dev/null +++ b/sound/soc/codecs/da732x.c @@ -0,0 +1,1627 @@ +/* + * da732x.c --- Dialog DA732X ALSA SoC Audio Driver + * + * Copyright (C) 2012 Dialog Semiconductor GmbH + * + * Author: Michal Hajduk <Michal.Hajduk@diasemi.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/regmap.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfs.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 "da732x.h" +#include "da732x_reg.h" + + +struct da732x_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + + unsigned int sysclk; + bool pll_en; +}; + +/* + * da732x register cache - default settings + */ +static struct reg_default da732x_reg_cache[] = { + { DA732X_REG_REF1 , 0x02 }, + { DA732X_REG_BIAS_EN , 0x80 }, + { DA732X_REG_BIAS1 , 0x00 }, + { DA732X_REG_BIAS2 , 0x00 }, + { DA732X_REG_BIAS3 , 0x00 }, + { DA732X_REG_BIAS4 , 0x00 }, + { DA732X_REG_MICBIAS2 , 0x00 }, + { DA732X_REG_MICBIAS1 , 0x00 }, + { DA732X_REG_MICDET , 0x00 }, + { DA732X_REG_MIC1_PRE , 0x01 }, + { DA732X_REG_MIC1 , 0x40 }, + { DA732X_REG_MIC2_PRE , 0x01 }, + { DA732X_REG_MIC2 , 0x40 }, + { DA732X_REG_AUX1L , 0x75 }, + { DA732X_REG_AUX1R , 0x75 }, + { DA732X_REG_MIC3_PRE , 0x01 }, + { DA732X_REG_MIC3 , 0x40 }, + { DA732X_REG_INP_PINBIAS , 0x00 }, + { DA732X_REG_INP_ZC_EN , 0x00 }, + { DA732X_REG_INP_MUX , 0x50 }, + { DA732X_REG_HP_DET , 0x00 }, + { DA732X_REG_HPL_DAC_OFFSET , 0x00 }, + { DA732X_REG_HPL_DAC_OFF_CNTL , 0x00 }, + { DA732X_REG_HPL_OUT_OFFSET , 0x00 }, + { DA732X_REG_HPL , 0x40 }, + { DA732X_REG_HPL_VOL , 0x0F }, + { DA732X_REG_HPR_DAC_OFFSET , 0x00 }, + { DA732X_REG_HPR_DAC_OFF_CNTL , 0x00 }, + { DA732X_REG_HPR_OUT_OFFSET , 0x00 }, + { DA732X_REG_HPR , 0x40 }, + { DA732X_REG_HPR_VOL , 0x0F }, + { DA732X_REG_LIN2 , 0x4F }, + { DA732X_REG_LIN3 , 0x4F }, + { DA732X_REG_LIN4 , 0x4F }, + { DA732X_REG_OUT_ZC_EN , 0x00 }, + { DA732X_REG_HP_LIN1_GNDSEL , 0x00 }, + { DA732X_REG_CP_HP1 , 0x0C }, + { DA732X_REG_CP_HP2 , 0x03 }, + { DA732X_REG_CP_CTRL1 , 0x00 }, + { DA732X_REG_CP_CTRL2 , 0x99 }, + { DA732X_REG_CP_CTRL3 , 0x25 }, + { DA732X_REG_CP_LEVEL_MASK , 0x3F }, + { DA732X_REG_CP_DET , 0x00 }, + { DA732X_REG_CP_STATUS , 0x00 }, + { DA732X_REG_CP_THRESH1 , 0x00 }, + { DA732X_REG_CP_THRESH2 , 0x00 }, + { DA732X_REG_CP_THRESH3 , 0x00 }, + { DA732X_REG_CP_THRESH4 , 0x00 }, + { DA732X_REG_CP_THRESH5 , 0x00 }, + { DA732X_REG_CP_THRESH6 , 0x00 }, + { DA732X_REG_CP_THRESH7 , 0x00 }, + { DA732X_REG_CP_THRESH8 , 0x00 }, + { DA732X_REG_PLL_DIV_LO , 0x00 }, + { DA732X_REG_PLL_DIV_MID , 0x00 }, + { DA732X_REG_PLL_DIV_HI , 0x00 }, + { DA732X_REG_PLL_CTRL , 0x02 }, + { DA732X_REG_CLK_CTRL , 0xaa }, + { DA732X_REG_CLK_DSP , 0x07 }, + { DA732X_REG_CLK_EN1 , 0x00 }, + { DA732X_REG_CLK_EN2 , 0x00 }, + { DA732X_REG_CLK_EN3 , 0x00 }, + { DA732X_REG_CLK_EN4 , 0x00 }, + { DA732X_REG_CLK_EN5 , 0x00 }, + { DA732X_REG_AIF_MCLK , 0x00 }, + { DA732X_REG_AIFA1 , 0x02 }, + { DA732X_REG_AIFA2 , 0x00 }, + { DA732X_REG_AIFA3 , 0x08 }, + { DA732X_REG_AIFB1 , 0x02 }, + { DA732X_REG_AIFB2 , 0x00 }, + { DA732X_REG_AIFB3 , 0x08 }, + { DA732X_REG_PC_CTRL , 0xC0 }, + { DA732X_REG_DATA_ROUTE , 0x00 }, + { DA732X_REG_DSP_CTRL , 0x00 }, + { DA732X_REG_CIF_CTRL2 , 0x00 }, + { DA732X_REG_HANDSHAKE , 0x00 }, + { DA732X_REG_SPARE1_OUT , 0x00 }, + { DA732X_REG_SPARE2_OUT , 0x00 }, + { DA732X_REG_SPARE1_IN , 0x00 }, + { DA732X_REG_ADC1_PD , 0x00 }, + { DA732X_REG_ADC1_HPF , 0x00 }, + { DA732X_REG_ADC1_SEL , 0x00 }, + { DA732X_REG_ADC1_EQ12 , 0x00 }, + { DA732X_REG_ADC1_EQ34 , 0x00 }, + { DA732X_REG_ADC1_EQ5 , 0x00 }, + { DA732X_REG_ADC2_PD , 0x00 }, + { DA732X_REG_ADC2_HPF , 0x00 }, + { DA732X_REG_ADC2_SEL , 0x00 }, + { DA732X_REG_ADC2_EQ12 , 0x00 }, + { DA732X_REG_ADC2_EQ34 , 0x00 }, + { DA732X_REG_ADC2_EQ5 , 0x00 }, + { DA732X_REG_DAC1_HPF , 0x00 }, + { DA732X_REG_DAC1_L_VOL , 0x00 }, + { DA732X_REG_DAC1_R_VOL , 0x00 }, + { DA732X_REG_DAC1_SEL , 0x00 }, + { DA732X_REG_DAC1_SOFTMUTE , 0x00 }, + { DA732X_REG_DAC1_EQ12 , 0x00 }, + { DA732X_REG_DAC1_EQ34 , 0x00 }, + { DA732X_REG_DAC1_EQ5 , 0x00 }, + { DA732X_REG_DAC2_HPF , 0x00 }, + { DA732X_REG_DAC2_L_VOL , 0x00 }, + { DA732X_REG_DAC2_R_VOL , 0x00 }, + { DA732X_REG_DAC2_SEL , 0x00 }, + { DA732X_REG_DAC2_SOFTMUTE , 0x00 }, + { DA732X_REG_DAC2_EQ12 , 0x00 }, + { DA732X_REG_DAC2_EQ34 , 0x00 }, + { DA732X_REG_DAC2_EQ5 , 0x00 }, + { DA732X_REG_DAC3_HPF , 0x00 }, + { DA732X_REG_DAC3_VOL , 0x00 }, + { DA732X_REG_DAC3_SEL , 0x00 }, + { DA732X_REG_DAC3_SOFTMUTE , 0x00 }, + { DA732X_REG_DAC3_EQ12 , 0x00 }, + { DA732X_REG_DAC3_EQ34 , 0x00 }, + { DA732X_REG_DAC3_EQ5 , 0x00 }, + { DA732X_REG_BIQ_BYP , 0x00 }, + { DA732X_REG_DMA_CMD , 0x00 }, + { DA732X_REG_DMA_ADDR0 , 0x00 }, + { DA732X_REG_DMA_ADDR1 , 0x00 }, + { DA732X_REG_DMA_DATA0 , 0x00 }, + { DA732X_REG_DMA_DATA1 , 0x00 }, + { DA732X_REG_DMA_DATA2 , 0x00 }, + { DA732X_REG_DMA_DATA3 , 0x00 }, + { DA732X_REG_UNLOCK , 0x00 }, +}; + +static inline int da732x_get_input_div(struct snd_soc_codec *codec, int sysclk) +{ + int val; + int ret; + + if (sysclk < DA732X_MCLK_10MHZ) { + val = DA732X_MCLK_RET_0_10MHZ; + ret = DA732X_MCLK_VAL_0_10MHZ; + } else if ((sysclk >= DA732X_MCLK_10MHZ) && + (sysclk < DA732X_MCLK_20MHZ)) { + val = DA732X_MCLK_RET_10_20MHZ; + ret = DA732X_MCLK_VAL_10_20MHZ; + } else if ((sysclk >= DA732X_MCLK_20MHZ) && + (sysclk < DA732X_MCLK_40MHZ)) { + val = DA732X_MCLK_RET_20_40MHZ; + ret = DA732X_MCLK_VAL_20_40MHZ; + } else if ((sysclk >= DA732X_MCLK_40MHZ) && + (sysclk <= DA732X_MCLK_54MHZ)) { + val = DA732X_MCLK_RET_40_54MHZ; + ret = DA732X_MCLK_VAL_40_54MHZ; + } else { + return -EINVAL; + } + + snd_soc_write(codec, DA732X_REG_PLL_CTRL, val); + + return ret; +} + +static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state) +{ + switch (state) { + case DA732X_ENABLE_CP: + snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN); + snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_EN | + DA732X_HP_CP_REG | DA732X_HP_CP_PULSESKIP); + snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA732X_CP_EN | + DA732X_CP_CTRL_CPVDD1); + snd_soc_write(codec, DA732X_REG_CP_CTRL2, + DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST); + snd_soc_write(codec, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ); + break; + case DA732X_DISABLE_CP: + snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS); + snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS); + snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS); + break; + default: + pr_err(KERN_ERR "Wrong charge pump state\n"); + break; + } +} + +static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, DA732X_MIC_PRE_VOL_DB_MIN, + DA732X_MIC_PRE_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, DA732X_MIC_VOL_DB_MIN, + DA732X_MIC_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(aux_pga_tlv, DA732X_AUX_VOL_DB_MIN, + DA732X_AUX_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(hp_pga_tlv, DA732X_HP_VOL_DB_MIN, + DA732X_AUX_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(lin2_pga_tlv, DA732X_LIN2_VOL_DB_MIN, + DA732X_LIN2_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(lin3_pga_tlv, DA732X_LIN3_VOL_DB_MIN, + DA732X_LIN3_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(lin4_pga_tlv, DA732X_LIN4_VOL_DB_MIN, + DA732X_LIN4_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(adc_pga_tlv, DA732X_ADC_VOL_DB_MIN, + DA732X_ADC_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(dac_pga_tlv, DA732X_DAC_VOL_DB_MIN, + DA732X_DAC_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(eq_band_pga_tlv, DA732X_EQ_BAND_VOL_DB_MIN, + DA732X_EQ_BAND_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(eq_overall_tlv, DA732X_EQ_OVERALL_VOL_DB_MIN, + DA732X_EQ_OVERALL_VOL_DB_INC, 0); + +/* High Pass Filter */ +static const char *da732x_hpf_mode[] = { + "Disable", "Music", "Voice", +}; + +static const char *da732x_hpf_music[] = { + "1.8Hz", "3.75Hz", "7.5Hz", "15Hz", +}; + +static const char *da732x_hpf_voice[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", + "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da732x_dac1_hpf_mode_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT, + DA732X_HPF_MODE_MAX, da732x_hpf_mode) +}; + +static const struct soc_enum da732x_dac2_hpf_mode_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT, + DA732X_HPF_MODE_MAX, da732x_hpf_mode) +}; + +static const struct soc_enum da732x_dac3_hpf_mode_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT, + DA732X_HPF_MODE_MAX, da732x_hpf_mode) +}; + +static const struct soc_enum da732x_adc1_hpf_mode_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT, + DA732X_HPF_MODE_MAX, da732x_hpf_mode) +}; + +static const struct soc_enum da732x_adc2_hpf_mode_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT, + DA732X_HPF_MODE_MAX, da732x_hpf_mode) +}; + +static const struct soc_enum da732x_dac1_hp_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT, + DA732X_HPF_MUSIC_MAX, da732x_hpf_music) +}; + +static const struct soc_enum da732x_dac2_hp_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT, + DA732X_HPF_MUSIC_MAX, da732x_hpf_music) +}; + +static const struct soc_enum da732x_dac3_hp_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT, + DA732X_HPF_MUSIC_MAX, da732x_hpf_music) +}; + +static const struct soc_enum da732x_adc1_hp_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT, + DA732X_HPF_MUSIC_MAX, da732x_hpf_music) +}; + +static const struct soc_enum da732x_adc2_hp_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT, + DA732X_HPF_MUSIC_MAX, da732x_hpf_music) +}; + +static const struct soc_enum da732x_dac1_voice_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT, + DA732X_HPF_VOICE_MAX, da732x_hpf_voice) +}; + +static const struct soc_enum da732x_dac2_voice_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT, + DA732X_HPF_VOICE_MAX, da732x_hpf_voice) +}; + +static const struct soc_enum da732x_dac3_voice_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT, + DA732X_HPF_VOICE_MAX, da732x_hpf_voice) +}; + +static const struct soc_enum da732x_adc1_voice_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT, + DA732X_HPF_VOICE_MAX, da732x_hpf_voice) +}; + +static const struct soc_enum da732x_adc2_voice_filter_enum[] = { + SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT, + DA732X_HPF_VOICE_MAX, da732x_hpf_voice) +}; + + +static int da732x_hpf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; + unsigned int reg = enum_ctrl->reg; + unsigned int sel = ucontrol->value.integer.value[0]; + unsigned int bits; + + switch (sel) { + case DA732X_HPF_DISABLED: + bits = DA732X_HPF_DIS; + break; + case DA732X_HPF_VOICE: + bits = DA732X_HPF_VOICE_EN; + break; + case DA732X_HPF_MUSIC: + bits = DA732X_HPF_MUSIC_EN; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, DA732X_HPF_MASK, bits); + + return 0; +} + +static int da732x_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; + unsigned int reg = enum_ctrl->reg; + int val; + + val = snd_soc_read(codec, reg) & DA732X_HPF_MASK; + + switch (val) { + case DA732X_HPF_VOICE_EN: + ucontrol->value.integer.value[0] = DA732X_HPF_VOICE; + break; + case DA732X_HPF_MUSIC_EN: + ucontrol->value.integer.value[0] = DA732X_HPF_MUSIC; + break; + default: + ucontrol->value.integer.value[0] = DA732X_HPF_DISABLED; + break; + } + + return 0; +} + +static const struct snd_kcontrol_new da732x_snd_controls[] = { + /* Input PGAs */ + SOC_SINGLE_RANGE_TLV("MIC1 Boost Volume", DA732X_REG_MIC1_PRE, + DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN, + DA732X_MICBOOST_MAX, 0, mic_boost_tlv), + SOC_SINGLE_RANGE_TLV("MIC2 Boost Volume", DA732X_REG_MIC2_PRE, + DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN, + DA732X_MICBOOST_MAX, 0, mic_boost_tlv), + SOC_SINGLE_RANGE_TLV("MIC3 Boost Volume", DA732X_REG_MIC3_PRE, + DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN, + DA732X_MICBOOST_MAX, 0, mic_boost_tlv), + + /* MICs */ + SOC_SINGLE("MIC1 Switch", DA732X_REG_MIC1, DA732X_MIC_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_RANGE_TLV("MIC1 Volume", DA732X_REG_MIC1, + DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN, + DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv), + SOC_SINGLE("MIC2 Switch", DA732X_REG_MIC2, DA732X_MIC_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_RANGE_TLV("MIC2 Volume", DA732X_REG_MIC2, + DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN, + DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv), + SOC_SINGLE("MIC3 Switch", DA732X_REG_MIC3, DA732X_MIC_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_RANGE_TLV("MIC3 Volume", DA732X_REG_MIC3, + DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN, + DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv), + + /* AUXs */ + SOC_SINGLE("AUX1L Switch", DA732X_REG_AUX1L, DA732X_AUX_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("AUX1L Volume", DA732X_REG_AUX1L, + DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX, + DA732X_NO_INVERT, aux_pga_tlv), + SOC_SINGLE("AUX1R Switch", DA732X_REG_AUX1R, DA732X_AUX_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("AUX1R Volume", DA732X_REG_AUX1R, + DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX, + DA732X_NO_INVERT, aux_pga_tlv), + + /* ADCs */ + SOC_DOUBLE_TLV("ADC1 Volume", DA732X_REG_ADC1_SEL, + DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT, + DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv), + + SOC_DOUBLE_TLV("ADC2 Volume", DA732X_REG_ADC2_SEL, + DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT, + DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv), + + /* DACs */ + SOC_DOUBLE("Digital Playback DAC12 Switch", DA732X_REG_DAC1_SEL, + DA732X_DACL_MUTE_SHIFT, DA732X_DACR_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_DOUBLE_R_TLV("Digital Playback DAC12 Volume", DA732X_REG_DAC1_L_VOL, + DA732X_REG_DAC1_R_VOL, DA732X_DAC_VOL_SHIFT, + DA732X_DAC_VOL_VAL_MAX, DA732X_INVERT, dac_pga_tlv), + SOC_SINGLE("Digital Playback DAC3 Switch", DA732X_REG_DAC2_SEL, + DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Digital Playback DAC3 Volume", DA732X_REG_DAC2_L_VOL, + DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX, + DA732X_INVERT, dac_pga_tlv), + SOC_SINGLE("Digital Playback DAC4 Switch", DA732X_REG_DAC2_SEL, + DA732X_DACR_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Digital Playback DAC4 Volume", DA732X_REG_DAC2_R_VOL, + DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX, + DA732X_INVERT, dac_pga_tlv), + SOC_SINGLE("Digital Playback DAC5 Switch", DA732X_REG_DAC3_SEL, + DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Digital Playback DAC5 Volume", DA732X_REG_DAC3_VOL, + DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX, + DA732X_INVERT, dac_pga_tlv), + + /* High Pass Filters */ + SOC_ENUM_EXT("DAC1 High Pass Filter Mode", + da732x_dac1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("DAC1 High Pass Filter", da732x_dac1_hp_filter_enum), + SOC_ENUM("DAC1 Voice Filter", da732x_dac1_voice_filter_enum), + + SOC_ENUM_EXT("DAC2 High Pass Filter Mode", + da732x_dac2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("DAC2 High Pass Filter", da732x_dac2_hp_filter_enum), + SOC_ENUM("DAC2 Voice Filter", da732x_dac2_voice_filter_enum), + + SOC_ENUM_EXT("DAC3 High Pass Filter Mode", + da732x_dac3_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("DAC3 High Pass Filter", da732x_dac3_hp_filter_enum), + SOC_ENUM("DAC3 Filter Mode", da732x_dac3_voice_filter_enum), + + SOC_ENUM_EXT("ADC1 High Pass Filter Mode", + da732x_adc1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("ADC1 High Pass Filter", da732x_adc1_hp_filter_enum), + SOC_ENUM("ADC1 Voice Filter", da732x_adc1_voice_filter_enum), + + SOC_ENUM_EXT("ADC2 High Pass Filter Mode", + da732x_adc2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("ADC2 High Pass Filter", da732x_adc2_hp_filter_enum), + SOC_ENUM("ADC2 Voice Filter", da732x_adc2_voice_filter_enum), + + /* Equalizers */ + SOC_SINGLE("ADC1 EQ Switch", DA732X_REG_ADC1_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("ADC1 EQ Band 1 Volume", DA732X_REG_ADC1_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 2 Volume", DA732X_REG_ADC1_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 3 Volume", DA732X_REG_ADC1_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 4 Volume", DA732X_REG_ADC1_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 5 Volume", DA732X_REG_ADC1_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Overall Volume", DA732X_REG_ADC1_EQ5, + DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX, + DA732X_INVERT, eq_overall_tlv), + + SOC_SINGLE("ADC2 EQ Switch", DA732X_REG_ADC2_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("ADC2 EQ Band 1 Volume", DA732X_REG_ADC2_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC2 EQ Band 2 Volume", DA732X_REG_ADC2_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC2 EQ Band 3 Volume", DA732X_REG_ADC2_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ACD2 EQ Band 4 Volume", DA732X_REG_ADC2_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ACD2 EQ Band 5 Volume", DA732X_REG_ADC2_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC2 EQ Overall Volume", DA732X_REG_ADC1_EQ5, + DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX, + DA732X_INVERT, eq_overall_tlv), + + SOC_SINGLE("DAC1 EQ Switch", DA732X_REG_DAC1_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("DAC1 EQ Band 1 Volume", DA732X_REG_DAC1_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 2 Volume", DA732X_REG_DAC1_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 3 Volume", DA732X_REG_DAC1_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 4 Volume", DA732X_REG_DAC1_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 5 Volume", DA732X_REG_DAC1_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + + SOC_SINGLE("DAC2 EQ Switch", DA732X_REG_DAC2_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("DAC2 EQ Band 1 Volume", DA732X_REG_DAC2_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 2 Volume", DA732X_REG_DAC2_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 3 Volume", DA732X_REG_DAC2_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 4 Volume", DA732X_REG_DAC2_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 5 Volume", DA732X_REG_DAC2_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + + SOC_SINGLE("DAC3 EQ Switch", DA732X_REG_DAC3_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("DAC3 EQ Band 1 Volume", DA732X_REG_DAC3_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 2 Volume", DA732X_REG_DAC3_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 3 Volume", DA732X_REG_DAC3_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 4 Volume", DA732X_REG_DAC3_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 5 Volume", DA732X_REG_DAC3_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + + /* Lineout 2 Reciever*/ + SOC_SINGLE("Lineout 2 Switch", DA732X_REG_LIN2, DA732X_LOUT_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Lineout 2 Volume", DA732X_REG_LIN2, + DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX, + DA732X_NO_INVERT, lin2_pga_tlv), + + /* Lineout 3 SPEAKER*/ + SOC_SINGLE("Lineout 3 Switch", DA732X_REG_LIN3, DA732X_LOUT_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Lineout 3 Volume", DA732X_REG_LIN3, + DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX, + DA732X_NO_INVERT, lin3_pga_tlv), + + /* Lineout 4 */ + SOC_SINGLE("Lineout 4 Switch", DA732X_REG_LIN4, DA732X_LOUT_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Lineout 4 Volume", DA732X_REG_LIN4, + DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX, + DA732X_NO_INVERT, lin4_pga_tlv), + + /* Headphones */ + SOC_DOUBLE_R("Headphone Switch", DA732X_REG_HPR, DA732X_REG_HPL, + DA732X_HP_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_DOUBLE_R_TLV("Headphone Volume", DA732X_REG_HPL_VOL, + DA732X_REG_HPR_VOL, DA732X_HP_VOL_SHIFT, + DA732X_HP_VOL_VAL_MAX, DA732X_NO_INVERT, hp_pga_tlv), +}; + +static int da732x_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (w->reg) { + case DA732X_REG_ADC1_PD: + snd_soc_update_bits(codec, DA732X_REG_CLK_EN3, + DA732X_ADCA_BB_CLK_EN, + DA732X_ADCA_BB_CLK_EN); + break; + case DA732X_REG_ADC2_PD: + snd_soc_update_bits(codec, DA732X_REG_CLK_EN3, + DA732X_ADCC_BB_CLK_EN, + DA732X_ADCC_BB_CLK_EN); + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK, + DA732X_ADC_SET_ACT); + snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK, + DA732X_ADC_ON); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK, + DA732X_ADC_OFF); + snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK, + DA732X_ADC_SET_RST); + + switch (w->reg) { + case DA732X_REG_ADC1_PD: + snd_soc_update_bits(codec, DA732X_REG_CLK_EN3, + DA732X_ADCA_BB_CLK_EN, 0); + break; + case DA732X_REG_ADC2_PD: + snd_soc_update_bits(codec, DA732X_REG_CLK_EN3, + DA732X_ADCC_BB_CLK_EN, 0); + break; + default: + return -EINVAL; + } + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int da732x_out_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, w->reg, + (1 << w->shift) | DA732X_OUT_HIZ_EN, + (1 << w->shift) | DA732X_OUT_HIZ_EN); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, w->reg, + (1 << w->shift) | DA732X_OUT_HIZ_EN, + (1 << w->shift) | DA732X_OUT_HIZ_DIS); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const char *adcl_text[] = { + "AUX1L", "MIC1" +}; + +static const char *adcr_text[] = { + "AUX1R", "MIC2", "MIC3" +}; + +static const char *enable_text[] = { + "Disabled", + "Enabled" +}; + +/* ADC1LMUX */ +static const struct soc_enum adc1l_enum = + SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT, + DA732X_ADCL_MUX_MAX, adcl_text); +static const struct snd_kcontrol_new adc1l_mux = + SOC_DAPM_ENUM("ADC Route", adc1l_enum); + +/* ADC1RMUX */ +static const struct soc_enum adc1r_enum = + SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT, + DA732X_ADCR_MUX_MAX, adcr_text); +static const struct snd_kcontrol_new adc1r_mux = + SOC_DAPM_ENUM("ADC Route", adc1r_enum); + +/* ADC2LMUX */ +static const struct soc_enum adc2l_enum = + SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT, + DA732X_ADCL_MUX_MAX, adcl_text); +static const struct snd_kcontrol_new adc2l_mux = + SOC_DAPM_ENUM("ADC Route", adc2l_enum); + +/* ADC2RMUX */ +static const struct soc_enum adc2r_enum = + SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT, + DA732X_ADCR_MUX_MAX, adcr_text); + +static const struct snd_kcontrol_new adc2r_mux = + SOC_DAPM_ENUM("ADC Route", adc2r_enum); + +static const struct soc_enum da732x_hp_left_output = + SOC_ENUM_SINGLE(DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT, + DA732X_DAC_EN_MAX, enable_text); + +static const struct snd_kcontrol_new hpl_mux = + SOC_DAPM_ENUM("HPL Switch", da732x_hp_left_output); + +static const struct soc_enum da732x_hp_right_output = + SOC_ENUM_SINGLE(DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT, + DA732X_DAC_EN_MAX, enable_text); + +static const struct snd_kcontrol_new hpr_mux = + SOC_DAPM_ENUM("HPR Switch", da732x_hp_right_output); + +static const struct soc_enum da732x_speaker_output = + SOC_ENUM_SINGLE(DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT, + DA732X_DAC_EN_MAX, enable_text); + +static const struct snd_kcontrol_new spk_mux = + SOC_DAPM_ENUM("SPK Switch", da732x_speaker_output); + +static const struct soc_enum da732x_lout4_output = + SOC_ENUM_SINGLE(DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT, + DA732X_DAC_EN_MAX, enable_text); + +static const struct snd_kcontrol_new lout4_mux = + SOC_DAPM_ENUM("LOUT4 Switch", da732x_lout4_output); + +static const struct soc_enum da732x_lout2_output = + SOC_ENUM_SINGLE(DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT, + DA732X_DAC_EN_MAX, enable_text); + +static const struct snd_kcontrol_new lout2_mux = + SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output); + +static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = { + /* Supplies */ + SND_SOC_DAPM_SUPPLY("ADC1 Supply", DA732X_REG_ADC1_PD, 0, + DA732X_NO_INVERT, da732x_adc_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("ADC2 Supply", DA732X_REG_ADC2_PD, 0, + DA732X_NO_INVERT, da732x_adc_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DAC1 CLK", DA732X_REG_CLK_EN4, + DA732X_DACA_BB_CLK_SHIFT, DA732X_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC2 CLK", DA732X_REG_CLK_EN4, + DA732X_DACC_BB_CLK_SHIFT, DA732X_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC3 CLK", DA732X_REG_CLK_EN5, + DA732X_DACE_BB_CLK_SHIFT, DA732X_NO_INVERT, + NULL, 0), + + /* Micbias */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", DA732X_REG_MICBIAS1, + DA732X_MICBIAS_EN_SHIFT, + DA732X_NO_INVERT, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", DA732X_REG_MICBIAS2, + DA732X_MICBIAS_EN_SHIFT, + DA732X_NO_INVERT, NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + SND_SOC_DAPM_INPUT("AUX1L"), + SND_SOC_DAPM_INPUT("AUX1R"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("ClassD"), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1L", NULL, DA732X_REG_ADC1_SEL, + DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_ADC("ADC1R", NULL, DA732X_REG_ADC1_SEL, + DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_ADC("ADC2L", NULL, DA732X_REG_ADC2_SEL, + DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_ADC("ADC2R", NULL, DA732X_REG_ADC2_SEL, + DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1L", NULL, DA732X_REG_DAC1_SEL, + DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC1R", NULL, DA732X_REG_DAC1_SEL, + DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC2L", NULL, DA732X_REG_DAC2_SEL, + DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC2R", NULL, DA732X_REG_DAC2_SEL, + DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC3", NULL, DA732X_REG_DAC3_SEL, + DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT), + + /* Input Pgas */ + SND_SOC_DAPM_PGA("MIC1 PGA", DA732X_REG_MIC1, DA732X_MIC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 PGA", DA732X_REG_MIC2, DA732X_MIC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC3 PGA", DA732X_REG_MIC3, DA732X_MIC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX1L PGA", DA732X_REG_AUX1L, DA732X_AUX_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX1R PGA", DA732X_REG_AUX1R, DA732X_AUX_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_PGA_E("HP Left", DA732X_REG_HPL, DA732X_HP_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HP Right", DA732X_REG_HPR, DA732X_HP_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LIN2", DA732X_REG_LIN2, DA732X_LIN_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LIN3", DA732X_REG_LIN3, DA732X_LIN_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LIN4", DA732X_REG_LIN4, DA732X_LIN_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* MUXs */ + SND_SOC_DAPM_MUX("ADC1 Left MUX", SND_SOC_NOPM, 0, 0, &adc1l_mux), + SND_SOC_DAPM_MUX("ADC1 Right MUX", SND_SOC_NOPM, 0, 0, &adc1r_mux), + SND_SOC_DAPM_MUX("ADC2 Left MUX", SND_SOC_NOPM, 0, 0, &adc2l_mux), + SND_SOC_DAPM_MUX("ADC2 Right MUX", SND_SOC_NOPM, 0, 0, &adc2r_mux), + + SND_SOC_DAPM_MUX("HP Left MUX", SND_SOC_NOPM, 0, 0, &hpl_mux), + SND_SOC_DAPM_MUX("HP Right MUX", SND_SOC_NOPM, 0, 0, &hpr_mux), + SND_SOC_DAPM_MUX("Speaker MUX", SND_SOC_NOPM, 0, 0, &spk_mux), + SND_SOC_DAPM_MUX("LOUT2 MUX", SND_SOC_NOPM, 0, 0, &lout2_mux), + SND_SOC_DAPM_MUX("LOUT4 MUX", SND_SOC_NOPM, 0, 0, &lout4_mux), + + /* AIF interfaces */ + SND_SOC_DAPM_AIF_OUT("AIFA Output", "AIFA Capture", 0, DA732X_REG_AIFA3, + DA732X_AIF_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("AIFA Input", "AIFA Playback", 0, DA732X_REG_AIFA3, + DA732X_AIF_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("AIFB Output", "AIFB Capture", 0, DA732X_REG_AIFB3, + DA732X_AIF_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("AIFB Input", "AIFB Playback", 0, DA732X_REG_AIFB3, + DA732X_AIF_EN_SHIFT, 0), +}; + +static const struct snd_soc_dapm_route da732x_dapm_routes[] = { + /* Inputs */ + {"AUX1L PGA", "NULL", "AUX1L"}, + {"AUX1R PGA", "NULL", "AUX1R"}, + {"MIC1 PGA", NULL, "MIC1"}, + {"MIC2 PGA", "NULL", "MIC2"}, + {"MIC3 PGA", "NULL", "MIC3"}, + + /* Capture Path */ + {"ADC1 Left MUX", "MIC1", "MIC1 PGA"}, + {"ADC1 Left MUX", "AUX1L", "AUX1L PGA"}, + + {"ADC1 Right MUX", "AUX1R", "AUX1R PGA"}, + {"ADC1 Right MUX", "MIC2", "MIC2 PGA"}, + {"ADC1 Right MUX", "MIC3", "MIC3 PGA"}, + + {"ADC2 Left MUX", "AUX1L", "AUX1L PGA"}, + {"ADC2 Left MUX", "MIC1", "MIC1 PGA"}, + + {"ADC2 Right MUX", "AUX1R", "AUX1R PGA"}, + {"ADC2 Right MUX", "MIC2", "MIC2 PGA"}, + {"ADC2 Right MUX", "MIC3", "MIC3 PGA"}, + + {"ADC1L", NULL, "ADC1 Supply"}, + {"ADC1R", NULL, "ADC1 Supply"}, + {"ADC2L", NULL, "ADC2 Supply"}, + {"ADC2R", NULL, "ADC2 Supply"}, + + {"ADC1L", NULL, "ADC1 Left MUX"}, + {"ADC1R", NULL, "ADC1 Right MUX"}, + {"ADC2L", NULL, "ADC2 Left MUX"}, + {"ADC2R", NULL, "ADC2 Right MUX"}, + + {"AIFA Output", NULL, "ADC1L"}, + {"AIFA Output", NULL, "ADC1R"}, + {"AIFB Output", NULL, "ADC2L"}, + {"AIFB Output", NULL, "ADC2R"}, + + {"HP Left MUX", "Enabled", "AIFA Input"}, + {"HP Right MUX", "Enabled", "AIFA Input"}, + {"Speaker MUX", "Enabled", "AIFB Input"}, + {"LOUT2 MUX", "Enabled", "AIFB Input"}, + {"LOUT4 MUX", "Enabled", "AIFB Input"}, + + {"DAC1L", NULL, "DAC1 CLK"}, + {"DAC1R", NULL, "DAC1 CLK"}, + {"DAC2L", NULL, "DAC2 CLK"}, + {"DAC2R", NULL, "DAC2 CLK"}, + {"DAC3", NULL, "DAC3 CLK"}, + + {"DAC1L", NULL, "HP Left MUX"}, + {"DAC1R", NULL, "HP Right MUX"}, + {"DAC2L", NULL, "Speaker MUX"}, + {"DAC2R", NULL, "LOUT4 MUX"}, + {"DAC3", NULL, "LOUT2 MUX"}, + + /* Output Pgas */ + {"HP Left", NULL, "DAC1L"}, + {"HP Right", NULL, "DAC1R"}, + {"LIN3", NULL, "DAC2L"}, + {"LIN4", NULL, "DAC2R"}, + {"LIN2", NULL, "DAC3"}, + + /* Outputs */ + {"ClassD", NULL, "LIN3"}, + {"LOUTL", NULL, "LIN2"}, + {"LOUTR", NULL, "LIN4"}, + {"HPL", NULL, "HP Left"}, + {"HPR", NULL, "HP Right"}, +}; + +static int da732x_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; + u32 aif = 0; + u32 reg_aif; + u32 fs; + + reg_aif = dai->driver->base; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + aif |= DA732X_AIF_WORD_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + aif |= DA732X_AIF_WORD_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + aif |= DA732X_AIF_WORD_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + aif |= DA732X_AIF_WORD_32; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000: + fs = DA732X_SR_8KHZ; + break; + case 11025: + fs = DA732X_SR_11_025KHZ; + break; + case 12000: + fs = DA732X_SR_12KHZ; + break; + case 16000: + fs = DA732X_SR_16KHZ; + break; + case 22050: + fs = DA732X_SR_22_05KHZ; + break; + case 24000: + fs = DA732X_SR_24KHZ; + break; + case 32000: + fs = DA732X_SR_32KHZ; + break; + case 44100: + fs = DA732X_SR_44_1KHZ; + break; + case 48000: + fs = DA732X_SR_48KHZ; + break; + case 88100: + fs = DA732X_SR_88_1KHZ; + break; + case 96000: + fs = DA732X_SR_96KHZ; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, reg_aif, DA732X_AIF_WORD_MASK, aif); + snd_soc_update_bits(codec, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs); + + return 0; +} + +static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u32 aif_mclk, pc_count; + u32 reg_aif1, aif1; + u32 reg_aif3, aif3; + + switch (dai->id) { + case DA732X_DAI_ID1: + reg_aif1 = DA732X_REG_AIFA1; + reg_aif3 = DA732X_REG_AIFA3; + pc_count = DA732X_PC_PULSE_AIFA | DA732X_PC_RESYNC_NOT_AUT | + DA732X_PC_SAME; + break; + case DA732X_DAI_ID2: + reg_aif1 = DA732X_REG_AIFB1; + reg_aif3 = DA732X_REG_AIFB3; + pc_count = DA732X_PC_PULSE_AIFB | DA732X_PC_RESYNC_NOT_AUT | + DA732X_PC_SAME; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + aif1 = DA732X_AIF_SLAVE; + aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif1 = DA732X_AIF_CLK_FROM_SRC; + aif_mclk = DA732X_CLK_GENERATION_AIF_A; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aif3 = DA732X_AIF_I2S_MODE; + break; + case SND_SOC_DAIFMT_RIGHT_J: + aif3 = DA732X_AIF_RIGHT_J_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + aif3 = DA732X_AIF_LEFT_J_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + aif3 = DA732X_AIF_DSP_MODE; + break; + default: + return -EINVAL; + } + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif3 |= DA732X_AIF_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: + aif3 |= DA732X_AIF_BCLK_INV | DA732X_AIF_WCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif3 |= DA732X_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif3 |= DA732X_AIF_WCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, DA732X_REG_AIF_MCLK, aif_mclk); + snd_soc_update_bits(codec, reg_aif1, DA732X_AIF1_CLK_MASK, aif1); + snd_soc_update_bits(codec, reg_aif3, DA732X_AIF_BCLK_INV | + DA732X_AIF_WCLK_INV | DA732X_AIF_MODE_MASK, aif3); + snd_soc_write(codec, DA732X_REG_PC_CTRL, pc_count); + + return 0; +} + + + +static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec); + int fref, indiv; + u8 div_lo, div_mid, div_hi; + u64 frac_div; + + /* Disable PLL */ + if (freq_out == 0) { + snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL, + DA732X_PLL_EN, 0); + da732x->pll_en = false; + return 0; + } + + if (da732x->pll_en) + return -EBUSY; + + if (source == DA732X_SRCCLK_MCLK) { + /* Validate Sysclk rate */ + switch (da732x->sysclk) { + case 11290000: + case 12288000: + case 22580000: + case 24576000: + case 45160000: + case 49152000: + snd_soc_write(codec, DA732X_REG_PLL_CTRL, + DA732X_PLL_BYPASS); + return 0; + default: + dev_err(codec->dev, + "Cannot use PLL Bypass, invalid SYSCLK rate\n"); + return -EINVAL; + } + } + + indiv = da732x_get_input_div(codec, da732x->sysclk); + if (indiv < 0) + return indiv; + + fref = (da732x->sysclk / indiv); + div_hi = freq_out / fref; + frac_div = (u64)(freq_out % fref) * 8192ULL; + do_div(frac_div, fref); + div_mid = (frac_div >> DA732X_1BYTE_SHIFT) & DA732X_U8_MASK; + div_lo = (frac_div) & DA732X_U8_MASK; + + snd_soc_write(codec, DA732X_REG_PLL_DIV_LO, div_lo); + snd_soc_write(codec, DA732X_REG_PLL_DIV_MID, div_mid); + snd_soc_write(codec, DA732X_REG_PLL_DIV_HI, div_hi); + + snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL, DA732X_PLL_EN, + DA732X_PLL_EN); + + da732x->pll_en = true; + + return 0; +} + +static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec); + + da732x->sysclk = freq; + + return 0; +} + +#define DA732X_RATES SNDRV_PCM_RATE_8000_96000 + +#define DA732X_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 da732x_dai1_ops = { + .hw_params = da732x_hw_params, + .set_fmt = da732x_set_dai_fmt, + .set_sysclk = da732x_set_dai_sysclk, +}; + +static struct snd_soc_dai_ops da732x_dai2_ops = { + .hw_params = da732x_hw_params, + .set_fmt = da732x_set_dai_fmt, + .set_sysclk = da732x_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver da732x_dai[] = { + { + .name = "DA732X_AIFA", + .id = DA732X_DAI_ID1, + .base = DA732X_REG_AIFA1, + .playback = { + .stream_name = "AIFA Playback", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .capture = { + .stream_name = "AIFA Capture", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .ops = &da732x_dai1_ops, + }, + { + .name = "DA732X_AIFB", + .id = DA732X_DAI_ID2, + .base = DA732X_REG_AIFB1, + .playback = { + .stream_name = "AIFB Playback", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .capture = { + .stream_name = "AIFB Capture", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .ops = &da732x_dai2_ops, + }, +}; + +static const struct regmap_config da732x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA732X_MAX_REG, + .reg_defaults = da732x_reg_cache, + .num_reg_defaults = ARRAY_SIZE(da732x_reg_cache), + .cache_type = REGCACHE_RBTREE, +}; + + +static void da732x_dac_offset_adjust(struct snd_soc_codec *codec) +{ + u8 offset[DA732X_HP_DACS]; + u8 sign[DA732X_HP_DACS]; + u8 step = DA732X_DAC_OFFSET_STEP; + + /* Initialize DAC offset calibration circuits and registers */ + snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET, + DA732X_HP_DAC_OFFSET_TRIM_VAL); + snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET, + DA732X_HP_DAC_OFFSET_TRIM_VAL); + snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_CALIBRATION | + DA732X_HP_DAC_OFF_SCALE_STEPS); + snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_CALIBRATION | + DA732X_HP_DAC_OFF_SCALE_STEPS); + + /* Wait for voltage stabilization */ + msleep(DA732X_WAIT_FOR_STABILIZATION); + + /* Check DAC offset sign */ + sign[DA732X_HPL_DAC] = (codec->hw_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO); + sign[DA732X_HPR_DAC] = (codec->hw_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO); + + /* Binary search DAC offset values (both channels at once) */ + offset[DA732X_HPL_DAC] = sign[DA732X_HPL_DAC] << DA732X_HP_DAC_COMPO_SHIFT; + offset[DA732X_HPR_DAC] = sign[DA732X_HPR_DAC] << DA732X_HP_DAC_COMPO_SHIFT; + + do { + offset[DA732X_HPL_DAC] |= step; + offset[DA732X_HPR_DAC] |= step; + snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET, + ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK); + snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET, + ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK); + + msleep(DA732X_WAIT_FOR_STABILIZATION); + + if ((codec->hw_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPL_DAC]) + offset[DA732X_HPL_DAC] &= ~step; + if ((codec->hw_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPR_DAC]) + offset[DA732X_HPR_DAC] &= ~step; + + step >>= 1; + } while (step); + + /* Write final DAC offsets to registers */ + snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET, + ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK); + snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET, + ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK); + + /* End DAC calibration mode */ + snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_SCALE_STEPS); + snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_SCALE_STEPS); +} + +static void da732x_output_offset_adjust(struct snd_soc_codec *codec) +{ + u8 offset[DA732X_HP_AMPS]; + u8 sign[DA732X_HP_AMPS]; + u8 step = DA732X_OUTPUT_OFFSET_STEP; + + offset[DA732X_HPL_AMP] = DA732X_HP_OUT_TRIM_VAL; + offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL; + + /* Initialize output offset calibration circuits and registers */ + snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL); + snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL); + snd_soc_write(codec, DA732X_REG_HPL, + DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN); + snd_soc_write(codec, DA732X_REG_HPR, + DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN); + + /* Wait for voltage stabilization */ + msleep(DA732X_WAIT_FOR_STABILIZATION); + + /* Check output offset sign */ + sign[DA732X_HPL_AMP] = codec->hw_read(codec, DA732X_REG_HPL) & + DA732X_HP_OUT_COMPO; + sign[DA732X_HPR_AMP] = codec->hw_read(codec, DA732X_REG_HPR) & + DA732X_HP_OUT_COMPO; + + snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_COMP | + (sign[DA732X_HPL_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) | + DA732X_HP_OUT_EN); + snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_COMP | + (sign[DA732X_HPR_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) | + DA732X_HP_OUT_EN); + + /* Binary search output offset values (both channels at once) */ + do { + offset[DA732X_HPL_AMP] |= step; + offset[DA732X_HPR_AMP] |= step; + snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, + offset[DA732X_HPL_AMP]); + snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, + offset[DA732X_HPR_AMP]); + + msleep(DA732X_WAIT_FOR_STABILIZATION); + + if ((codec->hw_read(codec, DA732X_REG_HPL) & + DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPL_AMP]) + offset[DA732X_HPL_AMP] &= ~step; + if ((codec->hw_read(codec, DA732X_REG_HPR) & + DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP]) + offset[DA732X_HPR_AMP] &= ~step; + + step >>= 1; + } while (step); + + /* Write final DAC offsets to registers */ + snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]); + snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]); +} + +static void da732x_hp_dc_offset_cancellation(struct snd_soc_codec *codec) +{ + /* Make sure that we have Soft Mute enabled */ + snd_soc_write(codec, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN | + DA732X_GAIN_RAMPED | DA732X_16_SAMPLES); + snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACL_EN | + DA732X_DACR_EN | DA732X_DACL_SDM | DA732X_DACR_SDM | + DA732X_DACL_MUTE | DA732X_DACR_MUTE); + snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN | + DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN); + snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_EN | + DA732X_HP_OUT_MUTE | DA732X_HP_OUT_DAC_EN); + + da732x_dac_offset_adjust(codec); + da732x_output_offset_adjust(codec); + + snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS); + snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_DIS); + snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_DIS); +} + +static int da732x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, + DA732X_BIAS_BOOST_MASK, + DA732X_BIAS_BOOST_100PC); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + /* Init Codec */ + snd_soc_write(codec, DA732X_REG_REF1, + DA732X_VMID_FASTCHG); + snd_soc_write(codec, DA732X_REG_BIAS_EN, + DA732X_BIAS_EN); + + mdelay(DA732X_STARTUP_DELAY); + + /* Disable Fast Charge and enable DAC ref voltage */ + snd_soc_write(codec, DA732X_REG_REF1, + DA732X_REFBUFX2_EN); + + /* Enable bypass DSP routing */ + snd_soc_write(codec, DA732X_REG_DATA_ROUTE, + DA732X_BYPASS_DSP); + + /* Enable Digital subsystem */ + snd_soc_write(codec, DA732X_REG_DSP_CTRL, + DA732X_DIGITAL_EN); + + snd_soc_write(codec, DA732X_REG_SPARE1_OUT, + DA732X_HP_DRIVER_EN | + DA732X_HP_GATE_LOW | + DA732X_HP_LOOP_GAIN_CTRL); + snd_soc_write(codec, DA732X_REG_HP_LIN1_GNDSEL, + DA732X_HP_OUT_GNDSEL); + + da732x_set_charge_pump(codec, DA732X_ENABLE_CP); + + snd_soc_write(codec, DA732X_REG_CLK_EN1, + DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN); + + /* Enable Zero Crossing */ + snd_soc_write(codec, DA732X_REG_INP_ZC_EN, + DA732X_MIC1_PRE_ZC_EN | + DA732X_MIC1_ZC_EN | + DA732X_MIC2_PRE_ZC_EN | + DA732X_MIC2_ZC_EN | + DA732X_AUXL_ZC_EN | + DA732X_AUXR_ZC_EN | + DA732X_MIC3_PRE_ZC_EN | + DA732X_MIC3_ZC_EN); + snd_soc_write(codec, DA732X_REG_OUT_ZC_EN, + DA732X_HPL_ZC_EN | DA732X_HPR_ZC_EN | + DA732X_LIN2_ZC_EN | DA732X_LIN3_ZC_EN | + DA732X_LIN4_ZC_EN); + + da732x_hp_dc_offset_cancellation(codec); + + regcache_cache_only(codec->control_data, false); + regcache_sync(codec->control_data); + } else { + snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, + DA732X_BIAS_BOOST_MASK, + DA732X_BIAS_BOOST_50PC); + snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL, + DA732X_PLL_EN, 0); + da732x->pll_en = false; + } + break; + case SND_SOC_BIAS_OFF: + regcache_cache_only(codec->control_data, true); + da732x_set_charge_pump(codec, DA732X_DISABLE_CP); + snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN, + DA732X_BIAS_DIS); + da732x->pll_en = false; + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static int da732x_probe(struct snd_soc_codec *codec) +{ + struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret = 0; + + da732x->codec = codec; + + dapm->idle_bias_off = false; + + codec->control_data = da732x->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec.\n"); + goto err; + } + + da732x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +err: + return ret; +} + +static int da732x_remove(struct snd_soc_codec *codec) +{ + + da732x_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_da732x = { + .probe = da732x_probe, + .remove = da732x_remove, + .set_bias_level = da732x_set_bias_level, + .controls = da732x_snd_controls, + .num_controls = ARRAY_SIZE(da732x_snd_controls), + .dapm_widgets = da732x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets), + .dapm_routes = da732x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes), + .set_pll = da732x_set_dai_pll, + .reg_cache_size = ARRAY_SIZE(da732x_reg_cache), +}; + +static __devinit int da732x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da732x_priv *da732x; + unsigned int reg; + int ret; + + da732x = devm_kzalloc(&i2c->dev, sizeof(struct da732x_priv), + GFP_KERNEL); + if (!da732x) + return -ENOMEM; + + i2c_set_clientdata(i2c, da732x); + + da732x->regmap = devm_regmap_init_i2c(i2c, &da732x_regmap); + if (IS_ERR(da732x->regmap)) { + ret = PTR_ERR(da732x->regmap); + dev_err(&i2c->dev, "Failed to initialize regmap\n"); + goto err; + } + + ret = regmap_read(da732x->regmap, DA732X_REG_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err; + } + + dev_info(&i2c->dev, "Revision: %d.%d\n", + (reg & DA732X_ID_MAJOR_MASK), (reg & DA732X_ID_MINOR_MASK)); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da732x, + da732x_dai, ARRAY_SIZE(da732x_dai)); + if (ret != 0) + dev_err(&i2c->dev, "Failed to register codec.\n"); + +err: + return ret; +} + +static __devexit int da732x_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + + return 0; +} + +static const struct i2c_device_id da732x_i2c_id[] = { + { "da7320", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, da732x_i2c_id); + +static struct i2c_driver da732x_i2c_driver = { + .driver = { + .name = "da7320", + .owner = THIS_MODULE, + }, + .probe = da732x_i2c_probe, + .remove = __devexit_p(da732x_i2c_remove), + .id_table = da732x_i2c_id, +}; + +module_i2c_driver(da732x_i2c_driver); + + +MODULE_DESCRIPTION("ASoC DA732X driver"); +MODULE_AUTHOR("Michal Hajduk <michal.hajduk@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h new file mode 100644 index 000000000000..c8ce5475de22 --- /dev/null +++ b/sound/soc/codecs/da732x.h @@ -0,0 +1,133 @@ +/* + * da732x.h -- Dialog DA732X ALSA SoC Audio Driver Header File + * + * Copyright (C) 2012 Dialog Semiconductor GmbH + * + * Author: Michal Hajduk <Michal.Hajduk@diasemi.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 __DA732X_H_ +#define __DA732X_H + +#include <sound/soc.h> + +/* General */ +#define DA732X_U8_MASK 0xFF +#define DA732X_4BYTES 4 +#define DA732X_3BYTES 3 +#define DA732X_2BYTES 2 +#define DA732X_1BYTE 1 +#define DA732X_1BYTE_SHIFT 8 +#define DA732X_2BYTES_SHIFT 16 +#define DA732X_3BYTES_SHIFT 24 +#define DA732X_4BYTES_SHIFT 32 + +#define DA732X_DACS_DIS 0x0 +#define DA732X_HP_DIS 0x0 +#define DA732X_CLEAR_REG 0x0 + +/* Calibration */ +#define DA732X_DAC_OFFSET_STEP 0x20 +#define DA732X_OUTPUT_OFFSET_STEP 0x80 +#define DA732X_HP_OUT_TRIM_VAL 0x0 +#define DA732X_WAIT_FOR_STABILIZATION 1 +#define DA732X_HPL_DAC 0 +#define DA732X_HPR_DAC 1 +#define DA732X_HP_DACS 2 +#define DA732X_HPL_AMP 0 +#define DA732X_HPR_AMP 1 +#define DA732X_HP_AMPS 2 + +/* Clock settings */ +#define DA732X_STARTUP_DELAY 100 +#define DA732X_PLL_OUT_196608 196608000 +#define DA732X_PLL_OUT_180634 180633600 +#define DA732X_PLL_OUT_SRM 188620800 +#define DA732X_MCLK_10MHZ 10000000 +#define DA732X_MCLK_20MHZ 20000000 +#define DA732X_MCLK_40MHZ 40000000 +#define DA732X_MCLK_54MHZ 54000000 +#define DA732X_MCLK_RET_0_10MHZ 0 +#define DA732X_MCLK_VAL_0_10MHZ 1 +#define DA732X_MCLK_RET_10_20MHZ 1 +#define DA732X_MCLK_VAL_10_20MHZ 2 +#define DA732X_MCLK_RET_20_40MHZ 2 +#define DA732X_MCLK_VAL_20_40MHZ 4 +#define DA732X_MCLK_RET_40_54MHZ 3 +#define DA732X_MCLK_VAL_40_54MHZ 8 +#define DA732X_DAI_ID1 0 +#define DA732X_DAI_ID2 1 +#define DA732X_SRCCLK_PLL 0 +#define DA732X_SRCCLK_MCLK 1 + +#define DA732X_LIN_LP_VOL 0x4F +#define DA732X_LP_VOL 0x40 + +/* Kcontrols */ +#define DA732X_DAC_EN_MAX 2 +#define DA732X_ADCL_MUX_MAX 2 +#define DA732X_ADCR_MUX_MAX 3 +#define DA732X_HPF_MODE_MAX 3 +#define DA732X_HPF_MODE_SHIFT 4 +#define DA732X_HPF_MUSIC_SHIFT 0 +#define DA732X_HPF_MUSIC_MAX 4 +#define DA732X_HPF_VOICE_SHIFT 4 +#define DA732X_HPF_VOICE_MAX 8 +#define DA732X_EQ_EN_MAX 1 +#define DA732X_HPF_VOICE 1 +#define DA732X_HPF_MUSIC 2 +#define DA732X_HPF_DISABLED 0 +#define DA732X_NO_INVERT 0 +#define DA732X_INVERT 1 +#define DA732X_SWITCH_MAX 1 +#define DA732X_ENABLE_CP 1 +#define DA732X_DISABLE_CP 0 +#define DA732X_DISABLE_ALL_CLKS 0 +#define DA732X_RESET_ADCS 0 + +/* dB values */ +#define DA732X_MIC_VOL_DB_MIN 0 +#define DA732X_MIC_VOL_DB_INC 50 +#define DA732X_MIC_PRE_VOL_DB_MIN 0 +#define DA732X_MIC_PRE_VOL_DB_INC 600 +#define DA732X_AUX_VOL_DB_MIN -6000 +#define DA732X_AUX_VOL_DB_INC 150 +#define DA732X_HP_VOL_DB_MIN -2250 +#define DA732X_HP_VOL_DB_INC 150 +#define DA732X_LIN2_VOL_DB_MIN -1650 +#define DA732X_LIN2_VOL_DB_INC 150 +#define DA732X_LIN3_VOL_DB_MIN -1650 +#define DA732X_LIN3_VOL_DB_INC 150 +#define DA732X_LIN4_VOL_DB_MIN -2250 +#define DA732X_LIN4_VOL_DB_INC 150 +#define DA732X_EQ_BAND_VOL_DB_MIN -1050 +#define DA732X_EQ_BAND_VOL_DB_INC 150 +#define DA732X_DAC_VOL_DB_MIN -7725 +#define DA732X_DAC_VOL_DB_INC 75 +#define DA732X_ADC_VOL_DB_MIN 0 +#define DA732X_ADC_VOL_DB_INC -1 +#define DA732X_EQ_OVERALL_VOL_DB_MIN -1800 +#define DA732X_EQ_OVERALL_VOL_DB_INC 600 + +#define DA732X_SOC_ENUM_DOUBLE_R(xreg, xrreg, xmax, xtext) \ + {.reg = xreg, .reg2 = xrreg, .max = xmax, .texts = xtext} + +enum da732x_sysctl { + DA732X_SR_8KHZ = 0x1, + DA732X_SR_11_025KHZ = 0x2, + DA732X_SR_12KHZ = 0x3, + DA732X_SR_16KHZ = 0x5, + DA732X_SR_22_05KHZ = 0x6, + DA732X_SR_24KHZ = 0x7, + DA732X_SR_32KHZ = 0x9, + DA732X_SR_44_1KHZ = 0xA, + DA732X_SR_48KHZ = 0xB, + DA732X_SR_88_1KHZ = 0xE, + DA732X_SR_96KHZ = 0xF, +}; + +#endif /* __DA732X_H_ */ diff --git a/sound/soc/codecs/da732x_reg.h b/sound/soc/codecs/da732x_reg.h new file mode 100644 index 000000000000..bdd03ca4b2de --- /dev/null +++ b/sound/soc/codecs/da732x_reg.h @@ -0,0 +1,654 @@ +/* + * da732x_reg.h --- Dialog DA732X ALSA SoC Audio Registers Header File + * + * Copyright (C) 2012 Dialog Semiconductor GmbH + * + * Author: Michal Hajduk <Michal.Hajduk@diasemi.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 __DA732X_REG_H_ +#define __DA732X_REG_H_ + +/* DA732X registers */ +#define DA732X_REG_STATUS_EXT 0x00 +#define DA732X_REG_STATUS 0x01 +#define DA732X_REG_REF1 0x02 +#define DA732X_REG_BIAS_EN 0x03 +#define DA732X_REG_BIAS1 0x04 +#define DA732X_REG_BIAS2 0x05 +#define DA732X_REG_BIAS3 0x06 +#define DA732X_REG_BIAS4 0x07 +#define DA732X_REG_MICBIAS2 0x0F +#define DA732X_REG_MICBIAS1 0x10 +#define DA732X_REG_MICDET 0x11 +#define DA732X_REG_MIC1_PRE 0x12 +#define DA732X_REG_MIC1 0x13 +#define DA732X_REG_MIC2_PRE 0x14 +#define DA732X_REG_MIC2 0x15 +#define DA732X_REG_AUX1L 0x16 +#define DA732X_REG_AUX1R 0x17 +#define DA732X_REG_MIC3_PRE 0x18 +#define DA732X_REG_MIC3 0x19 +#define DA732X_REG_INP_PINBIAS 0x1A +#define DA732X_REG_INP_ZC_EN 0x1B +#define DA732X_REG_INP_MUX 0x1D +#define DA732X_REG_HP_DET 0x20 +#define DA732X_REG_HPL_DAC_OFFSET 0x21 +#define DA732X_REG_HPL_DAC_OFF_CNTL 0x22 +#define DA732X_REG_HPL_OUT_OFFSET 0x23 +#define DA732X_REG_HPL 0x24 +#define DA732X_REG_HPL_VOL 0x25 +#define DA732X_REG_HPR_DAC_OFFSET 0x26 +#define DA732X_REG_HPR_DAC_OFF_CNTL 0x27 +#define DA732X_REG_HPR_OUT_OFFSET 0x28 +#define DA732X_REG_HPR 0x29 +#define DA732X_REG_HPR_VOL 0x2A +#define DA732X_REG_LIN2 0x2B +#define DA732X_REG_LIN3 0x2C +#define DA732X_REG_LIN4 0x2D +#define DA732X_REG_OUT_ZC_EN 0x2E +#define DA732X_REG_HP_LIN1_GNDSEL 0x37 +#define DA732X_REG_CP_HP1 0x3A +#define DA732X_REG_CP_HP2 0x3B +#define DA732X_REG_CP_CTRL1 0x40 +#define DA732X_REG_CP_CTRL2 0x41 +#define DA732X_REG_CP_CTRL3 0x42 +#define DA732X_REG_CP_LEVEL_MASK 0x43 +#define DA732X_REG_CP_DET 0x44 +#define DA732X_REG_CP_STATUS 0x45 +#define DA732X_REG_CP_THRESH1 0x46 +#define DA732X_REG_CP_THRESH2 0x47 +#define DA732X_REG_CP_THRESH3 0x48 +#define DA732X_REG_CP_THRESH4 0x49 +#define DA732X_REG_CP_THRESH5 0x4A +#define DA732X_REG_CP_THRESH6 0x4B +#define DA732X_REG_CP_THRESH7 0x4C +#define DA732X_REG_CP_THRESH8 0x4D +#define DA732X_REG_PLL_DIV_LO 0x50 +#define DA732X_REG_PLL_DIV_MID 0x51 +#define DA732X_REG_PLL_DIV_HI 0x52 +#define DA732X_REG_PLL_CTRL 0x53 +#define DA732X_REG_CLK_CTRL 0x54 +#define DA732X_REG_CLK_DSP 0x5A +#define DA732X_REG_CLK_EN1 0x5B +#define DA732X_REG_CLK_EN2 0x5C +#define DA732X_REG_CLK_EN3 0x5D +#define DA732X_REG_CLK_EN4 0x5E +#define DA732X_REG_CLK_EN5 0x5F +#define DA732X_REG_AIF_MCLK 0x60 +#define DA732X_REG_AIFA1 0x61 +#define DA732X_REG_AIFA2 0x62 +#define DA732X_REG_AIFA3 0x63 +#define DA732X_REG_AIFB1 0x64 +#define DA732X_REG_AIFB2 0x65 +#define DA732X_REG_AIFB3 0x66 +#define DA732X_REG_PC_CTRL 0x6A +#define DA732X_REG_DATA_ROUTE 0x70 +#define DA732X_REG_DSP_CTRL 0x71 +#define DA732X_REG_CIF_CTRL2 0x74 +#define DA732X_REG_HANDSHAKE 0x75 +#define DA732X_REG_MBOX0 0x76 +#define DA732X_REG_MBOX1 0x77 +#define DA732X_REG_MBOX2 0x78 +#define DA732X_REG_MBOX_STATUS 0x79 +#define DA732X_REG_SPARE1_OUT 0x7D +#define DA732X_REG_SPARE2_OUT 0x7E +#define DA732X_REG_SPARE1_IN 0x7F +#define DA732X_REG_ID 0x81 +#define DA732X_REG_ADC1_PD 0x90 +#define DA732X_REG_ADC1_HPF 0x93 +#define DA732X_REG_ADC1_SEL 0x94 +#define DA732X_REG_ADC1_EQ12 0x95 +#define DA732X_REG_ADC1_EQ34 0x96 +#define DA732X_REG_ADC1_EQ5 0x97 +#define DA732X_REG_ADC2_PD 0x98 +#define DA732X_REG_ADC2_HPF 0x9B +#define DA732X_REG_ADC2_SEL 0x9C +#define DA732X_REG_ADC2_EQ12 0x9D +#define DA732X_REG_ADC2_EQ34 0x9E +#define DA732X_REG_ADC2_EQ5 0x9F +#define DA732X_REG_DAC1_HPF 0xA0 +#define DA732X_REG_DAC1_L_VOL 0xA1 +#define DA732X_REG_DAC1_R_VOL 0xA2 +#define DA732X_REG_DAC1_SEL 0xA3 +#define DA732X_REG_DAC1_SOFTMUTE 0xA4 +#define DA732X_REG_DAC1_EQ12 0xA5 +#define DA732X_REG_DAC1_EQ34 0xA6 +#define DA732X_REG_DAC1_EQ5 0xA7 +#define DA732X_REG_DAC2_HPF 0xB0 +#define DA732X_REG_DAC2_L_VOL 0xB1 +#define DA732X_REG_DAC2_R_VOL 0xB2 +#define DA732X_REG_DAC2_SEL 0xB3 +#define DA732X_REG_DAC2_SOFTMUTE 0xB4 +#define DA732X_REG_DAC2_EQ12 0xB5 +#define DA732X_REG_DAC2_EQ34 0xB6 +#define DA732X_REG_DAC2_EQ5 0xB7 +#define DA732X_REG_DAC3_HPF 0xC0 +#define DA732X_REG_DAC3_VOL 0xC1 +#define DA732X_REG_DAC3_SEL 0xC3 +#define DA732X_REG_DAC3_SOFTMUTE 0xC4 +#define DA732X_REG_DAC3_EQ12 0xC5 +#define DA732X_REG_DAC3_EQ34 0xC6 +#define DA732X_REG_DAC3_EQ5 0xC7 +#define DA732X_REG_BIQ_BYP 0xD2 +#define DA732X_REG_DMA_CMD 0xD3 +#define DA732X_REG_DMA_ADDR0 0xD4 +#define DA732X_REG_DMA_ADDR1 0xD5 +#define DA732X_REG_DMA_DATA0 0xD6 +#define DA732X_REG_DMA_DATA1 0xD7 +#define DA732X_REG_DMA_DATA2 0xD8 +#define DA732X_REG_DMA_DATA3 0xD9 +#define DA732X_REG_DMA_STATUS 0xDA +#define DA732X_REG_BROWNOUT 0xDF +#define DA732X_REG_UNLOCK 0xE0 + +#define DA732X_MAX_REG DA732X_REG_UNLOCK +/* + * Bits + */ + +/* DA732X_REG_STATUS_EXT (addr=0x00) */ +#define DA732X_STATUS_EXT_DSP (1 << 4) +#define DA732X_STATUS_EXT_CLEAR (0 << 0) + +/* DA732X_REG_STATUS (addr=0x01) */ +#define DA732X_STATUS_PLL_LOCK (1 << 0) +#define DA732X_STATUS_PLL_MCLK_DET (1 << 1) +#define DA732X_STATUS_HPDET_OUT (1 << 2) +#define DA732X_STATUS_INP_MIXDET_1 (1 << 3) +#define DA732X_STATUS_INP_MIXDET_2 (1 << 4) +#define DA732X_STATUS_BO_STATUS (1 << 5) + +/* DA732X_REG_REF1 (addr=0x02) */ +#define DA732X_VMID_FASTCHG (1 << 1) +#define DA732X_VMID_FASTDISCHG (1 << 2) +#define DA732X_REFBUFX2_EN (1 << 6) +#define DA732X_REFBUFX2_DIS (0 << 6) + +/* DA732X_REG_BIAS_EN (addr=0x03) */ +#define DA732X_BIAS_BOOST_MASK (3 << 0) +#define DA732X_BIAS_BOOST_100PC (0 << 0) +#define DA732X_BIAS_BOOST_133PC (1 << 0) +#define DA732X_BIAS_BOOST_88PC (2 << 0) +#define DA732X_BIAS_BOOST_50PC (3 << 0) +#define DA732X_BIAS_EN (1 << 7) +#define DA732X_BIAS_DIS (0 << 7) + +/* DA732X_REG_BIAS1 (addr=0x04) */ +#define DA732X_BIAS1_HP_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS1_HP_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_BIAS2 (addr=0x05) */ +#define DA732X_BIAS2_LINE2_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS2_LINE2_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_BIAS3 (addr=0x06) */ +#define DA732X_BIAS3_LINE3_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS3_LINE3_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_BIAS4 (addr=0x07) */ +#define DA732X_BIAS4_LINE4_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS4_LINE4_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_SIF_VDD_SEL (addr=0x08) */ +#define DA732X_SIF_VDD_SEL_AIFA_VDD2 (1 << 0) +#define DA732X_SIF_VDD_SEL_AIFB_VDD2 (1 << 1) +#define DA732X_SIF_VDD_SEL_CIFA_VDD2 (1 << 4) + +/* DA732X_REG_MICBIAS2/1 (addr=0x0F/0x10) */ +#define DA732X_MICBIAS_VOLTAGE_MASK (0x0F << 0) +#define DA732X_MICBIAS_VOLTAGE_2V (0x00 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V05 (0x01 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V1 (0x02 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V15 (0x03 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V2 (0x04 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V25 (0x05 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V3 (0x06 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V35 (0x07 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V4 (0x08 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V45 (0x09 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V5 (0x0A << 0) +#define DA732X_MICBIAS_EN (1 << 7) +#define DA732X_MICBIAS_EN_SHIFT 7 +#define DA732X_MICBIAS_VOLTAGE_SHIFT 0 +#define DA732X_MICBIAS_VOLTAGE_MAX 0x0B + +/* DA732X_REG_MICDET (addr=0x11) */ +#define DA732X_MICDET_INP_MICRES (1 << 0) +#define DA732X_MICDET_INP_MICHOOK (1 << 1) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_8MS (0 << 0) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_16MS (1 << 0) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_32MS (2 << 0) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_64MS (3 << 0) +#define DA732X_MICDET_INP_MICDET_EN (1 << 7) + +/* DA732X_REG_MIC1/2/3_PRE (addr=0x11/0x14/0x18) */ +#define DA732X_MICBOOST_MASK 0x7 +#define DA732X_MICBOOST_SHIFT 0 +#define DA732X_MICBOOST_MIN 0x1 +#define DA732X_MICBOOST_MAX DA732X_MICBOOST_MASK + +/* DA732X_REG_MIC1/2/3 (addr=0x13/0x15/0x19) */ +#define DA732X_MIC_VOL_SHIFT 0 +#define DA732X_MIC_VOL_VAL_MASK 0x1F +#define DA732X_MIC_MUTE_SHIFT 6 +#define DA732X_MIC_EN_SHIFT 7 +#define DA732X_MIC_VOL_VAL_MIN 0x7 +#define DA732X_MIC_VOL_VAL_MAX DA732X_MIC_VOL_VAL_MASK + +/* DA732X_REG_AUX1L/R (addr=0x16/0x17) */ +#define DA732X_AUX_VOL_SHIFT 0 +#define DA732X_AUX_VOL_MASK 0x7 +#define DA732X_AUX_MUTE_SHIFT 6 +#define DA732X_AUX_EN_SHIFT 7 +#define DA732X_AUX_VOL_VAL_MAX DA732X_AUX_VOL_MASK + +/* DA732X_REG_INP_PINBIAS (addr=0x1A) */ +#define DA732X_INP_MICL_PINBIAS_EN (1 << 0) +#define DA732X_INP_MICR_PINBIAS_EN (1 << 1) +#define DA732X_INP_AUX1L_PINBIAS_EN (1 << 2) +#define DA732X_INP_AUX1R_PINBIAS_EN (1 << 3) +#define DA732X_INP_AUX2_PINBIAS_EN (1 << 4) + +/* DA732X_REG_INP_ZC_EN (addr=0x1B) */ +#define DA732X_MIC1_PRE_ZC_EN (1 << 0) +#define DA732X_MIC1_ZC_EN (1 << 1) +#define DA732X_MIC2_PRE_ZC_EN (1 << 2) +#define DA732X_MIC2_ZC_EN (1 << 3) +#define DA732X_AUXL_ZC_EN (1 << 4) +#define DA732X_AUXR_ZC_EN (1 << 5) +#define DA732X_MIC3_PRE_ZC_EN (1 << 6) +#define DA732X_MIC3_ZC_EN (1 << 7) + +/* DA732X_REG_INP_MUX (addr=0x1D) */ +#define DA732X_INP_ADC1L_MUX_SEL_AUX1L (0 << 0) +#define DA732X_INP_ADC1L_MUX_SEL_MIC1 (1 << 0) +#define DA732X_INP_ADC1R_MUX_SEL_MASK (3 << 2) +#define DA732X_INP_ADC1R_MUX_SEL_AUX1R (0 << 2) +#define DA732X_INP_ADC1R_MUX_SEL_MIC2 (1 << 2) +#define DA732X_INP_ADC1R_MUX_SEL_MIC3 (2 << 2) +#define DA732X_INP_ADC2L_MUX_SEL_AUX1L (0 << 4) +#define DA732X_INP_ADC2L_MUX_SEL_MICL (1 << 4) +#define DA732X_INP_ADC2R_MUX_SEL_MASK (3 << 6) +#define DA732X_INP_ADC2R_MUX_SEL_AUX1R (0 << 6) +#define DA732X_INP_ADC2R_MUX_SEL_MICR (1 << 6) +#define DA732X_INP_ADC2R_MUX_SEL_AUX2 (2 << 6) +#define DA732X_ADC1L_MUX_SEL_SHIFT 0 +#define DA732X_ADC1R_MUX_SEL_SHIFT 2 +#define DA732X_ADC2L_MUX_SEL_SHIFT 4 +#define DA732X_ADC2R_MUX_SEL_SHIFT 6 + +/* DA732X_REG_HP_DET (addr=0x20) */ +#define DA732X_HP_DET_AZ (1 << 0) +#define DA732X_HP_DET_SEL1 (1 << 1) +#define DA732X_HP_DET_IS_MASK (3 << 2) +#define DA732X_HP_DET_IS_0_5UA (0 << 2) +#define DA732X_HP_DET_IS_1UA (1 << 2) +#define DA732X_HP_DET_IS_2UA (2 << 2) +#define DA732X_HP_DET_IS_4UA (3 << 2) +#define DA732X_HP_DET_RS_MASK (3 << 4) +#define DA732X_HP_DET_RS_INFINITE (0 << 4) +#define DA732X_HP_DET_RS_100KOHM (1 << 4) +#define DA732X_HP_DET_RS_10KOHM (2 << 4) +#define DA732X_HP_DET_RS_1KOHM (3 << 4) +#define DA732X_HP_DET_EN (1 << 7) + +/* DA732X_REG_HPL_DAC_OFFSET (addr=0x21/0x26) */ +#define DA732X_HP_DAC_OFFSET_TRIM_MASK (0x3F << 0) +#define DA732X_HP_DAC_OFFSET_DAC_SIGN (1 << 6) + +/* DA732X_REG_HPL_DAC_OFF_CNTL (addr=0x22/0x27) */ +#define DA732X_HP_DAC_OFF_CNTL_CONT_MASK (7 << 0) +#define DA732X_HP_DAC_OFF_CNTL_COMPO (1 << 3) +#define DA732X_HP_DAC_OFF_CALIBRATION (1 << 0) +#define DA732X_HP_DAC_OFF_SCALE_STEPS (1 << 1) +#define DA732X_HP_DAC_OFF_MASK 0x7F +#define DA732X_HP_DAC_COMPO_SHIFT 3 + +/* DA732X_REG_HPL_OUT_OFFSET (addr=0x23/0x28) */ +#define DA732X_HP_OUT_OFFSET_MASK (0xFF << 0) +#define DA732X_HP_DAC_OFFSET_TRIM_VAL 0x7F + +/* DA732X_REG_HPL/R (addr=0x24/0x29) */ +#define DA732X_HP_OUT_SIGN (1 << 0) +#define DA732X_HP_OUT_COMP (1 << 1) +#define DA732X_HP_OUT_RESERVED (1 << 2) +#define DA732X_HP_OUT_COMPO (1 << 3) +#define DA732X_HP_OUT_DAC_EN (1 << 4) +#define DA732X_HP_OUT_HIZ_EN (1 << 5) +#define DA732X_HP_OUT_HIZ_DIS (0 << 5) +#define DA732X_HP_OUT_MUTE (1 << 6) +#define DA732X_HP_OUT_EN (1 << 7) +#define DA732X_HP_OUT_COMPO_SHIFT 3 +#define DA732X_HP_OUT_DAC_EN_SHIFT 4 +#define DA732X_HP_HIZ_SHIFT 5 +#define DA732X_HP_MUTE_SHIFT 6 +#define DA732X_HP_OUT_EN_SHIFT 7 + +#define DA732X_OUT_HIZ_EN (1 << 5) +#define DA732X_OUT_HIZ_DIS (0 << 5) + +/* DA732X_REG_HPL/R_VOL (addr=0x25/0x2A) */ +#define DA732X_HP_VOL_VAL_MASK 0xF +#define DA732X_HP_VOL_SHIFT 0 +#define DA732X_HP_VOL_VAL_MAX DA732X_HP_VOL_VAL_MASK + +/* DA732X_REG_LIN2/3/4 (addr=0x2B/0x2C/0x2D) */ +#define DA732X_LOUT_VOL_SHIFT 0 +#define DA732X_LOUT_VOL_MASK 0x0F +#define DA732X_LOUT_DAC_OFF (0 << 4) +#define DA732X_LOUT_DAC_EN (1 << 4) +#define DA732X_LOUT_HIZ_N_DIS (0 << 5) +#define DA732X_LOUT_HIZ_N_EN (1 << 5) +#define DA732X_LOUT_UNMUTED (0 << 6) +#define DA732X_LOUT_MUTED (1 << 6) +#define DA732X_LOUT_EN (0 << 7) +#define DA732X_LOUT_DIS (1 << 7) +#define DA732X_LOUT_DAC_EN_SHIFT 4 +#define DA732X_LOUT_MUTE_SHIFT 6 +#define DA732X_LIN_OUT_EN_SHIFT 7 +#define DA732X_LOUT_VOL_VAL_MAX DA732X_LOUT_VOL_MASK + +/* DA732X_REG_OUT_ZC_EN (addr=0x2E) */ +#define DA732X_HPL_ZC_EN_SHIFT 0 +#define DA732X_HPR_ZC_EN_SHIFT 1 +#define DA732X_HPL_ZC_EN (1 << 0) +#define DA732X_HPL_ZC_DIS (0 << 0) +#define DA732X_HPR_ZC_EN (1 << 1) +#define DA732X_HPR_ZC_DIS (0 << 1) +#define DA732X_LIN2_ZC_EN (1 << 2) +#define DA732X_LIN2_ZC_DIS (0 << 2) +#define DA732X_LIN3_ZC_EN (1 << 3) +#define DA732X_LIN3_ZC_DIS (0 << 3) +#define DA732X_LIN4_ZC_EN (1 << 4) +#define DA732X_LIN4_ZC_DIS (0 << 4) + +/* DA732X_REG_HP_LIN1_GNDSEL (addr=0x37) */ +#define DA732X_HP_OUT_GNDSEL (1 << 0) + +/* DA732X_REG_CP_HP2 (addr=0x3a) */ +#define DA732X_HP_CP_PULSESKIP (1 << 0) +#define DA732X_HP_CP_REG (1 << 1) +#define DA732X_HP_CP_EN (1 << 3) +#define DA732X_HP_CP_DIS (0 << 3) + +/* DA732X_REG_CP_CTRL1 (addr=0x40) */ +#define DA732X_CP_MODE_MASK (7 << 1) +#define DA732X_CP_CTRL_STANDBY (0 << 1) +#define DA732X_CP_CTRL_CPVDD6 (2 << 1) +#define DA732X_CP_CTRL_CPVDD5 (3 << 1) +#define DA732X_CP_CTRL_CPVDD4 (4 << 1) +#define DA732X_CP_CTRL_CPVDD3 (5 << 1) +#define DA732X_CP_CTRL_CPVDD2 (6 << 1) +#define DA732X_CP_CTRL_CPVDD1 (7 << 1) +#define DA723X_CP_DIS (0 << 7) +#define DA732X_CP_EN (1 << 7) + +/* DA732X_REG_CP_CTRL2 (addr=0x41) */ +#define DA732X_CP_BOOST (1 << 0) +#define DA732X_CP_MANAGE_MAGNITUDE (2 << 2) + +/* DA732X_REG_CP_CTRL3 (addr=0x42) */ +#define DA732X_CP_1MHZ (0 << 0) +#define DA732X_CP_500KHZ (1 << 0) +#define DA732X_CP_250KHZ (2 << 0) +#define DA732X_CP_125KHZ (3 << 0) +#define DA732X_CP_63KHZ (4 << 0) +#define DA732X_CP_0KHZ (5 << 0) + +/* DA732X_REG_PLL_CTRL (addr=0x53) */ +#define DA732X_PLL_INDIV_MASK (3 << 0) +#define DA732X_PLL_SRM_EN (1 << 2) +#define DA732X_PLL_EN (1 << 7) +#define DA732X_PLL_BYPASS (0 << 0) + +/* DA732X_REG_CLK_CTRL (addr=0x54) */ +#define DA732X_SR1_MASK (0xF) +#define DA732X_SR2_MASK (0xF0) + +/* DA732X_REG_CLK_DSP (addr=0x5A) */ +#define DA732X_DSP_FREQ_MASK (7 << 0) +#define DA732X_DSP_FREQ_12MHZ (0 << 0) +#define DA732X_DSP_FREQ_24MHZ (1 << 0) +#define DA732X_DSP_FREQ_36MHZ (2 << 0) +#define DA732X_DSP_FREQ_48MHZ (3 << 0) +#define DA732X_DSP_FREQ_60MHZ (4 << 0) +#define DA732X_DSP_FREQ_72MHZ (5 << 0) +#define DA732X_DSP_FREQ_84MHZ (6 << 0) +#define DA732X_DSP_FREQ_96MHZ (7 << 0) + +/* DA732X_REG_CLK_EN1 (addr=0x5B) */ +#define DA732X_DSP_CLK_EN (1 << 0) +#define DA732X_SYS3_CLK_EN (1 << 1) +#define DA732X_DSP12_CLK_EN (1 << 2) +#define DA732X_PC_CLK_EN (1 << 3) +#define DA732X_MCLK_SQR_EN (1 << 7) + +/* DA732X_REG_CLK_EN2 (addr=0x5C) */ +#define DA732X_UART_CLK_EN (1 << 1) +#define DA732X_CP_CLK_EN (1 << 2) +#define DA732X_CP_CLK_DIS (0 << 2) + +/* DA732X_REG_CLK_EN3 (addr=0x5D) */ +#define DA732X_ADCA_BB_CLK_EN (1 << 0) +#define DA732X_ADCC_BB_CLK_EN (1 << 4) + +/* DA732X_REG_CLK_EN4 (addr=0x5E) */ +#define DA732X_DACA_BB_CLK_EN (1 << 0) +#define DA732X_DACC_BB_CLK_EN (1 << 4) +#define DA732X_DACA_BB_CLK_SHIFT 0 +#define DA732X_DACC_BB_CLK_SHIFT 4 + +/* DA732X_REG_CLK_EN5 (addr=0x5F) */ +#define DA732X_DACE_BB_CLK_EN (1 << 0) +#define DA732X_DACE_BB_CLK_SHIFT 0 + +/* DA732X_REG_AIF_MCLK (addr=0x60) */ +#define DA732X_AIFM_FRAME_64 (1 << 2) +#define DA732X_AIFM_SRC_SEL_AIFA (1 << 6) +#define DA732X_CLK_GENERATION_AIF_A (1 << 4) +#define DA732X_NO_CLK_GENERATION 0x0 + +/* DA732X_REG_AIFA1 (addr=0x61) */ +#define DA732X_AIF_WORD_MASK (0x3 << 0) +#define DA732X_AIF_WORD_16 (0 << 0) +#define DA732X_AIF_WORD_20 (1 << 0) +#define DA732X_AIF_WORD_24 (2 << 0) +#define DA732X_AIF_WORD_32 (3 << 0) +#define DA732X_AIF_TDM_MONO_SHIFT (1 << 6) +#define DA732X_AIF1_CLK_MASK (1 << 7) +#define DA732X_AIF_SLAVE (0 << 7) +#define DA732X_AIF_CLK_FROM_SRC (1 << 7) + +/* DA732X_REG_AIFA3 (addr=0x63) */ +#define DA732X_AIF_MODE_SHIFT 0 +#define DA732X_AIF_MODE_MASK 0x3 +#define DA732X_AIF_I2S_MODE (0 << 0) +#define DA732X_AIF_LEFT_J_MODE (1 << 0) +#define DA732X_AIF_RIGHT_J_MODE (2 << 0) +#define DA732X_AIF_DSP_MODE (3 << 0) +#define DA732X_AIF_WCLK_INV (1 << 4) +#define DA732X_AIF_BCLK_INV (1 << 5) +#define DA732X_AIF_EN (1 << 7) +#define DA732X_AIF_EN_SHIFT 7 + +/* DA732X_REG_PC_CTRL (addr=0x6a) */ +#define DA732X_PC_PULSE_AIFA (0 << 0) +#define DA732X_PC_PULSE_AIFB (1 << 0) +#define DA732X_PC_RESYNC_AUT (1 << 6) +#define DA732X_PC_RESYNC_NOT_AUT (0 << 6) +#define DA732X_PC_SAME (1 << 7) + +/* DA732X_REG_DATA_ROUTE (addr=0x70) */ +#define DA732X_ADC1_TO_AIFA (0 << 0) +#define DA732X_DSP_TO_AIFA (1 << 0) +#define DA732X_ADC2_TO_AIFB (0 << 1) +#define DA732X_DSP_TO_AIFB (1 << 1) +#define DA732X_AIFA_TO_DAC1L (0 << 2) +#define DA732X_DSP_TO_DAC1L (1 << 2) +#define DA732X_AIFA_TO_DAC1R (0 << 3) +#define DA732X_DSP_TO_DAC1R (1 << 3) +#define DA732X_AIFB_TO_DAC2L (0 << 4) +#define DA732X_DSP_TO_DAC2L (1 << 4) +#define DA732X_AIFB_TO_DAC2R (0 << 5) +#define DA732X_DSP_TO_DAC2R (1 << 5) +#define DA732X_AIFB_TO_DAC3 (0 << 6) +#define DA732X_DSP_TO_DAC3 (1 << 6) +#define DA732X_BYPASS_DSP (0 << 0) +#define DA732X_ALL_TO_DSP (0x7F << 0) + +/* DA732X_REG_DSP_CTRL (addr=0x71) */ +#define DA732X_DIGITAL_EN (1 << 0) +#define DA732X_DIGITAL_RESET (0 << 0) +#define DA732X_DSP_CORE_EN (1 << 1) +#define DA732X_DSP_CORE_RESET (0 << 1) + +/* DA732X_REG_SPARE1_OUT (addr=0x7D)*/ +#define DA732X_HP_DRIVER_EN (1 << 0) +#define DA732X_HP_GATE_LOW (1 << 2) +#define DA732X_HP_LOOP_GAIN_CTRL (1 << 3) + +/* DA732X_REG_ID (addr=0x81)*/ +#define DA732X_ID_MINOR_MASK (0xF << 0) +#define DA732X_ID_MAJOR_MASK (0xF << 4) + +/* DA732X_REG_ADC1/2_PD (addr=0x90/0x98) */ +#define DA732X_ADC_RST_MASK (0x3 << 0) +#define DA732X_ADC_PD_MASK (0x3 << 2) +#define DA732X_ADC_SET_ACT (0x3 << 0) +#define DA732X_ADC_SET_RST (0x0 << 0) +#define DA732X_ADC_ON (0x3 << 2) +#define DA732X_ADC_OFF (0x0 << 2) + +/* DA732X_REG_ADC1/2_SEL (addr=0x94/0x9C) */ +#define DA732X_ADC_VOL_VAL_MASK 0x7 +#define DA732X_ADCL_VOL_SHIFT 0 +#define DA732X_ADCR_VOL_SHIFT 4 +#define DA732X_ADCL_EN_SHIFT 2 +#define DA732X_ADCR_EN_SHIFT 3 +#define DA732X_ADCL_EN (1 << 2) +#define DA732X_ADCR_EN (1 << 3) +#define DA732X_ADC_VOL_VAL_MAX DA732X_ADC_VOL_VAL_MASK + +/* + * DA732X_REG_ADC1/2_HPF (addr=0x93/0x9b) + * DA732x_REG_DAC1/2/3_HPG (addr=0xA5/0xB5/0xC5) + */ +#define DA732X_HPF_MUSIC_EN (1 << 3) +#define DA732X_HPF_VOICE_EN ((1 << 3) | (1 << 7)) +#define DA732X_HPF_MASK ((1 << 3) | (1 << 7)) +#define DA732X_HPF_DIS ((0 << 3) | (0 << 7)) + +/* DA732X_REG_DAC1/2/3_VOL */ +#define DA732X_DAC_VOL_VAL_MASK 0x7F +#define DA732X_DAC_VOL_SHIFT 0 +#define DA732X_DAC_VOL_VAL_MAX DA732X_DAC_VOL_VAL_MASK + +/* DA732X_REG_DAC1/2/3_SEL (addr=0xA3/0xB3/0xC3) */ +#define DA732X_DACL_EN_SHIFT 3 +#define DA732X_DACR_EN_SHIFT 7 +#define DA732X_DACL_MUTE_SHIFT 2 +#define DA732X_DACR_MUTE_SHIFT 6 +#define DA732X_DACL_EN (1 << 3) +#define DA732X_DACR_EN (1 << 7) +#define DA732X_DACL_SDM (1 << 0) +#define DA732X_DACR_SDM (1 << 4) +#define DA732X_DACL_MUTE (1 << 2) +#define DA732X_DACR_MUTE (1 << 6) + +/* DA732X_REG_DAC_SOFTMUTE (addr=0xA4/0xB4/0xC4) */ +#define DA732X_SOFTMUTE_EN (1 << 7) +#define DA732X_GAIN_RAMPED (1 << 6) +#define DA732X_16_SAMPLES (4 << 0) +#define DA732X_SOFTMUTE_MASK (1 << 7) +#define DA732X_SOFTMUTE_SHIFT 7 + +/* + * DA732x_REG_ADC1/2_EQ12 (addr=0x95/0x9D) + * DA732x_REG_ADC1/2_EQ34 (addr=0x96/0x9E) + * DA732x_REG_ADC1/2_EQ5 (addr=0x97/0x9F) + * DA732x_REG_DAC1/2/3_EQ12 (addr=0xA5/0xB5/0xC5) + * DA732x_REG_DAC1/2/3_EQ34 (addr=0xA6/0xB6/0xC6) + * DA732x_REG_DAC1/2/3_EQ5 (addr=0xA7/0xB7/0xB7) + */ +#define DA732X_EQ_VOL_VAL_MASK 0xF +#define DA732X_EQ_BAND1_SHIFT 0 +#define DA732X_EQ_BAND2_SHIFT 4 +#define DA732X_EQ_BAND3_SHIFT 0 +#define DA732X_EQ_BAND4_SHIFT 4 +#define DA732X_EQ_BAND5_SHIFT 0 +#define DA732X_EQ_OVERALL_SHIFT 4 +#define DA732X_EQ_OVERALL_VOL_VAL_MASK 0x3 +#define DA732X_EQ_DIS (0 << 7) +#define DA732X_EQ_EN (1 << 7) +#define DA732X_EQ_EN_SHIFT 7 +#define DA732X_EQ_VOL_VAL_MAX DA732X_EQ_VOL_VAL_MASK +#define DA732X_EQ_OVERALL_VOL_VAL_MAX DA732X_EQ_OVERALL_VOL_VAL_MASK + +/* DA732X_REG_DMA_CMD (addr=0xD3) */ +#define DA732X_SEL_DSP_DMA_MASK (3 << 0) +#define DA732X_SEL_DSP_DMA_DIS (0 << 0) +#define DA732X_SEL_DSP_DMA_PMEM (1 << 0) +#define DA732X_SEL_DSP_DMA_XMEM (2 << 0) +#define DA732X_SEL_DSP_DMA_YMEM (3 << 0) +#define DA732X_DSP_RW_MASK (1 << 4) +#define DA732X_DSP_DMA_WRITE (0 << 4) +#define DA732X_DSP_DMA_READ (1 << 4) + +/* DA732X_REG_DMA_STATUS (addr=0xDA) */ +#define DA732X_DSP_DMA_FREE (0 << 0) +#define DA732X_DSP_DMA_BUSY (1 << 0) + +#endif /* __DA732X_REG_H_ */ diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c new file mode 100644 index 000000000000..5d8f39e32978 --- /dev/null +++ b/sound/soc/codecs/isabelle.c @@ -0,0 +1,1176 @@ +/* + * isabelle.c - Low power high fidelity audio codec driver + * + * Copyright (c) 2012 Texas Instruments, Inc + * + * 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. + * + * + * Initially based on sound/soc/codecs/twl6040.c + * + */ +#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/regmap.h> +#include <linux/i2c.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/tlv.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <asm/div64.h> +#include "isabelle.h" + + +/* Register default values for ISABELLE driver. */ +static struct reg_default isabelle_reg_defs[] = { + { 0, 0x00 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x00 }, + { 5, 0x00 }, + { 6, 0x00 }, + { 7, 0x00 }, + { 8, 0x00 }, + { 9, 0x00 }, + { 10, 0x00 }, + { 11, 0x00 }, + { 12, 0x00 }, + { 13, 0x00 }, + { 14, 0x00 }, + { 15, 0x00 }, + { 16, 0x00 }, + { 17, 0x00 }, + { 18, 0x00 }, + { 19, 0x00 }, + { 20, 0x00 }, + { 21, 0x02 }, + { 22, 0x02 }, + { 23, 0x02 }, + { 24, 0x02 }, + { 25, 0x0F }, + { 26, 0x8F }, + { 27, 0x0F }, + { 28, 0x8F }, + { 29, 0x00 }, + { 30, 0x00 }, + { 31, 0x00 }, + { 32, 0x00 }, + { 33, 0x00 }, + { 34, 0x00 }, + { 35, 0x00 }, + { 36, 0x00 }, + { 37, 0x00 }, + { 38, 0x00 }, + { 39, 0x00 }, + { 40, 0x00 }, + { 41, 0x00 }, + { 42, 0x00 }, + { 43, 0x00 }, + { 44, 0x00 }, + { 45, 0x00 }, + { 46, 0x00 }, + { 47, 0x00 }, + { 48, 0x00 }, + { 49, 0x00 }, + { 50, 0x00 }, + { 51, 0x00 }, + { 52, 0x00 }, + { 53, 0x00 }, + { 54, 0x00 }, + { 55, 0x00 }, + { 56, 0x00 }, + { 57, 0x00 }, + { 58, 0x00 }, + { 59, 0x00 }, + { 60, 0x00 }, + { 61, 0x00 }, + { 62, 0x00 }, + { 63, 0x00 }, + { 64, 0x00 }, + { 65, 0x00 }, + { 66, 0x00 }, + { 67, 0x00 }, + { 68, 0x00 }, + { 69, 0x90 }, + { 70, 0x90 }, + { 71, 0x90 }, + { 72, 0x00 }, + { 73, 0x00 }, + { 74, 0x00 }, + { 75, 0x00 }, + { 76, 0x00 }, + { 77, 0x00 }, + { 78, 0x00 }, + { 79, 0x00 }, + { 80, 0x00 }, + { 81, 0x00 }, + { 82, 0x00 }, + { 83, 0x00 }, + { 84, 0x00 }, + { 85, 0x07 }, + { 86, 0x00 }, + { 87, 0x00 }, + { 88, 0x00 }, + { 89, 0x07 }, + { 90, 0x80 }, + { 91, 0x07 }, + { 92, 0x07 }, + { 93, 0x00 }, + { 94, 0x00 }, + { 95, 0x00 }, + { 96, 0x00 }, + { 97, 0x00 }, + { 98, 0x00 }, + { 99, 0x00 }, +}; + +static const char *isabelle_rx1_texts[] = {"VRX1", "ARX1"}; +static const char *isabelle_rx2_texts[] = {"VRX2", "ARX2"}; + +static const struct soc_enum isabelle_rx1_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3, 1, isabelle_rx1_texts), + SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5, 1, isabelle_rx1_texts), +}; + +static const struct soc_enum isabelle_rx2_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2, 1, isabelle_rx2_texts), + SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4, 1, isabelle_rx2_texts), +}; + +/* Headset DAC playback switches */ +static const struct snd_kcontrol_new rx1_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_rx1_enum); + +static const struct snd_kcontrol_new rx2_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_rx2_enum); + +/* TX input selection */ +static const char *isabelle_atx_texts[] = {"AMIC1", "DMIC"}; +static const char *isabelle_vtx_texts[] = {"AMIC2", "DMIC"}; + +static const struct soc_enum isabelle_atx_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7, 1, isabelle_atx_texts), + SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, 1, isabelle_atx_texts), +}; + +static const struct soc_enum isabelle_vtx_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6, 1, isabelle_vtx_texts), + SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, 1, isabelle_vtx_texts), +}; + +static const struct snd_kcontrol_new atx_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_atx_enum); + +static const struct snd_kcontrol_new vtx_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_vtx_enum); + +/* Left analog microphone selection */ +static const char *isabelle_amic1_texts[] = { + "Main Mic", "Headset Mic", "Aux/FM Left"}; + +/* Left analog microphone selection */ +static const char *isabelle_amic2_texts[] = {"Sub Mic", "Aux/FM Right"}; + +static const struct soc_enum isabelle_amic1_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 5, + ARRAY_SIZE(isabelle_amic1_texts), + isabelle_amic1_texts), +}; + +static const struct soc_enum isabelle_amic2_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 4, + ARRAY_SIZE(isabelle_amic2_texts), + isabelle_amic2_texts), +}; + +static const struct snd_kcontrol_new amic1_control = + SOC_DAPM_ENUM("Route", isabelle_amic1_enum); + +static const struct snd_kcontrol_new amic2_control = + SOC_DAPM_ENUM("Route", isabelle_amic2_enum); + +static const char *isabelle_st_audio_texts[] = {"ATX1", "ATX2"}; + +static const char *isabelle_st_voice_texts[] = {"VTX1", "VTX2"}; + +static const struct soc_enum isabelle_st_audio_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7, 1, + isabelle_st_audio_texts), + SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7, 1, + isabelle_st_audio_texts), +}; + +static const struct soc_enum isabelle_st_voice_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7, 1, + isabelle_st_voice_texts), + SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7, 1, + isabelle_st_voice_texts), +}; + +static const struct snd_kcontrol_new st_audio_control = + SOC_DAPM_ENUM("Route", isabelle_st_audio_enum); + +static const struct snd_kcontrol_new st_voice_control = + SOC_DAPM_ENUM("Route", isabelle_st_voice_enum); + +/* Mixer controls */ +static const struct snd_kcontrol_new isabelle_hs_left_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC1L Playback Switch", ISABELLE_HSDRV_CFG1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_hs_right_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC1R Playback Switch", ISABELLE_HSDRV_CFG1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_hf_left_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_HFLPGA_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HFLPGA_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_hf_right_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC2R Playback Switch", ISABELLE_HFRPGA_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HFRPGA_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_ep_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_EARDRV_CFG1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_EARDRV_CFG1_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_aux_left_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC3L Playback Switch", ISABELLE_LINEAMP_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_aux_right_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC3R Playback Switch", ISABELLE_LINEAMP_CFG_REG, 5, 1, 0), +SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga1_left_mixer_controls[] = { +SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 6, 1, 0), +SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 5, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga1_right_mixer_controls[] = { +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 2, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 1, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga2_left_mixer_controls[] = { +SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 6, 1, 0), +SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 5, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 4, 1, 0), +SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 2, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga2_right_mixer_controls[] = { +SOC_DAPM_SINGLE("USNC Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 2, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 1, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga3_left_mixer_controls[] = { +SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 6, 1, 0), +SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 5, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga3_right_mixer_controls[] = { +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 2, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 1, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx1_mixer_controls[] = { +SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DL1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx2_mixer_controls[] = { +SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 5, 1, 0), +SOC_DAPM_SINGLE("DL2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx3_mixer_controls[] = { +SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 3, 1, 0), +SOC_DAPM_SINGLE("DL3 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 2, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx4_mixer_controls[] = { +SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DL4 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 0, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx5_mixer_controls[] = { +SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DL5 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx6_mixer_controls[] = { +SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("DL6 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new ep_path_enable_control = + SOC_DAPM_SINGLE("Switch", ISABELLE_EARDRV_CFG2_REG, 0, 1, 0); + +/* TLV Declarations */ +static const DECLARE_TLV_DB_SCALE(mic_amp_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(afm_amp_tlv, -3300, 300, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -1200, 200, 0); +static const DECLARE_TLV_DB_SCALE(hf_tlv, -5000, 200, 0); + +/* from -63 to 0 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(dpga_tlv, -6300, 100, 1); + +/* from -63 to 9 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(rx_tlv, -6300, 100, 1); + +static const DECLARE_TLV_DB_SCALE(st_tlv, -2700, 300, 1); +static const DECLARE_TLV_DB_SCALE(tx_tlv, -600, 100, 0); + +static const struct snd_kcontrol_new isabelle_snd_controls[] = { + SOC_DOUBLE_TLV("Headset Playback Volume", ISABELLE_HSDRV_GAIN_REG, + 4, 0, 0xF, 0, dac_tlv), + SOC_DOUBLE_R_TLV("Handsfree Playback Volume", + ISABELLE_HFLPGA_CFG_REG, ISABELLE_HFRPGA_CFG_REG, + 0, 0x1F, 0, hf_tlv), + SOC_DOUBLE_TLV("Aux Playback Volume", ISABELLE_LINEAMP_GAIN_REG, + 4, 0, 0xF, 0, dac_tlv), + SOC_SINGLE_TLV("Earpiece Playback Volume", ISABELLE_EARDRV_CFG1_REG, + 0, 0xF, 0, dac_tlv), + + SOC_DOUBLE_TLV("Aux FM Volume", ISABELLE_APGA_GAIN_REG, 4, 0, 0xF, 0, + afm_amp_tlv), + SOC_SINGLE_TLV("Mic1 Capture Volume", ISABELLE_MIC1_GAIN_REG, 3, 0x1F, + 0, mic_amp_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", ISABELLE_MIC2_GAIN_REG, 3, 0x1F, + 0, mic_amp_tlv), + + SOC_DOUBLE_R_TLV("DPGA1 Volume", ISABELLE_DPGA1L_GAIN_REG, + ISABELLE_DPGA1R_GAIN_REG, 0, 0x3F, 0, dpga_tlv), + SOC_DOUBLE_R_TLV("DPGA2 Volume", ISABELLE_DPGA2L_GAIN_REG, + ISABELLE_DPGA2R_GAIN_REG, 0, 0x3F, 0, dpga_tlv), + SOC_DOUBLE_R_TLV("DPGA3 Volume", ISABELLE_DPGA3L_GAIN_REG, + ISABELLE_DPGA3R_GAIN_REG, 0, 0x3F, 0, dpga_tlv), + + SOC_SINGLE_TLV("Sidetone Audio TX1 Volume", + ISABELLE_ATX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv), + SOC_SINGLE_TLV("Sidetone Audio TX2 Volume", + ISABELLE_ATX_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv), + SOC_SINGLE_TLV("Sidetone Voice TX1 Volume", + ISABELLE_VTX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv), + SOC_SINGLE_TLV("Sidetone Voice TX2 Volume", + ISABELLE_VTX2_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv), + + SOC_SINGLE_TLV("Audio TX1 Volume", ISABELLE_ATX1_DPGA_REG, 4, 0xF, 0, + tx_tlv), + SOC_SINGLE_TLV("Audio TX2 Volume", ISABELLE_ATX2_DPGA_REG, 4, 0xF, 0, + tx_tlv), + SOC_SINGLE_TLV("Voice TX1 Volume", ISABELLE_VTX1_DPGA_REG, 4, 0xF, 0, + tx_tlv), + SOC_SINGLE_TLV("Voice TX2 Volume", ISABELLE_VTX2_DPGA_REG, 4, 0xF, 0, + tx_tlv), + + SOC_SINGLE_TLV("RX1 DPGA Volume", ISABELLE_RX1_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX2 DPGA Volume", ISABELLE_RX2_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX3 DPGA Volume", ISABELLE_RX3_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX4 DPGA Volume", ISABELLE_RX4_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX5 DPGA Volume", ISABELLE_RX5_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX6 DPGA Volume", ISABELLE_RX6_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + + SOC_SINGLE("Headset Noise Gate", ISABELLE_HS_NG_CFG1_REG, 7, 1, 0), + SOC_SINGLE("Handsfree Noise Gate", ISABELLE_HF_NG_CFG1_REG, 7, 1, 0), + + SOC_SINGLE("ATX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 7, 1, 0), + SOC_SINGLE("ATX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 6, 1, 0), + SOC_SINGLE("ARX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 5, 1, 0), + SOC_SINGLE("ARX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 4, 1, 0), + SOC_SINGLE("ARX3 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 3, 1, 0), + SOC_SINGLE("ARX4 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 2, 1, 0), + SOC_SINGLE("ARX5 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 1, 1, 0), + SOC_SINGLE("ARX6 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 0, 1, 0), + SOC_SINGLE("VRX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 3, 1, 0), + SOC_SINGLE("VRX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 2, 1, 0), + + SOC_SINGLE("ATX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 7, 1, 0), + SOC_SINGLE("ATX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 6, 1, 0), + SOC_SINGLE("VTX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 5, 1, 0), + SOC_SINGLE("VTX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 4, 1, 0), + SOC_SINGLE("RX1 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 5, 1, 0), + SOC_SINGLE("RX2 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 4, 1, 0), + SOC_SINGLE("RX3 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 3, 1, 0), + SOC_SINGLE("RX4 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 2, 1, 0), + SOC_SINGLE("RX5 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 1, 1, 0), + SOC_SINGLE("RX6 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 0, 1, 0), + + SOC_SINGLE("ULATX12 Capture Switch", ISABELLE_ULATX12_INTF_CFG_REG, + 7, 1, 0), + + SOC_SINGLE("DL12 Playback Switch", ISABELLE_DL12_INTF_CFG_REG, + 7, 1, 0), + SOC_SINGLE("DL34 Playback Switch", ISABELLE_DL34_INTF_CFG_REG, + 7, 1, 0), + SOC_SINGLE("DL56 Playback Switch", ISABELLE_DL56_INTF_CFG_REG, + 7, 1, 0), + + /* DMIC Switch */ + SOC_SINGLE("DMIC Switch", ISABELLE_DMIC_CFG_REG, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget isabelle_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("MAINMIC"), + SND_SOC_DAPM_INPUT("HSMIC"), + SND_SOC_DAPM_INPUT("SUBMIC"), + SND_SOC_DAPM_INPUT("LINEIN1"), + SND_SOC_DAPM_INPUT("LINEIN2"), + SND_SOC_DAPM_INPUT("DMICDAT"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HSOL"), + SND_SOC_DAPM_OUTPUT("HSOR"), + SND_SOC_DAPM_OUTPUT("HFL"), + SND_SOC_DAPM_OUTPUT("HFR"), + SND_SOC_DAPM_OUTPUT("EP"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + + SND_SOC_DAPM_PGA("DL1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL6", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog input muxes for the capture amplifiers */ + SND_SOC_DAPM_MUX("Analog Left Capture Route", + SND_SOC_NOPM, 0, 0, &amic1_control), + SND_SOC_DAPM_MUX("Analog Right Capture Route", + SND_SOC_NOPM, 0, 0, &amic2_control), + + SND_SOC_DAPM_MUX("Sidetone Audio Playback", SND_SOC_NOPM, 0, 0, + &st_audio_control), + SND_SOC_DAPM_MUX("Sidetone Voice Playback", SND_SOC_NOPM, 0, 0, + &st_voice_control), + + /* AIF */ + SND_SOC_DAPM_AIF_IN("INTF1_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 7, 0), + SND_SOC_DAPM_AIF_IN("INTF2_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 6, 0), + + SND_SOC_DAPM_AIF_OUT("INTF1_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 5, 0), + SND_SOC_DAPM_AIF_OUT("INTF2_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 4, 0), + + SND_SOC_DAPM_OUT_DRV("ULATX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("ULATX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("ULVTX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("ULVTX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog Capture PGAs */ + SND_SOC_DAPM_PGA("MicAmp1", ISABELLE_AMIC_CFG_REG, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("MicAmp2", ISABELLE_AMIC_CFG_REG, 4, 0, NULL, 0), + + /* Auxiliary FM PGAs */ + SND_SOC_DAPM_PGA("APGA1", ISABELLE_APGA_CFG_REG, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("APGA2", ISABELLE_APGA_CFG_REG, 6, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1", "Left Front Capture", + ISABELLE_AMIC_CFG_REG, 7, 0), + SND_SOC_DAPM_ADC("ADC2", "Right Front Capture", + ISABELLE_AMIC_CFG_REG, 6, 0), + + /* Microphone Bias */ + SND_SOC_DAPM_SUPPLY("Headset Mic Bias", ISABELLE_ABIAS_CFG_REG, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Main Mic Bias", ISABELLE_ABIAS_CFG_REG, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias", + ISABELLE_DBIAS_CFG_REG, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias", + ISABELLE_DBIAS_CFG_REG, 2, 0, NULL, 0), + + /* Mixers */ + SND_SOC_DAPM_MIXER("Headset Left Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hs_left_mixer_controls, + ARRAY_SIZE(isabelle_hs_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Headset Right Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hs_right_mixer_controls, + ARRAY_SIZE(isabelle_hs_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Handsfree Left Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hf_left_mixer_controls, + ARRAY_SIZE(isabelle_hf_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Handsfree Right Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hf_right_mixer_controls, + ARRAY_SIZE(isabelle_hf_right_mixer_controls)), + SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_aux_left_mixer_controls, + ARRAY_SIZE(isabelle_aux_left_mixer_controls)), + SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_aux_right_mixer_controls, + ARRAY_SIZE(isabelle_aux_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Earphone Mixer", SND_SOC_NOPM, 0, 0, + isabelle_ep_mixer_controls, + ARRAY_SIZE(isabelle_ep_mixer_controls)), + + SND_SOC_DAPM_MIXER("DPGA1L Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga1_left_mixer_controls, + ARRAY_SIZE(isabelle_dpga1_left_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA1R Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga1_right_mixer_controls, + ARRAY_SIZE(isabelle_dpga1_right_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA2L Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga2_left_mixer_controls, + ARRAY_SIZE(isabelle_dpga2_left_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA2R Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga2_right_mixer_controls, + ARRAY_SIZE(isabelle_dpga2_right_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA3L Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga3_left_mixer_controls, + ARRAY_SIZE(isabelle_dpga3_left_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA3R Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga3_right_mixer_controls, + ARRAY_SIZE(isabelle_dpga3_right_mixer_controls)), + + SND_SOC_DAPM_MIXER("RX1 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx1_mixer_controls, + ARRAY_SIZE(isabelle_rx1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX2 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx2_mixer_controls, + ARRAY_SIZE(isabelle_rx2_mixer_controls)), + SND_SOC_DAPM_MIXER("RX3 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx3_mixer_controls, + ARRAY_SIZE(isabelle_rx3_mixer_controls)), + SND_SOC_DAPM_MIXER("RX4 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx4_mixer_controls, + ARRAY_SIZE(isabelle_rx4_mixer_controls)), + SND_SOC_DAPM_MIXER("RX5 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx5_mixer_controls, + ARRAY_SIZE(isabelle_rx5_mixer_controls)), + SND_SOC_DAPM_MIXER("RX6 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx6_mixer_controls, + ARRAY_SIZE(isabelle_rx6_mixer_controls)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1L", "Headset Playback", ISABELLE_DAC_CFG_REG, + 5, 0), + SND_SOC_DAPM_DAC("DAC1R", "Headset Playback", ISABELLE_DAC_CFG_REG, + 4, 0), + SND_SOC_DAPM_DAC("DAC2L", "Handsfree Playback", ISABELLE_DAC_CFG_REG, + 3, 0), + SND_SOC_DAPM_DAC("DAC2R", "Handsfree Playback", ISABELLE_DAC_CFG_REG, + 2, 0), + SND_SOC_DAPM_DAC("DAC3L", "Lineout Playback", ISABELLE_DAC_CFG_REG, + 1, 0), + SND_SOC_DAPM_DAC("DAC3R", "Lineout Playback", ISABELLE_DAC_CFG_REG, + 0, 0), + + /* Analog Playback PGAs */ + SND_SOC_DAPM_PGA("Sidetone Audio PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sidetone Voice PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HF Left PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HF Right PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA1L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA1R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA2L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA2R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA3L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA3R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog Playback Mux */ + SND_SOC_DAPM_MUX("RX1 Playback", ISABELLE_ALU_RX_EN_REG, 5, 0, + &rx1_mux_controls), + SND_SOC_DAPM_MUX("RX2 Playback", ISABELLE_ALU_RX_EN_REG, 4, 0, + &rx2_mux_controls), + + /* TX Select */ + SND_SOC_DAPM_MUX("ATX Select", ISABELLE_TX_INPUT_CFG_REG, + 7, 0, &atx_mux_controls), + SND_SOC_DAPM_MUX("VTX Select", ISABELLE_TX_INPUT_CFG_REG, + 6, 0, &vtx_mux_controls), + + SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, + &ep_path_enable_control), + + /* Output Drivers */ + SND_SOC_DAPM_OUT_DRV("HS Left Driver", ISABELLE_HSDRV_CFG2_REG, + 1, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HS Right Driver", ISABELLE_HSDRV_CFG2_REG, + 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LINEOUT1 Left Driver", ISABELLE_LINEAMP_CFG_REG, + 1, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LINEOUT2 Right Driver", ISABELLE_LINEAMP_CFG_REG, + 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Earphone Driver", ISABELLE_EARDRV_CFG2_REG, + 1, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("HF Left Driver", ISABELLE_HFDRV_CFG_REG, + 1, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HF Right Driver", ISABELLE_HFDRV_CFG_REG, + 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route isabelle_intercon[] = { + /* Interface mapping */ + { "DL1", "DL12 Playback Switch", "INTF1_SDI" }, + { "DL2", "DL12 Playback Switch", "INTF1_SDI" }, + { "DL3", "DL34 Playback Switch", "INTF1_SDI" }, + { "DL4", "DL34 Playback Switch", "INTF1_SDI" }, + { "DL5", "DL56 Playback Switch", "INTF1_SDI" }, + { "DL6", "DL56 Playback Switch", "INTF1_SDI" }, + + { "DL1", "DL12 Playback Switch", "INTF2_SDI" }, + { "DL2", "DL12 Playback Switch", "INTF2_SDI" }, + { "DL3", "DL34 Playback Switch", "INTF2_SDI" }, + { "DL4", "DL34 Playback Switch", "INTF2_SDI" }, + { "DL5", "DL56 Playback Switch", "INTF2_SDI" }, + { "DL6", "DL56 Playback Switch", "INTF2_SDI" }, + + /* Input side mapping */ + { "Sidetone Audio PGA", NULL, "Sidetone Audio Playback" }, + { "Sidetone Voice PGA", NULL, "Sidetone Voice Playback" }, + + { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Audio PGA" }, + + { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" }, + { "RX1 Mixer", "DL1 Playback Switch", "DL1" }, + + { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Audio PGA" }, + + { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" }, + { "RX2 Mixer", "DL2 Playback Switch", "DL2" }, + + { "RX3 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" }, + { "RX3 Mixer", "DL3 Playback Switch", "DL3" }, + + { "RX4 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" }, + { "RX4 Mixer", "DL4 Playback Switch", "DL4" }, + + { "RX5 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" }, + { "RX5 Mixer", "DL5 Playback Switch", "DL5" }, + + { "RX6 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" }, + { "RX6 Mixer", "DL6 Playback Switch", "DL6" }, + + /* Capture path */ + { "Analog Left Capture Route", "Headset Mic", "HSMIC" }, + { "Analog Left Capture Route", "Main Mic", "MAINMIC" }, + { "Analog Left Capture Route", "Aux/FM Left", "LINEIN1" }, + + { "Analog Right Capture Route", "Sub Mic", "SUBMIC" }, + { "Analog Right Capture Route", "Aux/FM Right", "LINEIN2" }, + + { "MicAmp1", NULL, "Analog Left Capture Route" }, + { "MicAmp2", NULL, "Analog Right Capture Route" }, + + { "ADC1", NULL, "MicAmp1" }, + { "ADC2", NULL, "MicAmp2" }, + + { "ATX Select", "AMIC1", "ADC1" }, + { "ATX Select", "DMIC", "DMICDAT" }, + { "ATX Select", "AMIC2", "ADC2" }, + + { "VTX Select", "AMIC1", "ADC1" }, + { "VTX Select", "DMIC", "DMICDAT" }, + { "VTX Select", "AMIC2", "ADC2" }, + + { "ULATX1", "ATX1 Filter Enable Switch", "ATX Select" }, + { "ULATX1", "ATX1 Filter Bypass Switch", "ATX Select" }, + { "ULATX2", "ATX2 Filter Enable Switch", "ATX Select" }, + { "ULATX2", "ATX2 Filter Bypass Switch", "ATX Select" }, + + { "ULVTX1", "VTX1 Filter Enable Switch", "VTX Select" }, + { "ULVTX1", "VTX1 Filter Bypass Switch", "VTX Select" }, + { "ULVTX2", "VTX2 Filter Enable Switch", "VTX Select" }, + { "ULVTX2", "VTX2 Filter Bypass Switch", "VTX Select" }, + + { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX1" }, + { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX2" }, + { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX1" }, + { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX2" }, + + { "INTF1_SDO", NULL, "ULVTX1" }, + { "INTF1_SDO", NULL, "ULVTX2" }, + { "INTF2_SDO", NULL, "ULVTX1" }, + { "INTF2_SDO", NULL, "ULVTX2" }, + + /* AFM Path */ + { "APGA1", NULL, "LINEIN1" }, + { "APGA2", NULL, "LINEIN2" }, + + { "RX1 Playback", "VRX1 Filter Bypass Switch", "RX1 Mixer" }, + { "RX1 Playback", "ARX1 Filter Bypass Switch", "RX1 Mixer" }, + { "RX1 Playback", "RX1 Filter Enable Switch", "RX1 Mixer" }, + + { "RX2 Playback", "VRX2 Filter Bypass Switch", "RX2 Mixer" }, + { "RX2 Playback", "ARX2 Filter Bypass Switch", "RX2 Mixer" }, + { "RX2 Playback", "RX2 Filter Enable Switch", "RX2 Mixer" }, + + { "RX3 Playback", "ARX3 Filter Bypass Switch", "RX3 Mixer" }, + { "RX3 Playback", "RX3 Filter Enable Switch", "RX3 Mixer" }, + + { "RX4 Playback", "ARX4 Filter Bypass Switch", "RX4 Mixer" }, + { "RX4 Playback", "RX4 Filter Enable Switch", "RX4 Mixer" }, + + { "RX5 Playback", "ARX5 Filter Bypass Switch", "RX5 Mixer" }, + { "RX5 Playback", "RX5 Filter Enable Switch", "RX5 Mixer" }, + + { "RX6 Playback", "ARX6 Filter Bypass Switch", "RX6 Mixer" }, + { "RX6 Playback", "RX6 Filter Enable Switch", "RX6 Mixer" }, + + { "DPGA1L Mixer", "RX1 Playback Switch", "RX1 Playback" }, + { "DPGA1L Mixer", "RX3 Playback Switch", "RX3 Playback" }, + { "DPGA1L Mixer", "RX5 Playback Switch", "RX5 Playback" }, + + { "DPGA1R Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA1R Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA1R Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA1L", NULL, "DPGA1L Mixer" }, + { "DPGA1R", NULL, "DPGA1R Mixer" }, + + { "DAC1L", NULL, "DPGA1L" }, + { "DAC1R", NULL, "DPGA1R" }, + + { "DPGA2L Mixer", "RX1 Playback Switch", "RX1 Playback" }, + { "DPGA2L Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA2L Mixer", "RX3 Playback Switch", "RX3 Playback" }, + { "DPGA2L Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA2L Mixer", "RX5 Playback Switch", "RX5 Playback" }, + { "DPGA2L Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA2R Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA2R Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA2R Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA2L", NULL, "DPGA2L Mixer" }, + { "DPGA2R", NULL, "DPGA2R Mixer" }, + + { "DAC2L", NULL, "DPGA2L" }, + { "DAC2R", NULL, "DPGA2R" }, + + { "DPGA3L Mixer", "RX1 Playback Switch", "RX1 Playback" }, + { "DPGA3L Mixer", "RX3 Playback Switch", "RX3 Playback" }, + { "DPGA3L Mixer", "RX5 Playback Switch", "RX5 Playback" }, + + { "DPGA3R Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA3R Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA3R Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA3L", NULL, "DPGA3L Mixer" }, + { "DPGA3R", NULL, "DPGA3R Mixer" }, + + { "DAC3L", NULL, "DPGA3L" }, + { "DAC3R", NULL, "DPGA3R" }, + + { "Headset Left Mixer", "DAC1L Playback Switch", "DAC1L" }, + { "Headset Left Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "Headset Right Mixer", "DAC1R Playback Switch", "DAC1R" }, + { "Headset Right Mixer", "APGA2 Playback Switch", "APGA2" }, + + { "HS Left Driver", NULL, "Headset Left Mixer" }, + { "HS Right Driver", NULL, "Headset Right Mixer" }, + + { "HSOL", NULL, "HS Left Driver" }, + { "HSOR", NULL, "HS Right Driver" }, + + /* Earphone playback path */ + { "Earphone Mixer", "DAC2L Playback Switch", "DAC2L" }, + { "Earphone Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "Earphone Playback", "Switch", "Earphone Mixer" }, + { "Earphone Driver", NULL, "Earphone Playback" }, + { "EP", NULL, "Earphone Driver" }, + + { "Handsfree Left Mixer", "DAC2L Playback Switch", "DAC2L" }, + { "Handsfree Left Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "Handsfree Right Mixer", "DAC2R Playback Switch", "DAC2R" }, + { "Handsfree Right Mixer", "APGA2 Playback Switch", "APGA2" }, + + { "HF Left PGA", NULL, "Handsfree Left Mixer" }, + { "HF Right PGA", NULL, "Handsfree Right Mixer" }, + + { "HF Left Driver", NULL, "HF Left PGA" }, + { "HF Right Driver", NULL, "HF Right PGA" }, + + { "HFL", NULL, "HF Left Driver" }, + { "HFR", NULL, "HF Right Driver" }, + + { "LINEOUT1 Mixer", "DAC3L Playback Switch", "DAC3L" }, + { "LINEOUT1 Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "LINEOUT2 Mixer", "DAC3R Playback Switch", "DAC3R" }, + { "LINEOUT2 Mixer", "APGA2 Playback Switch", "APGA2" }, + + { "LINEOUT1 Driver", NULL, "LINEOUT1 Mixer" }, + { "LINEOUT2 Driver", NULL, "LINEOUT2 Mixer" }, + + { "LINEOUT1", NULL, "LINEOUT1 Driver" }, + { "LINEOUT2", NULL, "LINEOUT2 Driver" }, +}; + +static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, ISABELLE_DAC1_SOFTRAMP_REG, + BIT(4), (mute ? BIT(4) : 0)); + + return 0; +} + +static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, ISABELLE_DAC2_SOFTRAMP_REG, + BIT(4), (mute ? BIT(4) : 0)); + + return 0; +} + +static int isabelle_line_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, ISABELLE_DAC3_SOFTRAMP_REG, + BIT(4), (mute ? BIT(4) : 0)); + + return 0; +} + +static int isabelle_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG, + ISABELLE_CHIP_EN, BIT(0)); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG, + ISABELLE_CHIP_EN, 0); + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static int isabelle_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; + u16 aif = 0; + unsigned int fs_val = 0; + + switch (params_rate(params)) { + case 8000: + fs_val = ISABELLE_FS_RATE_8; + break; + case 11025: + fs_val = ISABELLE_FS_RATE_11; + break; + case 12000: + fs_val = ISABELLE_FS_RATE_12; + break; + case 16000: + fs_val = ISABELLE_FS_RATE_16; + break; + case 22050: + fs_val = ISABELLE_FS_RATE_22; + break; + case 24000: + fs_val = ISABELLE_FS_RATE_24; + break; + case 32000: + fs_val = ISABELLE_FS_RATE_32; + break; + case 44100: + fs_val = ISABELLE_FS_RATE_44; + break; + case 48000: + fs_val = ISABELLE_FS_RATE_48; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ISABELLE_FS_RATE_CFG_REG, + ISABELLE_FS_RATE_MASK, fs_val); + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S20_3LE: + aif |= ISABELLE_AIF_LENGTH_20; + break; + case SNDRV_PCM_FORMAT_S32_LE: + aif |= ISABELLE_AIF_LENGTH_32; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG, + ISABELLE_AIF_LENGTH_MASK, aif); + + return 0; +} + +static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + unsigned int aif_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + aif_val &= ~ISABELLE_AIF_MS; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif_val |= ISABELLE_AIF_MS; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aif_val |= ISABELLE_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + aif_val |= ISABELLE_LEFT_J_MODE; + break; + case SND_SOC_DAIFMT_PDM: + aif_val |= ISABELLE_PDM_MODE; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG, + (ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val); + + return 0; +} + +/* Rates supported by Isabelle driver */ +#define ISABELLE_RATES SNDRV_PCM_RATE_8000_48000 + +/* Formates supported by Isabelle driver. */ +#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops isabelle_hs_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, + .digital_mute = isabelle_hs_mute, +}; + +static struct snd_soc_dai_ops isabelle_hf_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, + .digital_mute = isabelle_hf_mute, +}; + +static struct snd_soc_dai_ops isabelle_line_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, + .digital_mute = isabelle_line_mute, +}; + +static struct snd_soc_dai_ops isabelle_ul_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, +}; + +/* ISABELLE dai structure */ +static struct snd_soc_dai_driver isabelle_dai[] = { + { + .name = "isabelle-dl1", + .playback = { + .stream_name = "Headset Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_hs_dai_ops, + }, + { + .name = "isabelle-dl2", + .playback = { + .stream_name = "Handsfree Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_hf_dai_ops, + }, + { + .name = "isabelle-lineout", + .playback = { + .stream_name = "Lineout Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_line_dai_ops, + }, + { + .name = "isabelle-ul", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_ul_dai_ops, + }, +}; + +static int isabelle_probe(struct snd_soc_codec *codec) +{ + int ret = 0; + + codec->control_data = dev_get_regmap(codec->dev, NULL); + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_isabelle = { + .probe = isabelle_probe, + .set_bias_level = isabelle_set_bias_level, + .controls = isabelle_snd_controls, + .num_controls = ARRAY_SIZE(isabelle_snd_controls), + .dapm_widgets = isabelle_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets), + .dapm_routes = isabelle_intercon, + .num_dapm_routes = ARRAY_SIZE(isabelle_intercon), + .idle_bias_off = true, +}; + +static const struct regmap_config isabelle_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = ISABELLE_MAX_REGISTER, + .reg_defaults = isabelle_reg_defs, + .num_reg_defaults = ARRAY_SIZE(isabelle_reg_defs), + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit isabelle_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *isabelle_regmap; + int ret = 0; + + isabelle_regmap = devm_regmap_init_i2c(i2c, &isabelle_regmap_config); + if (IS_ERR(isabelle_regmap)) { + ret = PTR_ERR(isabelle_regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(i2c, isabelle_regmap); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_isabelle, isabelle_dai, + ARRAY_SIZE(isabelle_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + return ret; +} + +static int __devexit isabelle_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id isabelle_i2c_id[] = { + { "isabelle", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id); + +static struct i2c_driver isabelle_i2c_driver = { + .driver = { + .name = "isabelle", + .owner = THIS_MODULE, + }, + .probe = isabelle_i2c_probe, + .remove = __devexit_p(isabelle_i2c_remove), + .id_table = isabelle_i2c_id, +}; + +module_i2c_driver(isabelle_i2c_driver); + +MODULE_DESCRIPTION("ASoC ISABELLE driver"); +MODULE_AUTHOR("Vishwas A Deshpande <vishwas.a.deshpande@ti.com>"); +MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/isabelle.h b/sound/soc/codecs/isabelle.h new file mode 100644 index 000000000000..96d839a8c956 --- /dev/null +++ b/sound/soc/codecs/isabelle.h @@ -0,0 +1,143 @@ +/* + * isabelle.h - Low power high fidelity audio codec driver header file + * + * Copyright (c) 2012 Texas Instruments, Inc + * + * 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. + * + */ + +#ifndef _ISABELLE_H +#define _ISABELLE_H + +#include <linux/bitops.h> + +/* ISABELLE REGISTERS */ + +#define ISABELLE_PWR_CFG_REG 0x01 +#define ISABELLE_PWR_EN_REG 0x02 +#define ISABELLE_PS_EN1_REG 0x03 +#define ISABELLE_INT1_STATUS_REG 0x04 +#define ISABELLE_INT1_MASK_REG 0x05 +#define ISABELLE_INT2_STATUS_REG 0x06 +#define ISABELLE_INT2_MASK_REG 0x07 +#define ISABELLE_HKCTL1_REG 0x08 +#define ISABELLE_HKCTL2_REG 0x09 +#define ISABELLE_HKCTL3_REG 0x0A +#define ISABELLE_ACCDET_STATUS_REG 0x0B +#define ISABELLE_BUTTON_ID_REG 0x0C +#define ISABELLE_PLL_CFG_REG 0x10 +#define ISABELLE_PLL_EN_REG 0x11 +#define ISABELLE_FS_RATE_CFG_REG 0x12 +#define ISABELLE_INTF_CFG_REG 0x13 +#define ISABELLE_INTF_EN_REG 0x14 +#define ISABELLE_ULATX12_INTF_CFG_REG 0x15 +#define ISABELLE_DL12_INTF_CFG_REG 0x16 +#define ISABELLE_DL34_INTF_CFG_REG 0x17 +#define ISABELLE_DL56_INTF_CFG_REG 0x18 +#define ISABELLE_ATX_STPGA1_CFG_REG 0x19 +#define ISABELLE_ATX_STPGA2_CFG_REG 0x1A +#define ISABELLE_VTX_STPGA1_CFG_REG 0x1B +#define ISABELLE_VTX2_STPGA2_CFG_REG 0x1C +#define ISABELLE_ATX1_DPGA_REG 0x1D +#define ISABELLE_ATX2_DPGA_REG 0x1E +#define ISABELLE_VTX1_DPGA_REG 0x1F +#define ISABELLE_VTX2_DPGA_REG 0x20 +#define ISABELLE_TX_INPUT_CFG_REG 0x21 +#define ISABELLE_RX_INPUT_CFG_REG 0x22 +#define ISABELLE_RX_INPUT_CFG2_REG 0x23 +#define ISABELLE_VOICE_HPF_CFG_REG 0x24 +#define ISABELLE_AUDIO_HPF_CFG_REG 0x25 +#define ISABELLE_RX1_DPGA_REG 0x26 +#define ISABELLE_RX2_DPGA_REG 0x27 +#define ISABELLE_RX3_DPGA_REG 0x28 +#define ISABELLE_RX4_DPGA_REG 0x29 +#define ISABELLE_RX5_DPGA_REG 0x2A +#define ISABELLE_RX6_DPGA_REG 0x2B +#define ISABELLE_ALU_TX_EN_REG 0x2C +#define ISABELLE_ALU_RX_EN_REG 0x2D +#define ISABELLE_IIR_RESYNC_REG 0x2E +#define ISABELLE_ABIAS_CFG_REG 0x30 +#define ISABELLE_DBIAS_CFG_REG 0x31 +#define ISABELLE_MIC1_GAIN_REG 0x32 +#define ISABELLE_MIC2_GAIN_REG 0x33 +#define ISABELLE_AMIC_CFG_REG 0x34 +#define ISABELLE_DMIC_CFG_REG 0x35 +#define ISABELLE_APGA_GAIN_REG 0x36 +#define ISABELLE_APGA_CFG_REG 0x37 +#define ISABELLE_TX_GAIN_DLY_REG 0x38 +#define ISABELLE_RX_GAIN_DLY_REG 0x39 +#define ISABELLE_RX_PWR_CTRL_REG 0x3A +#define ISABELLE_DPGA1LR_IN_SEL_REG 0x3B +#define ISABELLE_DPGA1L_GAIN_REG 0x3C +#define ISABELLE_DPGA1R_GAIN_REG 0x3D +#define ISABELLE_DPGA2L_IN_SEL_REG 0x3E +#define ISABELLE_DPGA2R_IN_SEL_REG 0x3F +#define ISABELLE_DPGA2L_GAIN_REG 0x40 +#define ISABELLE_DPGA2R_GAIN_REG 0x41 +#define ISABELLE_DPGA3LR_IN_SEL_REG 0x42 +#define ISABELLE_DPGA3L_GAIN_REG 0x43 +#define ISABELLE_DPGA3R_GAIN_REG 0x44 +#define ISABELLE_DAC1_SOFTRAMP_REG 0x45 +#define ISABELLE_DAC2_SOFTRAMP_REG 0x46 +#define ISABELLE_DAC3_SOFTRAMP_REG 0x47 +#define ISABELLE_DAC_CFG_REG 0x48 +#define ISABELLE_EARDRV_CFG1_REG 0x49 +#define ISABELLE_EARDRV_CFG2_REG 0x4A +#define ISABELLE_HSDRV_GAIN_REG 0x4B +#define ISABELLE_HSDRV_CFG1_REG 0x4C +#define ISABELLE_HSDRV_CFG2_REG 0x4D +#define ISABELLE_HS_NG_CFG1_REG 0x4E +#define ISABELLE_HS_NG_CFG2_REG 0x4F +#define ISABELLE_LINEAMP_GAIN_REG 0x50 +#define ISABELLE_LINEAMP_CFG_REG 0x51 +#define ISABELLE_HFL_VOL_CTRL_REG 0x52 +#define ISABELLE_HFL_SFTVOL_CTRL_REG 0x53 +#define ISABELLE_HFL_LIM_CTRL_1_REG 0x54 +#define ISABELLE_HFL_LIM_CTRL_2_REG 0x55 +#define ISABELLE_HFR_VOL_CTRL_REG 0x56 +#define ISABELLE_HFR_SFTVOL_CTRL_REG 0x57 +#define ISABELLE_HFR_LIM_CTRL_1_REG 0x58 +#define ISABELLE_HFR_LIM_CTRL_2_REG 0x59 +#define ISABELLE_HF_MODE_REG 0x5A +#define ISABELLE_HFLPGA_CFG_REG 0x5B +#define ISABELLE_HFRPGA_CFG_REG 0x5C +#define ISABELLE_HFDRV_CFG_REG 0x5D +#define ISABELLE_PDMOUT_CFG1_REG 0x5E +#define ISABELLE_PDMOUT_CFG2_REG 0x5F +#define ISABELLE_PDMOUT_L_WM_REG 0x60 +#define ISABELLE_PDMOUT_R_WM_REG 0x61 +#define ISABELLE_HF_NG_CFG1_REG 0x62 +#define ISABELLE_HF_NG_CFG2_REG 0x63 + +/* ISABELLE_PWR_EN_REG (0x02h) */ +#define ISABELLE_CHIP_EN BIT(0) + +/* ISABELLE DAI FORMATS */ +#define ISABELLE_AIF_FMT_MASK 0x70 +#define ISABELLE_I2S_MODE 0x0 +#define ISABELLE_LEFT_J_MODE 0x1 +#define ISABELLE_PDM_MODE 0x2 + +#define ISABELLE_AIF_LENGTH_MASK 0x30 +#define ISABELLE_AIF_LENGTH_20 0x00 +#define ISABELLE_AIF_LENGTH_32 0x10 + +#define ISABELLE_AIF_MS 0x80 + +#define ISABELLE_FS_RATE_MASK 0xF +#define ISABELLE_FS_RATE_8 0x0 +#define ISABELLE_FS_RATE_11 0x1 +#define ISABELLE_FS_RATE_12 0x2 +#define ISABELLE_FS_RATE_16 0x4 +#define ISABELLE_FS_RATE_22 0x5 +#define ISABELLE_FS_RATE_24 0x6 +#define ISABELLE_FS_RATE_32 0x8 +#define ISABELLE_FS_RATE_44 0x9 +#define ISABELLE_FS_RATE_48 0xA + +#define ISABELLE_MAX_REGISTER 0xFF + +#endif diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index 802b9f176b16..99b0a9dcff34 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -12,7 +12,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> @@ -1358,7 +1357,7 @@ static struct snd_soc_dai_ops lm49453_lineout_dai_ops = { }; /* LM49453 dai structure. */ -static const struct snd_soc_dai_driver lm49453_dai[] = { +static struct snd_soc_dai_driver lm49453_dai[] = { { .name = "LM49453 Headset", .playback = { diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 35179e2c23c9..7cd508e16a5c 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2216,7 +2216,7 @@ static irqreturn_t max98095_report_jack(int irq, void *data) return IRQ_HANDLED; } -int max98095_jack_detect_enable(struct snd_soc_codec *codec) +static int max98095_jack_detect_enable(struct snd_soc_codec *codec) { struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); int ret = 0; @@ -2245,7 +2245,7 @@ int max98095_jack_detect_enable(struct snd_soc_codec *codec) return ret; } -int max98095_jack_detect_disable(struct snd_soc_codec *codec) +static int max98095_jack_detect_disable(struct snd_soc_codec *codec) { int ret = 0; @@ -2286,6 +2286,7 @@ int max98095_jack_detect(struct snd_soc_codec *codec, max98095_report_jack(client->irq, codec); return 0; } +EXPORT_SYMBOL_GPL(max98095_jack_detect); #ifdef CONFIG_PM static int max98095_suspend(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 22cb5bf59273..96aa5fa05160 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -638,7 +638,7 @@ static __devinit int ml26124_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, priv); - priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap); + priv->regmap = devm_regmap_init_i2c(i2c, &ml26124_i2c_regmap); if (IS_ERR(priv->regmap)) { ret = PTR_ERR(priv->regmap); dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret); @@ -651,10 +651,7 @@ static __devinit int ml26124_i2c_probe(struct i2c_client *i2c, static __devexit int ml26124_i2c_remove(struct i2c_client *client) { - struct ml26124_priv *priv = i2c_get_clientdata(client); - snd_soc_unregister_codec(&client->dev); - regmap_exit(priv->regmap); return 0; } diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c new file mode 100644 index 000000000000..dd8d856053fc --- /dev/null +++ b/sound/soc/codecs/spdif_receiver.c @@ -0,0 +1,67 @@ +/* + * ALSA SoC SPDIF DIR (Digital Interface Reciever) driver + * + * Based on ALSA SoC SPDIF DIT driver + * + * This driver is used by controllers which can operate in DIR (SPDI/F) where + * no codec is needed. This file provides stub codec that can be used + * in these configurations. SPEAr SPDIF IN Audio controller uses this driver. + * + * Author: Vipin Kumar, <vipin.kumar@st.com> + * Copyright: (C) 2012 ST Microelectronics + * + * 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/slab.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/initval.h> + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static struct snd_soc_codec_driver soc_codec_spdif_dir; + +static struct snd_soc_dai_driver dir_stub_dai = { + .name = "dir-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, +}; + +static int spdif_dir_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dir, + &dir_stub_dai, 1); +} + +static int spdif_dir_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver spdif_dir_driver = { + .probe = spdif_dir_probe, + .remove = spdif_dir_remove, + .driver = { + .name = "spdif-dir", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(spdif_dir_driver); + +MODULE_DESCRIPTION("ASoC SPDIF DIR driver"); +MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c new file mode 100644 index 000000000000..0c225cd569d2 --- /dev/null +++ b/sound/soc/codecs/sta529.c @@ -0,0 +1,442 @@ +/* + * ASoC codec driver for spear platform + * + * sound/soc/codecs/sta529.c -- spear ALSA Soc codec driver + * + * Copyright (C) 2012 ST Microelectronics + * Rajeev Kumar <rajeev-dlh.kumar@st.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +/* STA529 Register offsets */ +#define STA529_FFXCFG0 0x00 +#define STA529_FFXCFG1 0x01 +#define STA529_MVOL 0x02 +#define STA529_LVOL 0x03 +#define STA529_RVOL 0x04 +#define STA529_TTF0 0x05 +#define STA529_TTF1 0x06 +#define STA529_TTP0 0x07 +#define STA529_TTP1 0x08 +#define STA529_S2PCFG0 0x0A +#define STA529_S2PCFG1 0x0B +#define STA529_P2SCFG0 0x0C +#define STA529_P2SCFG1 0x0D +#define STA529_PLLCFG0 0x14 +#define STA529_PLLCFG1 0x15 +#define STA529_PLLCFG2 0x16 +#define STA529_PLLCFG3 0x17 +#define STA529_PLLPFE 0x18 +#define STA529_PLLST 0x19 +#define STA529_ADCCFG 0x1E /*mic_select*/ +#define STA529_CKOCFG 0x1F +#define STA529_MISC 0x20 +#define STA529_PADST0 0x21 +#define STA529_PADST1 0x22 +#define STA529_FFXST 0x23 +#define STA529_PWMIN1 0x2D +#define STA529_PWMIN2 0x2E +#define STA529_POWST 0x32 + +#define STA529_MAX_REGISTER 0x32 + +#define STA529_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) + +#define STA529_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define S2PC_VALUE 0x98 +#define CLOCK_OUT 0x60 +#define LEFT_J_DATA_FORMAT 0x10 +#define I2S_DATA_FORMAT 0x12 +#define RIGHT_J_DATA_FORMAT 0x14 +#define CODEC_MUTE_VAL 0x80 + +#define POWER_CNTLMSAK 0x40 +#define POWER_STDBY 0x40 +#define FFX_MASK 0x80 +#define FFX_OFF 0x80 +#define POWER_UP 0x00 +#define FFX_CLK_ENB 0x01 +#define FFX_CLK_DIS 0x00 +#define FFX_CLK_MSK 0x01 +#define PLAY_FREQ_RANGE_MSK 0x70 +#define CAP_FREQ_RANGE_MSK 0x0C +#define PDATA_LEN_MSK 0xC0 +#define BCLK_TO_FS_MSK 0x30 +#define AUDIO_MUTE_MSK 0x80 + +static const struct reg_default sta529_reg_defaults[] = { + { 0, 0x35 }, /* R0 - FFX Configuration reg 0 */ + { 1, 0xc8 }, /* R1 - FFX Configuration reg 1 */ + { 2, 0x50 }, /* R2 - Master Volume */ + { 3, 0x00 }, /* R3 - Left Volume */ + { 4, 0x00 }, /* R4 - Right Volume */ + { 10, 0xb2 }, /* R10 - S2P Config Reg 0 */ + { 11, 0x41 }, /* R11 - S2P Config Reg 1 */ + { 12, 0x92 }, /* R12 - P2S Config Reg 0 */ + { 13, 0x41 }, /* R13 - P2S Config Reg 1 */ + { 30, 0xd2 }, /* R30 - ADC Config Reg */ + { 31, 0x40 }, /* R31 - clock Out Reg */ + { 32, 0x21 }, /* R32 - Misc Register */ +}; + +struct sta529 { + struct regmap *regmap; +}; + +static bool sta529_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + + case STA529_FFXCFG0: + case STA529_FFXCFG1: + case STA529_MVOL: + case STA529_LVOL: + case STA529_RVOL: + case STA529_S2PCFG0: + case STA529_S2PCFG1: + case STA529_P2SCFG0: + case STA529_P2SCFG1: + case STA529_ADCCFG: + case STA529_CKOCFG: + case STA529_MISC: + return true; + default: + return false; + } +} + + +static const char *pwm_mode_text[] = { "Binary", "Headphone", "Ternary", + "Phase-shift"}; + +static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -9150, 50, 0); +static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12750, 50, 0); +static const SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text); + +static const struct snd_kcontrol_new sta529_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume", STA529_LVOL, STA529_RVOL, 0, + 127, 0, out_gain_tlv), + SOC_SINGLE_TLV("Master Playback Volume", STA529_MVOL, 0, 127, 1, + master_vol_tlv), + SOC_ENUM("PWM Select", pwm_src), +}; + +static int sta529_set_bias_level(struct snd_soc_codec *codec, enum + snd_soc_bias_level level) +{ + struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK, + POWER_UP); + snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK, + FFX_CLK_ENB); + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + regcache_sync(sta529->regmap); + snd_soc_update_bits(codec, STA529_FFXCFG0, + POWER_CNTLMSAK, POWER_STDBY); + /* Making FFX output to zero */ + snd_soc_update_bits(codec, STA529_FFXCFG0, FFX_MASK, + FFX_OFF); + snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK, + FFX_CLK_DIS); + break; + case SND_SOC_BIAS_OFF: + break; + } + + /* + * store the label for powers down audio subsystem for suspend.This is + * used by soc core layer + */ + codec->dapm.bias_level = level; + + return 0; + +} + +static int sta529_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; + int pdata, play_freq_val, record_freq_val; + int bclk_to_fs_ratio; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + pdata = 1; + bclk_to_fs_ratio = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + pdata = 2; + bclk_to_fs_ratio = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + pdata = 3; + bclk_to_fs_ratio = 2; + break; + default: + dev_err(codec->dev, "Unsupported format\n"); + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000: + case 11025: + play_freq_val = 0; + record_freq_val = 2; + break; + case 16000: + case 22050: + play_freq_val = 1; + record_freq_val = 0; + break; + + case 32000: + case 44100: + case 48000: + play_freq_val = 2; + record_freq_val = 0; + break; + default: + dev_err(codec->dev, "Unsupported rate\n"); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_update_bits(codec, STA529_S2PCFG1, PDATA_LEN_MSK, + pdata << 6); + snd_soc_update_bits(codec, STA529_S2PCFG1, BCLK_TO_FS_MSK, + bclk_to_fs_ratio << 4); + snd_soc_update_bits(codec, STA529_MISC, PLAY_FREQ_RANGE_MSK, + play_freq_val << 4); + } else { + snd_soc_update_bits(codec, STA529_P2SCFG1, PDATA_LEN_MSK, + pdata << 6); + snd_soc_update_bits(codec, STA529_P2SCFG1, BCLK_TO_FS_MSK, + bclk_to_fs_ratio << 4); + snd_soc_update_bits(codec, STA529_MISC, CAP_FREQ_RANGE_MSK, + record_freq_val << 2); + } + + return 0; +} + +static int sta529_mute(struct snd_soc_dai *dai, int mute) +{ + u8 val = 0; + + if (mute) + val |= CODEC_MUTE_VAL; + + snd_soc_update_bits(dai->codec, STA529_FFXCFG0, AUDIO_MUTE_MSK, val); + + return 0; +} + +static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 mode = 0; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + mode = LEFT_J_DATA_FORMAT; + break; + case SND_SOC_DAIFMT_I2S: + mode = I2S_DATA_FORMAT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode = RIGHT_J_DATA_FORMAT; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, STA529_S2PCFG0, 0x0D, mode); + + return 0; +} + +static const struct snd_soc_dai_ops sta529_dai_ops = { + .hw_params = sta529_hw_params, + .set_fmt = sta529_set_dai_fmt, + .digital_mute = sta529_mute, +}; + +static struct snd_soc_dai_driver sta529_dai = { + .name = "sta529-audio", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA529_RATES, + .formats = STA529_FORMAT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = STA529_RATES, + .formats = STA529_FORMAT, + }, + .ops = &sta529_dai_ops, +}; + +static int sta529_probe(struct snd_soc_codec *codec) +{ + struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = sta529->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); + + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +/* power down chip */ +static int sta529_remove(struct snd_soc_codec *codec) +{ + sta529_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int sta529_suspend(struct snd_soc_codec *codec) +{ + sta529_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int sta529_resume(struct snd_soc_codec *codec) +{ + sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +struct snd_soc_codec_driver sta529_codec_driver = { + .probe = sta529_probe, + .remove = sta529_remove, + .set_bias_level = sta529_set_bias_level, + .suspend = sta529_suspend, + .resume = sta529_resume, + .controls = sta529_snd_controls, + .num_controls = ARRAY_SIZE(sta529_snd_controls), +}; + +static const struct regmap_config sta529_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = STA529_MAX_REGISTER, + .readable_reg = sta529_readable, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = sta529_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults), +}; + +static __devinit int sta529_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct sta529 *sta529; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL); + if (sta529 == NULL) { + dev_err(&i2c->dev, "Can not allocate memory\n"); + return -ENOMEM; + } + + sta529->regmap = devm_regmap_init_i2c(i2c, &sta529_regmap); + if (IS_ERR(sta529->regmap)) { + ret = PTR_ERR(sta529->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, sta529); + + ret = snd_soc_register_codec(&i2c->dev, + &sta529_codec_driver, &sta529_dai, 1); + if (ret != 0) + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + + return ret; +} + +static int __devexit sta529_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + + return 0; +} + +static const struct i2c_device_id sta529_i2c_id[] = { + { "sta529", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); + +static struct i2c_driver sta529_i2c_driver = { + .driver = { + .name = "sta529", + .owner = THIS_MODULE, + }, + .probe = sta529_i2c_probe, + .remove = __devexit_p(sta529_i2c_remove), + .id_table = sta529_i2c_id, +}; + +module_i2c_driver(sta529_i2c_driver); + +MODULE_DESCRIPTION("ASoC STA529 codec driver"); +MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index e9b62b5ea637..dc78f5a4bcbf 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -118,7 +118,9 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { 0x00, 0x00, 0x00, 0x00, /* 88 */ 0x00, 0x00, 0x00, 0x00, /* 92 */ 0x00, 0x00, 0x00, 0x00, /* 96 */ - 0x00, 0x00, 0x02, /* 100 */ + 0x00, 0x00, 0x02, 0x00, /* 100 */ + 0x00, 0x00, 0x00, 0x00, /* 104 */ + 0x00, 0x00, /* 108 */ }; #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ @@ -229,6 +231,25 @@ static const struct soc_enum aic3x_enum[] = { SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf), }; +static const char *aic3x_agc_level[] = + { "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" }; +static const struct soc_enum aic3x_agc_level_enum[] = { + SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level), + SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level), +}; + +static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" }; +static const struct soc_enum aic3x_agc_attack_enum[] = { + SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack), + SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack), +}; + +static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" }; +static const struct soc_enum aic3x_agc_decay_enum[] = { + SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay), + SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay), +}; + /* * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps */ @@ -353,6 +374,15 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { * adjust PGA to max value when ADC is on and will never go back. */ SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0), + SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]), + SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]), + SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]), + SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]), + SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]), + SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]), + + /* De-emphasis */ + SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0), /* Input */ SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL, @@ -368,7 +398,7 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0); static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl = - SOC_DOUBLE_TLV("Class-D Amplifier Gain", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv); + SOC_DOUBLE_TLV("Class-D Playback Volume", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv); /* Left DAC Mux */ static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = @@ -970,6 +1000,12 @@ static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai, struct snd_soc_codec *codec = codec_dai->codec; struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + /* set clock on MCLK or GPIO2 or BCLK */ + snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK, + clk_id << PLLCLK_IN_SHIFT); + snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK, + clk_id << CLKDIV_IN_SHIFT); + aic3x->sysclk = freq; return 0; } diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index 08c7f6685ff0..6db3c41b0163 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -13,7 +13,7 @@ #define _AIC3X_H /* AIC3X register space */ -#define AIC3X_CACHEREGNUM 103 +#define AIC3X_CACHEREGNUM 110 /* Page select register */ #define AIC3X_PAGE_SELECT 0 @@ -74,6 +74,8 @@ #define HPLCOM_CFG 37 /* Right High Power Output control registers */ #define HPRCOM_CFG 38 +/* High Power Output Stage Control Register */ +#define HPOUT_SC 40 /* DAC Output Switching control registers */ #define DAC_LINE_MUX 41 /* High Power Output Driver Pop Reduction registers */ @@ -148,6 +150,17 @@ #define AIC3X_GPIOB_REG 101 /* Clock generation control register */ #define AIC3X_CLKGEN_CTRL_REG 102 +/* New AGC registers */ +#define LAGCN_ATTACK 103 +#define LAGCN_DECAY 104 +#define RAGCN_ATTACK 105 +#define RAGCN_DECAY 106 +/* New Programmable ADC Digital Path and I2C Bus Condition Register */ +#define NEW_ADC_DIGITALPATH 107 +/* Passive Analog Signal Bypass Selection During Powerdown Register */ +#define PASSIVE_BYPASS 108 +/* DAC Quiescent Current Adjustment Register */ +#define DAC_ICC_ADJ 109 /* Page select register bits */ #define PAGE0_SELECT 0 @@ -163,6 +176,10 @@ #define DUAL_RATE_MODE ((1 << 5) | (1 << 6)) #define LDAC2LCH (0x1 << 3) #define RDAC2RCH (0x1 << 1) +#define LDAC2RCH (0x2 << 3) +#define RDAC2LCH (0x2 << 1) +#define LDAC2MONOMIX (0x3 << 3) +#define RDAC2MONOMIX (0x3 << 1) /* PLL registers bitfields */ #define PLLP_SHIFT 0 @@ -179,6 +196,14 @@ #define PLL_CLKIN_SHIFT 4 #define MCLK_SOURCE 0x0 #define PLL_CLKDIV_SHIFT 0 +#define PLLCLK_IN_MASK 0x30 +#define PLLCLK_IN_SHIFT 4 +#define CLKDIV_IN_MASK 0xc0 +#define CLKDIV_IN_SHIFT 6 +/* clock in source */ +#define CLKIN_MCLK 0 +#define CLKIN_GPIO2 1 +#define CLKIN_BCLK 2 /* Software reset register bits */ #define SOFT_RESET 0x80 diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index a36e9fcdf184..c084c549942e 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -553,7 +553,7 @@ static const struct snd_kcontrol_new vibrar_mux_controls = /* Headset power mode */ static const char *twl6040_power_mode_texts[] = { - "Low-Power", "High-Perfomance", + "Low-Power", "High-Performance", }; static const struct soc_enum twl6040_power_mode_enum = @@ -653,7 +653,7 @@ int twl6040_get_hs_step_size(struct snd_soc_codec *codec) { struct twl6040 *twl6040 = codec->control_data; - if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_2) + if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3) /* For ES under ES_1.3 HS step is 2 mV */ return 2; else diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index e0b51e9f8b12..951d7b49476a 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -121,20 +121,23 @@ static const struct snd_soc_dai_ops wm1250_ev1_ops = { .hw_params = wm1250_ev1_hw_params, }; +#define WM1250_EV1_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000) + static struct snd_soc_dai_driver wm1250_ev1_dai = { .name = "wm1250-ev1", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000, + .rates = WM1250_EV1_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000, + .rates = WM1250_EV1_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &wm1250_ev1_ops, diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 0418fa11e6bd..3fd5b29dc933 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -1,7 +1,7 @@ /* * wm2000.c -- WM2000 ALSA Soc Audio driver * - * Copyright 2008-2010 Wolfson Microelectronics PLC. + * Copyright 2008-2011 Wolfson Microelectronics PLC. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -674,9 +674,39 @@ static int wm2000_resume(struct snd_soc_codec *codec) #define wm2000_resume NULL #endif +static bool wm2000_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM2000_REG_SYS_START: + case WM2000_REG_SPEECH_CLARITY: + case WM2000_REG_SYS_WATCHDOG: + case WM2000_REG_ANA_VMID_PD_TIME: + case WM2000_REG_ANA_VMID_PU_TIME: + case WM2000_REG_CAT_FLTR_INDX: + case WM2000_REG_CAT_GAIN_0: + case WM2000_REG_SYS_STATUS: + case WM2000_REG_SYS_MODE_CNTRL: + case WM2000_REG_SYS_START0: + case WM2000_REG_SYS_START1: + case WM2000_REG_ID1: + case WM2000_REG_ID2: + case WM2000_REG_REVISON: + case WM2000_REG_SYS_CTL1: + case WM2000_REG_SYS_CTL2: + case WM2000_REG_ANC_STAT: + case WM2000_REG_IF_CTL: + return true; + default: + return false; + } +} + static const struct regmap_config wm2000_regmap = { .reg_bits = 8, .val_bits = 8, + + .max_register = WM2000_REG_IF_CTL, + .readable_reg = wm2000_readable_reg, }; static int wm2000_probe(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c index e167207a19cc..e239f4bf2460 100644 --- a/sound/soc/codecs/wm5100-tables.c +++ b/sound/soc/codecs/wm5100-tables.c @@ -1,7 +1,7 @@ /* * wm5100-tables.c -- WM5100 ALSA SoC Audio driver data * - * Copyright 2011 Wolfson Microelectronics plc + * Copyright 2011-2 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index cb6d5372103a..f4817292ef45 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1,7 +1,7 @@ /* * wm5100.c -- WM5100 ALSA SoC Audio driver * - * Copyright 2011 Wolfson Microelectronics plc + * Copyright 2011-2 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -2378,13 +2378,6 @@ static int wm5100_remove(struct snd_soc_codec *codec) return 0; } -static int wm5100_soc_volatile(struct snd_soc_codec *codec, - unsigned int reg) -{ - return true; -} - - static struct snd_soc_codec_driver soc_codec_dev_wm5100 = { .probe = wm5100_probe, .remove = wm5100_remove, @@ -2392,8 +2385,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = { .set_sysclk = wm5100_set_sysclk, .set_pll = wm5100_set_fll, .idle_bias_off = 1, - .reg_cache_size = WM5100_MAX_REGISTER, - .volatile_register = wm5100_soc_volatile, .seq_notifier = wm5100_seq_notifier, .controls = wm5100_snd_controls, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c new file mode 100644 index 000000000000..6537f16d383e --- /dev/null +++ b/sound/soc/codecs/wm5102.c @@ -0,0 +1,903 @@ +/* + * wm5102.c -- WM5102 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" +#include "wm5102.h" + +struct wm5102_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new wm5102_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1R_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2R_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3R_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5, + ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1_OSR_SHIFT, 1, 0), +SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUT2_OSR_SHIFT, 1, 0), +SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3_OSR_SHIFT, 1, 0), +SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("OUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("OUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUTPUT_PATH_CONFIG_1R, + ARIZONA_OUT1L_PGA_VOL_SHIFT, + 0x34, 0x40, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUTPUT_PATH_CONFIG_2R, + ARIZONA_OUT2L_PGA_VOL_SHIFT, + 0x34, 0x40, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), +ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"), +ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "IN3L", "IN3L PGA" }, \ + { name, "IN3R", "IN3R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" } + +static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "Mic Mute Mixer", NULL, "Noise Mixer" }, + { "Mic Mute Mixer", NULL, "Mic Mixer" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"), + ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"), + + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "EPOUTN", NULL, "OUT3L" }, + { "EPOUTP", NULL, "OUT3L" }, + + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, +}; + +static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM5102_FLL1: + return arizona_set_fll(&wm5102->fll[0], source, Fref, Fout); + case WM5102_FLL2: + return arizona_set_fll(&wm5102->fll[1], source, Fref, Fout); + default: + return -EINVAL; + } +} + +#define WM5102_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM5102_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_driver wm5102_dai[] = { + { + .name = "wm5102-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm5102-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm5102-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, +}; + +static int wm5102_codec_probe(struct snd_soc_codec *codec) +{ + struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + + codec->control_data = priv->core.arizona->regmap; + return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP); +} + +#define WM5102_DIG_VU 0x0200 + +static unsigned int wm5102_digital_vu[] = { + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_ADC_DIGITAL_VOLUME_3R, + + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_3R, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { + .probe = wm5102_codec_probe, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm5102_set_fll, + + .controls = wm5102_snd_controls, + .num_controls = ARRAY_SIZE(wm5102_snd_controls), + .dapm_widgets = wm5102_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets), + .dapm_routes = wm5102_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes), +}; + +static int __devinit wm5102_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm5102_priv *wm5102; + int i; + + wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv), + GFP_KERNEL); + if (wm5102 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm5102); + + wm5102->core.arizona = arizona; + + for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++) + wm5102->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm5102->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm5102->fll[1]); + + for (i = 0; i < ARRAY_SIZE(wm5102_dai); i++) + arizona_init_dai(&wm5102->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm5102_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm5102_digital_vu[i], + WM5102_DIG_VU, WM5102_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102, + wm5102_dai, ARRAY_SIZE(wm5102_dai)); +} + +static int __devexit wm5102_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm5102_codec_driver = { + .driver = { + .name = "wm5102-codec", + .owner = THIS_MODULE, + }, + .probe = wm5102_probe, + .remove = __devexit_p(wm5102_remove), +}; + +module_platform_driver(wm5102_codec_driver); + +MODULE_DESCRIPTION("ASoC WM5102 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm5102-codec"); diff --git a/sound/soc/codecs/wm5102.h b/sound/soc/codecs/wm5102.h new file mode 100644 index 000000000000..d30477f3070c --- /dev/null +++ b/sound/soc/codecs/wm5102.h @@ -0,0 +1,21 @@ +/* + * wm5102.h -- WM5102 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM5102_H +#define _WM5102_H + +#include "arizona.h" + +#define WM5102_FLL1 1 +#define WM5102_FLL2 2 + +#endif diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c new file mode 100644 index 000000000000..8033f7065189 --- /dev/null +++ b/sound/soc/codecs/wm5110.c @@ -0,0 +1,950 @@ +/* + * wm5110.c -- WM5110 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" +#include "wm5110.h" + +struct wm5110_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new wm5110_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, + ARIZONA_IN4_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1R_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2R_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3R_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5, + ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1_OSR_SHIFT, 1, 0), +SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUT2_OSR_SHIFT, 1, 0), +SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3_OSR_SHIFT, 1, 0), +SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_6L, + ARIZONA_OUT6_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("OUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_6L, + ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("OUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_6L, + ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUTPUT_PATH_CONFIG_1R, + ARIZONA_OUT1L_PGA_VOL_SHIFT, + 0x34, 0x40, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUTPUT_PATH_CONFIG_2R, + ARIZONA_OUT2L_PGA_VOL_SHIFT, + 0x34, 0x40, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, + ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT2L, ARIZONA_OUT6LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT2R, ARIZONA_OUT6RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_INPUT("IN4L"), +SND_SOC_DAPM_INPUT("IN4R"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT6L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT6R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), +ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"), +ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), +ARIZONA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"), +ARIZONA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPKDAT2L"), +SND_SOC_DAPM_OUTPUT("SPKDAT2R"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "IN3L", "IN3L PGA" }, \ + { name, "IN3R", "IN3R PGA" }, \ + { name, "IN4L", "IN4L PGA" }, \ + { name, "IN4R", "IN4R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" } + +static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + { "OUT6L", NULL, "SYSCLK" }, + { "OUT6R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "Mic Mute Mixer", NULL, "Noise Mixer" }, + { "Mic Mute Mixer", NULL, "Mic Mixer" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + ARIZONA_MIXER_ROUTES("OUT6L", "SPKDAT2L"), + ARIZONA_MIXER_ROUTES("OUT6R", "SPKDAT2R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"), + ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"), + + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "EPOUTN", NULL, "OUT3L" }, + { "EPOUTP", NULL, "OUT3L" }, + + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "SPKDAT2L", NULL, "OUT6L" }, + { "SPKDAT2R", NULL, "OUT6R" }, +}; + +static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM5110_FLL1: + return arizona_set_fll(&wm5110->fll[0], source, Fref, Fout); + case WM5110_FLL2: + return arizona_set_fll(&wm5110->fll[1], source, Fref, Fout); + default: + return -EINVAL; + } +} + +#define WM5110_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM5110_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_driver wm5110_dai[] = { + { + .name = "wm5110-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm5110-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm5110-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, +}; + +static int wm5110_codec_probe(struct snd_soc_codec *codec) +{ + struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); + + codec->control_data = priv->core.arizona->regmap; + return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP); +} + +#define WM5110_DIG_VU 0x0200 + +static unsigned int wm5110_digital_vu[] = { + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_ADC_DIGITAL_VOLUME_3R, + + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_3R, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { + .probe = wm5110_codec_probe, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm5110_set_fll, + + .controls = wm5110_snd_controls, + .num_controls = ARRAY_SIZE(wm5110_snd_controls), + .dapm_widgets = wm5110_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets), + .dapm_routes = wm5110_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes), +}; + +static int __devinit wm5110_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm5110_priv *wm5110; + int i; + + wm5110 = devm_kzalloc(&pdev->dev, sizeof(struct wm5110_priv), + GFP_KERNEL); + if (wm5110 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm5110); + + wm5110->core.arizona = arizona; + + for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++) + wm5110->fll[i].vco_mult = 3; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm5110->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm5110->fll[1]); + + for (i = 0; i < ARRAY_SIZE(wm5110_dai); i++) + arizona_init_dai(&wm5110->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm5110_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm5110_digital_vu[i], + WM5110_DIG_VU, WM5110_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110, + wm5110_dai, ARRAY_SIZE(wm5110_dai)); +} + +static int __devexit wm5110_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm5110_codec_driver = { + .driver = { + .name = "wm5110-codec", + .owner = THIS_MODULE, + }, + .probe = wm5110_probe, + .remove = __devexit_p(wm5110_remove), +}; + +module_platform_driver(wm5110_codec_driver); + +MODULE_DESCRIPTION("ASoC WM5110 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm5110-codec"); diff --git a/sound/soc/codecs/wm5110.h b/sound/soc/codecs/wm5110.h new file mode 100644 index 000000000000..75e9351ccab0 --- /dev/null +++ b/sound/soc/codecs/wm5110.h @@ -0,0 +1,21 @@ +/* + * wm5110.h -- WM5110 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM5110_H +#define _WM5110_H + +#include "arizona.h" + +#define WM5110_FLL1 1 +#define WM5110_FLL2 2 + +#endif diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 555ee146ae0d..d26c8ae4e6d9 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1,7 +1,7 @@ /* * wm8350.c -- WM8350 ALSA SoC audio driver * - * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * Copyright (C) 2007-12 Wolfson Microelectronics PLC. * * Author: Liam Girdwood <lrg@slimlogic.co.uk> * @@ -71,20 +71,6 @@ struct wm8350_data { int fll_freq_in; }; -static unsigned int wm8350_codec_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm8350 *wm8350 = codec->control_data; - return wm8350_reg_read(wm8350, reg); -} - -static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct wm8350 *wm8350 = codec->control_data; - return wm8350_reg_write(wm8350, reg, value); -} - /* * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown. */ @@ -1519,7 +1505,9 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; - codec->control_data = wm8350; + codec->control_data = wm8350->regmap; + + snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); @@ -1629,8 +1617,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8350 = { .remove = wm8350_codec_remove, .suspend = wm8350_suspend, .resume = wm8350_resume, - .read = wm8350_codec_read, - .write = wm8350_codec_write, .set_bias_level = wm8350_set_bias_level, .controls = wm8350_snd_controls, diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 5dc31ebcd0e7..5d277a915f81 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1,7 +1,7 @@ /* * wm8400.c -- WM8400 ALSA Soc Audio driver * - * Copyright 2008, 2009 Wolfson Microelectronics PLC. + * Copyright 2008-11 Wolfson Microelectronics PLC. * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 211285164d70..7c68226376e4 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1,7 +1,7 @@ /* * wm8580.c -- WM8580 ALSA Soc Audio driver * - * Copyright 2008, 2009 Wolfson Microelectronics PLC. + * Copyright 2008-11 Wolfson Microelectronics PLC. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9d1b9b0271f1..bb1d26919b10 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -2,6 +2,7 @@ * wm8731.c -- WM8731 ALSA SoC Audio driver * * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc * * Author: Richard Purdie <richard@openedhand.com> * diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 6e849cb04243..35f3d23200e0 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -1,7 +1,7 @@ /* * wm8741.c -- WM8741 ALSA SoC Audio driver * - * Copyright 2010 Wolfson Microelectronics plc + * Copyright 2010-1 Wolfson Microelectronics plc * * Author: Ian Lartey <ian@opensource.wolfsonmicro.com> * diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index a26482cd7654..13bff87ddcf5 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1,7 +1,7 @@ /* * wm8753.c -- WM8753 ALSA Soc Audio driver * - * Copyright 2003 Wolfson Microelectronics PLC. + * Copyright 2003-11 Wolfson Microelectronics PLC. * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index a19db5a0a17a..879c356a9045 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -1,7 +1,7 @@ /* * wm8776.c -- WM8776 ALSA SoC Audio driver * - * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2009-12 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 6bd1b767b138..c088020172ab 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -1,7 +1,7 @@ /* * wm8804.c -- WM8804 S/PDIF transceiver driver * - * Copyright 2010 Wolfson Microelectronics plc + * Copyright 2010-11 Wolfson Microelectronics plc * * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> * diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 86b8a2926591..73f1c8d7bafb 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1,8 +1,8 @@ /* * wm8903.c -- WM8903 ALSA SoC Audio driver * - * Copyright 2008 Wolfson Microelectronics - * Copyright 2011 NVIDIA, Inc. + * Copyright 2008-12 Wolfson Microelectronics + * Copyright 2011-2012 NVIDIA, Inc. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -116,6 +116,7 @@ static const struct reg_default wm8903_reg_defaults[] = { struct wm8903_priv { struct wm8903_platform_data *pdata; + struct device *dev; struct snd_soc_codec *codec; struct regmap *regmap; @@ -1635,17 +1636,27 @@ EXPORT_SYMBOL_GPL(wm8903_mic_detect); static irqreturn_t wm8903_irq(int irq, void *data) { - struct snd_soc_codec *codec = data; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - int mic_report; - int int_pol; - int int_val = 0; - int mask = ~snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1_MASK); + struct wm8903_priv *wm8903 = data; + int mic_report, ret; + unsigned int int_val, mask, int_pol; - int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask; + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1_MASK, + &mask); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read IRQ mask: %d\n", ret); + return IRQ_NONE; + } + + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1, &int_val); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read IRQ status: %d\n", ret); + return IRQ_NONE; + } + + int_val &= ~mask; if (int_val & WM8903_WSEQ_BUSY_EINT) { - dev_warn(codec->dev, "Write sequencer done\n"); + dev_warn(wm8903->dev, "Write sequencer done\n"); } /* @@ -1656,22 +1667,28 @@ static irqreturn_t wm8903_irq(int irq, void *data) * the polarity register. */ mic_report = wm8903->mic_last_report; - int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1); + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1, + &int_pol); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read interrupt polarity: %d\n", + ret); + return IRQ_HANDLED; + } #ifndef CONFIG_SND_SOC_WM8903_MODULE if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT)) - trace_snd_soc_jack_irq(dev_name(codec->dev)); + trace_snd_soc_jack_irq(dev_name(wm8903->dev)); #endif if (int_val & WM8903_MICSHRT_EINT) { - dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol); + dev_dbg(wm8903->dev, "Microphone short (pol=%x)\n", int_pol); mic_report ^= wm8903->mic_short; int_pol ^= WM8903_MICSHRT_INV; } if (int_val & WM8903_MICDET_EINT) { - dev_dbg(codec->dev, "Microphone detect (pol=%x)\n", int_pol); + dev_dbg(wm8903->dev, "Microphone detect (pol=%x)\n", int_pol); mic_report ^= wm8903->mic_det; int_pol ^= WM8903_MICDET_INV; @@ -1679,8 +1696,8 @@ static irqreturn_t wm8903_irq(int irq, void *data) msleep(wm8903->mic_delay); } - snd_soc_update_bits(codec, WM8903_INTERRUPT_POLARITY_1, - WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol); + regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1, + WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol); snd_soc_jack_report(wm8903->mic_jack, mic_report, wm8903->mic_short | wm8903->mic_det); @@ -1774,7 +1791,6 @@ static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) 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; int ret; @@ -1782,8 +1798,8 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | WM8903_GP1_DIR; - ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - mask, val); + ret = regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, mask, val); if (ret < 0) return ret; @@ -1793,10 +1809,9 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) 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; + unsigned int reg; - reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, ®); return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; } @@ -1805,7 +1820,6 @@ 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; int ret; @@ -1813,8 +1827,8 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | (value << WM8903_GP2_LVL_SHIFT); - ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - mask, val); + ret = regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, mask, val); if (ret < 0) return ret; @@ -1824,11 +1838,10 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, 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); + regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); } static struct gpio_chip wm8903_template_chip = { @@ -1842,15 +1855,14 @@ static struct gpio_chip wm8903_template_chip = { .can_sleep = 1, }; -static void wm8903_init_gpio(struct snd_soc_codec *codec) +static void wm8903_init_gpio(struct wm8903_priv *wm8903) { - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); struct wm8903_platform_data *pdata = wm8903->pdata; int ret; wm8903->gpio_chip = wm8903_template_chip; wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; - wm8903->gpio_chip.dev = codec->dev; + wm8903->gpio_chip.dev = wm8903->dev; if (pdata->gpio_base) wm8903->gpio_chip.base = pdata->gpio_base; @@ -1859,24 +1871,23 @@ static void wm8903_init_gpio(struct snd_soc_codec *codec) ret = gpiochip_add(&wm8903->gpio_chip); if (ret != 0) - dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); + dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret); } -static void wm8903_free_gpio(struct snd_soc_codec *codec) +static void wm8903_free_gpio(struct wm8903_priv *wm8903) { - 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); + dev_err(wm8903->dev, "Failed to remove GPIOs: %d\n", ret); } #else -static void wm8903_init_gpio(struct snd_soc_codec *codec) +static void wm8903_init_gpio(struct wm8903_priv *wm8903) { } -static void wm8903_free_gpio(struct snd_soc_codec *codec) +static void wm8903_free_gpio(struct wm8903_priv *wm8903) { } #endif @@ -1884,11 +1895,7 @@ static void wm8903_free_gpio(struct snd_soc_codec *codec) static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - struct wm8903_platform_data *pdata = wm8903->pdata; - int ret, i; - int trigger, irq_pol; - u16 val; - bool mic_gpio = false; + int ret; wm8903->codec = codec; codec->control_data = wm8903->regmap; @@ -1899,121 +1906,16 @@ static int wm8903_probe(struct snd_soc_codec *codec) return ret; } - /* Set up GPIOs, detect if any are MIC detect outputs */ - for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if ((!pdata->gpio_cfg[i]) || - (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO)) - continue; - - snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, - pdata->gpio_cfg[i] & 0x7fff); - - 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; - } - } - - /* Set up microphone detection */ - snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, - pdata->micdet_cfg); - - /* Microphone detection needs the WSEQ clock */ - if (pdata->micdet_cfg) - 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; - - if (wm8903->irq) { - if (pdata->irq_active_low) { - trigger = IRQF_TRIGGER_LOW; - irq_pol = WM8903_IRQ_POL; - } else { - trigger = IRQF_TRIGGER_HIGH; - irq_pol = 0; - } - - snd_soc_update_bits(codec, WM8903_INTERRUPT_CONTROL, - WM8903_IRQ_POL, irq_pol); - - ret = request_threaded_irq(wm8903->irq, NULL, wm8903_irq, - trigger | IRQF_ONESHOT, - "wm8903", codec); - if (ret != 0) { - dev_err(codec->dev, "Failed to request IRQ: %d\n", - ret); - return ret; - } - - /* Enable write sequencer interrupts */ - snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK, - WM8903_IM_WSEQ_BUSY_EINT, 0); - } - /* power on device */ wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Latch volume update bits */ - val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT); - val |= WM8903_ADCVU; - snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val); - snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val); - - val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT); - val |= WM8903_DACVU; - snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val); - snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val); - - val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT); - val |= WM8903_HPOUTVU; - snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val); - snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val); - - val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT); - val |= WM8903_LINEOUTVU; - snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val); - snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val); - - val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT); - val |= WM8903_SPKVU; - snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val); - snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); - - /* Enable DAC soft mute by default */ - snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, - WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, - WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); - - wm8903_init_gpio(codec); - return ret; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); - if (wm8903->irq) - free_irq(wm8903->irq, codec); return 0; } @@ -2123,15 +2025,18 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, { struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev); struct wm8903_priv *wm8903; - unsigned int val; - int ret; + int trigger; + bool mic_gpio = false; + unsigned int val, irq_pol; + int ret, i; wm8903 = devm_kzalloc(&i2c->dev, sizeof(struct wm8903_priv), GFP_KERNEL); if (wm8903 == NULL) return -ENOMEM; + wm8903->dev = &i2c->dev; - wm8903->regmap = regmap_init_i2c(i2c, &wm8903_regmap); + wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap); if (IS_ERR(wm8903->regmap)) { ret = PTR_ERR(wm8903->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", @@ -2140,7 +2045,6 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, } i2c_set_clientdata(i2c, wm8903); - wm8903->irq = i2c->irq; /* If no platform data was supplied, create storage for defaults */ if (pdata) { @@ -2167,6 +2071,8 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, } } + pdata = wm8903->pdata; + ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val); if (ret != 0) { dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); @@ -2189,6 +2095,107 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, /* Reset the device */ regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903); + wm8903_init_gpio(wm8903); + + /* Set up GPIO pin state, detect if any are MIC detect outputs */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { + if ((!pdata->gpio_cfg[i]) || + (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO)) + continue; + + regmap_write(wm8903->regmap, WM8903_GPIO_CONTROL_1 + i, + pdata->gpio_cfg[i] & 0x7fff); + + 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; + } + } + + /* Set up microphone detection */ + regmap_write(wm8903->regmap, WM8903_MIC_BIAS_CONTROL_0, + pdata->micdet_cfg); + + /* Microphone detection needs the WSEQ clock */ + if (pdata->micdet_cfg) + regmap_update_bits(wm8903->regmap, 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; + + if (i2c->irq) { + if (pdata->irq_active_low) { + trigger = IRQF_TRIGGER_LOW; + irq_pol = WM8903_IRQ_POL; + } else { + trigger = IRQF_TRIGGER_HIGH; + irq_pol = 0; + } + + regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_CONTROL, + WM8903_IRQ_POL, irq_pol); + + ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq, + trigger | IRQF_ONESHOT, + "wm8903", wm8903); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to request IRQ: %d\n", + ret); + return ret; + } + + /* Enable write sequencer interrupts */ + regmap_update_bits(wm8903->regmap, + WM8903_INTERRUPT_STATUS_1_MASK, + WM8903_IM_WSEQ_BUSY_EINT, 0); + } + + /* Latch volume update bits */ + regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_LEFT, + WM8903_ADCVU, WM8903_ADCVU); + regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_RIGHT, + WM8903_ADCVU, WM8903_ADCVU); + + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_LEFT, + WM8903_DACVU, WM8903_DACVU); + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_RIGHT, + WM8903_DACVU, WM8903_DACVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_LEFT, + WM8903_HPOUTVU, WM8903_HPOUTVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_RIGHT, + WM8903_HPOUTVU, WM8903_HPOUTVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_LEFT, + WM8903_LINEOUTVU, WM8903_LINEOUTVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_RIGHT, + WM8903_LINEOUTVU, WM8903_LINEOUTVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_LEFT, + WM8903_SPKVU, WM8903_SPKVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_RIGHT, + WM8903_SPKVU, WM8903_SPKVU); + + /* Enable DAC soft mute by default */ + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_1, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8903, &wm8903_dai, 1); if (ret != 0) @@ -2196,7 +2203,6 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, return 0; err: - regmap_exit(wm8903->regmap); return ret; } @@ -2204,7 +2210,9 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client) { struct wm8903_priv *wm8903 = i2c_get_clientdata(client); - regmap_exit(wm8903->regmap); + if (client->irq) + free_irq(client->irq, wm8903); + wm8903_free_gpio(wm8903); snd_soc_unregister_codec(&client->dev); return 0; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 812acd83fb48..0013afe48e66 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1,7 +1,7 @@ /* * wm8904.c -- WM8904 ALSA SoC Audio driver * - * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2009-12 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -314,11 +314,6 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg) } } -static int wm8904_reset(struct snd_soc_codec *codec) -{ - return snd_soc_write(codec, WM8904_SW_RESET_AND_ID, 0); -} - static int wm8904_configure_clocking(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); @@ -1945,25 +1940,6 @@ static struct snd_soc_dai_driver wm8904_dai = { .symmetric_rates = 1, }; -#ifdef CONFIG_PM -static int wm8904_suspend(struct snd_soc_codec *codec) -{ - wm8904_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - -static int wm8904_resume(struct snd_soc_codec *codec) -{ - wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - return 0; -} -#else -#define wm8904_suspend NULL -#define wm8904_resume NULL -#endif - static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); @@ -2078,8 +2054,7 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec) 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; - int ret, i; + int ret; codec->control_data = wm8904->regmap; @@ -2101,127 +2076,17 @@ static int wm8904_probe(struct snd_soc_codec *codec) return ret; } - for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++) - wm8904->supplies[i].supply = wm8904_supply_names[i]; - - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8904->supplies), - wm8904->supplies); - if (ret != 0) { - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - return ret; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), - wm8904->supplies); - if (ret != 0) { - dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); - goto err_get; - } - - ret = snd_soc_read(codec, WM8904_SW_RESET_AND_ID); - if (ret < 0) { - dev_err(codec->dev, "Failed to read ID register\n"); - goto err_enable; - } - if (ret != 0x8904) { - dev_err(codec->dev, "Device is not a WM8904, ID is %x\n", ret); - ret = -EINVAL; - goto err_enable; - } - - ret = snd_soc_read(codec, WM8904_REVISION); - if (ret < 0) { - dev_err(codec->dev, "Failed to read device revision: %d\n", - ret); - goto err_enable; - } - dev_info(codec->dev, "revision %c\n", ret + 'A'); - - ret = wm8904_reset(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset\n"); - goto err_enable; - } - - regcache_cache_only(wm8904->regmap, true); - /* Change some default settings - latch VU and enable ZC */ - 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) { - for (i = 0; i < WM8904_GPIO_REGS; i++) { - if (!pdata->gpio_cfg[i]) - continue; - - regmap_update_bits(wm8904->regmap, - WM8904_GPIO_CONTROL_1 + i, - 0xffff, - pdata->gpio_cfg[i]); - } - - /* Zero is the default value for these anyway */ - for (i = 0; i < WM8904_MIC_REGS; i++) - regmap_update_bits(wm8904->regmap, - WM8904_MIC_BIAS_CONTROL_0 + i, - 0xffff, - 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. - */ - snd_soc_update_bits(codec, WM8904_CLASS_W_0, - WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR); - - /* Use normal bias source */ - snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0, - WM8904_POBCTRL, 0); - - wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* Bias level configuration will have done an extra enable */ - regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); - wm8904_handle_pdata(codec); wm8904_add_widgets(codec); return 0; - -err_enable: - regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); -err_get: - regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); - return ret; } static int wm8904_remove(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - wm8904_set_bias_level(codec, SND_SOC_BIAS_OFF); - regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); kfree(wm8904->retune_mobile_texts); kfree(wm8904->drc_texts); @@ -2231,8 +2096,6 @@ static int wm8904_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_wm8904 = { .probe = wm8904_probe, .remove = wm8904_remove, - .suspend = wm8904_suspend, - .resume = wm8904_resume, .set_bias_level = wm8904_set_bias_level, .idle_bias_off = true, }; @@ -2254,14 +2117,15 @@ static __devinit int wm8904_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8904_priv *wm8904; - int ret; + unsigned int val; + int ret, i; wm8904 = devm_kzalloc(&i2c->dev, sizeof(struct wm8904_priv), GFP_KERNEL); if (wm8904 == NULL) return -ENOMEM; - wm8904->regmap = regmap_init_i2c(i2c, &wm8904_regmap); + wm8904->regmap = devm_regmap_init_i2c(i2c, &wm8904_regmap); if (IS_ERR(wm8904->regmap)) { ret = PTR_ERR(wm8904->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", @@ -2273,23 +2137,121 @@ static __devinit int wm8904_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8904); wm8904->pdata = i2c->dev.platform_data; + for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++) + wm8904->supplies[i].supply = wm8904_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(wm8904->regmap, WM8904_SW_RESET_AND_ID, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err_enable; + } + if (val != 0x8904) { + dev_err(&i2c->dev, "Device is not a WM8904, ID is %x\n", val); + ret = -EINVAL; + goto err_enable; + } + + ret = regmap_read(wm8904->regmap, WM8904_REVISION, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read device revision: %d\n", + ret); + goto err_enable; + } + dev_info(&i2c->dev, "revision %c\n", val + 'A'); + + ret = regmap_write(wm8904->regmap, WM8904_SW_RESET_AND_ID, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + goto err_enable; + } + + /* Change some default settings - latch VU and enable ZC */ + regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_LEFT, + WM8904_ADC_VU, WM8904_ADC_VU); + regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_RIGHT, + WM8904_ADC_VU, WM8904_ADC_VU); + regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_LEFT, + WM8904_DAC_VU, WM8904_DAC_VU); + regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_RIGHT, + WM8904_DAC_VU, WM8904_DAC_VU); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_LEFT, + WM8904_HPOUT_VU | WM8904_HPOUTLZC, + WM8904_HPOUT_VU | WM8904_HPOUTLZC); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_RIGHT, + WM8904_HPOUT_VU | WM8904_HPOUTRZC, + WM8904_HPOUT_VU | WM8904_HPOUTRZC); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_LEFT, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_RIGHT, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC); + regmap_update_bits(wm8904->regmap, WM8904_CLOCK_RATES_0, + WM8904_SR_MODE, 0); + + /* Apply configuration from the platform data. */ + if (wm8904->pdata) { + for (i = 0; i < WM8904_GPIO_REGS; i++) { + if (!wm8904->pdata->gpio_cfg[i]) + continue; + + regmap_update_bits(wm8904->regmap, + WM8904_GPIO_CONTROL_1 + i, + 0xffff, + wm8904->pdata->gpio_cfg[i]); + } + + /* Zero is the default value for these anyway */ + for (i = 0; i < WM8904_MIC_REGS; i++) + regmap_update_bits(wm8904->regmap, + WM8904_MIC_BIAS_CONTROL_0 + i, + 0xffff, + wm8904->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. + */ + regmap_update_bits(wm8904->regmap, WM8904_CLASS_W_0, + WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR); + + /* Use normal bias source */ + regmap_update_bits(wm8904->regmap, WM8904_BIAS_CONTROL_0, + WM8904_POBCTRL, 0); + + /* Can leave the device powered off until we need it */ + regcache_cache_only(wm8904->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8904, &wm8904_dai, 1); if (ret != 0) - goto err; + return ret; return 0; -err: - regmap_exit(wm8904->regmap); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); return ret; } static __devexit int wm8904_i2c_remove(struct i2c_client *client) { - struct wm8904_priv *wm8904 = i2c_get_clientdata(client); snd_soc_unregister_codec(&client->dev); - regmap_exit(wm8904->regmap); return 0; } @@ -2311,23 +2273,7 @@ static struct i2c_driver wm8904_i2c_driver = { .id_table = wm8904_i2c_id, }; -static int __init wm8904_modinit(void) -{ - int ret = 0; - ret = i2c_add_driver(&wm8904_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register wm8904 I2C driver: %d\n", - ret); - } - return ret; -} -module_init(wm8904_modinit); - -static void __exit wm8904_exit(void) -{ - i2c_del_driver(&wm8904_i2c_driver); -} -module_exit(wm8904_exit); +module_i2c_driver(wm8904_i2c_driver); MODULE_DESCRIPTION("ASoC WM8904 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 8bc659d8dd2e..96518ac8e24c 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1,6 +1,8 @@ /* * wm8960.c -- WM8960 ALSA SoC Audio driver * + * Copyright 2007-11 Wolfson Microelectronics, plc + * * Author: Liam Girdwood * * This program is free software; you can redistribute it and/or modify diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 05ea7c274093..01edbcc754d2 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -1,6 +1,8 @@ /* * wm8961.c -- WM8961 ALSA SoC Audio driver * + * Copyright 2009-10 Wolfson Microelectronics, plc + * * Author: Mark Brown * * This program is free software; you can redistribute it and/or modify diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 0cfce9999c89..eaf65863ec21 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1,7 +1,7 @@ /* * wm8962.c -- WM8962 ALSA SoC Audio driver * - * Copyright 2010 Wolfson Microelectronics plc + * Copyright 2010-2 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -2580,6 +2580,9 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, WM8962_SAMPLE_RATE_INT_MODE | WM8962_SAMPLE_RATE_MASK, adctl3); + dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n", + wm8962->bclk, wm8962->lrclk); + if (codec->dapm.bias_level == SND_SOC_BIAS_ON) wm8962_configure_bclk(codec); @@ -3722,6 +3725,9 @@ static int wm8962_runtime_resume(struct device *dev) } regcache_cache_only(wm8962->regmap, false); + + wm8962_reset(wm8962); + regcache_sync(wm8962->regmap); regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 36acfccab999..9fd80d688979 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1,7 +1,7 @@ /* * wm8993.c -- WM8993 ALSA SoC audio driver * - * Copyright 2009, 2010 Wolfson Microelectronics plc + * Copyright 2009-12 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1436b6ce74d1..bb62f4b3d563 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1,7 +1,7 @@ /* * wm8994.c -- WM8994 ALSA SoC Audio driver * - * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2009-12 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -2967,23 +2967,8 @@ static struct snd_soc_dai_driver wm8994_dai[] = { static int wm8994_codec_suspend(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = wm8994->wm8994; int i, ret; - switch (control->type) { - case WM8994: - snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0); - break; - case WM1811: - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM1811_JACKDET_MODE_MASK, 0); - /* Fall through */ - case WM8958: - snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, - WM8958_MICD_ENA, 0); - break; - } - for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i], sizeof(struct wm8994_fll_config)); @@ -3033,28 +3018,6 @@ static int wm8994_codec_resume(struct snd_soc_codec *codec) i + 1, ret); } - switch (control->type) { - case WM8994: - if (wm8994->micdet[0].jack || wm8994->micdet[1].jack) - snd_soc_update_bits(codec, WM8994_MICBIAS, - WM8994_MICD_ENA, WM8994_MICD_ENA); - break; - case WM1811: - if (wm8994->jackdet && wm8994->jack_cb) { - /* Restart from idle */ - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM1811_JACKDET_MODE_MASK, - WM1811_JACKDET_MODE_JACK); - break; - } - break; - case WM8958: - if (wm8994->jack_cb) - snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, - WM8958_MICD_ENA, WM8958_MICD_ENA); - break; - } - return 0; } #else @@ -3729,9 +3692,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) 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; pm_runtime_enable(codec->dev); pm_runtime_idle(codec->dev); @@ -3870,6 +3830,10 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) dev_warn(codec->dev, "Failed to request Mic detect IRQ: %d\n", ret); + } else { + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET, + wm8958_mic_irq, "Mic detect", + wm8994); } } diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index dc9b42b7fc4d..00f183dfa454 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1,7 +1,7 @@ /* * wm8996.c - WM8996 audio codec interface * - * Copyright 2011 Wolfson Microelectronics PLC. + * Copyright 2011-2 Wolfson Microelectronics PLC. * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify it @@ -296,184 +296,6 @@ static struct reg_default wm8996_reg[] = { { WM8996_RIGHT_PDM_SPEAKER, 0x1 }, { WM8996_PDM_SPEAKER_MUTE_SEQUENCE, 0x69 }, { WM8996_PDM_SPEAKER_VOLUME, 0x66 }, - { WM8996_WRITE_SEQUENCER_0, 0x1 }, - { WM8996_WRITE_SEQUENCER_1, 0x1 }, - { WM8996_WRITE_SEQUENCER_3, 0x6 }, - { WM8996_WRITE_SEQUENCER_4, 0x40 }, - { WM8996_WRITE_SEQUENCER_5, 0x1 }, - { WM8996_WRITE_SEQUENCER_6, 0xf }, - { WM8996_WRITE_SEQUENCER_7, 0x6 }, - { WM8996_WRITE_SEQUENCER_8, 0x1 }, - { WM8996_WRITE_SEQUENCER_9, 0x3 }, - { WM8996_WRITE_SEQUENCER_10, 0x104 }, - { WM8996_WRITE_SEQUENCER_12, 0x60 }, - { WM8996_WRITE_SEQUENCER_13, 0x11 }, - { WM8996_WRITE_SEQUENCER_14, 0x401 }, - { WM8996_WRITE_SEQUENCER_16, 0x50 }, - { WM8996_WRITE_SEQUENCER_17, 0x3 }, - { WM8996_WRITE_SEQUENCER_18, 0x100 }, - { WM8996_WRITE_SEQUENCER_20, 0x51 }, - { WM8996_WRITE_SEQUENCER_21, 0x3 }, - { WM8996_WRITE_SEQUENCER_22, 0x104 }, - { WM8996_WRITE_SEQUENCER_23, 0xa }, - { WM8996_WRITE_SEQUENCER_24, 0x60 }, - { WM8996_WRITE_SEQUENCER_25, 0x3b }, - { WM8996_WRITE_SEQUENCER_26, 0x502 }, - { WM8996_WRITE_SEQUENCER_27, 0x100 }, - { WM8996_WRITE_SEQUENCER_28, 0x2fff }, - { WM8996_WRITE_SEQUENCER_32, 0x2fff }, - { WM8996_WRITE_SEQUENCER_36, 0x2fff }, - { WM8996_WRITE_SEQUENCER_40, 0x2fff }, - { WM8996_WRITE_SEQUENCER_44, 0x2fff }, - { WM8996_WRITE_SEQUENCER_48, 0x2fff }, - { WM8996_WRITE_SEQUENCER_52, 0x2fff }, - { WM8996_WRITE_SEQUENCER_56, 0x2fff }, - { WM8996_WRITE_SEQUENCER_60, 0x2fff }, - { WM8996_WRITE_SEQUENCER_64, 0x1 }, - { WM8996_WRITE_SEQUENCER_65, 0x1 }, - { WM8996_WRITE_SEQUENCER_67, 0x6 }, - { WM8996_WRITE_SEQUENCER_68, 0x40 }, - { WM8996_WRITE_SEQUENCER_69, 0x1 }, - { WM8996_WRITE_SEQUENCER_70, 0xf }, - { WM8996_WRITE_SEQUENCER_71, 0x6 }, - { WM8996_WRITE_SEQUENCER_72, 0x1 }, - { WM8996_WRITE_SEQUENCER_73, 0x3 }, - { WM8996_WRITE_SEQUENCER_74, 0x104 }, - { WM8996_WRITE_SEQUENCER_76, 0x60 }, - { WM8996_WRITE_SEQUENCER_77, 0x11 }, - { WM8996_WRITE_SEQUENCER_78, 0x401 }, - { WM8996_WRITE_SEQUENCER_80, 0x50 }, - { WM8996_WRITE_SEQUENCER_81, 0x3 }, - { WM8996_WRITE_SEQUENCER_82, 0x100 }, - { WM8996_WRITE_SEQUENCER_84, 0x60 }, - { WM8996_WRITE_SEQUENCER_85, 0x3b }, - { WM8996_WRITE_SEQUENCER_86, 0x502 }, - { WM8996_WRITE_SEQUENCER_87, 0x100 }, - { WM8996_WRITE_SEQUENCER_88, 0x2fff }, - { WM8996_WRITE_SEQUENCER_92, 0x2fff }, - { WM8996_WRITE_SEQUENCER_96, 0x2fff }, - { WM8996_WRITE_SEQUENCER_100, 0x2fff }, - { WM8996_WRITE_SEQUENCER_104, 0x2fff }, - { WM8996_WRITE_SEQUENCER_108, 0x2fff }, - { WM8996_WRITE_SEQUENCER_112, 0x2fff }, - { WM8996_WRITE_SEQUENCER_116, 0x2fff }, - { WM8996_WRITE_SEQUENCER_120, 0x2fff }, - { WM8996_WRITE_SEQUENCER_124, 0x2fff }, - { WM8996_WRITE_SEQUENCER_128, 0x1 }, - { WM8996_WRITE_SEQUENCER_129, 0x1 }, - { WM8996_WRITE_SEQUENCER_131, 0x6 }, - { WM8996_WRITE_SEQUENCER_132, 0x40 }, - { WM8996_WRITE_SEQUENCER_133, 0x1 }, - { WM8996_WRITE_SEQUENCER_134, 0xf }, - { WM8996_WRITE_SEQUENCER_135, 0x6 }, - { WM8996_WRITE_SEQUENCER_136, 0x1 }, - { WM8996_WRITE_SEQUENCER_137, 0x3 }, - { WM8996_WRITE_SEQUENCER_138, 0x106 }, - { WM8996_WRITE_SEQUENCER_140, 0x61 }, - { WM8996_WRITE_SEQUENCER_141, 0x11 }, - { WM8996_WRITE_SEQUENCER_142, 0x401 }, - { WM8996_WRITE_SEQUENCER_144, 0x50 }, - { WM8996_WRITE_SEQUENCER_145, 0x3 }, - { WM8996_WRITE_SEQUENCER_146, 0x102 }, - { WM8996_WRITE_SEQUENCER_148, 0x51 }, - { WM8996_WRITE_SEQUENCER_149, 0x3 }, - { WM8996_WRITE_SEQUENCER_150, 0x106 }, - { WM8996_WRITE_SEQUENCER_151, 0xa }, - { WM8996_WRITE_SEQUENCER_152, 0x61 }, - { WM8996_WRITE_SEQUENCER_153, 0x3b }, - { WM8996_WRITE_SEQUENCER_154, 0x502 }, - { WM8996_WRITE_SEQUENCER_155, 0x100 }, - { WM8996_WRITE_SEQUENCER_156, 0x2fff }, - { WM8996_WRITE_SEQUENCER_160, 0x2fff }, - { WM8996_WRITE_SEQUENCER_164, 0x2fff }, - { WM8996_WRITE_SEQUENCER_168, 0x2fff }, - { WM8996_WRITE_SEQUENCER_172, 0x2fff }, - { WM8996_WRITE_SEQUENCER_176, 0x2fff }, - { WM8996_WRITE_SEQUENCER_180, 0x2fff }, - { WM8996_WRITE_SEQUENCER_184, 0x2fff }, - { WM8996_WRITE_SEQUENCER_188, 0x2fff }, - { WM8996_WRITE_SEQUENCER_192, 0x1 }, - { WM8996_WRITE_SEQUENCER_193, 0x1 }, - { WM8996_WRITE_SEQUENCER_195, 0x6 }, - { WM8996_WRITE_SEQUENCER_196, 0x40 }, - { WM8996_WRITE_SEQUENCER_197, 0x1 }, - { WM8996_WRITE_SEQUENCER_198, 0xf }, - { WM8996_WRITE_SEQUENCER_199, 0x6 }, - { WM8996_WRITE_SEQUENCER_200, 0x1 }, - { WM8996_WRITE_SEQUENCER_201, 0x3 }, - { WM8996_WRITE_SEQUENCER_202, 0x106 }, - { WM8996_WRITE_SEQUENCER_204, 0x61 }, - { WM8996_WRITE_SEQUENCER_205, 0x11 }, - { WM8996_WRITE_SEQUENCER_206, 0x401 }, - { WM8996_WRITE_SEQUENCER_208, 0x50 }, - { WM8996_WRITE_SEQUENCER_209, 0x3 }, - { WM8996_WRITE_SEQUENCER_210, 0x102 }, - { WM8996_WRITE_SEQUENCER_212, 0x61 }, - { WM8996_WRITE_SEQUENCER_213, 0x3b }, - { WM8996_WRITE_SEQUENCER_214, 0x502 }, - { WM8996_WRITE_SEQUENCER_215, 0x100 }, - { WM8996_WRITE_SEQUENCER_216, 0x2fff }, - { WM8996_WRITE_SEQUENCER_220, 0x2fff }, - { WM8996_WRITE_SEQUENCER_224, 0x2fff }, - { WM8996_WRITE_SEQUENCER_228, 0x2fff }, - { WM8996_WRITE_SEQUENCER_232, 0x2fff }, - { WM8996_WRITE_SEQUENCER_236, 0x2fff }, - { WM8996_WRITE_SEQUENCER_240, 0x2fff }, - { WM8996_WRITE_SEQUENCER_244, 0x2fff }, - { WM8996_WRITE_SEQUENCER_248, 0x2fff }, - { WM8996_WRITE_SEQUENCER_252, 0x2fff }, - { WM8996_WRITE_SEQUENCER_256, 0x60 }, - { WM8996_WRITE_SEQUENCER_258, 0x601 }, - { WM8996_WRITE_SEQUENCER_260, 0x50 }, - { WM8996_WRITE_SEQUENCER_262, 0x100 }, - { WM8996_WRITE_SEQUENCER_264, 0x1 }, - { WM8996_WRITE_SEQUENCER_266, 0x104 }, - { WM8996_WRITE_SEQUENCER_267, 0x100 }, - { WM8996_WRITE_SEQUENCER_268, 0x2fff }, - { WM8996_WRITE_SEQUENCER_272, 0x2fff }, - { WM8996_WRITE_SEQUENCER_276, 0x2fff }, - { WM8996_WRITE_SEQUENCER_280, 0x2fff }, - { WM8996_WRITE_SEQUENCER_284, 0x2fff }, - { WM8996_WRITE_SEQUENCER_288, 0x2fff }, - { WM8996_WRITE_SEQUENCER_292, 0x2fff }, - { WM8996_WRITE_SEQUENCER_296, 0x2fff }, - { WM8996_WRITE_SEQUENCER_300, 0x2fff }, - { WM8996_WRITE_SEQUENCER_304, 0x2fff }, - { WM8996_WRITE_SEQUENCER_308, 0x2fff }, - { WM8996_WRITE_SEQUENCER_312, 0x2fff }, - { WM8996_WRITE_SEQUENCER_316, 0x2fff }, - { WM8996_WRITE_SEQUENCER_320, 0x61 }, - { WM8996_WRITE_SEQUENCER_322, 0x601 }, - { WM8996_WRITE_SEQUENCER_324, 0x50 }, - { WM8996_WRITE_SEQUENCER_326, 0x102 }, - { WM8996_WRITE_SEQUENCER_328, 0x1 }, - { WM8996_WRITE_SEQUENCER_330, 0x106 }, - { WM8996_WRITE_SEQUENCER_331, 0x100 }, - { WM8996_WRITE_SEQUENCER_332, 0x2fff }, - { WM8996_WRITE_SEQUENCER_336, 0x2fff }, - { WM8996_WRITE_SEQUENCER_340, 0x2fff }, - { WM8996_WRITE_SEQUENCER_344, 0x2fff }, - { WM8996_WRITE_SEQUENCER_348, 0x2fff }, - { WM8996_WRITE_SEQUENCER_352, 0x2fff }, - { WM8996_WRITE_SEQUENCER_356, 0x2fff }, - { WM8996_WRITE_SEQUENCER_360, 0x2fff }, - { WM8996_WRITE_SEQUENCER_364, 0x2fff }, - { WM8996_WRITE_SEQUENCER_368, 0x2fff }, - { WM8996_WRITE_SEQUENCER_372, 0x2fff }, - { WM8996_WRITE_SEQUENCER_376, 0x2fff }, - { WM8996_WRITE_SEQUENCER_380, 0x2fff }, - { WM8996_WRITE_SEQUENCER_384, 0x60 }, - { WM8996_WRITE_SEQUENCER_386, 0x601 }, - { WM8996_WRITE_SEQUENCER_388, 0x61 }, - { WM8996_WRITE_SEQUENCER_390, 0x601 }, - { WM8996_WRITE_SEQUENCER_392, 0x50 }, - { WM8996_WRITE_SEQUENCER_394, 0x300 }, - { WM8996_WRITE_SEQUENCER_396, 0x1 }, - { WM8996_WRITE_SEQUENCER_398, 0x304 }, - { WM8996_WRITE_SEQUENCER_400, 0x40 }, - { WM8996_WRITE_SEQUENCER_402, 0xf }, - { WM8996_WRITE_SEQUENCER_404, 0x1 }, - { WM8996_WRITE_SEQUENCER_407, 0x100 }, }; static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0); @@ -1706,18 +1528,6 @@ static bool wm8996_volatile_register(struct device *dev, unsigned int reg) } } -static int wm8996_reset(struct wm8996_priv *wm8996) -{ - if (wm8996->pdata.ldo_ena > 0) { - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); - gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1); - return 0; - } else { - return regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET, - 0x8915); - } -} - static const int bclk_divs[] = { 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96 }; @@ -1809,8 +1619,10 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: regcache_cache_only(codec->control_data, true); - if (wm8996->pdata.ldo_ena >= 0) + if (wm8996->pdata.ldo_ena >= 0) { gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + regcache_cache_only(codec->control_data, true); + } regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); break; @@ -2807,7 +2619,7 @@ static int wm8996_probe(struct snd_soc_codec *codec) int ret; struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct i2c_client *i2c = to_i2c_client(codec->dev); - int i, irq_flags; + int irq_flags; wm8996->codec = codec; @@ -2822,177 +2634,12 @@ static int wm8996_probe(struct snd_soc_codec *codec) goto err; } - wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0; - wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1; - wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2; - - /* This should really be moved into the regulator core */ - for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { - ret = regulator_register_notifier(wm8996->supplies[i].consumer, - &wm8996->disable_nb[i]); - if (ret != 0) { - dev_err(codec->dev, - "Failed to register regulator notifier: %d\n", - ret); - } - } - - /* Apply platform data settings */ - snd_soc_update_bits(codec, WM8996_LINE_INPUT_CONTROL, - WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK, - wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT | - wm8996->pdata.inr_mode); - - for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) { - if (!wm8996->pdata.gpio_default[i]) - continue; - - snd_soc_write(codec, WM8996_GPIO_1 + i, - wm8996->pdata.gpio_default[i] & 0xffff); - } - - if (wm8996->pdata.spkmute_seq) - snd_soc_update_bits(codec, WM8996_PDM_SPEAKER_MUTE_SEQUENCE, - WM8996_SPK_MUTE_ENDIAN | - WM8996_SPK_MUTE_SEQ1_MASK, - wm8996->pdata.spkmute_seq); - - snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_2, - WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC | - WM8996_MICD_SRC, wm8996->pdata.micdet_def); - - /* Latch volume update bits */ - snd_soc_update_bits(codec, WM8996_LEFT_LINE_INPUT_VOLUME, - WM8996_IN1_VU, WM8996_IN1_VU); - snd_soc_update_bits(codec, WM8996_RIGHT_LINE_INPUT_VOLUME, - WM8996_IN1_VU, WM8996_IN1_VU); - - snd_soc_update_bits(codec, WM8996_DAC1_LEFT_VOLUME, - WM8996_DAC1_VU, WM8996_DAC1_VU); - snd_soc_update_bits(codec, WM8996_DAC1_RIGHT_VOLUME, - WM8996_DAC1_VU, WM8996_DAC1_VU); - snd_soc_update_bits(codec, WM8996_DAC2_LEFT_VOLUME, - WM8996_DAC2_VU, WM8996_DAC2_VU); - snd_soc_update_bits(codec, WM8996_DAC2_RIGHT_VOLUME, - WM8996_DAC2_VU, WM8996_DAC2_VU); - - snd_soc_update_bits(codec, WM8996_OUTPUT1_LEFT_VOLUME, - WM8996_DAC1_VU, WM8996_DAC1_VU); - snd_soc_update_bits(codec, WM8996_OUTPUT1_RIGHT_VOLUME, - WM8996_DAC1_VU, WM8996_DAC1_VU); - snd_soc_update_bits(codec, WM8996_OUTPUT2_LEFT_VOLUME, - WM8996_DAC2_VU, WM8996_DAC2_VU); - snd_soc_update_bits(codec, WM8996_OUTPUT2_RIGHT_VOLUME, - WM8996_DAC2_VU, WM8996_DAC2_VU); - - snd_soc_update_bits(codec, WM8996_DSP1_TX_LEFT_VOLUME, - WM8996_DSP1TX_VU, WM8996_DSP1TX_VU); - snd_soc_update_bits(codec, WM8996_DSP1_TX_RIGHT_VOLUME, - WM8996_DSP1TX_VU, WM8996_DSP1TX_VU); - snd_soc_update_bits(codec, WM8996_DSP2_TX_LEFT_VOLUME, - WM8996_DSP2TX_VU, WM8996_DSP2TX_VU); - snd_soc_update_bits(codec, WM8996_DSP2_TX_RIGHT_VOLUME, - WM8996_DSP2TX_VU, WM8996_DSP2TX_VU); - - snd_soc_update_bits(codec, WM8996_DSP1_RX_LEFT_VOLUME, - WM8996_DSP1RX_VU, WM8996_DSP1RX_VU); - snd_soc_update_bits(codec, WM8996_DSP1_RX_RIGHT_VOLUME, - WM8996_DSP1RX_VU, WM8996_DSP1RX_VU); - snd_soc_update_bits(codec, WM8996_DSP2_RX_LEFT_VOLUME, - WM8996_DSP2RX_VU, WM8996_DSP2RX_VU); - snd_soc_update_bits(codec, WM8996_DSP2_RX_RIGHT_VOLUME, - WM8996_DSP2RX_VU, WM8996_DSP2RX_VU); - - /* No support currently for the underclocked TDM modes and - * pick a default TDM layout with each channel pair working with - * slots 0 and 1. */ - snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_0_CONFIGURATION, - WM8996_AIF1RX_CHAN0_SLOTS_MASK | - WM8996_AIF1RX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_1_CONFIGURATION, - WM8996_AIF1RX_CHAN1_SLOTS_MASK | - WM8996_AIF1RX_CHAN1_START_SLOT_MASK, - 1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1); - snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_2_CONFIGURATION, - WM8996_AIF1RX_CHAN2_SLOTS_MASK | - WM8996_AIF1RX_CHAN2_START_SLOT_MASK, - 1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_3_CONFIGURATION, - WM8996_AIF1RX_CHAN3_SLOTS_MASK | - WM8996_AIF1RX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1); - snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_4_CONFIGURATION, - WM8996_AIF1RX_CHAN4_SLOTS_MASK | - WM8996_AIF1RX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_5_CONFIGURATION, - WM8996_AIF1RX_CHAN5_SLOTS_MASK | - WM8996_AIF1RX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1); - - snd_soc_update_bits(codec, WM8996_AIF2RX_CHANNEL_0_CONFIGURATION, - WM8996_AIF2RX_CHAN0_SLOTS_MASK | - WM8996_AIF2RX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF2RX_CHANNEL_1_CONFIGURATION, - WM8996_AIF2RX_CHAN1_SLOTS_MASK | - WM8996_AIF2RX_CHAN1_START_SLOT_MASK, - 1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1); - - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_0_CONFIGURATION, - WM8996_AIF1TX_CHAN0_SLOTS_MASK | - WM8996_AIF1TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, - WM8996_AIF1TX_CHAN1_SLOTS_MASK | - WM8996_AIF1TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1); - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_2_CONFIGURATION, - WM8996_AIF1TX_CHAN2_SLOTS_MASK | - WM8996_AIF1TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_3_CONFIGURATION, - WM8996_AIF1TX_CHAN3_SLOTS_MASK | - WM8996_AIF1TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1); - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_4_CONFIGURATION, - WM8996_AIF1TX_CHAN4_SLOTS_MASK | - WM8996_AIF1TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_5_CONFIGURATION, - WM8996_AIF1TX_CHAN5_SLOTS_MASK | - WM8996_AIF1TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1); - - snd_soc_update_bits(codec, WM8996_AIF2TX_CHANNEL_0_CONFIGURATION, - WM8996_AIF2TX_CHAN0_SLOTS_MASK | - WM8996_AIF2TX_CHAN0_START_SLOT_MASK, - 1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0); - snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, - WM8996_AIF2TX_CHAN1_SLOTS_MASK | - WM8996_AIF2TX_CHAN1_START_SLOT_MASK, - 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1); - if (wm8996->pdata.num_retune_mobile_cfgs) wm8996_retune_mobile_pdata(codec); else snd_soc_add_codec_controls(codec, wm8996_eq_controls, ARRAY_SIZE(wm8996_eq_controls)); - /* If the TX LRCLK pins are not in LRCLK mode configure the - * AIFs to source their clocks from the RX LRCLKs. - */ - if ((snd_soc_read(codec, WM8996_GPIO_1))) - snd_soc_update_bits(codec, WM8996_AIF1_TX_LRCLK_2, - WM8996_AIF1TX_LRCLK_MODE, - WM8996_AIF1TX_LRCLK_MODE); - - if ((snd_soc_read(codec, WM8996_GPIO_2))) - snd_soc_update_bits(codec, WM8996_AIF2_TX_LRCLK_2, - WM8996_AIF2TX_LRCLK_MODE, - WM8996_AIF2TX_LRCLK_MODE); - if (i2c->irq) { if (wm8996->pdata.irq_flags) irq_flags = wm8996->pdata.irq_flags; @@ -3036,9 +2683,7 @@ err: static int wm8996_remove(struct snd_soc_codec *codec) { - struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct i2c_client *i2c = to_i2c_client(codec->dev); - int i; snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL, WM8996_IM_IRQ, WM8996_IM_IRQ); @@ -3046,10 +2691,6 @@ static int wm8996_remove(struct snd_soc_codec *codec) if (i2c->irq) free_irq(i2c->irq, codec); - for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) - regulator_unregister_notifier(wm8996->supplies[i].consumer, - &wm8996->disable_nb[i]); - return 0; } @@ -3163,6 +2804,21 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c, goto err_gpio; } + wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0; + wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1; + wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { + ret = regulator_register_notifier(wm8996->supplies[i].consumer, + &wm8996->disable_nb[i]); + if (ret != 0) { + dev_err(&i2c->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); if (ret != 0) { @@ -3175,7 +2831,7 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c, msleep(5); } - wm8996->regmap = regmap_init_i2c(i2c, &wm8996_regmap); + wm8996->regmap = devm_regmap_init_i2c(i2c, &wm8996_regmap); if (IS_ERR(wm8996->regmap)) { ret = PTR_ERR(wm8996->regmap); dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); @@ -3203,15 +2859,199 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c, dev_info(&i2c->dev, "revision %c\n", (reg & WM8996_CHIP_REV_MASK) + 'A'); - ret = wm8996_reset(wm8996); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to issue reset\n"); - goto err_regmap; + if (wm8996->pdata.ldo_ena > 0) { + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + regcache_cache_only(wm8996->regmap, true); + } else { + ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET, + 0x8915); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + goto err_regmap; + } } - regcache_cache_only(wm8996->regmap, true); regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); + /* Apply platform data settings */ + regmap_update_bits(wm8996->regmap, WM8996_LINE_INPUT_CONTROL, + WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK, + wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT | + wm8996->pdata.inr_mode); + + for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) { + if (!wm8996->pdata.gpio_default[i]) + continue; + + regmap_write(wm8996->regmap, WM8996_GPIO_1 + i, + wm8996->pdata.gpio_default[i] & 0xffff); + } + + if (wm8996->pdata.spkmute_seq) + regmap_update_bits(wm8996->regmap, + WM8996_PDM_SPEAKER_MUTE_SEQUENCE, + WM8996_SPK_MUTE_ENDIAN | + WM8996_SPK_MUTE_SEQ1_MASK, + wm8996->pdata.spkmute_seq); + + regmap_update_bits(wm8996->regmap, WM8996_ACCESSORY_DETECT_MODE_2, + WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC | + WM8996_MICD_SRC, wm8996->pdata.micdet_def); + + /* Latch volume update bits */ + regmap_update_bits(wm8996->regmap, WM8996_LEFT_LINE_INPUT_VOLUME, + WM8996_IN1_VU, WM8996_IN1_VU); + regmap_update_bits(wm8996->regmap, WM8996_RIGHT_LINE_INPUT_VOLUME, + WM8996_IN1_VU, WM8996_IN1_VU); + + regmap_update_bits(wm8996->regmap, WM8996_DAC1_LEFT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_DAC1_RIGHT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_DAC2_LEFT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + regmap_update_bits(wm8996->regmap, WM8996_DAC2_RIGHT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_LEFT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_RIGHT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_LEFT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_RIGHT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + + regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_LEFT_VOLUME, + WM8996_DSP1TX_VU, WM8996_DSP1TX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_RIGHT_VOLUME, + WM8996_DSP1TX_VU, WM8996_DSP1TX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_LEFT_VOLUME, + WM8996_DSP2TX_VU, WM8996_DSP2TX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_RIGHT_VOLUME, + WM8996_DSP2TX_VU, WM8996_DSP2TX_VU); + + regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_LEFT_VOLUME, + WM8996_DSP1RX_VU, WM8996_DSP1RX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_RIGHT_VOLUME, + WM8996_DSP1RX_VU, WM8996_DSP1RX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_LEFT_VOLUME, + WM8996_DSP2RX_VU, WM8996_DSP2RX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_RIGHT_VOLUME, + WM8996_DSP2RX_VU, WM8996_DSP2RX_VU); + + /* No support currently for the underclocked TDM modes and + * pick a default TDM layout with each channel pair working with + * slots 0 and 1. */ + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_0_CONFIGURATION, + WM8996_AIF1RX_CHAN0_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_1_CONFIGURATION, + WM8996_AIF1RX_CHAN1_SLOTS_MASK | + WM8996_AIF1RX_CHAN1_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_2_CONFIGURATION, + WM8996_AIF1RX_CHAN2_SLOTS_MASK | + WM8996_AIF1RX_CHAN2_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_3_CONFIGURATION, + WM8996_AIF1RX_CHAN3_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_4_CONFIGURATION, + WM8996_AIF1RX_CHAN4_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_5_CONFIGURATION, + WM8996_AIF1RX_CHAN5_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1); + + regmap_update_bits(wm8996->regmap, + WM8996_AIF2RX_CHANNEL_0_CONFIGURATION, + WM8996_AIF2RX_CHAN0_SLOTS_MASK | + WM8996_AIF2RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF2RX_CHANNEL_1_CONFIGURATION, + WM8996_AIF2RX_CHAN1_SLOTS_MASK | + WM8996_AIF2RX_CHAN1_START_SLOT_MASK, + 1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1); + + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_0_CONFIGURATION, + WM8996_AIF1TX_CHAN0_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, + WM8996_AIF1TX_CHAN1_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_2_CONFIGURATION, + WM8996_AIF1TX_CHAN2_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_3_CONFIGURATION, + WM8996_AIF1TX_CHAN3_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_4_CONFIGURATION, + WM8996_AIF1TX_CHAN4_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_5_CONFIGURATION, + WM8996_AIF1TX_CHAN5_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1); + + regmap_update_bits(wm8996->regmap, + WM8996_AIF2TX_CHANNEL_0_CONFIGURATION, + WM8996_AIF2TX_CHAN0_SLOTS_MASK | + WM8996_AIF2TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, + WM8996_AIF2TX_CHAN1_SLOTS_MASK | + WM8996_AIF2TX_CHAN1_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1); + + /* If the TX LRCLK pins are not in LRCLK mode configure the + * AIFs to source their clocks from the RX LRCLKs. + */ + ret = regmap_read(wm8996->regmap, WM8996_GPIO_1, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read GPIO1: %d\n", ret); + goto err_regmap; + } + + if (reg & WM8996_GP1_FN_MASK) + regmap_update_bits(wm8996->regmap, WM8996_AIF1_TX_LRCLK_2, + WM8996_AIF1TX_LRCLK_MODE, + WM8996_AIF1TX_LRCLK_MODE); + + ret = regmap_read(wm8996->regmap, WM8996_GPIO_2, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read GPIO2: %d\n", ret); + goto err_regmap; + } + + if (reg & WM8996_GP2_FN_MASK) + regmap_update_bits(wm8996->regmap, WM8996_AIF2_TX_LRCLK_2, + WM8996_AIF2TX_LRCLK_MODE, + WM8996_AIF2TX_LRCLK_MODE); + wm8996_init_gpio(wm8996); ret = snd_soc_register_codec(&i2c->dev, @@ -3225,7 +3065,6 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c, err_gpiolib: wm8996_free_gpio(wm8996); err_regmap: - regmap_exit(wm8996->regmap); err_enable: if (wm8996->pdata.ldo_ena > 0) gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); @@ -3241,14 +3080,18 @@ err: static __devexit int wm8996_i2c_remove(struct i2c_client *client) { struct wm8996_priv *wm8996 = i2c_get_clientdata(client); + int i; snd_soc_unregister_codec(&client->dev); wm8996_free_gpio(wm8996); - regmap_exit(wm8996->regmap); if (wm8996->pdata.ldo_ena > 0) { gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); gpio_free(wm8996->pdata.ldo_ena); } + for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) + regulator_unregister_notifier(wm8996->supplies[i].consumer, + &wm8996->disable_nb[i]); + return 0; } diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 9328270df16c..2de74e1ea225 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -3,7 +3,7 @@ * * Author: Mark Brown * - * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2009-12 Wolfson Microelectronics plc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 4b263b6edf13..2c2346fdd637 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -1,7 +1,7 @@ /* * ALSA SoC WM9090 driver * - * Copyright 2009, 2010 Wolfson Microelectronics + * Copyright 2009-12 Wolfson Microelectronics * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index a1541414d904..099e6ec32125 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -1,7 +1,7 @@ /* * wm9712.c -- ALSA Soc WM9712 codec support * - * Copyright 2006 Wolfson Microelectronics PLC. + * Copyright 2006-12 Wolfson Microelectronics PLC. * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 2d22cc70d536..3eb19fb71d17 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1,7 +1,7 @@ /* * wm9713.c -- ALSA Soc WM9713 codec support * - * Copyright 2006 Wolfson Microelectronics PLC. + * Copyright 2006-10 Wolfson Microelectronics PLC. * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index dfe957a47f29..61baa48823cb 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -1,7 +1,7 @@ /* * wm_hubs.c -- WM8993/4 common code * - * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2009-12 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * |