This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TLV320AIC3262: TLV320AIC3262 driver supports suspend to resume state ?

Part Number: TLV320AIC3262

Hi ..

We are using TLV320AIC3262 codec on our platform.

We are trying to test suspend to resume state, We are facing an input/output error.

I have attached the codec driver used in our platform, Please let us know attached driver supports suspend and resume state or not.

/*
 * linux/sound/soc/codecs/tlv320aic326x.c
 *
 * Copyright (C) 2011 Texas Instruments Inc.,
 *
 * 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    TI	20-01-2011
 *
 *		The AIC325x ASoC driver is ported for the codec AIC3262.
 * Rev 0.2   ASoC driver support    TI	21-03-2011
 *		The AIC326x ASoC driver is updated for linux 2.6.32 Kernel.
 * Rev 0.3   ASoC driver support    TI	   20-04-2011
 *		The AIC326x ASoC driver is ported to 2.6.35 omap4 kernel
 */

/*
 *****************************************************************************
 * INCLUDES
 *****************************************************************************
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <sound/jack.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/input.h>

#include <sound/tlv.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include "tlv320aic3262-registers.h"
#include "tlv320aic3xxx-core.h"
#include "aic3xxx/aic3xxx_cfw_ops.h"

#include "tlv320aic326x.h"

/*****************************************************************************
			 Macros
******************************************************************************

******************************************************************************
		  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, int stream);

static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);

static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
				unsigned int Fin, unsigned int Fout);

static int aic3262_set_bias_level(struct snd_soc_component *codec,
				  enum snd_soc_bias_level level);

static int aic3262_set_mode_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol);
static int aic3262_set_mode_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol);

static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w,
				 struct snd_kcontrol *kcontrol, int event);
static int aic3262_get_runstate(struct snd_soc_component *codec);
static int aic3262_dsp_pwrdwn_status(struct snd_soc_component *codec);
static int aic3262_dsp_pwrup(struct snd_soc_component *codec, int state);
static int aic3262_restart_dsps_sync(struct snd_soc_component *codec, int rs);

static inline unsigned int dsp_non_sync_mode(unsigned int state)
			{ return (!((state & 0x03) && (state & 0x30))); }

static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0);
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1200, 50, 0);
static const DECLARE_TLV_DB_SCALE(spk_gain_tlv, 600, 600, 0);
static const DECLARE_TLV_DB_SCALE(output_gain_tlv, -600, 100, 1);
static const DECLARE_TLV_DB_SCALE(micpga_gain_tlv, 0, 50, 0);
static const DECLARE_TLV_DB_SCALE(adc_fine_gain_tlv, -40, 10, 0);
static const DECLARE_TLV_DB_SCALE(beep_gen_volume_tlv, -6300, 100, 0);

/* Chip-level Input and Output CM Mode Controls */
static const char * const input_common_mode_text[] = {
	"0.9v", "0.75v"
};

static const char * const output_common_mode_text[] = {
	"Input CM", "1.25v", "1.5v", "1.65v"
};

static const struct soc_enum input_cm_mode =
SOC_ENUM_SINGLE(AIC3262_CM_REG, 2, 2, input_common_mode_text);

static const struct soc_enum output_cm_mode =
SOC_ENUM_SINGLE(AIC3262_CM_REG, 0, 4, output_common_mode_text);
/*
 *****************************************************************************
 * 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_SX_TLV("PCM Playback Volume",
				AIC3262_DAC_LVOL, AIC3262_DAC_RVOL, 0,
				0x81, 0xaf, dac_vol_tlv),
#endif
	/*HP Driver Gain Control */
	SOC_DOUBLE_R_SX_TLV("HeadPhone Driver Amplifier Volume",
				AIC3262_HPL_VOL, AIC3262_HPR_VOL, 0, 0x39,
				0x15, output_gain_tlv),
	/*LO Driver Gain Control */
	SOC_DOUBLE_TLV("Speaker Amplifier Volume", AIC3262_SPK_AMP_CNTL_R4, 4,
			0, 5, 0, spk_gain_tlv),

	SOC_DOUBLE_R_SX_TLV("Receiver Amplifier Volume",
				AIC3262_REC_AMP_CNTL_R5, AIC3262_RAMPR_VOL, 0,
				0x39, 0x24, output_gain_tlv),

	SOC_DOUBLE_R_SX_TLV("PCM Capture Volume", AIC3262_LADC_VOL,
				AIC3262_RADC_VOL, 0, 0x68, 0x41,
				adc_vol_tlv),

	SOC_DOUBLE_R_TLV("MicPGA Volume Control", AIC3262_MICL_PGA,
			 AIC3262_MICR_PGA, 0, 0x5F, 0, micpga_gain_tlv),

	SOC_DOUBLE_TLV("PCM Capture Fine Gain Volume", AIC3262_ADC_FINE_GAIN,
			4, 0, 5, 1, adc_fine_gain_tlv),

	SOC_DOUBLE("ADC channel mute", AIC3262_ADC_FINE_GAIN, 7, 3, 1, 0),

	SOC_DOUBLE("DAC MUTE", AIC3262_DAC_MVOL_CONF, 2, 3, 1, 1),

	SOC_SINGLE("RESET", AIC3262_RESET_REG, 0, 1, 0),

	SOC_SINGLE("DAC VOL SOFT STEPPING", AIC3262_DAC_MVOL_CONF, 0, 2, 0),

	SOC_SINGLE("DAC AUTO MUTE CONTROL", AIC3262_DAC_MVOL_CONF, 4, 7, 0),

	SOC_SINGLE("RIGHT MODULATOR SETUP", AIC3262_DAC_MVOL_CONF, 7, 1, 0),

	SOC_SINGLE("ADC Volume soft stepping", AIC3262_ADC_CHANNEL_POW,
		   0, 3, 0),

	SOC_SINGLE("Mic Bias ext independent enable", AIC3262_MIC_BIAS_CNTL,
		   7, 1, 0),

	SOC_SINGLE("MICBIAS EXT Power Level", AIC3262_MIC_BIAS_CNTL, 4, 3, 0),

	SOC_SINGLE("MICBIAS INT Power Level", AIC3262_MIC_BIAS_CNTL, 0, 3, 0),

	SOC_SINGLE("BEEP_GEN_EN", AIC3262_BEEP_CNTL_R1, 7, 1, 0),

	SOC_DOUBLE_R("BEEP_VOL_CNTL", AIC3262_BEEP_CNTL_R1,
		     AIC3262_BEEP_CNTL_R2, 0, 0x0F, 1),

	SOC_SINGLE("BEEP_MAS_VOL", AIC3262_BEEP_CNTL_R2, 6, 3, 0),

	SOC_SINGLE("DAC PRB Selection", AIC3262_DAC_PRB, 0, 26, 0),

	SOC_SINGLE("ADC PRB Selection", AIC3262_ADC_PRB, 0, 18, 0),

	SOC_ENUM("Input CM mode", input_cm_mode),

	SOC_ENUM("Output CM mode", output_cm_mode),

//	SOC_SINGLE_EXT("FIRMWARE SET MODE", SND_SOC_NOPM, 0, 0xffff, 0,
//			aic3262_set_mode_get, aic3262_set_mode_put),
};

/*
 *----------------------------------------------------------------------------
 * @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_asi1_dai_ops = {
	.hw_params = aic3262_hw_params,
	.mute_stream = aic3262_mute,
	.set_fmt = aic3262_set_dai_fmt,
	.set_pll = aic3262_dai_set_pll,
};

struct snd_soc_dai_ops aic3262_asi2_dai_ops = {
	.hw_params = aic3262_hw_params,
	.mute_stream = aic3262_mute,
	.set_fmt = aic3262_set_dai_fmt,
	.set_pll = aic3262_dai_set_pll,
};

struct snd_soc_dai_ops aic3262_asi3_dai_ops = {
	.hw_params = aic3262_hw_params,
	.mute_stream = aic3262_mute,
	.set_fmt = aic3262_set_dai_fmt,
	.set_pll = aic3262_dai_set_pll,
};

struct snd_soc_dai_driver aic326x_dai_driver[] = {
	{
	 .name = "aic326x-asi1",
	 .playback = {
		      .stream_name = "ASI1 Playback",
		      .channels_min = 1,
		      .channels_max = 8,
		      .rates = AIC3262_RATES,
		      .formats = AIC3262_FORMATS,
		      },
	 .capture = {
		     .stream_name = "ASI1 Capture",
		     .channels_min = 1,
		     .channels_max = 8,
		     .rates = AIC3262_RATES,
		     .formats = AIC3262_FORMATS,
		     },
	 .ops = &aic3262_asi1_dai_ops,
	 },
	{
	 .name = "aic326x-asi2",
	 .playback = {
		      .stream_name = "ASI2 Playback",
		      .channels_min = 1,
		      .channels_max = 2,
		      .rates = AIC3262_RATES,
		      .formats = AIC3262_FORMATS,
		      },
	 .capture = {
		     .stream_name = "ASI2 Capture",
		     .channels_min = 1,
		     .channels_max = 2,
		     .rates = AIC3262_RATES,
		     .formats = AIC3262_FORMATS,
		     },
	 .ops = &aic3262_asi2_dai_ops,
	 },
	{
	 .name = "aic326x-asi3",
	 .playback = {
		      .stream_name = "ASI3 Playback",
		      .channels_min = 1,
		      .channels_max = 2,
		      .rates = AIC3262_RATES,
		      .formats = AIC3262_FORMATS,
		      },
	 .capture = {
		     .stream_name = "ASI3 Capture",
		     .channels_min = 1,
		     .channels_max = 2,
		     .rates = AIC3262_RATES,
		     .formats = AIC3262_FORMATS,
		     },
	 .ops = &aic3262_asi3_dai_ops,
	 },

};

static const unsigned int adc_ma_tlv[] = {
	TLV_DB_RANGE_HEAD(7),
	1, 1, TLV_DB_SCALE_ITEM(-3610, 0, 0),
	2, 2, TLV_DB_SCALE_ITEM(-3010, 0, 0),
	3, 3, TLV_DB_SCALE_ITEM(-2660, 0, 0),
	4, 4, TLV_DB_SCALE_ITEM(-2410, 0, 0),
	5, 7, TLV_DB_SCALE_ITEM(-2210, 1500, 0),
	8, 11, TLV_DB_SCALE_ITEM(-1810, 1000, 0),
	12, 41 , TLV_DB_SCALE_ITEM(-1450, 500, 0)
};

static const DECLARE_TLV_DB_SCALE(lo_hp_tlv, -7830, 50, 0);
static const struct snd_kcontrol_new mal_pga_mixer_controls[] = {
	SOC_DAPM_SINGLE("IN1 Left Capture Switch", AIC3262_MA_CNTL, 5, 1, 0),
	SOC_DAPM_SINGLE_TLV("Left MicPGA Volume", AIC3262_LADC_PGA_MAL_VOL,
				0, 0x3f, 1, adc_ma_tlv),
};

static const struct snd_kcontrol_new mar_pga_mixer_controls[] = {
	SOC_DAPM_SINGLE("IN1 Right Capture Switch", AIC3262_MA_CNTL, 4, 1, 0),
	SOC_DAPM_SINGLE_TLV("Right MicPGA Volume", AIC3262_RADC_PGA_MAR_VOL,
				0, 0x3f, 1, adc_ma_tlv),
};

/* Left HPL Mixer */
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
	SOC_DAPM_SINGLE("MA Left Playback Switch", AIC3262_HP_AMP_CNTL_R1, 7, 1,
			0),
	SOC_DAPM_SINGLE("Left DAC Playback Switch", AIC3262_HP_AMP_CNTL_R1,
			5, 1, 0),
	SOC_DAPM_SINGLE_TLV("LO Left-B1 Playback Volume",
				AIC3262_HP_AMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv),
};

/* Right HPR Mixer */
static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
	SOC_DAPM_SINGLE_TLV("LO Right-B1 Playback Volume",
				AIC3262_HP_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv),
	SOC_DAPM_SINGLE("Left DAC Playback Switch", AIC3262_HP_AMP_CNTL_R1,
			2, 1, 0),
	SOC_DAPM_SINGLE("Right DAC Playback Switch", AIC3262_HP_AMP_CNTL_R1,
			4, 1, 0),
	SOC_DAPM_SINGLE("MA Right Playback Switch", AIC3262_HP_AMP_CNTL_R1,
			6, 1, 0),
};

/* Left LOL Mixer */
static const struct snd_kcontrol_new lol_output_mixer_controls[] = {
	SOC_DAPM_SINGLE("MA Left Playback Switch", AIC3262_LINE_AMP_CNTL_R2,
			7, 1, 0),
	SOC_DAPM_SINGLE("IN1 Left-B Capture Switch", AIC3262_LINE_AMP_CNTL_R2,
			3, 1, 0),
	SOC_DAPM_SINGLE("Left DAC Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
			7, 1, 0),
	SOC_DAPM_SINGLE("Right DAC Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
			5, 1, 0),
};

/* Right LOR Mixer */
static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
	SOC_DAPM_SINGLE("LO Left Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
			2, 1, 0),
	SOC_DAPM_SINGLE("Right DAC Playback Switch", AIC3262_LINE_AMP_CNTL_R1,
			6, 1, 0),
	SOC_DAPM_SINGLE("MA Right Playback Switch", AIC3262_LINE_AMP_CNTL_R2,
			6, 1, 0),
	SOC_DAPM_SINGLE("IN1 Right-B Capture Switch", AIC3262_LINE_AMP_CNTL_R2,
			0, 1, 0),
};

/* Left SPKL Mixer */
static const struct snd_kcontrol_new spkl_output_mixer_controls[] = {
	SOC_DAPM_SINGLE("MA Left Playback Switch", AIC3262_SPK_AMP_CNTL_R1,
			7, 1, 0),
	SOC_DAPM_SINGLE_TLV("LO Left Playback Volume",
				AIC3262_SPK_AMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv),
	SOC_DAPM_SINGLE("SPR_IN Switch", AIC3262_SPK_AMP_CNTL_R1, 2, 1, 0),
};

/* Right SPKR Mixer */
static const struct snd_kcontrol_new spkr_output_mixer_controls[] = {
	SOC_DAPM_SINGLE_TLV("LO Right Playback Volume",
				AIC3262_SPK_AMP_CNTL_R3, 0, 0x7f, 1, lo_hp_tlv),
	SOC_DAPM_SINGLE("MA Right Playback Switch",
			AIC3262_SPK_AMP_CNTL_R1, 6, 1, 0),
};

/* REC Mixer */
static const struct snd_kcontrol_new rec_output_mixer_controls[] = {
	SOC_DAPM_SINGLE_TLV("LO Left-B2 Playback Volume",
				AIC3262_RAMP_CNTL_R1, 0, 0x7f, 1, lo_hp_tlv),
	SOC_DAPM_SINGLE_TLV("IN1 Left Capture Volume",
				AIC3262_IN1L_SEL_RM, 0, 0x7f, 1, lo_hp_tlv),
	SOC_DAPM_SINGLE_TLV("IN1 Right Capture Volume",
				AIC3262_IN1R_SEL_RM, 0, 0x7f, 1, lo_hp_tlv),
	SOC_DAPM_SINGLE_TLV("LO Right-B2 Playback Volume",
				AIC3262_RAMP_CNTL_R2, 0, 0x7f, 1, lo_hp_tlv),
};

/* Left Input Mixer */
static const struct snd_kcontrol_new left_input_mixer_controls[] = {
	SOC_DAPM_SINGLE("IN1 Left Capture Switch", AIC3262_LMIC_PGA_PIN,
			6, 3, 0),
	SOC_DAPM_SINGLE("IN2 Left Capture Switch", AIC3262_LMIC_PGA_PIN,
			4, 3, 0),
	SOC_DAPM_SINGLE("IN3 Left Capture Switch", AIC3262_LMIC_PGA_PIN,
			2, 3, 0),
	SOC_DAPM_SINGLE("IN4 Left Capture Switch", AIC3262_LMIC_PGA_PM_IN4,
			5, 1, 0),
	SOC_DAPM_SINGLE("IN1 Right Capture Switch", AIC3262_LMIC_PGA_PIN,
			0, 3, 0),
	SOC_DAPM_SINGLE("IN2 Right Capture Switch", AIC3262_LMIC_PGA_MIN,
			4, 3, 0),
	SOC_DAPM_SINGLE("IN3 Right Capture Switch", AIC3262_LMIC_PGA_MIN,
			2, 3, 0),
	SOC_DAPM_SINGLE("IN4 Right Capture Switch", AIC3262_LMIC_PGA_PM_IN4,
			4, 1, 0),
	SOC_DAPM_SINGLE("CM2 Left Capture Switch", AIC3262_LMIC_PGA_MIN,
			0, 3, 0),
	SOC_DAPM_SINGLE("CM1 Left Capture Switch", AIC3262_LMIC_PGA_MIN,
			6, 3, 0),
};

/* Right Input Mixer */
static const struct snd_kcontrol_new right_input_mixer_controls[] = {
	SOC_DAPM_SINGLE("IN1 Right Capture Switch", AIC3262_RMIC_PGA_PIN,
			6, 3, 0),
	SOC_DAPM_SINGLE("IN2 Right Capture Switch", AIC3262_RMIC_PGA_PIN,
			4, 3, 0),
	SOC_DAPM_SINGLE("IN3 Right Capture Switch", AIC3262_RMIC_PGA_PIN,
			2, 3, 0),
	SOC_DAPM_SINGLE("IN4 Right Capture Switch", AIC3262_RMIC_PGA_PM_IN4,
			5, 1, 0),
	SOC_DAPM_SINGLE("IN2 Left Capture Switch", AIC3262_RMIC_PGA_PIN,
			0, 3, 0),
	SOC_DAPM_SINGLE("IN1 Left Capture Switch", AIC3262_RMIC_PGA_MIN,
			4, 3, 0),
	SOC_DAPM_SINGLE("IN3 Left Capture Switch", AIC3262_RMIC_PGA_MIN,
			2, 3, 0),
	SOC_DAPM_SINGLE("IN4 Left Capture Switch", AIC3262_RMIC_PGA_PM_IN4,
			4, 1, 0),
	SOC_DAPM_SINGLE("CM1 Right Capture Switch", AIC3262_RMIC_PGA_MIN,
			6, 3, 0),
	SOC_DAPM_SINGLE("CM2 Right Capture Switch", AIC3262_RMIC_PGA_MIN,
			0, 3, 0),
};

static const char * const asi1lin_text[] = {
	"Off", "ASI1 Left In", "ASI1 Right In", "ASI1 MonoMix In"
};

SOC_ENUM_SINGLE_DECL(asi1lin_enum, AIC3262_ASI1_DAC_OUT_CNTL, 6, asi1lin_text);

static const struct snd_kcontrol_new asi1lin_control =
SOC_DAPM_ENUM("ASI1LIN Route", asi1lin_enum);

static const char * const asi1rin_text[] = {
	"Off", "ASI1 Right In", "ASI1 Left In", "ASI1 MonoMix In"
};

SOC_ENUM_SINGLE_DECL(asi1rin_enum, AIC3262_ASI1_DAC_OUT_CNTL, 4, asi1rin_text);

static const struct snd_kcontrol_new asi1rin_control =
SOC_DAPM_ENUM("ASI1RIN Route", asi1rin_enum);

static const char * const asi2lin_text[] = {
	"Off", "ASI2 Left In", "ASI2 Right In", "ASI2 MonoMix In"
};

SOC_ENUM_SINGLE_DECL(asi2lin_enum, AIC3262_ASI2_DAC_OUT_CNTL, 6, asi2lin_text);

static const struct snd_kcontrol_new asi2lin_control =
SOC_DAPM_ENUM("ASI2LIN Route", asi2lin_enum);

static const char * const asi2rin_text[] = {
	"Off", "ASI2 Right In", "ASI2 Left In", "ASI2 MonoMix In"
};

SOC_ENUM_SINGLE_DECL(asi2rin_enum, AIC3262_ASI2_DAC_OUT_CNTL, 4, asi2rin_text);

static const struct snd_kcontrol_new asi2rin_control =
SOC_DAPM_ENUM("ASI2RIN Route", asi2rin_enum);

static const char * const asi3lin_text[] = {
	"Off", "ASI3 Left In", "ASI3 Right In", "ASI3 MonoMix In"
};

SOC_ENUM_SINGLE_DECL(asi3lin_enum, AIC3262_ASI3_DAC_OUT_CNTL, 6, asi3lin_text);

static const struct snd_kcontrol_new asi3lin_control =
SOC_DAPM_ENUM("ASI3LIN Route", asi3lin_enum);

static const char * const asi3rin_text[] = {
	"Off", "ASI3 Right In", "ASI3 Left In", "ASI3 MonoMix In"
};

SOC_ENUM_SINGLE_DECL(asi3rin_enum, AIC3262_ASI3_DAC_OUT_CNTL, 4, asi3rin_text);

static const struct snd_kcontrol_new asi3rin_control =
SOC_DAPM_ENUM("ASI3RIN Route", asi3rin_enum);

static const char * const dacminidspin1_text[] = {
	"ASI1 In", "ASI2 In", "ASI3 In", "ADC MiniDSP Out"
};

SOC_ENUM_SINGLE_DECL(dacminidspin1_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 4,
		     dacminidspin1_text);

static const struct snd_kcontrol_new dacminidspin1_control =
SOC_DAPM_ENUM("DAC MiniDSP IN1 Route", dacminidspin1_enum);

static const char * const dacminidspin2_text[] = {
	"ASI1 In", "ASI2 In", "ASI3 In"
};

SOC_ENUM_SINGLE_DECL(dacminidspin2_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 2,
		     dacminidspin2_text);

static const struct snd_kcontrol_new dacminidspin2_control =
SOC_DAPM_ENUM("DAC MiniDSP IN2 Route", dacminidspin2_enum);

static const char * const dacminidspin3_text[] = {
	"ASI1 In", "ASI2 In", "ASI3 In"
};

SOC_ENUM_SINGLE_DECL(dacminidspin3_enum, AIC3262_MINIDSP_DATA_PORT_CNTL, 0,
		     dacminidspin3_text);

static const struct snd_kcontrol_new dacminidspin3_control =
SOC_DAPM_ENUM("DAC MiniDSP IN3 Route", dacminidspin3_enum);

static const char * const adcdac_route_text[] = {
	"Off",
	"On",
};

SOC_ENUM_SINGLE_DECL(adcdac_enum, 0, 2, adcdac_route_text);

static const struct snd_kcontrol_new adcdacroute_control =
SOC_DAPM_ENUM("ADC DAC Route", adcdac_enum);

static const char * const dout1_text[] = {
	"ASI1 Out",
	"DIN1 Bypass",
	"DIN2 Bypass",
	"DIN3 Bypass",
};

SOC_ENUM_SINGLE_DECL(dout1_enum, AIC3262_ASI1_DOUT_CNTL, 0, dout1_text);
static const struct snd_kcontrol_new dout1_control =
SOC_DAPM_ENUM("DOUT1 Route", dout1_enum);

static const char * const dout2_text[] = {
	"ASI2 Out",
	"DIN1 Bypass",
	"DIN2 Bypass",
	"DIN3 Bypass",
};

SOC_ENUM_SINGLE_DECL(dout2_enum, AIC3262_ASI2_DOUT_CNTL, 0, dout2_text);
static const struct snd_kcontrol_new dout2_control =
SOC_DAPM_ENUM("DOUT2 Route", dout2_enum);

static const char * const dout3_text[] = {
	"ASI3 Out",
	"DIN1 Bypass",
	"DIN2 Bypass",
	"DIN3 Bypass",
};

SOC_ENUM_SINGLE_DECL(dout3_enum, AIC3262_ASI3_DOUT_CNTL, 0, dout3_text);
static const struct snd_kcontrol_new dout3_control =
SOC_DAPM_ENUM("DOUT3 Route", dout3_enum);

static const char * const asi1out_text[] = {
	"Off",
	"ADC MiniDSP Out1",
	"ASI1In Bypass",
	"ASI2In Bypass",
	"ASI3In Bypass",
};

SOC_ENUM_SINGLE_DECL(asi1out_enum, AIC3262_ASI1_ADC_INPUT_CNTL,
		     0, asi1out_text);
static const struct snd_kcontrol_new asi1out_control =
SOC_DAPM_ENUM("ASI1OUT Route", asi1out_enum);

static const char * const asi2out_text[] = {
	"Off",
	"ADC MiniDSP Out1",
	"ASI1In Bypass",
	"ASI2In Bypass",
	"ASI3In Bypass",
	"ADC MiniDSP Out2",
};

SOC_ENUM_SINGLE_DECL(asi2out_enum, AIC3262_ASI2_ADC_INPUT_CNTL,
		     0, asi2out_text);
static const struct snd_kcontrol_new asi2out_control =
SOC_DAPM_ENUM("ASI2OUT Route", asi2out_enum);
static const char * const asi3out_text[] = {
	"Off",
	"ADC MiniDSP Out1",
	"ASI1In Bypass",
	"ASI2In Bypass",
	"ASI3In Bypass",
	"Reserved",
	"ADC MiniDSP Out3",
};

SOC_ENUM_SINGLE_DECL(asi3out_enum, AIC3262_ASI3_ADC_INPUT_CNTL,
		     0, asi3out_text);
static const struct snd_kcontrol_new asi3out_control =
SOC_DAPM_ENUM("ASI3OUT Route", asi3out_enum);
static const char * const asibclk_text[] = {
	"DAC_CLK",
	"DAC_MOD_CLK",
	"ADC_CLK",
	"ADC_MOD_CLK",
};

SOC_ENUM_SINGLE_DECL(asi1bclk_enum, AIC3262_ASI1_BCLK_N_CNTL, 0, asibclk_text);
static const struct snd_kcontrol_new asi1bclk_control =
SOC_DAPM_ENUM("ASI1_BCLK Route", asi1bclk_enum);

SOC_ENUM_SINGLE_DECL(asi2bclk_enum, AIC3262_ASI2_BCLK_N_CNTL, 0, asibclk_text);
static const struct snd_kcontrol_new asi2bclk_control =
SOC_DAPM_ENUM("ASI2_BCLK Route", asi2bclk_enum);
SOC_ENUM_SINGLE_DECL(asi3bclk_enum, AIC3262_ASI3_BCLK_N_CNTL, 0, asibclk_text);
static const struct snd_kcontrol_new asi3bclk_control =
SOC_DAPM_ENUM("ASI3_BCLK Route", asi3bclk_enum);

static const char * const adc_mux_text[] = {
	"Analog",
	"Digital",
};

SOC_ENUM_SINGLE_DECL(adcl_enum, AIC3262_ADC_CHANNEL_POW, 4, adc_mux_text);
SOC_ENUM_SINGLE_DECL(adcr_enum, AIC3262_ADC_CHANNEL_POW, 2, adc_mux_text);

static const struct snd_kcontrol_new adcl_mux =
SOC_DAPM_ENUM("Left ADC Route", adcl_enum);

static const struct snd_kcontrol_new adcr_mux =
SOC_DAPM_ENUM("Right ADC Route", adcr_enum);

/**
 * aic326x_hp_event: - To handle headphone related task before and after
 *			headphone powrup and power down
 * @w: pointer variable to dapm_widget
 * @kcontrol: mixer control
 * @event: event element information
 *
 * Returns 0 for success.
 */
static int aic326x_hp_event(struct snd_soc_dapm_widget *w,
			    struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	int reg_mask = 0;
	int mute_reg = 0;
	int ret_wbits = 0;
	int hpl_hpr;

	if (w->shift == 1) {
		reg_mask = AIC3262_HPL_POWER_STATUS_MASK;
		mute_reg = AIC3262_HPL_VOL;
	}
	if (w->shift == 0) {
		reg_mask = AIC3262_HPR_POWER_STATUS_MASK;
		mute_reg = AIC3262_HPR_VOL;
	}
	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
			snd_soc_component_update_bits(codec, AIC3262_CHARGE_PUMP_CNTL,
			AIC3262_DYNAMIC_OFFSET_CALIB_MASK,
			AIC3262_DYNAMIC_OFFSET_CALIB);
			snd_soc_component_write(codec, mute_reg, 0x80);
			snd_soc_component_update_bits(codec, AIC3262_HP_CTL,
			AIC3262_HP_STAGE_MASK ,
			AIC3262_HP_STAGE_25 << AIC3262_HP_STAGE_SHIFT);
		break;

	case SND_SOC_DAPM_POST_PMU:
		ret_wbits = aic3xxx_wait_bits(control_data,
					      AIC3262_HP_FLAG, reg_mask,
					      reg_mask, AIC326X_TIME_DELAY,
					      AIC326X_DELAY_COUNTER);
		if (!ret_wbits) {
			dev_err(codec->dev, "HP POST_PMU timedout\n");
			return -1;
		}
		snd_soc_component_update_bits(codec, AIC3262_HP_CTL,
			AIC3262_HP_STAGE_MASK ,
			AIC3262_HP_STAGE_100 << AIC3262_HP_STAGE_SHIFT);
		break;

	case SND_SOC_DAPM_PRE_PMD:
		snd_soc_component_update_bits(codec, AIC3262_HP_CTL,
				AIC3262_HP_STAGE_MASK ,
				AIC3262_HP_STAGE_25 << AIC3262_HP_STAGE_SHIFT);
		hpl_hpr = snd_soc_component_read(codec, AIC3262_HP_AMP_CNTL_R1);
		if((hpl_hpr & 0x3) == 0x3) {
			snd_soc_component_update_bits(codec, AIC3262_HP_AMP_CNTL_R1,
						AIC3262_HPL_POWER_MASK, 0x0);
			mdelay(1);
			snd_soc_component_update_bits(codec, AIC3262_HP_AMP_CNTL_R1,
						AIC3262_HPR_POWER_MASK, 0x0);
		}
		break;

	case SND_SOC_DAPM_POST_PMD:
		ret_wbits = aic3xxx_wait_bits(control_data,
					      AIC3262_HP_FLAG, reg_mask, 0,
					      AIC326X_TIME_DELAY,
					      AIC326X_DELAY_COUNTER);
		if (!ret_wbits) {
			dev_err(codec->dev, "HP POST_PMD timedout\n");
			return -1;
		}
		snd_soc_component_write(codec, mute_reg, 0xb9);
		hpl_hpr = snd_soc_component_read(codec, AIC3262_POWER_CONF);
		snd_soc_component_write(codec, AIC3262_POWER_CONF, hpl_hpr);
		break;
	default:
		BUG();
		return -EINVAL;
	}
	return 0;
}

/**
 *aic326x_dac_event: Headset popup reduction and powering up dsps together
 *			when they are in sync mode
 * @w: pointer variable to dapm_widget
 * @kcontrol: pointer to sound control
 * @event: event element information
 *
 * Returns 0 for success.
 */
static int aic326x_dac_event(struct snd_soc_dapm_widget *w,
			     struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	int reg_mask = 0;
	int ret_wbits = 0;
	int run_state_mask;
	struct aic3262_priv *aic3262 = control_data->control_data;
	int sync_needed = 0, non_sync_state = 0;
	int other_dsp = 0, run_state = 0;

	if (w->shift == 7) {
		reg_mask = AIC3262_LDAC_POWER_STATUS_MASK;
		run_state_mask = AIC3XXX_COPS_MDSP_D_L;
	}
	if (w->shift == 6) {
		reg_mask = AIC3262_RDAC_POWER_STATUS_MASK;
		run_state_mask = AIC3XXX_COPS_MDSP_D_R;
	}
	switch (event) {
	case SND_SOC_DAPM_POST_PMU:

		ret_wbits = aic3xxx_wait_bits(control_data,
					      AIC3262_DAC_FLAG, reg_mask,
					      reg_mask, AIC326X_TIME_DELAY,
					      AIC326X_DELAY_COUNTER);

		sync_needed = aic3xxx_reg_read(control_data,
							AIC3262_DAC_PRB);
		non_sync_state = dsp_non_sync_mode(aic3262->dsp_runstate);
		other_dsp = aic3262->dsp_runstate & AIC3XXX_COPS_MDSP_A;

		if (sync_needed && non_sync_state && other_dsp) {
			run_state = aic3262_get_runstate(
						aic3262->codec);
			aic3262_dsp_pwrdwn_status(aic3262->codec);
			aic3262_dsp_pwrup(aic3262->codec, run_state);
		}
		aic3262->dsp_runstate |= run_state_mask;

		if (!ret_wbits) {
			dev_err(codec->dev, "DAC POST_PMU timedout\n");
			return -1;
		}
		break;
	case SND_SOC_DAPM_POST_PMD:

		ret_wbits = aic3xxx_wait_bits(control_data,
					      AIC3262_DAC_FLAG, reg_mask, 0,
					      AIC326X_TIME_DELAY,
					      AIC326X_DELAY_COUNTER);

		aic3262->dsp_runstate = (aic3262->dsp_runstate &
					 ~run_state_mask);
		if (!ret_wbits) {
			dev_err(codec->dev, "DAC POST_PMD timedout\n");
			return -1;
		}
		break;
	default:
		BUG();
		return -EINVAL;
	}
	return 0;
}

/**
 * aic326x_spk_event: Speaker related task before and after
 *			 headphone powrup and power down$
 * @w: pointer variable to dapm_widget,
 * @kcontrolr: pointer variable to sound control,
 * @event:	integer to event,
 *
 * Return value: 0 for success
 */
static int aic326x_spk_event(struct snd_soc_dapm_widget *w,
			     struct snd_kcontrol *kcontrol, int event)
{
	int reg_mask;

	if (w->shift == 1)
		reg_mask = AIC3262_SPKL_POWER_STATUS_MASK;
	if (w->shift == 0)
		reg_mask = AIC3262_SPKR_POWER_STATUS_MASK;
	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		mdelay(1);
		break;
	case SND_SOC_DAPM_POST_PMD:
		mdelay(1);
		break;
	default:
		BUG();
		return -EINVAL;
	}
	return 0;
}

/**$
 * pll_power_on_event: provide delay after widget  power up
 * @w:  pointer variable to dapm_widget,
 * @kcontrolr: pointer variable to sound control,
 * @event:	integer to event,
 *
 * Return value: 0 for success
 */
static int pll_power_on_event(struct snd_soc_dapm_widget *w,
			      struct snd_kcontrol *kcontrol, int event)
{
	if (event == SND_SOC_DAPM_POST_PMU)
		mdelay(10);
	return 0;
}

/**
 * aic3262_set_mode_get: To get different mode of Firmware through tinymix
 * @kcontrolr: pointer to sound control,
 * ucontrol: pointer to control element value,
 *
 * Return value: 0 for success
 */
static int aic3262_set_mode_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *priv_ds = control_data->control_data;

	ucontrol->value.integer.value[0] = ((priv_ds->cfw_p->cur_mode << 8)
					    | priv_ds->cfw_p->cur_cfg);

	return 0;
}

/**
 * aic3262_set_mode_put: To set different mode of Firmware through tinymix
 * @kcontrolr: pointer to sound control,
 * ucontrol: pointer to control element value,
 *
 * Return value: 0 for success
 */
static int aic3262_set_mode_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *priv_ds = control_data->control_data;

	int next_mode = 0, next_cfg = 0;
	int ret = 0;

	next_mode = (ucontrol->value.integer.value[0] >> 8);
	next_cfg = (ucontrol->value.integer.value[0]) & 0xFF;
	if (priv_ds == NULL)
		dev_err(codec->dev, "failed to load firmware\n");
	else
		ret = aic3xxx_cfw_setmode_cfg(priv_ds->cfw_p,
					      next_mode, next_cfg);
	return ret;
}

/**
 * aic326x_adc_dsp_event: To get DSP run state to perform synchronization
 * @w: pointer variable to dapm_widget
 * @kcontrol: pointer to sound control
 * @event: event element information
 *
 * Returns 0 for success.
 */
static int aic326x_adc_dsp_event(struct snd_soc_dapm_widget *w,
				 struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
	int run_state = 0;
	int non_sync_state = 0, sync_needed = 0;
	int other_dsp = 0;
	int run_state_mask = 0;
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control_data->control_data;

	int reg_mask = 0;
	int ret_wbits = 0;

	if (w->shift == 7) {
		reg_mask = AIC3262_LADC_POWER_MASK;
		run_state_mask = AIC3XXX_COPS_MDSP_A_L;
	}
	if (w->shift == 6) {
		reg_mask = AIC3262_RADC_POWER_MASK;
		run_state_mask = AIC3XXX_COPS_MDSP_A_R;
	}
	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		ret_wbits = aic3xxx_wait_bits(control_data,
					      AIC3262_ADC_FLAG, reg_mask,
					      reg_mask, AIC326X_TIME_DELAY,
					      AIC326X_DELAY_COUNTER);
		sync_needed =  aic3xxx_reg_read(control_data,
							AIC3262_DAC_PRB);
		non_sync_state = dsp_non_sync_mode(aic3262->dsp_runstate);
		other_dsp = aic3262->dsp_runstate & AIC3XXX_COPS_MDSP_D;
		if (sync_needed && non_sync_state && other_dsp) {
			run_state = aic3262_get_runstate(
						aic3262->codec);
			aic3262_dsp_pwrdwn_status(aic3262->codec);
			aic3262_dsp_pwrup(aic3262->codec, run_state);
		}
		aic3262->dsp_runstate |= run_state_mask;
		if (!ret_wbits) {
			dev_err(codec->dev, "ADC POST_PMU timedout\n");
			return -1;
		}
		break;
	case SND_SOC_DAPM_POST_PMD:
		ret_wbits = aic3xxx_wait_bits(control_data,
					      AIC3262_ADC_FLAG, reg_mask, 0,
					      AIC326X_TIME_DELAY,
					      AIC326X_DELAY_COUNTER);
		aic3262->dsp_runstate = (aic3262->dsp_runstate &
					 ~run_state_mask);
		if (!ret_wbits) {
			dev_err(codec->dev, "ADC POST_PMD timedout\n");
			return -1;
		}
		break;
	default:
		BUG();
		return -EINVAL;
	}
	return 0;
}

static const struct snd_soc_dapm_widget aic3262_dapm_widgets[] = {
	/* TODO: Can we switch these off ? */
	SND_SOC_DAPM_AIF_IN("DIN1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_AIF_IN("DIN2", "ASI2 Playback", 0, SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_AIF_IN("DIN3", "ASI3 Playback", 0, SND_SOC_NOPM, 0, 0),

	SND_SOC_DAPM_DAC_E("Left DAC", NULL, AIC3262_PASI_DAC_DP_SETUP, 7, 0,
			   aic326x_dac_event, SND_SOC_DAPM_POST_PMU |
			   SND_SOC_DAPM_POST_PMD),
	SND_SOC_DAPM_DAC_E("Right DAC", NULL, AIC3262_PASI_DAC_DP_SETUP, 6, 0,
			   aic326x_dac_event, SND_SOC_DAPM_POST_PMU |
			   SND_SOC_DAPM_POST_PMD),

	/* dapm widget (path domain) for HPL Output Mixer */
	SND_SOC_DAPM_MIXER("HP Left Mixer", SND_SOC_NOPM, 0, 0,
			   &hpl_output_mixer_controls[0],
			   ARRAY_SIZE(hpl_output_mixer_controls)),

	/* dapm widget (path domain) for HPR Output Mixer */
	SND_SOC_DAPM_MIXER("HP Right Mixer", SND_SOC_NOPM, 0, 0,
			   &hpr_output_mixer_controls[0],
			   ARRAY_SIZE(hpr_output_mixer_controls)),

	SND_SOC_DAPM_PGA_S("HP Left Playback Driver", 3,
			    AIC3262_HP_AMP_CNTL_R1, 1, 0, aic326x_hp_event,
			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
	SND_SOC_DAPM_PGA_S("HP Right Playback Driver", 3,
			   AIC3262_HP_AMP_CNTL_R1, 0, 0, aic326x_hp_event,
			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),

	/* dapm widget (path domain) for LOL Output Mixer */
	SND_SOC_DAPM_MIXER("LO Left Mixer", SND_SOC_NOPM, 0, 0,
			   &lol_output_mixer_controls[0],
			   ARRAY_SIZE(lol_output_mixer_controls)),

	/* dapm widget (path domain) for LOR Output Mixer mixer */
	SND_SOC_DAPM_MIXER("LO Right Mixer", SND_SOC_NOPM, 0, 0,
			   &lor_output_mixer_controls[0],
			   ARRAY_SIZE(lor_output_mixer_controls)),

	SND_SOC_DAPM_PGA_S("LO Left Playback Driver", 2,
			  AIC3262_LINE_AMP_CNTL_R1, 1, 0, NULL, 0),
	SND_SOC_DAPM_PGA_S("LO Right Playback Driver", 2,
			  AIC3262_LINE_AMP_CNTL_R1, 0, 0, NULL, 0),

	/* dapm widget (path domain) for SPKL Output Mixer */
	SND_SOC_DAPM_MIXER("SPK Left Mixer", SND_SOC_NOPM, 0, 0,
			   &spkl_output_mixer_controls[0],
			   ARRAY_SIZE(spkl_output_mixer_controls)),

	/* dapm widget (path domain) for SPKR Output Mixer */
	SND_SOC_DAPM_MIXER("SPK Right Mixer", SND_SOC_NOPM, 0, 0,
			   &spkr_output_mixer_controls[0],
			   ARRAY_SIZE(spkr_output_mixer_controls)),

	SND_SOC_DAPM_PGA_S("SPK Left Playback Driver", 3,
			   AIC3262_SPK_AMP_CNTL_R1, 1, 0, aic326x_spk_event,
			   SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
	SND_SOC_DAPM_PGA_S("SPK Right Playback Driver", 3,
			   AIC3262_SPK_AMP_CNTL_R1, 0, 0, aic326x_spk_event,
			   SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),

	/* dapm widget (path domain) for SPKR Output Mixer */
	SND_SOC_DAPM_MIXER("REC Mixer", SND_SOC_NOPM, 0, 0,
			   &rec_output_mixer_controls[0],
			   ARRAY_SIZE(rec_output_mixer_controls)),

	SND_SOC_DAPM_PGA_S("RECP Playback Driver", 3, AIC3262_REC_AMP_CNTL_R5,
			 7, 0, NULL, 0),
	SND_SOC_DAPM_PGA_S("RECM Playback Driver", 3, AIC3262_REC_AMP_CNTL_R5,
			 6, 0, NULL, 0),

	SND_SOC_DAPM_MUX("ASI1LIN Route",
			 SND_SOC_NOPM, 0, 0, &asi1lin_control),
	SND_SOC_DAPM_MUX("ASI1RIN Route",
			 SND_SOC_NOPM, 0, 0, &asi1rin_control),
	SND_SOC_DAPM_MUX("ASI2LIN Route",
			 SND_SOC_NOPM, 0, 0, &asi2lin_control),
	SND_SOC_DAPM_MUX("ASI2RIN Route",
			 SND_SOC_NOPM, 0, 0, &asi2rin_control),
	SND_SOC_DAPM_MUX("ASI3LIN Route",
			 SND_SOC_NOPM, 0, 0, &asi3lin_control),
	SND_SOC_DAPM_MUX("ASI3RIN Route",
			 SND_SOC_NOPM, 0, 0, &asi3rin_control),

	SND_SOC_DAPM_PGA("ASI1LIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI1RIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI2LIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI2RIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI3LIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI3RIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI1MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI2MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI3MonoMixIN", SND_SOC_NOPM, 0, 0, NULL, 0),
	/* TODO: Can we switch the ASIxIN off? */
	SND_SOC_DAPM_PGA("ASI1IN Port", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI2IN Port", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI3IN Port", SND_SOC_NOPM, 0, 0, NULL, 0),

	SND_SOC_DAPM_MUX("DAC MiniDSP IN1 Route",
			 SND_SOC_NOPM, 0, 0, &dacminidspin1_control),
	SND_SOC_DAPM_MUX("DAC MiniDSP IN2 Route",
			 SND_SOC_NOPM, 0, 0, &dacminidspin2_control),
	SND_SOC_DAPM_MUX("DAC MiniDSP IN3 Route",
			 SND_SOC_NOPM, 0, 0, &dacminidspin3_control),

	SND_SOC_DAPM_MUX("ADC DAC Route",
			      SND_SOC_NOPM, 0, 0, &adcdacroute_control),

	SND_SOC_DAPM_PGA("CM", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("CM1 Left Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("CM2 Left Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("CM1 Right Capture", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("CM2 Right Capture", SND_SOC_NOPM, 0, 0, NULL, 0),

	/* TODO: Can we switch these off ? */
	SND_SOC_DAPM_AIF_OUT("DOUT1", "ASI1 Capture", 0, SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_AIF_OUT("DOUT2", "ASI2 Capture", 0, SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_AIF_OUT("DOUT3", "ASI3 Capture", 0, SND_SOC_NOPM, 0, 0),

	SND_SOC_DAPM_MUX("DOUT1 Route",
			 SND_SOC_NOPM, 0, 0, &dout1_control),
	SND_SOC_DAPM_MUX("DOUT2 Route",
			 SND_SOC_NOPM, 0, 0, &dout2_control),
	SND_SOC_DAPM_MUX("DOUT3 Route",
			 SND_SOC_NOPM, 0, 0, &dout3_control),

	SND_SOC_DAPM_PGA("ASI1OUT", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI2OUT", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ASI3OUT", SND_SOC_NOPM, 0, 0, NULL, 0),

	SND_SOC_DAPM_MUX("ASI1OUT Route",
			 SND_SOC_NOPM, 0, 0, &asi1out_control),
	SND_SOC_DAPM_MUX("ASI2OUT Route",
			 SND_SOC_NOPM, 0, 0, &asi2out_control),
	SND_SOC_DAPM_MUX("ASI3OUT Route",
			 SND_SOC_NOPM, 0, 0, &asi3out_control),

	/* TODO: Can we switch the ASI1 OUT1 off? */
	/* TODO: Can we switch them off? */
	SND_SOC_DAPM_PGA("ADC MiniDSP OUT1", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ADC MiniDSP OUT2", SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA("ADC MiniDSP OUT3", SND_SOC_NOPM, 0, 0, NULL, 0),

	SND_SOC_DAPM_MUX("Left ADC Route", SND_SOC_NOPM, 0, 0, &adcl_mux),
	SND_SOC_DAPM_MUX("Right ADC Route", SND_SOC_NOPM, 0, 0, &adcr_mux),

	SND_SOC_DAPM_ADC_E("Left ADC", NULL, AIC3262_ADC_CHANNEL_POW, 7, 0,
			   aic326x_adc_dsp_event, SND_SOC_DAPM_POST_PMU |
			   SND_SOC_DAPM_POST_PMD),
	SND_SOC_DAPM_ADC_E("Right ADC", NULL, AIC3262_ADC_CHANNEL_POW, 6, 0,
			   aic326x_adc_dsp_event, SND_SOC_DAPM_POST_PMU |
			   SND_SOC_DAPM_POST_PMD),

	SND_SOC_DAPM_PGA_S("Left MicPGA", 0, AIC3262_MICL_PGA, 7, 1, NULL, 0),
	SND_SOC_DAPM_PGA_S("Right MicPGA", 0, AIC3262_MICR_PGA, 7, 1, NULL, 0),

	SND_SOC_DAPM_PGA_S("MA Left Playback PGA", 1, AIC3262_MA_CNTL,
			 3, 0, NULL, 0),
	SND_SOC_DAPM_PGA_S("MA Right Playback PGA", 1, AIC3262_MA_CNTL,
			 2, 0, NULL, 0),

	/* dapm widget for MAL PGA Mixer */
	SND_SOC_DAPM_MIXER("MA Left PGA Mixer", SND_SOC_NOPM, 0, 0,
			   &mal_pga_mixer_controls[0],
			   ARRAY_SIZE(mal_pga_mixer_controls)),

	/* dapm widget for MAR PGA Mixer */
	SND_SOC_DAPM_MIXER("MA Right PGA Mixer", SND_SOC_NOPM, 0, 0,
			   &mar_pga_mixer_controls[0],
			   ARRAY_SIZE(mar_pga_mixer_controls)),

	/* dapm widget for Left Input Mixer */
	SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
			   &left_input_mixer_controls[0],
			   ARRAY_SIZE(left_input_mixer_controls)),

	/* dapm widget for Right Input Mixer */
	SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
			   &right_input_mixer_controls[0],
			   ARRAY_SIZE(right_input_mixer_controls)),

	SND_SOC_DAPM_OUTPUT("HP Left Playback"),
	SND_SOC_DAPM_OUTPUT("HP Right Playback"),
	SND_SOC_DAPM_OUTPUT("LO Left Playback"),
	SND_SOC_DAPM_OUTPUT("LO Right Playback"),
	SND_SOC_DAPM_OUTPUT("SPK Left Playback"),
	SND_SOC_DAPM_OUTPUT("SPK Right Playback"),
	SND_SOC_DAPM_OUTPUT("RECP Playback"),
	SND_SOC_DAPM_OUTPUT("RECM Playback"),

	SND_SOC_DAPM_INPUT("IN1 Left Capture"),
	SND_SOC_DAPM_INPUT("IN2 Left Capture"),
	SND_SOC_DAPM_INPUT("IN3 Left Capture"),
	SND_SOC_DAPM_INPUT("IN4 Left Capture"),
	SND_SOC_DAPM_INPUT("IN1 Right Capture"),
	SND_SOC_DAPM_INPUT("IN2 Right Capture"),
	SND_SOC_DAPM_INPUT("IN3 Right Capture"),
	SND_SOC_DAPM_INPUT("IN4 Right Capture"),
	SND_SOC_DAPM_INPUT("Left DMIC Capture"),
	SND_SOC_DAPM_INPUT("Right DMIC Capture"),

	SND_SOC_DAPM_MICBIAS("Mic Bias Ext", AIC3262_MIC_BIAS_CNTL, 6, 0),
	SND_SOC_DAPM_MICBIAS("Mic Bias Int", AIC3262_MIC_BIAS_CNTL, 2, 0),

	SND_SOC_DAPM_PGA_S("PLLCLK", 0, AIC3262_PLL_PR_POW_REG, 7, 0,
			    pll_power_on_event, SND_SOC_DAPM_POST_PMU),
	SND_SOC_DAPM_PGA_S("DACCLK", 2, AIC3262_NDAC_DIV_POW_REG, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("CODEC_CLK_IN", 1, SND_SOC_NOPM, 0, 0, NULL, 0),
	SND_SOC_DAPM_PGA_S("DAC_MOD_CLK", 3, AIC3262_MDAC_DIV_POW_REG,
			    7, 0, NULL, 0),
	SND_SOC_DAPM_PGA_S("ADCCLK", 2, AIC3262_NADC_DIV_POW_REG, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("ADC_MOD_CLK", 3, AIC3262_MADC_DIV_POW_REG,
			    7, 0, NULL, 0),
	SND_SOC_DAPM_PGA_S("ASI1_BCLK", 4, AIC3262_ASI1_BCLK_N, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("ASI1_WCLK", 4, AIC3262_ASI1_WCLK_N, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("ASI2_BCLK", 4, AIC3262_ASI2_BCLK_N, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("ASI2_WCLK", 4, AIC3262_ASI2_WCLK_N, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("ASI3_BCLK", 4, AIC3262_ASI3_BCLK_N, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_PGA_S("ASI3_WCLK", 4, AIC3262_ASI3_WCLK_N, 7, 0,
				NULL, 0),
	SND_SOC_DAPM_MUX("ASI1_BCLK Route",
			 SND_SOC_NOPM, 0, 0, &asi1bclk_control),
	SND_SOC_DAPM_MUX("ASI2_BCLK Route",
			 SND_SOC_NOPM, 0, 0, &asi2bclk_control),
	SND_SOC_DAPM_MUX("ASI3_BCLK Route",
			 SND_SOC_NOPM, 0, 0, &asi3bclk_control),
};

static const struct snd_soc_dapm_route aic3262_dapm_routes[] = {
	/* TODO: Do we need only DACCLK for ASIIN's and ADCCLK for ASIOUT??? */
	/* Clock portion */
	{"CODEC_CLK_IN", NULL, "PLLCLK"},
	{"DACCLK", NULL, "CODEC_CLK_IN"},
	{"ADCCLK", NULL, "CODEC_CLK_IN"},
	{"DAC_MOD_CLK", NULL, "DACCLK"},
#ifdef AIC3262_SYNC_MODE
	{"ADC_MOD_CLK", NULL, "DACCLK"},
#else
	{"ADC_MOD_CLK", NULL, "ADCCLK"},
#endif

	{"ASI1_BCLK Route", "DAC_CLK", "DACCLK"},
	{"ASI1_BCLK Route", "DAC_MOD_CLK", "DAC_MOD_CLK"},
	{"ASI1_BCLK Route", "ADC_CLK", "ADCCLK"},
	{"ASI1_BCLK Route", "ADC_MOD_CLK", "ADC_MOD_CLK"},

	{"ASI2_BCLK Route", "DAC_CLK", "DACCLK"},
	{"ASI2_BCLK Route", "DAC_MOD_CLK", "DAC_MOD_CLK"},
	{"ASI2_BCLK Route", "ADC_CLK", "ADCCLK"},
	{"ASI2_BCLK Route", "ADC_MOD_CLK", "ADC_MOD_CLK"},

	{"ASI3_BCLK Route", "DAC_CLK", "DACCLK"},
	{"ASI3_BCLK Route", "DAC_MOD_CLK", "DAC_MOD_CLK"},
	{"ASI3_BCLK Route", "ADC_CLK", "ADCCLK"},
	{"ASI3_BCLK Route", "ADC_MOD_CLK", "ADC_MOD_CLK"},

	{"ASI1_BCLK", NULL, "ASI1_BCLK Route"},
	{"ASI2_BCLK", NULL, "ASI2_BCLK Route"},
	{"ASI3_BCLK", NULL, "ASI3_BCLK Route"},
#ifdef AIC3262_ASI1_MASTER
	{"DIN1", NULL, "ASI1_BCLK"},
	{"DOUT1", NULL, "ASI1_BCLK"},
	{"DIN1", NULL, "ASI1_WCLK"},
	{"DOUT1", NULL, "ASI1_WCLK"},
#endif
#ifdef AIC3262_ASI2_MASTER
	{"DIN2", NULL, "ASI2_BCLK"},
	{"DOUT2", NULL, "ASI2_BCLK"},
	{"DIN2", NULL, "ASI2_WCLK"},
	{"DOUT2", NULL, "ASI2_WCLK"},
#endif
#ifdef AIC3262_ASI3_MASTER
	{"DIN3", NULL, "ASI3_BCLK"},
	{"DOUT3", NULL, "ASI3_BCLK"},
	{"DIN3", NULL, "ASI3_WCLK"},
	{"DOUT3", NULL, "ASI3_WCLK"},
#endif
	{"Left DAC", NULL, "DAC_MOD_CLK"},
	{"Right DAC", NULL, "DAC_MOD_CLK"},
	/* When we are master, ASI bclk and wclk are generated by
	 * DAC_MOD_CLK, so we put them as dependency for ADC too.
	 */
	{"Left ADC", NULL, "DAC_MOD_CLK"},
	{"Right ADC", NULL, "DAC_MOD_CLK"},
	{"Left ADC", NULL, "ADC_MOD_CLK"},
	{"Right ADC", NULL, "ADC_MOD_CLK"},
	/* Playback (DAC) Portion */
	{"HP Left Mixer", "Left DAC Playback Switch", "Left DAC"},
	{"HP Left Mixer", "MA Left Playback Switch", "MA Left Playback PGA"},
	{"HP Left Mixer", "LO Left-B1 Playback Volume", "LO Left Playback"},

	{"HP Right Mixer", "LO Right-B1 Playback Volume", "LO Right Playback"},
	{"HP Right Mixer", "Left DAC Playback Switch", "Left DAC"},
	{"HP Right Mixer", "Right DAC Playback Switch", "Right DAC"},
	{"HP Right Mixer", "MA Right Playback Switch", "MA Right Playback PGA"},

	{"HP Left Playback Driver", NULL, "HP Left Mixer"},
	{"HP Right Playback Driver", NULL, "HP Right Mixer"},

	{"HP Left Playback", NULL, "HP Left Playback Driver"},
	{"HP Right Playback", NULL, "HP Right Playback Driver"},

	{"LO Left Mixer", "MA Left Playback Switch", "MA Left Playback PGA"},
	{"LO Left Mixer", "IN1 Left-B Capture Switch", "IN1 Left Capture"},
	{"LO Left Mixer", "Left DAC Playback Switch", "Left DAC"},
	{"LO Left Mixer", "Right DAC Playback Switch", "Right DAC"},

	{"LO Right Mixer", "LO Left Playback Switch", "LO Left Playback"},
	{"LO Right Mixer", "Right DAC Playback Switch", "Right DAC"},
	{"LO Right Mixer", "MA Right Playback Switch", "MA Right Playback PGA"},
	{"LO Right Mixer", "IN1 Right-B Capture Switch", "IN1 Right Capture"},

	{"LO Left Playback Driver", NULL, "LO Left Mixer"},
	{"LO Right Playback Driver", NULL, "LO Right Mixer"},

	{"LO Left Playback", NULL, "LO Left Playback Driver"},
	{"LO Right Playback", NULL, "LO Right Playback Driver"},

	{"REC Mixer", "LO Left-B2 Playback Volume", "LO Left Playback"},
	{"REC Mixer", "IN1 Left Capture Volume", "IN1 Left Capture"},
	{"REC Mixer", "IN1 Right Capture Volume", "IN1 Right Capture"},
	{"REC Mixer", "LO Right-B2 Playback Volume", "LO Right Playback"},

	{"RECP Playback Driver", NULL, "REC Mixer"},
	{"RECM Playback Driver", NULL, "REC Mixer"},

	{"RECP Playback", NULL, "RECP Playback Driver"},
	{"RECM Playback", NULL, "RECM Playback Driver"},

	{"SPK Left Mixer", "MA Left Playback Switch", "MA Left Playback PGA"},
	{"SPK Left Mixer", "LO Left Playback Volume", "LO Left Playback"},
	{"SPK Left Mixer", "SPR_IN Switch", "SPK Right Mixer"},

	{"SPK Right Mixer", "LO Right Playback Volume", "LO Right Playback"},
	{"SPK Right Mixer", "MA Right Playback Switch",
	 "MA Right Playback PGA"},

	{"SPK Left Playback Driver", NULL, "SPK Left Mixer"},
	{"SPK Right Playback Driver", NULL, "SPK Right Mixer"},

	{"SPK Left Playback", NULL, "SPK Left Playback Driver"},
	{"SPK Right Playback", NULL, "SPK Right Playback Driver"},
	/* ASI Input routing */
	{"ASI1LIN", NULL, "DIN1"},
	{"ASI1RIN", NULL, "DIN1"},
	{"ASI1MonoMixIN", NULL, "DIN1"},
	{"ASI2LIN", NULL, "DIN2"},
	{"ASI2RIN", NULL, "DIN2"},
	{"ASI2MonoMixIN", NULL, "DIN2"},
	{"ASI3LIN", NULL, "DIN3"},
	{"ASI3RIN", NULL, "DIN3"},
	{"ASI3MonoMixIN", NULL, "DIN3"},

	{"ASI1LIN Route", "ASI1 Left In", "ASI1LIN"},
	{"ASI1LIN Route", "ASI1 Right In", "ASI1RIN"},
	{"ASI1LIN Route", "ASI1 MonoMix In", "ASI1MonoMixIN"},

	{"ASI1RIN Route", "ASI1 Right In", "ASI1RIN"},
	{"ASI1RIN Route", "ASI1 Left In", "ASI1LIN"},
	{"ASI1RIN Route", "ASI1 MonoMix In", "ASI1MonoMixIN"},

	{"ASI2LIN Route", "ASI2 Left In", "ASI2LIN"},
	{"ASI2LIN Route", "ASI2 Right In", "ASI2RIN"},
	{"ASI2LIN Route", "ASI2 MonoMix In", "ASI2MonoMixIN"},

	{"ASI2RIN Route", "ASI2 Right In", "ASI2RIN"},
	{"ASI2RIN Route", "ASI2 Left In", "ASI2LIN"},
	{"ASI2RIN Route", "ASI2 MonoMix In", "ASI2MonoMixIN"},

	{"ASI3LIN Route", "ASI3 Left In", "ASI3LIN"},
	{"ASI3LIN Route", "ASI3 Right In", "ASI3RIN"},
	{"ASI3LIN Route", "ASI3 MonoMix In", "ASI3MonoMixIN"},

	{"ASI3RIN Route", "ASI3 Right In", "ASI3RIN"},
	{"ASI3RIN Route", "ASI3 Left In", "ASI3LIN"},
	{"ASI3RIN Route", "ASI3 MonoMix In", "ASI3MonoMixIN"},

	{"ASI1IN Port", NULL, "ASI1LIN Route"},
	{"ASI1IN Port", NULL, "ASI1RIN Route"},
	{"ASI2IN Port", NULL, "ASI2LIN Route"},
	{"ASI2IN Port", NULL, "ASI2RIN Route"},
	{"ASI3IN Port", NULL, "ASI3LIN Route"},
	{"ASI3IN Port", NULL, "ASI3RIN Route"},

	{"DAC MiniDSP IN1 Route", "ASI1 In", "ASI1IN Port"},
	{"DAC MiniDSP IN1 Route", "ASI2 In", "ASI2IN Port"},
	{"DAC MiniDSP IN1 Route", "ASI3 In", "ASI3IN Port"},
	{"DAC MiniDSP IN1 Route", "ADC MiniDSP Out", "ADC MiniDSP OUT1"},

	{"DAC MiniDSP IN2 Route", "ASI1 In", "ASI1IN Port"},
	{"DAC MiniDSP IN2 Route", "ASI2 In", "ASI2IN Port"},
	{"DAC MiniDSP IN2 Route", "ASI3 In", "ASI3IN Port"},

	{"DAC MiniDSP IN3 Route", "ASI1 In", "ASI1IN Port"},
	{"DAC MiniDSP IN3 Route", "ASI2 In", "ASI2IN Port"},
	{"DAC MiniDSP IN3 Route", "ASI3 In", "ASI3IN Port"},

	/*{"Left DAC", "NULL", "DAC MiniDSP IN1 Route"},
	{"Right DAC", "NULL", "DAC MiniDSP IN1 Route"},
	{"Left DAC", "NULL", "DAC MiniDSP IN2 Route"},
	{"Right DAC", "NULL", "DAC MiniDSP IN2 Route"},
	{"Left DAC", "NULL", "DAC MiniDSP IN3 Route"},
	{"Right DAC", "NULL", "DAC MiniDSP IN3 Route"},*/

	/* Mixer Amplifier */
	{"MA Left PGA Mixer", "IN1 Left Capture Switch", "IN1 Left Capture"},
	{"MA Left PGA Mixer", "Left MicPGA Volume", "Left MicPGA"},

	{"MA Left Playback PGA", NULL, "MA Left PGA Mixer"},

	{"MA Right PGA Mixer", "IN1 Right Capture Switch", "IN1 Right Capture"},
	{"MA Right PGA Mixer", "Right MicPGA Volume", "Right MicPGA"},

	{"MA Right Playback PGA", NULL, "MA Right PGA Mixer"},

	/* Virtual connection between DAC and ADC for miniDSP IPC */
	{"ADC DAC Route", "On", "Left ADC"},
	{"ADC DAC Route", "On", "Right ADC"},

	{"Left DAC", NULL, "ADC DAC Route"},
	{"Right DAC", NULL, "ADC DAC Route"},

	/* Capture (ADC) portions */
	/* Left Positive PGA input */
	{"Left Input Mixer", "IN1 Left Capture Switch", "IN1 Left Capture"},
	{"Left Input Mixer", "IN2 Left Capture Switch", "IN2 Left Capture"},
	{"Left Input Mixer", "IN3 Left Capture Switch", "IN3 Left Capture"},
	{"Left Input Mixer", "IN4 Left Capture Switch", "IN4 Left Capture"},
	{"Left Input Mixer", "IN1 Right Capture Switch", "IN1 Right Capture"},
	/* Left Negative PGA input */
	{"Left Input Mixer", "IN2 Right Capture Switch", "IN2 Right Capture"},
	{"Left Input Mixer", "IN3 Right Capture Switch", "IN3 Right Capture"},
	{"Left Input Mixer", "IN4 Right Capture Switch", "IN4 Right Capture"},
	{"Left Input Mixer", "CM2 Left Capture Switch", "CM2 Left Capture"},
	{"Left Input Mixer", "CM1 Left Capture Switch", "CM1 Left Capture"},

	/* Right Positive PGA Input */
	{"Right Input Mixer", "IN1 Right Capture Switch", "IN1 Right Capture"},
	{"Right Input Mixer", "IN2 Right Capture Switch", "IN2 Right Capture"},
	{"Right Input Mixer", "IN3 Right Capture Switch", "IN3 Right Capture"},
	{"Right Input Mixer", "IN4 Right Capture Switch", "IN4 Right Capture"},
	{"Right Input Mixer", "IN2 Left Capture Switch", "IN2 Left Capture"},
	/* Right Negative PGA Input */
	{"Right Input Mixer", "IN1 Left Capture Switch", "IN1 Left Capture"},
	{"Right Input Mixer", "IN3 Left Capture Switch", "IN3 Left Capture"},
	{"Right Input Mixer", "IN4 Left Capture Switch", "IN4 Left Capture"},
	{"Right Input Mixer", "CM1 Right Capture Switch", "CM1 Right Capture"},
	{"Right Input Mixer", "CM2 Right Capture Switch", "CM2 Right Capture"},

	{"CM1 Left Capture", NULL, "CM"},
	{"CM2 Left Capture", NULL, "CM"},
	{"CM1 Right Capture", NULL, "CM"},
	{"CM2 Right Capture", NULL, "CM"},

	{"Left MicPGA", NULL, "Left Input Mixer"},
	{"Right MicPGA", NULL, "Right Input Mixer"},

	{"Left ADC Route", "Analog", "Left MicPGA"},
	{"Left ADC Route", "Digital", "Left DMIC Capture"},

	{"Right ADC Route", "Analog", "Right MicPGA"},
	{"Right ADC Route", "Digital", "Right DMIC Capture"},

	{"Left ADC", NULL, "Left ADC Route"},
	{"Right ADC", NULL, "Right ADC Route"},

	/* ASI Output Routing */
	{"ADC MiniDSP OUT1", NULL, "Left ADC"},
	{"ADC MiniDSP OUT1", NULL, "Right ADC"},
	{"ADC MiniDSP OUT2", NULL, "Left ADC"},
	{"ADC MiniDSP OUT2", NULL, "Right ADC"},
	{"ADC MiniDSP OUT3", NULL, "Left ADC"},
	{"ADC MiniDSP OUT3", NULL, "Right ADC"},

	{"ASI1OUT Route", "ADC MiniDSP Out1", "ADC MiniDSP OUT1"},
	{"ASI1OUT Route", "ASI1In Bypass", "ASI1IN Port"},
	{"ASI1OUT Route", "ASI2In Bypass", "ASI2IN Port"},
	{"ASI1OUT Route", "ASI3In Bypass", "ASI3IN Port"},

	{"ASI2OUT Route", "ADC MiniDSP Out1", "ADC MiniDSP OUT1"},
	{"ASI2OUT Route", "ASI1In Bypass", "ASI1IN Port"},
	{"ASI2OUT Route", "ASI2In Bypass", "ASI2IN Port"},
	{"ASI2OUT Route", "ASI3In Bypass", "ASI3IN Port"},
	{"ASI2OUT Route", "ADC MiniDSP Out2", "ADC MiniDSP OUT2"},

	{"ASI3OUT Route", "ADC MiniDSP Out1", "ADC MiniDSP OUT1"},
	{"ASI3OUT Route", "ASI1In Bypass", "ASI1IN Port"},
	{"ASI3OUT Route", "ASI2In Bypass", "ASI2IN Port"},
	{"ASI3OUT Route", "ASI3In Bypass", "ASI3IN Port"},
	{"ASI3OUT Route", "ADC MiniDSP Out3", "ADC MiniDSP OUT3"},

	{"ASI1OUT", NULL, "ASI1OUT Route"},
	{"ASI2OUT", NULL, "ASI2OUT Route"},
	{"ASI3OUT", NULL, "ASI3OUT Route"},

	{"DOUT1 Route", "ASI1 Out", "ASI1OUT"},
	{"DOUT1 Route", "DIN1 Bypass", "DIN1"},
	{"DOUT1 Route", "DIN2 Bypass", "DIN2"},
	{"DOUT1 Route", "DIN3 Bypass", "DIN3"},

	{"DOUT2 Route", "ASI2 Out", "ASI2OUT"},
	{"DOUT2 Route", "DIN1 Bypass", "DIN1"},
	{"DOUT2 Route", "DIN2 Bypass", "DIN2"},
	{"DOUT2 Route", "DIN3 Bypass", "DIN3"},

	{"DOUT3 Route", "ASI3 Out", "ASI3OUT"},
	{"DOUT3 Route", "DIN1 Bypass", "DIN1"},
	{"DOUT3 Route", "DIN2 Bypass", "DIN2"},
	{"DOUT3 Route", "DIN3 Bypass", "DIN3"},

	{"DOUT1", NULL, "DOUT1 Route"},
	{"DOUT2", NULL, "DOUT2 Route"},
	{"DOUT3", NULL, "DOUT3 Route"},
};

#define AIC3262_DAPM_ROUTE_NUM (ARRAY_SIZE(aic3262_dapm_routes)/ \
					sizeof(struct snd_soc_dapm_route))
/* aic3262_firmware_load:   This function is called by the
 *		request_firmware_nowait function as soon
 *		as the firmware has been loaded from the file.
 *		The firmware structure contains the data and$
 *		the size of the firmware loaded.
 * @fw: pointer to firmware file to be dowloaded
 * @context: pointer variable to codec
 *
 * Returns 0 for success.
 */
static void aic3262_firmware_load(const struct firmware *fw,
	void *context)
{
	struct snd_soc_component *codec = context;
	struct aic3xxx *control_data =
		snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *private_ds = control_data->control_data;

	int ret = 0;

	aic3xxx_cfw_lock(private_ds->cfw_p, 1);
	if (private_ds->cur_fw != NULL) {
		release_firmware(private_ds->cur_fw);
		private_ds->cur_fw = NULL;
	}
	if(private_ds->cfw_ps.pjt) {
		remove_cfw(private_ds->cfw_ps.pjt);
		private_ds->cfw_ps.pjt = NULL;
	}

	if (fw != NULL) {
		dev_info(codec->dev, "Firmware binary load\n");
		private_ds->cur_fw = (void *)fw;
		ret = aic3xxx_cfw_reload(private_ds->cfw_p, (void *)fw->data,
			fw->size);
		if (ret < 0) { /* reload failed */
			dev_err(codec->dev, "Firmware binary load failed\n");
			release_firmware(private_ds->cur_fw);
			private_ds->cur_fw = NULL;
			fw = NULL;
		}
	} else {
		/* request_firmware failed*/
		/* could not locate file tlv320aic3262_fw_v1.bin
			under /vendor/firmare
		*/
		dev_err(codec->dev, "request_firmware failed\n");
		ret = -1;
	}

	aic3xxx_cfw_lock(private_ds->cfw_p, 0);
	if (ret >= 0) {
		/*init function for transition */
		aic3xxx_cfw_transition(private_ds->cfw_p, "INIT");
		/* add firmware modes */
		aic3xxx_cfw_add_modes(codec, private_ds->cfw_p);
		/* add runtime controls */
		aic3xxx_cfw_add_controls(codec, private_ds->cfw_p);
		/* set the default firmware mode */
		aic3xxx_cfw_setmode_cfg(private_ds->cfw_p, 0, 0);
	}

}

/*=========================================================

 headset work and headphone/headset jack interrupt handlers

 ========================================================*/

enum headset_accessory_state {
	BIT_NO_ACCESSORY = 0,
	BIT_HEADSET = (1 << 0),
	BIT_HEADPHONE = (1 << 1),
};

/**
 * aic3262_hs_jack_report: Report jack notication to upper layor
 * @codec: pointer variable to codec having information related to codec
 * @jack: Pointer variable to snd_soc_jack having information of codec
 *		 and pin number$
 * @report: Provides informaton of whether it is headphone or microphone
 *
*/
static void aic3262_hs_jack_report(struct snd_soc_component *codec,
				   struct snd_soc_jack *jack, int report)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control_data->control_data;
	int status, state = 0, switch_state = BIT_NO_ACCESSORY;

	mutex_lock(&aic3262->mutex);

	/* Sync status */
	status = snd_soc_component_read(codec, AIC3262_DAC_FLAG);
	/* We will check only stereo MIC and headphone */
	switch (status & AIC3262_JACK_TYPE_MASK) {
	case AIC3262_JACK_WITH_MIC:
		state |= SND_JACK_HEADSET;
		break;
	case AIC3262_JACK_WITHOUT_MIC:
		state |= SND_JACK_HEADPHONE;
	}

	mutex_unlock(&aic3262->mutex);

	snd_soc_jack_report(jack, state, report);

	if ((state & SND_JACK_HEADSET) == SND_JACK_HEADSET)
		switch_state |= BIT_HEADSET;
	else if (state & SND_JACK_HEADPHONE)
		switch_state |= BIT_HEADPHONE;

}

/**
 * aic3262_hs_jack_detect: Detect headphone jack during boot time
 * @codec: pointer variable to codec having information related to codec
 * @jack: Pointer variable to snd_soc_jack having information of codec
 *	     and pin number$
 * @report: Provides informaton of whether it is headphone or microphone
 *
*/
void aic3262_hs_jack_detect(struct snd_soc_component *codec,
			    struct snd_soc_jack *jack, int report)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control_data->control_data;
	struct aic3262_jack_data *hs_jack = &aic3262->hs_jack;

	hs_jack->jack = jack;
	hs_jack->report = report;
	aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
}
EXPORT_SYMBOL_GPL(aic3262_hs_jack_detect);
/**
 * aic3262_accessory_work: Finished bottom half work from headphone jack
 *		insertion interupt
 * @work: pionter variable to work_struct which is maintaining work queqe
 *
*/
static void aic3262_accessory_work(struct work_struct *work)
{
	struct aic3262_priv *aic3262 = container_of(work,
						    struct aic3262_priv,
						    delayed_work.work);
	struct snd_soc_component *codec = aic3262->codec;
	struct aic3262_jack_data *hs_jack = &aic3262->hs_jack;
	aic3262_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
}

/**
 * aic3262_audio_handler: audio interrupt handler called
 *		when interupt is generated
 * @irq: provides interupt number which is assigned by aic3262_request_irq,
 * @data having information of data passed by aic3262_request_irq last arg,
 *
 * Return IRQ_HANDLED(means interupt handeled successfully)
*/
static irqreturn_t aic3262_audio_handler(int irq, void *data)
{
	struct snd_soc_component *codec = data;
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control_data->control_data;

	queue_delayed_work(aic3262->workqueue, &aic3262->delayed_work,
			   msecs_to_jiffies(200));
	return IRQ_HANDLED;
}

/**
 * Methods for CFW Operations
 *
 * Due to incompatibilites between structures used by MFD and CFW
 * we need to transform the register format before linking to
 * CFW operations.
 */
static inline unsigned int aic3262_ops_cfw2reg(unsigned int reg)
{
	union cfw_register *c = (union cfw_register *) ®
	union aic3xxx_reg_union mreg;

	mreg.aic3xxx_register.offset = c->offset;
	mreg.aic3xxx_register.page = c->page;
	mreg.aic3xxx_register.book = c->book;
	mreg.aic3xxx_register.reserved = 0;

	return mreg.aic3xxx_register_int;
}
static int aic3262_ops_reg_read(struct snd_soc_component *codec, unsigned int reg)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	return aic3xxx_reg_read(control_data, aic3262_ops_cfw2reg(reg));
}

static int aic3262_ops_reg_write(struct snd_soc_component *codec, unsigned int reg,
			  unsigned char val)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	return aic3xxx_reg_write(control_data,
					aic3262_ops_cfw2reg(reg), val);
}

static int aic3262_ops_i2c_reg_write(struct snd_soc_component *codec, unsigned char reg,
			  unsigned char val)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	return aic3xxx_i2c_reg_write(control_data,
					reg, val);
}

static int aic3262_ops_set_bits(struct snd_soc_component *codec, unsigned int reg,
				unsigned char mask, unsigned char val)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	return aic3xxx_set_bits(control_data,
					aic3262_ops_cfw2reg(reg), mask, val);

}

static int aic3262_ops_bulk_read(struct snd_soc_component *codec, unsigned int reg,
				 int count, u8 *buf)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	return aic3xxx_bulk_read(control_data,
					aic3262_ops_cfw2reg(reg), count, buf);
}

static int aic3262_ops_bulk_write(struct snd_soc_component *codec, unsigned int reg,
			   int count, const u8 *buf)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	return aic3xxx_bulk_write(control_data,
					aic3262_ops_cfw2reg(reg), count, buf);
}

/**
 * aic3262_ops_lock_lock: To Read the run state of the DAC and ADC
 *			by reading the codec and returning the run state
 * @pv: pointer argument to the codec
 *
 * Run state Bit format
 *
 * ------------------------------------------------------
 * D31|..........| D7 | D6|  D5  |  D4  | D3 | D2 | D1  |   D0  |
 * R               R    R   LADC   RADC    R    R   LDAC   RDAC
 * ------------------------------------------------------
 *
 * R- Reserved
 * LDAC- Left DAC
 * RDAC- Right DAC
 *
 * Return value  : Integer
 */
static int aic3262_ops_lock(struct snd_soc_component *codec)
{
	mutex_lock(&codec->io_mutex);
	/* Reading the run state of adc and dac */
	return aic3262_get_runstate(codec);

}

/**
 * aic3262_ops_dlock_unlock: To unlock the mutex acqiured for reading
 *			run state of the codec
 * @pv: pointer argument to the codec
 *
 * Return Value: integer returning 0
 */
static int aic3262_ops_unlock(struct snd_soc_component *codec)
{
	/*Releasing the lock of mutex */
	mutex_unlock(&codec->io_mutex);
	return 0;
}

/**
 * aic3262_ops_dlock_stop:
 * @pv: pointer Argument to the codec
 * @mask: tells us the bit format of the codec running state
 *
 * Bit Format:
 * ------------------------------------------------------
 * D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 |
 * R               R    R   AL   AR    R    R   DL   DR
 * ------------------------------------------------------
 * R  - Reserved
 * A  - minidsp_A
 * D  - minidsp_D
 *
 * Return: return run state
 */
static int aic3262_ops_stop(struct snd_soc_component *codec, int mask)
{
	int run_state = 0;
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);

	run_state = aic3262_get_runstate(codec);

	if (mask & AIC3XXX_COPS_MDSP_A)
		aic3xxx_set_bits(control_data,
				 AIC3262_ADC_DATAPATH_SETUP, 0xC0, 0);

	if (mask & AIC3XXX_COPS_MDSP_D)
		aic3xxx_set_bits(control_data,
				 AIC3262_DAC_DATAPATH_SETUP, 0xC0, 0);

	if ((mask & AIC3XXX_COPS_MDSP_A) &&
		!aic3xxx_wait_bits(control_data,
				      AIC3262_ADC_FLAG, AIC3262_ADC_POWER_MASK,
				      0, AIC326X_TIME_DELAY,
				      AIC326X_DELAY_COUNTER))
		goto err;

	if ((mask & AIC3XXX_COPS_MDSP_D) &&
		!aic3xxx_wait_bits(control_data,
				      AIC3262_DAC_FLAG, AIC3262_DAC_POWER_MASK,
				      0, AIC326X_TIME_DELAY,
				      AIC326X_DELAY_COUNTER))
		goto err;

	return run_state;
err:
	dev_err(codec->dev, "Unable to turn off ADCs or DACs at [%s:%d]",
				__FILE__, __LINE__);
	return -EINVAL;
}

/**
 * aic3262_ops_lock_restore: To unlock the mutex acqiured for reading
 * @pv: pointer argument to the codec,run_state
 * @run_state:  run state of the codec and to restore the states of the dsp
 *
 * Return Value	: integer returning 0
 */

static int aic3262_ops_restore(struct snd_soc_component *codec, int run_state)
{
	int sync_state;
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);

	/* This is for read the sync mode register state  */
	sync_state = aic3xxx_reg_read(control_data, AIC3262_DAC_PRB);

	/*checking whether the sync mode has been set or
	   not and checking the current state */
	if (((run_state & 0x30) && (run_state & 0x03))&& (sync_state & 0x80))
		aic3262_restart_dsps_sync(codec, run_state);
	else
		aic3262_dsp_pwrup(codec, run_state);

	return 0;
}

/**
 * aic3262_ops_adaptivebuffer_swap: To swap the coefficient buffers
 *				 of minidsp according to mask
 * @pv: pointer argument to the codec,
 * @mask: tells us which dsp has to be chosen for swapping
 *
 * Return Value    : returning 0 on success
 */
int aic3262_ops_adaptivebuffer_swap(struct snd_soc_component *codec, int mask)
{
	const int sbuf[][2] = {
		{ AIC3XXX_ABUF_MDSP_A, AIC3262_ADC_ADAPTIVE_CRAM_REG },
		{ AIC3XXX_ABUF_MDSP_D1, AIC3262_DAC_ADAPTIVE_BANK1_REG },
		{ AIC3XXX_ABUF_MDSP_D2, AIC3262_DAC_ADAPTIVE_BANK2_REG },
	};
	int i;
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);

	for (i = 0; i < sizeof(sbuf)/sizeof(sbuf[0]); ++i) {
		if ((mask & sbuf[i][0])) {
			aic3xxx_set_bits(control_data, sbuf[i][1],
					0x1, 0x1);
			if (!aic3xxx_wait_bits(control_data,
						sbuf[i][1], 0x1, 0, 5, 1)) {
			dev_err(codec->dev, "miniDSP buffer swap failure");
			return -EINVAL;
			}
		}
	}
		return 0;
}

/**
 * get_runstate: To read the current state of the dac's and adc's
 * @ps: pointer argument to the codec
 *
 * Return Value	: returning the runstate
 */
static int aic3262_get_runstate(struct snd_soc_component *codec)
{
	unsigned int dac, adc;
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	/* Read the run state */
	dac = aic3xxx_reg_read(control_data, AIC3262_DAC_FLAG);
	adc = aic3xxx_reg_read(control_data, AIC3262_ADC_FLAG);

	return (((adc>>6)&1)<<5)  |
		(((adc>>2)&1)<<4) |
		(((dac>>7)&1)<<1) |
		(((dac>>3)&1)<<0);
}

/**
 * aic3262_dsp_pwrdwn_status: To read the status of dsp's
 * @pv: pointer argument to the codec , cur_state of dac's and adc's
 *
 * Return Value	: integer returning 0
 */
static int aic3262_dsp_pwrdwn_status(struct snd_soc_component *codec)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);

	aic3xxx_set_bits(control_data,
			AIC3262_ADC_DATAPATH_SETUP, 0XC0, 0);
	aic3xxx_set_bits(control_data,
			AIC3262_DAC_DATAPATH_SETUP, 0XC0, 0);

	if (!aic3xxx_wait_bits(control_data, AIC3262_ADC_FLAG,
			      AIC3262_ADC_POWER_MASK, 0, AIC326X_TIME_DELAY,
			      AIC326X_DELAY_COUNTER))
		goto err;
	if (!aic3xxx_wait_bits(control_data, AIC3262_DAC_FLAG,
			      AIC3262_DAC_POWER_MASK, 0, AIC326X_TIME_DELAY,
			      AIC326X_DELAY_COUNTER))
		goto err;

	return 0;
err:
	dev_err(codec->dev, "DAC/ADC Power down timedout at [%s:%d]",
				__FILE__, __LINE__);
	return -EINVAL;
}
static int aic3262_dsp_pwrup(struct snd_soc_component *codec, int state)
{
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
	int adc_reg_mask = 0;
	int adc_power_mask = 0;
	int dac_reg_mask = 0;
	int dac_power_mask = 0;
	int ret_wbits;

	if (state & AIC3XXX_COPS_MDSP_A_L) {
		adc_reg_mask |= 0x80;
		adc_power_mask |= AIC3262_LADC_POWER_MASK;
	}
	if (state & AIC3XXX_COPS_MDSP_A_R) {
		adc_reg_mask |= 0x40;
		adc_power_mask |= AIC3262_RADC_POWER_MASK;
	}

	if (state & AIC3XXX_COPS_MDSP_A)
		aic3xxx_set_bits(control_data,
					AIC3262_ADC_DATAPATH_SETUP, 0XC0,
					adc_reg_mask);

	if (state & AIC3XXX_COPS_MDSP_D_L) {
		dac_reg_mask |= 0x80;
		dac_power_mask |= AIC3262_LDAC_POWER_STATUS_MASK;
	}
	if (state & AIC3XXX_COPS_MDSP_D_R) {
		dac_reg_mask |= 0x40;
		dac_power_mask |= AIC3262_RDAC_POWER_STATUS_MASK;
	}

	if (state & AIC3XXX_COPS_MDSP_D)
		aic3xxx_set_bits(control_data,
					AIC3262_DAC_DATAPATH_SETUP, 0XC0,
					dac_reg_mask);

	if (state & AIC3XXX_COPS_MDSP_A) {
		ret_wbits = aic3xxx_wait_bits(control_data,
				AIC3262_ADC_FLAG, AIC3262_ADC_POWER_MASK,
				adc_power_mask, AIC326X_TIME_DELAY,
				AIC326X_DELAY_COUNTER);
		if (!ret_wbits)
			dev_err(codec->dev, "ADC Power down timedout\n");
	}

	if (state & AIC3XXX_COPS_MDSP_D) {
		ret_wbits = aic3xxx_wait_bits(control_data,
				AIC3262_DAC_FLAG, AIC3262_DAC_POWER_MASK,
				dac_power_mask, AIC326X_TIME_DELAY,
				AIC326X_DELAY_COUNTER);
		if (!ret_wbits)
			dev_err(codec->dev, "ADC Power down timedout\n");
	}

	return 0;
}

static int aic3262_restart_dsps_sync(struct snd_soc_component *codec, int run_state)
{

	aic3262_dsp_pwrdwn_status(codec);
	aic3262_dsp_pwrup(codec, run_state);

	return 0;
}

static const struct aic3xxx_codec_ops aic3262_cfw_codec_ops = {
	.reg_read  =	aic3262_ops_reg_read,
	.reg_write =	aic3262_ops_reg_write,
	.reg_i2c_write =	aic3262_ops_i2c_reg_write,
	.set_bits  =	aic3262_ops_set_bits,
	.bulk_read =	aic3262_ops_bulk_read,
	.bulk_write =	aic3262_ops_bulk_write,
	.lock      =	aic3262_ops_lock,
	.unlock    =	aic3262_ops_unlock,
	.stop      =	aic3262_ops_stop,
	.restore   =	aic3262_ops_restore,
	.bswap     =	aic3262_ops_adaptivebuffer_swap,
};


/**
 * aic3262_codec_read: provide read api to read aic3262 registe space
 * @codec: pointer variable to codec having codec information,
 * @reg: register address,
 *
 * Return: Return value will be value read.
 */
unsigned int aic3262_codec_read(struct snd_soc_component *codec, unsigned int reg)
{

	u8 value;

	//printk("%s enter", __func__);
	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) ®
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);

	value = aic3xxx_reg_read(control_data, reg);
/*
	dev_info(codec->dev, "p %d , r 30 %x %x\n",
		aic_reg->aic3xxx_register.page,
		aic_reg->aic3xxx_register.offset, value);
*/
	//printk("%s exit", __func__);
	return value;
}

/**
 * aic3262_codec_write: provide write api to write at aic3262 registe space
 * @codec: Pointer variable to codec having codec information,
 * @reg: Register address,
 * @value: Value to be written to address space
 *
 * Return: Total no of byte written to address space.
 */
int aic3262_codec_write(struct snd_soc_component *codec, unsigned int reg,
			unsigned int value)
{
	//printk("%s enter", __func__);
	union aic3xxx_reg_union *aic_reg = (union aic3xxx_reg_union *) ®
	struct aic3xxx *control_data = snd_soc_component_get_drvdata(codec);
/*
	dev_info(codec->dev, "p %d, w 30 %x %x\n",
		aic_reg->aic3xxx_register.page,
		aic_reg->aic3xxx_register.offset, value);
*/
	//printk("%s exit", __func__);
	return aic3xxx_reg_write(control_data, reg, value);
}

/**
 * aic3262_set_interface_fmt: Setting interface ASI1/2/3 data format
 * @dai: ponter to dai Holds runtime data for a DAI,
 * @fmt: asi format info,
 * @channel: number of channel,
 *
 * Return: On success return 0.
*/
static int aic3262_set_interface_fmt(struct snd_soc_dai *dai, unsigned int fmt,
					unsigned int channel)
{
	int aif_interface_reg;
	int aif_bclk_offset_reg;
	struct snd_soc_component *codec = dai->component;
	u8 iface_val = 0;
	u8 dsp_a_val = 0;

	switch (dai->id) {
	case 0:
		aif_interface_reg = AIC3262_ASI1_BUS_FMT;
		aif_bclk_offset_reg = AIC3262_ASI1_LCH_OFFSET;
		break;
	case 1:
		aif_interface_reg = AIC3262_ASI2_BUS_FMT;
		aif_bclk_offset_reg = AIC3262_ASI2_LCH_OFFSET;
		break;
	case 2:
		aif_interface_reg = AIC3262_ASI3_BUS_FMT;
		aif_bclk_offset_reg = AIC3262_ASI3_LCH_OFFSET;
		break;
	default:
		return -EINVAL;

	}
	/* interface format */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		iface_val = 0;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		dsp_a_val = 0x1;	/* Intentionally falling through */
	case SND_SOC_DAIFMT_DSP_B:
		if (channel == 1)
			iface_val = 0x80;	/* Choose mono PCM */
		else if (channel <= 8)
			iface_val = 0x20;	/* choose multichannel PCM */
		else
			return -EINVAL;
		break;
	case SND_SOC_DAIFMT_RIGHT_J:
		iface_val = 0x40;
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		iface_val = 0x60;
		break;
	default:
		dev_err(codec->dev, "Invalid DAI interface format\n");
		return -EINVAL;
	}
	snd_soc_component_update_bits(codec, aif_interface_reg,
					AIC3262_ASI_INTERFACE_MASK, iface_val);
	snd_soc_component_update_bits(codec, aif_bclk_offset_reg,
					AIC3262_BCLK_OFFSET_MASK, dsp_a_val);
	return 0;

}

/**
 * aic3262_hw_params: This function is to set the hardware parameters
 *		for AIC3262.
 *		The functions set the sample rate and audio serial data word
 *		length.
 * @substream: pointer variable to sn_pcm_substream,
 * @params: pointer to snd_pcm_hw_params structure,
 * @dai: ponter to dai Holds runtime data for a DAI,
 *
 * Return: Return 0 on success.
 */
int aic3262_hw_params(struct snd_pcm_substream *substream,
			struct snd_pcm_hw_params *params,
			struct snd_soc_dai *dai)
{
	struct snd_soc_component *codec = dai->component;
	struct aic3xxx *control = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control->control_data;

	int asi_reg, val = 0, ret = 0;
	u8 data = 0, value = 0, wclk_div = 0, bclk_div = 0;
	unsigned int channels = params_channels(params);

//	dev_info(codec->dev, "%s enter\n", __func__);
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		aic3262->stream_status = 1;
	else
		aic3262->stream_status = 0;

	switch (dai->id) {
	case 0:
		asi_reg = AIC3262_ASI1_BUS_FMT;
		break;
	case 1:
		asi_reg = AIC3262_ASI2_BUS_FMT;
		break;
	case 2:
		asi_reg = AIC3262_ASI3_BUS_FMT;
		break;
	default:
		return -EINVAL;
	}

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		data = data | 0x00;
		break;
	case SNDRV_PCM_FORMAT_S20_LE:
		data |= (0x08);
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		data |= (0x10);
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		data |= (0x18);
		break;
	}

	/* Configure TDM for multi chennels */
	switch (channels) {
	case 4:
		value = value | 0x40;
		bclk_div = 0x03;
		wclk_div = 0x40;
		break;
	case 6:
		bclk_div = 0x02;
		wclk_div = 0x60;
		value = value | 0x80;
		break;
	case 8:
		bclk_div = 0x01;
		wclk_div = 0x00;
		value = value | 0xC0;
		break;
	default:
		bclk_div = 0x04;
		wclk_div = 0x20;
	}

	snd_soc_component_update_bits(codec, AIC3262_ASI1_CHNL_SETUP,
				AIC3262_ASI1_CHNL_MASK, value);

	snd_soc_component_update_bits(codec, AIC3262_ASI1_BCLK_N,
				AIC3262_ASI1_BCLK_N_MASK, bclk_div);

	snd_soc_component_update_bits(codec, AIC3262_ASI1_WCLK_N,
				AIC3262_ASI1_WCLK_N_MASK, wclk_div);


	val = snd_soc_component_read(codec, AIC3262_ASI1_BUS_FMT);
	val = snd_soc_component_read(codec, AIC3262_ASI1_CHNL_SETUP);

	/* configure the respective Registers for the above configuration */
	snd_soc_component_update_bits(codec, asi_reg,
			    AIC3262_ASI_DATA_WORD_LENGTH_MASK, data);
	ret = aic3262_set_interface_fmt(dai, aic3262->asi_fmt[dai->id],
					 channels);
	if (ret < 0) {
		dev_err(codec->dev, "failed to set hardware params for AIC3262\n");
		return ret;
	}

//	printk("%s exit", __func__);
	return 0;
}

/**
 * aic3262_mute: This function is to mute or unmute the left and right DAC
 * @dai: ponter to dai Holds runtime data for a DAI,
 * @mute: integer value one if we using mute else unmute,
 *
 * Return: return 0 on success.
 */
static int aic3262_mute(struct snd_soc_dai *dai, int mute, int stream)
{
	struct snd_soc_component *codec = dai->component;
	struct aic3xxx *control = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control->control_data;

//	dev_info(codec->dev, "codec : %s : started\n", __func__);
	if (dai->id > 2)
		return -EINVAL;
	if (mute) {
		aic3262->mute_asi &= ~((0x1) << dai->id);
		if (aic3262->mute_asi == 0)
			/* Mute only when all asi's are muted */
			snd_soc_component_update_bits(codec,
						   AIC3262_DAC_MVOL_CONF,
						   AIC3262_DAC_LR_MUTE_MASK,
						   AIC3262_DAC_LR_MUTE);

	} else {	/* Unmute */
		if (aic3262->mute_asi == 0)
			/* Unmute for the first asi that need to unmute.
			   rest unmute will pass */
			snd_soc_component_update_bits(codec,
						   AIC3262_DAC_MVOL_CONF,
						   AIC3262_DAC_LR_MUTE_MASK,
						   0x0);
		aic3262->mute_asi |= ((0x1) << dai->id);
	}
//	dev_info(codec->dev, "codec : %s : ended\n", __func__);
//	printk("%s exit", __func__);
	return 0;
}

/**
 * aic3262_set_dai_fmt: This function is to set the DAI format
 * @codec_dai: ponter to dai Holds runtime data for a DAI,
 * @fmt: asi format info,
 *
 * return: return 0 on success.
 */
static int aic3262_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
	struct snd_soc_component *codec = codec_dai->component;
	struct aic3xxx *control = snd_soc_component_get_drvdata(codec);
	struct aic3262_priv *aic3262 = control->control_data;
	u8 iface_val, master;
	int aif_bclk_wclk_reg;

	dev_info(codec->dev, "%s enter\n", __func__);
	iface_val = 0x00;
	master = 0x0;

	switch (codec_dai->id) {
	case 0:
		aif_bclk_wclk_reg = AIC3262_ASI1_BWCLK_CNTL_REG;
		break;
	case 1:
		aif_bclk_wclk_reg = AIC3262_ASI2_BWCLK_CNTL_REG;
		break;
	case 2:
		aif_bclk_wclk_reg = AIC3262_ASI3_BWCLK_CNTL_REG;
		break;
	default:
		return -EINVAL;

	}
	aic3262->asi_fmt[codec_dai->id] = fmt;
	/* set master/slave audio interface */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBM_CFM:
		aic3262->master = 1;
		master |= (AIC3262_WCLK_OUT_MASK | AIC3262_BCLK_OUT_MASK);
		break;
	case SND_SOC_DAIFMT_CBS_CFS:
		aic3262->master = 0;
		break;
	case SND_SOC_DAIFMT_CBS_CFM:	/* new case..for debug purpose */
		master |= (AIC3262_WCLK_OUT_MASK);
		aic3262->master = 0;
		break;
	case SND_SOC_DAIFMT_CBM_CFS:
		master |= (AIC3262_BCLK_OUT_MASK);
		aic3262->master = 0;
		break;

	default:
		dev_err(codec->dev, "Invalid DAI master/slave" " interface\n");

		return -EINVAL;
	}
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_DSP_A:
	case SND_SOC_DAIFMT_DSP_B:
		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
		case SND_SOC_DAIFMT_NB_NF:
			break;
		case SND_SOC_DAIFMT_IB_NF:
			master |= AIC3262_BCLK_INV_MASK;
			break;
		default:
			return -EINVAL;
		}
		break;
	case SND_SOC_DAIFMT_I2S:
	case SND_SOC_DAIFMT_RIGHT_J:
	case SND_SOC_DAIFMT_LEFT_J:
		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
		case SND_SOC_DAIFMT_NB_NF:
			break;
		case SND_SOC_DAIFMT_IB_NF:
			master |= AIC3262_BCLK_INV_MASK;
			break;
		default:
			return -EINVAL;
		}
		break;
	default:
		return -EINVAL;
	}
	snd_soc_component_update_bits(codec, aif_bclk_wclk_reg,
			    AIC3262_WCLK_BCLK_MASTER_MASK, master);
//	printk("%s exit", __func__);
	return 0;
}

/**
 * aic3262_dai_set_pll: This function is to Set pll for aic3262 codec dai
 * @dai: ponter to dai Holds runtime data for a DAI,$
 * @pll_id: integer pll_id
 * @source: DAI specific source for the PLL
 * @fin: PLL Input clock frequency in Hz,
 * @fout: Frequency out,
 *
 * Return: return 0 on success
*/
static int aic3262_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
				unsigned int Fin, unsigned int Fout)
{
	struct snd_soc_component *codec = dai->component;
	struct aic3262_priv *aic3262 = snd_soc_component_get_drvdata(codec);

	dev_info(codec->dev, "%s enter\n", __func__);
	dev_info(codec->dev, "In aic3262: dai_set_pll\n");
	dev_info(codec->dev, "%d, %s, dai->id = %d\n", __LINE__,
		__func__, dai->id);
	/* select the PLL_CLKIN */
	snd_soc_component_update_bits(codec, AIC3262_PLL_CLKIN_REG,
			    AIC3262_PLL_CLKIN_MASK, source <<
			    AIC3262_PLL_CLKIN_SHIFT);
	/* TODO: How to select low/high clock range? */

	aic3xxx_cfw_set_pll(aic3262->cfw_p, dai->id);

//	printk("%s exit", __func__);
	return 0;
}

/**
 *
 * aic3262_set_bias_level: This function is to get triggered
 *			 when dapm events occurs.
 * @codec: pointer variable to codec having informaton related to codec,
 * @level: Bias level-> ON, PREPARE, STANDBY, OFF.
 *
 * Return: Return 0 on success.
 */
static int aic3262_set_bias_level(struct snd_soc_component *codec,
				  enum snd_soc_bias_level level)
{

	printk("%s enter", __func__);
	switch (level) {
		/* full On */
	case SND_SOC_BIAS_ON:

		dev_info(codec->dev, "set_bias_on\n");
		break;

		/* partial On */
	case SND_SOC_BIAS_PREPARE:
		dev_info(codec->dev, "set_bias_prepare\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
		 */
		dev_info(codec->dev, "set_bias_stby\n");
		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
			pm_runtime_get_sync(codec->dev);
			snd_soc_component_update_bits(codec, AIC3262_POWER_CONF,
					    (AIC3262_AVDD_TO_DVDD_MASK |
					     AIC3262_EXT_ANALOG_SUPPLY_MASK),
					    0x0);
			snd_soc_component_update_bits(codec, AIC3262_REF_PWR_DLY,
					    AIC3262_CHIP_REF_PWR_ON_MASK,
					    AIC3262_CHIP_REF_PWR_ON);
			mdelay(40);

			snd_soc_component_update_bits(codec, AIC3262_CHARGE_PUMP_CNTL,
				AIC3262_DYNAMIC_OFFSET_CALIB_MASK,
				AIC3262_DYNAMIC_OFFSET_CALIB);
		}
		break;

		/* Off, without power */
	case SND_SOC_BIAS_OFF:
		dev_info(codec->dev, "set_bias_off\n");
		/* force all power off */
		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
			snd_soc_component_update_bits(codec, AIC3262_REF_PWR_DLY,
					    AIC3262_CHIP_REF_PWR_ON_MASK, 0x0);
			snd_soc_component_update_bits(codec, AIC3262_POWER_CONF,
					    (AIC3262_AVDD_TO_DVDD_MASK |
					     AIC3262_EXT_ANALOG_SUPPLY_MASK),
					    (AIC3262_AVDD_TO_DVDD |
					     AIC3262_EXT_ANALOG_SUPPLY_OFF));
			pm_runtime_put(codec->dev);
		}
		break;
	}

	codec->dapm.bias_level = level;
//	printk("%s exit", __func__);
	return 0;
}

/**
 *
 * aic3262_suspend; This function is to suspend the AIC3262 driver.
 * @codec: pointer variable to codec having informaton related to codec,
 *
 * Return: Return 0 on success.
 */
static int aic3262_suspend(struct snd_soc_component *codec)
{
//	printk("%s enter", __func__);
	aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF);
//	printk("%s exit", __func__);
	return 0;
}

/**
 * aic3262_resume: This function is to resume the AIC3262 driver
 *		 from off state to standby
 * @codec: pointer variable to codec having informaton related to codec,
 *
 * Return: Return 0 on success.
 */
static int aic3262_resume(struct snd_soc_component *codec)
{
//	printk("%s enter", __func__);
	aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
//	printk("%s exit", __func__);

	return 0;
}

/**
 * aic3262_probe: This is first driver function called by the SoC core driver.
 * @codec: pointer variable to codec having informaton related to codec,
 *
 * Return: Return 0 on success.
 */
static int aic3262_codec_probe(struct snd_soc_component *codec)
{
	int ret = 0;
	struct aic3xxx *control;
	struct aic3262_priv *aic3262;

//	printk("%s enter", __func__);

	if (codec == NULL) {
		dev_err(codec->dev, "codec pointer is NULL.\n");
		return -EINVAL;
	}

	control = snd_soc_component_get_drvdata(codec);
	aic3262 = kzalloc(sizeof(struct aic3262_priv), GFP_KERNEL);
	if (aic3262 == NULL) {
		dev_err(codec->dev, "memory pointer is NULL.\n");
		return -ENOMEM;
	}

	control->control_data = aic3262;

	aic3262->pdata = dev_get_platdata(codec->dev->parent);
	aic3262->codec = codec;
	aic3262->cur_fw = NULL;
	aic3262->cfw_p = &(aic3262->cfw_ps);
	aic3xxx_cfw_init(aic3262->cfw_p, &aic3262_cfw_codec_ops,
							aic3262->codec);
	aic3262->workqueue = create_singlethread_workqueue("aic3262-codec");
	if (!aic3262->workqueue) {
		ret = -ENOMEM;
		goto work_err;
	}
	INIT_DELAYED_WORK(&aic3262->delayed_work, aic3262_accessory_work);
	mutex_init(&aic3262->mutex);
	mutex_init(&codec->io_mutex);
	mutex_init(&aic3262->cfw_mutex);
	pm_runtime_enable(codec->dev);
	pm_runtime_resume(codec->dev);
		aic3262->dsp_runstate = 0;

	if (control->irq) {
		ret = aic3xxx_request_irq(control,
					  AIC3262_IRQ_HEADSET_DETECT,
					  aic3262_audio_handler,
					  IRQF_NO_SUSPEND,
					  "aic3262_irq_headset", codec);

		if (ret) {
			dev_err(codec->dev,
				"HEADSET detect irq request failed:%d\n", ret);
			goto irq_err;
		} else {

			/*  Dynamic Headset Detection Enabled */

			snd_soc_component_update_bits(codec, AIC3262_HP_DETECT,
					AIC3262_HEADSET_IN_MASK,
					AIC3262_HEADSET_IN_MASK);
		}
	}
	/* Keep the reference voltage ON while in
	   STANDBY mode for fast power up */

	snd_soc_component_update_bits(codec, AIC3262_REF_PWR_DLY,
			    AIC3262_CHIP_REF_PWR_ON_MASK,
			    AIC3262_CHIP_REF_PWR_ON);
	mdelay(40);

	snd_soc_component_update_bits(codec, AIC3262_CHARGE_PUMP_CNTL,
				AIC3262_DYNAMIC_OFFSET_CALIB_MASK,
				AIC3262_DYNAMIC_OFFSET_CALIB);

	//aic3262_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	aic3262->mute_asi = 0;


	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
				"tlv320aic3262_fw_v1.bin", codec->dev,
				GFP_KERNEL, codec, aic3262_firmware_load);
	if (ret < 0) {
		dev_err(codec->dev, "Firmware request failed\n");
		goto firm_err;
	}

//	printk("%s exit", __func__);
	return 0;
firm_err:
	aic3xxx_free_irq(control,
			 AIC3262_IRQ_HEADSET_DETECT, codec);
irq_err:
	destroy_workqueue(aic3262->workqueue);
work_err:
	kfree(aic3262);
	return 0;
}

/*
 * aic3262_remove: Cleans up and Remove aic3262 soc device
 * @codec: pointer variable to codec having informaton related to codec,
 *
 * Return: Return 0 on success.
 */
static void aic3262_codec_remove(struct snd_soc_component *codec)
{
	/* power down chip */
	struct aic3xxx *control;
	struct aic3262_priv *aic3262;

//	printk("%s enter", __func__);
	if (codec == NULL) {
		dev_err(codec->dev, "codec pointer is NULL.\n");
		return;
	}

	control = snd_soc_component_get_drvdata(codec);
	aic3262 = control->control_data;

	aic3262_set_bias_level(codec, SND_SOC_BIAS_OFF);

	/* free_irq if any */
	switch (control->type) {
	case TLV320AIC3262:
		if (control->irq)
			aic3xxx_free_irq(control,
					 AIC3262_IRQ_HEADSET_DETECT, codec);
		break;
	default:
		dev_info(codec->dev, "Coded is not TLV320AIC3262\n");
	}
	/* release firmware if any */
	if (aic3262->cur_fw != NULL)
		release_firmware(aic3262->cur_fw);
	if(aic3262->cfw_ps.pjt) {
		remove_cfw(aic3262->cfw_ps.pjt);
		aic3262->cfw_ps.pjt = NULL;
	}
	/* destroy workqueue for jac dev */
	destroy_workqueue(aic3262->workqueue);

	kfree(aic3262);
//	printk("%s exit", __func__);

}

static struct snd_soc_component_driver soc_codec_driver_aic326x = {
	.probe = aic3262_codec_probe,
	.remove = aic3262_codec_remove,
	.suspend = aic3262_suspend,
	.resume = aic3262_resume,
	.read = aic3262_codec_read,
	.write = aic3262_codec_write,
	.controls = aic3262_snd_controls,
	.num_controls = ARRAY_SIZE(aic3262_snd_controls),
	.dapm_widgets = aic3262_dapm_widgets,
	.num_dapm_widgets = ARRAY_SIZE(aic3262_dapm_widgets),
	.dapm_routes = aic3262_dapm_routes,
	.num_dapm_routes = ARRAY_SIZE(aic3262_dapm_routes),
	.set_bias_level = aic3262_set_bias_level,
};

int aic326x_register_codec(struct aic3xxx *pdev)
{
	return snd_soc_register_component(pdev->dev, &soc_codec_driver_aic326x,
				      aic326x_dai_driver,
				      ARRAY_SIZE(aic326x_dai_driver));

}

int aic326x_deregister_codec(struct aic3xxx *pdev)
{
	snd_soc_unregister_component(pdev->dev);
	return 0;
}

MODULE_ALIAS("platform:tlv320aic326x-codec");
MODULE_DESCRIPTION("ASoC TLV320AIC326X codec driver");
MODULE_AUTHOR("Y Preetam Sashank Reddy ");
MODULE_AUTHOR("Barani Prashanth ");
MODULE_AUTHOR("Mukund Navada K <navada@ti.com>");
MODULE_AUTHOR("Naren Vasanad <naren.vasanad@ti.com>");
MODULE_LICENSE("GPL");

logs:

[ 356.715868] aic3262_set_bias_level enter
[ 356.715885] tlv320aic3262-codec 2-0018: set_bias_prepare
[ 356.716033] aic3262_set_bias_level enter
[ 356.716044] tlv320aic3262-codec 2-0018: set_bias_stby
[ 356.716050] aic3262_set_bias_level enter
[ 356.716055] tlv320aic3262-codec 2-0018: set_bias_off

root:~# aplay -Dhw:0 3.wav
Playing WAVE '3.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Suspended. Trying resume. Done.
aplay: pcm_write:2061: write error: Input/output error

Thanks and regards

Rizwan Chikodi

  • Hi Rizwan,

    I'm looping in our software support team to assist you.

    Thanks,
    Jeff McPherson

  • Hi Rizwan

    I don't think suspend can be verified in aplay. Suspend is a system test, aplay command is suitable for the unit test.

    For example, in smartphone, the system can't be suspended during audio-player(not media player which contain both video and audio) or telephone is working.

    For media player, if system receives the suspend command, the media player will be paused before the system suspend.

    In my view, if you use aplay to test suspend, you will write such script, when getting the suspend command, the test script must close the aplay before system suspend.

    after that operation, system can be suspend. When resume, script start aplay again to the last playback point.

    Actually, audio player will vote against suspend operation to the system during playing audio. For media player, is won't vote against suspend operation to the system. So it will be paused is suspend.

    BR

    Shenghao Ding