summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorVinod G <vinodg@nvidia.com>2011-11-29 17:32:11 -0800
committerVarun Wadekar <vwadekar@nvidia.com>2011-12-08 17:04:07 +0530
commit5fac479d8dddb8b9bfada756379357726dc57ed0 (patch)
treea44fde297338237951eff0cbb23aa0cdce8497ef /sound
parent8d3d9f8fbde05efd16b159c3d059d643e74746fd (diff)
kernel: sound: Adding TI codec support
Adding the code for supporting TI AIC3262 codec. bug 816608 Change-Id: I19c3e03e8fd442c0c19a72a7efeddcd0ca05a7e1 Reviewed-on: http://git-master/r/67279 Reviewed-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com> Tested-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com> Reviewed-by: Scott Peterson <speterson@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile3
-rwxr-xr-xsound/soc/codecs/tlv320aic326x.c3896
-rwxr-xr-xsound/soc/codecs/tlv320aic326x.h629
-rwxr-xr-xsound/soc/codecs/tlv320aic326x_mini-dsp.c1587
-rwxr-xr-xsound/soc/codecs/tlv320aic326x_minidsp_config.c410
6 files changed, 6529 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 665d9240c4ae..f14d61e2834f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -48,6 +48,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TVL320AIC32X4 if I2C
+ select SND_SOC_TLV320AIC326X if I2C
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
@@ -246,6 +247,9 @@ config SND_SOC_TVL320AIC32X4
config SND_SOC_TLV320AIC3X
tristate
+config SND_SOC_TLV320AIC326X
+ tristate "TI AIC326x Codec"
+
config SND_SOC_TLV320DAC33
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5119a7e2c1a8..e866eee843b5 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -35,6 +35,8 @@ snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320aic326x-objs := tlv320aic326x.o tlv320aic326x_minidsp_config.o
+snd-soc-tlv320aic326x-objs += tlv320aic326x_mini-dsp.o
snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
@@ -133,6 +135,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o
+obj-$(CONFIG_SND_SOC_TLV320AIC326X) += snd-soc-tlv320aic326x.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
diff --git a/sound/soc/codecs/tlv320aic326x.c b/sound/soc/codecs/tlv320aic326x.c
new file mode 100755
index 000000000000..40b6093737e4
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic326x.c
@@ -0,0 +1,3896 @@
+/*
+* linux/sound/soc/codecs/tlv320aic3262.c
+*
+* Copyright (C) 2011 Mistral Solutions Pvt Ltd.
+*
+* Based on sound/soc/codecs/tlv320aic3262.c
+*
+* This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*
+* The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio
+* codec with digital microphone inputs and programmable outputs.
+*
+* History:
+*
+* Rev 0.1 ASoC driver support Mistral 20-01-2011
+*
+* The AIC325x ASoC driver is ported for the codec AIC3262.
+* Rev 0.2 ASoC driver support Mistral 21-03-2011
+* The AIC326x ASoC driver is updated abe changes.
+*
+* Rev 0.3 ASoC driver support Mistral 12.09.2011
+* fixed the compilation issues for Whistler support
+*
+* Rev 0.4 ASoC driver support Mistral 27.09.2011
+* The AIC326x driver ported for Nvidia cardhu.
+*
+* Rev 0.5 Modified to support Multiple ASI Ports 08-Nov-2011
+* Driver updated to support ASI Ports of AIC3262
+*
+* Modified by Nvidia 23-Nov-2011 for K39 ASoC changes.
+*/
+
+/*
+ *****************************************************************************
+ * INCLUDES
+ *****************************************************************************
+ */
+#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/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+#include <sound/tlv320aic326x.h>
+#include <sound/jack.h>
+
+#include "tlv320aic326x.h"
+
+/*
+ *****************************************************************************
+ * Global Variable
+ *****************************************************************************
+ */
+static u8 aic3262_reg_ctl;
+
+u8 dac_reg = 0, adc_gain = 0, hpl = 0, hpr = 0;
+u8 rec_amp = 0, rampr = 0, spk_amp = 0;
+/* whenever aplay/arecord is run, aic3262_hw_params() function gets called.
+ * This function reprograms the clock dividers etc. this flag can be used to
+ * disable this when the clock dividers are programmed by pps config file
+ */
+static int soc_static_freq_config = 1;
+
+/*
+ *****************************************************************************
+ * Macros
+ *****************************************************************************
+ */
+
+/* ASoC Widget Control definition for a single Register based Control */
+#define SOC_SINGLE_AIC3262(xname) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = __new_control_info, .get = __new_control_get,\
+ .put = __new_control_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+}
+#define SOC_SINGLE_N(xname, xreg, xshift, xmax, xinvert) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = n_control_info, .get = n_control_get,\
+ .put = n_control_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .private_value = ((unsigned long)&(struct soc_mixer_control)) \
+ {.reg = xreg, .shift = xshift, .rshift = xshift, .max = xmax, \
+ .invert = xinvert} }
+
+/* ASoC Widget Control definition for a Double Register based Control */
+
+#define SOC_DOUBLE_R_N(xname, reg_left, reg_right, xshift, xmax, xinvert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = snd_soc_info_volsw_2r_n, \
+ .get = snd_soc_get_volsw_2r_n, .put = snd_soc_put_volsw_2r_n, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+ .max = xmax, .invert = xinvert} }
+
+#define SND_SOC_DAPM_SWITCH_N(wname, wreg, wshift, winvert) \
+{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift,\
+ .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
+/*
+ *****************************************************************************
+ * Function Prototype
+ *****************************************************************************
+ */
+static int aic3262_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+
+static int aic3262_mute(struct snd_soc_dai *dai, int mute);
+
+static int aic3262_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir);
+
+static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
+
+static int aic3262_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level);
+
+static int __new_control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+
+static int __new_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static int __new_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static inline int aic3262_get_divs(int mclk, int rate);
+
+static int aic3262_multi_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+
+static int aic3262_multi_i2s_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir);
+static int aic3262_multi_i2s_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out);
+
+static int aic3262_multi_i2s_asi1_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt);
+
+static int aic3262_multi_i2s_asi2_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt);
+
+static int aic3262_multi_i2s_asi3_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt);
+
+static int aic3262_multi_i2s_asi1_mute(struct snd_soc_dai *dai, int mute);
+
+static int aic3262_multi_i2s_asi2_mute(struct snd_soc_dai *dai, int mute);
+
+static int aic3262_multi_i2s_asi3_mute(struct snd_soc_dai *dai, int mute);
+
+static const char *wclk1_pincontrol[] = {
+ "ASI1 Word Clock Input/Output", "CLKOUT output"};
+static const char *dout1_pincontrol[] = {
+ "disabled", "ASI1 data output", "gpio", "clock out",
+ "INT1", "INT2", "SAR ADC interrupt"};
+
+static const char *din1_pincontrol[] = {"disabled", "enabled"};
+
+static const char *wclk2_pincontrol[] = {
+ "diabled", "ASI1 secondary wclk", "general purpose input",
+ "general purpose output", "clkout", "INT1 interrupt",
+ "IN2 interrupt", "output digital microphone",
+ "SAR ADC interrupt", "data output for ASI1"};
+
+static const char *bclk2_pincontrol[] = {
+ "diabled", "ASI1 secondary wclk", "general purpose input",
+ "general purpose output", "clkout", "INT1 interrupt",
+ "IN2 interrupt", "output digital microphone",
+ "SAR ADC interrupt", "data output for ASI1"};
+
+static const char *dout2_pincontrol[] = {
+ "disabled", "ASI2 Data Output", "General Purpose Output",
+ "INT1 Interrupt", "INT2 Interrupt", "SAR ADC interrupt",
+ "Output for digital microphone", "Data Output for ASI1"};
+
+static const char *din2_pincontrol[] = {"disabled", "enabled"};
+
+static const char *wclk3_pincontrol[] = {
+ "Disabled", "ASI3 WCLK", "General Purpose Input",
+ "General Purpose output", "Data Output for ASI1"};
+
+static const char *bclk3_pincontrol[] = {
+ "Disabled", "ASI3 BCLK", "General Purpose Input",
+ "General Purpose output", "Data Output for ASI1"};
+
+static const char *dout3_pincontrol[] = {
+ "disabled", "ASI3 data ooutput", "General Purpose Output",
+ "ASI1 Word Clock Output", "Data Output for ASI1"};
+
+static const char *din3_pincontrol[] = {"disabled", "enabled"};
+
+static const char *clkin[] = {
+ "mclk1", "bclk1", "gpio1", "pll_clk", "bclk2", "gpi1",
+ "hf_ref_clk", "hf_osc_clk", "mclk2", "gpio2", "gpi2"};
+
+/* List of SOC_ENUM structures for the AIC3262 PIN Control Amixer Controls */
+static const struct soc_enum aic326x_enum[] = {
+ SOC_ENUM_SINGLE(WCLK1_PIN_CNTL_REG, 2, 2, wclk1_pincontrol),
+ SOC_ENUM_SINGLE(DOUT1_PIN_CNTL_REG, 1, 7, dout1_pincontrol),
+ SOC_ENUM_SINGLE(DIN1_PIN_CNTL_REG, 5, 2, din1_pincontrol),
+ SOC_ENUM_SINGLE(WCLK2_PIN_CNTL_REG, 2, 10, wclk2_pincontrol),
+ SOC_ENUM_SINGLE(BCLK2_PIN_CNTL_REG, 2, 10, bclk2_pincontrol),
+ SOC_ENUM_SINGLE(DOUT2_PIN_CNTL_REG, 1, 8, dout2_pincontrol),
+ SOC_ENUM_SINGLE(DIN2_PIN_CNTL_REG, 5, 2, din2_pincontrol),
+ SOC_ENUM_SINGLE(WCLK3_PIN_CNTL_REG, 2, 5, wclk3_pincontrol),
+ SOC_ENUM_SINGLE(BCLK3_PIN_CNTL_REG, 2, 5, bclk3_pincontrol),
+ SOC_ENUM_SINGLE(DOUT3_PIN_CNTL_REG, 1, 5, dout3_pincontrol),
+ SOC_ENUM_SINGLE(DIN3_PIN_CNTL_REG, 5, 2, din3_pincontrol),
+ SOC_ENUM_DOUBLE(DAC_ADC_CLKIN_REG, 0, 4, 11, clkin),
+};
+
+#ifdef DAC_INDEPENDENT_VOL
+/*
+ *----------------------------------------------------------------------------
+ * Function : n_control_info
+ * Purpose : This function is to initialize data for new control required to
+ * program the AIC3262 registers.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int n_control_info(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;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+
+ if (max == 1)
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = shift == rshift ? 1 : 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = max;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : n_control_get
+ * Purpose : This function is to read data of new control for
+ * program the AIC3262 registers.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int n_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u32 val;
+ unsigned short mask, shift;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ if (!strcmp(kcontrol->id.name, "Left DAC Volume")) {
+ mask = AIC3262_8BITS_MASK;
+ shift = 0;
+ val = aic3262_read(codec, mc->reg);
+ ucontrol->value.integer.value[0] =
+ (val <= 48) ? (val + 127) : (val - 129);
+ }
+ if (!strcmp(kcontrol->id.name, "Right DAC Volume")) {
+ mask = AIC3262_8BITS_MASK;
+ shift = 0;
+ val = aic3262_read(codec, mc->reg);
+ ucontrol->value.integer.value[0] =
+ (val <= 48) ? (val + 127) : (val - 129);
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_put
+ * Purpose : new_control_put is called to pass data from user/application to
+ * the driver.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int n_control_put(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;
+ u8 val, val_mask;
+ int reg, err;
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ DBG("n_control_put\n");
+ reg = mc->reg;
+ val = ucontrol->value.integer.value[0];
+ if (invert)
+ val = max - val;
+ if (!strcmp(kcontrol->id.name, "Left DAC Volume")) {
+ DBG("LDAC\n");
+ val = (val >= 127) ? (val - 127) : (val + 129);
+ val_mask = AIC3262_8BITS_MASK;
+ }
+ if (!strcmp(kcontrol->id.name, "Right DAC Volume")) {
+ DBG("RDAC\n");
+ val = (val >= 127) ? (val - 127) : (val + 129);
+ val_mask = AIC3262_8BITS_MASK;
+ }
+
+ err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+ if (err < 0) {
+ printk(KERN_ERR "Error while updating bits\n");
+ return err;
+ }
+
+ return 0;
+}
+#endif /*#ifdef DAC_INDEPENDENT_VOL*/
+/*
+ *------------------------------------------------------------------------------
+ * snd_soc_info_volsw_2r_n - double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double mixer control that
+ * spans 2 codec registers.
+ *
+ * Returns 0 for success.
+ *------------------------------------------------------------------------------
+ */
+int snd_soc_info_volsw_2r_n(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;
+}
+
+/*
+ *------------------------------------------------------------------------------
+ * snd_soc_get_volsw_2r_n - double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ *------------------------------------------------------------------------------
+ */
+int snd_soc_get_volsw_2r_n(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask;
+ unsigned int invert = mc->invert;
+ unsigned short val, val2;
+
+ if (!strcmp(kcontrol->id.name, "PCM Playback Volume")) {
+ mask = AIC3262_8BITS_MASK;
+ shift = 0;
+ } else if (!strcmp(kcontrol->id.name, "HP Driver Gain")) {
+ mask = 0x3F;
+ shift = 0;
+ } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) {
+ mask = 0x7F;
+ shift = 0;
+ } else if (!strcmp(kcontrol->id.name, "REC Driver Volume")) {
+ mask = 0x3F;
+ shift = 0;
+ } else if (!strcmp(kcontrol->id.name, "LO to HP Volume")) {
+ mask = 0x7F;
+ shift = 0;
+ } else if (!strcmp(kcontrol->id.name, "MA Volume")) {
+ mask = 0x7F;
+ shift = 0;
+ } else {
+ printk(KERN_ERR "Invalid kcontrol name\n");
+ return -1;
+ }
+
+ /* Read, update the corresponding Registers */
+ val = (snd_soc_read(codec, reg) >> shift) & mask;
+ val2 = (snd_soc_read(codec, reg2) >> shift) & mask;
+
+ if (!strcmp(kcontrol->id.name, "PCM Playback Volume")) {
+ ucontrol->value.integer.value[0] =
+ (val <= 48) ? (val + 127) : (val - 129);
+ ucontrol->value.integer.value[1] =
+ (val2 <= 48) ? (val2 + 127) : (val2 - 129);
+ } else if (!strcmp(kcontrol->id.name, "HP Driver Gain")) {
+ ucontrol->value.integer.value[0] =
+ (val >= 57) ? (val - 57) : (val + 7);
+ ucontrol->value.integer.value[1] =
+ (val2 >= 57) ? (val2 - 57) : (val2 + 7);
+ } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) {
+ ucontrol->value.integer.value[0] =
+ (val <= 40) ? (val + 24) : (val - 104);
+ ucontrol->value.integer.value[1] =
+ (val2 <= 40) ? (val2 + 24) : (val2 - 104);
+ } else if (!strcmp(kcontrol->id.name, "REC Driver Volume")) {
+ ucontrol->value.integer.value[0] = ((val >= 0) & (val <= 29)) ?
+ (val + 7) : (val - 57);
+ ucontrol->value.integer.value[1] = ((val2 >= 0) &
+ (val2 <= 29)) ? (val2 + 7) : (val2 - 57);
+
+ } else if (!strcmp(kcontrol->id.name, "LO to HP Volume")) {
+ ucontrol->value.integer.value[0] = ((val >= 0) & (val <= 116)) ?
+ (val + 1) : ((val == 127) ? (0) : (117));
+ ucontrol->value.integer.value[1] = ((val2 >= 0) & (val2 <= 116))
+ ? (val2 + 1) : ((val2 == 127) ? (0) : (117));
+
+ } else if (!strcmp(kcontrol->id.name, "MA Volume")) {
+ ucontrol->value.integer.value[0] = (val <= 40) ?
+ (41 - val) : (val = 0);
+ ucontrol->value.integer.value[1] = (val2 <= 40) ?
+ (41 - val2) : (val2 = 0);
+ }
+
+ if (invert) {
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] =
+ max - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+/*
+*-------------------------------------------------------------------------------
+* snd_soc_put_volsw_2r_n - double mixer set callback
+* @kcontrol: mixer control
+* @ucontrol: control element information
+*
+* Callback to set the value of a double mixer control that spans 2 registers.
+*
+* Returns 0 for success.
+*-------------------------------------------------------------------------------
+*/
+int snd_soc_put_volsw_2r_n(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask;
+ unsigned int invert = mc->invert;
+ int err;
+ unsigned short val, val2, val_mask;
+
+ mask = 0x00FF;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+ val2 = (ucontrol->value.integer.value[1] & mask);
+ if (invert) {
+ val = max - val;
+ val2 = max - val2;
+ }
+
+ /* Check for the string name of the kcontrol */
+ if (!strcmp(kcontrol->id.name, "PCM Playback Volume")) {
+ val = (val >= 127) ? (val - 127) : (val + 129);
+ val2 = (val2 >= 127) ? (val2 - 127) : (val2 + 129);
+ val_mask = AIC3262_8BITS_MASK; /* 8 bits */
+ } else if ((!strcmp(kcontrol->id.name, "HP Driver Gain")) ||
+ (!strcmp(kcontrol->id.name, "LO Driver Gain"))) {
+ val = (val <= 6) ? (val + 57) : (val - 7);
+ val2 = (val2 <= 6) ? (val2 + 57) : (val2 - 7);
+ val_mask = 0x3F; /* 6 bits */
+ DBG("val=%d, val2=%d", val, val2);
+ } else if (!strcmp(kcontrol->id.name, "PGA Capture Volume")) {
+ val = (val >= 24) ? ((val <= 64) ?
+ (val-24) : (40)) : (val + 104);
+ val2 = (val2 >= 24) ?
+ ((val2 <= 64) ? (val2 - 24) : (40)) : (val2 + 104);
+ val_mask = 0x7F; /* 7 bits */
+ } else if (!strcmp(kcontrol->id.name, "LO to REC Volume")) {
+
+ val = (val <= 116) ?
+ (val % 116) : ((val == 117) ? (127) : (117));
+ val2 = (val2 <= 116) ?
+ (val2 % 116) : ((val2 == 117) ? (127) : (117));
+ val_mask = 0x7F;
+ } else if (!strcmp(kcontrol->id.name, "REC Driver Volume")) {
+
+ val = (val <= 7) ? (val + 57) : ((val < 36) ? (val - 7) : (29));
+ val2 = (val2 <= 7) ?
+ (val2 + 57) : ((val2 < 36) ? (val2 - 7) : (29));
+ val_mask = 0x3F;
+ } else if (!strcmp(kcontrol->id.name, "LO to HP Volume")) {
+
+ val = ((val > 0) & (val <= 117)) ?
+ (val - 1) : ((val == 0) ? (127) : (116));
+ val2 = ((val2 > 0) & (val2 <= 117)) ?
+ (val2 - 1) : ((val2 == 0) ? (127) : (116));
+ val_mask = 0x7F;
+ } else if (!strcmp(kcontrol->id.name, "MA Volume")) {
+
+ val = ((val <= 41) & (val > 0)) ?
+ (41 - val) : ((val > 41) ? (val = 41) : (63));
+ val2 = ((val2 <= 41) & (val2 > 0)) ?
+ (41 - val2) : ((val2 > 41) ? (val2 = 41) : (63));
+ val_mask = 0x7F;
+ } else {
+ printk(KERN_ERR "Invalid control name\n");
+ return -1;
+ }
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
+ return err;
+}
+
+static int __new_control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 65535;
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_get
+ * Purpose : This function is to read data of new control for
+ * program the AIC3262 registers.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int __new_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u32 val;
+ val = aic3262_read(codec, aic3262_reg_ctl);
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_put
+ * Purpose : new_control_put is called to pass data from user/application to
+ * the driver.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int __new_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 data[2];
+ int ret = 0;
+
+ u32 data_from_user = ucontrol->value.integer.value[0];
+
+ aic3262_change_book(codec, 0);
+ aic3262_reg_ctl = data[0] = (u8) ((data_from_user & 0xFF00) >> 8);
+ data[1] = (u8) ((data_from_user & 0x00FF));
+
+ if (!data[0])
+ aic3262->page_no = data[1];
+
+ DBG("reg = %d val = %x\n", data[0], data[1]);
+
+ ret = snd_soc_write(codec, data[0], data[1]);
+ if (ret)
+ printk(KERN_ERR "Error in i2c write\n");
+
+ return ret;
+}
+
+
+/*
+ *****************************************************************************
+ * Structure Initialization
+ *****************************************************************************
+ */
+static const struct snd_kcontrol_new aic3262_snd_controls[] = {
+ /* Output */
+ #ifndef DAC_INDEPENDENT_VOL
+ /* sound new kcontrol for PCM Playback volume control */
+ SOC_DOUBLE_R_N("PCM Playback Volume", DAC_LVOL, DAC_RVOL, 0, 0xAf, 0),
+ #endif
+ /* sound new kcontrol for HP driver gain */
+ SOC_DOUBLE_R_N("HP Driver Gain", HPL_VOL, HPR_VOL, 0, 21, 0),
+ /* sound new kcontrol for SPK driver gain*/
+ SOC_DOUBLE("SPK Driver Gain", SPK_AMP_CNTL_R4, 0, 4, 5, 0),
+ /* Reveiver driver Gain volume*/
+ SOC_DOUBLE_R_N("REC Driver Volume",
+ REC_AMP_CNTL_R5, RAMPR_VOL, 0, 36, 0),
+ /* sound new kcontrol for PGA capture volume */
+ SOC_DOUBLE_R_N("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x3F,
+ 0),
+ SOC_DOUBLE("ADC Fine Gain ", ADC_FINE_GAIN, 4, 0, 4, 1),
+ SOC_DOUBLE_R("PGA MIC Volume", MICL_PGA, MICR_PGA, 0, 95, 0),
+
+ SOC_DOUBLE_R("LO to REC Volume", RAMP_CNTL_R1, RAMP_CNTL_R2, 0, 116, 1),
+ SOC_DOUBLE_R_N("LO to HP Volume",
+ HP_AMP_CNTL_R2, HP_AMP_CNTL_R3, 0, 117, 0),
+ SOC_DOUBLE_R("LO to SPK Volume",
+ SPK_AMP_CNTL_R2, SPK_AMP_CNTL_R3, 0, 116, 1),
+ SOC_DOUBLE("ADC channel mute", ADC_FINE_GAIN, 7, 3, 1, 0),
+
+ SOC_DOUBLE("DAC MUTE", DAC_MVOL_CONF, 2, 3, 1, 1),
+
+ /* sound new kcontrol for Programming the registers from user space */
+ SOC_SINGLE_AIC3262("Program Registers"),
+
+ SOC_SINGLE("RESET", RESET_REG, 0 , 1, 0),
+
+ SOC_SINGLE("DAC VOL SOFT STEPPING", DAC_MVOL_CONF, 0, 2, 0),
+
+ #ifdef DAC_INDEPENDENT_VOL
+ SOC_SINGLE_N("Left DAC Volume", DAC_LVOL, 0, 0xAF, 0),
+ SOC_SINGLE_N("Right DAC Volume", DAC_RVOL, 0, 0xAF, 0),
+ #endif
+
+ SOC_SINGLE("DAC AUTO MUTE CONTROL", DAC_MVOL_CONF, 4, 7, 0),
+ SOC_SINGLE("RIGHT MODULATOR SETUP", DAC_MVOL_CONF, 7, 1, 0),
+
+ SOC_SINGLE("ADC Volume soft stepping", ADC_CHANNEL_POW, 0, 3, 0),
+
+ SOC_DOUBLE_R_N("MA Volume",
+ LADC_PGA_MAL_VOL, RADC_PGA_MAR_VOL, 0, 42, 0),
+
+ SOC_SINGLE("Mic Bias ext independent enable", MIC_BIAS_CNTL, 7, 1, 0),
+ SOC_SINGLE("MICBIAS_EXT ON", MIC_BIAS_CNTL, 6, 1, 0),
+ SOC_SINGLE("MICBIAS EXT Power Level", MIC_BIAS_CNTL, 4, 3, 0),
+
+ SOC_SINGLE("MICBIAS_INT ON", MIC_BIAS_CNTL, 2, 1, 0),
+ SOC_SINGLE("MICBIAS INT Power Level", MIC_BIAS_CNTL, 0, 3, 0),
+
+ SOC_DOUBLE("DRC_EN_CTL", DRC_CNTL_R1, 6, 5, 1, 0),
+ SOC_SINGLE("DRC_THRESHOLD_LEVEL", DRC_CNTL_R1, 2, 7, 1),
+ SOC_SINGLE("DRC_HYSTERISIS_LEVEL", DRC_CNTL_R1, 0, 7, 0),
+
+ SOC_SINGLE("DRC_HOLD_LEVEL", DRC_CNTL_R2, 3, 0x0F, 0),
+ SOC_SINGLE("DRC_GAIN_RATE", DRC_CNTL_R2, 0, 4, 0),
+ SOC_SINGLE("DRC_ATTACK_RATE", DRC_CNTL_R3, 4, 0x0F, 1),
+ SOC_SINGLE("DRC_DECAY_RATE", DRC_CNTL_R3, 0, 0x0F, 1),
+
+ SOC_SINGLE("BEEP_GEN_EN", BEEP_CNTL_R1, 7, 1, 0),
+ SOC_DOUBLE_R("BEEP_VOL_CNTL", BEEP_CNTL_R1, BEEP_CNTL_R2, 0, 0x0F, 1),
+ SOC_SINGLE("BEEP_MAS_VOL", BEEP_CNTL_R2, 6, 3, 0),
+
+ SOC_DOUBLE_R("AGC_EN", LAGC_CNTL, RAGC_CNTL, 7, 1, 0),
+ SOC_DOUBLE_R("AGC_TARGET_LEVEL", LAGC_CNTL, RAGC_CNTL, 4, 7, 1),
+
+ SOC_DOUBLE_R("AGC_GAIN_HYSTERESIS", LAGC_CNTL, RAGC_CNTL, 0, 3, 0),
+ SOC_DOUBLE_R("AGC_HYSTERESIS", LAGC_CNTL_R2, RAGC_CNTL_R2, 6, 3, 0),
+ SOC_DOUBLE_R("AGC_NOISE_THRESHOLD",
+ LAGC_CNTL_R2, RAGC_CNTL_R2, 1, 31, 1),
+
+ SOC_DOUBLE_R("AGC_MAX_GAIN", LAGC_CNTL_R3, RAGC_CNTL_R3, 0, 116, 0),
+ SOC_DOUBLE_R("AGC_ATCK_TIME", LAGC_CNTL_R4, RAGC_CNTL_R4, 3, 31, 0),
+ SOC_DOUBLE_R("AGC_ATCK_SCALE_FACTOR",
+ LAGC_CNTL_R4, RAGC_CNTL_R4, 0, 7, 0),
+
+ SOC_DOUBLE_R("AGC_DECAY_TIME", LAGC_CNTL_R5, RAGC_CNTL_R5, 3, 31, 0),
+ SOC_DOUBLE_R("AGC_DECAY_SCALE_FACTOR",
+ LAGC_CNTL_R5, RAGC_CNTL_R5, 0, 7, 0),
+ SOC_DOUBLE_R("AGC_NOISE_DEB_TIME",
+ LAGC_CNTL_R6, RAGC_CNTL_R6, 0, 31, 0),
+
+ SOC_DOUBLE_R("AGC_SGL_DEB_TIME",
+ LAGC_CNTL_R7, RAGC_CNTL_R7, 0, 0x0F, 0),
+ SOC_SINGLE("DAC PRB Selection", DAC_PRB, 0, 25, 0),
+
+ SOC_SINGLE("INTERRUPT FLAG - Read only", 46, 0, 255, 0),
+ SOC_SINGLE("INTERRUPT STICKY FLAG - Read only", 44, 0, 255, 0),
+ SOC_SINGLE("INT1 CONTROL", 48, 0, 255, 0),
+ SOC_SINGLE("GPIO1 CONTROL", (PAGE_4 + 86), 0, 255, 0),
+ SOC_SINGLE("HP_DEPOP", HP_DEPOP, 0, 255, 0),
+
+ #if defined(FULL_IN_CNTL)
+
+ SOC_SINGLE("IN1L_2_LMPGA_P_CTL", LMIC_PGA_PIN, 6, 3, 0),
+ SOC_SINGLE("IN2L_2_LMPGA_P_CTL", LMIC_PGA_PIN, 4, 3, 0),
+ SOC_SINGLE("IN3L_2_LMPGA_P_CTL", LMIC_PGA_PIN, 2, 3, 0),
+ SOC_SINGLE("IN1R_2_LMPGA_P_CTL", LMIC_PGA_PIN, 0, 3, 0),
+
+ SOC_SINGLE("IN4L_2_LMPGA_P_CTL", LMIC_PGA_PM_IN4, 5, 1, 0),
+ SOC_SINGLE("IN4R_2_LMPGA_M_CTL", LMIC_PGA_PM_IN4, 4, 1, 0),
+
+ SOC_SINGLE("CM1_2_LMPGA_M_CTL", LMIC_PGA_MIN, 6, 3, 0),
+ SOC_SINGLE("IN2R_2_LMPGA_M_CTL", LMIC_PGA_MIN, 4, 3, 0),
+ SOC_SINGLE("IN3R_2_LMPGA_M_CTL", LMIC_PGA_MIN, 2, 3, 0),
+ SOC_SINGLE("CM2_2_LMPGA_M_CTL", LMIC_PGA_MIN, 0, 3, 0),
+
+ SOC_SINGLE("IN1R_2_RMPGA_P_CTL", RMIC_PGA_PIN, 6, 3, 0),
+ SOC_SINGLE("IN2R_2_RMPGA_P_CTL", RMIC_PGA_PIN, 4, 3, 0),
+ SOC_SINGLE("IN3R_2_RMPGA_P_CTL", RMIC_PGA_PIN, 2, 3, 0),
+ SOC_SINGLE("IN2L_2_RMPGA_P_CTL", RMIC_PGA_PIN, 0, 3, 0),
+
+ SOC_SINGLE("IN4R_2_RMPGA_P_CTL", RMIC_PGA_PM_IN4, 5, 1, 0),
+ SOC_SINGLE("IN4L_2_RMPGA_M_CTL", RMIC_PGA_PM_IN4, 4, 1, 0),
+
+ SOC_SINGLE("CM1_2_RMPGA_M_CTL", RMIC_PGA_MIN, 6, 3, 0),
+ SOC_SINGLE("IN1L_2_RMPGA_M_CTL", RMIC_PGA_MIN, 4, 3, 0),
+ SOC_SINGLE("IN3L_2_RMPGA_M_CTL", RMIC_PGA_MIN, 2, 3, 0),
+ SOC_SINGLE("CM2_2_RMPGA_M_CTL", RMIC_PGA_MIN, 0, 3, 0),
+
+ #endif
+
+ SOC_DOUBLE("MA_EN_CNTL", MA_CNTL, 3, 2, 1, 0),
+ SOC_DOUBLE("IN1 LO DIRECT BYPASS", MA_CNTL, 5, 4, 1, 0),
+ SOC_DOUBLE("IN1 LO BYPASS VOLUME" , LINE_AMP_CNTL_R2, 3, 0, 3, 1),
+ SOC_DOUBLE("MA LO BYPASS EN", LINE_AMP_CNTL_R2, 7, 6, 1, 0),
+
+ /* Pin control Macros */
+ SOC_ENUM("DOUT1 Pin Control", aic326x_enum[DOUT1_ENUM]),
+ SOC_ENUM("DIN1 Pin Control", aic326x_enum[DIN1_ENUM]),
+ SOC_ENUM("WCLK2 Pin Control", aic326x_enum[WCLK2_ENUM]),
+ SOC_ENUM("BCLK2 Pin Control", aic326x_enum[BCLK2_ENUM]),
+ SOC_ENUM("DOUT2 Pin Control", aic326x_enum[DOUT2_ENUM]),
+ SOC_ENUM("DIN2 Pin Control", aic326x_enum[DIN2_ENUM]),
+ SOC_ENUM("WCLK3 Pin Control", aic326x_enum[WCLK3_ENUM]),
+ SOC_ENUM("BCLK3 Pin Control", aic326x_enum[BCLK3_ENUM]),
+ SOC_ENUM("DOUT3 Pin Control", aic326x_enum[DOUT3_ENUM]),
+ SOC_ENUM("DIN3 Pin Control", aic326x_enum[DIN3_ENUM]),
+ SOC_ENUM("DAC CLK IN", aic326x_enum[CLKIN_ENUM]),
+ SOC_ENUM("ADC CLK IN", aic326x_enum[CLKIN_ENUM]),
+
+};
+
+/* the sturcture contains the different values for mclk */
+static const struct aic3262_rate_divs aic3262_divs[] = {
+/*
+ * mclk, rate, p_val, pll_j, pll_d, dosr, ndac, mdac, aosr, nadc, madc, blck_N,
+ * codec_speficic_initializations
+ */
+ /* 8k rate */
+#ifdef CONFIG_MINI_DSP
+ {12000000, 8000, 1, 8, 1920, 768, 8, 2, 128, 8, 12, 4,
+ {{0, 60, 0}, {0, 61, 0} } },
+#else
+ {12000000, 8000, 1, 8, 1920, 128, 12, 8, 128, 8, 6, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 8000, 1, 1, 3333, 128, 12, 8, 128, 8, 6, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 8000, 1, 4, 96, 128, 12, 8, 128, 12, 8, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+#endif
+ /* 11.025k rate */
+ {12000000, 11025, 1, 1, 8816, 1024, 8, 2, 128, 8, 2, 48,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 11025, 1, 1, 8375, 1024, 8, 2, 128, 8, 2, 48,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 11025, 1, 3, 7632, 128, 8, 8, 128, 8, 8, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+
+ /* 16k rate */
+#ifdef CONFIG_MINI_DSP
+ {12000000, 16000, 1, 8, 1920, 384, 4, 4, 128, 4, 12, 12,
+ {{0, 60, 0}, {0, 61, 0} } },
+#else
+ {12000000, 16000, 1, 8, 1920, 128, 8, 6, 128, 8, 6, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 16000, 1, 2, 6667, 128, 8, 6, 128, 8, 6, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 16000, 1, 4, 96, 128, 8, 6, 128, 8, 6, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+#endif
+ /* 22.05k rate */
+ {12000000, 22050, 1, 3, 7632, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 22050, 1, 3, 675, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 22050, 1, 3, 7632, 128, 8, 3, 128, 8, 3, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ /* 32k rate */
+ {12000000, 32000, 1, 5, 4613, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 32000, 1, 5, 3333, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 32000, 1, 4, 96, 128, 6, 4, 128, 6, 4, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+
+#ifdef CONFIG_MINI_DSP
+ {12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4,
+ {{0, 60, 0}, {0, 61, 0} } },
+ {12288000, 44100, 1, 7, 3548, 128, 2, 8, 128, 8, 2, 4,
+ {{0, 60, 0}, {0, 61, 0} } },
+#else
+ /* 44.1k rate */
+ {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 44100, 1, 7, 3548, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 44100, 1, 3, 7632, 128, 4, 4, 64, 4, 4, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+#endif
+
+#ifdef CONFIG_MINI_DSP
+ {12288000, 48000, 1, 8, 52, 128, 2, 8, 128, 2, 8, 4,
+ {{0, 60, 0}, {0, 61, 0} } },
+#else
+ /* 48k rate */
+ {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {12288000, 48000, 1, 8, 52, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+ {24000000, 48000, 1, 4, 960, 128, 4, 4, 128, 4, 4, 4,
+ {{0, 60, 1}, {0, 61, 1} } },
+#endif
+
+ /*96k rate */
+ {12000000, 96000, 1, 16, 3840, 128, 8, 2, 128, 8, 2 , 4,
+ {{0, 60, 7}, {0, 61, 7} } },
+ {24000000, 96000, 1, 4, 960, 128, 4, 2, 128, 4, 2, 2,
+ {{0, 60, 7}, {0, 61, 7} } },
+ /*192k */
+ {12000000, 192000, 1, 32, 7680, 128, 8, 2, 128, 8, 2, 4,
+ {{0, 60, 17}, {0, 61, 13} } },
+ {24000000, 192000, 1, 4, 960, 128, 2, 2, 128, 2, 2, 4,
+ {{0, 60, 17}, {0, 61, 13} } },
+};
+
+
+
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_dump_regs
+* Purpose : This function is to mute or unmute the left and right DAC
+*
+*----------------------------------------------------------------------------
+*/
+static void aic3262_multi_i2s_dump_regs(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ unsigned int counter;
+
+ DBG(KERN_INFO "#%s: Dai Active %d ASI%d REGS DUMP\n",
+ __func__, aic3262->active_count, dai->id);
+
+ aic3262_change_page(codec, 0);
+ aic3262_change_book(codec, 0);
+
+ DBG(KERN_INFO "#Page0 REGS..\n");
+ for (counter = 0; counter < 85; counter++) {
+ DBG(KERN_INFO "#%2d -> 0x%x\n", counter,
+ aic3262_read(codec, counter));
+ }
+
+ /*DBG(KERN_INFO "#Page1 REGS..\n");
+ for (counter = 128; counter < 176; counter++) {
+ DBG(KERN_INFO "#%2d -> 0x%x\n", (counter % 128),
+ aic3262_read(codec, counter));
+ }*/
+
+ DBG(KERN_INFO "#Page4 REGS..\n");
+ for (counter = 512; counter < 631; counter++) {
+ DBG(KERN_INFO "#%2d -> 0x%x\n",
+ (counter % 128), aic3262_read(codec, counter));
+ }
+
+ for (counter = 0; counter < MAX_ASI_COUNT; counter++) {
+ DBG(KERN_INFO "#ASI%d Frame %s @ %dHz Playback %d Record %d\n",
+ (counter + 1),
+ (aic3262->asiCtxt[counter].master == 1) ? "Master" : "Slave",
+ aic3262->asiCtxt[counter].sampling_rate,
+ aic3262->asiCtxt[counter].playback_mode,
+ aic3262->asiCtxt[counter].capture_mode);
+ DBG(KERN_INFO "#DAC Option [%d,%d] ADC Option %d WLEN %d\n\n",
+ aic3262->asiCtxt[counter].left_dac_output,
+ aic3262->asiCtxt[counter].right_dac_output,
+ aic3262->asiCtxt[counter].adc_input,
+ aic3262->asiCtxt[counter].word_len);
+ }
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_multi_i2s_mute
+ * Purpose : This function is to mute or unmute the left and right DAC
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_multi_i2s_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s : mute entered with %d\n", __func__, mute);
+
+ /* If we are doing both Recording and Playback on this DAI interface,
+ * do not MUTE the Codec.
+ */
+ if (mute && (aic3262->asiCtxt[dai->id - 1].asi_active > 1)) {
+ DBG("#%s Cannot Mute the ASI%d Now..\n",
+ __func__, dai->id);
+ } else {
+ switch (dai->id) {
+ case 1:
+ aic3262_multi_i2s_asi1_mute(dai, mute);
+ break;
+ case 2:
+ aic3262_multi_i2s_asi2_mute(dai, mute);
+ break;
+ case 3:
+ aic3262_multi_i2s_asi3_mute(dai, mute);
+ break;
+ default:
+ printk(KERN_ERR "#%s: Invalid DAI id\n", __func__);
+ return -EINVAL;
+ }
+ }
+ DBG(KERN_INFO "#%s : mute ended\n", __func__);
+ return 0;
+}
+
+
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_asi1_mute
+* Purpose : This function is to mute or unmute the left and right DAC
+*
+*----------------------------------------------------------------------------
+*/
+static int aic3262_multi_i2s_asi1_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s : mute %d started\n", __func__, mute);
+
+ if (mute) {
+ DBG(KERN_INFO "Mute if part\n");
+
+ if (!aic3262->asiCtxt[0].port_muted) {
+ dac_reg = aic3262_read(codec, DAC_MVOL_CONF);
+ adc_gain = aic3262_read(codec, ADC_FINE_GAIN);
+ hpl = aic3262_read(codec, HPL_VOL);
+ hpr = aic3262_read(codec, HPR_VOL);
+ rec_amp = aic3262_read(codec, REC_AMP_CNTL_R5);
+ rampr = aic3262_read(codec, RAMPR_VOL);
+ spk_amp = aic3262_read(codec, SPK_AMP_CNTL_R4);
+ }
+ DBG(KERN_INFO "spk_reg = %2x\n\n", spk_amp);
+
+ /* First check if both Playback and Recording is going on
+ * this interface.
+ */
+ if (aic3262->asiCtxt[0].asi_active > 1) {
+ DBG("#%s Cannot Mute the ASI Now..\n", __func__);
+ } else if (!(aic3262->asiCtxt[1].playback_mode) &&
+ !(aic3262->asiCtxt[2].playback_mode)) {
+ /* Before Muting, please check if any other
+ * ASI is active. if so, we cannot simply mute the
+ * DAC and ADC Registers.
+ */
+ aic3262_write(codec, DAC_MVOL_CONF,
+ ((dac_reg & 0xF3) | 0x0C));
+ aic3262_write(codec, ADC_FINE_GAIN,
+ ((adc_gain & 0x77) | 0x88));
+ aic3262_write(codec, HPL_VOL, 0xB9);
+ aic3262_write(codec, HPR_VOL, 0xB9);
+ aic3262_write(codec, REC_AMP_CNTL_R5, 0x39);
+ aic3262_write(codec, RAMPR_VOL, 0x39);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, 0x00);
+ aic3262->asiCtxt[0].port_muted = 1;
+ } else {
+ DBG(KERN_INFO
+ "#%s: Other ASI Active. Cannot MUTE Codec..\n",
+ __func__);
+ }
+ } else {
+ DBG(KERN_INFO "Mute else part\n");
+ aic3262_write(codec, DAC_MVOL_CONF, (dac_reg & 0xF3));
+ mdelay(5);
+ aic3262_write(codec, ADC_FINE_GAIN, (adc_gain & 0x77));
+ mdelay(5);
+ aic3262_write(codec, HPL_VOL, hpl);
+ mdelay(5);
+ aic3262_write(codec, HPR_VOL, hpr);
+ mdelay(5);
+ aic3262_write(codec, REC_AMP_CNTL_R5, rec_amp);
+ mdelay(5);
+ aic3262_write(codec, RAMPR_VOL, rampr);
+ mdelay(5);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, spk_amp);
+ mdelay(5);
+
+ /* Basic hack */
+ aic3262_write(codec, LINE_AMP_CNTL_R1, 0xc3);
+ aic3262_write(codec, HP_AMP_CNTL_R1, 0x33);
+
+ aic3262->asiCtxt[0].port_muted = 0;
+ aic3262_multi_i2s_dump_regs(dai);
+ }
+
+ DBG(KERN_INFO "#%s : mute %d ended\n", __func__, mute);
+
+ return 0;
+}
+
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_asi2_maic3262_asi3_clk_configute
+* Purpose : This function is to mute or unmute the left and right DAC
+*
+*----------------------------------------------------------------------------
+*/
+static int aic3262_multi_i2s_asi2_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s : mute %d started\n", __func__, mute);
+
+ if (mute) {
+ DBG(KERN_INFO "Mute if part\n");
+
+ if (!aic3262->asiCtxt[1].port_muted) {
+ dac_reg = aic3262_read(codec, DAC_MVOL_CONF);
+ adc_gain = aic3262_read(codec, ADC_FINE_GAIN);
+ hpl = aic3262_read(codec, HPL_VOL);
+ hpr = aic3262_read(codec, HPR_VOL);
+ rec_amp = aic3262_read(codec, REC_AMP_CNTL_R5);
+ rampr = aic3262_read(codec, RAMPR_VOL);
+ spk_amp = aic3262_read(codec, SPK_AMP_CNTL_R4);
+ DBG(KERN_INFO "spk_reg = %2x\n\n", spk_amp);
+ }
+
+ /* First check if both Playback and Recording is going on
+ * this interface.
+ */
+ if (aic3262->asiCtxt[1].asi_active > 1) {
+ DBG("#%s Cannot Mute the ASI Now..\n", __func__);
+ } else if (!(aic3262->asiCtxt[0].playback_mode) &&
+ !(aic3262->asiCtxt[2].playback_mode)) {
+ /* Before Muting, please check if any other
+ * ASI is active. if so, we cannot simply mute the
+ * DAC and ADC Registers.
+ */
+ aic3262_write(codec, DAC_MVOL_CONF,
+ ((dac_reg & 0xF3) | 0x0C));
+ aic3262_write(codec, ADC_FINE_GAIN,
+ ((adc_gain & 0x77) | 0x88));
+ aic3262_write(codec, HPL_VOL, 0xB9);
+ aic3262_write(codec, HPR_VOL, 0xB9);
+ aic3262_write(codec, REC_AMP_CNTL_R5, 0x39);
+ aic3262_write(codec, RAMPR_VOL, 0x39);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, 0x00);
+ aic3262->asiCtxt[1].port_muted = 1;
+ } else {
+ DBG("#%s: Other ASI Active. Cannot MUTE Codec..\n",
+ __func__);
+ }
+ } else {
+ DBG(KERN_INFO "Mute else part\n");
+ aic3262_write(codec, DAC_MVOL_CONF, (dac_reg & 0xF3));
+ mdelay(5);
+ aic3262_write(codec, ADC_FINE_GAIN, (adc_gain & 0x77));
+ mdelay(5);
+ aic3262_write(codec, HPL_VOL, hpl);
+ mdelay(5);
+ aic3262_write(codec, HPR_VOL, hpr);
+ mdelay(5);
+ aic3262_write(codec, REC_AMP_CNTL_R5, rec_amp);
+ mdelay(5);
+ aic3262_write(codec, RAMPR_VOL, rampr);
+ mdelay(5);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, spk_amp);
+ mdelay(5);
+
+ /* Basic hack */
+ aic3262_write(codec, LINE_AMP_CNTL_R1, 0xc3);
+ aic3262_write(codec, HP_AMP_CNTL_R1, 0x33);
+
+ aic3262->asiCtxt[1].port_muted = 0;
+ aic3262_multi_i2s_dump_regs(dai);
+ }
+
+ DBG(KERN_INFO "#%s : mute %d ended\n", __func__, mute);
+
+ return 0;
+}
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_asi3_mute
+* Purpose : This function is to mute or unmute the left and right DAC
+*
+*----------------------------------------------------------------------------
+*/
+static int aic3262_multi_i2s_asi3_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s : mute %d started\n", __func__, mute);
+
+ if (mute) {
+ DBG("Mute if part\n");
+
+ if (!aic3262->asiCtxt[2].port_muted) {
+ dac_reg = aic3262_read(codec, DAC_MVOL_CONF);
+ adc_gain = aic3262_read(codec, ADC_FINE_GAIN);
+ hpl = aic3262_read(codec, HPL_VOL);
+ hpr = aic3262_read(codec, HPR_VOL);
+ rec_amp = aic3262_read(codec, REC_AMP_CNTL_R5);
+ rampr = aic3262_read(codec, RAMPR_VOL);
+ spk_amp = aic3262_read(codec, SPK_AMP_CNTL_R4);
+ }
+ DBG("spk_reg = %2x\n\n", spk_amp);
+
+ /* First check if both Playback and Recording is going on
+ * this interface.
+ */
+ if (aic3262->asiCtxt[2].asi_active > 1) {
+ DBG("#%s Cannot Mute the ASI Now..\n", __func__);
+ } else if (!(aic3262->asiCtxt[0].playback_mode) &&
+ !(aic3262->asiCtxt[1].playback_mode)) {
+ /* Before Muting, please check if any other
+ * ASI is active. if so, we cannot simply mute the
+ * DAC and ADC Registers.
+ */
+ aic3262_write(codec, DAC_MVOL_CONF,
+ ((dac_reg & 0xF3) | 0x0C));
+ aic3262_write(codec, ADC_FINE_GAIN,
+ ((adc_gain & 0x77) | 0x88));
+ aic3262_write(codec, HPL_VOL, 0xB9);
+ aic3262_write(codec, HPR_VOL, 0xB9);
+ aic3262_write(codec, REC_AMP_CNTL_R5, 0x39);
+ aic3262_write(codec, RAMPR_VOL, 0x39);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, 0x00);
+ aic3262->asiCtxt[2].port_muted = 1;
+ } else {
+ DBG("#%s: Other ASI Active. Cannot MUTE Codec..\n",
+ __func__);
+ }
+ } else {
+ DBG("Mute else part\n");
+ aic3262_write(codec, DAC_MVOL_CONF, (dac_reg & 0xF3));
+ mdelay(5);
+ aic3262_write(codec, ADC_FINE_GAIN, (adc_gain & 0x77));
+ mdelay(5);
+ aic3262_write(codec, HPL_VOL, hpl);
+ mdelay(5);
+ aic3262_write(codec, HPR_VOL, hpr);
+ mdelay(5);
+ aic3262_write(codec, REC_AMP_CNTL_R5, rec_amp);
+ mdelay(5);
+ aic3262_write(codec, RAMPR_VOL, rampr);
+ mdelay(5);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, spk_amp);
+ mdelay(5);
+
+ /* Basic hack */
+ aic3262_write(codec, LINE_AMP_CNTL_R1, 0xc3);
+ aic3262_write(codec, HP_AMP_CNTL_R1, 0x33);
+
+ aic3262->asiCtxt[2].port_muted = 0;
+ aic3262_multi_i2s_dump_regs(dai);
+ }
+
+ DBG(KERN_INFO "#%s : mute %d ended\n", __func__, mute);
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_multi_i2s_set_dai_fmt
+ * Purpose : This function is to set the DAI format
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_multi_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ /* Check the DAI Id and based on that switch the configuration for
+ * the Individual ASI Port.
+ */
+ switch (codec_dai->id) {
+ case 1:
+ aic3262_multi_i2s_asi1_set_dai_fmt(codec_dai, fmt);
+ break;
+ case 2:
+ aic3262_multi_i2s_asi2_set_dai_fmt(codec_dai, fmt);
+ break;
+ case 3:
+ aic3262_multi_i2s_asi3_set_dai_fmt(codec_dai, fmt);
+ break;
+ default:
+ printk(KERN_ERR
+ "#%s: Invalid DAI interface format\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_asi1_set_dai_fmt
+* Purpose : This function is to set the DAI format for ASI1 Port
+*
+*----------------------------------------------------------------------------
+*/
+static int aic3262_multi_i2s_asi1_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 iface_reg, clk_reg;
+ u8 regvalue;
+
+ DBG(KERN_INFO "%s: DAI_ID %d fmt %d\n",
+ __func__, codec_dai->id, fmt);
+
+ /* Read the B0_P4_R4 and B0_P4_R10 Registers to configure the
+ * ASI1 Bus and Clock Formats depending on the PCM Format.
+ */
+ iface_reg = aic3262_read(codec, ASI1_BUS_FMT);
+ clk_reg = aic3262_read(codec, ASI1_BWCLK_CNTL_REG);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Master..\n",
+ __func__, codec_dai->id);
+ aic3262->asiCtxt[0].master = 1;
+ clk_reg |= (BIT5 | BIT2); /* Codec Interface as Master */
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Slave..\n",
+ __func__, codec_dai->id);
+ clk_reg &= ~0xFC; /* Reset bits D[7:5] and D[4:2] to zero */
+ aic3262->asiCtxt[0].master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /* new case..just for debugging */
+ DBG(KERN_INFO "%s: SND_SOC_DAIFMT_CBS_CFM\n", __func__);
+ aic3262->asiCtxt[0].master = 0;
+ clk_reg |= BIT5; /* Only WCLK1 Output from Codec */
+ clk_reg &= ~0x1C; /* BCLK1 Input to Codec */
+ break;
+ default:
+ printk(KERN_ERR "#%s: Invalid DAI master/slave interface\n",
+ __func__);
+ return -EINVAL;
+ }
+ aic3262->asiCtxt[0].pcm_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for I2s Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_A Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f) | 0x20;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x40;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x60;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_B Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f) | 0x80;
+ /* voice call need data offset in 1 bitclock */
+ aic3262_write(codec, ASI1_LCH_OFFSET, 1);
+ break;
+ default:
+ printk(KERN_ERR
+ "#%s: Invalid DAI interface format\n", __func__);
+ return -EINVAL;
+ }
+ /* Also Configure the Pin Control Registers before writing into
+ * the ASI specific Clock Control and Format Registers
+ */
+
+ /* Configure B0_P4_R65_D[5:2] to 001 This configures the
+ * WCLK1 Pin to ASI1
+ */
+ regvalue = aic3262_read(codec, WCLK1_PIN_CNTL_REG);
+ aic3262_write(codec, WCLK1_PIN_CNTL_REG, (regvalue | BIT2));
+
+ /* Configure B0_P4_R68_d[6:5] = 01 and B0_P4_R67_D[4:1] to 0001
+ * to ensure that the DIN1 and DOUT1 Pins are configured
+ * correctly
+ */
+ regvalue = aic3262_read(codec, DIN1_PIN_CNTL_REG);
+ aic3262_write(codec, DIN1_PIN_CNTL_REG, (regvalue | BIT5));
+ regvalue = aic3262_read(codec, DOUT1_PIN_CNTL_REG);
+ aic3262_write(codec, DOUT1_PIN_CNTL_REG, (regvalue | BIT1));
+
+ aic3262_write(codec, ASI1_BWCLK_CNTL_REG, clk_reg);
+
+ aic3262_write(codec, ASI1_BUS_FMT, iface_reg);
+
+ return 0;
+}
+
+
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_asi2_set_dai_fmt
+* Purpose : This function is to set the DAI format for ASI2 Port
+*
+*----------------------------------------------------------------------------
+*/
+static int aic3262_multi_i2s_asi2_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 iface_reg, clk_reg;
+ u8 regvalue;
+
+ DBG(KERN_INFO "%s: DAI_ID %d fmt %d\n",
+ __func__, codec_dai->id, fmt);
+
+ /* Read the B0_P4_R17 and B0_P4_R26 Registers to configure the
+ * ASI1 Bus and Clock Formats depending on the PCM Format.
+ */
+ iface_reg = aic3262_read(codec, ASI2_BUS_FMT);
+ clk_reg = aic3262_read(codec, ASI2_BWCLK_CNTL_REG);
+ /*
+ iface_reg = ifce_reg & ~(3 << 6 | 3 << 2);
+ */
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Master..\n",
+ __func__, codec_dai->id);
+ aic3262->asiCtxt[1].master = 1;
+ clk_reg |= (BIT5 | BIT2);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Slave..\n",
+ __func__, codec_dai->id);
+
+ clk_reg &= ~0xFC;
+ aic3262->asiCtxt[1].master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /*new case..just for debugging */
+ DBG(KERN_INFO "%s: SND_SOC_DAIFMT_CBS_CFM\n", __func__);
+ aic3262->asiCtxt[1].master = 0;
+ clk_reg |= BIT5;
+ clk_reg &= ~0x1C;
+ break;
+ default:
+ printk(KERN_ERR "#%s:Invalid DAI master/slave interface\n",
+ __func__);
+ return -EINVAL;
+ }
+ aic3262->asiCtxt[1].pcm_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for I2S Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_A Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f) | 0x20;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x40;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x60;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for DSP Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f) | 0x80;
+ /* voice call need data offset in 1 bitclock */
+ aic3262_write(codec, ASI2_LCH_OFFSET, 1);
+ break;
+ default:
+ printk(KERN_ERR "#%s:Invalid DAI interface format\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Also Configure the Pin Control Registers before writing into
+ * the ASI2 specific Clock Control and Format Registers
+ */
+
+ /* Configure B0_P4_R69_D[5:2] to 001 This configures the
+ * WCLK2 Pin to ASI2
+ */
+
+ regvalue = aic3262_read(codec, WCLK2_PIN_CNTL_REG);
+ aic3262_write(codec, WCLK2_PIN_CNTL_REG, (regvalue | BIT2));
+
+ regvalue = aic3262_read(codec, BCLK2_PIN_CNTL_REG);
+ aic3262_write(codec, BCLK2_PIN_CNTL_REG, (regvalue | BIT2));
+
+ /* Configure B0_P4_R72_d[6:5] = 01 and B0_P4_R71_D[4:1] to 0001
+ * to ensure that the DIN2 and DOUT2 Pins are configured
+ * correctly
+ */
+ regvalue = aic3262_read(codec, DIN2_PIN_CNTL_REG);
+ aic3262_write(codec, DIN2_PIN_CNTL_REG, (regvalue | BIT5));
+
+ regvalue = aic3262_read(codec, DOUT2_PIN_CNTL_REG);
+ aic3262_write(codec, DOUT2_PIN_CNTL_REG, (regvalue | BIT5 | BIT1));
+
+ aic3262_write(codec, ASI2_BWCLK_CNTL_REG, clk_reg);
+
+ aic3262_write(codec, ASI2_BUS_FMT, iface_reg);
+
+ return 0;
+}
+
+/*
+*----------------------------------------------------------------------------
+* Function : aic3262_multi_i2s_asi3_set_dai_fmt
+* Purpose : This function is to set the DAI format for ASI3 Port
+*
+*----------------------------------------------------------------------------
+*/
+static int aic3262_multi_i2s_asi3_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 iface_reg, clk_reg;
+ u8 regvalue;
+
+ DBG(KERN_INFO "%s: DAI_ID %d fmt %d\n",
+ __func__, codec_dai->id, fmt);
+
+ /* Read the B0_P4_R33 and B0_P4_R42 Registers to configure the
+ * ASI1 Bus and Clock Formats depending on the PCM Format.
+ */
+ iface_reg = aic3262_read(codec, ASI3_BUS_FMT);
+ clk_reg = aic3262_read(codec, ASI3_BWCLK_CNTL_REG);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Master..\n",
+ __func__, codec_dai->id);
+ aic3262->asiCtxt[2].master = 1;
+ clk_reg |= (BIT5 | BIT2);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ DBG(KERN_INFO "#%s: Configuring ASI%d as Frame Slave..\n",
+ __func__, codec_dai->id);
+ clk_reg &= ~0xFC;
+ aic3262->asiCtxt[2].master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /* new case..just for debugging */
+ DBG(KERN_INFO "%s: SND_SOC_DAIFMT_CBS_CFM\n", __func__);
+ aic3262->asiCtxt[2].master = 0;
+ clk_reg |= BIT5;
+ clk_reg &= ~0x1C;
+ break;
+ default:
+ printk(KERN_ERR "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+ aic3262->asiCtxt[2].pcm_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for I2S Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for DSP_A Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f) | 0x20;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x40;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x60;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ DBG(KERN_INFO "#%s: Configuring ASI%d for DSP Mode..\n",
+ __func__, codec_dai->id);
+ iface_reg = (iface_reg & 0x1f) | 0x80;
+ /* voice call need data offset in 1 bitclock */
+ aic3262_write(codec, ASI3_LCH_OFFSET, 1);
+ break;
+ default:
+ printk(KERN_ERR
+ "#%s: Invalid DAI interface format\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Also Configure the Pin Control Registers before writing into
+ * the ASI specific Clock Control and Format Registers
+ */
+ /* Configure B0_P4_R73_D[5:2] to 0001 This configures the
+ * WCLK1 Pin to ASI1
+ */
+ regvalue = aic3262_read(codec, WCLK3_PIN_CNTL_REG);
+ aic3262_write(codec, WCLK3_PIN_CNTL_REG, (regvalue | BIT2));
+
+ regvalue = aic3262_read(codec, BCLK3_PIN_CNTL_REG);
+ aic3262_write(codec, BCLK3_PIN_CNTL_REG, (regvalue | BIT2));
+
+ /* Configure B0_P4_R76_d[6:5] = 01 and B0_P4_R75_D[4:1] to 0001
+ * to ensure that the DIN1 and DOUT1 Pins are configured
+ * correctly
+ */
+ regvalue = aic3262_read(codec, DIN3_PIN_CNTL_REG);
+ aic3262_write(codec, DIN3_PIN_CNTL_REG, (regvalue | BIT5));
+ regvalue = aic3262_read(codec, DOUT3_PIN_CNTL_REG);
+ aic3262_write(codec, DOUT3_PIN_CNTL_REG, (regvalue | BIT1));
+
+ aic3262_write(codec, ASI3_BWCLK_CNTL_REG, clk_reg);
+
+ aic3262_write(codec, ASI3_BUS_FMT, iface_reg);
+
+ return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int aic3262_multi_i2s_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s: DAI ID %d Freq %d Direction %d\n",
+ __func__, codec_dai->id, freq, dir);
+ switch (freq) {
+ case AIC3262_FREQ_12000000:
+ case AIC3262_FREQ_12288000:
+ aic3262->sysclk = freq;
+ return 0;
+ case AIC3262_FREQ_24000000:
+ aic3262->sysclk = freq;
+ return 0;
+ break;
+ }
+ printk(KERN_ERR "Invalid frequency to set DAI system clock\n");
+ return -EINVAL;
+}
+
+/*
+* aic3262_multi_i2s_set_pll
+*
+* This function is invoked as part of the PLL call-back
+* handler from the ALSA layer.
+*/
+static int aic3262_multi_i2s_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ /*u16 reg, enable;
+ int offset;
+ struct snd_soc_codec *codec = codec_dai->codec;*/
+
+ printk(KERN_INFO "%s: DAI ID %d PLL_ID %d InFreq %d OutFreq %d\n",
+ __func__, pll_id, codec_dai->id, freq_in, freq_out);
+
+ return 0;
+}
+
+/*
+* aic3262_asi1_clk_config
+*
+* This function is used to configure the BCLK1, WCLK1 pins which
+* are specific to ASI1 Interface. This function just enables the
+* BCLk and WCLK along with the miniDSP Port Control Registers.
+* However, depending on the user requirement, this function can also be
+* extended to configure the sourc for the BCLK and WCLK on a ASI basis.
+*/
+static int aic3262_asi1_clk_config(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ u8 bclk_N_value, wclk_N_value;
+ u8 minidspD_data, minidspA_data;
+ u8 regval;
+
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "%s: Invoked\n", __func__);
+
+ if (aic3262->asiCtxt[0].master == 1) {
+ DBG(KERN_INFO
+ "#%s: Codec Master on ASI1 Port. Enabling BCLK WCLK Divider.\n",
+ __func__);
+ bclk_N_value = aic3262->asiCtxt[0].bclk_div;
+ aic3262_write(codec, ASI1_BCLK_N, (bclk_N_value | 0x80));
+
+ wclk_N_value = aic3262_read(codec, ASI1_WCLK_N);
+ aic3262_write(codec, ASI1_WCLK_N, (wclk_N_value | 0xA0));
+ }
+ /* Configure the BCLK and WCLK Output Mux Options */
+ regval = aic3262_read(codec, ASI1_BWCLK_OUT_CNTL);
+ regval &= ~(AIC3262_ASI_BCLK_MUX_MASK | AIC3262_ASI_WCLK_MUX_MASK);
+
+ regval |= (aic3262->asiCtxt[0].bclk_output <<
+ AIC3262_ASI_BCLK_MUX_SHIFT);
+ regval |= aic3262->asiCtxt[0].wclk_output;
+ aic3262_write(codec, ASI1_BWCLK_OUT_CNTL, regval);
+
+ /* Configure the corresponding miniDSP Data Ports */
+ minidspD_data = aic3262_read(codec, MINIDSP_PORT_CNTL_REG);
+ minidspD_data &= ~(BIT5 | BIT4);
+ aic3262_write(codec, MINIDSP_PORT_CNTL_REG, minidspD_data);
+
+ minidspA_data = aic3262_read(codec, ASI1_ADC_INPUT_CNTL);
+ minidspA_data &= ~(BIT2 | BIT1 | BIT0);
+ minidspA_data |= aic3262->asiCtxt[0].adc_input;
+ aic3262_write(codec, ASI1_ADC_INPUT_CNTL, minidspA_data);
+
+ return 0;
+
+}
+
+/*
+* aic3262_asi2_clk_config
+*
+* This function is used to configure the BCLK2, WCLK2 pins which
+* are specific to ASI2 Interface. This function just enables the
+* BCLk and WCLK along with the miniDSP Port Control Registers.
+* However, depending on the user requirement, this function can also be
+* extended to configure the sourc for the BCLK and WCLK on a ASI basis.
+*/
+static int aic3262_asi2_clk_config(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ u8 bclk_N_value, wclk_N_value, minidspD_data, minidspA_data;
+ u8 regval;
+
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "%s: Invoked\n", __func__);
+
+ if (aic3262->asiCtxt[1].master == 1) {
+ DBG(KERN_INFO
+ "#%s: Codec Master on ASI2 Port. Enabling BCLK WCLK Divider.\n",
+ __func__);
+ bclk_N_value = aic3262->asiCtxt[1].bclk_div;
+ aic3262_write(codec, ASI2_BCLK_N, (bclk_N_value | 0x80));
+
+ wclk_N_value = aic3262_read(codec, ASI2_WCLK_N);
+ aic3262_write(codec, ASI2_WCLK_N, (wclk_N_value | 0xA0));
+ }
+ /* Configure the BCLK and WCLK Output Mux Options */
+ regval = aic3262_read(codec, ASI2_BWCLK_OUT_CNTL);
+ regval &= ~(AIC3262_ASI_BCLK_MUX_MASK | AIC3262_ASI_WCLK_MUX_MASK);
+ regval |= (aic3262->asiCtxt[1].bclk_output <<
+ AIC3262_ASI_BCLK_MUX_SHIFT);
+ regval |= aic3262->asiCtxt[1].wclk_output;
+
+ aic3262_write(codec, ASI2_BWCLK_OUT_CNTL, regval);
+ /* Configure the corresponding miniDSP Data Ports */
+ minidspD_data = aic3262_read(codec, MINIDSP_PORT_CNTL_REG);
+ minidspD_data |= (BIT2);
+ aic3262_write(codec, MINIDSP_PORT_CNTL_REG, minidspD_data);
+
+ minidspA_data = aic3262_read(codec, ASI2_ADC_INPUT_CNTL);
+ minidspA_data &= ~(BIT2 | BIT1 | BIT0);
+ minidspA_data |= aic3262->asiCtxt[1].adc_input;
+ aic3262_write(codec, ASI2_ADC_INPUT_CNTL, minidspA_data);
+
+ return 0;
+
+}
+
+/*
+* aic3262_asi3_clk_config
+*
+* This function is used to configure the BCLK3, WCLK3 pins which
+* are specific to ASI3 Interface. This function just enables the
+* BCLk and WCLK along with the miniDSP Port Control Registers.
+* However, depending on the user requirement, this function can also be
+* extended to configure the sourc for the BCLK and WCLK on a ASI basis.
+*/
+static int aic3262_asi3_clk_config(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ u8 bclk_N_value, wclk_N_value, minidspD_data, minidspA_data;
+ u8 regval;
+
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "%s:\n", __func__);
+
+ if (aic3262->asiCtxt[2].master == 1) {
+ DBG(KERN_INFO
+ "#%s: Codec Master on ASI3 Port. Enabling BCLK WCLK Divider.\n",
+ __func__);
+ bclk_N_value = aic3262->asiCtxt[2].bclk_div;
+ aic3262_write(codec, ASI2_BCLK_N, (bclk_N_value | 0x80));
+
+ wclk_N_value = aic3262_read(codec, ASI3_WCLK_N);
+ aic3262_write(codec, ASI3_WCLK_N, (wclk_N_value | 0xA0));
+ }
+
+ /* Configure the BCLK and WCLK Output Mux Options */
+ regval = aic3262_read(codec, ASI3_BWCLK_OUT_CNTL);
+ regval &= ~(AIC3262_ASI_BCLK_MUX_MASK | AIC3262_ASI_WCLK_MUX_MASK);
+ regval |= (aic3262->asiCtxt[2].bclk_output <<
+ AIC3262_ASI_BCLK_MUX_SHIFT);
+ regval |= aic3262->asiCtxt[2].wclk_output;
+ aic3262_write(codec, ASI3_BWCLK_OUT_CNTL, regval);
+
+ minidspD_data = aic3262_read(codec, MINIDSP_PORT_CNTL_REG);
+ minidspD_data |= (BIT1);
+ aic3262_write(codec, MINIDSP_PORT_CNTL_REG, minidspD_data);
+
+ minidspA_data = aic3262_read(codec, ASI3_ADC_INPUT_CNTL);
+ minidspA_data &= ~(BIT2 | BIT1 | BIT0);
+ minidspA_data |= aic3262->asiCtxt[2].adc_input;
+ aic3262_write(codec, ASI3_ADC_INPUT_CNTL, minidspA_data);
+
+ return 0;
+
+}
+
+/*
+* aic3262_multi_i2s_hw_params
+*
+* This function is used to configure the individual ASI port registers
+* depending on the configuration passed on by the snd_pcm_hw_params
+* structure.
+* This function internally configures the ASI specific pins and clock
+* Control Registers.
+*/
+static int aic3262_multi_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int i, j;
+ u8 data;
+ u16 regoffset = 0;
+ u8 dacpath = 0;
+ u8 adcpath = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s: Invoked for ASI%d Port for %s Mode\n",
+ __func__, dai->id,
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ? "Playback" : "Record");
+
+ i = aic3262_get_divs(aic3262->sysclk, params_rate(params));
+
+ i2c_verify_book0(codec);
+
+ if (i < 0) {
+ printk(KERN_ERR "#%s: Sampling rate %d not supported\n",
+ __func__, params_rate(params));
+ return i;
+ }
+
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Configure the PLL J, R D values only if none of the ASI
+ * Interfaces are Active.
+ */
+ /*if (!(AIC3262_MULTI_ASI_ACTIVE(aic3262))) {*/
+ if (1) {
+ DBG(KERN_INFO "#%s: None of the ASIs active yet...\n",
+ __func__);
+ /*We will fix R value to 1 and make P & J=K.D as variable */
+ /* Setting P & R values are set to 1 and 1 at init*/
+
+ /* J value */
+ aic3262_write(codec, PLL_J_REG, aic3262_divs[i].pll_j);
+
+ /* MSB & LSB for D value */
+
+ aic3262_write(codec, PLL_D_MSB, (aic3262_divs[i].pll_d >> 8));
+ aic3262_write(codec, PLL_D_LSB,
+ (aic3262_divs[i].pll_d & AIC3262_8BITS_MASK));
+
+ /* NDAC divider value */
+ data = aic3262_read(codec, NDAC_DIV_POW_REG);
+ DBG(KERN_INFO "# reading NDAC = %d , NDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].ndac, data);
+ aic3262_write(codec, NDAC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].ndac)));
+ DBG(KERN_INFO "# writing NDAC = %d , NDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].ndac,
+ ((data & 0x80)|(aic3262_divs[i].ndac)));
+
+ /* MDAC divider value */
+ data = aic3262_read(codec, MDAC_DIV_POW_REG);
+ DBG(KERN_INFO "# reading MDAC = %d , MDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].mdac, data);
+ aic3262_write(codec, MDAC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].mdac)));
+ DBG(KERN_INFO "# writing MDAC = %d , MDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].mdac, ((data & 0x80)|(aic3262_divs[i].mdac)));
+
+ /* DOSR MSB & LSB values */
+ aic3262_write(codec, DOSR_MSB_REG, aic3262_divs[i].dosr >> 8);
+ DBG(KERN_INFO "# writing DOSR_MSB_REG = %d\n",
+ (aic3262_divs[i].dosr >> 8));
+ aic3262_write(codec, DOSR_LSB_REG,
+ aic3262_divs[i].dosr & AIC3262_8BITS_MASK);
+ DBG(KERN_INFO "# writing DOSR_LSB_REG = %d\n",
+ (aic3262_divs[i].dosr & AIC3262_8BITS_MASK));
+
+ /* NADC divider value */
+ data = aic3262_read(codec, NADC_DIV_POW_REG);
+ aic3262_write(codec, NADC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].nadc)));
+ DBG(KERN_INFO "# writing NADC_DIV_POW_REG = %d\n",
+ aic3262_divs[i].nadc);
+
+ /* MADC divider value */
+ data = aic3262_read(codec, MADC_DIV_POW_REG);
+ aic3262_write(codec, MADC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].madc)));
+ DBG(KERN_INFO "# writing MADC_DIV_POW_REG = %d\n",
+ aic3262_divs[i].madc);
+
+ /* AOSR value */
+ aic3262_write(codec, AOSR_REG, aic3262_divs[i].aosr);
+ DBG(KERN_INFO "# writing AOSR = %d\n", aic3262_divs[i].aosr);
+ } else {
+ DBG(KERN_INFO "#Atleast 1 ASI Active. Cannot Program PLL..\n");
+ }
+ /* Check for the DAI ID to know which ASI needs
+ * Configuration.
+ */
+ switch (dai->id) {
+ case 1:
+ regoffset = ASI1_BUS_FMT;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ DBG(KERN_INFO "#%s: ASI1 DAC Inputs enabled..\n",
+ __func__);
+ /* Read the DAC Control Register and configure it
+ * as per the ASIContext Structure Settings.
+ */
+ dacpath = aic3262_read(codec, ASI1_DAC_OUT_CNTL);
+ dacpath &= ~(AIC3262_ASI_LDAC_PATH_MASK |
+ AIC3262_ASI_RDAC_PATH_MASK);
+ dacpath |= (aic3262->asiCtxt[0].left_dac_output
+ << AIC3262_ASI_LDAC_PATH_SHIFT);
+
+ dacpath |= (aic3262->asiCtxt[0].right_dac_output
+ << AIC3262_ASI_RDAC_PATH_SHIFT);
+ aic3262_write(codec, ASI1_DAC_OUT_CNTL, dacpath);
+
+ aic3262->asiCtxt[0].playback_mode = 1;
+ aic3262->asiCtxt[0].bclk_div =
+ aic3262_divs[i].blck_N;
+ } else {
+ /* For Recording, Configure the DOUT Pin as per
+ * ASIContext Structure Settings.
+ */
+ adcpath = aic3262_read(codec, ASI1_DATA_OUT);
+ adcpath &= ~(AIC3262_ASI_DOUT_MASK);
+
+ adcpath |= aic3262->asiCtxt[0].dout_option;
+ aic3262_write(codec, ASI1_DATA_OUT, adcpath);
+
+ aic3262->asiCtxt[0].capture_mode = 1;
+ }
+ break;
+ case 2:
+ regoffset = ASI2_BUS_FMT;
+
+ /* Since we are configuring ASI2, please check if Playback
+ * is expected. If so, enable ASI2 Inputs to Left and
+ * Right DACs
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ DBG(KERN_INFO "#%s: ASI2 DAC Inputs enabled..\n",
+ __func__);
+ /* Read the DAC Control Register and configure it
+ * as per theASIContext Structure Settings.
+ */
+ dacpath = aic3262_read(codec, ASI2_DAC_OUT_CNTL);
+ dacpath &= ~(AIC3262_ASI_LDAC_PATH_MASK |
+ AIC3262_ASI_RDAC_PATH_MASK);
+ dacpath |= (aic3262->asiCtxt[1].left_dac_output
+ << AIC3262_ASI_LDAC_PATH_SHIFT);
+
+ dacpath |= (aic3262->asiCtxt[1].right_dac_output
+ << AIC3262_ASI_RDAC_PATH_SHIFT);
+ aic3262_write(codec, ASI2_DAC_OUT_CNTL, dacpath);
+ aic3262->asiCtxt[1].playback_mode = 1;
+
+ aic3262->asiCtxt[1].bclk_div =
+ aic3262_divs[i].blck_N;
+ } else {
+ /* For Recording, Configure the DOUT Pin as per
+ * ASIContext Structure Settings.
+ */
+ adcpath = aic3262_read(codec, ASI2_DATA_OUT);
+ adcpath &= ~(AIC3262_ASI_DOUT_MASK);
+ adcpath |= aic3262->asiCtxt[1].dout_option;
+ aic3262_write(codec, ASI2_DATA_OUT, adcpath);
+
+ aic3262->asiCtxt[1].capture_mode = 1;
+ }
+ break;
+ case 3:
+ regoffset = ASI3_BUS_FMT;
+ /* Since we are configuring ASI3, please check if Playback
+ * is expected. If so, enable ASI3 Inputs to Left and
+ * Right DACs
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ DBG(KERN_INFO "#%s:ASI3 DAC Inputs enabled.\n",
+ __func__);
+ /* Read the DAC Control Register and configure
+ * it as per the ASIContext Structure Settings.
+ */
+ dacpath = aic3262_read(codec, ASI3_DAC_OUT_CNTL);
+ dacpath &= ~(AIC3262_ASI_LDAC_PATH_MASK |
+ AIC3262_ASI_RDAC_PATH_MASK);
+ dacpath |= (aic3262->asiCtxt[2].left_dac_output
+ << AIC3262_ASI_LDAC_PATH_SHIFT);
+ dacpath |= (aic3262->asiCtxt[2].right_dac_output
+ << AIC3262_ASI_RDAC_PATH_SHIFT);
+ aic3262_write(codec,
+ ASI3_DAC_OUT_CNTL, dacpath);
+
+ aic3262->asiCtxt[2].playback_mode = 1;
+
+ aic3262->asiCtxt[2].bclk_div =
+ aic3262_divs[i].blck_N;
+ } else {
+ /* For Recording, Configure the DOUT Pin as per
+ * ASIContext Structure Settings.
+ */
+ adcpath &= ~(AIC3262_ASI_DOUT_MASK);
+ adcpath |= aic3262->asiCtxt[2].dout_option;
+ aic3262_write(codec, ASI3_DATA_OUT, adcpath);
+
+ aic3262->asiCtxt[2].capture_mode = 1;
+ }
+ break;
+ default:
+ printk(KERN_ERR "Invalid Dai ID %d in %s",
+ dai->id, __func__);
+ break;
+ }
+ DBG(KERN_INFO "#%s: Reading Pg %d Reg %d for Bus Format Control.\n",
+ __func__, (regoffset/128), (regoffset % 128));
+
+ /* Read the correspondig ASI DAI Interface Register */
+ data = aic3262_read(codec, regoffset);
+
+ data = data & 0xe7;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ DBG(KERN_INFO "#%s: Configuring ASI%d S16_LE Fmt..\n",
+ __func__, dai->id);
+ data = data | 0x00;
+ aic3262->asiCtxt[dai->id - 1].word_len = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ data |= (0x08);
+ aic3262->asiCtxt[dai->id - 1].word_len = 20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ DBG(KERN_INFO "#%s: Configuring ASI%d S24_LE Fmt..\n",
+ __func__, dai->id);
+ data |= (0x10);
+ aic3262->asiCtxt[dai->id - 1].word_len = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ DBG(KERN_INFO "#%s: Configuring ASI%d S32_LE Fmt..\n",
+ __func__, dai->id);
+ data |= (0x18);
+ aic3262->asiCtxt[dai->id - 1].word_len = 32;
+ break;
+ }
+
+ /* configure the respective Registers for the above configuration */
+ aic3262_write(codec, regoffset, data);
+
+ for (j = 0; j < NO_FEATURE_REGS; j++) {
+ aic3262_write(codec,
+ aic3262_divs[i].codec_specific_regs[j].reg_offset,
+ aic3262_divs[i].codec_specific_regs[j].reg_val);
+ }
+
+ /* Enable the PLL, MDAC, NDAC, NADC, MADC and BCLK Dividers */
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+ /* Based on the DAI ID we enable the corresponding pins related to the
+ * ASI Port.
+ */
+ switch (dai->id) {
+ case 1:
+ aic3262_asi1_clk_config(codec, params);
+ break;
+ case 2:
+ aic3262_asi2_clk_config(codec, params);
+ break;
+ case 3:
+ aic3262_asi3_clk_config(codec, params);
+ break;
+ default:
+ printk(KERN_ERR "Invalid Dai ID %d in %s",
+ dai->id, __func__);
+ break;
+ }
+ /* Depending on the DAI->ID update the local Flags */
+ aic3262->asiCtxt[dai->id - 1].asi_active++;
+ aic3262->asiCtxt[dai->id - 1].sampling_rate = params_rate(params);
+ /* Update the active_count flag */
+ aic3262->active_count++;
+
+ return 0;
+}
+
+/*
+*
+* aic3262_multi_i2s_hw_free
+*
+* This function is used to configure the Codec after the usage is completed.
+* We can use this function to disable the DAC and ADC specific inputs from the
+* individual ASI Ports of the Audio Codec.
+*/
+static int aic3262_multi_i2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ u8 value;
+ u8 dacpath;
+ u8 adcpath;
+ u16 dacregoffset = 0;
+ u16 adcregoffset = 0;
+
+ DBG(KERN_INFO "#%s: ASI%d Port for %s Mode\n",
+ __func__, dai->id,
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ "Playback" : "Record");
+
+ /* Check if this function was already executed earlier for the same
+ * ASI Port
+ */
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (aic3262->asiCtxt[dai->id - 1].playback_mode == 0)) {
+ DBG(KERN_INFO "#%s: Function Already Executed. Exiting..\n",
+ __func__);
+ goto err;
+ } else if ((substream->stream != SNDRV_PCM_STREAM_PLAYBACK) &&
+ (aic3262->asiCtxt[dai->id - 1].capture_mode == 0)) {
+ DBG(KERN_INFO "#%s: Function Already Executed. Exiting..\n",
+ __func__);
+ goto err;
+ }
+
+ switch (dai->id) {
+ case 1:
+ /* In case we are Frame Master on this Interface, Switch off
+ * the Bit Clock Divider and Word Clock Dividers
+ */
+ if (aic3262->asiCtxt[0].master == 1) {
+ /* Also check if either Playback or Recording is still
+ * going on this ASI Interface
+ */
+
+ value = aic3262_read(codec, ASI1_BCLK_N);
+ aic3262_write(codec, ASI1_BCLK_N, (value & 0x7f));
+
+ value = aic3262_read(codec, ASI1_WCLK_N);
+ aic3262_write(codec, ASI1_WCLK_N, (value & 0x7f));
+ }
+
+ dacregoffset = ASI1_DAC_OUT_CNTL;
+ adcregoffset = ASI1_ADC_INPUT_CNTL;
+ break;
+ case 2:
+ /* In case we are Frame Master on this Interface, Switch off
+ * the Bit Clock Divider and Word Clock Dividers
+ */
+ if (aic3262->asiCtxt[1].master == 1) {
+ value = aic3262_read(codec, ASI2_BCLK_N);
+ aic3262_write(codec, ASI2_BCLK_N, (value & 0x7f));
+
+ value = aic3262_read(codec, ASI2_WCLK_N);
+ aic3262_write(codec, ASI2_WCLK_N, (value & 0x7f));
+ }
+ dacregoffset = ASI2_DAC_OUT_CNTL;
+ adcregoffset = ASI2_ADC_INPUT_CNTL;
+ break;
+ case 3:
+ /* In case we are Frame Master on this Interface, Switch off
+ * the Bit Clock Divider and Word Clock Dividers
+ */
+ if (aic3262->asiCtxt[2].master == 1) {
+ value = aic3262_read(codec, ASI3_BCLK_N);
+ aic3262_write(codec, ASI3_BCLK_N, (value & 0x7f));
+
+ value = aic3262_read(codec, ASI3_WCLK_N);
+ aic3262_write(codec, ASI3_WCLK_N, (value & 0x7f));
+ }
+ dacregoffset = ASI3_DAC_OUT_CNTL;
+ adcregoffset = ASI3_ADC_INPUT_CNTL;
+ break;
+ default:
+ printk(KERN_ERR "#%s: Invalid dai id\n", __func__);
+ }
+ /* If this was a Playback Stream Stop, then only
+ * switch off the DAC Inputs
+ */
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (dacregoffset != 0)) {
+ printk(KERN_INFO "#%s: Disabling Pg %d Reg %d DAC Inputs ..\n",
+ __func__, (dacregoffset/128), (dacregoffset % 128));
+
+ dacpath = aic3262_read(codec, dacregoffset);
+ aic3262_write(codec, dacregoffset, (dacpath & ~(BIT6 | BIT4)));
+
+ aic3262->asiCtxt[dai->id - 1].playback_mode = 0;
+ } else {
+ /* Switch off the ADC Input Control Registers here */
+ DBG(KERN_INFO "#%s: Disabling Pg %d Reg %d for ADC Inputs..\n",
+ __func__, (adcregoffset/128), (adcregoffset % 128));
+
+ adcpath = aic3262_read(codec, adcregoffset);
+ aic3262_write(codec, adcregoffset,
+ (adcpath & ~(BIT2 | BIT1 | BIT0)));
+
+ aic3262->asiCtxt[dai->id - 1].capture_mode = 0;
+ }
+
+ /* If we were configured in mono PCM Mode earlier, then reset the
+ * Left Channel and Right Channel offset Registers here.
+ */
+ switch (dai->id) {
+ case 1:
+ if (aic3262->asiCtxt[0].pcm_format == SND_SOC_DAIFMT_DSP_B) {
+ aic3262_write(codec, ASI1_LCH_OFFSET, 0x00);
+ aic3262_write(codec, ASI1_RCH_OFFSET, 0x00);
+ }
+ break;
+ case 2:
+ if (aic3262->asiCtxt[1].pcm_format == SND_SOC_DAIFMT_DSP_B) {
+ aic3262_write(codec, ASI2_LCH_OFFSET, 0x00);
+ aic3262_write(codec, ASI2_RCH_OFFSET, 0x00);
+ }
+
+ break;
+ case 3:
+ if (aic3262->asiCtxt[2].pcm_format == SND_SOC_DAIFMT_DSP_B) {
+ aic3262_write(codec, ASI3_LCH_OFFSET, 0x00);
+ aic3262_write(codec, ASI3_RCH_OFFSET, 0x00);
+ }
+ break;
+ }
+ /* Depending on the DAI->ID update the asi_active Flags */
+ if (aic3262->asiCtxt[dai->id - 1].asi_active) {
+ aic3262->asiCtxt[dai->id - 1].asi_active--;
+
+ /* Update the active_count flag */
+ if (aic3262->active_count)
+ aic3262->active_count--;
+ }
+err:
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * @struct snd_soc_codec_dai |
+ * It is SoC Codec DAI structure which has DAI capabilities viz.,
+ * playback and capture, DAI runtime information viz. state of DAI
+ * and pop wait state, and DAI private data.
+ * The AIC3262 rates ranges from 8k to 192k
+ * The PCM bit format supported are 16, 20, 24 and 32 bits
+ *----------------------------------------------------------------------------
+ */
+struct snd_soc_dai_ops aic3262_dai_ops = {
+ .hw_params = aic3262_hw_params,
+ .digital_mute = aic3262_mute,
+ .set_sysclk = aic3262_set_dai_sysclk,
+ .set_fmt = aic3262_set_dai_fmt,
+};
+
+struct snd_soc_dai_ops aic3262_multi_i2s_dai_ops = {
+ .hw_params = aic3262_multi_i2s_hw_params,
+ .digital_mute = aic3262_multi_i2s_mute,
+ .set_fmt = aic3262_multi_i2s_set_dai_fmt,
+ .set_pll = aic3262_multi_i2s_set_dai_pll,
+ .set_sysclk = aic3262_multi_i2s_set_dai_sysclk,
+ .hw_free = aic3262_multi_i2s_hw_free,
+};
+
+
+static struct snd_soc_dai_driver tlv320aic3262_dai[] = {
+/* AIC3262 ASI1 DAI */
+{
+ .name = "aic3262-asi1",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC3262_RATES,
+ .formats = AIC3262_FORMATS},
+ .capture = { /* dummy for fast DAI switching */
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC3262_RATES,
+ .formats = AIC3262_FORMATS},
+ .ops = &aic3262_multi_i2s_dai_ops,
+},
+/* AIC3262 ASI2 DAI */
+{
+ .name = "aic3262-asi2",
+ .id = 2,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC3262_RATES,
+ .formats = AIC3262_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC3262_RATES,
+ .formats = AIC3262_FORMATS,},
+ .ops = &aic3262_multi_i2s_dai_ops,
+
+},
+/* AIC3262 ASI3 DAI */
+{
+ .name = "aic3262-asi3",
+ .id = 3,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC3262_RATES,
+ .formats = AIC3262_FORMATS, },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC3262_RATES,
+ .formats = AIC3262_FORMATS, },
+ .ops = &aic3262_multi_i2s_dai_ops,
+
+},
+};
+
+/*
+ *****************************************************************************
+ * Initializations
+ *****************************************************************************
+ */
+/*
+ * AIC3262 register cache
+ * We are caching the registers here.
+ * There is no point in caching the reset register.
+ *
+ * NOTE: In AIC3262, there are 127 registers supported in both page0 and page1
+ * The following table contains the page0 and page 1 and page 3
+ * registers values.
+ */
+static const u8 aic3262_reg[AIC3262_CACHEREGNUM] = {
+ 0x00, 0x00, 0x10, 0x00, /* 0 */
+ 0x03, 0x40, 0x11, 0x08, /* 4 */
+ 0x00, 0x00, 0x00, 0x82, /* 8 */
+ 0x88, 0x00, 0x80, 0x02, /* 12 */
+ 0x00, 0x08, 0x01, 0x01, /* 16 */
+ 0x80, 0x01, 0x00, 0x04, /* 20 */
+ 0x00, 0x00, 0x01, 0x00, /* 24 */
+ 0x00, 0x00, 0x01, 0x00, /* 28 */
+ 0x00, 0x00, 0x00, 0x00, /* 32 */
+ 0x00, 0x00, 0x00, 0x00, /* 36 */
+ 0x00, 0x00, 0x00, 0x00, /* 40 */
+ 0x00, 0x00, 0x00, 0x00, /* 44 */
+ 0x00, 0x00, 0x00, 0x00, /* 48 */
+ 0x00, 0x42, 0x02, 0x02, /* 52 */
+ 0x42, 0x02, 0x02, 0x02, /* 56 */
+ 0x00, 0x00, 0x00, 0x01, /* 60 */
+ 0x01, 0x00, 0x14, 0x00, /* 64 */
+ 0x0C, 0x00, 0x00, 0x00, /* 68 */
+ 0x00, 0x00, 0x00, 0xEE, /* 72 */
+ 0x10, 0xD8, 0x10, 0xD8, /* 76 */
+ 0x00, 0x00, 0x88, 0x00, /* 80 */
+ 0x00, 0x00, 0x00, 0x00, /* 84 */
+ 0x7F, 0x00, 0x00, 0x00, /* 88 */
+ 0x00, 0x00, 0x00, 0x00, /* 92 */
+ 0x7F, 0x00, 0x00, 0x00, /* 96 */
+ 0x00, 0x00, 0x00, 0x00, /* 100 */
+ 0x00, 0x00, 0x00, 0x00, /* 104 */
+ 0x00, 0x00, 0x00, 0x00, /* 108 */
+ 0x00, 0x00, 0x00, 0x00, /* 112 */
+ 0x00, 0x00, 0x00, 0x00, /* 116 */
+ 0x00, 0x00, 0x00, 0x00, /* 120 */
+ 0x00, 0x00, 0x00, 0x00, /* 124 - PAGE0 Registers(127) ends here */
+ 0x01, 0x00, 0x08, 0x00, /* 128, PAGE1-0 */
+ 0x00, 0x00, 0x00, 0x00, /* 132, PAGE1-4 */
+ 0x00, 0x00, 0x00, 0x10, /* 136, PAGE1-8 */
+ 0x00, 0x00, 0x00, 0x00, /* 140, PAGE1-12 */
+ 0x40, 0x40, 0x40, 0x40, /* 144, PAGE1-16 */
+ 0x00, 0x00, 0x00, 0x00, /* 148, PAGE1-20 */
+ 0x00, 0x00, 0x00, 0x00, /* 152, PAGE1-24 */
+ 0x00, 0x00, 0x00, 0x00, /* 156, PAGE1-28 */
+ 0x00, 0x00, 0x00, 0x00, /* 160, PAGE1-32 */
+ 0x00, 0x00, 0x00, 0x00, /* 164, PAGE1-36 */
+ 0x00, 0x00, 0x00, 0x00, /* 168, PAGE1-40 */
+ 0x00, 0x00, 0x00, 0x00, /* 172, PAGE1-44 */
+ 0x00, 0x00, 0x00, 0x00, /* 176, PAGE1-48 */
+ 0x00, 0x00, 0x00, 0x00, /* 180, PAGE1-52 */
+ 0x00, 0x00, 0x00, 0x80, /* 184, PAGE1-56 */
+ 0x80, 0x00, 0x00, 0x00, /* 188, PAGE1-60 */
+ 0x00, 0x00, 0x00, 0x00, /* 192, PAGE1-64 */
+ 0x00, 0x00, 0x00, 0x00, /* 196, PAGE1-68 */
+ 0x00, 0x00, 0x00, 0x00, /* 200, PAGE1-72 */
+ 0x00, 0x00, 0x00, 0x00, /* 204, PAGE1-76 */
+ 0x00, 0x00, 0x00, 0x00, /* 208, PAGE1-80 */
+ 0x00, 0x00, 0x00, 0x00, /* 212, PAGE1-84 */
+ 0x00, 0x00, 0x00, 0x00, /* 216, PAGE1-88 */
+ 0x00, 0x00, 0x00, 0x00, /* 220, PAGE1-92 */
+ 0x00, 0x00, 0x00, 0x00, /* 224, PAGE1-96 */
+ 0x00, 0x00, 0x00, 0x00, /* 228, PAGE1-100 */
+ 0x00, 0x00, 0x00, 0x00, /* 232, PAGE1-104 */
+ 0x00, 0x00, 0x00, 0x00, /* 236, PAGE1-108 */
+ 0x00, 0x00, 0x00, 0x00, /* 240, PAGE1-112 */
+ 0x00, 0x00, 0x00, 0x00, /* 244, PAGE1-116 */
+ 0x00, 0x00, 0x00, 0x00, /* 248, PAGE1-120 */
+ 0x00, 0x00, 0x00, 0x00, /* 252, PAGE1-124 Page 1 Registers Ends Here */
+ 0x00, 0x00, 0x00, 0x00, /* 256, PAGE2-0 */
+ 0x00, 0x00, 0x00, 0x00, /* 260, PAGE2-4 */
+ 0x00, 0x00, 0x00, 0x00, /* 264, PAGE2-8 */
+ 0x00, 0x00, 0x00, 0x00, /* 268, PAGE2-12 */
+ 0x00, 0x00, 0x00, 0x00, /* 272, PAGE2-16 */
+ 0x00, 0x00, 0x00, 0x00, /* 276, PAGE2-20 */
+ 0x00, 0x00, 0x00, 0x00, /* 280, PAGE2-24 */
+ 0x00, 0x00, 0x00, 0x00, /* 284, PAGE2-28 */
+ 0x00, 0x00, 0x00, 0x00, /* 288, PAGE2-32 */
+ 0x00, 0x00, 0x00, 0x00, /* 292, PAGE2-36 */
+ 0x00, 0x00, 0x00, 0x00, /* 296, PAGE2-40 */
+ 0x00, 0x00, 0x00, 0x00, /* 300, PAGE2-44 */
+ 0x00, 0x00, 0x00, 0x00, /* 304, PAGE2-48 */
+ 0x00, 0x00, 0x00, 0x00, /* 308, PAGE2-52 */
+ 0x00, 0x00, 0x00, 0x00, /* 312, PAGE2-56 */
+ 0x00, 0x00, 0x00, 0x00, /* 316, PAGE2-60 */
+ 0x00, 0x00, 0x00, 0x00, /* 320, PAGE2-64 */
+ 0x00, 0x00, 0x00, 0x00, /* 324, PAGE2-68 */
+ 0x00, 0x00, 0x00, 0x00, /* 328, PAGE2-72 */
+ 0x00, 0x00, 0x00, 0x00, /* 332, PAGE2-76 */
+ 0x00, 0x00, 0x00, 0x00, /* 336, PAGE2-80 */
+ 0x00, 0x00, 0x00, 0x00, /* 340, PAGE2-84 */
+ 0x00, 0x00, 0x00, 0x00, /* 344, PAGE2-88 */
+ 0x00, 0x00, 0x00, 0x00, /* 348, PAGE2-92 */
+ 0x00, 0x00, 0x00, 0x00, /* 352, PAGE2-96 */
+ 0x00, 0x00, 0x00, 0x00, /* 356, PAGE2-100 */
+ 0x00, 0x00, 0x00, 0x00, /* 360, PAGE2-104 */
+ 0x00, 0x00, 0x00, 0x00, /* 364, PAGE2-108 */
+ 0x00, 0x00, 0x00, 0x00, /* 368, PAGE2-112*/
+ 0x00, 0x00, 0x00, 0x00, /* 372, PAGE2-116*/
+ 0x00, 0x00, 0x00, 0x00, /* 376, PAGE2-120*/
+ 0x00, 0x00, 0x00, 0x00, /* 380, PAGE2-124 Page 2 Registers Ends Here */
+ 0x00, 0x00, 0x00, 0x00, /* 384, PAGE3-0 */
+ 0x00, 0x00, 0x00, 0x00, /* 388, PAGE3-4 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-8 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-12 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-16 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-20 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-24 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-28 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-32 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-36 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-40 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-44 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-48 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-52 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-56 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-60 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-64 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE3-68 */
+ 0x00, 0x00, 0x00, 0x00, /* 328, PAGE3-72 */
+ 0x00, 0x00, 0x00, 0x00, /* 332, PAGE3-76 */
+ 0x00, 0x00, 0x00, 0x00, /* 336, PAGE3-80 */
+ 0x00, 0x00, 0x00, 0x00, /* 340, PAGE3-84 */
+ 0x00, 0x00, 0x00, 0x00, /* 344, PAGE3-88 */
+ 0x00, 0x00, 0x00, 0x00, /* 348, PAGE3-92 */
+ 0x00, 0x00, 0x00, 0x00, /* 352, PAGE3-96 */
+ 0x00, 0x00, 0x00, 0x00, /* 356, PAGE3-100 */
+ 0x00, 0x00, 0x00, 0x00, /* 360, PAGE3-104 */
+ 0x00, 0x00, 0x00, 0x00, /* 364, PAGE3-108 */
+ 0x00, 0x00, 0x00, 0x00, /* 368, PAGE3-112*/
+ 0x00, 0x00, 0x00, 0x00, /* 372, PAGE3-116*/
+ 0x00, 0x00, 0x00, 0x00, /* 376, PAGE3-120*/
+ 0x00, 0x00, 0x00, 0x00, /* 380, PAGE3-124 Page 3 Registers Ends Here */
+ 0x00, 0x00, 0x00, 0x00, /* 384, PAGE4-0 */
+ 0x00, 0x00, 0x00, 0x00, /* 388, PAGE4-4 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-8 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-12 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-16 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-20 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-24 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-28 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-32 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-36 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-40 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-44 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-48 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-52 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-56 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-60 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-64 */
+ 0x00, 0x00, 0x00, 0x00, /* 392, PAGE4-68 */
+ 0x00, 0x00, 0x00, 0x00, /* 328, PAGE4-72 */
+ 0x00, 0x00, 0x00, 0x00, /* 332, PAGE4-76 */
+ 0x00, 0x00, 0x00, 0x00, /* 336, PAGE4-80 */
+ 0x00, 0x00, 0x00, 0x00, /* 340, PAGE4-84 */
+ 0x00, 0x00, 0x00, 0x00, /* 344, PAGE4-88 */
+ 0x00, 0x00, 0x00, 0x00, /* 348, PAGE4-92 */
+ 0x00, 0x00, 0x00, 0x00, /* 352, PAGE4-96 */
+ 0x00, 0x00, 0x00, 0x00, /* 356, PAGE4-100 */
+ 0x00, 0x00, 0x00, 0x00, /* 360, PAGE4-104 */
+ 0x00, 0x00, 0x00, 0x00, /* 364, PAGE4-108 */
+ 0x00, 0x00, 0x00, 0x00, /* 368, PAGE4-112*/
+ 0x00, 0x00, 0x00, 0x00, /* 372, PAGE4-116*/
+ 0x00, 0x00, 0x00, 0x00, /* 376, PAGE4-120*/
+ 0x00, 0x00, 0x00, 0x00, /* 380, PAGE4-124 Page 2 Registers Ends Here */
+
+};
+
+/*
+ *------------------------------------------------------------------------------
+ * aic3262 initialization data
+ * This structure initialization contains the initialization required for
+ * AIC326x.
+ * These registers values (reg_val) are written into the respective AIC3262
+ * register offset (reg_offset) to initialize AIC326x.
+ * These values are used in aic3262_init() function only.
+ *------------------------------------------------------------------------------
+ */
+static const struct aic3262_configs aic3262_reg_init[] = {
+ /* CLOCKING */
+
+ {0, RESET_REG, 1},
+ {0, RESET_REG, 0},
+
+ {0, PASI_DAC_DP_SETUP, 0xc0}, /*DAC */
+ {0, DAC_MVOL_CONF, 0x00}, /*DAC un-muted*/
+ /* set default volumes */
+ {0, DAC_LVOL, 0x01},
+ {0, DAC_RVOL, 0x01},
+ {0, HPL_VOL, 0x3a},
+ {0, HPR_VOL, 0x3a},
+ {0, SPK_AMP_CNTL_R2, 0x14},
+ {0, SPK_AMP_CNTL_R3, 0x14},
+ {0, SPK_AMP_CNTL_R4, 0x33},
+ {0, REC_AMP_CNTL_R5, 0x82},
+ {0, RAMPR_VOL, 20},
+ {0, RAMP_CNTL_R1, 70},
+ {0, RAMP_CNTL_R2, 70},
+
+ /* DRC Defaults */
+ {0, DRC_CNTL_R1, 0x6c},
+ {0, DRC_CNTL_R2, 16},
+
+ /* DEPOP SETTINGS */
+ {0, HP_DEPOP, 0x14},
+ {0, RECV_DEPOP, 0x14},
+
+ {0, POWER_CONF, 0x00}, /* Disconnecting AVDD-DVD weak link*/
+ {0, REF_PWR_DLY, 0x01},
+ {0, CM_REG, 0x00}, /*CM - default*/
+ {0, LDAC_PTM, 0}, /*LDAC_PTM - default*/
+ {0, RDAC_PTM, 0}, /*RDAC_PTM - default*/
+ {0, HP_CTL, 0x30}, /*HP output percentage - at 75%*/
+ {0, LADC_VOL, 0x01}, /*LADC volume*/
+ {0, RADC_VOL, 0x01}, /*RADC volume*/
+
+ {0, DAC_ADC_CLKIN_REG, 0x33}, /*DAC ADC CLKIN*/
+ {0, PLL_CLKIN_REG, 0x00}, /*PLL CLKIN*/
+ {0, PLL_PR_POW_REG, 0x11}, /*PLL Power=0-down, P=1, R=1 vals*/
+ {0, 0x3d, 1},
+
+ {0, LMIC_PGA_PIN, 0x55}, /*IN1_L select - - 10k -LMICPGA_P*/
+ {0, LMIC_PGA_MIN, 0x40}, /*CM to LMICPGA-M*/
+ {0, RMIC_PGA_PIN, 0x55}, /*IN1_R select - - 10k -RMIC_PGA_P*/
+ {0, RMIC_PGA_MIN, 0x40}, /*CM to RMICPGA_M*/
+ {0, (PAGE_1 + 0x79), 33}, /*LMIC-PGA-POWERUP-DELAY - default*/
+ {0, (PAGE_1 + 0x7a), 1}, /*FIXMELATER*/
+
+
+ {0, ADC_CHANNEL_POW, 0xc2}, /*ladc, radc ON , SOFT STEP disabled*/
+ {0, ADC_FINE_GAIN, 0x00}, /*ladc - unmute, radc - unmute*/
+ {0, MICL_PGA, 0x4f},
+ {0, MICR_PGA, 0x4f},
+ {0, MIC_BIAS_CNTL, 0xFC},
+ /* ASI1 Configuration */
+ {0, ASI1_BUS_FMT, 0},
+ {0, ASI1_BWCLK_CNTL_REG, 0x00}, /* originaly 0x24*/
+ {0, ASI1_BCLK_N_CNTL, 1},
+ {0, ASI1_BCLK_N, 0x84},
+
+ {0, MA_CNTL, 0}, /* Mixer Amp disabled */
+ {0, LINE_AMP_CNTL_R2, 0x00}, /* Line Amp Cntl disabled */
+
+ /* ASI2 Configuration */
+ {0, ASI2_BUS_FMT, 0},
+ {0, ASI2_BCLK_N_CNTL, 1},
+ {0, ASI2_BCLK_N, 0x84},
+ {0, ASI2_BWCLK_OUT_CNTL, 0x20},
+
+ {0, BEEP_CNTL_R1, 0x05},
+ {0, BEEP_CNTL_R2, 0x04},
+
+ /* Interrupt config for headset detection */
+ {0, INT1_CNTL, 0x80},
+ {0, INT_FMT, 0x40},
+ {0, GPIO1_IO_CNTL, 0x14},
+ {0, HP_DETECT, 0x94},
+
+#if defined(CONFIG_MINI_DSP)
+ {0, 60, 0},
+ {0, 61, 0},
+ /* Added the below set of values after consulting the miniDSP
+ * Program Section Array
+ */
+ {0, MINIDSP_ACCESS_CTRL, 0x00},
+#endif
+
+
+};
+
+static int reg_init_size =
+ sizeof(aic3262_reg_init) / sizeof(struct aic3262_configs);
+
+static const struct snd_kcontrol_new aic3262_snd_controls2[] = {
+
+ SOC_DOUBLE_R("IN1 MPGA Route", LMIC_PGA_PIN, RMIC_PGA_PIN, 6, 3, 0),
+ SOC_DOUBLE_R("IN2 MPGA Route", LMIC_PGA_PIN, RMIC_PGA_PIN, 4, 3, 0),
+ SOC_DOUBLE_R("IN3 MPGA Route", LMIC_PGA_PIN, RMIC_PGA_PIN, 2, 3, 0),
+ SOC_DOUBLE_R("IN4 MPGA Route",
+ LMIC_PGA_PM_IN4, RMIC_PGA_PM_IN4, 5, 1, 0),
+};
+static const struct snd_soc_dapm_widget aic3262_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("Left DAC", "Playback", PASI_DAC_DP_SETUP, 7, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Playback", PASI_DAC_DP_SETUP, 6, 0),
+
+ SND_SOC_DAPM_SWITCH_N("LDAC_2_HPL", HP_AMP_CNTL_R1, 5, 0),
+ SND_SOC_DAPM_SWITCH_N("RDAC_2_HPR", HP_AMP_CNTL_R1, 4, 0),
+
+ SND_SOC_DAPM_PGA("HPL Driver", HP_AMP_CNTL_R1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HPR Driver", HP_AMP_CNTL_R1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH_N("LDAC_2_LOL", LINE_AMP_CNTL_R1, 7, 0),
+ SND_SOC_DAPM_SWITCH_N("RDAC_2_LOR", LINE_AMP_CNTL_R1, 6, 0),
+
+ SND_SOC_DAPM_PGA("LOL Driver", LINE_AMP_CNTL_R1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LOR Driver", LINE_AMP_CNTL_R1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPKL Driver", SPK_AMP_CNTL_R1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKR Driver", SPK_AMP_CNTL_R1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RECL Driver", REC_AMP_CNTL_R5, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RECR Driver", REC_AMP_CNTL_R5, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("Left ADC", "Capture", ADC_CHANNEL_POW, 7, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Capture", ADC_CHANNEL_POW, 6, 0),
+
+ SND_SOC_DAPM_SWITCH("IN1L Route",
+ LMIC_PGA_PIN, 6, 0, &aic3262_snd_controls2[0]),
+ SND_SOC_DAPM_SWITCH("IN2L Route",
+ LMIC_PGA_PIN, 4, 0, &aic3262_snd_controls2[1]),
+ SND_SOC_DAPM_SWITCH("IN3L Route",
+ LMIC_PGA_PIN, 2, 0, &aic3262_snd_controls2[2]),
+ SND_SOC_DAPM_SWITCH("IN4L Route",
+ LMIC_PGA_PM_IN4, 5, 0, &aic3262_snd_controls2[3]),
+ SND_SOC_DAPM_SWITCH("IN1R Route",
+ RMIC_PGA_PIN, 6, 0, &aic3262_snd_controls2[4]),
+ SND_SOC_DAPM_SWITCH("IN2R Route",
+ RMIC_PGA_PIN, 4, 0, &aic3262_snd_controls2[5]),
+ SND_SOC_DAPM_SWITCH("IN3R Route",
+ RMIC_PGA_PIN, 2, 0, &aic3262_snd_controls2[6]),
+ SND_SOC_DAPM_SWITCH("IN4R Route",
+ RMIC_PGA_PM_IN4, 5, 0, &aic3262_snd_controls2[7]),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LOL"),
+ SND_SOC_DAPM_OUTPUT("LOR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RECL"),
+ SND_SOC_DAPM_OUTPUT("RECR"),
+
+ SND_SOC_DAPM_INPUT("IN1L"),
+ SND_SOC_DAPM_INPUT("IN2L"),
+ SND_SOC_DAPM_INPUT("IN3L"),
+ SND_SOC_DAPM_INPUT("IN4L"),
+
+ SND_SOC_DAPM_INPUT("IN1R"),
+ SND_SOC_DAPM_INPUT("IN2R"),
+ SND_SOC_DAPM_INPUT("IN3R"),
+ SND_SOC_DAPM_INPUT("IN4R"),
+};
+
+static const struct snd_soc_dapm_route aic3262_dapm_routes[] = {
+ {"LDAC_2_HPL", NULL, "Left DAC"},
+ {"HPL Driver", NULL, "LDAC_2_HPL"},
+ {"HPL", NULL, "HPL Driver"},
+ {"RDAC_2_HPR", NULL, "Right DAC"},
+ {"HPR Driver", NULL, "RDAC_2_HPR"},
+ {"HPR", NULL, "HPR Driver"},
+
+ {"LDAC_2_LOL", NULL, "Left DAC"},
+ {"LOL Driver", NULL, "LDAC_2_LOL"},
+ {"LOL", NULL, "LOL Driver"},
+ {"RDAC_2_LOR", NULL, "Right DAC"},
+ {"LOR Driver", NULL, "RDAC_2_LOR"},
+ {"LOR", NULL, "LOR Driver"},
+
+ {"SPKL Driver", NULL, "LOL"},
+ {"SPKL", NULL, "SPKL Driver"},
+ {"SPKR Driver", NULL, "LOR"},
+ {"SPKR", NULL, "SPKR Driver"},
+
+ {"RECL Driver", NULL, "LOL"},
+ {"RECL", NULL, "RECL Driver"},
+ {"RECR Driver", NULL, "LOR"},
+ {"RECR", NULL, "RECR Driver"},
+
+ {"Left ADC", "IN1L Route", "IN1L"},
+ {"Left ADC", "IN2L Route", "IN2L"},
+ {"Left ADC", "IN3L Route", "IN3L"},
+ {"Left ADC", "IN4L Route", "IN4L"},
+
+ {"Right ADC", "IN1R Route", "IN1R"},
+ {"Right ADC", "IN2R Route", "IN2R"},
+ {"Right ADC", "IN3R Route", "IN3R"},
+ {"Right ADC", "IN4R Route", "IN4R"},
+/*
+ {"LOL Driver", NULL, "IN1L"},
+ {"LOR Driver", NULL, "IN1R"},
+*/
+};
+
+/*
+ *****************************************************************************
+ * Function Definitions
+ *****************************************************************************
+ */
+
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_change_page
+ * Purpose : This function is to switch between page 0 and page 1.
+ *
+ *----------------------------------------------------------------------------
+ */
+int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 data[2];
+ int ret = 0;
+
+ data[0] = 0;
+ data[1] = new_page;
+ aic3262->page_no = new_page;
+
+ ret = snd_soc_write(codec, data[0], data[1]);
+ if (ret)
+ printk(KERN_ERR "Error in changing page to %d\n", new_page);
+
+ DBG("# Changing page to %d\r\n", new_page);
+
+ return ret;
+}
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_change_book
+ * Purpose : This function is to switch between books
+ *
+ *----------------------------------------------------------------------------
+ */
+int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 data[2];
+ int ret = 0;
+
+ data[0] = 0x7F;
+ data[1] = new_book;
+ aic3262->book_no = new_book;
+
+ ret = aic3262_change_page(codec, 0);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_write(codec, data[0], data[1]);
+ if (ret)
+ printk(KERN_ERR "Error in changing Book\n");
+
+ DBG("# Changing book to %d\r\n", new_book);
+
+ return ret;
+}
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_write_reg_cache
+ * Purpose : This function is to write aic3262 register cache
+ *
+ *----------------------------------------------------------------------------
+ */
+void aic3262_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, u8 value)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= AIC3262_CACHEREGNUM)
+ return;
+
+ if (cache)
+ cache[reg] = value;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_read
+ * Purpose : This function is to read the aic3262 register space.
+ *
+ *----------------------------------------------------------------------------
+ */
+u8 aic3262_read(struct snd_soc_codec *codec, u16 reg)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 value;
+ u8 page = reg / 128;
+
+ reg = reg % 128;
+
+ if (aic3262->page_no != page)
+ aic3262_change_page(codec, page);
+
+ value = snd_soc_read(codec, reg);
+ /*DBG("r %2x %02x\r\n", reg, value);*/
+ return value;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_write
+ * Purpose : This function is to write to the aic3262 register space.
+ *
+ *----------------------------------------------------------------------------
+ */
+int aic3262_write(struct snd_soc_codec *codec, u16 reg, u8 value)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 data[2];
+ u8 page;
+ int ret = 0;
+
+ page = reg / 128;
+ data[AIC3262_REG_OFFSET_INDEX] = reg % 128;
+ if (aic3262->page_no != page)
+ aic3262_change_page(codec, page);
+
+ /* data is
+ * D15..D8 aic3262 register offset
+ * D7...D0 register data
+ */
+ data[AIC3262_REG_DATA_INDEX] = value & AIC3262_8BITS_MASK;
+#if defined(EN_REG_CACHE)
+ if ((page >= 0) & (page <= 4))
+ aic3262_write_reg_cache(codec, reg, value);
+
+#endif
+ if (!data[AIC3262_REG_OFFSET_INDEX]) {
+ /* if the write is to reg0 update aic3262->page_no */
+ aic3262->page_no = value;
+ }
+
+ /*DBG("w %2x %02x\r\n",
+ data[AIC3262_REG_OFFSET_INDEX], data[AIC3262_REG_DATA_INDEX]);*/
+ ret = snd_soc_write(codec, data[AIC3262_REG_OFFSET_INDEX],
+ data[AIC3262_REG_DATA_INDEX]);
+ if (ret)
+ printk(KERN_ERR "Error in i2c write\n");
+
+ return ret;
+}
+
+/*
+ *------------------------------------------------------------------------------
+ * Function : aic3262_write__
+ * Purpose : This function is to write to the aic3262 register space.
+ * (low level).
+ *------------------------------------------------------------------------------
+ */
+
+int aic3262_write__(struct i2c_client *client, const char *buf, int count)
+{
+ u8 data[3];
+ int ret;
+ data[0] = *buf;
+ data[1] = *(buf+1);
+ data[2] = *(buf+2);
+ /*DBG("w %2x %02x\r\n",
+ data[AIC3262_REG_OFFSET_INDEX], data[AIC3262_REG_DATA_INDEX]);*/
+ ret = i2c_master_send(client, data, 2);
+ if (ret < 2) {
+ printk(
+ KERN_ERR"I2C write Error : bytes written = %d\n\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_reset_cache
+ * Purpose : This function is to reset the cache.
+ *----------------------------------------------------------------------------
+ */
+int aic3262_reset_cache(struct snd_soc_codec *codec)
+{
+#if defined(EN_REG_CACHE)
+ if (codec->reg_cache) {
+ memcpy(codec->reg_cache, aic3262_reg, sizeof(aic3262_reg));
+ return 0;
+ }
+#endif
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_get_divs
+ * Purpose : This function is to get required divisor from the "aic3262_divs"
+ * table.
+ *
+ *----------------------------------------------------------------------------
+ */
+static inline int aic3262_get_divs(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aic3262_divs); i++) {
+ if ((aic3262_divs[i].rate == rate)
+ && (aic3262_divs[i].mclk == mclk)) {
+ DBG(KERN_INFO "#%s: Found Entry %d in Clock_Array\n",
+ __func__, i);
+ return i;
+ }
+ }
+ printk(KERN_ERR "Master clock and sample rate is not supported\n");
+ return -EINVAL;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_add_controls
+ * Purpose : This function is to add non dapm kcontrols. The different
+ * controls are in "aic3262_snd_controls" table.
+ * The following different controls are supported
+ * # PCM Playback volume control
+ * # PCM Playback Volume
+ * # HP Driver Gain
+ * # HP DAC Playback Switch
+ * # PGA Capture Volume
+ * # Program Registers
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_add_controls(struct snd_soc_codec *codec)
+{
+ int err;
+
+ err = snd_soc_add_controls(codec, aic3262_snd_controls,
+ ARRAY_SIZE(aic3262_snd_controls));
+ if (err < 0) {
+ printk(KERN_ERR "Invalid control\n");
+ return err;
+ }
+
+ err = snd_soc_add_controls(codec, aic3262_snd_controls2,
+ ARRAY_SIZE(aic3262_snd_controls2));
+ if (err < 0) {
+ printk(KERN_ERR "Invalid control\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_add_widgets
+ * Purpose : This function is to add the dapm widgets
+ * The following are the main widgets supported
+ * # Left DAC to Left Outputs
+ * # Right DAC to Right Outputs
+ * # Left Inputs to Left ADC
+ * # Right Inputs to Right ADC
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_add_widgets(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+#ifndef AIC3262_MULTI_I2S
+ int i;
+ for (i = 0; i < ARRAY_SIZE(aic3262_dapm_widgets); i++)
+ ret = snd_soc_dapm_new_control(dapm, &aic3262_dapm_widgets[i]);
+#else
+ ret = snd_soc_dapm_new_controls(dapm, aic3262_dapm_widgets,
+ ARRAY_SIZE(aic3262_dapm_widgets));
+ if (ret != 0) {
+ printk(KERN_ERR "#%s: Unable to add DAPM Controls. Err %d\n",
+ __func__, ret);
+ }
+#endif
+ /* set up audio path interconnects */
+ DBG("#Completed adding new dapm widget controls size=%d\n",
+ ARRAY_SIZE(aic3262_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, aic3262_dapm_routes,
+ ARRAY_SIZE(aic3262_dapm_routes));
+ DBG("#Completed adding DAPM routes\n");
+ /*snd_soc_dapm_new_widgets(codec);*/
+ DBG("#Completed updating dapm\n");
+ return 0;
+}
+/*
+ *----------------------------------------------------------------------------
+ * Function : reg_def_conf
+ * Purpose : This function is to reset the codec book 0 registers
+ *
+ *----------------------------------------------------------------------------
+ */
+int reg_def_conf(struct snd_soc_codec *codec)
+{
+ int i = 0, ret;
+
+ aic3262_change_page(codec, 0);
+ aic3262_change_book(codec, 0);
+
+ /* Configure the Codec with the default Initialization Values */
+ for (i = 0; i < reg_init_size; i++) {
+ ret = aic3262_write(codec, aic3262_reg_init[i].reg_offset,
+ aic3262_reg_init[i].reg_val);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+/*
+ * i2c_verify_book0
+ *
+ * This function is used to dump the values of the Book 0 Pages.
+ */
+int i2c_verify_book0(struct snd_soc_codec *codec)
+{
+ int i, j, k = 0;
+ u8 val1;
+
+ DBG("starting i2c_verify\n");
+ DBG("Resetting page to 0\n");
+ aic3262_change_book(codec, 0);
+ for (j = 0; j < 3; j++) {
+ if (j == 0) {
+ aic3262_change_page(codec, 0);
+ k = 0;
+ }
+ if (j == 1) {
+ aic3262_change_page(codec, 1);
+ k = 1;
+ }
+ /*
+ if (j == 2) {
+ aic3262_change_page(codec, 4);
+ k = 4;
+ }*/
+ for (i = 0; i <= 127; i++) {
+ val1 = snd_soc_read(codec, i);
+ /* printk("[%d][%d]=[0x%2x]\n",k,i,val1); */
+ }
+ }
+ return 0;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_hw_params
+ * Purpose : This function is to set the hardware parameters for AIC3262.
+ * The functions set the sample rate and audio serial data word
+ * length.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_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 aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ int i, j;
+ u8 data;
+
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ i = aic3262_get_divs(aic3262->sysclk, params_rate(params));
+
+ i2c_verify_book0(codec);
+
+ if (i < 0) {
+ printk(KERN_ERR "sampling rate not supported\n");
+ return i;
+ }
+
+ if (soc_static_freq_config) {
+ /*We will fix R value to 1 and make P & J=K.D as varialble */
+
+ /* Setting P & R values are set to 1 and 1 at init*/
+
+ /* J value */
+ aic3262_write(codec, PLL_J_REG, aic3262_divs[i].pll_j);
+
+ /* MSB & LSB for D value */
+
+ aic3262_write(codec, PLL_D_MSB, (aic3262_divs[i].pll_d >> 8));
+ aic3262_write(codec, PLL_D_LSB,
+ (aic3262_divs[i].pll_d & AIC3262_8BITS_MASK));
+
+ /* NDAC divider value */
+ data = aic3262_read(codec, NDAC_DIV_POW_REG);
+ DBG(KERN_INFO "# reading NDAC = %d , NDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].ndac, data);
+ aic3262_write(codec, NDAC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].ndac)));
+ DBG(KERN_INFO "# writing NDAC = %d , NDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].ndac,
+ ((data & 0x80)|(aic3262_divs[i].ndac)));
+
+ /* MDAC divider value */
+ data = aic3262_read(codec, MDAC_DIV_POW_REG);
+ DBG(KERN_INFO "# reading MDAC = %d , MDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].mdac, data);
+ aic3262_write(codec, MDAC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].mdac)));
+ DBG(KERN_INFO "# writing MDAC = %d , MDAC_DIV_POW_REG = %x\n",
+ aic3262_divs[i].mdac, ((data & 0x80)|(aic3262_divs[i].mdac)));
+
+ /* DOSR MSB & LSB values */
+ aic3262_write(codec, DOSR_MSB_REG, aic3262_divs[i].dosr >> 8);
+ DBG(KERN_INFO "# writing DOSR_MSB_REG = %d\n",
+ (aic3262_divs[i].dosr >> 8));
+ aic3262_write(codec, DOSR_LSB_REG,
+ aic3262_divs[i].dosr & AIC3262_8BITS_MASK);
+ DBG(KERN_INFO "# writing DOSR_LSB_REG = %d\n",
+ (aic3262_divs[i].dosr & AIC3262_8BITS_MASK));
+
+ /* NADC divider value */
+ data = aic3262_read(codec, NADC_DIV_POW_REG);
+ aic3262_write(codec, NADC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].nadc)));
+ DBG(KERN_INFO "# writing NADC_DIV_POW_REG = %d\n",
+ aic3262_divs[i].nadc);
+
+ /* MADC divider value */
+ data = aic3262_read(codec, MADC_DIV_POW_REG);
+ aic3262_write(codec, MADC_DIV_POW_REG,
+ ((data & 0x80)|(aic3262_divs[i].madc)));
+ DBG(KERN_INFO "# writing MADC_DIV_POW_REG = %d\n",
+ aic3262_divs[i].madc);
+
+ /* AOSR value */
+ aic3262_write(codec, AOSR_REG, aic3262_divs[i].aosr);
+ DBG(KERN_INFO "# writing AOSR = %d\n", aic3262_divs[i].aosr);
+ }
+
+
+ data = aic3262_read(codec, ASI1_BUS_FMT);
+
+ data = data & 0xe7;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ data = data | 0x00;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ data |= (0x08);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ data |= (0x10);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ data |= (0x18);
+ break;
+ }
+
+ /* configure the respective Registers for the above configuration */
+ aic3262_write(codec, ASI1_BUS_FMT, data);
+
+ for (j = 0; j < NO_FEATURE_REGS; j++) {
+ aic3262_write(codec,
+ aic3262_divs[i].codec_specific_regs[j].reg_offset,
+ aic3262_divs[i].codec_specific_regs[j].reg_val);
+ }
+
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_ON);
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_mute
+ * Purpose : This function is to mute or unmute the left and right DAC
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#aic3262 codec : mute started\n");
+ if (mute) {
+ DBG("Mute if part\n");
+
+ if (!aic3262->mute_codec) {
+ dac_reg = aic3262_read(codec, DAC_MVOL_CONF);
+ adc_gain = aic3262_read(codec, ADC_FINE_GAIN);
+ hpl = aic3262_read(codec, HPL_VOL);
+ hpr = aic3262_read(codec, HPR_VOL);
+ rec_amp = aic3262_read(codec, REC_AMP_CNTL_R5);
+ rampr = aic3262_read(codec, RAMPR_VOL);
+ spk_amp = aic3262_read(codec, SPK_AMP_CNTL_R4);
+ }
+ DBG("spk_reg = %2x\n\n", spk_amp);
+
+ aic3262_write(codec, DAC_MVOL_CONF, ((dac_reg & 0xF3) | 0x0C));
+ aic3262_write(codec, ADC_FINE_GAIN, ((adc_gain & 0x77) | 0x88));
+ aic3262_write(codec, HPL_VOL, 0xB9);
+ aic3262_write(codec, HPR_VOL, 0xB9);
+ aic3262_write(codec, REC_AMP_CNTL_R5, 0x39);
+ aic3262_write(codec, RAMPR_VOL, 0x39);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, 0x00);
+ aic3262->mute_codec = 1;
+ } else {
+ DBG("Mute else part\n");
+ aic3262_write(codec, DAC_MVOL_CONF, (dac_reg & 0xF3));
+ mdelay(5);
+ aic3262_write(codec, ADC_FINE_GAIN, (adc_gain & 0x77));
+ mdelay(5);
+ aic3262_write(codec, HPL_VOL, hpl);
+ mdelay(5);
+ aic3262_write(codec, HPR_VOL, hpr);
+ mdelay(5);
+ aic3262_write(codec, REC_AMP_CNTL_R5, rec_amp);
+ mdelay(5);
+ aic3262_write(codec, RAMPR_VOL, rampr);
+ mdelay(5);
+ aic3262_write(codec, SPK_AMP_CNTL_R4, spk_amp);
+ mdelay(5);
+ aic3262->mute_codec = 0;
+ }
+ DBG(KERN_INFO "#aic3262 codec : mute ended\n");
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_set_dai_sysclk
+ * Purpose : This function is to set the DAI system clock
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case AIC3262_FREQ_12000000:
+ case AIC3262_FREQ_12288000:
+ aic3262->sysclk = freq;
+ return 0;
+ case AIC3262_FREQ_24000000:
+ aic3262->sysclk = freq;
+ return 0;
+ break;
+ }
+ printk(KERN_ERR "Invalid frequency to set DAI system clock\n");
+ return -EINVAL;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_set_dai_fmt
+ * Purpose : This function is to set the DAI format
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 iface_reg, clk_reg;
+
+ iface_reg = aic3262_read(codec, ASI1_BUS_FMT);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ DBG("setdai_fmt : SND_SOC_DAIFMT_CBM_CFM : master=1\n");
+ aic3262->master = 1;
+ /*Test added */
+ clk_reg = aic3262_read(codec, ASI1_BWCLK_CNTL_REG);
+ clk_reg = (clk_reg & 0x03);
+ aic3262_write(codec, ASI1_BWCLK_CNTL_REG,
+ (clk_reg | 0x24));
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ DBG("setdai_fmt : SND_SOC_DAIFMT_CBS_CFS : master=0\n");
+ aic3262->master = 0;
+ /*Test added */
+ clk_reg = aic3262_read(codec, ASI1_BWCLK_CNTL_REG);
+ aic3262_write(codec, ASI1_BWCLK_CNTL_REG,
+ (clk_reg & 0x03));
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM: /*new case..just for debugging*/
+ DBG("SND_SOC_DAIFMT_CBS_CFM\n");
+ aic3262->master = 0;
+ break;
+ default:
+ printk(KERN_ERR "Invalid DAI master/slave interface\n");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg = (iface_reg & 0x1f);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg = (iface_reg & 0x1f) | 0x20;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x40;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg = (iface_reg & 0x1f) | 0x60;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface_reg = (iface_reg & 0x1f) | 0x80;
+ /* voice call need data offset in 1 bitclock */
+ aic3262_write(codec, ASI1_LCH_OFFSET, 1);
+ break;
+ default:
+ printk(KERN_ERR "Invalid DAI interface format\n");
+ return -EINVAL;
+ }
+
+ aic3262_write(codec, ASI1_BUS_FMT, iface_reg);
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_set_bias_level
+ * Purpose : This function is to get triggered when dapm events occurs.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 value;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ DBG(KERN_INFO "#%s: Codec Active %d[%d]\n",
+ __func__, codec->active, aic3262->active_count);
+ switch (level) {
+ /* full On */
+ case SND_SOC_BIAS_ON:
+ DBG(KERN_INFO "#aic3262 codec : set_bias_on started\n");
+ case SND_SOC_BIAS_PREPARE:
+ /* all power is driven by DAPM system */
+ DBG(KERN_INFO "#aic3262 codec : set_bias_prepare started\n");
+
+ /* Switch on PLL */
+ value = aic3262_read(codec, PLL_PR_POW_REG);
+ aic3262_write(codec, PLL_PR_POW_REG, ((value | 0x80)));
+
+ /* Switch on NDAC Divider */
+ value = aic3262_read(codec, NDAC_DIV_POW_REG);
+ aic3262_write(codec, NDAC_DIV_POW_REG,
+ ((value & 0x7f) | (0x80)));
+
+ /* Switch on MDAC Divider */
+ value = aic3262_read(codec, MDAC_DIV_POW_REG);
+ aic3262_write(codec, MDAC_DIV_POW_REG,
+ ((value & 0x7f) | (0x80)));
+
+ /* Switch on NADC Divider */
+ value = aic3262_read(codec, NADC_DIV_POW_REG);
+ aic3262_write(codec, NADC_DIV_POW_REG,
+ ((value & 0x7f) | (0x80)));
+
+ /* Switch on MADC Divider */
+ value = aic3262_read(codec, MADC_DIV_POW_REG);
+ aic3262_write(codec, MADC_DIV_POW_REG,
+ ((value & 0x7f) | (0x80)));
+
+
+ aic3262_write(codec, ADC_CHANNEL_POW, 0xc2);
+ aic3262_write(codec, ADC_FINE_GAIN, 0x00);
+
+ DBG("#aic3262 codec : set_bias_on complete\n");
+
+ break;
+
+
+ /* Off, with power */
+ case SND_SOC_BIAS_STANDBY:
+ /*
+ * all power is driven by DAPM system,
+ * so output power is safe if bypass was set
+ */
+
+ DBG("#aic3262 codec : set_bias_stby inside if condn\n");
+
+ if (!aic3262->active_count) {
+ /* Switch off NDAC Divider */
+ value = aic3262_read(codec, NDAC_DIV_POW_REG);
+ aic3262_write(codec, NDAC_DIV_POW_REG,
+ (value & 0x7f));
+
+ /* Switch off MDAC Divider */
+ value = aic3262_read(codec, MDAC_DIV_POW_REG);
+ aic3262_write(codec, MDAC_DIV_POW_REG,
+ (value & 0x7f));
+
+ /* Switch off NADC Divider */
+ value = aic3262_read(codec, NADC_DIV_POW_REG);
+ aic3262_write(codec, NADC_DIV_POW_REG,
+ (value & 0x7f));
+
+ /* Switch off MADC Divider */
+ value = aic3262_read(codec, MADC_DIV_POW_REG);
+ aic3262_write(codec, MADC_DIV_POW_REG,
+ (value & 0x7f));
+
+ /* Switch off PLL */
+ value = aic3262_read(codec, PLL_PR_POW_REG);
+ aic3262_write(codec, PLL_PR_POW_REG, (value & 0x7f));
+
+ DBG("#%s: set_bias_stby complete\n", __func__);
+ } else
+ DBG(KERN_INFO
+ "#%s: Another Stream Active. No STANDBY\n", __func__);
+ break;
+
+ /* Off, without power */
+ case SND_SOC_BIAS_OFF:
+ /* force all power off */
+
+ /* Switch off PLL */
+ value = aic3262_read(codec, PLL_PR_POW_REG);
+ aic3262_write(codec,
+ PLL_PR_POW_REG, (value & ~(0x01 << 7)));
+
+ /* Switch off NDAC Divider */
+ value = aic3262_read(codec, NDAC_DIV_POW_REG);
+ aic3262_write(codec, NDAC_DIV_POW_REG,
+ (value & ~(0x01 << 7)));
+
+ /* Switch off MDAC Divider */
+ value = aic3262_read(codec, MDAC_DIV_POW_REG);
+ aic3262_write(codec, MDAC_DIV_POW_REG,
+ (value & ~(0x01 << 7)));
+
+ /* Switch off NADC Divider */
+ value = aic3262_read(codec, NADC_DIV_POW_REG);
+ aic3262_write(codec, NADC_DIV_POW_REG,
+ (value & ~(0x01 << 7)));
+
+ /* Switch off MADC Divider */
+ value = aic3262_read(codec, MADC_DIV_POW_REG);
+ aic3262_write(codec, MADC_DIV_POW_REG,
+ (value & ~(0x01 << 7)));
+ value = aic3262_read(codec, ASI1_BCLK_N);
+
+ break;
+ }
+ codec->dapm.bias_level = level;
+ DBG(KERN_INFO "#aic3262 codec : set_bias exiting\n");
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_suspend
+ * Purpose : This function is to suspend the AIC3262 driver.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ if (aic3262)
+ disable_irq(aic3262->irq);
+
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_resume
+ * Purpose : This function is to resume the AIC3262 driver
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_resume(struct snd_soc_codec *codec)
+{
+ int i;
+ u8 data[2];
+ int ret = 0;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u8 *cache = codec->reg_cache;
+
+ ret = aic3262_change_page(codec, 0);
+ if (ret)
+ return ret;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(aic3262_reg); i++) {
+ data[0] = i % 128;
+ data[1] = cache[i];
+
+ ret = snd_soc_write(codec, data[0], data[1]);
+ if (ret)
+ break;
+ }
+
+ if (!ret) {
+ aic3262_change_page(codec, 0);
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+ if (aic3262)
+ enable_irq(aic3262->irq);
+ }
+ return ret;
+}
+
+/*
+* aic3262_jack_handler
+*
+* This function is called from the Interrupt Handler
+* to check the status of the AIC3262 Registers related to Headset Detection
+*/
+static irqreturn_t aic3262_jack_handler(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+ unsigned int micbits, hsbits = 0;
+
+ DBG("%s++\n", __func__);
+
+ /* Read the Jack Status Register*/
+ value = snd_soc_read(codec, STICKY_FLAG2);
+ DBG(KERN_INFO "reg44 0x%x\n", value);
+
+ value = snd_soc_read(codec, INT_FLAG2);
+ DBG("reg46 0x%x\n", value);
+
+ value = snd_soc_read(codec, DAC_FLAG_R1);
+ DBG("reg37 0x%x\n", value);
+
+ micbits = value & DAC_FLAG_MIC_MASKBITS;
+ DBG("micbits 0x%x\n", micbits);
+
+ hsbits = value & DAC_FLAG_HS_MASKBITS;
+ DBG("hsbits 0x%x\n", hsbits);
+
+ /* sleep for debounce time */
+ msleep(aic3262->pdata->debounce_time_ms);
+
+ /* No Headphone or Headset*/
+ if (!micbits && !hsbits) {
+ snd_soc_jack_report(aic3262->headset_jack,
+ 0, SND_JACK_HEADSET);
+ }
+
+ /* Headphone Detected */
+ if ((micbits == DAC_FLAG_R1_NOMIC) || (hsbits)) {
+ snd_soc_jack_report(aic3262->headset_jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ }
+
+ /* Headset Detected - only with capless */
+ if (micbits == DAC_FLAG_R1_MIC) {
+ snd_soc_jack_report(aic3262->headset_jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+* aic326x_headset_detect
+*
+* Call-back function called to check the status of Headset Pin.
+*/
+int aic326x_headset_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, int jack_type)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ aic3262->headset_jack = jack;
+
+ /*TODO*/
+ aic3262_jack_handler(aic3262->irq, codec);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aic326x_headset_detect);
+
+
+#ifdef AIC3262_MULTI_I2S
+/*
+* aic3262_asi_default_config
+*
+* This function is used to perform the default pin configurations for
+* the functionalities which are specific to each ASI Port of the AIC3262
+* Audio Codec Chipset. The user is encouraged to change these values
+* if required on their platforms.
+*/
+static void aic3262_asi_default_config(struct snd_soc_codec *codec)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u16 counter;
+
+ DBG(KERN_INFO
+ "#%s: Invoked. Will Config ASI Registers to Defaults..\n",
+ __func__);
+ for (counter = 0; counter < MAX_ASI_COUNT; counter++) {
+ aic3262->asiCtxt[counter].asi_active = 0;
+ aic3262->asiCtxt[counter].bclk_div = 1;
+ aic3262->asiCtxt[counter].wclk_div = 1;
+ aic3262->asiCtxt[counter].port_muted = 1;
+ aic3262->asiCtxt[counter].bclk_div_option =
+ BDIV_CLKIN_DAC_MOD_CLK;
+ aic3262->asiCtxt[counter].offset1 = 0;
+ aic3262->asiCtxt[counter].offset2 = 0;
+ }
+ /* ASI1 Defaults */
+ aic3262->asiCtxt[0].bclk_output = ASI1_BCLK_DIVIDER_OUTPUT;
+ aic3262->asiCtxt[0].wclk_output = GENERATED_DAC_FS;
+ aic3262->asiCtxt[0].left_dac_output = DAC_PATH_LEFT;
+ aic3262->asiCtxt[0].right_dac_output = DAC_PATH_LEFT;
+ aic3262->asiCtxt[0].adc_input = ADC_PATH_MINIDSP_1;
+ aic3262->asiCtxt[0].dout_option = ASI_OUTPUT;
+
+ /* ASI2 Defaults */
+ aic3262->asiCtxt[1].bclk_output = ASI2_BCLK_DIVIDER_OUTPUT;
+ aic3262->asiCtxt[1].wclk_output = GENERATED_DAC_FS;
+ aic3262->asiCtxt[1].left_dac_output = DAC_PATH_LEFT;
+ aic3262->asiCtxt[1].right_dac_output = DAC_PATH_LEFT;
+ aic3262->asiCtxt[1].adc_input = ADC_PATH_MINIDSP_2;
+ aic3262->asiCtxt[1].dout_option = ASI_OUTPUT;
+
+ /* ASI3 Defaults */
+ aic3262->asiCtxt[2].bclk_output = ASI3_BCLK_DIVIDER_OUTPUT;
+ aic3262->asiCtxt[2].wclk_output = GENERATED_DAC_FS;
+ aic3262->asiCtxt[2].left_dac_output = DAC_PATH_LEFT;
+ aic3262->asiCtxt[2].right_dac_output = DAC_PATH_LEFT;
+ aic3262->asiCtxt[2].adc_input = ADC_PATH_MINIDSP_3;
+ aic3262->asiCtxt[2].dout_option = ASI2_INPUT;
+
+ return;
+}
+
+#endif /* #ifdef AIC3262_MULTI_I2S */
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_probe
+ * Purpose : This is first driver function called by the SoC core driver.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_probe(struct snd_soc_codec *codec)
+{
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ DBG(KERN_INFO "#%s: Invoked..\n", __func__);
+
+ /* Setting cache bypass - not to overwrite the cache registers,
+ Codec registers have 4 pages which is not handled in the common
+ cache code properly - bypass it in write value and save it
+ using separate call*/
+ codec->cache_bypass = 1;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ ret = reg_def_conf(codec);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to init TI codec: %d\n", ret);
+ return ret;
+ }
+
+ if (aic3262->irq) {
+ /* audio interrupt */
+ ret = request_threaded_irq(aic3262->irq, NULL,
+ aic3262_jack_handler,
+ IRQF_TRIGGER_FALLING,
+ "tlv320aic3262", codec);
+ if (ret) {
+ printk(KERN_INFO "#%s: IRQ Registration failed..[%d]",
+ __func__, ret);
+ dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ } else
+ printk(KERN_INFO
+ "#%s: irq Registration for IRQ %d done..\n",
+ __func__, aic3262->irq);
+ } else {
+ printk(KERN_INFO "#%s: I2C IRQ Configuration is Wrong. \
+ Please check it..\n", __func__);
+ }
+
+ aic3262_asi_default_config(codec);
+
+ /* off, with power on */
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ aic3262_add_controls(codec);
+ aic3262_add_widgets(codec);
+ /*TODO*/
+ aic3262_write(codec, MIC_BIAS_CNTL, 0x66);
+
+#ifdef CONFIG_MINI_DSP
+ /* Program MINI DSP for ADC and DAC */
+ aic3262_minidsp_program(codec);
+ aic3262_add_minidsp_controls(codec);
+ aic3262_change_book(codec, 0x0);
+#endif
+
+#ifdef MULTIBYTE_CONFIG_SUPPORT
+ aic3262_add_multiconfig_controls(codec);
+#endif
+
+ return ret;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_remove
+ * Purpose : to remove aic3262 soc device
+ *
+ *----------------------------------------------------------------------------
+ */
+static int aic3262_remove(struct snd_soc_codec *codec)
+{
+
+ /* power down chip */
+ aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ * @struct snd_soc_codec_device |
+ * This structure is soc audio codec device sturecute which pointer
+ * to basic functions aic3262_probe(), aic3262_remove(),
+ * aic3262_suspend() and aic3262_resume()
+ *----------------------------------------------------------------------------
+ */
+static struct snd_soc_codec_driver soc_codec_dev_aic3262 = {
+ .probe = aic3262_probe,
+ .remove = aic3262_remove,
+ .suspend = aic3262_suspend,
+ .resume = aic3262_resume,
+ .set_bias_level = aic3262_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(aic3262_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = aic3262_reg,
+ .compress_type = SND_SOC_FLAT_COMPRESSION,
+};
+
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_codec_probe
+ * Purpose : This function attaches the i2c client and initializes
+ * AIC3262 CODEC.
+ * NOTE:
+ * This function is called from i2c core when the I2C address is
+ * valid.
+ * If the i2c layer weren't so broken, we could pass this kind of
+ * data around
+ *
+ *----------------------------------------------------------------------------
+ */
+static __devinit int aic3262_codec_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ int ret;
+
+ struct aic3262_priv *aic3262;
+
+ DBG(KERN_INFO "#%s: Entered\n", __func__);
+
+ aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL);
+
+ if (!aic3262) {
+ printk(KERN_ERR "#%s: Unable to Allocate Priv struct..\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c, aic3262);
+ aic3262->control_type = SND_SOC_I2C;
+ aic3262->irq = i2c->irq;
+ aic3262->pdata = i2c->dev.platform_data;
+
+ /* The Configuration Support will be by default to 3 which
+ * holds the MAIN Patch Configuration.
+ */
+ aic3262->current_dac_config[0] = -1;
+ aic3262->current_dac_config[1] = -1;
+ aic3262->current_adc_config[0] = -1;
+ aic3262->current_adc_config[1] = -1;
+
+ aic3262->mute_codec = 1;
+
+ aic3262->page_no = 0;
+ aic3262->book_no = 0;
+ aic3262->active_count = 0;
+ aic3262->dac_clkin_option = 3;
+ aic3262->adc_clkin_option = 3;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_aic3262,
+ tlv320aic3262_dai, ARRAY_SIZE(tlv320aic3262_dai));
+
+ if (ret < 0)
+ kfree(aic3262);
+
+ return ret;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_i2c_remove
+ * Purpose : This function removes the i2c client and uninitializes
+ * AIC3262 CODEC.
+ * NOTE:
+ * This function is called from i2c core
+ * If the i2c layer weren't so broken, we could pass this kind of
+ * data around
+ *
+ *----------------------------------------------------------------------------
+ */
+static __devexit int aic3262_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ kfree(i2c_get_clientdata(i2c));
+ return 0;
+}
+
+static const struct i2c_device_id tlv320aic3262_id[] = {
+ {"tlv320aic3262", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tlv320aic3262_id);
+
+static struct i2c_driver tlv320aic3262_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic3262",
+ .owner = THIS_MODULE,
+ },
+ .probe = aic3262_codec_probe,
+ .remove = __devexit_p(aic3262_i2c_remove),
+ .id_table = tlv320aic3262_id,
+};
+#endif /*#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)*/
+
+static int __init tlv320aic3262_modinit(void)
+{
+ int ret = 0;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&tlv320aic3262_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "Failed to register aic326x i2c driver %d\n",
+ ret);
+#endif
+ return ret;
+
+}
+module_init(tlv320aic3262_modinit);
+
+static void __exit tlv320aic3262_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&tlv320aic3262_i2c_driver);
+#endif
+}
+module_exit(tlv320aic3262_exit);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3262 codec driver");
+MODULE_AUTHOR("Barani Prashanth<gvbarani@mistralsolutions.com>");
+MODULE_AUTHOR("Ravindra<ravindra@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic326x.h b/sound/soc/codecs/tlv320aic326x.h
new file mode 100755
index 000000000000..e6e67350a5e8
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic326x.h
@@ -0,0 +1,629 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic3262.h
+ *
+ *
+ * Copyright (C) 2011 Mistral Solutions Pvt Ltd.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ * Rev 0.1 ASoC driver support Mistral 20-01-2011
+ *
+ * The AIC3262 ASoC driver is ported for the codec AIC3262.
+ *
+ */
+
+#ifndef _TLV320AIC3262_H
+#define _TLV320AIC3262_H
+
+#define AUDIO_NAME "aic3262"
+#define AIC3262_VERSION "1.1"
+
+/* Enable this macro allow for different ASI formats */
+/*#define ASI_MULTI_FMT*/
+#undef ASI_MULTI_FMT
+/* Enable register caching on write */
+#define EN_REG_CACHE
+
+#define MULTIBYTE_CONFIG_SUPPORT
+
+
+/* Macro enables or disables support for miniDSP in the driver */
+/* Enable the AIC3262_TiLoad macro first before enabling these macros */
+#define CONFIG_MINI_DSP
+/*#undef CONFIG_MINI_DSP*/
+
+/* Enable or disable controls to have Input routing*/
+/*#define FULL_IN_CNTL */
+#undef FULL_IN_CNTL
+/* AIC3262 supported sample rate are 8k to 192k */
+#define AIC3262_RATES SNDRV_PCM_RATE_8000_192000
+
+/* AIC3262 supports the word formats 16bits, 20bits, 24bits and 32 bits */
+#define AIC3262_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AIC3262_FREQ_12000000 12000000
+#define AIC3262_FREQ_12288000 12288000
+#define AIC3262_FREQ_24000000 24000000
+
+/* Macro for enabling the Multi_I2S Support in Driver */
+#define AIC3262_MULTI_I2S 1
+
+/* Driver Debug Messages Enabled */
+#define DEBUG
+
+#ifdef DEBUG
+ #define DBG(x...) printk(x)
+#else
+ #define DBG(x...)
+#endif
+
+/*Select the below macro to decide on the DAC master volume controls.
+ *2 independent or one combined
+ */
+/*#define DAC_INDEPENDENT_VOL*/
+#undef DAC_INDEPENDENT_VOL
+
+/* Audio data word length = 16-bits (default setting) */
+#define AIC3262_WORD_LEN_16BITS 0x00
+#define AIC3262_WORD_LEN_20BITS 0x01
+#define AIC3262_WORD_LEN_24BITS 0x02
+#define AIC3262_WORD_LEN_32BITS 0x03
+
+/* sink: name of target widget */
+#define AIC3262_WIDGET_NAME 0
+/* control: mixer control name */
+#define AIC3262_CONTROL_NAME
+/* source: name of source name */
+#define AIC3262_SOURCE_NAME 2
+
+/* D15..D8 aic3262 register offset */
+#define AIC3262_REG_OFFSET_INDEX 0
+/* D7...D0 register data */
+#define AIC3262_REG_DATA_INDEX 1
+
+/* Serial data bus uses I2S mode (Default mode) */
+#define AIC3262_I2S_MODE 0x00
+#define AIC3262_DSP_MODE 0x01
+#define AIC3262_RIGHT_JUSTIFIED_MODE 0x02
+#define AIC3262_LEFT_JUSTIFIED_MODE 0x03
+
+/* 8 bit mask value */
+#define AIC3262_8BITS_MASK 0xFF
+
+/* shift value for CLK_REG_3 register */
+#define CLK_REG_3_SHIFT 6
+/* shift value for DAC_OSR_MSB register */
+#define DAC_OSR_MSB_SHIFT 4
+
+/* number of codec specific register for configuration */
+#define NO_FEATURE_REGS 2
+
+/* Total number of ASI Ports */
+#define MAX_ASI_COUNT 3
+
+/* AIC3262 register space */
+/* Updated from 256 to support Page 3 registers */
+#define AIC3262_CACHEREGNUM 1024
+#define BIT7 (0x01 << 7)
+#define BIT6 (0x01 << 6)
+#define BIT5 (0x01 << 5)
+#define BIT4 (0x01 << 4)
+#define BIT3 (0x01 << 3)
+#define BIT2 (0x01 << 2)
+#define BIT1 (0x01 << 1)
+#define BIT0 (0x01 << 0)
+
+#define DAC_FLAG_MIC_MASKBITS 0x30
+#define DAC_FLAG_HS_MASKBITS 0x03
+#define DAC_FLAG_R1_NOJACK 0
+#define DAC_FLAG_R1_NOMIC (0x1 << 4)
+#define DAC_FLAG_R1_MIC (0x3 << 4)
+#define DAC_FLAG_R1_NOHS 0
+#define DAC_FLAG_R1_MONOHS 1
+#define DAC_FLAG_R1_STEREOHS 2
+/* ****************** Book 0 Registers **************************************/
+
+/* ****************** Page 0 Registers **************************************/
+
+#define PAGE_SEL_REG 0
+#define RESET_REG 1
+#define DAC_ADC_CLKIN_REG 4
+#define PLL_CLKIN_REG 5
+#define PLL_CLK_RANGE_REG 5
+#define PLL_PR_POW_REG 6
+#define PLL_J_REG 7
+#define PLL_D_MSB 8
+#define PLL_D_LSB 9
+#define PLL_CKIN_DIV 10
+
+#define NDAC_DIV_POW_REG 11
+#define MDAC_DIV_POW_REG 12
+#define DOSR_MSB_REG 13
+#define DOSR_LSB_REG 14
+
+#define NADC_DIV_POW_REG 18
+#define MADC_DIV_POW_REG 19
+#define AOSR_REG 20
+#define CLKOUT_MUX 21
+#define CLKOUT_MDIV_VAL 22
+#define TIMER_REG 23
+
+#define LF_CLK_CNTL 24
+#define HF_CLK_CNTL_R1 25
+#define HF_CLK_CNTL_R2 26
+#define HF_CLK_CNTL_R3 27
+#define HF_CLK_CNTL_R4 28
+#define HF_CLK_TRIM_R1 29
+#define HF_CLK_TRIM_R2 30
+#define HF_CLK_TRIM_R3 31
+#define HF_CLK_TRIM_R4 32
+#define DAC_FLAG_R1 37
+#define DAC_FLAG_R2 38
+
+#define STICKY_FLAG1 42
+#define INT_FLAG1 43
+#define STICKY_FLAG2 44
+#define INT_FLAG2 46
+#define INT1_CNTL 48
+#define INT2_CNTL 49
+#define INT_FMT 51
+
+#define DAC_PRB 60
+#define ADC_PRB 61
+#define PASI_DAC_DP_SETUP 63
+#define DAC_MVOL_CONF 64
+#define DAC_LVOL 65
+#define DAC_RVOL 66
+#define HP_DETECT 67
+#define DRC_CNTL_R1 68
+#define DRC_CNTL_R2 69
+#define DRC_CNTL_R3 70
+#define BEEP_CNTL_R1 71
+#define BEEP_CNTL_R2 72
+
+#define ADC_CHANNEL_POW 81
+#define ADC_FINE_GAIN 82
+#define LADC_VOL 83
+#define RADC_VOL 84
+#define ADC_PHASE 85
+
+#define LAGC_CNTL 86
+#define LAGC_CNTL_R2 87
+#define LAGC_CNTL_R3 88
+#define LAGC_CNTL_R4 89
+#define LAGC_CNTL_R5 90
+#define LAGC_CNTL_R6 91
+#define LAGC_CNTL_R7 92
+#define LAGC_CNTL_R8 93
+
+#define RAGC_CNTL 94
+#define RAGC_CNTL_R2 95
+#define RAGC_CNTL_R3 96
+#define RAGC_CNTL_R4 97
+#define RAGC_CNTL_R5 98
+#define RAGC_CNTL_R6 99
+#define RAGC_CNTL_R7 100
+#define RAGC_CNTL_R8 101
+#define MINIDSP_ACCESS_CTRL 121
+/* ****************** Page 1 Registers **************************************/
+#define PAGE_1 128
+
+#define POWER_CONF (PAGE_1 + 1)
+#define LDAC_PTM (PAGE_1 + 3)
+#define RDAC_PTM (PAGE_1 + 4)
+#define CM_REG (PAGE_1 + 8)
+#define HP_CTL (PAGE_1 + 9)
+#define HP_DEPOP (PAGE_1 + 11)
+#define RECV_DEPOP (PAGE_1 + 12)
+#define MA_CNTL (PAGE_1 + 17)
+#define LADC_PGA_MAL_VOL (PAGE_1 + 18)
+#define RADC_PGA_MAR_VOL (PAGE_1 + 19)
+
+
+#define LINE_AMP_CNTL_R1 (PAGE_1 + 22)
+#define LINE_AMP_CNTL_R2 (PAGE_1 + 23)
+
+#define HP_AMP_CNTL_R1 (PAGE_1 + 27)
+#define HP_AMP_CNTL_R2 (PAGE_1 + 28)
+#define HP_AMP_CNTL_R3 (PAGE_1 + 29)
+
+#define HPL_VOL (PAGE_1 + 31)
+#define HPR_VOL (PAGE_1 + 32)
+#define INT1_SEL_L (PAGE_1 + 34)
+#define RAMP_CNTL_R1 (PAGE_1 + 36)
+#define RAMP_CNTL_R2 (PAGE_1 + 37)
+#define INT1_SEL_RM (PAGE_1 + 39)
+#define REC_AMP_CNTL_R5 (PAGE_1 + 40)
+#define RAMPR_VOL (PAGE_1 + 41)
+#define RAMP_TIME_CNTL (PAGE_1 + 42)
+#define SPK_AMP_CNTL_R1 (PAGE_1 + 45)
+#define SPK_AMP_CNTL_R2 (PAGE_1 + 46)
+#define SPK_AMP_CNTL_R3 (PAGE_1 + 47)
+#define SPK_AMP_CNTL_R4 (PAGE_1 + 48)
+#define MIC_BIAS_CNTL (PAGE_1 + 51)
+
+#define LMIC_PGA_PIN (PAGE_1 + 52)
+#define LMIC_PGA_PM_IN4 (PAGE_1 + 53)
+#define LMIC_PGA_MIN (PAGE_1 + 54)
+#define RMIC_PGA_PIN (PAGE_1 + 55)
+#define RMIC_PGA_PM_IN4 (PAGE_1 + 56)
+#define RMIC_PGA_MIN (PAGE_1 + 57)
+/* MIC PGA Gain Registers */
+#define MICL_PGA (PAGE_1 + 59)
+#define MICR_PGA (PAGE_1 + 60)
+#define HEADSET_TUNING1_REG (PAGE_1 + 119)
+#define HEADSET_TUNING2_REG (PAGE_1 + 120)
+#define MIC_PWR_DLY (PAGE_1 + 121)
+#define REF_PWR_DLY (PAGE_1 + 122)
+
+/* ****************** Page 4 Registers **************************************/
+#define PAGE_4 512
+#define ASI1_BUS_FMT (PAGE_4 + 1)
+#define ASI1_LCH_OFFSET (PAGE_4 + 2)
+#define ASI1_RCH_OFFSET (PAGE_4 + 3)
+#define ASI1_CHNL_SETUP (PAGE_4 + 4)
+#define ASI1_MULTI_CH_SETUP_R1 (PAGE_4 + 5)
+#define ASI1_MULTI_CH_SETUP_R2 (PAGE_4 + 6)
+#define ASI1_ADC_INPUT_CNTL (PAGE_4 + 7)
+#define ASI1_DAC_OUT_CNTL (PAGE_4 + 8)
+#define ASI1_ADC_OUT_TRISTATE (PAGE_4 + 9)
+#define ASI1_BWCLK_CNTL_REG (PAGE_4 + 10)
+#define ASI1_BCLK_N_CNTL (PAGE_4 + 11)
+#define ASI1_BCLK_N (PAGE_4 + 12)
+#define ASI1_WCLK_N (PAGE_4 + 13)
+#define ASI1_BWCLK_OUT_CNTL (PAGE_4 + 14)
+#define ASI1_DATA_OUT (PAGE_4 + 15)
+#define ASI2_BUS_FMT (PAGE_4 + 17)
+#define ASI2_LCH_OFFSET (PAGE_4 + 18)
+#define ASI2_RCH_OFFSET (PAGE_4 + 19)
+#define ASI2_ADC_INPUT_CNTL (PAGE_4 + 23)
+#define ASI2_DAC_OUT_CNTL (PAGE_4 + 24)
+#define ASI2_BWCLK_CNTL_REG (PAGE_4 + 26)
+#define ASI2_BCLK_N_CNTL (PAGE_4 + 27)
+#define ASI2_BCLK_N (PAGE_4 + 28)
+#define ASI2_WCLK_N (PAGE_4 + 29)
+#define ASI2_BWCLK_OUT_CNTL (PAGE_4 + 30)
+#define ASI2_DATA_OUT (PAGE_4 + 31)
+#define ASI3_BUS_FMT (PAGE_4 + 33)
+#define ASI3_LCH_OFFSET (PAGE_4 + 34)
+#define ASI3_RCH_OFFSET (PAGE_4 + 35)
+#define ASI3_ADC_INPUT_CNTL (PAGE_4 + 39)
+#define ASI3_DAC_OUT_CNTL (PAGE_4 + 40)
+#define ASI3_BWCLK_CNTL_REG (PAGE_4 + 42)
+#define ASI3_BCLK_N (PAGE_4 + 44)
+#define ASI3_WCLK_N (PAGE_4 + 45)
+#define ASI3_BWCLK_OUT_CNTL (PAGE_4 + 46)
+#define ASI3_DATA_OUT (PAGE_4 + 47)
+#define WCLK1_PIN_CNTL_REG (PAGE_4 + 65)
+#define DOUT1_PIN_CNTL_REG (PAGE_4 + 67)
+#define DIN1_PIN_CNTL_REG (PAGE_4 + 68)
+#define WCLK2_PIN_CNTL_REG (PAGE_4 + 69)
+#define BCLK2_PIN_CNTL_REG (PAGE_4 + 70)
+#define DOUT2_PIN_CNTL_REG (PAGE_4 + 71)
+#define DIN2_PIN_CNTL_REG (PAGE_4 + 72)
+#define WCLK3_PIN_CNTL_REG (PAGE_4 + 73)
+#define BCLK3_PIN_CNTL_REG (PAGE_4 + 74)
+#define DOUT3_PIN_CNTL_REG (PAGE_4 + 75)
+#define DIN3_PIN_CNTL_REG (PAGE_4 + 76)
+#define MCLK2_PIN_CNTL_REG (PAGE_4 + 82)
+#define GPIO1_IO_CNTL (PAGE_4 + 86)
+#define GPIO2_IO_CNTL (PAGE_4 + 87)
+#define GPI1_EN (PAGE_4 + 91)
+#define GPO2_EN (PAGE_4 + 92)
+#define GPO1_PIN_CNTL (PAGE_4 + 96)
+#define MINIDSP_PORT_CNTL_REG (PAGE_4 + 118)
+
+/****************************************************************************
+* Mixer control related #defines
+***************************************************************************
+*/
+#define WCLK1_ENUM 0
+#define DOUT1_ENUM 1
+#define DIN1_ENUM 2
+#define WCLK2_ENUM 3
+#define BCLK2_ENUM 4
+#define DOUT2_ENUM 5
+#define DIN2_ENUM 6
+#define WCLK3_ENUM 7
+#define BCLK3_ENUM 8
+#define DOUT3_ENUM 9
+#define DIN3_ENUM 10
+#define CLKIN_ENUM 11
+/*
+*****************************************************************************
+* Enumeration Definitions
+*****************************************************************************
+*/
+/* The below enumeration lists down all the possible inputs to the
+* the PLL of the AIC3262. The Private structure will hold a member
+* of this Enumeration Type.
+*/
+enum AIC3262_PLL_OPTION {
+ PLL_CLKIN_MCLK1 = 0, /* 0000: (Device Pin) */
+ PLL_CLKIN_BLKC1, /* 0001: (Device Pin) */
+ PLL_CLKIN_GPIO1, /* 0010: (Device Pin)*/
+ PLL_CLKIN_DIN1, /* 0011: (Device Pin)*/
+ PLL_CLKIN_BCLK2, /* 0100: (Device Pin)*/
+ PLL_CLKIN_GPI1, /* 0101: (Device Pin)*/
+ PLL_CLKIN_HF_REF_CLK, /* 0110: (Device Pin)*/
+ PLL_CLKIN_GPIO2, /* 0111: (Device Pin)*/
+ PLL_CLKIN_GPI2, /* 1000: (Device Pin)*/
+ PLL_CLKIN_MCLK2 /* 1001: (Device Pin)*/
+};
+
+/* ASI Specific Bit Clock Divider Input Options.
+* Please refer to Page 4 Reg 11, Reg 27 and Reg 43
+*/
+enum ASI_BDIV_CLKIN_OPTION {
+ BDIV_CLKIN_DAC_CLK = 0, /* 00 DAC_CLK */
+ BDIV_CLKIN_DAC_MOD_CLK, /* 01 DAC_MOD_CLK */
+ BDIV_CLKIN_ADC_CLK, /* 02 ADC_CLK */
+ BDIV_CLKIN_ADC_MOD_CLK /* 03 ADC_MOD_CLK */
+};
+
+/* ASI Specific Bit Clock Output Mux Options.
+* Please refer to Page 4 Reg 14, Reg 30 and Reg 46
+* Please note that we are not handling the Reserved
+* cases here.
+*/
+enum ASI_BCLK_OPTION {
+ ASI1_BCLK_DIVIDER_OUTPUT = 0, /* 00 ASI1 Bit Clock Divider Output */
+ ASI1_BCLK_INPUT, /* 01 ASI1 Bit Clock Input */
+ ASI2_BCLK_DIVIDER_OUTPUT, /* 02 ASI2 Bit Clock Divider Output */
+ ASI2_BCLK_INPUT, /* 03 ASI2 Bit Clock Input */
+ ASI3_BCLK_DIVIDER_OUTPUT, /* 04 ASI3 Bit Clock Divider Output */
+ ASI3_BBCLK_INPUT /* 05 ASi3 Bit Clock Input */
+};
+
+/* Above bits are to be configured after Shifting 4 bits */
+#define AIC3262_ASI_BCLK_MUX_SHIFT 4
+#define AIC3262_ASI_BCLK_MUX_MASK (BIT6 | BIT5 | BIT4)
+#define AIC3262_ASI_WCLK_MUX_MASK (BIT2 | BIT1 | BIT0)
+
+/* ASI Specific Word Clock Output Mux Options */
+enum ASI_WCLK_OPTION {
+ GENERATED_DAC_FS = 0, /* 00 WCLK = DAC_FS */
+ GENERATED_ADC_FS = 1, /* 01 WCLK = ADC_FS */
+ ASI1_WCLK_DIV_OUTPUT = 2, /* 02 WCLK = ASI1 WCLK_DIV_OUT */
+ ASI1_WCLK_INPUT = 3, /* 03 WCLK = ASI1 WCLK Input */
+ ASI2_WCLK_DIV_OUTPUT = 4, /* 04 WCLK = ASI2 WCLK_DIV_OUT */
+ ASI2_WCLK_INPUT = 5, /* 05 WCLK = ASI2 WCLK Input */
+ ASI3_WCLK_DIV_OUTPUT = 6, /* 06 WCLK = ASI3 WCLK_DIV_OUT */
+ ASI3_WCLK_INPUT = 7 /* 07 WCLK = ASI3 WCLK Input */
+};
+
+/* ASI DAC Output Control Options */
+enum ASI_DAC_OUTPUT_OPTION {
+ DAC_PATH_OFF = 0, /* 00 DAC Datapath Off */
+ DAC_PATH_LEFT, /* 01 DAC Datapath left Data */
+ DAC_PATH_RIGHT, /* 02 DAC Datapath Right Data */
+};
+
+/* Shift the above options by so many bits */
+#define AIC3262_ASI_LDAC_PATH_SHIFT 6
+#define AIC3262_ASI_LDAC_PATH_MASK (BIT5 | BIT4)
+#define AIC3262_ASI_RDAC_PATH_SHIFT 4
+#define AIC3262_ASI_RDAC_PATH_MASK (BIT7 | BIT6)
+
+/* ASI specific ADC Input Control Options */
+enum ASI_ADC_INPUT_OPTION {
+ ADC_PATH_OFF = 0, /* 00 ASI Digital Output Disabled */
+ ADC_PATH_MINIDSP_1, /* 01 ASI Digital O/P from miniDSP_A(L1,R1) */
+ ADC_PATH_ASI1, /* 02 ASI Digital Output from ASI1 */
+ ADC_PATH_ASI2, /* 03 ASI Digital Output from ASI2 */
+ ADC_PATH_ASI3, /* 04 ASI Digital Output from ASI3 */
+ ADC_PATH_MINIDSP_2, /* 05 ASI Digital O/P from miniDSP_A(L2,R2) */
+ ADC_PATH_MINIDSP_3 /* 05 ASI Digital O/P from miniDSP_A(L3,R3) */
+};
+
+/* ASI Specific DOUT Pin Options */
+enum ASI_DOUT_OPTION {
+ ASI_OUTPUT = 0, /* 00 Default ASI Output */
+ ASI1_INPUT, /* 01 ASI1 Data Input */
+ ASI2_INPUT, /* 02 ASI2 Data Input */
+ ASI3_INPUT /* 03 ASI3 Data Input */
+};
+
+#define AIC3262_ASI_DOUT_MASK (BIT1 | BIT0)
+
+/*
+ *****************************************************************************
+ * Structures Definitions
+ *****************************************************************************
+ */
+#define AIC3262_MULTI_ASI_ACTIVE(x) (((x)->asiCtxt[0].asi_active) || \
+ ((x)->asiCtxt[1].asi_active) || \
+ ((x)->asiCtxt[2].asi_active))
+
+/*
+*----------------------------------------------------------------------------
+* @struct aic3262_setup_data |
+* i2c specific data setup for AIC3262.
+* @field unsigned short |i2c_address |
+* Unsigned short for i2c address.
+*----------------------------------------------------------------------------
+*/
+ struct aic3262_setup_data {
+ unsigned short i2c_address;
+};
+
+/*
+*----------------------------------------------------------------------------
+* @struct aic3262_asi_data
+* ASI specific data stored for each ASI Interface
+*
+*
+*---------------------------------------------------------------------------
+*/
+struct aic3262_asi_data {
+ u8 asi_active; /* ASI Active Flag */
+ u8 master; /* Frame Master */
+ u32 sampling_rate; /* Sampling Rate */
+ enum ASI_BDIV_CLKIN_OPTION bclk_div_option; /* BCLK DIV Mux Option*/
+ enum ASI_BCLK_OPTION bclk_output; /* BCLK Output Option*/
+ enum ASI_WCLK_OPTION wclk_output; /* WCLK Output Option*/
+ u8 bclk_div; /* BCLK Divider */
+ u8 wclk_div; /* WCLK Divider */
+ enum ASI_DAC_OUTPUT_OPTION left_dac_output; /* LDAC Path */
+ enum ASI_DAC_OUTPUT_OPTION right_dac_output; /* RDAC Path */
+ enum ASI_ADC_INPUT_OPTION adc_input; /* ADC Input Control */
+ enum ASI_DOUT_OPTION dout_option; /* DOUT Option */
+ u8 playback_mode; /* Playback Selected */
+ u8 capture_mode; /* Record Selected */
+ u8 port_muted; /* ASI Muted */
+ u8 pcm_format; /* PCM Format */
+ u8 word_len; /* Word Length */
+ u8 offset1; /* Left Ch offset */
+ u8 offset2; /* Right Ch Offset */
+};
+
+/*
+*----------------------------------------------------------------------------
+* @struct aic3262_priv |
+* AIC3262 priviate data structure to set the system clock, mode and
+* page number.
+* @field u32 | sysclk |
+* system clock
+* @field s32 | master |
+* master/slave mode setting for AIC3262
+* @field u8 | book_no |
+* book number.
+* @field u8 | page_no |
+* page number. Here, page 0 and page 1 are used.
+*----------------------------------------------------------------------------
+*/
+struct aic3262_priv {
+ enum snd_soc_control_type control_type;
+ struct aic326x_pdata *pdata;
+ u32 sysclk;
+ s32 master;
+ u8 book_no;
+ u8 page_no;
+ u8 process_flow;
+ u8 mute_codec;
+ u8 stream_status;
+ u32 active_count;
+ int current_dac_config[MAX_ASI_COUNT];
+ int current_adc_config[MAX_ASI_COUNT];
+ struct aic3262_asi_data asiCtxt[MAX_ASI_COUNT];
+ enum AIC3262_PLL_OPTION aic3262_pllclkin_option;
+ u8 dac_clkin_option;
+ u8 adc_clkin_option;
+ int irq;
+ struct snd_soc_jack *headset_jack;
+};
+
+/*
+ *----------------------------------------------------------------------------
+ * @struct aic3262_configs |
+ * AIC3262 initialization data which has register offset and register
+ * value.
+ * @field u8 | book_no |
+ * AIC3262 Book Number Offsets required for initialization..
+ * @field u16 | reg_offset |
+ * AIC3262 Register offsets required for initialization..
+ * @field u8 | reg_val |
+ * value to set the AIC3262 register to initialize the AIC3262.
+ *----------------------------------------------------------------------------
+ */
+struct aic3262_configs {
+ u8 book_no;
+ u16 reg_offset;
+ u8 reg_val;
+};
+
+/*
+ *----------------------------------------------------------------------------
+ * @struct aic3262_rate_divs |
+ * Setting up the values to get different freqencies
+ * @field u32 | mclk |
+ * Master clock
+ * @field u32 | rate |
+ * sample rate
+ * @field u8 | p_val |
+ * value of p in PLL
+ * @field u32 | pll_j |
+ * value for pll_j
+ * @field u32 | pll_d |
+ * value for pll_d
+ * @field u32 | dosr |
+ * value to store dosr
+ * @field u32 | ndac |
+ * value for ndac
+ * @field u32 | mdac |
+ * value for mdac
+ * @field u32 | aosr |
+ * value for aosr
+ * @field u32 | nadc |
+ * value for nadc
+ * @field u32 | madc |
+ * value for madc
+ * @field u32 | blck_N |
+ * value for block N
+ * @field u32 | aic3262_configs |
+ * configurations for aic3262 register value
+ *----------------------------------------------------------------------------
+ */
+struct aic3262_rate_divs {
+ u32 mclk;
+ u32 rate;
+ u8 p_val;
+ u8 pll_j;
+ u16 pll_d;
+ u16 dosr;
+ u8 ndac;
+ u8 mdac;
+ u8 aosr;
+ u8 nadc;
+ u8 madc;
+ u8 blck_N;
+ struct aic3262_configs codec_specific_regs[NO_FEATURE_REGS];
+};
+
+/*
+*****************************************************************************
+* EXTERN DECLARATIONS
+*****************************************************************************
+*/
+/*
+ *----------------------------------------------------------------------------
+ * @func aic326x_headset_detect
+ * This function help to setup the needed registers to
+ * enable the headset detection
+ *
+ */
+extern int aic326x_headset_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, int jack_type);
+
+
+extern u8 aic3262_read(struct snd_soc_codec *codec, u16 reg);
+extern u16 aic3262_read_2byte(struct snd_soc_codec *codec, u16 reg);
+extern int aic3262_reset_cache(struct snd_soc_codec *codec);
+extern int aic3262_change_page(struct snd_soc_codec *codec, u8 new_page);
+extern int aic3262_write(struct snd_soc_codec *codec, u16 reg, u8 value);
+extern void aic3262_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, u8 value);
+extern int aic3262_change_book(struct snd_soc_codec *codec, u8 new_book);
+extern int reg_def_conf(struct snd_soc_codec *codec);
+extern int i2c_verify_book0(struct snd_soc_codec *codec);
+
+#ifdef CONFIG_MINI_DSP
+extern int aic3262_minidsp_program(struct snd_soc_codec *codec);
+extern int aic3262_add_minidsp_controls(struct snd_soc_codec *codec);
+#endif
+
+
+#ifdef MULTIBYTE_CONFIG_SUPPORT
+extern int aic3262_add_multiconfig_controls(struct snd_soc_codec *codec);
+#endif
+
+#endif /* _TLV320AIC3262_H */
+
diff --git a/sound/soc/codecs/tlv320aic326x_mini-dsp.c b/sound/soc/codecs/tlv320aic326x_mini-dsp.c
new file mode 100755
index 000000000000..4d9c4de7e59a
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic326x_mini-dsp.c
@@ -0,0 +1,1587 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic326x_mini-dsp.c
+ *
+ * Copyright (C) 2011 Mistral Solutions Pvt Ltd.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio
+ * codec with digital microphone inputs and programmable outputs.
+ *
+ * History:
+ *
+ * Rev 0.1 Added the miniDSP Support Mistral 01-03-2011
+ *
+ * Rev 0.2 Updated the code-base for miniDSP switching and
+ * mux control update. Mistral 21-03-2011
+ *
+ * Rev 0.3 Updated the code-base to support Multi-Configuration feature
+ * of PPS GDE
+ */
+
+/*
+ *****************************************************************************
+ * INCLUDES
+ *****************************************************************************
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/core.h>
+#include <sound/soc-dapm.h>
+#include <sound/control.h>
+#include <linux/time.h> /* For timing computations */
+#include "tlv320aic326x.h"
+#include "tlv320aic326x_mini-dsp.h"
+
+#include "first_rate_pps_driver.h"
+#include "second_rate_pps_driver.h"
+
+#ifdef CONFIG_MINI_DSP
+
+#ifdef REG_DUMP_MINIDSP
+static void aic3262_dump_page(struct i2c_client *i2c, u8 page);
+#endif
+
+/*
+ *****************************************************************************
+ * LOCAL STATIC DECLARATIONS
+ *****************************************************************************
+ */
+static int m_control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+static int m_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int m_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+/*
+ *****************************************************************************
+ * MINIDSP RELATED GLOBALS
+ *****************************************************************************
+ */
+/* The below variable is used to maintain the I2C Transactions
+ * to be carried out during miniDSP switching.
+ */
+minidsp_parser_data dsp_parse_data[MINIDSP_PARSER_ARRAY_SIZE*2];
+
+struct i2c_msg i2c_transaction[MINIDSP_PARSER_ARRAY_SIZE * 2];
+/* Total count of I2C Messages are stored in the i2c_count */
+int i2c_count;
+
+/* The below array is used to store the burst array for I2C Multibyte
+ * Operations
+ */
+minidsp_i2c_page i2c_page_array[MINIDSP_PARSER_ARRAY_SIZE];
+int i2c_page_count;
+
+/* kcontrol structure used to register with ALSA Core layer */
+static struct snd_kcontrol_new snd_mux_controls[MAX_MUX_CONTROLS];
+
+/* mode variables */
+static int amode;
+static int dmode;
+
+/* k-control macros used for miniDSP related Kcontrols */
+#define SOC_SINGLE_VALUE_M(xmax, xinvert) \
+ ((unsigned long)&(struct soc_mixer_control) \
+ {.max = xmax, \
+ .invert = xinvert})
+#define SOC_SINGLE_M(xname, max, invert) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = m_control_info, .get = m_control_get,\
+ .put = m_control_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .private_value = SOC_SINGLE_VALUE_M(max, invert) }
+#define SOC_SINGLE_AIC3262_M(xname) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = m_control_info, .get = m_control_get,\
+ .put = m_control_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+}
+
+/*
+ * aic3262_minidsp_controls
+ *
+ * Contains the list of the Kcontrol macros required for modifying the
+ * miniDSP behavior at run-time.
+ */
+static const struct snd_kcontrol_new aic3262_minidsp_controls[] = {
+ SOC_SINGLE_AIC3262_M("Minidsp mode") ,
+ SOC_SINGLE_AIC3262_M("ADC Adaptive mode Enable") ,
+ SOC_SINGLE_AIC3262_M("DAC Adaptive mode Enable") ,
+ SOC_SINGLE_AIC3262_M("Dump Regs Book0") ,
+ SOC_SINGLE_AIC3262_M("Verify minidsp program") ,
+};
+
+#ifdef REG_DUMP_MINIDSP
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_dump_page
+ * Purpose : Read and display one codec register page, for debugging purpose
+ *----------------------------------------------------------------------------
+ */
+static void aic3262_dump_page(struct i2c_client *i2c, u8 page)
+{
+ int i;
+ u8 data;
+ u8 test_page_array[256];
+
+ aic3262_change_page(codec, page);
+
+ data = 0x0;
+
+ i2c_master_send(i2c, data, 1);
+ i2c_master_recv(i2c, test_page_array, 128);
+
+ DBG("\n------- MINI_DSP PAGE %d DUMP --------\n", page);
+ for (i = 0; i < 128; i++)
+ DBG(KERN_INFO " [ %d ] = 0x%x\n", i, test_page_array[i]);
+
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : update_kcontrols
+ * Purpose : Given the miniDSP process flow, this function reads the
+ * corresponding Page Numbers and then performs I2C Read for those
+ * Pages.
+ *----------------------------------------------------------------------------
+ */
+void update_kcontrols(struct snd_soc_codec *codec, int process_flow)
+{
+ int i, val1, array_size;
+ char **knames;
+ control *cntl;
+
+ if (process_flow == 1) {
+ knames = Second_Rate_MUX_control_names;
+ cntl = Second_Rate_MUX_controls;
+ array_size = ARRAY_SIZE(Second_Rate_MUX_controls);
+ } else {
+ knames = main44_MUX_control_names;
+ cntl = main44_MUX_controls;
+ array_size = ARRAY_SIZE(main44_MUX_controls);
+ }
+
+ DBG(KERN_INFO "%s: ARRAY_SIZE = %d\tmode=%d\n", __func__,
+ array_size, process_flow);
+ for (i = 0; i < array_size; i++) {
+ aic3262_change_book(codec, cntl[i].control_book);
+ aic3262_change_page(codec, cntl[i].control_page);
+ val1 = i2c_smbus_read_byte_data(codec->control_data,
+ cntl[i].control_base);
+ snd_mux_controls[i].private_value = 0;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : byte_i2c_array_transfer
+ * Purpose : Function used only for debugging purpose. This function will
+ * be used while switching miniDSP Modes register by register.
+ * This needs to be used only during development.
+ *-----------------------------------------------------------------------------
+ */
+int byte_i2c_array_transfer(struct snd_soc_codec *codec,
+ reg_value *program_ptr,
+ int size)
+{
+ int j;
+ u8 buf[3];
+
+ for (j = 0; j < size; j++) {
+ /* Check if current Reg offset is zero */
+ if (program_ptr[j].reg_off == 0) {
+ /* Check for the Book Change Request */
+ if ((j < (size - 1)) &&
+ (program_ptr[j+1].reg_off == 127)) {
+ aic3262_change_book(codec,
+ program_ptr[j+1].reg_val);
+ /* Increment for loop counter across Book Change */
+ j++;
+ continue;
+ }
+ /* Check for the Page Change Request in Current book */
+ aic3262_change_page(codec, program_ptr[j].reg_val);
+ continue;
+ }
+
+ buf[AIC3262_REG_OFFSET_INDEX] = program_ptr[j].reg_off % 128;
+ buf[AIC3262_REG_DATA_INDEX] =
+ program_ptr[j].reg_val & AIC3262_8BITS_MASK;
+
+ if (codec->hw_write(codec->control_data, buf, 2) != 2) {
+ printk(KERN_ERR "Error in i2c write\n");
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : byte_i2c_array_read
+ * Purpose : This function is used to perform Byte I2C Read. This is used
+ * only for debugging purposes to read back the Codec Page
+ * Registers after miniDSP Configuration.
+ *----------------------------------------------------------------------------
+ */
+int byte_i2c_array_read(struct snd_soc_codec *codec,
+ reg_value *program_ptr, int size)
+{
+ int j;
+ u8 val1;
+ u8 cur_page = 0;
+ u8 cur_book = 0;
+ for (j = 0; j < size; j++) {
+ /* Check if current Reg offset is zero */
+ if (program_ptr[j].reg_off == 0) {
+ /* Check for the Book Change Request */
+ if ((j < (size - 1)) &&
+ (program_ptr[j+1].reg_off == 127)) {
+ aic3262_change_book(codec,
+ program_ptr[j+1].reg_val);
+ cur_book = program_ptr[j+1].reg_val;
+ /* Increment for loop counter across Book Change */
+ j++;
+ continue;
+ }
+ /* Check for the Page Change Request in Current book */
+ aic3262_change_page(codec, program_ptr[j].reg_val);
+ cur_page = program_ptr[j].reg_val;
+ continue;
+ }
+
+ val1 = i2c_smbus_read_byte_data(codec->control_data,
+ program_ptr[j].reg_off);
+ if (val1 < 0)
+ printk(KERN_ERR "Error in smbus read\n");
+
+ DBG(KERN_INFO "[%d][%d][%d]= %x\n",
+ cur_book, cur_page, program_ptr[j].reg_off, val1);
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : minidsp_get_burst
+ * Purpose : Format one I2C burst for transfer from mini dsp program array.
+ * This function will parse the program array and get next burst
+ * data for doing an I2C bulk transfer.
+ *----------------------------------------------------------------------------
+ */
+static void
+minidsp_get_burst(reg_value *program_ptr,
+ int program_size,
+ minidsp_parser_data *parse_data)
+{
+ int index = parse_data->current_loc;
+ int burst_write_count = 0;
+
+ /*DBG("GET_BURST: start\n");*/
+ /* check if first location is page register, and populate page addr */
+ if (program_ptr[index].reg_off == 0) {
+ if ((index < (program_size - 1)) &&
+ (program_ptr[index+1].reg_off == 127)) {
+ parse_data->book_change = 1;
+ parse_data->book_no = program_ptr[index+1].reg_val;
+ index += 2;
+ goto finish_out;
+
+ }
+ parse_data->page_num = program_ptr[index].reg_val;
+ parse_data->burst_array[burst_write_count++] =
+ program_ptr[index].reg_off;
+ parse_data->burst_array[burst_write_count++] =
+ program_ptr[index].reg_val;
+ index++;
+ goto finish_out;
+ }
+
+ parse_data->burst_array[burst_write_count++] =
+ program_ptr[index].reg_off;
+ parse_data->burst_array[burst_write_count++] =
+ program_ptr[index].reg_val;
+ index++;
+
+ for (; index < program_size; index++) {
+ if (program_ptr[index].reg_off !=
+ (program_ptr[index - 1].reg_off + 1))
+ break;
+ else
+ parse_data->burst_array[burst_write_count++] =
+ program_ptr[index].reg_val;
+
+ }
+finish_out:
+ parse_data->burst_size = burst_write_count;
+ if (index == program_size)
+ /* parsing completed */
+ parse_data->current_loc = MINIDSP_PARSING_END;
+ else
+ parse_data->current_loc = index;
+ /*DBG("GET_BURST: end\n");*/
+}
+/*
+ *----------------------------------------------------------------------------
+ * Function : minidsp_i2c_multibyte_transfer
+ * Purpose : Function used to perform multi-byte I2C Writes. Used to configure
+ * the miniDSP Pages.
+ *----------------------------------------------------------------------------
+ */
+int
+minidsp_i2c_multibyte_transfer(struct snd_soc_codec *codec,
+ reg_value *program_ptr,
+ int program_size)
+{
+ struct i2c_client *client = codec->control_data;
+
+ minidsp_parser_data parse_data;
+ int count = 0;
+
+#ifdef DEBUG_MINIDSP_LOADING
+ int i = 0, j = 0;
+#endif
+ /* point the current location to start of program array */
+ parse_data.current_loc = 0;
+ parse_data.page_num = 0;
+ parse_data.book_change = 0;
+ parse_data.book_no = 0;
+
+ DBG(KERN_INFO "size is : %d", program_size);
+ do {
+ do {
+ /* Get first burst data */
+ minidsp_get_burst(program_ptr, program_size,
+ &parse_data);
+ if (parse_data.book_change == 1)
+ break;
+ dsp_parse_data[count] = parse_data;
+
+ i2c_transaction[count].addr = client->addr;
+ i2c_transaction[count].flags =
+ client->flags & I2C_M_TEN;
+ i2c_transaction[count].len =
+ dsp_parse_data[count].burst_size;
+ i2c_transaction[count].buf =
+ dsp_parse_data[count].burst_array;
+
+#ifdef DEBUG_MINIDSP_LOADING
+ DBG(KERN_INFO
+ "i: %d\taddr: %d\tflags: %d\tlen: %d\tbuf:",
+ i, client->addr, client->flags & I2C_M_TEN,
+ dsp_parse_data[count].burst_size);
+
+ for (j = 0; j <= dsp_parse_data[count].burst_size; j++)
+ DBG(KERN_INFO "%x ",
+ dsp_parse_data[i].burst_array[j]);
+
+ DBG(KERN_INFO "\n\n");
+ i++;
+#endif
+
+ count++;
+ /* Proceed to the next burst reg_addr_incruence */
+ } while (parse_data.current_loc != MINIDSP_PARSING_END);
+
+ if (count > 0) {
+ if (i2c_transfer(client->adapter,
+ i2c_transaction, count) != count) {
+ printk(KERN_ERR "Write burst i2c data error!\n");
+ }
+ }
+ if (parse_data.book_change == 1) {
+ aic3262_change_book(codec, parse_data.book_no);
+ parse_data.book_change = 0;
+ }
+ } while (parse_data.current_loc != MINIDSP_PARSING_END);
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : set_minidsp_mode
+ * Purpose : Switch to the first minidsp mode.
+ *----------------------------------------------------------------------------
+ */
+int
+set_minidsp_mode(struct snd_soc_codec *codec, int new_mode)
+{
+ struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
+
+ DBG("%s: switch mode start\n", __func__);
+ aic3262_reset_cache(codec);
+ reg_def_conf(codec);
+
+ if (new_mode == 0) {
+
+ /* General Programming */
+ DBG(KERN_INFO "$Writing reg_section_init_program\n");
+ if (ARRAY_SIZE(main44_REG_Section_init_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_REG_Section_init_program,
+ ARRAY_SIZE(main44_REG_Section_init_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_REG_Section_init_program,
+ ARRAY_SIZE(main44_REG_Section_init_program));
+#endif
+
+ } else {
+ printk(KERN_ERR
+ "_CODEC_REGS: Insufficient data for programming\n");
+ }
+
+ /* minidsp A programming */
+ DBG(KERN_INFO "#Writing minidsp_A_reg_values\n");
+
+ if ((main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_miniDSP_A_reg_values,
+ (main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_miniDSP_A_reg_values,
+ (main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE));
+#endif
+ } else {
+ printk(KERN_ERR
+ "MINI_DSP_A_second: Insufficient data for programming\n");
+ }
+ /* minidsp D programming */
+ DBG(KERN_INFO "#Writing minidsp_D_reg_values\n");
+ if ((main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE) > 0) {
+
+#ifdef MULTIBYTE_CONFIG_SUPPORT
+ /*Multibyte for DAC */
+#endif
+
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_miniDSP_D_reg_values,
+ (main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_miniDSP_D_reg_values,
+ (main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE));
+#endif
+
+ } else {
+ printk(KERN_ERR
+ "MINI_DSP_D_second: Insufficient data for programming\n");
+ }
+ DBG(KERN_INFO "#Writing reg_section_post_program\n");
+ if (ARRAY_SIZE(main44_REG_Section_post_program) > 0) {
+ #ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_REG_Section_post_program,
+ ARRAY_SIZE(REG_Section_post_program));
+ #else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_REG_Section_post_program,
+ ARRAY_SIZE(main44_REG_Section_post_program));
+ #endif
+ } else {
+ printk(KERN_ERR
+ "second_CODEC_REGS: Insufficient data for programming\n");
+ }
+ }
+
+ if (new_mode == 1) {
+ /* General Programming */
+ DBG(KERN_INFO "#Writing reg_section_init_program\n");
+ if (ARRAY_SIZE(Second_Rate_REG_Section_init_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_REG_Section_init_program,
+ ARRAY_SIZE(Second_Rate_REG_Section_init_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_REG_Section_init_program,
+ ARRAY_SIZE(Second_Rate_REG_Section_init_program));
+#endif
+
+ } else {
+ printk(KERN_ERR
+ "_CODEC_REGS: Insufficient data for programming\n");
+ }
+ /* minidsp A programming */
+ DBG(KERN_INFO "#Writing minidsp_A_reg_values\n");
+
+ if ((Second_Rate_miniDSP_A_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_A_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_miniDSP_A_reg_values,
+ (Second_Rate_miniDSP_A_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_A_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_miniDSP_A_reg_values,
+ (Second_Rate_miniDSP_A_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_A_reg_values_INST_SIZE));
+#endif
+ } else {
+ printk(KERN_ERR
+ "MINI_DSP_A_second: Insufficient data for programming\n");
+ }
+
+ /* minidsp D programming */
+ DBG(KERN_INFO "#Writing minidsp_D_reg_values\n");
+
+ if ((Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_miniDSP_D_reg_values,
+ (Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_miniDSP_D_reg_values,
+ (Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE));
+#endif
+ } else {
+ printk(KERN_ERR
+ "MINI_DSP_D_second: Insufficient data for programming\n");
+ }
+ DBG(KERN_INFO "Writing reg_section_post_program\n");
+ if (ARRAY_SIZE(Second_Rate_REG_Section_post_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_REG_Section_post_program,
+ ARRAY_SIZE(REG_Section_post_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_REG_Section_post_program,
+ ARRAY_SIZE(Second_Rate_REG_Section_post_program));
+#endif
+ } else {
+ printk(KERN_ERR
+ "second_CODEC_REGS: Insufficient data for programming\n");
+ }
+ }
+
+#ifdef MULTIBYTE_CONFIG_SUPPORT
+ /*Multibyte for DAC */
+ aic326x->process_flow = new_mode;
+ config_multibyte_for_mode(codec, new_mode);
+#endif
+ DBG("%s: switch mode finished\n", __func__);
+ return 0;
+}
+
+/*
+ * i2c_verify
+ *
+ * Function used to validate the contents written into the miniDSP
+ * pages after miniDSP Configuration.
+*/
+int i2c_verify(struct snd_soc_codec *codec)
+{
+
+ DBG(KERN_INFO "#%s: Invoked.. Resetting to page 0\n", __func__);
+
+ aic3262_change_book(codec, 0);
+ DBG(KERN_INFO "#Reading reg_section_init_program\n");
+
+ byte_i2c_array_read(codec, main44_REG_Section_init_program,
+ ARRAY_SIZE(main44_REG_Section_init_program));
+
+ DBG(KERN_INFO "#Reading minidsp_A_reg_values\n");
+ byte_i2c_array_read(codec, main44_miniDSP_A_reg_values,
+ (main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE));
+
+ DBG(KERN_INFO "#Reading minidsp_D_reg_values\n");
+ byte_i2c_array_read(codec, main44_miniDSP_D_reg_values,
+ (main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE));
+
+ DBG(KERN_INFO "#Reading reg_section_post_program\n");
+ byte_i2c_array_read(codec, main44_REG_Section_post_program,
+ ARRAY_SIZE(main44_REG_Section_post_program));
+
+ DBG(KERN_INFO "i2c_verify completed\n");
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : set_minidsp_mode1
+ * Purpose : for laoding the default minidsp mode for the first time .
+ *----------------------------------------------------------------------------
+ */
+int
+set_minidsp_mode1(struct snd_soc_codec *codec, int new_mode)
+{
+ DBG("#%s: switch mode start\n", __func__);
+ aic3262_reset_cache(codec);
+
+ if (new_mode == 0) {
+ /* General Programming */
+ DBG(KERN_INFO "#Writing reg_section_init_program\n");
+ if (ARRAY_SIZE(main44_REG_Section_init_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_REG_Section_init_program,
+ ARRAY_SIZE(main44_REG_Section_init_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_REG_Section_init_program,
+ ARRAY_SIZE(main44_REG_Section_init_program));
+#endif
+
+ } else {
+ printk(KERN_ERR
+ "_CODEC_REGS: Insufficient data for programming\n");
+ }
+ /* minidsp A programming */
+ DBG(KERN_INFO "#Writing minidsp_A_reg_values\n");
+ if ((main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_miniDSP_A_reg_values,
+ (main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_miniDSP_A_reg_values,
+ (main44_miniDSP_A_reg_values_COEFF_SIZE +
+ main44_miniDSP_A_reg_values_INST_SIZE));
+#endif
+ } else {
+ printk(KERN_ERR
+ "MINI_DSP_A_second: Insufficient data for programming\n");
+ }
+
+ /* minidsp D programming */
+ DBG(KERN_INFO "#Writing minidsp_D_reg_values\n");
+ if ((main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_miniDSP_D_reg_values,
+ (main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_miniDSP_D_reg_values,
+ (main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE));
+#endif
+ } else {
+ printk(KERN_ERR
+ "MINI_DSP_D_second: Insufficient data for programming\n");
+ }
+
+ DBG(KERN_INFO "#Writing reg_section_post_program\n");
+ if (ARRAY_SIZE(main44_REG_Section_post_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ main44_REG_Section_post_program,
+ ARRAY_SIZE(REG_Section_post_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ main44_REG_Section_post_program,
+ ARRAY_SIZE(main44_REG_Section_post_program));
+#endif
+ } else {
+ printk(KERN_ERR
+ "second_CODEC_REGS: Insufficient data for programming\n");
+ }
+ }
+
+ if (new_mode == 1) {
+ /* General Programming */
+ DBG(KERN_INFO "#Writing reg_section_init_program\n");
+ if (ARRAY_SIZE(Second_Rate_REG_Section_init_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ second_Rate_REG_Section_init_program,
+ ARRAY_SIZE(Second_Rate_REG_Section_init_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_REG_Section_init_program,
+ ARRAY_SIZE(Second_Rate_REG_Section_init_program));
+#endif
+ } else {
+ printk(KERN_ERR
+ "_CODEC_REGS: Insufficient data for programming\n");
+ }
+ /* minidsp A programming */
+ DBG(KERN_INFO "#Writing minidsp_A_reg_values\n");
+ if ((Second_Rate_miniDSP_A_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_A_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_miniDSP_A_reg_values,
+ (Second_Rate_miniDSP_A_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_A_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_miniDSP_A_reg_values,
+ (Second_Rate_miniDSP_A_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_A_reg_values_INST_SIZE));
+#endif
+ } else {
+ printk(KERN_ERR\
+ "MINI_DSP_A_second: Insufficient data for programming\n");
+ }
+ /* minidsp D programming */
+ DBG(KERN_INFO "#Writing minidsp_D_reg_values\n");
+ if ((Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_miniDSP_D_reg_values,
+ (Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_miniDSP_D_reg_values,
+ (Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE));
+#endif
+ } else
+ printk(KERN_ERR "MINI_DSP_D_second: Insufficient data for programming\n");
+
+ DBG(KERN_INFO "#Writing reg_section_post_program\n");
+ if (ARRAY_SIZE(Second_Rate_REG_Section_post_program) > 0) {
+#ifndef MULTIBYTE_I2C
+ byte_i2c_array_transfer(codec,
+ Second_Rate_REG_Section_post_program,
+ ARRAY_SIZE(REG_Section_post_program));
+#else
+ minidsp_i2c_multibyte_transfer(codec,
+ Second_Rate_REG_Section_post_program,
+ ARRAY_SIZE(Second_Rate_REG_Section_post_program));
+#endif
+ } else
+ printk(KERN_ERR\
+ "second_CODEC_REGS: Insufficient data for programming\n");
+
+ }
+
+ DBG("#%s: switch mode completed\n", __func__);
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : aic3262_minidsp_program
+ * Purpose : Program mini dsp for AIC3262 codec chip. This routine is
+ * called from the aic3262 codec driver, if mini dsp programming
+ * is enabled.
+ *----------------------------------------------------------------------------
+ */
+int aic3262_minidsp_program(struct snd_soc_codec *codec)
+{
+ DBG(KERN_INFO "#AIC3262: programming mini dsp\n");
+
+#if defined(PROGRAM_MINI_DSP_first)
+ #ifdef DEBUG
+ DBG("#Verifying book 0\n");
+ i2c_verify_book0(codec);
+#endif
+ aic3262_change_book(codec, 0);
+ set_minidsp_mode1(codec, 0);
+ aic3262_change_book(codec, 0);
+#ifdef DEBUG
+ DBG("#verifying book 0\n");
+ i2c_verify_book0(codec);
+#endif
+#endif
+#if defined(PROGRAM_MINI_DSP_second)
+#ifdef DEBUG
+ DBG("#Verifying book 0\n");
+ i2c_verify_book0(codec);
+#endif
+ set_minidsp_mode1(codec, 1);
+#ifdef DEBUG
+ DBG("#verifying book 0\n");
+ i2c_verify_book0(codec);
+#endif
+#endif
+ return 0;
+}
+/*
+ *----------------------------------------------------------------------------
+ * Function : m_control_info
+ * Purpose : This function is to initialize data for new control required to
+ * program the AIC3262 registers.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int m_control_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->count = 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : m_control_get
+ * Purpose : This function is to read data of new control for
+ * program the AIC3262 registers.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int m_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+ u32 val;
+ u8 val1;
+
+ if (!strcmp(kcontrol->id.name, "Minidsp mode")) {
+ val = aic3262->process_flow;
+ ucontrol->value.integer.value[0] = val;
+ DBG(KERN_INFO "control get : mode=%d\n", aic3262->process_flow);
+ }
+ if (!strcmp(kcontrol->id.name, "DAC Adaptive mode Enable")) {
+ aic3262_change_book(codec, 80);
+ val1 = i2c_smbus_read_byte_data(codec->control_data, 1);
+ ucontrol->value.integer.value[0] = ((val1>>1)&0x01);
+ DBG(KERN_INFO "control get : mode=%d\n", aic3262->process_flow);
+ }
+ if (!strcmp(kcontrol->id.name, "ADC Adaptive mode Enable")) {
+ aic3262_change_book(codec, 40);
+ val1 = i2c_smbus_read_byte_data(codec->control_data, 1);
+ ucontrol->value.integer.value[0] = ((val1>>1)&0x01);
+ DBG(KERN_INFO "control get : mode=%d\n", dmode);
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : m_new_control_put
+ * Purpose : new_control_put is called to pass data from user/application to
+ * the driver.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int m_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ u32 val;
+ u8 val1;
+ int mode = aic3262->process_flow;
+
+ DBG("n_control_put\n");
+ val = ucontrol->value.integer.value[0];
+ if (!strcmp(kcontrol->id.name, "Minidsp mode")) {
+ DBG(KERN_INFO "\nMini dsp put\n mode = %d, val=%d\n",
+ aic3262->process_flow, val);
+ if (val != mode) {
+ if (aic3262->mute_codec == 1) {
+ i2c_verify_book0(codec);
+ aic3262_change_book(codec, 0);
+ set_minidsp_mode(codec, val);
+
+ aic3262_change_book(codec, 0);
+ i2c_verify_book0(codec);
+ /* update_kcontrols(codec, val);*/
+ } else {
+ printk(KERN_ERR
+ " Cant Switch Processflows, Playback in progress");
+ }
+ }
+ }
+
+ if (!strcmp(kcontrol->id.name, "DAC Adaptive mode Enable")) {
+ DBG(KERN_INFO "\nMini dsp put\n mode = %d, val=%d\n",
+ aic3262->process_flow, val);
+ if (val != amode) {
+ aic3262_change_book(codec, 80);
+ val1 = i2c_smbus_read_byte_data(codec->control_data, 1);
+ aic3262_write(codec, 1, (val1&0xfb)|(val<<1));
+ }
+ amode = val;
+ }
+
+ if (!strcmp(kcontrol->id.name, "ADC Adaptive mode Enable")) {
+ DBG(KERN_INFO "\nMini dsp put\n mode = %d, val=%d\n",
+ aic3262->process_flow, val);
+ if (val != dmode) {
+ aic3262_change_book(codec, 40);
+ val1 = i2c_smbus_read_byte_data(codec->control_data, 1);
+ aic3262_write(codec, 1, (val1&0xfb)|(val<<1));
+ }
+ dmode = val;
+ }
+
+ if (!strcmp(kcontrol->id.name, "Dump Regs Book0"))
+ i2c_verify_book0(codec);
+
+
+ if (!strcmp(kcontrol->id.name, "Verify minidsp program")) {
+ if (mode == 0) {
+ DBG("Current mod=%d\nVerifying minidsp_D_regs", mode);
+ byte_i2c_array_read(codec, main44_miniDSP_D_reg_values,
+ (main44_miniDSP_D_reg_values_COEFF_SIZE +
+ main44_miniDSP_D_reg_values_INST_SIZE));
+ } else {
+ byte_i2c_array_read(codec,
+ Second_Rate_miniDSP_D_reg_values,
+ (Second_Rate_miniDSP_D_reg_values_COEFF_SIZE +
+ Second_Rate_miniDSP_D_reg_values_INST_SIZE));
+ }
+ }
+ DBG("\nmode = %d\n", mode);
+ return mode;
+}
+
+/************************** MUX CONTROL section *****************************/
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_info_minidsp_mux
+ * Purpose : info routine for mini dsp mux control amixer kcontrols
+ *----------------------------------------------------------------------------
+ */
+static int __new_control_info_minidsp_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int index;
+ int ret_val = -1;
+
+ for (index = 0; index < ARRAY_SIZE(main44_MUX_controls); index++) {
+ if (strstr(kcontrol->id.name, main44_MUX_control_names[index]))
+ break;
+ }
+
+ if (index < ARRAY_SIZE(main44_MUX_controls)) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = MIN_MUX_CTRL;
+ uinfo->value.integer.max = MAX_MUX_CTRL;
+ ret_val = 0;
+ }
+ return ret_val;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_get_minidsp_mux
+ *
+ * Purpose : get routine for mux control amixer kcontrols,
+ * read current register values to user.
+ * Used for for mini dsp 'MUX control' amixer controls.
+ *----------------------------------------------------------------------------
+ */
+static int __new_control_get_minidsp_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_put_minidsp_mux
+ *
+ * Purpose : put routine for amixer kcontrols, write user values to registers
+ * values. Used for for mini dsp 'MUX control' amixer controls.
+ *----------------------------------------------------------------------------
+ */
+static int __new_control_put_minidsp_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 data[MUX_CTRL_REG_SIZE + 1];
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int index = 1;
+ int user_value = ucontrol->value.integer.value[0];
+ struct i2c_client *i2c;
+ u8 value[2], swap_reg_pre, swap_reg_post;
+ u8 page;
+ int ret_val = -1, array_size;
+ control *array;
+ char **array_names;
+ char *control_name, *control_name1;
+ struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
+ i2c = codec->control_data;
+
+ if (aic326x->process_flow == 0) {
+ DBG("#the current process flow is %d", aic326x->process_flow);
+ array = main44_MUX_controls;
+ array_size = ARRAY_SIZE(main44_MUX_controls);
+ array_names = main44_MUX_control_names;
+ control_name = "Stereo_Mux_TwoToOne_1";
+ control_name1 = "Mono_Mux_1_1";
+
+ /* Configure only for process flow 1 controls */
+ if (strcmp(kcontrol->id.name, control_name) &&
+ strcmp(kcontrol->id.name, control_name1))
+ return 0;
+ } else {
+ array = Second_Rate_MUX_controls;
+ array_size = ARRAY_SIZE(Second_Rate_MUX_controls);
+ array_names = Second_Rate_MUX_control_names;
+ control_name = "Stereo_Mux_TwoToOne_1_Second";
+ control_name1 = "Mono_Mux_1_1_Second";
+
+ /* Configure only for process flow 2 controls */
+ if (strcmp(kcontrol->id.name, control_name) &&
+ strcmp(kcontrol->id.name, control_name1))
+ return 0;
+ }
+
+ page = array[index].control_page;
+
+ DBG("#user value = 0x%x\n", user_value);
+ for (index = 0; index < array_size; index++) {
+ if (strstr(kcontrol->id.name, array_names[index]))
+ break;
+ }
+ if (index < array_size) {
+ DBG(KERN_INFO "#Index %d Changing to Page %d\n", index,
+ array[index].control_page);
+
+ aic3262_change_book(codec,
+ array[index].control_book);
+ aic3262_change_page(codec,
+ array[index].control_page);
+
+ if (!strcmp(array_names[index], control_name)) {
+ if (user_value > 0) {
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ } else {
+ data[1] = 0xFF;
+ data[2] = 0xFf;
+ data[3] = 0xFF;
+ }
+ } else {
+ if (user_value > 0) {
+ data[1] =
+ (u8) ((user_value >> 16) &
+ AIC3262_8BITS_MASK);
+ data[2] =
+ (u8) ((user_value >> 8) &
+ AIC3262_8BITS_MASK);
+ data[3] =
+ (u8)((user_value) & AIC3262_8BITS_MASK);
+ }
+ }
+ /* start register address */
+ data[0] = array[index].control_base;
+
+ DBG(KERN_INFO
+ "#Writing %d %d %d \r\n", data[0], data[1], data[2]);
+
+ ret_val = i2c_master_send(i2c, data, MUX_CTRL_REG_SIZE + 1);
+
+ if (ret_val != MUX_CTRL_REG_SIZE + 1)
+ printk(KERN_ERR "i2c_master_send transfer failed\n");
+ else {
+ /* store the current level */
+ kcontrol->private_value = user_value;
+ ret_val = 0;
+ /* Enable adaptive filtering for ADC/DAC */
+ }
+
+ /* Perform a BUFFER SWAP Command. Check if we are currently not
+ * in Page 8, if so, swap to Page 8 first
+ */
+
+ value[0] = 1;
+
+ if (i2c_master_send(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not write register address\n");
+
+ /* Read the Value of the Page 8 Register 1 which controls the
+ Adaptive Switching Mode */
+ if (i2c_master_recv(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not read codec registers\n");
+
+ swap_reg_pre = value[0];
+ /* Write the Register bit updates */
+ value[1] = value[0] | 1;
+ value[0] = 1;
+
+ if (i2c_master_send(i2c, value, 2) != 2)
+ printk(KERN_ERR "Can not write register address\n");
+
+ value[0] = 1;
+ /* verify buffer swap */
+ if (i2c_master_send(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not write register address\n");
+
+ /* Read the Value of the Page 8 Register 1 which controls the
+ Adaptive Switching Mode */
+ if (i2c_master_recv(i2c, &swap_reg_post, 1) != 1)
+ printk(KERN_ERR "Can not read codec registers\n");
+
+ if ((swap_reg_pre == 4 && swap_reg_post == 6)
+ || (swap_reg_pre == 6 && swap_reg_post == 4))
+ DBG("Buffer swap success\n");
+ else
+ printk(KERN_ERR
+ "Buffer swap...FAILED\nswap_reg_pre=%x, \
+ swap_reg_post=%x\n", swap_reg_pre, swap_reg_post);
+
+ }
+ /* update the new buffer value in the old, just swapped out buffer */
+ aic3262_change_book(codec, array[index].control_book);
+ aic3262_change_page(codec, array[index].control_page);
+ ret_val = i2c_master_send(i2c, data, MUX_CTRL_REG_SIZE + 1);
+ ret_val = 0;
+
+ aic3262_change_book(codec, 0);
+ return ret_val;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : minidsp_mux_ctrl_mixer_controls
+ *
+ * Purpose : Add amixer kcontrols for mini dsp mux controls,
+ *----------------------------------------------------------------------------
+ */
+static int minidsp_mux_ctrl_mixer_controls(struct snd_soc_codec *codec,
+ int size, control *cntl,
+ char **name)
+{
+ int i, err;
+ int val1;
+
+ /* DBG("%d mixer controls for mini dsp MUX\n", no_mux_controls);*/
+
+ if (size) {
+ for (i = 0; i < size; i++) {
+
+ snd_mux_controls[i].name = name[i];
+ snd_mux_controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ snd_mux_controls[i].access =
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_mux_controls[i].info =
+ __new_control_info_minidsp_mux;
+ snd_mux_controls[i].get = __new_control_get_minidsp_mux;
+ snd_mux_controls[i].put = __new_control_put_minidsp_mux;
+ /*
+ * TBD: read volume reg and update the index number
+ */
+ aic3262_change_book(codec, cntl[i].control_book);
+ aic3262_change_page(codec, cntl[i].control_page);
+ val1 = i2c_smbus_read_byte_data(codec->control_data,
+ cntl[i].control_base);
+ DBG(KERN_INFO "Control data %x\n", val1);
+ /*
+ if( val1 >= 0 )
+ snd_mux_controls[i].private_value = val1;
+ else
+ snd_mux_controls[i].private_value = 0;
+ */
+ DBG(KERN_INFO
+ "the value of amixer control mux=%d", val1);
+ if (val1 >= 0 && val1 != 255)
+ snd_mux_controls[i].private_value = val1;
+ else
+ snd_mux_controls[i].private_value = 0;
+
+ snd_mux_controls[i].count = 0;
+
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_mux_controls[i],
+ codec));
+ if (err < 0)
+ printk(KERN_ERR
+ "%s:Invalid control %s\n", __FILE__,
+ snd_mux_controls[i].name);
+ }
+ }
+ return 0;
+}
+
+/*------------------------- Volume Controls -----------------------*/
+static int volume_lite_table[] = {
+
+ 0x00000D, 0x00000E, 0x00000E, 0x00000F,
+ 0x000010, 0x000011, 0x000012, 0x000013,
+ 0x000015, 0x000016, 0x000017, 0x000018,
+ 0x00001A, 0x00001C, 0x00001D, 0x00001F,
+ 0x000021, 0x000023, 0x000025, 0x000027,
+ 0x000029, 0x00002C, 0x00002F, 0x000031,
+ 0x000034, 0x000037, 0x00003B, 0x00003E,
+ 0x000042, 0x000046, 0x00004A, 0x00004F,
+ 0x000053, 0x000058, 0x00005D, 0x000063,
+ 0x000069, 0x00006F, 0x000076, 0x00007D,
+ 0x000084, 0x00008C, 0x000094, 0x00009D,
+ 0x0000A6, 0x0000B0, 0x0000BB, 0x0000C6,
+ 0x0000D2, 0x0000DE, 0x0000EB, 0x0000F9,
+ 0x000108, 0x000118, 0x000128, 0x00013A,
+ 0x00014D, 0x000160, 0x000175, 0x00018B,
+ 0x0001A3, 0x0001BC, 0x0001D6, 0x0001F2,
+ 0x000210, 0x00022F, 0x000250, 0x000273,
+ 0x000298, 0x0002C0, 0x0002E9, 0x000316,
+ 0x000344, 0x000376, 0x0003AA, 0x0003E2,
+ 0x00041D, 0x00045B, 0x00049E, 0x0004E4,
+ 0x00052E, 0x00057C, 0x0005D0, 0x000628,
+ 0x000685, 0x0006E8, 0x000751, 0x0007C0,
+ 0x000836, 0x0008B2, 0x000936, 0x0009C2,
+ 0x000A56, 0x000AF3, 0x000B99, 0x000C49,
+ 0x000D03, 0x000DC9, 0x000E9A, 0x000F77,
+ 0x001062, 0x00115A, 0x001262, 0x001378,
+ 0x0014A0, 0x0015D9, 0x001724, 0x001883,
+ 0x0019F7, 0x001B81, 0x001D22, 0x001EDC,
+ 0x0020B0, 0x0022A0, 0x0024AD, 0x0026DA,
+ 0x002927, 0x002B97, 0x002E2D, 0x0030E9,
+ 0x0033CF, 0x0036E1, 0x003A21, 0x003D93,
+ 0x004139, 0x004517, 0x00492F, 0x004D85,
+ 0x00521D, 0x0056FA, 0x005C22, 0x006197,
+ 0x006760, 0x006D80, 0x0073FD, 0x007ADC,
+ 0x008224, 0x0089DA, 0x009205, 0x009AAC,
+ 0x00A3D7, 0x00B7D4, 0x00AD8C, 0x00C2B9,
+ 0x00CE43, 0x00DA7B, 0x00E76E, 0x00F524,
+ 0x0103AB, 0x01130E, 0x01235A, 0x01349D,
+ 0x0146E7, 0x015A46, 0x016ECA, 0x018486,
+ 0x019B8C, 0x01B3EE, 0x01CDC3, 0x01E920,
+ 0x02061B, 0x0224CE, 0x024553, 0x0267C5,
+ 0x028C42, 0x02B2E8, 0x02DBD8, 0x030736,
+ 0x033525, 0x0365CD, 0x039957, 0x03CFEE,
+ 0x0409C2, 0x044703, 0x0487E5, 0x04CCA0,
+ 0x05156D, 0x05628A, 0x05B439, 0x060ABF,
+ 0x066666, 0x06C77B, 0x072E50, 0x079B3D,
+ 0x080E9F, 0x0888D7, 0x090A4D, 0x09936E,
+ 0x0A24B0, 0x0ABE8D, 0x0B6188, 0x0C0E2B,
+ 0x0CC509, 0x0D86BD, 0x0E53EB, 0x0F2D42,
+ 0x101379, 0x110754, 0x1209A3, 0x131B40,
+ 0x143D13, 0x157012, 0x16B543, 0x180DB8,
+ 0x197A96, 0x1AFD13, 0x1C9676, 0x1E481C,
+ 0x201373, 0x21FA02, 0x23FD66, 0x261F54,
+ 0x28619A, 0x2AC625, 0x2D4EFB, 0x2FFE44,
+ 0x32D646, 0x35D96B, 0x390A41, 0x3C6B7E,
+ 0x400000, 0x43CAD0, 0x47CF26, 0x4C106B,
+ 0x50923B, 0x55586A, 0x5A6703, 0x5FC253,
+ 0x656EE3, 0x6B7186, 0x71CF54, 0x788DB4,
+ 0x7FB260,
+};
+
+static struct snd_kcontrol_new snd_vol_controls[MAX_VOLUME_CONTROLS];
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_info_main44_minidsp_volume
+ * Purpose : info routine for volumeLite amixer kcontrols
+ *----------------------------------------------------------------------------
+ */
+
+static int
+__new_control_info_minidsp_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int index, index8;
+ int ret_val = -1;
+
+ for (index = 0; index < ARRAY_SIZE(main44_VOLUME_controls); index++) {
+ if (strstr
+ (kcontrol->id.name, main44_VOLUME_control_names[index]))
+ break;
+ }
+ for (index8 = 0; index8 < ARRAY_SIZE(Second_Rate_VOLUME_controls);
+ index8++) {
+ if (strstr
+ (kcontrol->id.name,
+ Second_Rate_VOLUME_control_names[index]))
+ break;
+ }
+ if ((index < ARRAY_SIZE(main44_VOLUME_controls))
+ || (index8 < ARRAY_SIZE(Second_Rate_VOLUME_controls))) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = MIN_VOLUME;
+ uinfo->value.integer.max = MAX_VOLUME;
+ ret_val = 0;
+ }
+ return ret_val;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_get_main44_minidsp_vol
+ * Purpose : get routine for amixer kcontrols, read current register
+ * values. Used for for mini dsp 'VolumeLite' amixer controls.
+ *----------------------------------------------------------------------------
+ */
+static int
+__new_control_get_minidsp_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : __new_control_put_main44_minidsp_volume
+ * Purpose : put routine for amixer kcontrols, write user values to registers
+ * values. Used for for mini dsp 'VolumeLite' amixer controls.
+ *----------------------------------------------------------------------------
+ */
+static int
+__new_control_put_minidsp_volume(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 data[4];
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int user_value = ucontrol->value.integer.value[0];
+ struct i2c_client *i2c = codec->control_data;
+ int ret_val = -1;
+ int coeff;
+ u8 value[2], swap_reg_pre, swap_reg_post;
+ struct aic3262_priv *aic3262 = snd_soc_codec_get_drvdata(codec);
+
+ control *volume_controls = NULL;
+ printk(KERN_INFO "user value = 0x%x\n", user_value);
+
+ if (aic3262->process_flow == 0)
+ volume_controls = main44_VOLUME_controls;
+ else
+ volume_controls = Second_Rate_VOLUME_controls;
+
+ aic3262_change_book(codec, volume_controls->control_book);
+ aic3262_change_page(codec, volume_controls->control_page);
+
+ coeff = volume_lite_table[user_value << 1];
+
+ data[1] = (u8) ((coeff >> 16) & AIC3262_8BITS_MASK);
+ data[2] = (u8) ((coeff >> 8) & AIC3262_8BITS_MASK);
+ data[3] = (u8) ((coeff) & AIC3262_8BITS_MASK);
+
+ /* Start register address */
+ data[0] = volume_controls->control_base;
+ ret_val = i2c_master_send(i2c, data, VOLUME_REG_SIZE + 1);
+ if (ret_val != VOLUME_REG_SIZE + 1)
+ printk(KERN_ERR "i2c_master_send transfer failed\n");
+ else {
+ /* store the current level */
+ kcontrol->private_value = user_value;
+ ret_val = 0;
+ }
+ /* Initiate buffer swap */
+ value[0] = 1;
+
+ if (i2c_master_send(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not write register address\n");
+
+ /* Read the Value of the Page 8 Register 1 which controls the
+ Adaptive Switching Mode */
+ if (i2c_master_recv(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not read codec registers\n");
+
+ swap_reg_pre = value[0];
+ /* Write the Register bit updates */
+ value[1] = value[0] | 1;
+ value[0] = 1;
+ if (i2c_master_send(i2c, value, 2) != 2)
+ printk(KERN_ERR "Can not write register address\n");
+
+ value[0] = 1;
+ /* verify buffer swap */
+ if (i2c_master_send(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not write register address\n");
+
+ /* Read the Value of the Page 8 Register 1 which controls the
+ Adaptive Switching Mode */
+ if (i2c_master_recv(i2c, &swap_reg_post, 1) != 1)
+ printk(KERN_ERR "Can not read codec registers\n");
+
+ if ((swap_reg_pre == 4 && swap_reg_post == 6)
+ || (swap_reg_pre == 6 && swap_reg_post == 4))
+ DBG("Buffer swap success\n");
+ else
+ DBG("Buffer swap...FAILED\nswap_reg_pre=%x, swap_reg_post=%x\n",
+ swap_reg_pre, swap_reg_post);
+
+ /* update the new buffer value in the old, just swapped out buffer */
+ aic3262_change_book(codec, volume_controls->control_book);
+ aic3262_change_page(codec, volume_controls->control_page);
+ i2c_master_send(i2c, data, MUX_CTRL_REG_SIZE + 1);
+
+ aic3262_change_book(codec, 0);
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : minidsp_volume_main44_mixer_controls
+ * Purpose : Add amixer kcontrols for mini dsp volume Lite controls,
+ *----------------------------------------------------------------------------
+ */
+static int minidsp_volume_mixer_controls(struct snd_soc_codec *codec)
+{
+ int i, err, no_volume_controls;
+ static char volume_control_name[MAX_VOLUME_CONTROLS][40];
+
+ /* ADD first process volume controls */
+ no_volume_controls = ARRAY_SIZE(main44_VOLUME_controls);
+
+ printk(KERN_INFO " %d mixer controls for mini dsp 'volumeLite'\n",
+ no_volume_controls);
+
+ if (no_volume_controls) {
+
+ for (i = 0; i < no_volume_controls; i++) {
+ strcpy(volume_control_name[i],
+ main44_VOLUME_control_names[i]);
+ strcat(volume_control_name[i], VOLUME_KCONTROL_NAME);
+
+ printk(KERN_ERR "Volume controls: %s\n",
+ volume_control_name[i]);
+
+ snd_vol_controls[i].name = volume_control_name[i];
+ snd_vol_controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ snd_vol_controls[i].access =
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_vol_controls[i].info =
+ __new_control_info_minidsp_volume;
+ snd_vol_controls[i].get =
+ __new_control_get_minidsp_volume;
+ snd_vol_controls[i].put =
+ __new_control_put_minidsp_volume;
+ /*
+ * TBD: read volume reg and update the index number
+ */
+ snd_vol_controls[i].private_value = 0;
+ snd_vol_controls[i].count = 0;
+
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_vol_controls[i],
+ codec));
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s:Invalid control %s\n", __FILE__,
+ snd_vol_controls[i].name);
+ }
+ }
+ }
+ /* ADD second process volume controls */
+ no_volume_controls = ARRAY_SIZE(Second_Rate_VOLUME_controls);
+
+ printk(KERN_ERR " %d mixer controls for mini dsp 'volumeLite'\n",
+ no_volume_controls);
+
+ if (no_volume_controls) {
+
+ for (i = 0; i < no_volume_controls; i++) {
+ strcpy(volume_control_name[i],
+ Second_Rate_VOLUME_control_names[i]);
+ strcat(volume_control_name[i], VOLUME_KCONTROL_NAME);
+
+ printk(KERN_ERR "Volume controls: %s\n",
+ volume_control_name[i]);
+
+ snd_vol_controls[i].name = volume_control_name[i];
+ snd_vol_controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ snd_vol_controls[i].access =
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_vol_controls[i].info =
+ __new_control_info_minidsp_volume;
+ snd_vol_controls[i].get =
+ __new_control_get_minidsp_volume;
+ snd_vol_controls[i].put =
+ __new_control_put_minidsp_volume;
+ /*
+ * TBD: read volume reg and update the index number
+ */
+ snd_vol_controls[i].private_value = 0;
+ snd_vol_controls[i].count = 0;
+
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_vol_controls[i],
+ codec));
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s:Invalid control %s\n", __FILE__,
+ snd_vol_controls[i].name);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ *--------------------------------------------------------------------------
+ * Function : aic3262_add_minidsp_controls
+ * Purpose : Configures the AMIXER Control Interfaces that can be exercised by
+ * the user at run-time. Utilizes the the snd_adaptive_controls[]
+ * array to specify two run-time controls.
+ *---------------------------------------------------------------------------
+ */
+int aic3262_add_minidsp_controls(struct snd_soc_codec *codec)
+{
+#ifdef ADD_MINI_DSP_CONTROLS
+ int i, err, no_mux_controls;
+ /* add mode k control */
+ for (i = 0; i < ARRAY_SIZE(aic3262_minidsp_controls); i++) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&aic3262_minidsp_controls[i], codec));
+ if (err < 0) {
+ printk(KERN_ERR "Invalid control\n");
+ return err;
+ }
+ }
+
+ /* add mux controls */
+ no_mux_controls = ARRAY_SIZE(main44_MUX_controls);
+ minidsp_mux_ctrl_mixer_controls(codec, no_mux_controls,
+ main44_MUX_controls, main44_MUX_control_names);
+
+ no_mux_controls = ARRAY_SIZE(Second_Rate_MUX_controls);
+ minidsp_mux_ctrl_mixer_controls(codec, no_mux_controls,
+ Second_Rate_MUX_controls, Second_Rate_MUX_control_names);
+
+ /* add volume controls*/
+ minidsp_volume_mixer_controls(codec);
+#endif /* ADD_MINI_DSP_CONTROLS */
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC TLV320AIC3262 miniDSP driver");
+MODULE_AUTHOR("Y Preetam Sashank Reddy <preetam@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
+#endif /* End of CONFIG_MINI_DSP */
diff --git a/sound/soc/codecs/tlv320aic326x_minidsp_config.c b/sound/soc/codecs/tlv320aic326x_minidsp_config.c
new file mode 100755
index 000000000000..07b762aeedbc
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic326x_minidsp_config.c
@@ -0,0 +1,410 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic326x_minidsp_config.c
+ *
+ * Copyright (C) 2011 Mistral Solutions Pvt Ltd.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * The TLV320AIC3262 is a flexible, low-power, low-voltage stereo audio
+ * codec with digital microphone inputs and programmable outputs.
+ *
+ * History:
+ *
+ * Rev 0.1 Added the multiconfig support Mistral 17-08-2011
+ *
+ * Rev 0.2 Migrated for aic3262 nVidia
+ * Mistral 21-10-2011
+ */
+
+/*
+ *****************************************************************************
+ * INCLUDES
+ *****************************************************************************
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/core.h>
+#include <sound/soc-dapm.h>
+#include <sound/control.h>
+#include <linux/time.h> /* For timing computations */
+#include "tlv320aic326x.h"
+#include "tlv320aic326x_mini-dsp.h"
+
+#include "Patch_base_jazz_Rate48_pps_driver.h"
+#include "Patch_base_main_Rate48_pps_driver.h"
+#include "Patch_base_pop_Rate48_pps_driver.h"
+#include "Patch_base_rock_Rate48_pps_driver.h"
+
+#ifdef CONFIG_MINI_DSP
+
+#define MAX_CONFIG_D_ARRAYS 4
+#define MAX_CONFIG_A_ARRAYS 0
+#define MAX_CONFIG_ARRAYS 4
+#define MINIDSP_DMODE 0
+#define MINIDSP_AMODE 1
+
+/*
+ *****************************************************************************
+ * LOCAL STATIC DECLARATIONS
+ *****************************************************************************
+ */
+static int multibyte_coeff_change(struct snd_soc_codec *codec, int);
+
+static int m_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int m_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+/* k-control macros used for miniDSP related Kcontrols */
+#define SOC_SINGLE_VALUE_M(xmax, xinvert) \
+ ((unsigned long)&(struct soc_mixer_control) \
+ {.max = xmax, \
+ .invert = xinvert})
+#define SOC_SINGLE_M(xname, max, invert) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = m_control_info, .get = m_control_get,\
+ .put = m_control_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .private_value = SOC_SINGLE_VALUE_M(max, invert) }
+#define SOC_SINGLE_AIC3262_M(xname) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = m_control_info, .get = m_control_get,\
+ .put = m_control_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+}
+
+
+/* The Multi-Configurations generated through PPS GDE has been
+ * named as Rock, Pop, Jazz and Main. These were the Configurations
+ * that were used while testing this AUdio Driver. If the user
+ * creates Process-flow with different names, it is advised to
+ * modify the names present in the below array.
+ */
+static const char *multi_config_support_DAC[] = {
+ "ROCK",
+ "POP",
+ "JAZZ",
+ "MAIN",
+};
+
+/* SOC_ENUM Declaration and kControl for switching Configurations
+ * at run-time.
+ */
+static const struct soc_enum aic3262_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(multi_config_support_DAC),
+ multi_config_support_DAC);
+
+static const struct snd_kcontrol_new aic3262_minidsp_controls1[] = {
+
+ SOC_ENUM_EXT("Multiconfig support for DAC",
+ aic3262_enum, m_control_get, m_control_put),
+
+};
+
+
+/*
+ * multibyte_config
+ *
+ * This structure has been devised to maintain information about each
+ * configuration provided with the PPS GDE Processflow. For each
+ * configuration, the Driver needs to know where the starting offset
+ * of the Coefficient change, Total Size of Coefficients being affected,
+ * and the Instruction Sizes.
+ * This has to be replicated for both miniDSP_A and miniDSP_D
+ */
+struct multibyte_config {
+ reg_value *regs;
+ unsigned int d_coeff_start;
+ unsigned int d_coeff_size;
+ unsigned int d_inst_start;
+ unsigned int d_inst_size;
+ unsigned int a_coeff_start;
+ unsigned int a_coeff_size;
+ unsigned int a_inst_start;
+ unsigned int a_inst_size;
+} config_array[][2][MAX_CONFIG_ARRAYS] = {
+ /* Process flow 1 */
+ {
+ {
+ /* DAC */
+ {rock_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
+ {pop_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
+ {jazz_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
+ {main_D_reg_values, 0, 67, 67, 0, 0, 0, 0, 0},
+ },
+ /* ADC */
+ {},
+ },
+
+ /* Process flow 2 */
+ {
+#if 0
+ {
+ {main, 0, 0, 0, 0, 0, 0, 0, 0},
+ {pop, 0, 0, 0, 0, 0, 0, 0, 0},
+ {jazz, 0, 0, 0, 0, 0, 0, 0, 0},
+ {rock, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ /* ADC */
+ {},
+#endif
+ },
+};
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : m_control_get
+ * Purpose : This function is to read data of new control for
+ * program the AIC3262 registers.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int m_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
+ u32 val = 0;
+ u32 mode = aic326x->process_flow;
+
+
+ if (!strcmp(kcontrol->id.name, "Multiconfig support for DAC"))
+ val = aic326x->current_dac_config[mode];
+ else if (!strcmp(kcontrol->id.name, "Multiconfig support for ADC"))
+ val = aic326x->current_adc_config[mode];
+
+
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : m_new_control_put
+ * Purpose : new_control_put is called to pass data from user/application to
+ * the driver.
+ *
+ *----------------------------------------------------------------------------
+ */
+static int m_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
+ u32 val;
+ u8 value;
+ int pf = aic326x->process_flow;
+ struct multibyte_config *array;
+
+ val = ucontrol->value.integer.value[0];
+ if (!strcmp(kcontrol->id.name, "Multiconfig support for DAC")) {
+ if (aic326x->process_flow == MINIDSP_DMODE) {
+ if (val > MAX_CONFIG_D_ARRAYS) {
+ dev_err(codec->dev, "Value not in range\n");
+ return -1;
+ }
+
+ value = aic3262_read(codec, ADC_CHANNEL_POW);
+ value = aic3262_read(codec, PASI_DAC_DP_SETUP);
+
+ array = &config_array[pf][MINIDSP_DMODE][val];
+ minidsp_i2c_multibyte_transfer(codec,
+ array->regs,
+ (array->d_inst_size + array->d_coeff_size));
+
+
+ /*coefficent buffer change*/
+ multibyte_coeff_change(codec, 0x50);
+
+ minidsp_i2c_multibyte_transfer(codec,
+ array->regs,
+ (array->d_inst_size + array->d_coeff_size));
+
+
+
+ value = aic3262_read(codec, ADC_CHANNEL_POW);
+ value = aic3262_read(codec, PASI_DAC_DP_SETUP);
+ }
+ aic326x->current_dac_config[pf] = val;
+
+ } else {
+ if (aic326x->process_flow == MINIDSP_AMODE) {
+ if (val > MAX_CONFIG_A_ARRAYS) {
+ dev_err(codec->dev, "Value not in range\n");
+ return -1;
+ }
+ array = &config_array[pf][MINIDSP_AMODE][val];
+ minidsp_i2c_multibyte_transfer(codec,
+ array->regs,
+ (array->a_inst_size + array->a_coeff_size));
+ }
+ aic326x->current_adc_config[pf] = val;
+ }
+ return val;
+}
+
+
+/*
+ *--------------------------------------------------------------------------
+ * Function : aic3262_add_multiconfig_controls
+ * Purpose : Configures the AMIXER Control Interfaces that can be exercised by
+ * the user at run-time. Utilizes the the snd_adaptive_controls[]
+ * array to specify two run-time controls.
+ *---------------------------------------------------------------------------
+ */
+int aic3262_add_multiconfig_controls(struct snd_soc_codec *codec)
+{
+ int i, err;
+
+ DBG(KERN_INFO
+ "#%s: Invoked to add controls for Multi-Configuration\n",
+ __func__);
+
+ /* add mode k control */
+ for (i = 0; i < ARRAY_SIZE(aic3262_minidsp_controls1); i++) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&aic3262_minidsp_controls1[i],
+ codec));
+ if (err < 0) {
+ printk(KERN_ERR
+ "Cannot add controls for mulibyte configuration\n");
+ return err;
+ }
+ }
+ DBG(KERN_INFO "#%s: Completed control addition.\n", __func__);
+ return 0;
+}
+
+/*
+ *--------------------------------------------------------------------------
+ * Function : config_multibyte_for_mode
+ * Purpose : Function which is invoked when user changes the configuration
+ * at run-time. Internally configures/switches both
+ * miniDSP_D and miniDSP_A Coefficient arrays.
+ *---------------------------------------------------------------------------
+ */
+void config_multibyte_for_mode(struct snd_soc_codec *codec, int mode)
+{
+ int val;
+ int pf = mode;
+ struct aic3262_priv *aic326x = snd_soc_codec_get_drvdata(codec);
+ struct multibyte_config *array;
+
+ DBG(KERN_INFO "#%s: Invoked for miniDSP Mode %d\n", __func__, mode);
+
+ array = config_array[pf][MINIDSP_DMODE];
+ if ((aic326x->current_dac_config[pf] >= 0) &&
+ (aic326x->current_dac_config[pf] < MAX_CONFIG_ARRAYS)) {
+ val = aic326x->current_dac_config[pf];
+ array = &config_array[pf][MINIDSP_DMODE][val];
+ byte_i2c_array_transfer(codec,
+ array->regs,
+ (array->d_inst_size +
+ array->d_coeff_size));
+ } else {
+ DBG(KERN_INFO "#%s: Invalid Configuration ID %d specified.\n",
+ __func__, aic326x->current_dac_config[pf]);
+ }
+
+ array = config_array[pf][MINIDSP_AMODE];
+ if ((aic326x->current_adc_config[pf] >= 0) &&
+ (aic326x->current_adc_config[pf] < MAX_CONFIG_ARRAYS)) {
+ val = aic326x->current_adc_config[pf];
+ minidsp_i2c_multibyte_transfer(codec,
+ array[val].regs,
+ array[val].a_inst_size +
+ array[val].a_coeff_size);
+ } else {
+ DBG(KERN_INFO "#%s: Invalid Configuration ID %d specified.\n",
+ __func__, aic326x->current_dac_config[pf]);
+ }
+ return;
+}
+
+/*
+ *--------------------------------------------------------------------------
+ * Function : config_multibyte_for_mode
+ * Purpose : Function which is invoked when user changes the configuration
+ * at run-time. Internally configures/switches both
+ * miniDSP_D and miniDSP_A Coefficient arrays.
+ *---------------------------------------------------------------------------
+ */
+static int multibyte_coeff_change(struct snd_soc_codec *codec, int bk)
+{
+
+ u8 value[2], swap_reg_pre, swap_reg_post;
+ struct i2c_client *i2c;
+ i2c = codec->control_data;
+
+ aic3262_change_book(codec, bk);
+
+ value[0] = 1;
+
+ if (i2c_master_send(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not write register address\n");
+ else {
+ /* Read the Value of the Page 8 Register 1 which controls the
+ Adaptive Switching Mode */
+ if (i2c_master_recv(i2c, value, 1) != 1) {
+ printk(KERN_ERR "Can not read codec registers\n");
+ goto err;
+ }
+ swap_reg_pre = value[0];
+
+ /* Write the Register bit updates */
+ value[1] = value[0] | 1;
+ value[0] = 1;
+
+ if (i2c_master_send(i2c, value, 2) != 2) {
+ printk(KERN_ERR "Can not write register address\n");
+ goto err;
+ }
+ value[0] = 1;
+ /* verify buffer swap */
+ if (i2c_master_send(i2c, value, 1) != 1)
+ printk(KERN_ERR "Can not write register address\n");
+
+ /* Read the Value of the Page 8 Register 1 which controls the
+ Adaptive Switching Mode */
+ if (i2c_master_recv(i2c, &swap_reg_post, 1) != 1)
+ printk(KERN_ERR "Can not read codec registers\n");
+
+ if ((swap_reg_pre == 4 && swap_reg_post == 6)
+ || (swap_reg_pre == 6 && swap_reg_post == 4))
+ DBG(KERN_INFO "Buffer swap success\n");
+ else
+ printk(KERN_ERR
+ "Buffer swap...FAILED\nswap_reg_pre=%x, swap_reg_post=%x\n",
+ swap_reg_pre, swap_reg_post);
+ }
+
+err:
+ return 0;
+}
+
+#endif
+
+MODULE_DESCRIPTION("ASoC AIC3262 miniDSP multi-configuration");
+MODULE_AUTHOR("Barani Prashanth <gvbarani@mistralsolutions.com>");
+MODULE_LICENSE("GPL");