[alsa-devel] [PATCH] ASoC: Add support for Cirrus Logic CS42L73 Codec
Brian Austin
brian.austin at cirrus.com
Thu Sep 15 16:40:48 CEST 2011
---
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 at nucleusys.com>
+ * Brian Austin, Cirrus Logic Inc, <brian.austin at 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 at 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 at 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__ */
--
1.7.5.4
More information about the Alsa-devel
mailing list