--- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs42l73.c | 1372 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs42l73.h | 225 ++++++++ 4 files changed, 1603 insertions(+), 0 deletions(-) create mode 100644 sound/soc/codecs/cs42l73.c create mode 100644 sound/soc/codecs/cs42l73.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 71b46c8..dca1183 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ALC5623 if I2C select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C + select SND_SOC_CS42L73 if I2C select SND_SOC_CS4270 if I2C select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 @@ -173,6 +174,9 @@ config SND_SOC_CQ0093VC config SND_SOC_CS42L51 tristate
+config SND_SOC_CS42L73 + tristate + # Cirrus Logic CS4270 Codec config SND_SOC_CS4270 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70c1769..bdbc58d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -15,6 +15,7 @@ snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o +snd-soc-cs42l73-objs := cs42l73.o snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o @@ -113,6 +114,7 @@ obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o +obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c new file mode 100644 index 0000000..a2b9ce3 --- /dev/null +++ b/sound/soc/codecs/cs42l73.c @@ -0,0 +1,1372 @@ +/* + * cs42l73.c -- CS42L73 ALSA Soc Audio driver + * + * Copyright 2011 Cirrus Logic, Inc. + * + * Authors: Georgi Vlaev, Nucleus Systems Ltd, office@nucleusys.com + * Brian Austin, Cirrus Logic Inc, brian.austin@cirrus.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/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <linux/gpio.h> + +#include "cs42l73.h" + +struct sp_config { + u8 spc, mmcc, spfs; + u32 srate; +}; + +struct cs42l73_private { + enum snd_soc_control_type control_type; + void *control_data; + u8 reg_cache[CS42L73_CACHEREGNUM]; + u32 sysclk; /* external MCLK */ + u8 mclksel; /* MCLKx */ + u32 mclk; /* internal MCLK */ + struct sp_config config[3]; +}; + +static const u8 cs42l73_reg[CS42L73_CACHEREGNUM] = { +/* 0*/ 0x00, 0x42, 0xA7, 0x30, +/* 4*/ 0x00, 0x00, 0xF1, 0xDF, +/* 8*/ 0x3F, 0x57, 0x53, 0x00, +/* C*/ 0x00, 0x15, 0x00, 0x15, +/*10*/ 0x00, 0x15, 0x00, 0x06, +/*14*/ 0x00, 0x00, 0x00, 0x00, +/*18*/ 0x00, 0x00, 0x00, 0x00, +/*1C*/ 0x00, 0x00, 0x00, 0x00, +/*20*/ 0x00, 0x00, 0x00, 0x00, +/*24*/ 0x00, 0x00, 0x00, 0x7F, +/*28*/ 0x00, 0x00, 0x3F, 0x00, +/*2C*/ 0x00, 0x3F, 0x00, 0x00, +/*30*/ 0x3F, 0x00, 0x00, 0x00, +/*34*/ 0x18, 0x3F, 0x3F, 0x3F, +/*38*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*3C*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*40*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*44*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*48*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*4C*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*50*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*54*/ 0x3F, 0xAA, 0x3F, 0x3F, +/*58*/ 0x3F, 0x3F, 0x3F, 0x3F, +/*5C*/ 0x3F, 0x3F, 0x00, 0x00, +/*60*/ 0x00, 0x00 +}; + +/* + CS42L73 I2C read/write. + 7 bit address, 8 bit data +*/ +static inline int cs42l73_read_reg_cache(struct snd_soc_codec *codec, u_int reg) +{ + u8 *cache = codec->reg_cache; + + return reg > CS42L73_CACHEREGNUM ? -EINVAL : cache[reg]; +} + +static inline void cs42l73_write_reg_cache(struct snd_soc_codec *codec, + u_int reg, u_int val) +{ + u8 *cache = codec->reg_cache; + + if (reg > CS42L73_CACHEREGNUM) + return; + + cache[reg] = val & 0xff; +} + +int cs42l73_write(struct snd_soc_codec *codec, unsigned reg, u_int val) +{ + u8 data[2]; + + if (reg > CS42L73_CACHEREGNUM) + return -EINVAL; + + cs42l73_write_reg_cache(codec, reg, val); + + data[0] = reg & 0x7f; /* reg address */ + data[1] = val & 0xff; /* reg value */ + + dev_dbg(codec->dev, "%s: reg 0x%x = %02x (%d)\n", + __FUNCTION__, reg, val, val); + + if (codec->hw_write(codec->control_data, data, 2) != 2) + return -EIO; + + return 0; +} + +unsigned int cs42l73_read(struct snd_soc_codec *codec, u_int reg) +{ + u8 *cache = codec->reg_cache; + + dev_dbg(codec->dev, "%s: reg 0x%x = %02x (%d)\n", + __FUNCTION__, reg, cache[reg], cache[reg]); + + return cache[reg]; +} + +int cs42l73_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mmax = (max > min) ? max:min; + unsigned int mask = (1 << fls(mmax)) - 1; + + ucontrol->value.integer.value[0] = + ((cs42l73_read(codec, reg) >> shift) - min) & mask; + if (shift != rshift) + ucontrol->value.integer.value[1] = + ((cs42l73_read(codec, reg) >> rshift) - min) & mask; + + return 0; +} + +int cs42l73_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int min = mc->min; + int mmax = (max > min) ? max:min; + unsigned int mask = (1 << fls(mmax)) - 1; + unsigned short val, val2, val_mask; + + val = ((ucontrol->value.integer.value[0] + min) & mask); + + val_mask = mask << shift; + val = val << shift; + if (shift != rshift) { + val2 = ((ucontrol->value.integer.value[1] + min) & mask); + val_mask |= mask << rshift; + val |= val2 << rshift; + } + return snd_soc_update_bits(codec, reg, val_mask, val); +} + +int cs42l73_info_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + int max = mc->max; + + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max; + return 0; +} + +int cs42l73_get_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + int max = mc->max; + int min = mc->min; + int mmax = (max > min) ? max:min; + unsigned int mask = (1 << fls(mmax)) - 1; + int val, val2; + + val = cs42l73_read(codec, reg); + val2 = cs42l73_read(codec, reg2); + ucontrol->value.integer.value[0] = (val - min) & mask; + ucontrol->value.integer.value[1] = (val2 - min) & mask; + return 0; +} + +int cs42l73_put_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + int max = mc->max; + int min = mc->min; + int mmax = (max > min) ? max:min; + unsigned int mask = (1 << fls(mmax)) - 1; + int err; + unsigned short val, val2; + + val = (ucontrol->value.integer.value[0] + min) & mask; + val2 = (ucontrol->value.integer.value[1] + min) & mask; + + if ((err = snd_soc_update_bits(codec, reg, mask, val)) < 0) + return err; + + return snd_soc_update_bits(codec, reg2, mask, val2); +} + +#define SOC_SINGLE_S8_C_TLV(xname, xreg, xshift, xmax, xmin, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = snd_soc_info_volsw, .get = cs42l73_get_volsw,\ + .put = cs42l73_put_volsw, .tlv.p = (tlv_array),\ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = xshift, .rshift = xshift, \ + .max = xmax, .min = xmin} } + +#define SOC_DOUBLE_R_S8_C_TLV(xname, xreg, xrreg, xmax, xmin, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = cs42l73_info_volsw_2r, \ + .get = cs42l73_get_volsw_2r, .put = cs42l73_put_volsw_2r, \ + .tlv.p = (tlv_array), \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xrreg, .max = xmax, .min = xmin} } + +/* + HP,LO Analog Volume TLV + -76dB ... -50 dB in 2dB steps + -50dB ... 12dB in 1dB steps +*/ +static const unsigned int hpaloa_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 13, TLV_DB_SCALE_ITEM(-7600, 200, 0), + 14,75, TLV_DB_SCALE_ITEM(-4900, 100, 0), +}; + +/* -102dB ... 12 dB in 0.5 dB steps */ +static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0); + +/* -96dB ... 12 dB in 1 dB steps */ +static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0); + +/* -6dB ... 12 dB in 0.5 dB steps */ +static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0); + +/* + HL, ESL, SPK, Limiter Threshold/Cushion TLV + 0dB -12 dB in -3dB steps + -12dB -30dB in -6dB steps +*/ +static const unsigned int limiter_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), +}; + +/* + * Stereo Mixer Input Attenuation (regs 35h-54h) TLV + * Mono Mixer Input Attenuation (regs 56h-5Dh) + + -62dB ... 0dB in 1dB steps, < -62dB = mute +*/ +static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1); + +/* Stereo Attenuation Group */ +#define SOC_DOUBLE_R_CS42L73_ATTN_GRP(xdest, xregl_start) \ + SOC_DOUBLE_R_TLV(xdest"-IP Attenuation Volume",\ + xregl_start + 0, xregl_start + 1, 0, 0x3F, 1, attn_tlv), \ + SOC_DOUBLE_R_TLV(xdest"-XSP Attenuation Volume",\ + xregl_start + 2, xregl_start + 3, 0, 0x3F, 1, attn_tlv), \ + SOC_DOUBLE_R_TLV(xdest"-ASP Attenuation Volume",\ + xregl_start + 4, xregl_start + 5, 0, 0x3F, 1, attn_tlv), \ + SOC_DOUBLE_R_TLV(xdest"-VSP Attenuation Volume",\ + xregl_start + 6, xregl_start + 7, 0, 0x3F, 1, attn_tlv) + +/* Mono Attenuation Group */ +#define SOC_SINGLE_CS42L73_ATTN_GRP(xdest, xreg_start) \ + SOC_SINGLE_TLV(xdest"-IP Mono Attenuation Volume",\ + xreg_start + 0, 0, 0x3F, 1, attn_tlv), \ + SOC_SINGLE_TLV(xdest"-XSP Mono Attenuation Volume",\ + xreg_start + 1, 0, 0x3F, 1, attn_tlv), \ + SOC_SINGLE_TLV(xdest"-ASP Mono Attenuation Volume",\ + xreg_start + 2, 0, 0x3F, 1, attn_tlv), \ + SOC_SINGLE_TLV(xdest"-VSP Mono Attenuation Volume",\ + xreg_start + 3, 0, 0x3F, 1, attn_tlv) + +/* Analog Input PGA Mux */ +static const char *cs42l73_pgaa_text[] = { "Line A", "Mic 1" }; +static const char *cs42l73_pgab_text[] = { "Line B", "Mic 2" }; + +static const struct soc_enum pgaa_enum = + SOC_ENUM_SINGLE(CS42L73_ADCIPC, 3, + ARRAY_SIZE(cs42l73_pgaa_text), cs42l73_pgaa_text); + +static const struct soc_enum pgab_enum = + SOC_ENUM_SINGLE(CS42L73_ADCIPC, 7, + ARRAY_SIZE (cs42l73_pgab_text), cs42l73_pgab_text); + +static const struct snd_kcontrol_new pgaa_mux = +SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum); + +static const struct snd_kcontrol_new pgab_mux = +SOC_DAPM_ENUM("Right Analog Input Capture Mux", pgab_enum); + +/* NG */ +static const char *cs42l73_ng_delay_text[] = + { "50ms", "100ms", "150ms", "200ms" }; + +static const struct soc_enum ng_delay_enum = + SOC_ENUM_SINGLE(CS42L73_NGCAB, 0, + ARRAY_SIZE (cs42l73_ng_delay_text), cs42l73_ng_delay_text); + +/* Mono Mixer Select*/ +static const char *cs42l73_mono_mixer_text[] = + { "Left", "Right", "Mono Mix"}; + +/* ESL-ASP, ESL-XSP, SPK-ASP, SPK-XSP Mono Mixer Selects */ +static const struct soc_enum mono_mixer_enum[] = +{ + SOC_ENUM_SINGLE(CS42L73_MMIXCTL, 6, + ARRAY_SIZE (cs42l73_mono_mixer_text), cs42l73_mono_mixer_text), + SOC_ENUM_SINGLE(CS42L73_MMIXCTL, 4, + ARRAY_SIZE (cs42l73_mono_mixer_text), cs42l73_mono_mixer_text), + SOC_ENUM_SINGLE(CS42L73_MMIXCTL, 2, + ARRAY_SIZE (cs42l73_mono_mixer_text), cs42l73_mono_mixer_text), + SOC_ENUM_SINGLE(CS42L73_MMIXCTL, 0, + ARRAY_SIZE (cs42l73_mono_mixer_text), cs42l73_mono_mixer_text), +}; + +static const char *cs42l73_ip_swap_text[] = + { "Stereo", "Mono A", "Mono B", "Swap A-B"}; + +static const struct soc_enum ip_swap_enum = + SOC_ENUM_SINGLE( CS42L73_MIOPC, 6, + ARRAY_SIZE(cs42l73_ip_swap_text), cs42l73_ip_swap_text); + +/* XSPOUT, VSPOUT Mixer output */ +static const char *cs42l73_spo_mixer_text[] = + { "Mono", "Stereo"}; + +static const struct soc_enum spo_mixer_enum[] = +{ + SOC_ENUM_SINGLE(CS42L73_MIXERCTL, 5, + ARRAY_SIZE (cs42l73_spo_mixer_text), cs42l73_spo_mixer_text), + SOC_ENUM_SINGLE(CS42L73_MIXERCTL, 4, + ARRAY_SIZE (cs42l73_spo_mixer_text), cs42l73_spo_mixer_text), +}; + +static const struct snd_kcontrol_new cs42l73_snd_controls[] = { +/* + SOC_DOUBLE_R_S8_C (..., max, min) + min - min from CS datasheet + max - fls(max) - min + max from CS datasheet +*/ +/* Volume */ + SOC_DOUBLE_R_S8_C_TLV("Headphone Analog Playback Volume", CS42L73_HPAAVOL, + CS42L73_HPBAVOL, 0x4B, 0x41, hpaloa_tlv), + + SOC_DOUBLE_R_S8_C_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL, + CS42L73_LOBAVOL, 0x4B, 0x41, hpaloa_tlv), + + SOC_DOUBLE_R_S8_C_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL, + CS42L73_MICBPREPGABVOL, 0x24, 0x34, micpga_tlv), + + SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL, + CS42L73_MICBPREPGABVOL, 6, 1, 1), + + SOC_DOUBLE_R_S8_C_TLV("Input Path Digital Volume", CS42L73_IPADVOL, + CS42L73_IPBDVOL, 0x6C, 0xA0, ipd_tlv), + + SOC_DOUBLE_R_S8_C_TLV("HL Digital Playback Volume", + CS42L73_HLADVOL, CS42L73_HLBDVOL, 0xE4, 0x34, hl_tlv), + +/* Single select Volume */ + SOC_SINGLE_S8_C_TLV("Headphone A Analog Playback Volume", CS42L73_HPAAVOL, + 0, 0x4B, 0x41, hpaloa_tlv), + SOC_SINGLE_S8_C_TLV("Headphone B Analog Playback Volume", CS42L73_HPBAVOL, + 0, 0x4B, 0x41, hpaloa_tlv), + + SOC_SINGLE_S8_C_TLV("HL-A Digital Playback Volume", + 0, CS42L73_HLADVOL, 0xE4, 0x34, hl_tlv), + SOC_SINGLE_S8_C_TLV("HL-B Digital Playback Volume", + 0, CS42L73_HLBDVOL, 0xE4, 0x34, hl_tlv), + + SOC_SINGLE_S8_C_TLV("LineOut Analog A Playback Volume", CS42L73_LOAAVOL, + 0, 0x4B, 0x41, hpaloa_tlv), + SOC_SINGLE_S8_C_TLV("LineOut Analog B Playback Volume", CS42L73_LOBAVOL, + 0, 0x4B, 0x41, hpaloa_tlv), + + SOC_SINGLE_S8_C_TLV("MIC 1 PGA Analog Volume", CS42L73_MICAPREPGAAVOL, + 0, 0x24, 0x34, micpga_tlv), + SOC_SINGLE_S8_C_TLV("MIC 2 PGA Analog Volume", CS42L73_MICBPREPGABVOL, + 0, 0x24, 0x34, micpga_tlv), + + SOC_SINGLE_S8_C_TLV("Input Path A Digital Volume", CS42L73_IPADVOL, + 0, 0x6C, 0xA0, ipd_tlv), + SOC_SINGLE_S8_C_TLV("Input Path B Digital Volume", CS42L73_IPBDVOL, + 0, 0x6C, 0xA0, ipd_tlv), + + SOC_SINGLE_S8_C_TLV("Speakerphone Digital Playback Volume", CS42L73_SPKDVOL, + 0, 0xE4, 0x34, hl_tlv), + + SOC_SINGLE_S8_C_TLV("Ear Speaker Digital Playback Volume", CS42L73_ESLDVOL, + 0, 0xE4, 0x34, hl_tlv), + +/* Digital/Analog Mute */ + SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL, + CS42L73_HPBAVOL, 7, 1, 1), + SOC_SINGLE("Headphone A Analog Playback Switch", CS42L73_HPAAVOL, 7, 1, 1), + SOC_SINGLE("Headphone B Analog Playback Switch", CS42L73_HPBAVOL, 7, 1, 1), + + SOC_DOUBLE_R("LineOut Analog Playback Switch", CS42L73_LOAAVOL, + CS42L73_LOBAVOL, 7, 1, 1), + SOC_SINGLE("LineOut A Analog Playback Switch", CS42L73_LOAAVOL, 7, 1, 1), + SOC_SINGLE("LineOut B Analog Playback Switch", CS42L73_LOBAVOL, 7, 1, 1), + + SOC_DOUBLE("Input Path Digital Switch", CS42L73_ADCIPC, 0, 4, 1, 1), + SOC_DOUBLE("HL Digital Playback Switch", CS42L73_PBDC, 0, + 1, 1, 1), + SOC_SINGLE("Speakerphone Digital Playback Switch", CS42L73_PBDC, 2, 1, + 1), + SOC_SINGLE("Ear Speaker Digital Playback Switch", CS42L73_PBDC, 3, 1, + 1), + + SOC_SINGLE("PGA Soft-Ramp Switch", CS42L73_MIOPC, 3, 1, 0), + SOC_SINGLE("Analog Zero Cross Switch", CS42L73_MIOPC, 2, 1, 0), + SOC_SINGLE("Digital Soft-Ramp Switch", CS42L73_MIOPC, 1, 1, 0), + SOC_SINGLE("Analog Output Soft-Ramp Switch", CS42L73_MIOPC, 0, 1, 0), + +/* ADC */ + SOC_DOUBLE("Invert ADC Signal Polarity Switch", CS42L73_ADCIPC, 1, 5, 1, + 0), + SOC_DOUBLE("ADC Boost Switch", CS42L73_ADCIPC, 2, 6, 1, 0), + + SOC_SINGLE("Charge Pump Frequency Volume", CS42L73_CPFCHC, 4, 15, 0), + +/* Headphone/LineOut (HL) Limiter */ + SOC_SINGLE("HL Limiter Attack Rate Volume", CS42L73_LIMARATEHL, 0, 0x3F, + 0), + SOC_SINGLE("HL Limiter Release Rate Volume", CS42L73_LIMRRATEHL, 0, + 0x3F, 0), + SOC_SINGLE("HL Limiter Switch", CS42L73_LIMRRATEHL, 7, 1, 0), + SOC_SINGLE("HL Limiter All Channels Switch", CS42L73_LIMRRATEHL, 6, 1, + 0), + + SOC_SINGLE_TLV("HL Limiter Max Threshold Volume", CS42L73_LMAXHL, 5, 7, + 1, + limiter_tlv), + SOC_SINGLE_TLV("HL Limiter Cushion Volume", CS42L73_LMAXHL, 2, 7, 1, + limiter_tlv), + +/* Speakerphone Limiter */ + SOC_SINGLE("SPK Limiter Attack Rate Volume", CS42L73_LIMARATESPK, 0, + 0x3F, 0), + SOC_SINGLE("SPK Limiter Release Rate Volume", CS42L73_LIMRRATESPK, 0, + 0x3F, 0), + SOC_SINGLE("SPK Limiter Switch", CS42L73_LIMRRATESPK, 7, 1, 0), + SOC_SINGLE("SPK Limiter All Channels Switch", CS42L73_LIMRRATESPK, 6, 1, + 0), + SOC_SINGLE_TLV("SPK Limiter Max Threshold Volume", CS42L73_LMAXSPK, 5, + 7, 1, + limiter_tlv), + SOC_SINGLE_TLV("SPK Limiter Cushion Volume", CS42L73_LMAXSPK, 2, 7, 1, + limiter_tlv), + +/* Earphone/Speakerphone LO Limiter */ + SOC_SINGLE("ESL Limiter Attack Rate Volume", CS42L73_LIMARATEESL, 0, + 0x3F, 0), + SOC_SINGLE("ESL Limiter Release Rate Volume", CS42L73_LIMRRATEESL, 0, + 0x3F, 0), + SOC_SINGLE("ESL Limiter Switch", CS42L73_LIMRRATEESL, 7, 1, 0), + SOC_SINGLE_TLV("ESL Limiter Max Threshold Volume", CS42L73_LMAXESL, 5, + 7, 1, + limiter_tlv), + SOC_SINGLE_TLV("ESL Limiter Cushion Volume", CS42L73_LMAXESL, 2, 7, 1, + limiter_tlv), + +/* ALC */ + + SOC_SINGLE("ALC Attack Rate Volume", CS42L73_ALCARATE, 0, 0x3F, 0), + SOC_SINGLE("ALC Release Rate Volume", CS42L73_ALCRRATE, 0, 0x3F, 0), + SOC_DOUBLE("ALC Switch", CS42L73_ALCARATE, 6, 7, 1, 0), + SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L73_ALCMINMAX, 5, 7, 1, + limiter_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L73_ALCMINMAX, 2, 7, 1, + limiter_tlv), + +/* Noise Gate */ + SOC_DOUBLE("NG Enable Switch", CS42L73_NGCAB, 6, 7, 1, 0), + SOC_SINGLE("NG Boost Switch", CS42L73_NGCAB, 5, 1, 0), + /* + NG Threshold depends on NG_BOOTSAB, which selects + between two threshold scales in decibels. + Set linear values for now .. + */ + SOC_SINGLE("NG Threshold", CS42L73_NGCAB, 2, 7, 0), + SOC_ENUM("NG Delay", ng_delay_enum), + +/* Digital IO Attenuation */ + SOC_DOUBLE_R_CS42L73_ATTN_GRP("XSP", CS42L73_XSPAIPAA), + SOC_DOUBLE_R_CS42L73_ATTN_GRP("ASP", CS42L73_ASPAIPAA), + SOC_DOUBLE_R_CS42L73_ATTN_GRP("VSP", CS42L73_VSPAIPAA), + +/* Output Attenuation */ + SOC_DOUBLE_R_CS42L73_ATTN_GRP("HL", CS42L73_HLAIPAA), + SOC_SINGLE_CS42L73_ATTN_GRP("SPK", CS42L73_SPKMIPMA), + SOC_SINGLE_CS42L73_ATTN_GRP("ESL", CS42L73_ESLMIPMA), + +/* Channel Swap Record Enum */ + SOC_ENUM("IP Digital Swap/Mono Select", ip_swap_enum), + +/* Mono Mixer Select*/ + SOC_ENUM("ESL-ASP Mono Mixer Select", mono_mixer_enum[0]), + SOC_ENUM("ESL-XSP Mono Mixer Select", mono_mixer_enum[1]), + SOC_ENUM("SPK-ASP Mono Mixer Select", mono_mixer_enum[2]), + SOC_ENUM("SPK-XSP Mono Mixer Select", mono_mixer_enum[3]), + +/* XSPOUT, VSPOUT Output Mixer Path Select */ + SOC_ENUM("VSP Output Mixer Select", spo_mixer_enum[0]), + SOC_ENUM("XSP Output Mixer Select", spo_mixer_enum[1]), + + + +}; + +static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = { +/* Platform / Analog Inputs */ + SND_SOC_DAPM_INPUT("LINEINA"), + SND_SOC_DAPM_INPUT("LINEINB"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_MICBIAS("MIC1 Bias", CS42L73_PWRCTL2, 6, 1), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_MICBIAS("MIC2 Bias", CS42L73_PWRCTL2, 7, 1), + SND_SOC_DAPM_INPUT("DMICA"), + SND_SOC_DAPM_INPUT("DMICB"), + +/* Stream */ + /* Digital Outputs*/ + SND_SOC_DAPM_AIF_OUT("XSPOUT", "XSP Capture", 0, CS42L73_PWRCTL2, 1, 1), + SND_SOC_DAPM_AIF_OUT("ASPOUT", "ASP Capture", 0, CS42L73_PWRCTL2, 3, 1), + SND_SOC_DAPM_AIF_OUT("VSPOUT", "VSP Capture", 0, CS42L73_PWRCTL2, 4, 1), + + /* Digital Inputs*/ + SND_SOC_DAPM_AIF_IN("XSPIN", "XSP Playback", 0, CS42L73_PWRCTL2, 0, 1), + SND_SOC_DAPM_AIF_IN("ASPIN", "ASP Playback", 0, CS42L73_PWRCTL2, 2, 1), + SND_SOC_DAPM_AIF_IN("VSPIN", "VSP Playback", 0, CS42L73_PWRCTL2, 4, 1), + + SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L73_PWRCTL1, 5, 1), + SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L73_PWRCTL1, 7, 1), + SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0), + +/* Path */ + SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA Right", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("PGA Mux Left", SND_SOC_NOPM, 0, 0, &pgaa_mux), + SND_SOC_DAPM_MUX("PGA Mux Right", SND_SOC_NOPM, 0, 0, &pgab_mux), +/* HP Output PGA */ + SND_SOC_DAPM_PGA("HP Amp Left", CS42L73_PWRCTL3, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("HP Amp Right", CS42L73_PWRCTL3, 0, 1, NULL, 0), +/* Line Output PGA */ + SND_SOC_DAPM_PGA("LO Amp Left", CS42L73_PWRCTL3, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("LO Amp Right", CS42L73_PWRCTL3, 1, 1, NULL, 0), +/* SPK Output PGA */ + SND_SOC_DAPM_PGA("SPK Amp", CS42L73_PWRCTL3, 2, 1, NULL, 0), +/* ESL Output PGA */ + SND_SOC_DAPM_PGA("EAR Amp", CS42L73_PWRCTL3, 3, 1, NULL, 0), +/* SPK LO PGA */ + SND_SOC_DAPM_PGA("SPKLO Amp", CS42L73_PWRCTL3, 4, 1, NULL, 0), +/* Outputs */ + SND_SOC_DAPM_OUTPUT("HPOUTA"), + SND_SOC_DAPM_OUTPUT("HPOUTB"), + SND_SOC_DAPM_OUTPUT("LINEOUTA"), + SND_SOC_DAPM_OUTPUT("LINEOUTB"), + SND_SOC_DAPM_OUTPUT("EAROUT"), + SND_SOC_DAPM_OUTPUT("SPKOUT"), + SND_SOC_DAPM_OUTPUT("SPKLINEOUT"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* outputs */ + {"HPOUTA", NULL, "HP Amp Left"}, + {"HPOUTB", NULL, "HP Amp Right"}, + {"LINEOUTA", NULL, "LO Amp Left"}, + {"LINEOUTB", NULL, "LO Amp Right"}, + {"SPKOUT", NULL, "SPK Amp"}, + {"EAROUT", NULL, "EAR Amp"}, + {"SPKLINEOUT", NULL, "SPKLO Amp"}, + + {"HP Amp Left", "DAC", "DAC Left"}, + {"HP Amp Right", "DAC", "DAC Right"}, + {"LO Amp Left", "DAC", "DAC Left"}, + {"LO Amp Right", "DAC", "DAC Right"}, + {"SPK Amp", "DAC", "DAC Left"}, + {"SPKLO Amp", "DAC", "DAC Right"}, + {"EAR Amp", "DAC", "DAC Right"}, + + /* inputs */ + {"PGA Mux Left", NULL, "LINEINA"}, + {"PGA Mux Right", NULL, "LINEINB"}, + {"PGA Mux Left", NULL, "MIC1"}, + {"PGA Mux Right", NULL, "MIC2"}, + + {"PGA Left", NULL, "PGA Mux Left"}, + {"PGA Right", NULL, "PGA Mux Right"}, + {"ADC Left", "ADC", "PGA Left"}, + {"ADC Right", "ADC", "PGA Right"}, + + /* AIFx = [XSP,ASP,VSP] */ + {"XSPOUT", NULL, "ADC Left"}, + {"XSPOUT", NULL, "ADC Right"}, + {"DAC Left", NULL, "XSPIN"}, + {"DAC Right", NULL, "XSPIN"}, + + {"ASPOUT", NULL, "ADC Left"}, + {"ASPOUT", NULL, "ADC Right"}, + {"DAC Left", NULL, "ASPIN"}, + {"DAC Right", NULL, "ASPIN"}, + + {"VSPOUT", NULL, "ADC Left"}, + {"VSPOUT", NULL, "ADC Right"}, + {"DAC Left", NULL, "VSPIN"}, + {"DAC Right", NULL, "VSPIN"}, + +}; + +static int cs42l73_add_widgets(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_new_controls(dapm, cs42l73_dapm_widgets, + ARRAY_SIZE(cs42l73_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + + return 0; +} + +struct cs42l73_mclk_div { + u32 mclk; /* MCLK (MHz) */ + u32 srate; /* Sample rate (KHz) */ + u8 mmcc; /* x_MMCC[5:0] divider */ +}; + +struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = { + /* MCLK, Sample Rate, xMMCC[5:0] */ + {5644800, 11025, 0x30}, + {5644800, 22050, 0x20}, + {5644800, 44100, 0x10}, + + {6000000, 8000, 0x39}, + {6000000, 11025, 0x33}, + {6000000, 12000, 0x31}, + {6000000, 16000, 0x29}, + {6000000, 22050, 0x23}, + {6000000, 24000, 0x21}, + {6000000, 32000, 0x19}, + {6000000, 44100, 0x13}, + {6000000, 48000, 0x11}, + + {6144000, 8000, 0x38}, + {6144000, 12000, 0x30}, + {6144000, 16000, 0x28}, + {6144000, 24000, 0x20}, + {6144000, 32000, 0x18}, + {6144000, 48000, 0x10}, + + {6500000, 8000, 0x3C}, + {6500000, 11025, 0x35}, + {6500000, 12000, 0x34}, + {6500000, 16000, 0x2C}, + {6500000, 22050, 0x25}, + {6500000, 24000, 0x24}, + {6500000, 32000, 0x1C}, + {6500000, 44100, 0x15}, + {6500000, 48000, 0x14}, + + {6400000, 8000, 0x3E}, + {6400000, 11025, 0x37}, + {6400000, 12000, 0x36}, + {6400000, 16000, 0x2E}, + {6400000, 22050, 0x27}, + {6400000, 24000, 0x26}, + {6400000, 32000, 0x1E}, + {6400000, 44100, 0x17}, + {6400000, 48000, 0x16}, +}; + +struct cs42l73_mcklx_div { + u32 mclkx; /* MCLK1/2 (MHz) */ + u8 ratio; /* Required Divide Ratio */ + u8 mclkdiv; /* MCLKDIV[2:0] */ +}; + +struct cs42l73_mcklx_div cs42l73_mclkx_coeffs[] = { + {5644800, 1, 0}, /* 5644800 */ + {6000000, 1, 0}, /* 6000000 */ + {6144000, 1, 0}, /* 6144000 */ + {11289600, 2, 2}, /* 5644800 */ + {12288000, 2, 2}, /* 6144000 */ + {12000000, 2, 2}, /* 6000000 */ + {13000000, 2, 2}, /* 6500000 */ + {19200000, 3, 3}, /* 6400000 */ + {24000000, 4, 4}, /* 6000000 */ + {26000000, 4, 4}, /* 6500000 */ + {38400000, 6, 5} /* 6400000 */ +}; + +int cs42l74_get_mclkx_coeff(int mclkx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l73_mclkx_coeffs); i++) { + if (cs42l73_mclkx_coeffs[i].mclkx == mclkx) + return i; + } + return -EINVAL; +} + +int cs42l74_get_mclk_coeff(int mclk, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l73_mclk_coeffs); i++) { + if (cs42l73_mclk_coeffs[i].mclk == mclk && + cs42l73_mclk_coeffs[i].srate == srate) + return i; + } + return -EINVAL; + +} + +static int cs42l73_set_mclk(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); + + int mclkx_coeff; + u32 mclk = 0; + u8 dmmcc = 0; + + /* MCLKX -> MCLK */ + mclkx_coeff = cs42l74_get_mclkx_coeff(priv->sysclk); + + if (mclkx_coeff < 0) + return -EINVAL; + + mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx / + cs42l73_mclkx_coeffs[mclkx_coeff].ratio; + + dev_dbg(codec->dev, "MCLK%u %u <-> internal MCLK %u\n", + priv->mclksel + 1, cs42l73_mclkx_coeffs[mclkx_coeff].mclkx, + mclk); + + dmmcc = + (priv->mclksel << 4) | (cs42l73_mclkx_coeffs[mclkx_coeff].mclkdiv << 1); + + cs42l73_write(codec, CS42L73_DMMCC, dmmcc); + + + priv->mclk = mclk; + + return 0; +} + +static int cs42l73_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); + + if (clk_id != CS42L73_CLKID_MCLK1 && clk_id != CS42L73_CLKID_MCLK2) { + dev_err(codec->dev, "Invalid clk_id %u\n", clk_id); + return -EINVAL; + } + + if ((cs42l74_get_mclkx_coeff(freq) < 0)) { + dev_err(codec->dev, "Invalid sysclk %u\n", freq); + return -EINVAL; + } + + priv->sysclk = freq; + priv->mclksel = clk_id; + + return cs42l73_set_mclk(dai); +} + +/* + cs42l73_set_dai_clkdiv() + Setup MCLKx, MMCC dividers. + The dividers are selected from the MCLKX and the + sample rate. +*/ +static int cs42l73_set_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int id = codec_dai->id; + u8 reg; + + switch (div_id) { + case CS42L73_MCLKXDIV: + /* MCLKDIV */ + reg = cs42l73_read(codec, CS42L73_DMMCC) & 0xf1; + cs42l73_write(codec, CS42L73_DMMCC, + reg | ((div & 0x07) << 1)); + break; + case CS42L73_MMCCDIV: + /* xSP MMCC */ + reg = cs42l73_read(codec, CS42L73_MMCC(id)) & 0xc0; + cs42l73_write(codec, CS42L73_MMCC(id), + reg | (div & 0x3f)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); + int id = codec_dai->id; + int inv, format; + u8 spc, mmcc; + + spc = cs42l73_read(codec, CS42L73_SPC(id)); + mmcc = cs42l73_read(codec, CS42L73_MMCC(id)); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + mmcc |= MS_MASTER; + break; + + case SND_SOC_DAIFMT_CBS_CFS: + mmcc &= ~MS_MASTER; + break; + + default: + return -EINVAL; + } + + format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); + inv = (fmt & SND_SOC_DAIFMT_INV_MASK); + + /* interface format */ + switch (format) { + case SND_SOC_DAIFMT_I2S: + spc &= ~xSPDIF_PCM; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + if (mmcc & MS_MASTER) { + dev_err(codec->dev, + "PCM format is supported only in slave mode\n"); + return -EINVAL; + } + if (id == CS42L73_ASP) { + dev_err(codec->dev, + "PCM format is not supported on ASP port\n"); + return -EINVAL; + } + spc |= xSPDIF_PCM; + break; + default: + return -EINVAL; + } + + if (spc & xSPDIF_PCM) { + spc &= (31 << 3); /* Clear PCM mode, set MSB->LSB */ + if (format == SND_SOC_DAIFMT_DSP_B + && inv == SND_SOC_DAIFMT_IB_IF) + spc |= (xPCM_MODE0 << 4); + else + + if (format == SND_SOC_DAIFMT_DSP_B + && inv == SND_SOC_DAIFMT_IB_NF) + spc |= (xPCM_MODE1 << 4); + else + + if (format == SND_SOC_DAIFMT_DSP_A + && inv == SND_SOC_DAIFMT_IB_IF) + spc |= (xPCM_MODE1 << 4); + else + return -EINVAL; + } + + priv->config[id].spc = spc; + priv->config[id].mmcc = mmcc; + + return 0; +} + +/* Sample rate converters */ +static u32 cs42l73_asrc_rates[] = { + 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000 +}; + +static unsigned int cs42l73_get_xspfs_coeff(u32 rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(cs42l73_asrc_rates); i++) { + if (cs42l73_asrc_rates[i] == rate) + return (i + 1); + } + return 0; /* 0 = Don't know */ +} + +static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate) +{ + u8 spfs = 0; + u8 reg; + + if (srate > 0) + spfs = cs42l73_get_xspfs_coeff(srate); + + switch (id) { + case CS42L73_XSP: + reg = cs42l73_read(codec, CS42L73_VXSPFS); + reg &= ~0x0f; + cs42l73_write(codec, CS42L73_VXSPFS, + reg | spfs ); + break; + case CS42L73_ASP: + reg = cs42l73_read(codec, CS42L73_ASPC); + reg &= ~0x3c; + cs42l73_write(codec, CS42L73_ASPC, + reg | (spfs << 2)); + break; + case CS42L73_VSP: + reg = cs42l73_read(codec, CS42L73_VXSPFS); + reg &= ~0xf0; + cs42l73_write(codec, CS42L73_VXSPFS, + reg | (spfs << 4) ); + break; + default: + break; + } +} + +static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); + int id = dai->id; + int mclk_coeff; + int srate = params_rate(params); + + if (priv->config[id].mmcc & MS_MASTER) { + /* CS42L73 Master */ + /* MCLK -> srate */ + mclk_coeff = + cs42l74_get_mclk_coeff(priv->mclk, srate); + + if (mclk_coeff < 0) + return -EINVAL; + + dev_dbg(codec->dev, + "DAI[%d]: MCLK %u, srate %u, MMCC[5:0] = %x\n", + id, priv->mclk, srate, + cs42l73_mclk_coeffs[mclk_coeff].mmcc); + + priv->config[id].mmcc &= 0xC0; + priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc; + priv->config[id].spc &= 0xFC; + priv->config[id].spc |= xMCK_SCLK_64FS; + + } else { + /* CS42L73 Slave */ + dev_dbg(codec->dev, "DAI[%d]: Slave\n", id); + priv->config[id].spc &= 0xFC; + priv->config[id].spc |= xMCK_SCLK_64FS; + } + /* Update ASRCs */ + priv->config[id].srate = srate; + cs42l73_update_asrc(codec, id, srate); + cs42l73_write(codec, CS42L73_SPC(id), priv->config[id].spc); + cs42l73_write(codec, CS42L73_MMCC(id), priv->config[id].mmcc); + + return 0; +} + +static int cs42l73_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u8 pwrctl1 = cs42l73_read(codec, CS42L73_PWRCTL1); + u8 dmmcc = cs42l73_read(codec, CS42L73_DMMCC); + + dev_dbg(codec->dev, + "%s: Level %d, PWRCTL1 0x%02x, DMMCC 0x%02x\n", + __FUNCTION__, level, pwrctl1, dmmcc); + + switch (level) { + case SND_SOC_BIAS_ON: + cs42l73_write(codec, CS42L73_DMMCC, dmmcc & ~ MCLKDIS); + cs42l73_write(codec, CS42L73_PWRCTL1, pwrctl1 & ~ PDN); + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + /* Powerdown all ports, inputs and outputs */ + cs42l73_write(codec, CS42L73_DMMCC, dmmcc & ~ MCLKDIS); + + cs42l73_write(codec, CS42L73_PWRCTL3, + PDN_THMS | PDN_SPKLO | PDN_EAR | + PDN_SPK | PDN_LO | PDN_HP); + + cs42l73_write(codec, CS42L73_PWRCTL2, + PDN_MIC2_BIAS | PDN_MIC1_BIAS | + PDN_VSP | PDN_ASP_SDOUT | PDN_ASP_SDIN | + PDN_XSP_SDOUT | PDN_XSP_SDIN); + + cs42l73_write(codec, CS42L73_PWRCTL1, + PDN_ADCB | PDN_ADCA | PDN_DMICB | + PDN_DMICA | PDN); + break; + + case SND_SOC_BIAS_OFF: + /* Powerdown all ports, inputs and outputs */ + cs42l73_write(codec, CS42L73_PWRCTL3, + PDN_THMS | PDN_SPKLO | PDN_EAR | + PDN_SPK | PDN_LO | PDN_HP); + + cs42l73_write(codec, CS42L73_PWRCTL2, + PDN_MIC2_BIAS | PDN_MIC1_BIAS | + PDN_VSP | PDN_ASP_SDOUT | PDN_ASP_SDIN | + PDN_XSP_SDOUT | PDN_XSP_SDIN); + + cs42l73_write(codec, CS42L73_PWRCTL1, + PDN_ADCB | PDN_ADCA | PDN_DMICB | + PDN_DMICA | PDN); + + cs42l73_write(codec, CS42L73_DMMCC, dmmcc | MCLKDIS); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +/* + cs42l73_set_tristate() + Tristate xSP SDOUT +*/ +static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + int id = dai->id; + + u8 sp = cs42l73_read(codec, CS42L73_SPC(id)) & 0x7F; + + return cs42l73_write(codec, CS42L73_SPC(id), sp | (tristate << 7)); +} + +static void cs42l73_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + int id = dai->id; + struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec); + priv->config[id].srate = 0; + cs42l73_update_asrc(codec,id,0); +} + + +static struct snd_pcm_hw_constraint_list constraints_12_24 = { + .count = ARRAY_SIZE(cs42l73_asrc_rates), + .list = cs42l73_asrc_rates, +}; + +static int cs42l73_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_12_24); + return 0; +} + +/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */ +#define CS42L73_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) + + +#define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops cs42l73_ops = { + .startup = cs42l73_pcm_startup, + .hw_params = cs42l73_pcm_hw_params, + .set_fmt = cs42l73_set_dai_fmt, + .set_sysclk = cs42l73_set_sysclk, + .set_clkdiv = cs42l73_set_clkdiv, + .set_tristate = cs42l73_set_tristate, + .shutdown = cs42l73_shutdown, +}; + +struct snd_soc_dai_driver cs42l73_dai[] = { + { + .name = "cs42l73-xsp", + .id = CS42L73_XSP, + .playback = { + .stream_name = "XSP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L73_RATES, + .formats = CS42L73_FORMATS,}, + + .capture = { + .stream_name = "XSP Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L73_RATES, + .formats = CS42L73_FORMATS,}, + + .ops = &cs42l73_ops, + .symmetric_rates = 1, + }, + { + .name = "cs42l73-asp", + .id = CS42L73_ASP, + .playback = { + .stream_name = "ASP Playback", + .channels_min = 2, + .channels_max = 2, + .rates = CS42L73_RATES, + .formats = CS42L73_FORMATS,}, + .capture = { + .stream_name = "ASP Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS42L73_RATES, + .formats = CS42L73_FORMATS,}, + .ops = &cs42l73_ops, + .symmetric_rates = 1, + }, + { + .name = "cs42l73-vsp", + .id = CS42L73_VSP, + .playback = { + .stream_name = "VSP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L73_RATES, + .formats = CS42L73_FORMATS,}, + .capture = { + .stream_name = "VSP Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L73_RATES, + .formats = CS42L73_FORMATS,}, + .ops = &cs42l73_ops, + .symmetric_rates = 1, + } +}; + +static int cs42l73_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int cs42l73_resume(struct snd_soc_codec *codec) +{ + int i; + u8 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware */ + for (i = CS42L73_PWRCTL1; i < ARRAY_SIZE(cs42l73_reg); i++) { + cs42l73_write(codec, i, cache[i]); + } + + cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static int cs42l73_probe(struct snd_soc_codec *codec) +{ + int ret, i; + unsigned int devid = 0; + struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec); + + codec->control_data = cs42l73->control_data; + codec->hw_write = (hw_write_t)i2c_master_send; + + cs42l73_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* initialize codec */ + ret = cs42l73_read(codec, CS42L73_DEVID_AB); + devid = (ret & 0xFF) << 12; + + ret = cs42l73_read(codec, CS42L73_DEVID_CD); + devid |= (ret & 0xFF) << 4; + + ret = cs42l73_read(codec, CS42L73_DEVID_E); + devid |= (ret & 0xF0) >> 4; + + + if (devid != CS42L73_DEVID) { + dev_err(codec->dev, + "CS42L73 Device ID (%X). Expected %X\n", + devid, CS42L73_DEVID); + return ret; + } + + ret = cs42l73_read(codec, CS42L73_REVID); + if (ret < 0) { + dev_err(codec->dev, "Get Revision ID failed\n"); + return ret; + } + + dev_info(codec->dev, + "Cirrus Logic CS42L73, Revision: %02X\n", ret & 0xFF); + + cs42l73->mclksel = CS42L73_CLKID_MCLK1; /* MCLK1 as master clk */ + cs42l73->mclk = 0; + + for (i = CS42L73_PWRCTL1; i < CS42L73_IS1; i++) + cs42l73_write(codec, i, cs42l73_reg[i]); + + snd_soc_add_controls(codec, cs42l73_snd_controls, + ARRAY_SIZE(cs42l73_snd_controls)); + + cs42l73_add_widgets(codec); + + return ret; +} + +static int cs42l73_remove(struct snd_soc_codec *codec) +{ + cs42l73_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +struct snd_soc_codec_driver soc_codec_dev_cs42l73 = { + .probe = cs42l73_probe, + .remove = cs42l73_remove, + .suspend = cs42l73_suspend, + .resume = cs42l73_resume, + .write = cs42l73_write, + .read = cs42l73_read, + .set_bias_level = cs42l73_set_bias_level, + .reg_cache_size = CS42L73_CACHEREGNUM, + .reg_cache_default = cs42l73_reg, +}; + +static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs42l73_private *cs42l73; + int ret; + + cs42l73 = kzalloc(sizeof(struct cs42l73_private), GFP_KERNEL); + if (!cs42l73) { + dev_err(&i2c_client->dev, "could not allocate codec\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c_client, cs42l73); + cs42l73->control_data = i2c_client; + cs42l73->control_type = SND_SOC_I2C; + + + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs42l73, cs42l73_dai, ARRAY_SIZE(cs42l73_dai)); + if (ret < 0) + kfree(cs42l73); + 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); + kfree(cs42l73); + + return 0; +} + +static const struct i2c_device_id cs42l73_id[] = { + {"cs42l73", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs42l73_id); + +static struct i2c_driver cs42l73_i2c_driver = { + .driver = { + .name = "cs42l73", + .owner = THIS_MODULE, + }, + .id_table = cs42l73_id, + .probe = cs42l73_i2c_probe, + .remove = __devexit_p(cs42l73_i2c_remove), + +}; + +static int __init cs42l73_modinit(void) +{ + int ret; + ret = i2c_add_driver(&cs42l73_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "%s: can't add i2c driver\n", __func__); + return ret; + } + return 0; +} + +module_init(cs42l73_modinit); + +static void __exit cs42l73_exit(void) +{ + i2c_del_driver(&cs42l73_i2c_driver); +} + +module_exit(cs42l73_exit); + +MODULE_DESCRIPTION("ASoC CS42L73 driver"); +MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, office@nucleusys.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h new file mode 100644 index 0000000..c96843f --- /dev/null +++ b/sound/soc/codecs/cs42l73.h @@ -0,0 +1,225 @@ +/* + * ALSA SoC CS42L73 codec driver + * + * Copyright 2010 Cirrus Logic, Inc. + * + * Author: Georgi Vlaev office@nucleusys.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __CS42L73_H__ +#define __CS42L73_H__ + +/* I2C Registers */ +/* I2C Address: 1001010[R/W] - 10010100 = 0x94(Write); 10010101 = 0x95(Read) */ +#define CS42L73_CHIP_ID 0x4a +#define CS42L73_DEVID_AB 0x01 /* Device ID A & B [RO]. */ +#define CS42L73_DEVID_CD 0x02 /* Device ID C & D [RO]. */ +#define CS42L73_DEVID_E 0x03 /* Device ID E [RO]. */ +#define CS42L73_REVID 0x05 /* Revision ID [RO]. */ +#define CS42L73_PWRCTL1 0x06 /* Power Control 1. */ +#define CS42L73_PWRCTL2 0x07 /* Power Control 2. */ +#define CS42L73_PWRCTL3 0x08 /* Power Control 3, Thermal Overload Threshold. */ +#define CS42L73_CPFCHC 0x09 /* Charge Pump Freq. & Class H Control. */ +#define CS42L73_OLMBMSDC 0x0A /* Output Load, MIC Bias, & MIC2 Short Detect Config. */ +#define CS42L73_DMMCC 0x0B /* Digital MIC & Master Clock Control. */ +#define CS42L73_XSPC 0x0C /* Auxiliary Serial Port (XSP) Control. */ +#define CS42L73_XSPMMCC 0x0D /* XSP Master Mode Clocking Control. */ +#define CS42L73_ASPC 0x0E /* Audio Serial Port (ASP) Control. */ +#define CS42L73_ASPMMCC 0x0F /* ASP Master Mode Clocking Control. */ +#define CS42L73_VSPC 0x10 /* Voice Serial Port (VSP) Control. */ +#define CS42L73_VSPMMCC 0x11 /* VSP Master Mode Clocking Control. */ +#define CS42L73_VXSPFS 0x12 /* VSP & XSP Sample Rate. */ +#define CS42L73_MIOPC 0x13 /* Misc. Input & Output Path Control. */ +#define CS42L73_ADCIPC 0x14 /* ADC/IP Control. */ +#define CS42L73_MICAPREPGAAVOL 0x15 /* MIC 1 [A] PreAmp, PGAA Vol. */ +#define CS42L73_MICBPREPGABVOL 0x16 /* MIC 2 [B] PreAmp, PGAB Vol. */ +#define CS42L73_IPADVOL 0x17 /* Input Pat7h A Digital Volume. */ +#define CS42L73_IPBDVOL 0x18 /* Input Path B Digital Volume. */ +#define CS42L73_PBDC 0x19 /* Playback Digital Control. */ +#define CS42L73_HLADVOL 0x1A /* Headphone/Line A Out Digital Vol. */ +#define CS42L73_HLBDVOL 0x1B /* Headphone/Line B Out Digital Vol. */ +#define CS42L73_SPKDVOL 0x1C /* Speakerphone Out [A] Digital Vol. */ +#define CS42L73_ESLDVOL 0x1D /* Ear/Speakerphone Line Out [B] Digital Vol. */ +#define CS42L73_HPAAVOL 0x1E /* Headphone A Analog Volume. */ +#define CS42L73_HPBAVOL 0x1F /* Headphone B Analog Volume. */ +#define CS42L73_LOAAVOL 0x20 /* Line Out A Analog Volume. */ +#define CS42L73_LOBAVOL 0x21 /* Line Out B Analog Volume. */ +#define CS42L73_STRINV 0x22 /* Stereo Input Path Adv. Vol. */ +#define CS42L73_XSPINV 0x23 /* Auxiliary Serial Port Input Advisory Vol. */ +#define CS42L73_ASPINV 0x24 /* Audio Serial Port Input Advisory Vol. */ +#define CS42L73_VSPINV 0x25 /* Voice Serial Port Input Advisory Vol. */ +#define CS42L73_LIMARATEHL 0x26 /* Limiter Attack Rate Headphone/Line. */ +#define CS42L73_LIMRRATEHL 0x27 /* Limiter Ctl, Rel.Rate Headphone/Line. */ +#define CS42L73_LMAXHL 0x28 /* Limiter Thresholds Headphone/Line. */ +#define CS42L73_LIMARATESPK 0x29 /* Limiter Attack Rate Speakerphone [A]. */ +#define CS42L73_LIMRRATESPK 0x2A /* Limiter Ctl,Release Rate Speakerph. [A]. */ +#define CS42L73_LMAXSPK 0x2B /* Limiter Thresholds Speakerphone [A]. */ +#define CS42L73_LIMARATEESL 0x2C /* Limiter Attack Rate Ear/Speakerph.Line [B]. */ +#define CS42L73_LIMRRATEESL 0x2D /* Limiter Ctl,Release Rate Ear/Speakerphone Line [B]. */ +#define CS42L73_LMAXESL 0x2E /* Limiter Thresholds Ear/Speakerph. Line [B]. */ +#define CS42L73_ALCARATE 0x2F /* ALC Enable, Attack Rate AB. */ +#define CS42L73_ALCRRATE 0x30 /* ALC Release Rate AB. */ +#define CS42L73_ALCMINMAX 0x31 /* ALC Thresholds AB. */ +#define CS42L73_NGCAB 0x32 /* Noise Gate Ctl AB. */ +#define CS42L73_ALCNGMC 0x33 /* ALC & Noise Gate Misc Ctl. */ +#define CS42L73_MIXERCTL 0x34 /* Mixer Control. */ +#define CS42L73_HLAIPAA 0x35 /* HP/LO Left Mixer: Input Path Left Atten. */ +#define CS42L73_HLBIPBA 0x36 /* HP/LO Right Mixer: Input Path Rt. Atten. */ +#define CS42L73_HLAXSPAA 0x37 /* HP/LO Left Mixer: XSP Left Attenuation. */ +#define CS42L73_HLBXSPBA 0x38 /* HP/LO Right Mixer: XSP Rt. Attenuation. */ +#define CS42L73_HLAASPAA 0x39 /* HP/LO Left Mixer: ASP Left Attenuation. */ +#define CS42L73_HLBASPBA 0x3A /* HP/LO Right Mixer: ASP Rt. Attenuation. */ +#define CS42L73_HLAVSPMA 0x3B /* HP/LO Left Mixer: VSP Mono Atten. */ +#define CS42L73_HLBVSPMA 0x3C /* HP/LO Right Mixer: VSP Mono Atten. */ +#define CS42L73_XSPAIPAA 0x3D /* XSP Left Mixer: Input Path Left Attenuation. */ +#define CS42L73_XSPBIPBA 0x3E /* XSP Rt. Mixer: Input Path Right Attenuation. */ +#define CS42L73_XSPAXSPAA 0x3F /* XSP Left Mixer: XSP Left Attenuation. */ +#define CS42L73_XSPBXSPBA 0x40 /* XSP Rt. Mixer: XSP Right Attenuation. */ +#define CS42L73_XSPAASPAA 0x41 /* XSP Left Mixer: ASP Left Attenuation. */ +#define CS42L73_XSPAASPBA 0x42 /* XSP Rt. Mixer: ASP Right Attenuation. */ +#define CS42L73_XSPAVSPMA 0x43 /* XSP Left Mixer: VSP Mono Attenuation. */ +#define CS42L73_XSPBVSPMA 0x44 /* XSP Rt. Mixer: VSP Mono Attenuation. */ +#define CS42L73_ASPAIPAA 0x45 /* ASP Left Mixer: Input Path Left Attenuation. */ +#define CS42L73_ASPBIPBA 0x46 /* ASP Rt. Mixer: Input Path Right Attenuation. */ +#define CS42L73_ASPAXSPAA 0x47 /* ASP Left Mixer: XSP Left Attenuation. */ +#define CS42L73_ASPBXSPBA 0x48 /* ASP Rt. Mixer: XSP Right Attenuation. */ +#define CS42L73_ASPAASPAA 0x49 /* ASP Left Mixer: ASP Left Attenuation. */ +#define CS42L73_ASPBASPBA 0x4A /* ASP Rt. Mixer: ASP Right Attenuation. */ +#define CS42L73_ASPAVSPMA 0x4B /* ASP Left Mixer: VSP Mono Attenuation. */ +#define CS42L73_ASPBVSPMA 0x4C /* ASP Rt. Mixer: VSP Mono Attenuation. */ +#define CS42L73_VSPAIPAA 0x4D /* VSP Left Mixer: Input Path Left Attenuation. */ +#define CS42L73_VSPBIPBA 0x4E /* VSP Rt. Mixer: Input Path Right Attenuation. */ +#define CS42L73_VSPAXSPAA 0x4F /* VSP Left Mixer: XSP Left Attenuation. */ +#define CS42L73_VSPBXSPBA 0x50 /* VSP Rt. Mixer: XSP Right Attenuation. */ +#define CS42L73_VSPAASPAA 0x51 /* VSP Left Mixer: ASP Left Attenuation. */ +#define CS42L73_VSPBASPBA 0x52 /* VSP Rt. Mixer: ASP Right Attenuation. */ +#define CS42L73_VSPAVSPMA 0x53 /* VSP Left Mixer: VSP Mono Attenuation.*/ +#define CS42L73_VSPBVSPMA 0x54 /* VSP Rt. Mixer: VSP Mono Attenuation. */ +#define CS42L73_MMIXCTL 0x55 /* Mono Mixer Controls. */ +#define CS42L73_SPKMIPMA 0x56 /* SPK Mono Mixer: In. Path Mono Atten. */ +#define CS42L73_SPKMXSPA 0x57 /* SPK Mono Mixer: XSP Mono/L/R Att. */ +#define CS42L73_SPKMASPA 0x58 /* SPK Mono Mixer: ASP Mono/L/R Att. */ +#define CS42L73_SPKMVSPMA 0x59 /* SPK Mono Mixer: VSP Mono Atten. */ +#define CS42L73_ESLMIPMA 0x5A /* Ear/SpLO Mono Mixer: In. Path Mono Atten. */ +#define CS42L73_ESLMXSPA 0x5B /* Ear/SpLO Mono Mixer: XSP Mono/L/R Att. */ +#define CS42L73_ESLMASPA 0x5C /* Ear/SpLO Mono Mixer: ASP Mono/L/R Att. */ +#define CS42L73_ESLMVSPMA 0x5D /* Ear/SpLO Mono Mixer: VSP Mono Atten. */ +#define CS42L73_IM1 0x5E /* Interrupt Mask 1. */ +#define CS42L73_IM2 0x5F /* Interrupt Mask 2. */ +#define CS42L73_IS1 0x60 /* Interrupt Status 1 [RO]. */ +#define CS42L73_IS2 0x61 /* Interrupt Status 2 [RO]. */ + +#define CS42L73_CACHEREGNUM (CS42L73_IS2 + 1) + +/* Bitfield Definitions */ + +/* CS42L73_PWRCTL1 */ +#define PDN_ADCB (1 << 7) +#define PDN_DMICB (1 << 6) +#define PDN_ADCA (1 << 5) +#define PDN_DMICA (1 << 4) +#define PDN_LDO (1 << 2) +#define DISCHG_FILT (1 << 1) +#define PDN (1 << 0) + +/* CS42L73_PWRCTL2 */ +#define PDN_MIC2_BIAS (1 << 7) +#define PDN_MIC1_BIAS (1 << 6) +#define PDN_VSP (1 << 4) +#define PDN_ASP_SDOUT (1 << 3) +#define PDN_ASP_SDIN (1 << 2) +#define PDN_XSP_SDOUT (1 << 1) +#define PDN_XSP_SDIN (1 << 0) + +/* CS42L73_PWRCTL3 */ +#define PDN_THMS (1 << 5) +#define PDN_SPKLO (1 << 4) +#define PDN_EAR (1 << 3) +#define PDN_SPK (1 << 2) +#define PDN_LO (1 << 1) +#define PDN_HP (1 << 0) + +/* Thermal Overload Detect. Requires interrupt ... */ +#define THMOVLD_150C 0 +#define THMOVLD_132C 1 +#define THMOVLD_115C 2 +#define THMOVLD_098C 3 + + +/* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */ +#define xSP_3ST (1 << 7) +#define xSPDIF_I2S 0 +#define xSPDIF_PCM (1 << 6) +#define xPCM_MODE0 0 +#define xPCM_MODE1 1 +#define xPCM_MODE2 2 +#define xPCM_BO_MSBLSB 0 +#define xPCM_BO_LSBMSB 1 +#define xMCK_SCLK_64FS 0 +#define xMCK_SCLK_MCLK 2 +#define xMCK_SCLK_PREMCLK 3 + +/* CS42L73_xSPMMCC */ +#define MS_MASTER (1 << 7) + + +/* CS42L73_DMMCC */ +#define MCLKDIS (1 << 0) +#define MCLKSEL_MCLK2 (1 << 4) +#define MCLKSEL_MCLK1 (0 << 4) + +/* CS42L73 MCLK derived from MCLK1 or MCLK2 */ +#define CS42L73_CLKID_MCLK1 0 +#define CS42L73_CLKID_MCLK2 1 + +#define CS42L73_MCLKXDIV 0 +#define CS42L73_MMCCDIV 1 + +#define CS42L73_XSP 0 +#define CS42L73_ASP 1 +#define CS42L73_VSP 2 + +/* IS1, IM1 */ +#define MIC2_SDET (1 << 6) +#define THMOVLD (1 << 4) +#define DIGMIXOVFL (1 << 3) +#define IPBOVFL (1 << 1) +#define IPAOVFL (1 << 0) + +/* Analog Softramp */ +#define ANLGOSFT (1 << 0) + +/* HP A/B Mute */ +#define HPMUTE (1 << 7) +/* LO A/B Mute */ +#define LOMUTE (1 << 7) +/* SPK Digital Mute */ +#define SPKDMUTE (1 << 2) + +/* Misc defines for codec */ +#define CS42L73_RESET_GPIO 143 + +#define CS42L73_DEVID 0x00042A73 +#define CS42L73_MCLKX_MIN 5644800 +#define CS42L73_MCLKX_MAX 38400000 + +#define CS42L73_SPC(id) (CS42L73_XSPC + (id << 1)) +#define CS42L73_MMCC(id) (CS42L73_XSPMMCC + (id << 1)) +#define CS42L73_SPFS(id) ((id == CS42L73_ASP) ? CS42L73_ASPC : CS42L73_VXSPFS) + +#endif /* __CS42L73_H__ */