diff options
author | Ravindra Lokhande <rlokhande@nvidia.com> | 2013-03-19 19:08:57 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 13:04:36 -0700 |
commit | b5b6ea7bb5e930c936d81254a6a6d2f5cbb846e1 (patch) | |
tree | 7df2b9d847c78d78d476a0bebf98452216c75006 | |
parent | 3900262fa3136605796b70ca6fa5ba2f1ee53d39 (diff) |
ASoC: codec: aic325x: Add TI codec support
This code is from TI
Change-Id: I8d0d78f651655145d5320c2a22f93dcbe5a3fb0f
Signed-off-by: Ravindra Lokhande <rlokhande@nvidia.com>
Reviewed-on: http://git-master/r/210811
GVS: Gerrit_Virtual_Submit
Reviewed-by: Scott Peterson <speterson@nvidia.com>
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/aic3xxx_verbose.h | 59 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic325x.c | 2084 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic325x.h | 361 |
5 files changed, 2510 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6a778fcfb9ce..fc85b34cbde3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -73,6 +73,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC32X4 if I2C select SND_SOC_TLV320AIC326X if I2C + select SND_SOC_TLV320AIC325X if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -367,6 +368,9 @@ config SND_SOC_TWL4030 config SND_SOC_TWL6040 tristate +config SND_SOC_TLV320AIC325X + tristate "TI AIC325x Codec" + config SND_SOC_UDA134X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 484a10136e00..567c4c56f9a7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -61,6 +61,7 @@ snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320aic326x-objs := tlv320aic326x.o aic3xxx/aic3xxx_cfw_ops.o +snd-soc-tlv320aic325x-objs := tlv320aic325x.o aic3xxx/aic3xxx_cfw_ops.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o @@ -190,6 +191,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320AIC326X) += snd-soc-tlv320aic326x.o +obj-$(CONFIG_SND_SOC_TLV320AIC325X) += snd-soc-tlv320aic325x.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/aic3xxx_verbose.h b/sound/soc/codecs/aic3xxx_verbose.h new file mode 100644 index 000000000000..4f7aeb42f91a --- /dev/null +++ b/sound/soc/codecs/aic3xxx_verbose.h @@ -0,0 +1,59 @@ +#ifndef _AIC3XXX_VERBOSE_H_ +#define _AIC3XXX_VERBOSE_H_ + +enum aic3xxx_verbose_enum { + AIC3XXX_NO_PRINT = 0, /* No debug prints */ + AIC3XXX_ALERT, /* Init functions can use this level */ + AIC3XXX_CRIT, /* vebosity levels for warning messages */ + AIC3XXX_ERR, /* if/else/switch case decisions */ + AIC3XXX_WARNING, /* function entry/exit */ + AIC3XXX_NOTICE, /* upper level codec read/write */ + AIC3XXX_INFO, /* low level i2c read/write */ + AIC3XXX_DEBUG, /* verbose level for bottom halves */ + AIC3XXX_ENUM_MAX +}; + +/* Defined in sound/soc/codecs/tlv320aic326x.c */ +extern unsigned long debug_level; + +#define __aic3xxx_debug(PRIORITY, FORMAT, ARGV...) \ +do { \ + if (debug_level > 0) { \ + if ((debug_level >= PRIORITY) && \ + (PRIORITY > AIC3XXX_NO_PRINT)) \ + printk(FORMAT, ##ARGV); \ + } \ +} while (0) + +#ifdef CONFIG_AIC3XXX_VERBOSE +#define aic3xxx_print(PRIORITY, FORMAT, ARGV...) \ + __aic3xxx_debug(PRIORITY, FORMAT, ##ARGV) +#else +#define aic3xxx_print(PRIORITY, FORMAT, ARGV...) +#endif + +#define INIT_PRINT(FORMAT, ARGV...) \ + aic3xxx_print(AIC3XXX_ALERT, FORMAT, ##ARGV) + +#define WARN_PRINT(FORMAT, ARGV...) \ + aic3xxx_print(AIC3XXX_CRIT, FORMAT, ##ARGV) + +#define DECISION_PRINT(FORMAT, ARGV...) \ + aic3xxx_print(AIC3XXX_ERR, FORMAT, ##ARGV) + +#define FUNCTION_ENTRY_PRINT() \ + aic3xxx_print(AIC3XXX_WARNING, "%s Entered\n", __func__) + +#define FUNCTION_EXIT_PRINT() \ + aic3xxx_print(AIC3XXX_WARNING, "Line=%d, %s Exit\n", __LINE__, __func__) + +#define CODEC_PRINT(FORMAT, ARGV...) \ + aic3xxx_print(AIC3XXX_NOTICE, FORMAT, ##ARGV) + +#define LVL_MFD_PRINT(FORMAT, ARGV...) \ + aic3xxx_print(AIC3XXX_INFO, FORMAT, ##ARGV) + +#define BTM_HALF_PRINT(FORMAT, ARGV...) \ + aic3xxx_print(AIC3XXX_DEBUG, FORMAT, ##ARGV) + +#endif /* _AIC3XXX_VERBOSE_H_ */ diff --git a/sound/soc/codecs/tlv320aic325x.c b/sound/soc/codecs/tlv320aic325x.c new file mode 100644 index 000000000000..e849ffd62962 --- /dev/null +++ b/sound/soc/codecs/tlv320aic325x.c @@ -0,0 +1,2084 @@ +/* + * linux/sound/soc/codecs/tlv320aic325x.c + * + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Based on sound/soc/codecs/wm8753.c by Liam Girdwood + * + * 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 31-04-2009 + * The AIC32 ASoC driver is ported for the codec AIC325x. + * + * + * Rev 1.0 Mini DSP support 11-05-2009 + * Added mini DSP programming support + * + * Rev 1.1 Mixer controls 18-01-2011 + * Added all the possible mixer controls. + * + * Rev 1.2 Additional Codec driver support 2-02-2011 + * Support for AIC3253, AIC3206, AIC3256 + * + * Rev 2.0 Ported the Codec driver to 2.6.39 kernel 30-03-2012 + * + * Rev 2.1 PLL DAPM support added to the codec driver 03-04-2012 + * + * Rev 2.2 Added event handlers for DAPM widgets 16-05-2012 + * Updated ENUM declerations + * + */ + +/* + * 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/platform_device.h> +#include <linux/cdev.h> +#include <linux/mutex.h> +#include <linux/switch.h> + +#include <linux/slab.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/jack.h> + +#include "tlv320aic325x.h" +#include <linux/mfd/tlv320aic3256-registers.h> +#include <linux/mfd/tlv320aic325x-core.h> + + +/* + * enable debug prints in the driver + */ +#define DBG +#ifdef DBG + #define dprintk(x...) printk(x) +#else + #define dprintk(x...) +#endif + +#define AIC3256_IRQ_HEADSET_DETECT 0 +/* +* Function Prototype +*/ +static int aic325x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *); +static int aic325x_mute(struct snd_soc_dai *dai, int mute); +static int aic325x_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); +static int aic325x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level); +static int aic325x_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int Fin, unsigned int Fout); +static unsigned int aic325x_codec_read(struct snd_soc_codec *codec, + unsigned int reg); +static int aic325x_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value); +static int aic325x_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int aic3256_get_runstate(struct snd_soc_codec *codec); +static int aic3256_dsp_pwrdwn_status(struct snd_soc_codec *codec); +static int aic3256_dsp_pwrup(struct snd_soc_codec *codec, int state); +static int aic3256_restart_dsps_sync(struct snd_soc_codec *codec, int rs); + +static inline unsigned int dsp_non_sync_mode(unsigned int state) + { return (!((state & 0x03) && (state & 0x30))); } + +static void aic3256_firmware_load(const struct firmware *fw, void *context); + +/* DAC Volume Soft Step Control */ +static const char * const dacsoftstep_control[] = { "1 step/sample", + "1 step/2 sample", + "disabled" }; +SOC_ENUM_SINGLE_DECL(dac_vol_soft_setp_enum, AIC3256_DAC_CHN_REG, 0, + dacsoftstep_control); + +/* Volume Mode Selection Control */ +static const char * const volume_extra[] = { "L_R Ind Vol", "LVol=RVol", + "RVol=LVol" }; +/* DAC Volume Mode Selection */ +SOC_ENUM_SINGLE_DECL(dac_vol_extra_enum, AIC3256_DAC_MUTE_CTRL_REG, 0, + volume_extra); + +/* Beep Master Volume Control */ +SOC_ENUM_SINGLE_DECL(beep_master_vol_enum, AIC3256_BEEP_CTRL_REG2, 6, + volume_extra); + +/* Headset Detection Enable/Disable Control */ +static const char * const headset_detection[] = { "Enabled", "Disabled" }; +SOC_ENUM_SINGLE_DECL(hs_det_ctrl_enum, AIC3256_HEADSET_DETECT, 7, + headset_detection); + +/* MIC BIAS Voltage Control */ +static const char * const micbias_voltage[] = { "1.04/1.25V", "1.425/1.7V", + "2.075/2.5V", "POWER SUPPY" }; +SOC_ENUM_SINGLE_DECL(micbias_voltage_enum, AIC3256_MICBIAS_CTRL, 4, + micbias_voltage); + +/* IN1L to Left MICPGA Positive Terminal Selection */ +static const char * const micpga_selection[] = { "off", "10k", "20k", "40k" }; +SOC_ENUM_SINGLE_DECL(IN1L_LMICPGA_P_sel_enum, AIC3256_LMICPGA_PIN_CFG, 6, + micpga_selection); + +/* IN2L to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(IN2L_LMICPGA_P_sel_enum, AIC3256_LMICPGA_PIN_CFG, 4, + micpga_selection); + +/* IN3L to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(IN3L_LMICPGA_P_sel_enum, AIC3256_LMICPGA_PIN_CFG, 4, + micpga_selection); + +/* IN1R to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(IN1R_LMICPGA_P_sel_enum, AIC3256_LMICPGA_PIN_CFG, 2, + micpga_selection); + +/* CM1L to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(CM1L_LMICPGA_P_sel_enum, AIC3256_LMICPGA_PIN_CFG, 0, + micpga_selection); + +/* IN2R to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(IN2R_LMICPGA_P_sel_enum, AIC3256_LMICPGA_NIN_CFG, 6, + micpga_selection); + +/* IN3R to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(IN3R_LMICPGA_P_sel_enum, AIC3256_LMICPGA_NIN_CFG, 4, + micpga_selection); + +/*CM2L to Left MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(CM2L_LMICPGA_P_sel_enum, AIC3256_LMICPGA_NIN_CFG, 2, + micpga_selection); + +/* IN1R to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(in1r_rmicpga_enum, AIC3256_RMICPGA_PIN_CFG, 6, + micpga_selection); + +/* IN2R to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(in2r_rmicpga_enum, AIC3256_RMICPGA_PIN_CFG, 4, + micpga_selection); + +/* IN3R to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(in3r_rmicpga_enum, AIC3256_RMICPGA_PIN_CFG, 2, + micpga_selection); + +/* IN2L to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(in2l_rmicpga_enum, AIC3256_RMICPGA_PIN_CFG, 0, + micpga_selection); + +/* CM1R to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(cm1r_rmicpga_enum, AIC3256_RMICPGA_NIN_CFG, 6, + micpga_selection); + +/* IN1L to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(in1l_rmicpga_enum, AIC3256_RMICPGA_NIN_CFG, 4, + micpga_selection); + +/* IN3L to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(in3l_rmicpga_enum, AIC3256_RMICPGA_NIN_CFG, 2, + micpga_selection); + +/* CM2R to Right MICPGA Positive Terminal Selection */ +SOC_ENUM_SINGLE_DECL(cm2r_rmicpga_enum, AIC3256_RMICPGA_NIN_CFG, 0, + micpga_selection); + +/* Power up/down */ +static const char * const powerup[] = { "Power Down", "Power Up" }; + +/* Mic Bias Power up/down */ +SOC_ENUM_SINGLE_DECL(micbias_pwr_ctrl_enum, AIC3256_MICBIAS_CTRL, 6, powerup); + +/* Left DAC Power Control */ +SOC_ENUM_SINGLE_DECL(ldac_power_enum, AIC3256_DAC_CHN_REG, 7, powerup); + +/* Right DAC Power Control */ +SOC_ENUM_SINGLE_DECL(rdac_power_enum, AIC3256_DAC_CHN_REG, 6, powerup); + +/* Left ADC Power Control */ +SOC_ENUM_SINGLE_DECL(ladc_pwr_ctrl_enum, AIC3256_ADC_CHN_REG, 7, powerup); +/* Right ADC Power Control */ +SOC_ENUM_SINGLE_DECL(radc_pwr_ctrl_enum, AIC3256_ADC_CHN_REG, 6, powerup); + +/* HeadPhone Driver Power Control */ +SOC_ENUM_DOUBLE_DECL(hp_pwr_ctrl_enum, AIC3256_OUT_PWR_CTRL, 5, 4, powerup); + +/*Line-Out Driver Power Control */ +SOC_ENUM_DOUBLE_DECL(lineout_pwr_ctrl_enum, AIC3256_OUT_PWR_CTRL, 3, 2, + powerup); + +/* Mixer Amplifiers Power Control */ +SOC_ENUM_DOUBLE_DECL(mixer_amp_pwr_ctrl_enum, AIC3256_OUT_PWR_CTRL, 1, 0, + powerup); + +/* Mic Bias Generation */ +static const char * const vol_generation[] = { "AVDD", "LDOIN" }; +SOC_ENUM_SINGLE_DECL(micbias_voltage_ctrl_enum, AIC3256_MICBIAS_CTRL, 3, + vol_generation); + +/* DAC Data Path Control */ +static const char * const path_control[] = { "Disabled", "LDAC Data", + "RDAC Data", "L_RDAC Data" }; +/* Left DAC Data Path Control */ +SOC_ENUM_SINGLE_DECL(ldac_data_path_ctrl_enum, AIC3256_DAC_CHN_REG, 4, + path_control); + +/* Right DAC Data Path Control */ +SOC_ENUM_SINGLE_DECL(rdac_data_path_ctrl_enum, AIC3256_DAC_CHN_REG, 2, + path_control); + +/* Audio gain control (AGC) Enable/Disable Control */ +static const char * const disable_enable[] = { "Disabled", "Enabled" }; + +/* Left Audio gain control (AGC) Enable/Disable Control */ +SOC_ENUM_SINGLE_DECL(left_agc_enable_disable_enum, AIC3256_LEFT_AGC_REG1, 7, + disable_enable); + +/* Left/Right Audio gain control (AGC) Enable/Disable Control */ +SOC_ENUM_SINGLE_DECL(right_agc_enable_disable_enum, AIC3256_RIGHT_AGC_REG1, 7, + disable_enable); + +/* Left MICPGA Gain Enabled/Disable */ +SOC_ENUM_SINGLE_DECL(left_micpga_ctrl_enum, AIC3256_LMICPGA_VOL_CTRL, 7, + disable_enable); + +/* Right MICPGA Gain Enabled/Disable */ +SOC_ENUM_SINGLE_DECL(right_micpga_ctrl_enum, AIC3256_RMICPGA_VOL_CTRL, 7, + disable_enable); + +/* DRC Enable/Disable Control */ +SOC_ENUM_DOUBLE_DECL(drc_ctrl_enum, AIC3256_DRC_CTRL_REG1, 6, 5, + disable_enable); + +/* Beep generator Enable/Disable control */ +SOC_ENUM_SINGLE_DECL(beep_gen_ctrl_enum, AIC3256_BEEP_CTRL_REG1, 7, + disable_enable); + +/* Headphone ground centered mode enable/disable control */ +SOC_ENUM_SINGLE_DECL(hp_gnd_centred_mode_ctrl, AIC3256_HP_DRIVER_CONF_REG, 4, + disable_enable); + +/* Audio loopback enable/disable control */ +SOC_ENUM_SINGLE_DECL(audio_loopback_enum, AIC3256_AIS_REG_3, 5, + disable_enable); + +/* DMIC intput Selection control */ +static const char * const dmic_input_sel[] = { "GPIO", "SCLK", "DIN" }; +SOC_ENUM_SINGLE_DECL(dmic_input_enum, AIC3256_ADC_CHN_REG, 4, dmic_input_sel); + +/*charge pump Enable*/ +static const char * const charge_pump_ctrl_enum[] = { "Power_Down", + "Reserved", + "Power_Up" }; +SOC_ENUM_SINGLE_DECL(charge_pump_ctrl, AIC3256_POW_CFG, 0, + charge_pump_ctrl_enum); + +/* DAC volume DB scale */ +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0); +/* ADC volume DB scale */ +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1200, 50, 0); +/* Output Gain in DB scale */ +static const DECLARE_TLV_DB_SCALE(output_gain_tlv, -600, 100, 0); +/* MicPGA Gain in DB */ +static const DECLARE_TLV_DB_SCALE(micpga_gain_tlv, 0, 50, 0); + +/* Various Controls For AIC325x */ +static const struct snd_kcontrol_new aic325x_snd_controls[] = { + /* IN1L to HPL Volume Control */ + SOC_SINGLE("IN1L to HPL volume control", AIC3256_IN1L_HPL_CTRL, + 0, 0x72, 1), + + /* IN1R to HPR Volume Control */ + SOC_SINGLE("IN1R to HPR volume control", AIC3256_IN1R_HPR_CTRL, + 0, 0x72, 1), + + /* IN1L to HPL routing */ + SOC_SINGLE("IN1L to HPL Route", AIC3256_HPL_ROUTE_CTRL, 2, 1, 0), + + /* MAL output to HPL */ + SOC_SINGLE("MAL Output to HPL Route", AIC3256_HPL_ROUTE_CTRL, 1, 1, 0), + + /*MAR output to HPL */ + SOC_SINGLE("MAR Output to HPL Route", AIC3256_HPL_ROUTE_CTRL, 0, 1, 0), + + /* IN1R to HPR routing */ + SOC_SINGLE("IN1R to HPR Route", AIC3256_HPR_ROUTE_CTRL, 2, 1, 0), + + /* MAR to HPR routing */ + SOC_SINGLE("MAR Output to HPR Route", AIC3256_HPR_ROUTE_CTRL, 1, 1, 0), + + /* HPL Output to HRP routing */ + SOC_SINGLE("HPL Output to HPR Route", AIC3256_HPR_ROUTE_CTRL, 0, 1, 0), + + /* MAL Output to LOL routing*/ + SOC_SINGLE("MAL Output to LOL Route", AIC3256_LOL_ROUTE_CTRL, 1, 1, 0), + + /* LOR Output to LOL routing*/ + SOC_SINGLE("LOR Output to LOL Route", AIC3256_LOL_ROUTE_CTRL, 0, 1, 0), + + /* MAR Output to LOR routing*/ + SOC_SINGLE("MAR Outout to LOR Route", AIC3256_LOR_ROUTE_CTRL, 1, 1, 0), + + /* DRC Threshold value Control */ + SOC_SINGLE("DRC Threshold value", + AIC3256_DRC_CTRL_REG1, 2, 0x07, 0), + + /* DRC Hysteresis value control */ + SOC_SINGLE("DRC Hysteresis value", + AIC3256_DRC_CTRL_REG1, 0, 0x03, 0), + + /* DRC Hold time control */ + SOC_SINGLE("DRC hold time", AIC3256_DRC_CTRL_REG2, 3, 0x0F, 0), + + /* DRC Attack rate control */ + SOC_SINGLE("DRC attack rate", AIC3256_DRC_CTRL_REG3, 4, 0x0F, 0), + + /* DRC Decay rate control */ + SOC_SINGLE("DRC decay rate", AIC3256_DRC_CTRL_REG3, 0, 0x0F, 0), + + /* Beep Length MSB control */ + SOC_SINGLE("Beep Length MSB", AIC3256_BEEP_CTRL_REG3, 0, 255, 0), + + /* Beep Length MID control */ + SOC_SINGLE("Beep Length MID", AIC3256_BEEP_CTRL_REG4, 0, 255, 0), + + /* Beep Length LSB control */ + SOC_SINGLE("Beep Length LSB", AIC3256_BEEP_CTRL_REG5, 0, 255, 0), + + /* Beep Sin(x) MSB control */ + SOC_SINGLE("Beep Sin(x) MSB", AIC3256_BEEP_CTRL_REG6, 0, 255, 0), + /* Beep Sin(x) LSB control */ + SOC_SINGLE("Beep Sin(x) LSB", AIC3256_BEEP_CTRL_REG7, 0, 255, 0), + + /* Beep Cos(x) MSB control */ + SOC_SINGLE("Beep Cos(x) MSB", AIC3256_BEEP_CTRL_REG8, 0, 255, 0), + + /* Beep Cos(x) LSB control */ + SOC_SINGLE("Beep Cos(x) LSB", AIC3256_BEEP_CTRL_REG9, 0, 255, 0), + + + /* Left/Right DAC Digital Volume Control */ + SOC_DOUBLE_R_SX_TLV("DAC Digital Volume Control", + AIC3256_LDAC_VOL, AIC3256_RDAC_VOL, 8, 0xffffff81, 0x30, + dac_vol_tlv), + + /* Left/Right ADC Fine Gain Adjust */ + SOC_DOUBLE("L_R ADC Fine Gain Adjust", AIC3256_ADC_FGA, 4, 0, 0x04, 0), + + /* Left/Right ADC Volume Control */ + SOC_DOUBLE_R_SX_TLV("ADC Digital Volume Control", + AIC3256_LADC_VOL, AIC3256_RADC_VOL, 7, 0xffffff68, 0x28 , + adc_vol_tlv), + + /*HP Driver Gain Control*/ + SOC_DOUBLE_R_SX_TLV("HP Driver Gain", AIC3256_HPL_GAIN, + AIC3256_HPR_GAIN, 6, 0xfffffffa, + 0xe, output_gain_tlv), + + /*LO Driver Gain Control*/ + SOC_DOUBLE_R_SX_TLV("Line Driver Gain", AIC3256_LOL_GAIN, + AIC3256_LOR_GAIN, 6, + 0xfffffffa, 0x1d , output_gain_tlv), + + + /* Mixer Amplifier Volume Control */ + SOC_DOUBLE_R("Mixer_Amp_Vol_Ctrl", + AIC3256_MAL_CTRL_REG, AIC3256_MAR_CTRL_REG, + 0, 0x28, 1), + + + /*Left/Right MICPGA Volume Control */ + SOC_DOUBLE_R_TLV("MicPGA Volume Control", + AIC3256_LMICPGA_VOL_CTRL, AIC3256_RMICPGA_VOL_CTRL, 0, 0x5F, + 0, micpga_gain_tlv), + + /* Beep generator Volume Control */ + SOC_DOUBLE_R("Beep_gen_Vol_Ctrl", + AIC3256_BEEP_CTRL_REG1, AIC3256_BEEP_CTRL_REG2, + 0, 0x3F, 1), + + /* Left/Right AGC Target level control */ + SOC_DOUBLE_R("AGC Target Level Control", + AIC3256_LEFT_AGC_REG1, AIC3256_RIGHT_AGC_REG1, + 4, 0x07, 1), + + /* Left/Right AGC Hysteresis Control */ + SOC_DOUBLE_R("AGC Hysteresis Control", + AIC3256_LEFT_AGC_REG1, AIC3256_RIGHT_AGC_REG1, + 0, 0x03, 0), + + /*Left/Right AGC Maximum PGA applicable */ + SOC_DOUBLE_R("AGC Maximum PGA Control", + AIC3256_LEFT_AGC_REG3, AIC3256_RIGHT_AGC_REG3, + 0, 0x7F, 0), + + /* Left/Right AGC Noise Threshold */ + SOC_DOUBLE_R("AGC Noise Threshold", + AIC3256_LEFT_AGC_REG2, AIC3256_RIGHT_AGC_REG2, + 1, 0x1F, 1), + + /* Left/Right AGC Attack Time control */ + SOC_DOUBLE_R("AGC Attack Time control", + AIC3256_LEFT_AGC_REG4, AIC3256_RIGHT_AGC_REG4, + 3, 0x1F, 0), + + /* Left/Right AGC Decay Time control */ + SOC_DOUBLE_R("AGC Decay Time control", + AIC3256_LEFT_AGC_REG5, AIC3256_RIGHT_AGC_REG5, + 3, 0x1F, 0), + + /* Left/Right AGC Noise Debounce control */ + SOC_DOUBLE_R("AGC Noice bounce control", + AIC3256_LEFT_AGC_REG6, AIC3256_RIGHT_AGC_REG6, + 0, 0x1F, 0), + + /* Left/Right AGC Signal Debounce control */ + SOC_DOUBLE_R("AGC_Signal bounce ctrl", + AIC3256_LEFT_AGC_REG7, AIC3256_RIGHT_AGC_REG7, 0, 0x0F, 0), + + /* DAC Signal Processing Block Control*/ + SOC_SINGLE("DAC PRB Selection(1 to 25)", AIC3256_DAC_PRB, 0, 0x19, 0), + /* ADC Signal Processing Block Control */ + SOC_SINGLE("ADC PRB Selection(1 to 18)", AIC3256_ADC_PRB, 0, 0x12, 0), + + /*charge pump configuration for n/8 peak load current*/ + SOC_SINGLE("Charge_pump_peak_load_conf", + AIC3256_CHRG_CTRL_REG, 4, 8, 0), + + /*charge pump clock divide control*/ + SOC_SINGLE("charge_pump_clk_divider_ctrl", AIC3256_CHRG_CTRL_REG, + 0, 16, 0), + + /*HPL, HPR master gain control in ground centerd mode */ + SOC_SINGLE("HP_gain_ctrl_gnd_centered_mode", + AIC3256_HP_DRIVER_CONF_REG, 5, 3, 0), + + /*headphone amplifier compensation adjustment */ + SOC_SINGLE(" hp_amp_compensation_adjustment", + AIC3256_HP_DRIVER_CONF_REG, 7, 1, 0), + + /*headphone driver power configuration*/ + SOC_SINGLE(" HP_drv_pwr_conf", + AIC3256_HP_DRIVER_CONF_REG, 2, 4, 0), + + /*DC offset correction*/ + SOC_SINGLE("DC offset correction", AIC3256_HP_DRIVER_CONF_REG, 0, 4, 0), + + + SOC_ENUM("Mic_Bias_Power_ctrl", micbias_pwr_ctrl_enum), + +}; + +/* + *---------------------------------------------------------------------------- + * @struct snd_soc_codec_dai_ops | + * It is SoC Codec DAI Operations structure + *---------------------------------------------------------------------------- + */ +struct snd_soc_dai_ops aic325x_dai_ops = { + .hw_params = aic325x_hw_params, + .digital_mute = aic325x_mute, +/* .set_sysclk = aic325x_set_dai_sysclk, */ + .set_fmt = aic325x_set_dai_fmt, + .set_pll = aic325x_dai_set_pll, +}; + +/* + * 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 aic31xx rates ranges from 8k to 192k The PCM + * bit format supported are 16, 20, 24 and 32 bits + */ + + +static struct snd_soc_dai_driver tlv320aic325x_dai_driver[] = { + { + .name = "tlv320aic325x-MM_EXT", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC325x_RATES, + .formats = AIC325x_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC325x_RATES, + .formats = AIC325x_FORMATS, + }, + .ops = &aic325x_dai_ops, +}, + +}; + +/* Left HPL Mixer */ +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC switch", AIC3256_HPL_ROUTE_CTRL, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L switch", AIC3256_HPL_ROUTE_CTRL, 2, 1, 0), + SOC_DAPM_SINGLE("MAL switch", AIC3256_HPL_ROUTE_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("MAR switch", AIC3256_HPL_ROUTE_CTRL, 0, 1, 0), +}; + +static const char * const adc_mux_text[] = { + "Analog", "Digital" +}; + +SOC_ENUM_SINGLE_DECL(adcl_enum, AIC3256_ADC_CHN_REG, 3, adc_mux_text); +SOC_ENUM_SINGLE_DECL(adcr_enum, AIC3256_ADC_CHN_REG, 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); + +/* Right HPR Mixer */ +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC switch", AIC3256_HPR_ROUTE_CTRL, 4, 1, 0), + SOC_DAPM_SINGLE("R_DAC switch", AIC3256_HPR_ROUTE_CTRL, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R switch", AIC3256_HPR_ROUTE_CTRL, 2, 1, 0), + SOC_DAPM_SINGLE("MAR switch", AIC3256_HPR_ROUTE_CTRL, 1, 1, 0), +}; + +/* Left Line out mixer */ +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC switch", AIC3256_LOL_ROUTE_CTRL, 4, 1, 0), + SOC_DAPM_SINGLE("L_DAC switch", AIC3256_LOL_ROUTE_CTRL, 3, 1, 0), + SOC_DAPM_SINGLE("MAL switch", AIC3256_LOL_ROUTE_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("LOR switch", AIC3256_LOL_ROUTE_CTRL, 0, 1, 0), +}; +/* Right Line out Mixer */ +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC switch", AIC3256_LOR_ROUTE_CTRL, 3, 1, 0), + SOC_DAPM_SINGLE("MAR switch", AIC3256_LOR_ROUTE_CTRL, 1, 1, 0), +}; + +/* Left Input Mixer */ +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L switch", AIC3256_LMICPGA_PIN_CFG, 6, 3, 0), + SOC_DAPM_SINGLE("IN2_L switch", AIC3256_LMICPGA_PIN_CFG, 4, 3, 0), + SOC_DAPM_SINGLE("IN3_L switch", AIC3256_LMICPGA_PIN_CFG, 2, 3, 0), + SOC_DAPM_SINGLE("IN1_R switch", AIC3256_LMICPGA_PIN_CFG, 0, 3, 0), + + SOC_DAPM_SINGLE("CM1L switch", AIC3256_LMICPGA_NIN_CFG, 6, 3, 0), + SOC_DAPM_SINGLE("IN2_R switch", AIC3256_LMICPGA_NIN_CFG, 4, 3, 0), + SOC_DAPM_SINGLE("IN3_R switch", AIC3256_LMICPGA_NIN_CFG, 2, 3, 0), + SOC_DAPM_SINGLE("CM2L switch", AIC3256_LMICPGA_NIN_CFG, 0, 3, 0), +}; + +/* Right Input Mixer */ +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R switch", AIC3256_RMICPGA_PIN_CFG, 6, 3, 0), + SOC_DAPM_SINGLE("IN2_R switch", AIC3256_RMICPGA_PIN_CFG, 4, 3, 0), + SOC_DAPM_SINGLE("IN3_R switch", AIC3256_RMICPGA_PIN_CFG, 2, 3, 0), + SOC_DAPM_SINGLE("IN2_L switch", AIC3256_RMICPGA_PIN_CFG, 0, 3, 0), + SOC_DAPM_SINGLE("CM1R switch", AIC3256_RMICPGA_NIN_CFG, 6, 3, 0), + SOC_DAPM_SINGLE("IN1_L switch", AIC3256_RMICPGA_NIN_CFG, 4, 3, 0), + SOC_DAPM_SINGLE("IN3_L switch", AIC3256_RMICPGA_NIN_CFG, 2, 3, 0), + SOC_DAPM_SINGLE("CM2R switch", AIC3256_RMICPGA_NIN_CFG, 0, 3, 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 | SND_SOC_DAPM_POST_PMD)) + mdelay(10); + return 0; +} + +/** + *aic325x_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 aic325x_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int reg_mask = 0; + int ret_wbits = 0; + int run_state_mask; + int sync_needed = 0, non_sync_state = 0; + int other_dsp = 0, run_state = 0; + struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(w->codec); + + if (w->shift == 7) { + reg_mask = AIC3256_LDAC_POWER_STATUS_MASK ; + run_state_mask = AIC3XXX_COPS_MDSP_D_L; + } + if (w->shift == 6) { + reg_mask = AIC3256_RDAC_POWER_STATUS_MASK ; + run_state_mask = AIC3XXX_COPS_MDSP_D_R ; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret_wbits = aic325x_wait_bits(w->codec->control_data, + AIC3256_DAC_FLAG, + reg_mask, reg_mask, + AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER); + sync_needed = aic325x_reg_read(w->codec->control_data, + AIC3256_DAC_PRB); + non_sync_state = + dsp_non_sync_mode(aic325x->dsp_runstate); + other_dsp = + aic325x->dsp_runstate & AIC3XXX_COPS_MDSP_A; + + if (sync_needed && non_sync_state && other_dsp) { + run_state = + aic3256_get_runstate( + aic325x->codec); + aic3256_dsp_pwrdwn_status(aic325x->codec); + aic3256_dsp_pwrup(aic325x->codec, run_state); + } + aic325x->dsp_runstate |= run_state_mask; + if (!ret_wbits) { + dev_err(w->codec->dev, "DAC_post_pmu timed out\n"); + return -1; + } + break; + case SND_SOC_DAPM_POST_PMD: + ret_wbits = aic325x_wait_bits(w->codec->control_data, + AIC3256_DAC_FLAG, reg_mask, 0, + AIC3256_TIME_DELAY, AIC3256_DELAY_COUNTER); + aic325x->dsp_runstate = + (aic325x->dsp_runstate & ~run_state_mask); + if (!ret_wbits) { + dev_err(w->codec->dev, "DAC_post_pmd timed out\n"); + return -1; + } + break; + default: + BUG(); + return -EINVAL; + } + return 0; +} + +/** + * aic325x_adc_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 aic325x_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + + int run_state = 0; + int non_sync_state = 0, sync_needed = 0; + int other_dsp = 0; + int run_state_mask = 0; + struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(w->codec); + int reg_mask = 0; + int ret_wbits = 0; + + if (w->shift == 7) { + reg_mask = AIC3256_LADC_POWER_MASK; + run_state_mask = AIC3XXX_COPS_MDSP_A_L; + } + if (w->shift == 6) { + reg_mask = AIC3256_RADC_POWER_MASK; + run_state_mask = AIC3XXX_COPS_MDSP_A_R; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret_wbits = aic325x_wait_bits(w->codec->control_data, + AIC3256_ADC_FLAG , reg_mask, + reg_mask, AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER); + + sync_needed = aic325x_reg_read(w->codec->control_data, + AIC3256_DAC_PRB); + non_sync_state = dsp_non_sync_mode(aic3256->dsp_runstate); + other_dsp = aic3256->dsp_runstate & AIC3XXX_COPS_MDSP_D; + if (sync_needed && non_sync_state && other_dsp) { + run_state = aic3256_get_runstate( + aic3256->codec); + aic3256_dsp_pwrdwn_status(aic3256->codec); + aic3256_dsp_pwrup(aic3256->codec, run_state); + } + aic3256->dsp_runstate |= run_state_mask; + if (!ret_wbits) { + dev_err(w->codec->dev, "ADC POST_PMU timedout\n"); + return -1; + } + break; + + case SND_SOC_DAPM_POST_PMD: + ret_wbits = aic325x_wait_bits(w->codec->control_data, + AIC3256_ADC_FLAG, reg_mask, 0, + AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER); + aic3256->dsp_runstate = (aic3256->dsp_runstate & + ~run_state_mask); + if (!ret_wbits) { + dev_err(w->codec->dev, "ADC POST_PMD timedout\n"); + return -1; + } + break; + + default: + BUG(); + return -EINVAL; + } + + return 0; +} + +/* AIC325x Widget Structure */ +static const struct snd_soc_dapm_widget aic325x_dapm_widgets[] = { + + /* dapm widget (stream domain) for left DAC */ + SND_SOC_DAPM_DAC_E("Left DAC", "Left Playback", AIC3256_DAC_CHN_REG, + 7, 0, aic325x_dac_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* dapm widget (stream domain) for right DAC */ + SND_SOC_DAPM_DAC_E("Right DAC", "Right Playback", AIC3256_DAC_CHN_REG, + 6, 0, aic325x_dac_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* dapm widget (path domain) for left DAC_L Mixer */ + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + + /* dapm widget (path domain) for right DAC_R mixer */ + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], ARRAY_SIZE(hpr_output_mixer_controls)), + + /* dapm widget for Left Head phone Power */ + SND_SOC_DAPM_PGA_E("HPL PGA", AIC3256_OUT_PWR_CTRL, 5, 0, NULL, 0, + aic325x_hp_event, SND_SOC_DAPM_PRE_PMU), + + /* dapm widget (path domain) for Left Line-out Output Mixer */ + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], ARRAY_SIZE(lol_output_mixer_controls)), + + /* dapm widget for Left Line-out Power */ + SND_SOC_DAPM_PGA_E("LOL PGA", AIC3256_OUT_PWR_CTRL, 3, 0, NULL, 0, + NULL, SND_SOC_DAPM_POST_PMU), + + + + /* dapm widget for Right Head phone Power */ + SND_SOC_DAPM_PGA_E("HPR PGA", AIC3256_OUT_PWR_CTRL, 4, 0, NULL, 0, + aic325x_hp_event, SND_SOC_DAPM_POST_PMU), + + /* dapm widget for (path domain) Right Line-out Output Mixer */ + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + + /* dapm widget for Right Line-out Power */ + SND_SOC_DAPM_PGA_E("LOR PGA", AIC3256_OUT_PWR_CTRL, 2, 0, NULL, 0, + NULL, SND_SOC_DAPM_POST_PMU), + + /* dapm supply widget for Charge pump */ + SND_SOC_DAPM_SUPPLY("Charge Pump", AIC3256_POW_CFG, 1, 0, NULL, + SND_SOC_DAPM_POST_PMU), + /* Input DAPM widget for CM */ + SND_SOC_DAPM_INPUT("CM"), + /* Input DAPM widget for CM1L */ + SND_SOC_DAPM_INPUT("CM1L"), + /* Input DAPM widget for CM2L */ + SND_SOC_DAPM_INPUT("CM2L"), + /* Input DAPM widget for CM1R */ + SND_SOC_DAPM_INPUT("CM1R"), + /* Input DAPM widget for CM2R */ + SND_SOC_DAPM_INPUT("CM2R"), + + /* Stream widget for Left ADC */ + SND_SOC_DAPM_ADC_E("Left ADC", "Left Capture", AIC3256_ADC_CHN_REG, + 7, 0, aic325x_adc_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + + /* Stream widget for Right ADC */ + SND_SOC_DAPM_ADC_E("Right ADC", "Right Capture", AIC3256_ADC_CHN_REG, + 6, 0, aic325x_adc_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Left Inputs to Left MicPGA */ + SND_SOC_DAPM_PGA("Left MicPGA", AIC3256_LMICPGA_VOL_CTRL , + 7, 1, NULL, 0), + + /* Right Inputs to Right MicPGA */ + SND_SOC_DAPM_PGA("Right MicPGA", AIC3256_RMICPGA_VOL_CTRL, + 7, 1, NULL, 0), + + /* Left MicPGA to Mixer PGA Left */ + SND_SOC_DAPM_PGA("MAL PGA", AIC3256_OUT_PWR_CTRL , 1, 0, NULL, 0), + + /* Right Inputs to Mixer PGA Right */ + SND_SOC_DAPM_PGA("MAR PGA", AIC3256_OUT_PWR_CTRL, 0, 0, NULL, 0), + + /* 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)), + /* dapm widget (platform domain) name for HPLOUT */ + SND_SOC_DAPM_OUTPUT("HPL"), + + /* dapm widget (platform domain) name for HPROUT */ + SND_SOC_DAPM_OUTPUT("HPR"), + + /* dapm widget (platform domain) name for LOLOUT */ + SND_SOC_DAPM_OUTPUT("LOL"), + + /* dapm widget (platform domain) name for LOROUT */ + SND_SOC_DAPM_OUTPUT("LOR"), + + /* dapm widget (platform domain) name for LINE1L */ + SND_SOC_DAPM_INPUT("IN1_L"), + + /* dapm widget (platform domain) name for LINE1R */ + SND_SOC_DAPM_INPUT("IN1_R"), + + /* dapm widget (platform domain) name for LINE2L */ + SND_SOC_DAPM_INPUT("IN2_L"), + + /* dapm widget (platform domain) name for LINE2R */ + SND_SOC_DAPM_INPUT("IN2_R"), + + /* dapm widget (platform domain) name for LINE3L */ + SND_SOC_DAPM_INPUT("IN3_L"), + + /* dapm widget (platform domain) name for LINE3R */ + SND_SOC_DAPM_INPUT("IN3_R"), + + /* DAPM widget for MICBIAS power control */ + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC3256_MICBIAS_CTRL, 6, 0), + + /* Left DMIC Input Widget */ + SND_SOC_DAPM_INPUT("Left DMIC"), + /* Right DMIC Input Widget */ + SND_SOC_DAPM_INPUT("Right DMIC"), + + /* Left Channel ADC Route */ + SND_SOC_DAPM_MUX("Left ADC Route", SND_SOC_NOPM, 0, 0, &adcl_mux), + /* Right Channel ADC Route */ + SND_SOC_DAPM_MUX("Right ADC Route", SND_SOC_NOPM, 0, 0, &adcr_mux), + + /* Supply widget for PLL */ + SND_SOC_DAPM_SUPPLY("PLLCLK", AIC3256_CLK_REG_2, 7, 0, + pll_power_on_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Supply widget for CODEC_CLK_IN */ + SND_SOC_DAPM_SUPPLY("CODEC_CLK_IN", SND_SOC_NOPM, 0, 0, NULL, 0), + /* Supply widget for NDAC divider */ + SND_SOC_DAPM_SUPPLY("NDAC_DIV", AIC3256_NDAC_CLK_REG_6, 7, 0, NULL, 0), + /* Supply widget for MDAC divider */ + SND_SOC_DAPM_SUPPLY("MDAC_DIV", AIC3256_MDAC_CLK_REG_7, 7, 0, NULL, 0), + /* Supply widget for NADC divider */ + SND_SOC_DAPM_SUPPLY("NADC_DIV", AIC3256_NADC_CLK_REG_8, 7, 0, NULL, 0), + /* Supply widget for MADC divider */ + SND_SOC_DAPM_SUPPLY("MADC_DIV", AIC3256_MADC_CLK_REG_9, 7, 0, NULL, 0), + /* Supply widget for Bit Clock divider */ + SND_SOC_DAPM_SUPPLY("BCLK_N_DIV", AIC3256_CLK_REG_11, 7, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route aic325x_dapm_routes[] = { + + /* PLL routing */ + {"CODEC_CLK_IN", NULL, "PLLCLK"}, + {"NDAC_DIV", NULL, "CODEC_CLK_IN"}, + {"NADC_DIV", NULL, "CODEC_CLK_IN"}, + {"MDAC_DIV", NULL, "NDAC_DIV"}, + {"MADC_DIV", NULL, "NADC_DIV"}, + {"BCLK_N_DIV", NULL, "MADC_DIV"}, + {"BCLK_N_DIV", NULL, "MDAC_DIV"}, + + /* Clock routing for ADC */ + {"Left ADC", NULL, "MADC_DIV"}, + {"Right ADC", NULL, "MADC_DIV"}, + + /* Clock routing for DAC */ + {"Left DAC", NULL, "MDAC_DIV" }, + {"Right DAC", NULL, "MDAC_DIV"}, + + /* Left Headphone Output */ + {"HPL Output Mixer", "L_DAC switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L switch", "IN1_L"}, + {"HPL Output Mixer", "MAL switch", "MAL PGA"}, + {"HPL Output Mixer", "MAR switch", "MAR PGA"}, + + /* Right Headphone Output */ + {"HPR Output Mixer", "R_DAC switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R switch", "IN1_R"}, + {"HPR Output Mixer", "MAR switch", "MAR PGA"}, + {"HPR Output Mixer", "L_DAC switch", "Left DAC"}, + + /* HP output mixer to HP PGA */ + {"HPL PGA", NULL, "HPL Output Mixer"}, + {"HPR PGA", NULL, "HPR Output Mixer"}, + + /* HP PGA to HP output pins */ + {"HPL", NULL, "HPL PGA"}, + {"HPR", NULL, "HPR PGA"}, + + /* Charge pump to HP PGA */ + {"HPL PGA", NULL, "Charge Pump"}, + {"HPR PGA", NULL, "Charge Pump"}, + + /* Left Line-out Output */ + {"LOL Output Mixer", "L_DAC switch", "Left DAC"}, + {"LOL Output Mixer", "MAL switch", "MAL PGA"}, + {"LOL Output Mixer", "R_DAC switch", "Right DAC"}, + {"LOL Output Mixer", "LOR switch", "LOR PGA"}, + + /* Right Line-out Output */ + {"LOR Output Mixer", "R_DAC switch", "Right DAC"}, + {"LOR Output Mixer", "MAR switch", "MAR PGA"}, + + {"LOL PGA", NULL, "LOL Output Mixer"}, + {"LOR PGA", NULL, "LOR Output Mixer"}, + + {"LOL", NULL, "LOL PGA"}, + {"LOR", NULL, "LOR PGA"}, + + /* ADC portions */ + /* Left Positive PGA input */ + {"Left Input Mixer", "IN1_L switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L switch", "IN3_L"}, + {"Left Input Mixer", "IN1_R switch", "IN1_R"}, + /* Left Negative PGA input */ + {"Left Input Mixer", "IN2_R switch", "IN2_R"}, + {"Left Input Mixer", "IN3_R switch", "IN3_R"}, + {"Left Input Mixer", "CM1L switch", "CM1L"}, + {"Left Input Mixer", "CM2L switch", "CM2L"}, + /* Right Positive PGA Input */ + {"Right Input Mixer", "IN1_R switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R switch", "IN3_R"}, + {"Right Input Mixer", "IN2_L switch", "IN2_L"}, + /* Right Negative PGA Input */ + {"Right Input Mixer", "IN1_L switch", "IN1_L"}, + {"Right Input Mixer", "IN3_L switch", "IN3_L"}, + {"Right Input Mixer", "CM1R switch", "CM1R"}, + {"Right Input Mixer", "CM2R switch", "CM2R"}, + + {"CM1L", NULL, "CM"}, + {"CM2L", NULL, "CM"}, + {"CM1R", NULL, "CM"}, + {"CM1R", NULL, "CM"}, + + /* Left MicPGA */ + {"Left MicPGA", NULL, "Left Input Mixer"}, + + /* Right MicPGA */ + {"Right MicPGA", NULL, "Right Input Mixer"}, + + {"Left ADC", NULL, "Left MicPGA"}, + {"Right ADC", NULL, "Right MicPGA"}, + + {"Left ADC Route", "Analog", "Left MicPGA"}, + {"Left ADC Route", "Digital", "Left DMIC"}, + + /* Selection of Digital/Analog Mic */ + {"Right ADC Route", "Analog", "Right MicPGA"}, + {"Right ADC Route", "Digital", "Right DMIC"}, + + {"Left ADC", NULL, "Left ADC Route"}, + {"Right ADC", NULL, "Right ADC Route"}, + + {"MAL PGA", NULL, "Left MicPGA"}, + {"MAR PGA", NULL, "Right MicPGA"}, +}; + + + + +/* aic3256_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. + */ +void aic3256_firmware_load(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct aic325x_priv *private_ds = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + aic3xxx_cfw_lock(private_ds->cfw_p, 1); /* take the lock */ + if (private_ds->cur_fw != NULL) + release_firmware(private_ds->cur_fw); + private_ds->cur_fw = NULL; + + if (fw != NULL) { + dev_dbg(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 { + dev_err(codec->dev, "Codec Firmware failed\n"); + ret = -1; + } + aic3xxx_cfw_lock(private_ds->cfw_p, 0); /* release the lock */ + if (ret >= 0) { + /* init function for transition */ + aic3xxx_cfw_transition(private_ds->cfw_p, "INIT"); + aic3xxx_cfw_add_modes(codec, private_ds->cfw_p); + aic3xxx_cfw_add_controls(codec, private_ds->cfw_p); + aic3xxx_cfw_setmode_cfg(private_ds->cfw_p, 0, 0); + } +} + +enum headset_accessory_state { + BIT_NO_ACCESSORY = 0, + BIT_HEADSET = (1 << 0), + BIT_HEADPHONE = (1 << 1), +}; +/** + * aic3256_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 aic3256_hs_jack_report(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) +{ + struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(codec); + int status, state = 0, switch_state = BIT_NO_ACCESSORY; + int debounce_bits, debounce_time_ms; + + /* Sync status */ + status = snd_soc_update_bits(codec, AIC3256_HEADSET_DETECT, + AIC3256_HEADSET_IN_MASK, + AIC3256_HEADSET_DISABLE); + mdelay(1); + status = snd_soc_update_bits(codec, AIC3256_HEADSET_DETECT, + AIC3256_HEADSET_IN_MASK, + AIC3256_HEADSET_ENABLE); + state = aic325x_reg_read(codec->control_data, + AIC3256_HEADSET_DETECT); + + debounce_bits = (snd_soc_read(codec, AIC3256_HEADSET_DETECT) + & 0x1c) >> 2; + debounce_time_ms = (debounce_bits == 0) ? 16 : + ((debounce_bits == 1) ? 32 : + ((debounce_bits == 2) ? 64 : + ((debounce_bits == 3) ? 128 : + ((debounce_bits == 4) ? 256 : 512)))); + mdelay(2*debounce_time_ms); + mdelay(50); + status = snd_soc_read(codec, AIC3256_HEADSET_DETECT); + /* We will check only stereo MIC and headphone */ + switch (status & AIC3256_JACK_TYPE_MASK) { + case AIC3256_JACK_WITH_MIC: + state |= SND_JACK_HEADSET; + break; + case AIC3256_JACK_WITHOUT_MIC: + state |= SND_JACK_HEADPHONE; + break; + default: + break; + } + 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; + + if (&aic3256->hs_jack.sdev) + switch_set_state(&aic3256->hs_jack.sdev, switch_state); + +} + +/** + * aic3256_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 aic3256_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) +{ + struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(codec); + struct aic3256_jack_data *hs_jack = &aic3256->hs_jack; + + hs_jack->jack = jack; + hs_jack->report = report; + aic3256_hs_jack_report(codec, hs_jack->jack, hs_jack->report); +} +EXPORT_SYMBOL_GPL(aic3256_hs_jack_detect); + +/** + * aic3256_accessory_work: Finished bottom half work from headphone jack + * insertion interupt + * @work: pionter variable to work_struct which is maintaining work queqe + * +*/ +static void aic3256_accessory_work(struct work_struct *work) +{ + struct aic325x_priv *aic3256 = container_of(work, + struct aic325x_priv, + delayed_work.work); + struct snd_soc_codec *codec = aic3256->codec; + struct aic3256_jack_data *hs_jack = &aic3256->hs_jack; + + /* GPIO Input/Output control register is configured to INT1 Output */ + + snd_soc_update_bits(codec, AIC3256_GPIO_CTRL, + AIC3256_GPIO_D6_D2, 0x0); + if (aic3256->hs_half_insert_count < 3) { + aic3256->hs_half_insert_count++; + aic3256_hs_jack_report(codec, hs_jack->jack, hs_jack->report); + /* Schedule the delayed work again after 250 ms */ + queue_delayed_work(aic3256->workqueue, &aic3256->delayed_work, + msecs_to_jiffies(250)); + } + snd_soc_update_bits(codec, AIC3256_GPIO_CTRL, AIC3256_GPIO_D6_D2, + 0x14); +} + +/** + * aic3256_audio_handler: audio interrupt handler called + * when interupt is generated + * @irq: provides interupt number which is assigned by aic3256_request_irq, + * @data having information of data passed by aic3256_request_irq last arg, + * + * Return IRQ_HANDLED(means interupt handeled successfully) +*/ +static irqreturn_t aic3256_audio_handler(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(codec); + /* cancel any pending delayed work.*/ + cancel_delayed_work(&aic3256->delayed_work); + + aic3256->hs_half_insert_count = 0; + queue_delayed_work(aic3256->workqueue, &aic3256->delayed_work, + msecs_to_jiffies(200)); + return IRQ_HANDLED; +} +/** + * aic325x_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 aic325x_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + int ret_wbits = 0; + + if (w->shift == 5) { + /* HPL */ + snd_soc_update_bits(codec, AIC3256_CM_CTRL_REG, + GCHP_HPL_STATUS, + GCHP_HPL_STATUS); + } + if (w->shift == 4) { + /* HPR */ + snd_soc_update_bits(codec, AIC3256_CM_CTRL_REG, + GCHP_HPL_STATUS, 0x0); + } + /* Wait for HP power on */ + if (event & SND_SOC_DAPM_POST_PMU) { + + ret_wbits = aic325x_wait_bits(codec->control_data, + AIC3256_PWR_CTRL_REG, + HP_DRIVER_BUSY_MASK, HP_DRIVER_BUSY_MASK, + AIC3256_TIME_DELAY, AIC3256_DELAY_COUNTER); + if (!ret_wbits) + dev_err(codec->dev, "HP %s power-up timedout\n", \ + (w->shift == 5) ? "Left" : "Right"); + } else if (event & SND_SOC_DAPM_POST_PMD) { + ret_wbits = aic325x_wait_bits(codec->control_data, + AIC3256_PWR_CTRL_REG, HP_DRIVER_BUSY_MASK, 0x0, + AIC3256_TIME_DELAY, AIC3256_DELAY_COUNTER); + if (!ret_wbits) + dev_err(codec->dev, "HP %s power- down timedout\n", \ + (w->shift == 5) ? "Left" : "Right"); + } + return 0; + +} + +/** + * 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 aic3256_ops_cfw2reg(unsigned int reg) +{ + union cfw_register *c = (union cfw_register *) ® + union aic325x_reg_union mreg; + + mreg.aic325x_register.offset = c->offset; + mreg.aic325x_register.page = c->page; + mreg.aic325x_register.reserved = 0; + + return mreg.aic325x_register_int; +} + +static int aic3256_ops_reg_read(struct snd_soc_codec *codec, unsigned int reg) +{ + return aic325x_reg_read(codec->control_data, aic3256_ops_cfw2reg(reg)); +} + +static int aic3256_ops_reg_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned char val) +{ + return aic325x_reg_write(codec->control_data, + aic3256_ops_cfw2reg(reg), val); +} + +static int aic3256_ops_set_bits(struct snd_soc_codec *codec, unsigned int reg, + unsigned char mask, unsigned char val) +{ + return aic325x_set_bits(codec->control_data, + aic3256_ops_cfw2reg(reg), mask, val); + +} + +static int aic3256_ops_bulk_read(struct snd_soc_codec *codec, unsigned int reg, + int count, u8 *buf) +{ + return aic325x_bulk_read(codec->control_data, + aic3256_ops_cfw2reg(reg), count, buf); +} + +static int aic3256_ops_bulk_write(struct snd_soc_codec *codec, unsigned int reg, + int count, const u8 *buf) +{ + return aic325x_bulk_write(codec->control_data, + aic3256_ops_cfw2reg(reg), count, buf); +} + + +/* +******************************************************************************** +Function Name : aic3256_ops_dlock_lock +Argument : pointer argument to the codec +Return value : Integer +Purpose : To Read the run state of the DAC and ADC +by reading the codec and returning the run state + +Run state Bit format + +------------------------------------------------------ +D31|..........| D7 | D6| D5 | D4 | D3 | D2 | D1 | D0 | +R R R LADC RADC R R LDAC RDAC +------------------------------------------------------ + +******************************************************************************** +*/ +int aic3256_ops_lock(struct snd_soc_codec *codec) +{ + mutex_lock(&codec->mutex); + /* Reading the run state of adc and dac */ + return aic3256_get_runstate(codec); +} + +/* +******************************************************************************* +Function name : aic3256_ops_dlock_unlock +Argument : pointer argument to the codec +Return Value : integer returning 0 +Purpose : To unlock the mutex acqiured for reading +run state of the codec +******************************************************************************** +*/ +int aic3256_ops_unlock(struct snd_soc_codec *codec) +{ + /*Releasing the lock of mutex */ + mutex_unlock(&codec->mutex); + return 0; +} +/* +******************************************************************************* +Function Name : aic3256_ops_dlock_stop +Argument : 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 +******************************************************************************** +*/ +int aic3256_ops_stop(struct snd_soc_codec *codec, int mask) +{ + int run_state = 0; + + run_state = aic3256_get_runstate(codec); + + if (mask & AIC3XXX_COPS_MDSP_A) /* power-down ADCs */ + aic325x_set_bits(codec->control_data, + AIC3256_ADC_DATAPATH_SETUP, 0xC0, 0); + + if (mask & AIC3XXX_COPS_MDSP_D) /* power-down DACs */ + aic325x_set_bits(codec->control_data, + AIC3256_DAC_DATAPATH_SETUP, 0xC0, 0); + + if ((mask & AIC3XXX_COPS_MDSP_A) && + !aic325x_wait_bits(codec->control_data, AIC3256_ADC_FLAG, + AIC3256_ADC_POWER_MASK, + 0, AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER)) + goto err; + + if ((mask & AIC3XXX_COPS_MDSP_D) && + !aic325x_wait_bits(codec->control_data, AIC3256_DAC_FLAG, + AIC3256_DAC_POWER_MASK, 0, + AIC3256_TIME_DELAY, + AIC3256_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; + +} + +/* +**************************************************************************** +Function name : aic3256_ops_dlock_restore +Argument : pointer argument to the codec, run_state +Return Value : integer returning 0 +Purpose : To unlock the mutex acqiured for reading +run state of the codec and to restore the states of the dsp +****************************************************************************** +*/ +static int aic3256_ops_restore(struct snd_soc_codec *codec, int run_state) +{ + int sync_state; + + /* This is for read the sync mode register state */ + sync_state = aic325x_reg_read(codec->control_data, AIC3256_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)) + aic3256_restart_dsps_sync(codec, run_state); + else + aic3256_dsp_pwrup(codec, run_state); + + return 0; +} +/** + * aic3256_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 aic3256_ops_adaptivebuffer_swap(struct snd_soc_codec *codec, int mask) +{ + const int sbuf[][2] = { + { AIC3XXX_ABUF_MDSP_A, AIC3256_ADC_ADAPTIVE_CRAM_REG }, + { AIC3XXX_ABUF_MDSP_D1, AIC3256_DAC_ADAPTIVE_CRAM_REG}, + /* { AIC3XXX_ABUF_MDSP_D2, AIC3256_DAC_ADAPTIVE_BANK2_REG }, */ + }; + int i; + + for (i = 0; i < sizeof(sbuf)/sizeof(sbuf[0]); ++i) { + if (!(mask & sbuf[i][0])) + continue; + aic325x_set_bits(codec->control_data, sbuf[i][1], 0x1, 0x1); + if (!aic325x_wait_bits(codec->control_data, + sbuf[i][1], 0x1, 0, 15, 1)) + goto err; + } + return 0; +err: + dev_err(codec->dev, "miniDSP buffer swap failure at [%s:%d]", + __FILE__, __LINE__); + return -EINVAL; +} + +/***************************************************************************** +Function name : aic3256_get_runstate +Argument : pointer argument to the codec +Return Value : integer returning the runstate +Purpose : To read the current state of the dac's and adc's + ******************************************************************************/ + +static int aic3256_get_runstate(struct snd_soc_codec *codec) +{ + unsigned int dac, adc; + /* Read the run state */ + dac = aic325x_reg_read(codec->control_data, AIC3256_DAC_FLAG); + adc = aic325x_reg_read(codec->control_data, AIC3256_ADC_FLAG); + + return (((adc>>6)&1)<<5) | + (((adc>>2)&1)<<4) | + (((dac>>7)&1)<<1) | + (((dac>>3)&1)<<0); +} + +/***************************************************************************** +Function name : aic3256_dsp_pwrdwn_status +Argument : pointer argument to the codec , cur_state of dac's and adc's +Return Value : integer returning 0 +Purpose : To read the status of dsp's + ******************************************************************************/ + +int aic3256_dsp_pwrdwn_status( + struct snd_soc_codec *codec /* ptr to the priv data structure */ + ) +{ + + aic325x_set_bits(codec->control_data, AIC3256_ADC_DATAPATH_SETUP, + 0XC0, 0); + aic325x_set_bits(codec->control_data, AIC3256_DAC_DATAPATH_SETUP, + 0XC0, 0); + + if (!aic325x_wait_bits(codec->control_data, AIC3256_ADC_FLAG, + AIC3256_ADC_POWER_MASK, 0, AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER)) + goto err; + + if (!aic325x_wait_bits(codec->control_data, AIC3256_DAC_FLAG, + AIC3256_DAC_POWER_MASK, 0, AIC3256_TIME_DELAY, + AIC3256_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 aic3256_dsp_pwrup(struct snd_soc_codec *codec, int state) +{ + 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 |= AIC3256_LADC_POWER_MASK; + } + if (state & AIC3XXX_COPS_MDSP_A_R) { + adc_reg_mask |= 0x40; + adc_power_mask |= AIC3256_RADC_POWER_MASK; + } + + if (state & AIC3XXX_COPS_MDSP_A) + aic325x_set_bits(codec->control_data, + AIC3256_ADC_DATAPATH_SETUP, + 0XC0, adc_reg_mask); + + if (state & AIC3XXX_COPS_MDSP_D_L) { + dac_reg_mask |= 0x80; + dac_power_mask |= AIC3256_LDAC_POWER_MASK; + } + + if (state & AIC3XXX_COPS_MDSP_D_R) { + dac_reg_mask |= 0x40; + dac_power_mask |= AIC3256_RDAC_POWER_MASK; + } + + if (state & AIC3XXX_COPS_MDSP_D) + aic325x_set_bits(codec->control_data, + AIC3256_DAC_DATAPATH_SETUP, + 0XC0, dac_reg_mask); + + if (state & AIC3XXX_COPS_MDSP_A) { + ret_wbits = aic325x_wait_bits(codec->control_data, + AIC3256_ADC_FLAG, AIC3256_ADC_POWER_MASK, + adc_power_mask, AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER); + if (!ret_wbits) + dev_err(codec->dev, "ADC Power down timedout\n"); + } + + if (state & AIC3XXX_COPS_MDSP_D) { + ret_wbits = aic325x_wait_bits(codec->control_data, + AIC3256_DAC_FLAG, AIC3256_DAC_POWER_MASK, + dac_power_mask, AIC3256_TIME_DELAY, + AIC3256_DELAY_COUNTER); + if (!ret_wbits) + dev_err(codec->dev, "ADC Power down timedout\n"); + } + + return 0; +} + +static int aic3256_restart_dsps_sync(struct snd_soc_codec *codec, int run_state) +{ + + aic3256_dsp_pwrdwn_status(codec); + aic3256_dsp_pwrup(codec, run_state); + return 0; +} + +static const struct aic3xxx_codec_ops aic3256_cfw_codec_ops = { + .reg_read = aic3256_ops_reg_read, + .reg_write = aic3256_ops_reg_write, + .set_bits = aic3256_ops_set_bits, + .bulk_read = aic3256_ops_bulk_read, + .bulk_write = aic3256_ops_bulk_write, + .lock = aic3256_ops_lock, + .unlock = aic3256_ops_unlock, + .stop = aic3256_ops_stop, + .restore = aic3256_ops_restore, + .bswap = aic3256_ops_adaptivebuffer_swap, +}; + +/** + * aic325x_codec_read: provide read api to read aic3256 registe space + * @codec: pointer variable to codec having codec information, + * @reg: register address, + * + * Return: Return value will be value read. + */ +unsigned int aic325x_codec_read(struct snd_soc_codec *codec, unsigned int reg) +{ + u8 value; + + union aic325x_reg_union *aic_reg = (union aic325x_reg_union *)® + value = aic325x_reg_read(codec->control_data, reg); + dev_dbg(codec->dev, "p %d, r 30 %x %x\n", + aic_reg->aic325x_register.page, + aic_reg->aic325x_register.offset, value); + return value; +} + +/** + * aic325x_codec_write: provide write api to write at aic3256 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 aic325x_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + union aic325x_reg_union *aic_reg = (union aic325x_reg_union *)® + dev_dbg(codec->dev, "p %d, w 30 %x %x\n", + aic_reg->aic325x_register.page, + aic_reg->aic325x_register.offset, value); + return aic325x_reg_write(codec->control_data, reg, value); +} + +/** + * aic325x_hw_params: This function is to set the hardware parameters + * for AIC3256. + * 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. + */ +static int aic325x_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 aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); + + u8 data = 0; + + /* Setting the playback status */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + aic325x->playback_stream = 1; + else if ((substream->stream != + SNDRV_PCM_STREAM_PLAYBACK) && (codec->active < 2)) + aic325x->playback_stream = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + aic325x->record_stream = 1; + else if ((substream->stream != SNDRV_PCM_STREAM_CAPTURE) && \ + (codec->active < 2)) + aic325x->record_stream = 0; + + + dprintk(KERN_INFO "Function: %s, %d\n", __func__, aic325x->sysclk); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + 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; + } + + snd_soc_update_bits(codec, AIC3256_INTERFACE_SET_REG_1, + AIC3256_INTERFACE_REG_MASK, data); + + return 0; +} + +/** + * aic325x_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 aic325x_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); + + if (mute) { + if (aic325x->playback_stream) { + snd_soc_update_bits(codec, AIC3256_DAC_MUTE_CTRL_REG, + AIC3256_DAC_MUTE_MASK, + AIC3256_DAC_MUTE_ON); + } + if (aic325x->record_stream) { + snd_soc_update_bits(codec, AIC3256_ADC_FGA, + AIC3256_ADC_MUTE_MASK, + AIC3256_ADC_MUTE_ON); + } + } else { + if (aic325x->playback_stream) { + snd_soc_update_bits(codec, AIC3256_DAC_MUTE_CTRL_REG, + AIC3256_DAC_MUTE_MASK, + (~AIC3256_DAC_MUTE_ON)); + } + if (aic325x->record_stream) { + snd_soc_update_bits(codec, AIC3256_ADC_FGA, + AIC3256_ADC_MUTE_MASK, + (~AIC3256_ADC_MUTE_ON)); + } + } + dprintk(KERN_INFO "Function: %s Exiting\n", __func__); + + return 0; +} + +/** + * aic325x_set_dai_fmt: This function is to set the DAI format + * @codec_dai: pointer to dai Holds runtime data for a DAI, + * @fmt: asi format info, + * + * return: return 0 on success. + */ +static int aic325x_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec; + struct aic325x_priv *aic325x; + + u8 iface_reg1 = 0; + u8 iface_reg3 = 0; + u8 dsp_a_val = 0; + + codec = codec_dai->codec; + aic325x = snd_soc_codec_get_drvdata(codec); + + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic325x->master = 1; + iface_reg1 |= AIC3256_BIT_CLK_MASTER | AIC3256_WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic325x->master = 0; + iface_reg1 &= ~(AIC3256_BIT_CLK_MASTER | + AIC3256_WORD_CLK_MASTER); + break; + case SND_SOC_DAIFMT_CBS_CFM: + aic325x->master = 0; + iface_reg1 |= AIC3256_BIT_CLK_MASTER; + iface_reg1 &= ~(AIC3256_WORD_CLK_MASTER); + break; + default: + printk(KERN_INFO "Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + dsp_a_val = 0x1; + 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: + iface_reg3 |= BCLK_INV_MASK; + break; + default: + return -EINVAL; + } + iface_reg1 |= (AIC325x_DSP_MODE << CLK_REG_3_SHIFT); + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg1 |= (AIC325x_RIGHT_JUSTIFIED_MODE << CLK_REG_3_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg1 |= (AIC325x_LEFT_JUSTIFIED_MODE << CLK_REG_3_SHIFT); + break; + default: + printk(KERN_INFO "Invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, AIC3256_INTERFACE_SET_REG_1, + INTERFACE_REG1_DATA_TYPE_MASK | + INTERFACE_REG1_MASTER_MASK, + iface_reg1); + snd_soc_update_bits(codec, AIC3256_INTERFACE_SET_REG_2, + INTERFACE_REG2_MASK, dsp_a_val); + snd_soc_update_bits(codec, AIC3256_INTERFACE_SET_REG_3, + INTERFACE_REG3_MASK, iface_reg3); + return 0; +} + +/** + * aic325x_dai_set_pll: This function is to Set pll for aic3256 codec dai + * @dai: ponter to dai Holds runtime data for a DAI, $ + * @pll_id: integer pll_id + * @fin: frequency in, + * @fout: Frequency out, + * + * Return: return 0 on success +*/ +static int aic325x_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int Fin, unsigned int Fout) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = aic3xxx_cfw_set_pll(aic3256->cfw_p, dai->id); + return ret; +} + +/** + * + * aic325x_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 aic325x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic325x_priv *aic325x = snd_soc_codec_get_drvdata(codec); + + switch (level) { + /* full On */ + case SND_SOC_BIAS_ON: + /* all power is driven by DAPM system */ + if (aic325x->master == 1) { + snd_soc_update_bits(codec, AIC3256_CLK_REG_11, + BCLK_DIV_POWER_MASK, 0x80); + } + break; + /* partial On */ + case SND_SOC_BIAS_PREPARE: + if (codec->dapm.bias_level == SND_SOC_BIAS_ON) { + snd_soc_update_bits(codec, AIC3256_CLK_REG_11, + BCLK_DIV_POWER_MASK, 0); + } + break; + /* Off, with power */ + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, AIC3256_REF_PWR_UP_CONF_REG, + AIC3256_REF_PWR_UP_MASK, + AIC3256_FORCED_REF_PWR_UP); + /* + * all power is driven by DAPM system, + * so output power is safe if bypass was set + */ + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_update_bits(codec, AIC3256_POW_CFG, + AIC3256_AVDD_CONNECTED_TO_DVDD_MASK, + AIC3256_DISABLE_AVDD_TO_DVDD); + snd_soc_update_bits(codec, AIC3256_PWR_CTRL_REG, + AIC3256_ANALOG_BLOCK_POWER_CONTROL_MASK, + AIC3256_ENABLE_ANALOG_BLOCK); + } + break; + /* Off, without power */ + case SND_SOC_BIAS_OFF: + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + snd_soc_update_bits(codec, AIC3256_REF_PWR_UP_CONF_REG, + AIC3256_REF_PWR_UP_MASK, + AIC3256_AUTO_REF_PWR_UP); + snd_soc_update_bits(codec, AIC3256_PWR_CTRL_REG, + AIC3256_ANALOG_BLOCK_POWER_CONTROL_MASK, + AIC3256_DISABLE_ANALOG_BLOCK); + snd_soc_update_bits(codec, AIC3256_POW_CFG, + AIC3256_AVDD_CONNECTED_TO_DVDD_MASK, + AIC3256_ENABLE_AVDD_TO_DVDD); + } + /* force all power off */ + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +/** + * + * aic325x_suspend; This function is to suspend the AIC3256 driver. + * @codec: pointer variable to codec having informaton related to codec, + * + * Return: Return 0 on success. + */ +static int aic325x_suspend(struct snd_soc_codec *codec) +{ + dprintk(KERN_INFO "Function: %s\n", __func__); + aic325x_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + + +/** + * aic325x_resume: This function is to resume the AIC3256 driver + * from off state to standby + * @codec: pointer variable to codec having informaton related to codec, + * + * Return: Return 0 on success. + */ +static int aic325x_resume(struct snd_soc_codec *codec) +{ + aic325x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +/** + * aic325x_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 aic325x_probe(struct snd_soc_codec *codec) +{ + int ret = 0; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct aic325x_priv *aic325x; + struct aic325x *control; + struct aic3256_jack_data *jack; + + if (codec == NULL) + dev_err(codec->dev, "codec pointer is NULL\n"); + + codec->control_data = dev_get_drvdata(codec->dev->parent); + + control = codec->control_data; + + aic325x = kzalloc(sizeof(struct aic325x_priv), GFP_KERNEL); + if (aic325x == NULL) + return -ENOMEM; + + snd_soc_codec_set_drvdata(codec, aic325x); + aic325x->pdata = dev_get_platdata(codec->dev->parent); + aic325x->codec = codec; + + aic325x->cur_fw = NULL; + + aic325x->cfw_p = &(aic325x->cfw_ps); + + aic3xxx_cfw_init(aic325x->cfw_p, &aic3256_cfw_codec_ops, + aic325x->codec); + + snd_soc_dapm_new_controls(dapm, aic325x_dapm_widgets, + ARRAY_SIZE(aic325x_dapm_widgets)); + + ret = snd_soc_dapm_add_routes(dapm, aic325x_dapm_routes, + ARRAY_SIZE(aic325x_dapm_routes)); + if (!ret) + dprintk("#Completed adding DAPM routes = %d\n", + ARRAY_SIZE(aic325x_dapm_routes)); + + /* firmware load */ + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "tlv320aic3206_fw_v1.bin", + codec->dev, GFP_KERNEL, codec, + aic3256_firmware_load); + + return ret; + +} + +/* + *---------------------------------------------------------------------------- + * Function : aic325x_remove + * Purpose : to remove aic325x soc device + * + *---------------------------------------------------------------------------- + */ +static int aic325x_remove(struct snd_soc_codec *codec) +{ + /* power down chip */ + struct aic325x_priv *aic3256 = snd_soc_codec_get_drvdata(codec); + + aic325x_set_bias_level(codec, SND_SOC_BIAS_OFF); + if (aic3256->cur_fw != NULL) + release_firmware(aic3256->cur_fw); + + kfree(aic3256); + return 0; +} + + +static struct snd_soc_codec_driver soc_codec_driver_aic325x = { + .probe = aic325x_probe, + .remove = aic325x_remove, + .suspend = aic325x_suspend, + .resume = aic325x_resume, + .read = aic325x_codec_read, + .write = aic325x_codec_write, + .set_bias_level = aic325x_set_bias_level, + .controls = aic325x_snd_controls , + .num_controls = ARRAY_SIZE(aic325x_snd_controls), + .reg_cache_size = 0, + .reg_word_size = sizeof(u8), + .reg_cache_default = NULL, +}; + +static int aic3256_probe(struct platform_device *pdev) +{ + int ret; + ret = snd_soc_register_codec( + &pdev->dev, + &soc_codec_driver_aic325x, + tlv320aic325x_dai_driver, + ARRAY_SIZE(tlv320aic325x_dai_driver) + ); + return ret; +} + +static int aic3256_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver aic325x_codec_driver = { + .driver = { + .name = "tlv320aic325x-codec", + .owner = THIS_MODULE, + }, + .probe = aic3256_probe, + .remove = __devexit_p(aic3256_remove), +}; + +/* + *---------------------------------------------------------------------------- + * Function : tlv320aic3256_modinit + * Purpose : module init function. First function to run. + * + *---------------------------------------------------------------------------- + */ +static int __init tlv320aic325x_modinit(void) +{ + return platform_driver_register(&aic325x_codec_driver); +} +module_init(tlv320aic325x_modinit); + +/* + *---------------------------------------------------------------------------- + * Function : tlv320aic3256_exit + * Purpose : module init function. First function to run. + * + *---------------------------------------------------------------------------- + */ +static void __exit tlv320aic325x_exit(void) +{ + platform_driver_unregister(&aic325x_codec_driver); +} + +module_exit(tlv320aic325x_exit); + + +MODULE_ALIAS("platform:tlv320aic325x-codec"); +MODULE_DESCRIPTION("ASoC TLV320AIC325x codec driver"); +MODULE_AUTHOR("Aravindan Muthukumar"); +MODULE_AUTHOR("Suresh Pm"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic325x.h b/sound/soc/codecs/tlv320aic325x.h new file mode 100644 index 000000000000..62bbd2220515 --- /dev/null +++ b/sound/soc/codecs/tlv320aic325x.h @@ -0,0 +1,361 @@ +/* + * linux/sound/soc/codecs/tlv320aic325x.h + * + * + * 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. + * + * History: + * Rev 1.1 Added More ENUM Macros 18-01-2011 + * + */ + +#ifndef _TLV320AIC325x_H +#define _TLV320AIC325x_H + +#include "./aic3xxx/aic3xxx_cfw.h" +#include "./aic3xxx/aic3xxx_cfw_ops.h" +#define AUDIO_NAME "aic325x" +#define AIC325x_VERSION "1.1" + +/* #define AIC3256_CODEC_SUPPORT 1 */ + +/* Enable slave / master mode for codec */ +#define AIC325x_MCBSP_SLAVE +/*#undef AIC325x_MCBSP_SLAVE*/ +/* Macro enables or disables support for miniDSP in the driver */ + +/*#undef CONFIG_MINI_DSP*/ + +/* Enable headset detection */ +/*#define HEADSET_DETECTION*/ +#undef HEADSET_DETECTION + +/* Macro enables or disables AIC3xxx TiLoad Support */ +#define AIC3256_TiLoad +/* #undef AIC3xxx_TiLoad */ +/* Enable register caching on write */ +#define EN_REG_CACHE + +/* Flag to Select OMAP PANDA Board */ +#define CONFIG_SND_SOC_OMAP_AIC3256 + +/* AIC325x supported sample rate are 8k to 192k */ +#define AIC325x_RATES SNDRV_PCM_RATE_8000_192000 + +/* AIC325x supports the word formats 16bits, 20bits, 24bits and 32 bits */ +#define AIC325x_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define AIC325x_FREQ_12000000 12000000 +#define AIC325x_FREQ_12288000 12288000 +#define AIC325x_FREQ_24000000 24000000 +#define AIC325x_FREQ_19200000 19200000 +#define AIC325x_FREQ_38400000 38400000 + +/* Audio data word length = 16-bits (default setting) */ +#define AIC325x_WORD_LEN_16BITS 0x00 +#define AIC325x_WORD_LEN_20BITS 0x01 +#define AIC325x_WORD_LEN_24BITS 0x02 +#define AIC325x_WORD_LEN_32BITS 0x03 + +/* #define AIC325x_8BITS_MASK 0XFF */ +/* sink: name of target widget */ +#define AIC325x_WIDGET_NAME 0 +/* control: mixer control name */ +#define AIC325x_CONTROL_NAME 1 +/* source: name of source name */ +#define AIC325x_SOURCE_NAME 2 + +/* D15..D8 aic325x register offset */ +#define AIC325x_REG_OFFSET_INDEX 0 +/* D7...D0 register data */ +#define AIC325x_REG_DATA_INDEX 1 + +/* Serial data bus uses I2S mode (Default mode) */ +#define AIC325x_I2S_MODE 0x00 +#define AIC325x_DSP_MODE 0x01 +#define AIC325x_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC325x_LEFT_JUSTIFIED_MODE 0x03 + +/* 8 bit mask value */ +#define AIC325x_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 + +/* Mask for Headset detection status */ +#define FLAG_HS_MASKBITS 0x10 + +/* number of codec specific register for configuration */ +#define NO_FEATURE_REGS 2 + +/* AIC325x register space */ +#define AIC325x_CACHEREGNUM 256 + +#endif + + +/* SASKEN :: Moved the registers to tlv320aic3256-registers.h file + as part of the MFD changes */ + +/****************************************************************************/ +#define BIT7 (0x01 << 7) +#define CODEC_CLKIN_MASK 0x03 +#define MCLK_2_CODEC_CLKIN 0x00 +#define PLLCLK_2_CODEC_CLKIN 0x03 +/*Bclk_in selection*/ +#define BDIV_CLKIN_MASK 0x03 +#define DAC_MOD_CLK_2_BDIV_CLKIN 0x01 +#define SOFT_RESET 0x01 +#define PAGE0 0x00 +#define PAGE1 0x01 +#define BIT_CLK_MASTER 0x08 +#define WORD_CLK_MASTER 0x04 +#define BCLK_INV_MASK 0x08 +#define HIGH_PLL (0x01 << 6) +#define ENABLE_PLL BIT7 +#define ENABLE_NDAC BIT7 +#define ENABLE_MDAC BIT7 +#define ENABLE_NADC BIT7 +#define ENABLE_MADC BIT7 +#define ENABLE_BCLK BIT7 +#define ENABLE_DAC (0x03 << 6) +#define LDAC_2_LCHN (0x01 << 4) +#define RDAC_2_RCHN (0x01 << 2) +#define LDAC_CHNL_2_HPL (0x01 << 3) +#define RDAC_CHNL_2_HPR (0x01 << 3) +#define SOFT_STEP_2WCLK (0x01) +#define DAC_MUTE_ON 0x0C +#define ADC_MUTE_ON 0x88 +#define DEFAULT_VOL 0x0 +#define DISABLE_ANALOG (0x01 << 3) +#define LDAC_2_HPL_ROUTEON 0x08 +#define RDAC_2_HPR_ROUTEON 0x08 + +#define HP_DRIVER_BUSY_MASK 0x04 +/* Headphone driver Configuration Register Page 1, Register 125 */ +#define GCHP_ENABLE 0x10 +#define DC_OC_FOR_ALL_COMB 0x03 +#define DC_OC_FOR_PROG_COMB 0x02 + +/* Reference Power-Up configuration register */ +#define REF_PWR_UP_MASK 0x4 +#define AUTO_REF_PWR_UP 0x0 +#define FORCED_REF_PWR_UP 0x4 + +/* Power Configuration register 1 */ +#define WEAK_AVDD_TO_DVDD_DIS 0x8 + +/* Power Configuration register 1 */ +#define ANALOG_BLOCK_POWER_CONTROL_MASK 0x08 +#define ENABLE_ANALOG_BLOCK 0x0 +#define DISABLE_ANALOG_BLOCK 0x8 + +/* Floating input Configuration register P1_R58 */ +#define WEAK_BIAS_INPUTS_MASK 0xFC + +/* Common Mode Control Register */ +#define GCHP_HPL_STATUS 0x4 + +/* Audio Interface Register 3 P0_R29 */ +#define BCLK_WCLK_BUFFER_POWER_CONTROL_MASK 0x4 +#define BCLK_WCLK_BUFFER_ON 0x4 + +/* Power Configuration Register */ +#define AVDD_CONNECTED_TO_DVDD_MASK 0x8 +#define DISABLE_AVDD_TO_DVDD 0x8 +#define ENABLE_AVDD_TO_DVDD 0x0 + + +/* Masks used for updating register bits */ +#define PLL_P_DIV_MASK 0x7F +#define PLL_J_DIV_MASK 0x7F +#define PLL_NDAC_DIV_MASK 0x7F +#define PLL_MDAC_DIV_MASK 0x7F +#define PLL_NADC_DIV_MASK 0x7F +#define PLL_MADC_DIV_MASK 0x7F +#define PLL_BCLK_DIV_MASK 0x7F +#define INTERFACE_REG_MASK 0x7F +#define INTERFACE_REG1_DATA_TYPE_MASK 0xC0 +#define INTERFACE_REG1_MASTER_MASK 0x0C +#define INTERFACE_REG2_MASK 0xFF +#define INTERFACE_REG3_MASK 0x08 + +#define PLL_D_MSB_DIV_MASK 0x3F +#define PLL_D_LSB_DIV_MASK 0xFF +#define PLL_DOSR_MSB_MASK 0x03 +#define PLL_DOSR_LSB_MASK 0xFF +#define PLL_AOSR_DIV_MASK 0xFF + +#define BCLK_DIV_POWER_MASK 0x80 +#define DAC_MUTE_MASK 0x0C +#define ADC_MUTE_MASK 0x88 +#define CODEC_RESET_MASK 0x01 + +#define TIME_DELAY 5 +#define DELAY_COUNTER 100 +/* + ***************************************************************************** + * Structures Definitions + ***************************************************************************** + */ +/* + *---------------------------------------------------------------------------- + * @struct aic325x_setup_data | + * i2c specific data setup for AIC325x. + * @field unsigned short |i2c_address | + * Unsigned short for i2c address. + *---------------------------------------------------------------------------- + */ +struct aic325x_setup_data { + unsigned short i2c_address; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic325x_priv | + * AIC325x 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 AIC325x + * @field u8 | page_no | + * page number. Here, page 0 and page 1 are used. + *---------------------------------------------------------------------------- + */ +struct aic3256_jack_data { + struct snd_soc_jack *jack; + int report; + struct switch_dev sdev; +}; + +struct aic325x_priv { + u32 sysclk; + s32 master; + u8 page_no; + struct aic3256_jack_data hs_jack; + struct workqueue_struct *workqueue; + struct delayed_work delayed_work; + int hs_half_insert_count; + struct snd_soc_codec *codec; + struct i2c_client *control_data; + int irq; + struct snd_soc_jack *headset_jack; + struct mutex io_lock; + int playback_stream; + int record_stream; + + void *pdata; + struct mutex mutex; + struct mutex *cfw_mutex; + struct cfw_state cfw_ps; + struct cfw_state *cfw_p; + struct firmware *cur_fw; + int dsp_runstate; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic325x_configs | + * AIC325x initialization data which has register offset and register + * value. + * @field u16 | reg_offset | + * AIC325x Register offsets required for initialization.. + * @field u8 | reg_val | + * value to set the AIC325x register to initialize the AIC325x. + *---------------------------------------------------------------------------- + */ +struct aic325x_configs { + u8 reg_offset; + u8 reg_val; +}; + +/* + *---------------------------------------------------------------------------- + * @struct aic325x_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 | aic325x_configs | + * configurations for aic325x register value + *---------------------------------------------------------------------------- + */ +struct aic325x_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 aic325x_configs codec_specific_regs[NO_FEATURE_REGS]; +}; + + +void aic3256_hs_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report); + + + +/* + *---------------------------------------------------------------------------- + * @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. + *---------------------------------------------------------------------------- + */ +extern struct snd_soc_dai tlv320aic325x_dai; + +/* + *---------------------------------------------------------------------------- + * @struct snd_soc_codec_device | + * This structure is soc audio codec device sturecute which pointer + * to basic functions aic325x_probe(), aic325x_remove(), + * aic325x_suspend() and aic325x_resume() + * + */ +extern struct snd_soc_codec_device soc_codec_dev_aic325x; + + /* _TLV320AIC325x_H */ |