/* * sound\soc\codec\ac10x.c * (C) Copyright 2014-2017 * Reuuimlla Technology Co., Ltd. * * huangxin * liushaohua * * some simple description for this code * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ac101.h" //#define CONFIG_SWITCH_DETECT_EXTERNAL #ifndef CONFIG_SWITCH_DETECT_EXTERNAL static volatile int reset_flag = 0; static int hook_flag1 = 0; static int hook_flag2 = 0; static int KEY_VOLUME_FLAG = 0; /*1=headphone in slot, else 0*/ static int headphone_state = 0; static volatile int irq_flag = 0; static struct workqueue_struct *switch_detect_queue; static struct workqueue_struct *codec_irq_queue; /* key define */ #define KEY_HEADSETHOOK 226 #define HEADSET_CHECKCOUNT (10) #define HEADSET_CHECKCOUNT_SUM (2) #define SWITCH_DETECT 364 #endif #define PA_CTL 205 #define I2C_BUS 1 /*Default initialize configuration*/ #define SPEAKER_DOUBLE_USED 1 #define D_SPEAKER_VOL 0x1b #define S_SPEAKER_VOL 0x19 #define HEADPHONE_VOL 0x3b #define EARPIECE_VOL 0x1e #define MAINMIC_GAIN 0x4 #define HDSETMIC_GAIN 0x4 #define DMIC_USED 0 #define ADC_DIGITAL_GAIN 0xb0b0 #define AGC_USED 0 #define DRC_USED 1 static bool speaker_double_used = false; static int double_speaker_val = 0; static int single_speaker_val = 0; static int headset_val = 0; static int earpiece_val = 0; static int mainmic_val = 0; static int headsetmic_val = 0; static bool dmic_used = false; static int adc_digital_val = 0; static bool agc_used = false; static bool drc_used = false; #define ac10x_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) #define ac10x_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) enum headphone_mode_u { HEADPHONE_IDLE, FOUR_HEADPHONE_PLUGIN, THREE_HEADPHONE_PLUGIN, }; /*supply voltage*/ static const char *ac10x_supplies[] = { "vcc-avcc", "vcc-io1", "vcc-io2", "vcc-ldoin", "vcc-cpvdd", }; /*struct for ac10x*/ struct ac10x_priv { //struct ac100 *ac10x; struct snd_soc_codec *codec; struct mutex dac_mutex; struct mutex adc_mutex; u8 dac_enable; u8 adc_enable; struct mutex aifclk_mutex; u8 aif1_clken; u8 aif2_clken; u8 aif3_clken; /*voltage supply*/ int num_supplies; struct regulator_bulk_data *supplies; /*headset*/ int virq; /*headset irq*/ struct switch_dev sdev; int state; int check_count; int check_count_sum; int reset_flag; enum headphone_mode_u mode; struct work_struct work; struct work_struct clear_codec_irq; struct work_struct codec_resume; struct work_struct state_work; struct semaphore sem; struct timer_list timer; struct input_dev *key; }; void get_configuration(void) { speaker_double_used = SPEAKER_DOUBLE_USED; double_speaker_val = D_SPEAKER_VOL; single_speaker_val = S_SPEAKER_VOL; headset_val = HEADPHONE_VOL; earpiece_val = EARPIECE_VOL; mainmic_val = MAINMIC_GAIN; headsetmic_val = HDSETMIC_GAIN; dmic_used = DMIC_USED; if (dmic_used) { adc_digital_val = ADC_DIGITAL_GAIN; } agc_used = AGC_USED; drc_used = DRC_USED; } void agc_config(struct snd_soc_codec *codec) { int reg_val; reg_val = snd_soc_read(codec, 0xb4); reg_val |= (0x3<<6); snd_soc_write(codec, 0xb4, reg_val); reg_val = snd_soc_read(codec, 0x84); reg_val &= ~(0x3f<<8); reg_val |= (0x31<<8); snd_soc_write(codec, 0x84, reg_val); reg_val = snd_soc_read(codec, 0x84); reg_val &= ~(0xff<<0); reg_val |= (0x28<<0); snd_soc_write(codec, 0x84, reg_val); reg_val = snd_soc_read(codec, 0x85); reg_val &= ~(0x3f<<8); reg_val |= (0x31<<8); snd_soc_write(codec, 0x85, reg_val); reg_val = snd_soc_read(codec, 0x85); reg_val &= ~(0xff<<0); reg_val |= (0x28<<0); snd_soc_write(codec, 0x85, reg_val); reg_val = snd_soc_read(codec, 0x8a); reg_val &= ~(0x7fff<<0); reg_val |= (0x24<<0); snd_soc_write(codec, 0x8a, reg_val); reg_val = snd_soc_read(codec, 0x8b); reg_val &= ~(0x7fff<<0); reg_val |= (0x2<<0); snd_soc_write(codec, 0x8b, reg_val); reg_val = snd_soc_read(codec, 0x8c); reg_val &= ~(0x7fff<<0); reg_val |= (0x24<<0); snd_soc_write(codec, 0x8c, reg_val); reg_val = snd_soc_read(codec, 0x8d); reg_val &= ~(0x7fff<<0); reg_val |= (0x2<<0); snd_soc_write(codec, 0x8d, reg_val); reg_val = snd_soc_read(codec, 0x8e); reg_val &= ~(0x1f<<8); reg_val |= (0xf<<8); reg_val &= ~(0x1f<<0); reg_val |= (0xf<<0); snd_soc_write(codec, 0x8e, reg_val); reg_val = snd_soc_read(codec, 0x93); reg_val &= ~(0x7ff<<0); reg_val |= (0xfc<<0); snd_soc_write(codec, 0x93, reg_val); snd_soc_write(codec, 0x94, 0xabb3); } void drc_config(struct snd_soc_codec *codec) { int reg_val; reg_val = snd_soc_read(codec, 0xa3); reg_val &= ~(0x7ff<<0); reg_val |= 1<<0; snd_soc_write(codec, 0xa3, reg_val); snd_soc_write(codec, 0xa4, 0x2baf); reg_val = snd_soc_read(codec, 0xa5); reg_val &= ~(0x7ff<<0); reg_val |= 1<<0; snd_soc_write(codec, 0xa5, reg_val); snd_soc_write(codec, 0xa6, 0x2baf); reg_val = snd_soc_read(codec, 0xa7); reg_val &= ~(0x7ff<<0); snd_soc_write(codec, 0xa7, reg_val); snd_soc_write(codec, 0xa8, 0x44a); reg_val = snd_soc_read(codec, 0xa9); reg_val &= ~(0x7ff<<0); snd_soc_write(codec, 0xa9, reg_val); snd_soc_write(codec, 0xaa, 0x1e06); reg_val = snd_soc_read(codec, 0xab); reg_val &= ~(0x7ff<<0); reg_val |= (0x352<<0); snd_soc_write(codec, 0xab, reg_val); snd_soc_write(codec, 0xac, 0x6910); reg_val = snd_soc_read(codec, 0xad); reg_val &= ~(0x7ff<<0); reg_val |= (0x77a<<0); snd_soc_write(codec, 0xad, reg_val); snd_soc_write(codec, 0xae, 0xaaaa); reg_val = snd_soc_read(codec, 0xaf); reg_val &= ~(0x7ff<<0); reg_val |= (0x2de<<0); snd_soc_write(codec, 0xaf, reg_val); snd_soc_write(codec, 0xb0, 0xc982); snd_soc_write(codec, 0x16, 0x9f9f); } void agc_enable(struct snd_soc_codec *codec,bool on) { int reg_val; if (on) { reg_val = snd_soc_read(codec, MOD_CLK_ENA); reg_val |= (0x1<<7); snd_soc_write(codec, MOD_CLK_ENA, reg_val); reg_val = snd_soc_read(codec, MOD_RST_CTRL); reg_val |= (0x1<<7); snd_soc_write(codec, MOD_RST_CTRL, reg_val); reg_val = snd_soc_read(codec, 0x82); reg_val &= ~(0xf<<0); reg_val |= (0x6<<0); reg_val &= ~(0x7<<12); reg_val |= (0x7<<12); snd_soc_write(codec, 0x82, reg_val); reg_val = snd_soc_read(codec, 0x83); reg_val &= ~(0xf<<0); reg_val |= (0x6<<0); reg_val &= ~(0x7<<12); reg_val |= (0x7<<12); snd_soc_write(codec, 0x83, reg_val); } else { reg_val = snd_soc_read(codec, MOD_CLK_ENA); reg_val &= ~(0x1<<7); snd_soc_write(codec, MOD_CLK_ENA, reg_val); reg_val = snd_soc_read(codec, MOD_RST_CTRL); reg_val &= ~(0x1<<7); snd_soc_write(codec, MOD_RST_CTRL, reg_val); reg_val = snd_soc_read(codec, 0x82); reg_val &= ~(0xf<<0); reg_val &= ~(0x7<<12); snd_soc_write(codec, 0x82, reg_val); reg_val = snd_soc_read(codec, 0x83); reg_val &= ~(0xf<<0); reg_val &= ~(0x7<<12); snd_soc_write(codec, 0x83, reg_val); } } void drc_enable(struct snd_soc_codec *codec,bool on) { int reg_val; if (on) { snd_soc_write(codec, 0xb5, 0x80); reg_val = snd_soc_read(codec, MOD_CLK_ENA); reg_val |= (0x1<<6); snd_soc_write(codec, MOD_CLK_ENA, reg_val); reg_val = snd_soc_read(codec, MOD_RST_CTRL); reg_val |= (0x1<<6); snd_soc_write(codec, MOD_RST_CTRL, reg_val); reg_val = snd_soc_read(codec, 0xa0); reg_val |= (0x7<<0); snd_soc_write(codec, 0xa0, reg_val); } else { snd_soc_write(codec, 0xb5, 0x0); reg_val = snd_soc_read(codec, MOD_CLK_ENA); reg_val &= ~(0x1<<6); snd_soc_write(codec, MOD_CLK_ENA, reg_val); reg_val = snd_soc_read(codec, MOD_RST_CTRL); reg_val &= ~(0x1<<6); snd_soc_write(codec, MOD_RST_CTRL, reg_val); reg_val = snd_soc_read(codec, 0xa0); reg_val &= ~(0x7<<0); snd_soc_write(codec, 0xa0, reg_val); } } void set_configuration(struct snd_soc_codec *codec) { if (speaker_double_used) { snd_soc_update_bits(codec, SPKOUT_CTRL, (0x1f<codec; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); mutex_lock(&ac10x->dac_mutex); switch (event) { case SND_SOC_DAPM_PRE_PMU: AC10X_DBG("%s,line:%d\n",__func__,__LINE__); if (ac10x->dac_enable == 0){ /*enable dac module clk*/ snd_soc_update_bits(codec, MOD_CLK_ENA, (0x1<dac_enable++; break; case SND_SOC_DAPM_POST_PMD: if (ac10x->dac_enable > 0){ ac10x->dac_enable--; if (ac10x->dac_enable == 0){ snd_soc_update_bits(codec, DAC_DIG_CTRL, (0x1<dac_mutex); return 0; } static int late_enable_adc(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); mutex_lock(&ac10x->adc_mutex); switch (event) { case SND_SOC_DAPM_PRE_PMU: if (ac10x->adc_enable == 0){ /*enable adc module clk*/ snd_soc_update_bits(codec, MOD_CLK_ENA, (0x1<adc_enable++; break; case SND_SOC_DAPM_POST_PMD: if (ac10x->adc_enable > 0){ ac10x->adc_enable--; if (ac10x->adc_enable == 0){ snd_soc_update_bits(codec, ADC_DIG_CTRL, (0x1<adc_mutex); return 0; } static int ac10x_speaker_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { struct snd_soc_codec *codec = w->codec; switch (event) { case SND_SOC_DAPM_POST_PMU: AC10X_DBG("[speaker open ]%s,line:%d\n",__func__,__LINE__); if (drc_used) { drc_enable(codec,1); } msleep(30); gpio_set_value(PA_CTL, 1); break; case SND_SOC_DAPM_PRE_PMD : AC10X_DBG("[speaker close ]%s,line:%d\n",__func__,__LINE__); gpio_set_value(PA_CTL, 0); if (drc_used) { drc_enable(codec,0); } default: break; } return 0; } static int ac10x_headphone_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { struct snd_soc_codec *codec = w->codec; switch (event) { case SND_SOC_DAPM_POST_PMU: /*open*/ AC10X_DBG("post:open:%s,line:%d\n", __func__, __LINE__); snd_soc_update_bits(codec, OMIXER_DACA_CTRL, (0xf<codec; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); mutex_lock(&ac10x->aifclk_mutex); switch (event) { case SND_SOC_DAPM_PRE_PMU: if (ac10x->aif1_clken == 0){ /*enable AIF1CLK*/ snd_soc_update_bits(codec, SYSCLK_CTRL, (0x1<aif2_clken == 0 && ac10x->aif3_clken == 0) snd_soc_update_bits(codec, SYSCLK_CTRL, (0x1<aif1_clken++; break; case SND_SOC_DAPM_POST_PMD: if (ac10x->aif1_clken > 0){ ac10x->aif1_clken--; if (ac10x->aif1_clken == 0){ /*disable AIF1CLK*/ snd_soc_update_bits(codec, SYSCLK_CTRL, (0x1<aif2_clken == 0 && ac10x->aif3_clken == 0) snd_soc_update_bits(codec, SYSCLK_CTRL, (0x1<aifclk_mutex); return 0; } static int dmic_mux_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); switch (event){ case SND_SOC_DAPM_PRE_PMU: snd_soc_update_bits(codec, ADC_DIG_CTRL, (0x1<adc_mutex); switch (event) { case SND_SOC_DAPM_PRE_PMU: if (ac10x->adc_enable == 0){ /*enable adc module clk*/ snd_soc_update_bits(codec, MOD_CLK_ENA, (0x1<adc_enable++; break; case SND_SOC_DAPM_POST_PMD: if (ac10x->adc_enable > 0){ ac10x->adc_enable--; if (ac10x->adc_enable == 0){ snd_soc_update_bits(codec, ADC_DIG_CTRL, (0x1<adc_mutex); return 0; } static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0); static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0); static const DECLARE_TLV_DB_SCALE(aif1_ad_slot0_vol_tlv, -11925, 75, 0); static const DECLARE_TLV_DB_SCALE(aif1_ad_slot1_vol_tlv, -11925, 75, 0); static const DECLARE_TLV_DB_SCALE(aif1_da_slot0_vol_tlv, -11925, 75, 0); static const DECLARE_TLV_DB_SCALE(aif1_da_slot1_vol_tlv, -11925, 75, 0); static const DECLARE_TLV_DB_SCALE(aif1_ad_slot0_mix_vol_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(aif1_ad_slot1_mix_vol_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -11925, 75, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0); static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0); static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(adc_input_vol_tlv, -450, 150, 0); /*mic1/mic2: 0db when 000, and from 30db to 48db when 001 to 111*/ static const DECLARE_TLV_DB_SCALE(mic1_boost_vol_tlv, 0, 200, 0); static const DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, 0, 200, 0); static const DECLARE_TLV_DB_SCALE(linein_amp_vol_tlv, -1200, 300, 0); static const DECLARE_TLV_DB_SCALE(axin_to_l_r_mix_vol_tlv, -450, 150, 0); static const DECLARE_TLV_DB_SCALE(mic1_to_l_r_mix_vol_tlv, -450, 150, 0); static const DECLARE_TLV_DB_SCALE(mic2_to_l_r_mix_vol_tlv, -450, 150, 0); static const DECLARE_TLV_DB_SCALE(linein_to_l_r_mix_vol_tlv, -450, 150, 0); static const struct snd_kcontrol_new ac10x_controls[] = { /*AIF1*/ SOC_DOUBLE_TLV("AIF1 ADC timeslot 0 volume", AIF1_VOL_CTRL1, AIF1_AD0L_VOL, AIF1_AD0R_VOL, 0xff, 0, aif1_ad_slot0_vol_tlv), SOC_DOUBLE_TLV("AIF1 ADC timeslot 1 volume", AIF1_VOL_CTRL2, AIF1_AD1L_VOL, AIF1_AD1R_VOL, 0xff, 0, aif1_ad_slot1_vol_tlv), SOC_DOUBLE_TLV("AIF1 DAC timeslot 0 volume", AIF1_VOL_CTRL3, AIF1_DA0L_VOL, AIF1_DA0R_VOL, 0xff, 0, aif1_da_slot0_vol_tlv), SOC_DOUBLE_TLV("AIF1 DAC timeslot 1 volume", AIF1_VOL_CTRL4, AIF1_DA1L_VOL, AIF1_DA1R_VOL, 0xff, 0, aif1_da_slot1_vol_tlv), SOC_DOUBLE_TLV("AIF1 ADC timeslot 0 mixer gain", AIF1_MXR_GAIN, AIF1_AD0L_MXR_GAIN, AIF1_AD0R_MXR_GAIN, 0xf, 0, aif1_ad_slot0_mix_vol_tlv), SOC_DOUBLE_TLV("AIF1 ADC timeslot 1 mixer gain", AIF1_MXR_GAIN, AIF1_AD1L_MXR_GAIN, AIF1_AD1R_MXR_GAIN, 0x3, 0, aif1_ad_slot1_mix_vol_tlv), /*ADC*/ SOC_DOUBLE_TLV("ADC volume", ADC_VOL_CTRL, ADC_VOL_L, ADC_VOL_R, 0xff, 0, adc_vol_tlv), /*DAC*/ SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv), SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN, 0xf, 0, dac_mix_vol_tlv), SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 0, dig_vol_tlv), /*ADC*/ SOC_DOUBLE_TLV("ADC input gain", ADC_APC_CTRL, ADCLG, ADCRG, 0x7, 0, adc_input_vol_tlv), SOC_SINGLE_TLV("MIC1 boost amplifier gain", ADC_SRCBST_CTRL, ADC_MIC1G, 0x7, 0, mic1_boost_vol_tlv), SOC_SINGLE_TLV("MIC2 boost amplifier gain", ADC_SRCBST_CTRL, ADC_MIC2G, 0x7, 0, mic2_boost_vol_tlv), SOC_SINGLE_TLV("LINEINL-LINEINR pre-amplifier gain", ADC_SRCBST_CTRL, LINEIN_PREG, 0x7, 0, linein_amp_vol_tlv), SOC_SINGLE_TLV("MIC1 BST stage to L_R outp mixer gain", OMIXER_BST1_CTRL, OMIXER_MIC1G, 0x7, 0, mic1_to_l_r_mix_vol_tlv), SOC_SINGLE_TLV("MIC2 BST stage to L_R outp mixer gain", OMIXER_BST1_CTRL, OMIXER_MIC2G, 0x7, 0, mic2_to_l_r_mix_vol_tlv), SOC_SINGLE_TLV("LINEINL/R to L_R output mixer gain", OMIXER_BST1_CTRL, LINEING, 0x7, 0, linein_to_l_r_mix_vol_tlv), SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv), SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv), }; /*AIF1 AD0 OUT */ static const char *aif1out0l_text[] = { "AIF1_AD0L", "AIF1_AD0R","SUM_AIF1AD0L_AIF1AD0R", "AVE_AIF1AD0L_AIF1AD0R" }; static const char *aif1out0r_text[] = { "AIF1_AD0R", "AIF1_AD0L","SUM_AIF1AD0L_AIF1AD0R", "AVE_AIF1AD0L_AIF1AD0R" }; static const struct soc_enum aif1out0l_enum = SOC_ENUM_SINGLE(AIF1_ADCDAT_CTRL, 10, 4, aif1out0l_text); static const struct snd_kcontrol_new aif1out0l_mux = SOC_DAPM_ENUM("AIF1OUT0L Mux", aif1out0l_enum); static const struct soc_enum aif1out0r_enum = SOC_ENUM_SINGLE(AIF1_ADCDAT_CTRL, 8, 4, aif1out0r_text); static const struct snd_kcontrol_new aif1out0r_mux = SOC_DAPM_ENUM("AIF1OUT0R Mux", aif1out0r_enum); /*AIF1 AD1 OUT */ static const char *aif1out1l_text[] = { "AIF1_AD1L", "AIF1_AD1R","SUM_AIF1ADC1L_AIF1ADC1R", "AVE_AIF1ADC1L_AIF1ADC1R" }; static const char *aif1out1r_text[] = { "AIF1_AD1R", "AIF1_AD1L","SUM_AIF1ADC1L_AIF1ADC1R", "AVE_AIF1ADC1L_AIF1ADC1R" }; static const struct soc_enum aif1out1l_enum = SOC_ENUM_SINGLE(AIF1_ADCDAT_CTRL, 6, 4, aif1out1l_text); static const struct snd_kcontrol_new aif1out1l_mux = SOC_DAPM_ENUM("AIF1OUT1L Mux", aif1out1l_enum); static const struct soc_enum aif1out1r_enum = SOC_ENUM_SINGLE(AIF1_ADCDAT_CTRL, 4, 4, aif1out1r_text); static const struct snd_kcontrol_new aif1out1r_mux = SOC_DAPM_ENUM("AIF1OUT1R Mux", aif1out1r_enum); /*AIF1 DA0 IN*/ static const char *aif1in0l_text[] = { "AIF1_DA0L", "AIF1_DA0R", "SUM_AIF1DA0L_AIF1DA0R", "AVE_AIF1DA0L_AIF1DA0R" }; static const char *aif1in0r_text[] = { "AIF1_DA0R", "AIF1_DA0L", "SUM_AIF1DA0L_AIF1DA0R", "AVE_AIF1DA0L_AIF1DA0R" }; static const struct soc_enum aif1in0l_enum = SOC_ENUM_SINGLE(AIF1_DACDAT_CTRL, 10, 4, aif1in0l_text); static const struct snd_kcontrol_new aif1in0l_mux = SOC_DAPM_ENUM("AIF1IN0L Mux", aif1in0l_enum); static const struct soc_enum aif1in0r_enum = SOC_ENUM_SINGLE(AIF1_DACDAT_CTRL, 8, 4, aif1in0r_text); static const struct snd_kcontrol_new aif1in0r_mux = SOC_DAPM_ENUM("AIF1IN0R Mux", aif1in0r_enum); /*AIF1 DA1 IN*/ static const char *aif1in1l_text[] = { "AIF1_DA1L", "AIF1_DA1R","SUM_AIF1DA1L_AIF1DA1R", "AVE_AIF1DA1L_AIF1DA1R" }; static const char *aif1in1r_text[] = { "AIF1_DA1R", "AIF1_DA1L","SUM_AIF1DA1L_AIF1DA1R", "AVE_AIF1DA1L_AIF1DA1R" }; static const struct soc_enum aif1in1l_enum = SOC_ENUM_SINGLE(AIF1_DACDAT_CTRL, 6, 4, aif1in1l_text); static const struct snd_kcontrol_new aif1in1l_mux = SOC_DAPM_ENUM("AIF1IN1L Mux", aif1in1l_enum); static const struct soc_enum aif1in1r_enum = SOC_ENUM_SINGLE(AIF1_DACDAT_CTRL, 4, 4, aif1in1r_text); static const struct snd_kcontrol_new aif1in1r_mux = SOC_DAPM_ENUM("AIF1IN1R Mux", aif1in1r_enum); /*0x13register*/ /*AIF1 ADC0 MIXER SOURCE*/ static const struct snd_kcontrol_new aif1_ad0l_mxr_src_ctl[] = { SOC_DAPM_SINGLE("AIF1 DA0L Switch", AIF1_MXR_SRC, AIF1_AD0L_AIF1_DA0L_MXR, 1, 0), SOC_DAPM_SINGLE("ADCL Switch", AIF1_MXR_SRC, AIF1_AD0L_ADCL_MXR, 1, 0), }; static const struct snd_kcontrol_new aif1_ad0r_mxr_src_ctl[] = { SOC_DAPM_SINGLE("AIF1 DA0R Switch", AIF1_MXR_SRC, AIF1_AD0R_AIF1_DA0R_MXR, 1, 0), SOC_DAPM_SINGLE("ADCR Switch", AIF1_MXR_SRC, AIF1_AD0R_ADCR_MXR, 1, 0), }; /*AIF1 ADC1 MIXER SOURCE*/ static const struct snd_kcontrol_new aif1_ad1l_mxr_src_ctl[] = { SOC_DAPM_SINGLE("ADCL Switch", AIF1_MXR_SRC, AIF1_AD1L_ADCL_MXR, 1, 0), }; static const struct snd_kcontrol_new aif1_ad1r_mxr_src_ctl[] = { SOC_DAPM_SINGLE("ADCR Switch", AIF1_MXR_SRC, AIF1_AD1R_ADCR_MXR, 1, 0), }; /*4C register*/ static const struct snd_kcontrol_new dacl_mxr_src_controls[] = { SOC_DAPM_SINGLE("ADCL Switch", DAC_MXR_SRC, DACL_MXR_ADCL, 1, 0), SOC_DAPM_SINGLE("AIF1DA1L Switch", DAC_MXR_SRC, DACL_MXR_AIF1_DA1L, 1, 0), SOC_DAPM_SINGLE("AIF1DA0L Switch", DAC_MXR_SRC, DACL_MXR_AIF1_DA0L, 1, 0), }; static const struct snd_kcontrol_new dacr_mxr_src_controls[] = { SOC_DAPM_SINGLE("ADCR Switch", DAC_MXR_SRC, DACR_MXR_ADCR, 1, 0), SOC_DAPM_SINGLE("AIF1DA1R Switch", DAC_MXR_SRC, DACR_MXR_AIF1_DA1R, 1, 0), SOC_DAPM_SINGLE("AIF1DA0R Switch", DAC_MXR_SRC, DACR_MXR_AIF1_DA0R, 1, 0), }; /*output mixer source select*/ /*defined left output mixer*/ static const struct snd_kcontrol_new ac10x_loutmix_controls[] = { SOC_DAPM_SINGLE("DACR Switch", OMIXER_SR, LMIXMUTEDACR, 1, 0), SOC_DAPM_SINGLE("DACL Switch", OMIXER_SR, LMIXMUTEDACL, 1, 0), SOC_DAPM_SINGLE("LINEINL Switch", OMIXER_SR, LMIXMUTELINEINL, 1, 0), SOC_DAPM_SINGLE("LINEINL-LINEINR Switch", OMIXER_SR, LMIXMUTELINEINLR, 1, 0), SOC_DAPM_SINGLE("MIC2Booststage Switch", OMIXER_SR, LMIXMUTEMIC2BOOST, 1, 0), SOC_DAPM_SINGLE("MIC1Booststage Switch", OMIXER_SR, LMIXMUTEMIC1BOOST, 1, 0), }; /*defined right output mixer*/ static const struct snd_kcontrol_new ac10x_routmix_controls[] = { SOC_DAPM_SINGLE("DACL Switch", OMIXER_SR, RMIXMUTEDACL, 1, 0), SOC_DAPM_SINGLE("DACR Switch", OMIXER_SR, RMIXMUTEDACR, 1, 0), SOC_DAPM_SINGLE("LINEINR Switch", OMIXER_SR, RMIXMUTELINEINR, 1, 0), SOC_DAPM_SINGLE("LINEINL-LINEINR Switch", OMIXER_SR, RMIXMUTELINEINLR, 1, 0), SOC_DAPM_SINGLE("MIC2Booststage Switch", OMIXER_SR, RMIXMUTEMIC2BOOST, 1, 0), SOC_DAPM_SINGLE("MIC1Booststage Switch", OMIXER_SR, RMIXMUTEMIC1BOOST, 1, 0), }; /*hp source select*/ /*headphone input source*/ static const char *ac10x_hp_r_func_sel[] = { "DACR HPR Switch", "Right Analog Mixer HPR Switch"}; static const struct soc_enum ac10x_hp_r_func_enum = SOC_ENUM_SINGLE(HPOUT_CTRL, RHPS, 2, ac10x_hp_r_func_sel); static const struct snd_kcontrol_new ac10x_hp_r_func_controls = SOC_DAPM_ENUM("HP_R Mux", ac10x_hp_r_func_enum); static const char *ac10x_hp_l_func_sel[] = { "DACL HPL Switch", "Left Analog Mixer HPL Switch"}; static const struct soc_enum ac10x_hp_l_func_enum = SOC_ENUM_SINGLE(HPOUT_CTRL, LHPS, 2, ac10x_hp_l_func_sel); static const struct snd_kcontrol_new ac10x_hp_l_func_controls = SOC_DAPM_ENUM("HP_L Mux", ac10x_hp_l_func_enum); /*spk source select*/ static const char *ac10x_rspks_func_sel[] = { "MIXER Switch", "MIXR MIXL Switch"}; static const struct soc_enum ac10x_rspks_func_enum = SOC_ENUM_SINGLE(SPKOUT_CTRL, RSPKS, 2, ac10x_rspks_func_sel); static const struct snd_kcontrol_new ac10x_rspks_func_controls = SOC_DAPM_ENUM("SPK_R Mux", ac10x_rspks_func_enum); static const char *ac10x_lspks_l_func_sel[] = { "MIXEL Switch", "MIXL MIXR Switch"}; static const struct soc_enum ac10x_lspks_func_enum = SOC_ENUM_SINGLE(SPKOUT_CTRL, LSPKS, 2, ac10x_lspks_l_func_sel); static const struct snd_kcontrol_new ac10x_lspks_func_controls = SOC_DAPM_ENUM("SPK_L Mux", ac10x_lspks_func_enum); /*defined left input adc mixer*/ static const struct snd_kcontrol_new ac10x_ladcmix_controls[] = { SOC_DAPM_SINGLE("MIC1 boost Switch", ADC_SRC, LADCMIXMUTEMIC1BOOST, 1, 0), SOC_DAPM_SINGLE("MIC2 boost Switch", ADC_SRC, LADCMIXMUTEMIC2BOOST, 1, 0), SOC_DAPM_SINGLE("LININL-R Switch", ADC_SRC, LADCMIXMUTELINEINLR, 1, 0), SOC_DAPM_SINGLE("LINEINL Switch", ADC_SRC, LADCMIXMUTELINEINL, 1, 0), SOC_DAPM_SINGLE("Lout_Mixer_Switch", ADC_SRC, LADCMIXMUTELOUTPUT, 1, 0), SOC_DAPM_SINGLE("Rout_Mixer_Switch", ADC_SRC, LADCMIXMUTEROUTPUT, 1, 0), }; /*defined right input adc mixer*/ static const struct snd_kcontrol_new ac10x_radcmix_controls[] = { SOC_DAPM_SINGLE("MIC1 boost Switch", ADC_SRC, RADCMIXMUTEMIC1BOOST, 1, 0), SOC_DAPM_SINGLE("MIC2 boost Switch", ADC_SRC, RADCMIXMUTEMIC2BOOST, 1, 0), SOC_DAPM_SINGLE("LINEINL-R Switch", ADC_SRC, RADCMIXMUTELINEINLR, 1, 0), SOC_DAPM_SINGLE("LINEINR Switch", ADC_SRC, RADCMIXMUTELINEINR, 1, 0), SOC_DAPM_SINGLE("Rout_Mixer_Switch", ADC_SRC, RADCMIXMUTEROUTPUT, 1, 0), SOC_DAPM_SINGLE("Lout_Mixer_Switch", ADC_SRC, RADCMIXMUTELOUTPUT, 1, 0), }; /*mic2 source select*/ static const char *mic2src_text[] = { "none","MIC2"}; static const struct soc_enum mic2src_enum = SOC_ENUM_SINGLE(ADC_SRCBST_CTRL, 7, 2, mic2src_text); static const struct snd_kcontrol_new mic2src_mux = SOC_DAPM_ENUM("MIC2 SRC", mic2src_enum); /*DMIC*/ static const char *adc_mux_text[] = { "ADC", "DMIC", }; static const struct soc_enum adc_enum = SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text); static const struct snd_kcontrol_new adcl_mux = SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum); static const struct snd_kcontrol_new adcr_mux = SOC_DAPM_ENUM_VIRT("ADCR Mux", adc_enum); /*built widget*/ static const struct snd_soc_dapm_widget ac10x_dapm_widgets[] = { SND_SOC_DAPM_MUX("AIF1OUT0L Mux", AIF1_ADCDAT_CTRL, 15, 0, &aif1out0l_mux), SND_SOC_DAPM_MUX("AIF1OUT0R Mux", AIF1_ADCDAT_CTRL, 14, 0, &aif1out0r_mux), SND_SOC_DAPM_MUX("AIF1OUT1L Mux", AIF1_ADCDAT_CTRL, 13, 0, &aif1out1l_mux), SND_SOC_DAPM_MUX("AIF1OUT1R Mux", AIF1_ADCDAT_CTRL, 12, 0, &aif1out1r_mux), SND_SOC_DAPM_MUX("AIF1IN0L Mux", AIF1_DACDAT_CTRL, 15, 0, &aif1in0l_mux), SND_SOC_DAPM_MUX("AIF1IN0R Mux", AIF1_DACDAT_CTRL, 14, 0, &aif1in0r_mux), SND_SOC_DAPM_MUX("AIF1IN1L Mux", AIF1_DACDAT_CTRL, 13, 0, &aif1in1l_mux), SND_SOC_DAPM_MUX("AIF1IN1R Mux", AIF1_DACDAT_CTRL, 12, 0, &aif1in1r_mux), SND_SOC_DAPM_MIXER("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0, aif1_ad0l_mxr_src_ctl, ARRAY_SIZE(aif1_ad0l_mxr_src_ctl)), SND_SOC_DAPM_MIXER("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0, aif1_ad0r_mxr_src_ctl, ARRAY_SIZE(aif1_ad0r_mxr_src_ctl)), SND_SOC_DAPM_MIXER("AIF1 AD1L Mixer", SND_SOC_NOPM, 0, 0, aif1_ad1l_mxr_src_ctl, ARRAY_SIZE(aif1_ad1l_mxr_src_ctl)), SND_SOC_DAPM_MIXER("AIF1 AD1R Mixer", SND_SOC_NOPM, 0, 0, aif1_ad1r_mxr_src_ctl, ARRAY_SIZE(aif1_ad1r_mxr_src_ctl)), SND_SOC_DAPM_MIXER_E("DACL Mixer", OMIXER_DACA_CTRL, DACALEN, 0, dacl_mxr_src_controls, ARRAY_SIZE(dacl_mxr_src_controls), late_enable_dac, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("DACR Mixer", OMIXER_DACA_CTRL, DACAREN, 0, dacr_mxr_src_controls, ARRAY_SIZE(dacr_mxr_src_controls), late_enable_dac, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), /*dac digital enble*/ SND_SOC_DAPM_DAC("DAC En", NULL, DAC_DIG_CTRL, ENDA, 0), /*ADC digital enble*/ SND_SOC_DAPM_ADC("ADC En", NULL, ADC_DIG_CTRL, ENAD, 0), SND_SOC_DAPM_MIXER("Left Output Mixer", OMIXER_DACA_CTRL, LMIXEN, 0, ac10x_loutmix_controls, ARRAY_SIZE(ac10x_loutmix_controls)), SND_SOC_DAPM_MIXER("Right Output Mixer", OMIXER_DACA_CTRL, RMIXEN, 0, ac10x_routmix_controls, ARRAY_SIZE(ac10x_routmix_controls)), SND_SOC_DAPM_MUX("HP_R Mux", SND_SOC_NOPM, 0, 0, &ac10x_hp_r_func_controls), SND_SOC_DAPM_MUX("HP_L Mux", SND_SOC_NOPM, 0, 0, &ac10x_hp_l_func_controls), SND_SOC_DAPM_MUX("SPK_R Mux", SPKOUT_CTRL, RSPK_EN, 0, &ac10x_rspks_func_controls), SND_SOC_DAPM_MUX("SPK_L Mux", SPKOUT_CTRL, LSPK_EN, 0, &ac10x_lspks_func_controls), SND_SOC_DAPM_PGA("SPK_LR Adder", SND_SOC_NOPM, 0, 0, NULL, 0), /*output widget*/ SND_SOC_DAPM_OUTPUT("HPOUTL"), SND_SOC_DAPM_OUTPUT("HPOUTR"), SND_SOC_DAPM_OUTPUT("SPK1P"), SND_SOC_DAPM_OUTPUT("SPK2P"), SND_SOC_DAPM_OUTPUT("SPK1N"), SND_SOC_DAPM_OUTPUT("SPK2N"), SND_SOC_DAPM_MIXER_E("LEFT ADC input Mixer", ADC_APC_CTRL, ADCLEN, 0, ac10x_ladcmix_controls, ARRAY_SIZE(ac10x_ladcmix_controls),late_enable_adc, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER_E("RIGHT ADC input Mixer", ADC_APC_CTRL, ADCREN, 0, ac10x_radcmix_controls, ARRAY_SIZE(ac10x_radcmix_controls),late_enable_adc, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), /*mic reference*/ SND_SOC_DAPM_PGA("MIC1 PGA", ADC_SRCBST_CTRL, MIC1AMPEN, 0, NULL, 0), SND_SOC_DAPM_PGA("MIC2 PGA", ADC_SRCBST_CTRL, MIC2AMPEN, 0, NULL, 0), SND_SOC_DAPM_PGA("LINEIN PGA", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MUX("MIC2 SRC", SND_SOC_NOPM, 0, 0, &mic2src_mux), /*INPUT widget*/ SND_SOC_DAPM_INPUT("MIC1P"), SND_SOC_DAPM_INPUT("MIC1N"), SND_SOC_DAPM_MICBIAS("MainMic Bias", ADC_APC_CTRL, MBIASEN, 0), SND_SOC_DAPM_MICBIAS("HMic Bias", SND_SOC_NOPM, 0, 0), //SND_SOC_DAPM_MICBIAS("HMic Bias", ADC_APC_CTRL, HBIASEN, 0), SND_SOC_DAPM_INPUT("MIC2"), SND_SOC_DAPM_INPUT("LINEINP"), SND_SOC_DAPM_INPUT("LINEINN"), SND_SOC_DAPM_INPUT("D_MIC"), /*aif1 interface*/ SND_SOC_DAPM_AIF_IN_E("AIF1DACL", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0,ac10x_aif1clk, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_IN_E("AIF1DACR", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0,ac10x_aif1clk, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF1ADCL", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0,ac10x_aif1clk, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF1ADCR", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0,ac10x_aif1clk, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), /*headphone*/ SND_SOC_DAPM_HP("Headphone", ac10x_headphone_event), /*speaker*/ SND_SOC_DAPM_SPK("External Speaker", ac10x_speaker_event), /*DMIC*/ SND_SOC_DAPM_VIRT_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0, &adcl_mux), SND_SOC_DAPM_VIRT_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, &adcr_mux), SND_SOC_DAPM_PGA_E("DMICL VIR", SND_SOC_NOPM, 0, 0, NULL, 0, dmic_mux_ev, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("DMICR VIR", SND_SOC_NOPM, 0, 0, NULL, 0, dmic_mux_ev, SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route ac10x_dapm_routes[] = { {"AIF1ADCL", NULL, "AIF1OUT0L Mux"}, {"AIF1ADCR", NULL, "AIF1OUT0R Mux"}, {"AIF1ADCL", NULL, "AIF1OUT1L Mux"}, {"AIF1ADCR", NULL, "AIF1OUT1R Mux"}, /* aif1out0 mux 11---13*/ {"AIF1OUT0L Mux", "AIF1_AD0L", "AIF1 AD0L Mixer"}, {"AIF1OUT0L Mux", "AIF1_AD0R", "AIF1 AD0R Mixer"}, {"AIF1OUT0R Mux", "AIF1_AD0R", "AIF1 AD0R Mixer"}, {"AIF1OUT0R Mux", "AIF1_AD0L", "AIF1 AD0L Mixer"}, /*AIF1OUT1 mux 11--13 */ {"AIF1OUT1L Mux", "AIF1_AD1L", "AIF1 AD1L Mixer"}, {"AIF1OUT1L Mux", "AIF1_AD1R", "AIF1 AD1R Mixer"}, {"AIF1OUT1R Mux", "AIF1_AD1R", "AIF1 AD1R Mixer"}, {"AIF1OUT1R Mux", "AIF1_AD1L", "AIF1 AD1L Mixer"}, /*AIF1 AD0L Mixer*/ {"AIF1 AD0L Mixer", "AIF1 DA0L Switch", "AIF1IN0L Mux"}, {"AIF1 AD0L Mixer", "ADCL Switch", "ADCL Mux"}, /*AIF1 AD0R Mixer*/ {"AIF1 AD0R Mixer", "AIF1 DA0R Switch", "AIF1IN0R Mux"}, {"AIF1 AD0R Mixer", "ADCR Switch", "ADCR Mux"}, /*AIF1 AD1L Mixer*/ {"AIF1 AD1L Mixer", "ADCL Switch", "ADCL Mux"}, /*AIF1 AD1R Mixer*/ {"AIF1 AD1R Mixer", "ADCR Switch", "ADCR Mux"}, /*AIF1 DA0 IN 12h*/ {"AIF1IN0L Mux", "AIF1_DA0L", "AIF1DACL"}, {"AIF1IN0L Mux", "AIF1_DA0R", "AIF1DACR"}, {"AIF1IN0R Mux", "AIF1_DA0R", "AIF1DACR"}, {"AIF1IN0R Mux", "AIF1_DA0L", "AIF1DACL"}, /*AIF1 DA1 IN 12h*/ {"AIF1IN1L Mux", "AIF1_DA1L", "AIF1DACL"}, {"AIF1IN1L Mux", "AIF1_DA1R", "AIF1DACR"}, {"AIF1IN1R Mux", "AIF1_DA1R", "AIF1DACR"}, {"AIF1IN1R Mux", "AIF1_DA1L", "AIF1DACL"}, /*4c*/ {"DACL Mixer", "AIF1DA0L Switch", "AIF1IN0L Mux"}, {"DACL Mixer", "AIF1DA1L Switch", "AIF1IN1L Mux"}, {"DACL Mixer", "ADCL Switch", "ADCL Mux"}, {"DACR Mixer", "AIF1DA0R Switch", "AIF1IN0R Mux"}, {"DACR Mixer", "AIF1DA1R Switch", "AIF1IN1R Mux"}, {"DACR Mixer", "ADCR Switch", "ADCR Mux"}, {"Right Output Mixer", "DACR Switch", "DACR Mixer"}, {"Right Output Mixer", "DACL Switch", "DACL Mixer"}, {"Right Output Mixer", "LINEINR Switch", "LINEINN"}, {"Right Output Mixer", "LINEINL-LINEINR Switch", "LINEIN PGA"}, {"Right Output Mixer", "MIC2Booststage Switch", "MIC2 PGA"}, {"Right Output Mixer", "MIC1Booststage Switch", "MIC1 PGA"}, {"Left Output Mixer", "DACL Switch", "DACL Mixer"}, {"Left Output Mixer", "DACR Switch", "DACR Mixer"}, {"Left Output Mixer", "LINEINL Switch", "LINEINP"}, {"Left Output Mixer", "LINEINL-LINEINR Switch", "LINEIN PGA"}, {"Left Output Mixer", "MIC2Booststage Switch", "MIC2 PGA"}, {"Left Output Mixer", "MIC1Booststage Switch", "MIC1 PGA"}, /*hp mux*/ {"HP_R Mux", "DACR HPR Switch", "DACR Mixer"}, {"HP_R Mux", "Right Analog Mixer HPR Switch", "Right Output Mixer"}, {"HP_L Mux", "DACL HPL Switch", "DACL Mixer"}, {"HP_L Mux", "Left Analog Mixer HPL Switch", "Left Output Mixer"}, /*hp endpoint*/ {"HPOUTR", NULL, "HP_R Mux"}, {"HPOUTL", NULL, "HP_L Mux"}, {"Headphone", NULL, "HPOUTR"}, {"Headphone", NULL, "HPOUTL"}, /*External Speaker*/ {"External Speaker", NULL, "SPK1P"}, {"External Speaker", NULL, "SPK1N"}, {"External Speaker", NULL, "SPK2P"}, {"External Speaker", NULL, "SPK2N"}, /*spk mux*/ {"SPK_LR Adder", NULL, "Right Output Mixer"}, {"SPK_LR Adder", NULL, "Left Output Mixer"}, {"SPK_L Mux", "MIXL MIXR Switch", "SPK_LR Adder"}, {"SPK_L Mux", "MIXEL Switch", "Left Output Mixer"}, {"SPK_R Mux", "MIXR MIXL Switch", "SPK_LR Adder"}, {"SPK_R Mux", "MIXER Switch", "Right Output Mixer"}, {"SPK1P", NULL, "SPK_R Mux"}, {"SPK1N", NULL, "SPK_R Mux"}, {"SPK2P", NULL, "SPK_L Mux"}, {"SPK2N", NULL, "SPK_L Mux"}, /*LADC SOURCE mixer*/ {"LEFT ADC input Mixer", "MIC1 boost Switch", "MIC1 PGA"}, {"LEFT ADC input Mixer", "MIC2 boost Switch", "MIC2 PGA"}, {"LEFT ADC input Mixer", "LININL-R Switch", "LINEIN PGA"}, {"LEFT ADC input Mixer", "LINEINL Switch", "LINEINN"}, {"LEFT ADC input Mixer", "Lout_Mixer_Switch", "Left Output Mixer"}, {"LEFT ADC input Mixer", "Rout_Mixer_Switch", "Right Output Mixer"}, /*RADC SOURCE mixer*/ {"RIGHT ADC input Mixer", "MIC1 boost Switch", "MIC1 PGA"}, {"RIGHT ADC input Mixer", "MIC2 boost Switch", "MIC2 PGA"}, {"RIGHT ADC input Mixer", "LINEINL-R Switch", "LINEIN PGA"}, {"RIGHT ADC input Mixer", "LINEINR Switch", "LINEINP"}, {"RIGHT ADC input Mixer", "Rout_Mixer_Switch", "Right Output Mixer"}, {"RIGHT ADC input Mixer", "Lout_Mixer_Switch", "Left Output Mixer"}, {"MIC1 PGA", NULL, "MIC1P"}, {"MIC1 PGA", NULL, "MIC1N"}, {"MIC2 PGA", NULL, "MIC2 SRC"}, {"MIC2 SRC", "MIC2", "MIC2"}, {"LINEIN PGA", NULL, "LINEINP"}, {"LINEIN PGA", NULL, "LINEINN"}, /*ADC--ADCMUX*/ {"ADCR Mux", "ADC", "RIGHT ADC input Mixer"}, {"ADCL Mux", "ADC", "LEFT ADC input Mixer"}, /*DMIC*/ {"ADCR Mux", "DMIC", "DMICR VIR"}, {"ADCL Mux", "DMIC", "DMICL VIR"}, {"DMICL VIR", NULL, "D_MIC"}, {"DMICR VIR", NULL, "D_MIC"}, }; /* PLL divisors */ struct pll_div { unsigned int pll_in; unsigned int pll_out; int m; int n_i; int n_f; }; struct aif1_fs { unsigned int samplerate; int aif1_bclk_div; int aif1_srbit; }; struct aif1_lrck { int aif1_lrlk_div; int aif1_lrlk_bit; }; struct aif1_word_size { int aif1_wsize_val; int aif1_wsize_bit; }; /* * Note : pll code from original tdm/i2s driver. * freq_out = freq_in * N/(m*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2; */ static const struct pll_div codec_pll_div[] = { {128000, 22579200, 1, 529, 1}, {192000, 22579200, 1, 352, 4}, {256000, 22579200, 1, 264, 3}, {384000, 22579200, 1, 176, 2},/*((176+2*0.2)*6000000)/(38*(2*1+1))*/ {6000000, 22579200, 38, 429, 0},/*((429+0*0.2)*6000000)/(38*(2*1+1))*/ {13000000, 22579200, 19, 99, 0}, {19200000, 22579200, 25, 88, 1}, {128000, 24576000, 1, 576, 0}, {192000, 24576000, 1, 384, 0}, {256000, 24576000, 1, 288, 0}, {384000, 24576000, 1, 192, 0}, {1411200, 22579200, 1, 48, 0}, {2048000, 24576000, 1, 36, 0}, {6000000, 24576000, 25, 307, 1}, {13000000, 24576000, 42, 238, 1}, {19200000, 24576000, 25, 96, 0}, {11289600, 22579200, 1, 6, 0}, {12288000, 24576000, 1, 6, 0}, }; static const struct aif1_fs codec_aif1_fs[] = { {44100, 4, 7}, {48000, 4, 8}, {8000, 9, 0}, {11025, 8, 1}, {12000, 8, 2}, {16000, 7, 3}, {22050, 6, 4}, {24000, 6, 5}, {32000, 5, 6}, {96000, 2, 9}, {192000, 1, 10}, }; static const struct aif1_lrck codec_aif1_lrck[] = { {16, 0}, {32, 1}, {64, 2}, {128, 3}, {256, 4}, }; static const struct aif1_word_size codec_aif1_wsize[] = { {8, 0}, {16, 1}, {20, 2}, {24, 3}, }; static int ac10x_aif_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; if(mute){ snd_soc_write(codec, DAC_VOL_CTRL, 0); }else{ snd_soc_write(codec, DAC_VOL_CTRL, 0xa0a0); } return 0; } static void ac10x_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) { struct snd_soc_codec *codec = codec_dai->codec; int reg_val; AC10X_DBG("%s,line:%d\n", __func__, __LINE__); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if(agc_used){ agc_enable(codec, 0); } reg_val = (snd_soc_read(codec, AIF_SR_CTRL) >> 12); reg_val &= 0xf; if (codec_dai->playback_active && dmic_used && reg_val == 0x4) { snd_soc_update_bits(codec, AIF_SR_CTRL, (0xf<codec; switch (codec_dai->id) { case 1: AIF_CLK_CTRL = AIF1_CLK_CTRL; aif1_lrlk_div = 64; break; default: return -EINVAL; } for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) { if (codec_aif1_lrck[i].aif1_lrlk_div == aif1_lrlk_div) { snd_soc_update_bits(codec, AIF_CLK_CTRL, (0x7<capture_active && dmic_used && codec_aif1_fs[i].samplerate == 44100) { snd_soc_update_bits(codec, AIF_SR_CTRL, (0xf<codec; switch (clk_id) { case AIF1_CLK: AC10X_DBG("%s,line:%d,snd_soc_read(codec, SYSCLK_CTRL):%x\n", __func__, __LINE__, snd_soc_read(codec, SYSCLK_CTRL)); /*system clk from aif1*/ snd_soc_update_bits(codec, SYSCLK_CTRL, (0x1<codec; switch (codec_dai->id) { case 1: AC10X_DBG("%s,line:%d\n", __func__, __LINE__); AIF_CLK_CTRL = AIF1_CLK_CTRL; break; default: return -EINVAL; } AC10X_DBG("%s,line:%d\n", __func__, __LINE__); /* * master or slave selection * 0 = Master mode * 1 = Slave mode */ reg_val = snd_soc_read(codec, AIF_CLK_CTRL); reg_val &=~(0x1<codec; AC10X_DBG("%s, line:%d, pll_id:%d\n", __func__, __LINE__, pll_id); if (!freq_out) return 0; if ((freq_in < 128000) || (freq_in > 24576000)) { return -EINVAL; } else if ((freq_in == 24576000) || (freq_in == 22579200)) { switch (pll_id) { case AC10X_MCLK1: /*select aif1 clk source from mclk1*/ snd_soc_update_bits(codec, SYSCLK_CTRL, (0x3<codec; AC10X_DBG("%s,line:%d\n", __func__, __LINE__); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if(agc_used){ agc_enable(codec, 1); } } return 0; } #ifndef CONFIG_SWITCH_DETECT_EXTERNAL /* **switch_hw_config:config the 53 codec register */ static void switch_hw_config(struct snd_soc_codec *codec) { /*HMIC/MMIC BIAS voltage level select:2.5v*/ snd_soc_update_bits(codec, OMIXER_BST1_CTRL, (0xf<dapm.bias_level = level; return 0; } static const struct snd_soc_dai_ops ac10x_aif1_dai_ops = { .set_sysclk = ac10x_set_dai_sysclk, .set_fmt = ac10x_set_dai_fmt, .hw_params = ac10x_hw_params, .shutdown = ac10x_aif_shutdown, .digital_mute = ac10x_aif_mute, .set_pll = ac10x_set_fll, .startup = ac10x_audio_startup, }; static struct snd_soc_dai_driver ac10x_dai[] = { { .name = "ac10x-aif1", .id = 1, .playback = { .stream_name = "AIF1 Playback", .channels_min = 1, .channels_max = 2, .rates = ac10x_RATES, .formats = ac10x_FORMATS, }, .capture = { .stream_name = "AIF1 Capture", .channels_min = 1, .channels_max = 2, .rates = ac10x_RATES, .formats = ac10x_FORMATS, }, .ops = &ac10x_aif1_dai_ops, } }; #ifndef CONFIG_SWITCH_DETECT_EXTERNAL static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) { struct ac10x_priv *ac10x = container_of(sdev, struct ac10x_priv, sdev); return sprintf(buf, "%d\n", ac10x->state); } static ssize_t print_headset_name(struct switch_dev *sdev, char *buf) { struct ac10x_priv *ac10x = container_of(sdev, struct ac10x_priv, sdev); return sprintf(buf, "%s\n", ac10x->sdev.name); } /* **switch_status_update: update the switch state. */ static void switch_status_update(struct ac10x_priv *para) { struct ac10x_priv *ac10x = para; AC10X_DBG("%s,line:%d,ac10x->state:%d\n",__func__, __LINE__, ac10x->state); down(&ac10x->sem); switch_set_state(&ac10x->sdev, ac10x->state); up(&ac10x->sem); } /* **clear_codec_irq_work: clear audiocodec pending and Record the interrupt. */ static void clear_codec_irq_work(struct work_struct *work) { int reg_val = 0; struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, clear_codec_irq); struct snd_soc_codec *codec = ac10x->codec; irq_flag = irq_flag+1; reg_val = snd_soc_read(codec, HMIC_STS); if ((0x1<<4)®_val) { reset_flag++; AC10X_DBG("reset_flag:%d\n",reset_flag); } reg_val |= (0x1f<<0); snd_soc_write(codec, HMIC_STS, reg_val); reg_val = snd_soc_read(codec, HMIC_STS); if((reg_val&0x1f) != 0){ reg_val |= (0x1f<<0); snd_soc_write(codec, HMIC_STS, reg_val); } if (cancel_work_sync(&ac10x->work) != 0) { irq_flag--; } if (0 == queue_work(switch_detect_queue, &ac10x->work)) { irq_flag--; pr_err("[clear_codec_irq_work]add work struct failed!\n"); } } /* **earphone_switch_work: judge the status of the headphone */ static void earphone_switch_work(struct work_struct *work) { int reg_val = 0; int tmp = 0; unsigned int temp_value[11]; struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work); struct snd_soc_codec *codec = ac10x->codec; irq_flag--; ac10x->check_count = 0; ac10x->check_count_sum = 0; /*read HMIC_DATA */ tmp = snd_soc_read(codec, HMIC_STS); reg_val = tmp; tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if ((tmp>=0xb) && (ac10x->mode== FOUR_HEADPHONE_PLUGIN)) { tmp = snd_soc_read(codec, HMIC_STS); tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if(tmp>=0x19){ msleep(150); tmp = snd_soc_read(codec, HMIC_STS); tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if(((tmp<0xb && tmp>=0x1) || tmp>=0x19)&&(reset_flag == 0)){ input_report_key(ac10x->key, KEY_HEADSETHOOK, 1); input_sync(ac10x->key); AC10X_DBG("%s,line:%d,KEY_HEADSETHOOK1\n",__func__,__LINE__); if(hook_flag1 != hook_flag2){ hook_flag1 = hook_flag2 = 0; } hook_flag1++; } if(reset_flag) reset_flag--; }else if(tmp<0x19 && tmp>=0x17){ msleep(80); tmp = snd_soc_read(codec, HMIC_STS); tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if(tmp<0x19 && tmp>=0x17 &&(reset_flag == 0)) { KEY_VOLUME_FLAG = 1; input_report_key(ac10x->key, KEY_VOLUMEUP, 1); input_sync(ac10x->key); input_report_key(ac10x->key, KEY_VOLUMEUP, 0); input_sync(ac10x->key); AC10X_DBG("%s,line:%d,tmp:%d,KEY_VOLUMEUP\n",__func__,__LINE__,tmp); } if(reset_flag) reset_flag--; }else if(tmp<0x17 && tmp>=0x13){ msleep(80); tmp = snd_soc_read(codec, HMIC_STS); tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if(tmp<0x17 && tmp>=0x13 && (reset_flag == 0)){ KEY_VOLUME_FLAG = 1; input_report_key(ac10x->key, KEY_VOLUMEDOWN, 1); input_sync(ac10x->key); input_report_key(ac10x->key, KEY_VOLUMEDOWN, 0); input_sync(ac10x->key); AC10X_DBG("%s,line:%d,KEY_VOLUMEDOWN\n",__func__,__LINE__); } if(reset_flag) reset_flag--; } } else if ((tmp<0xb && tmp>=0x2) && (ac10x->mode== FOUR_HEADPHONE_PLUGIN)) { /*read HMIC_DATA */ tmp = snd_soc_read(codec, HMIC_STS); tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if (tmp<0xb && tmp>=0x2) { if(KEY_VOLUME_FLAG) { KEY_VOLUME_FLAG = 0; } if(hook_flag1 == (++hook_flag2)) { hook_flag1 = hook_flag2 = 0; input_report_key(ac10x->key, KEY_HEADSETHOOK, 0); input_sync(ac10x->key); AC10X_DBG("%s,line:%d,KEY_HEADSETHOOK0\n",__func__,__LINE__); } } } else { while (irq_flag == 0) { msleep(20); /*read HMIC_DATA */ tmp = snd_soc_read(codec, HMIC_STS); tmp = (tmp>>HMIC_DATA); tmp &= 0x1f; if(ac10x->check_count_sum <= HEADSET_CHECKCOUNT_SUM){ if (ac10x->check_count <= HEADSET_CHECKCOUNT){ temp_value[ac10x->check_count] = tmp; ac10x->check_count++; if(ac10x->check_count >= 2){ if( !(temp_value[ac10x->check_count - 1] == temp_value[(ac10x->check_count) - 2])){ ac10x->check_count = 0; ac10x->check_count_sum = 0; } } }else{ ac10x->check_count_sum++; } }else{ if (temp_value[ac10x->check_count -2] >= 0xb) { ac10x->state = 2; ac10x->mode = THREE_HEADPHONE_PLUGIN; switch_status_update(ac10x); ac10x->check_count = 0; ac10x->check_count_sum = 0; reset_flag = 0; break; } else if(temp_value[ac10x->check_count - 2]>=0x1 && temp_value[ac10x->check_count -2]<0xb) { ac10x->mode = FOUR_HEADPHONE_PLUGIN; ac10x->state = 1; switch_status_update(ac10x); ac10x->check_count = 0; ac10x->check_count_sum = 0; reset_flag = 0; break; } else { ac10x->mode = HEADPHONE_IDLE; ac10x->state = 0; switch_status_update(ac10x); ac10x->check_count = 0; ac10x->check_count_sum = 0; reset_flag = 0; break; } } } } } /* **audio_hmic_irq: the interrupt handlers */ static irqreturn_t audio_hmic_irq(int irq, void *para) { struct ac10x_priv *ac10x = (struct ac10x_priv *)para; if (ac10x == NULL) { return -EINVAL; } if(codec_irq_queue == NULL) pr_err("------------codec_irq_queue is null!!----------"); if(&ac10x->clear_codec_irq == NULL) pr_err("------------ac10x->clear_codec_irq is null!!----------"); if(0 == queue_work(codec_irq_queue, &ac10x->clear_codec_irq)){ pr_err("[audio_hmic_irq]add work struct failed!\n"); } return 0; } #endif static void codec_resume_work(struct work_struct *work) { struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume); struct snd_soc_codec *codec = ac10x->codec; int i ,ret =0; #ifndef CONFIG_SWITCH_DETECT_EXTERNAL ac10x->virq = gpio_to_irq(SWITCH_DETECT); if (IS_ERR_VALUE(ac10x->virq)) { pr_warn("[ac10x] map gpio to virq failed, errno = %d\n",ac10x->virq); //return -EINVAL; } /* request virq, set virq type to high level trigger */ ret = devm_request_irq(codec->dev, ac10x->virq, audio_hmic_irq, IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x); if (IS_ERR_VALUE(ret)) { pr_warn("[ac10x] request virq %d failed, errno = %d\n", ac10x->virq, ret); //return -EINVAL; } #endif for (i = 0; i < ARRAY_SIZE(ac10x_supplies); i++){ ret = regulator_enable(ac10x->supplies[i].consumer); if (0 != ret) { pr_err("[ac10x] %s: some error happen, fail to enable regulator!\n", __func__); } } msleep(50); set_configuration(codec); if (agc_used) { agc_config(codec); } if (drc_used) { drc_config(codec); } /*enable this bit to prevent leakage from ldoin*/ snd_soc_update_bits(codec, ADDA_TUNE3, (0x1<>HMIC_DATA); ret &= 0x1f; if (ret < 1) { ac10x->state = 0; switch_status_update(ac10x); } #endif } /***************************************************************************/ static ssize_t ac10x_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { static int val = 0, flag = 0; u8 reg,num,i=0; u16 value_w,value_r[128]; struct ac10x_priv *ac10x = dev_get_drvdata(dev); val = simple_strtol(buf, NULL, 16); flag = (val >> 24) & 0xF; if(flag) {//write reg = (val >> 16) & 0xFF; value_w = val & 0xFFFF; snd_soc_write(ac10x->codec, reg, value_w); printk("write 0x%x to reg:0x%x\n",value_w,reg); } else { reg =(val>>8)& 0xFF; num=val&0xff; printk("\n"); printk("read:start add:0x%x,count:0x%x\n",reg,num); do{ value_r[i] = snd_soc_read(ac10x->codec, reg); printk("0x%x: 0x%04x ",reg,value_r[i]); reg+=1; i++; if(i == num) printk("\n"); if(i%4==0) printk("\n"); }while(i ac10x\n"); printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n"); printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n"); return 0; } static DEVICE_ATTR(ac10x, 0644, ac10x_debug_show, ac10x_debug_store); static struct attribute *audio_debug_attrs[] = { &dev_attr_ac10x.attr, NULL, }; static struct attribute_group audio_debug_attr_group = { .name = "ac10x_debug", .attrs = audio_debug_attrs, }; /************************************************************/ static int ac10x_codec_probe(struct snd_soc_codec *codec) { int ret = 0; int i = 0; struct ac10x_priv *ac10x; struct snd_soc_dapm_context *dapm = &codec->dapm; ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); if (ret < 0) { printk(KERN_ERR "ac10x: failed to set cache I/O: %d\n", ret); return ret; } ac10x = dev_get_drvdata(codec->dev); if (ac10x == NULL) { return -ENOMEM; } ac10x->codec = codec; #ifndef CONFIG_SWITCH_DETECT_EXTERNAL ac10x->sdev.state = 0; ac10x->state = -1; ac10x->check_count = 0; ac10x->check_count_sum = 0; ac10x->sdev.name = "h2w"; ac10x->sdev.print_name = print_headset_name; ac10x->sdev.print_state = switch_gpio_print_state; ret = switch_dev_register(&ac10x->sdev); if (ret < 0) { goto err_switch_dev_register; } /*use for judge the state of switch*/ INIT_WORK(&ac10x->work, earphone_switch_work); INIT_WORK(&ac10x->clear_codec_irq,clear_codec_irq_work); /********************create input device************************/ ac10x->key = input_allocate_device(); if (!ac10x->key) { pr_err("[ac10x] input_allocate_device: not enough memory for input device\n"); ret = -ENOMEM; goto err_input_allocate_device; } ac10x->key->name = "headset"; ac10x->key->phys = "headset/input0"; ac10x->key->id.bustype = BUS_HOST; ac10x->key->id.vendor = 0x0001; ac10x->key->id.product = 0xffff; ac10x->key->id.version = 0x0100; ac10x->key->evbit[0] = BIT_MASK(EV_KEY); set_bit(KEY_HEADSETHOOK, ac10x->key->keybit); set_bit(KEY_VOLUMEUP, ac10x->key->keybit); set_bit(KEY_VOLUMEDOWN, ac10x->key->keybit); ret = input_register_device(ac10x->key); if (ret) { pr_err("[ac10x] input_register_device: input_register_device failed\n"); goto err_input_register_device; } headphone_state = 0; ac10x->mode = HEADPHONE_IDLE; sema_init(&ac10x->sem, 1); codec_irq_queue = create_singlethread_workqueue("codec_irq"); switch_detect_queue = create_singlethread_workqueue("codec_resume"); if (switch_detect_queue == NULL || codec_irq_queue == NULL) { pr_err("[ac10x] try to create workqueue for codec failed!\n"); ret = -ENOMEM; goto err_switch_work_queue; } #endif INIT_WORK(&ac10x->codec_resume, codec_resume_work); ac10x->dac_enable = 0; ac10x->adc_enable = 0; ac10x->aif1_clken = 0; ac10x->aif2_clken = 0; ac10x->aif3_clken = 0; mutex_init(&ac10x->dac_mutex); mutex_init(&ac10x->adc_mutex); mutex_init(&ac10x->aifclk_mutex); /*request pa gpio*/ ret = gpio_request(PA_CTL, NULL); if (0 != ret) { pr_err("request gpio failed!\n"); } else { /* * config gpio info of audio_pa_ctrl, the default pa config is close(check pa sys_config1.fex) */ gpio_direction_output(PA_CTL, 1); gpio_set_value(PA_CTL, 0); } #ifndef CONFIG_SWITCH_DETECT_EXTERNAL ac10x->virq = gpio_to_irq(SWITCH_DETECT); if (IS_ERR_VALUE(ac10x->virq)) { pr_err("[ac10x] map gpio to virq failed, errno = %d\n",ac10x->virq); } /* request virq, set virq type to high level trigger */ ret = devm_request_irq(codec->dev, ac10x->virq, audio_hmic_irq, IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x); if (IS_ERR_VALUE(ret)) { pr_err("[ac10x] request virq %d failed, errno = %d\n", ac10x->virq, ret); } #endif ac10x->num_supplies = ARRAY_SIZE(ac10x_supplies); ac10x->supplies = devm_kzalloc(ac10x->codec->dev, sizeof(struct regulator_bulk_data) * ac10x->num_supplies, GFP_KERNEL); if (!ac10x->supplies) { pr_err("[ac10x] Failed to get mem.\n"); return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(ac10x_supplies); i++) ac10x->supplies[i].supply = ac10x_supplies[i]; ret = regulator_bulk_get(NULL, ac10x->num_supplies, ac10x->supplies); if (ret != 0) { pr_err("[ac10x] Failed to get supplies: %d\n", ret); } for (i = 0; i < ARRAY_SIZE(ac10x_supplies); i++){ ret = regulator_enable(ac10x->supplies[i].consumer); if (0 != ret) { pr_err("[ac10x] %s: some error happen, fail to enable regulator!\n", __func__); } } get_configuration(); set_configuration(ac10x->codec); /*enable this bit to prevent leakage from ldoin*/ snd_soc_update_bits(codec, ADDA_TUNE3, (0x1<dev,ac10x->virq,NULL); err_input_register_device: if(ac10x->key){ input_free_device(ac10x->key); } err_input_allocate_device: switch_dev_unregister(&ac10x->sdev); err_switch_dev_register: kfree(ac10x); #endif return ret; } /* power down chip */ static int ac10x_codec_remove(struct snd_soc_codec *codec) { struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); struct device *dev = codec->dev; int i = 0; int ret = 0; devm_free_irq(dev,ac10x->virq,NULL); if (ac10x->key) { input_unregister_device(ac10x->key); input_free_device(ac10x->key); } switch_dev_unregister(&ac10x->sdev); for (i = 0; i < ARRAY_SIZE(ac10x_supplies); i++){ ret = regulator_disable(ac10x->supplies[i].consumer); if (0 != ret) { pr_err("[ac10x] %s: some error happen, fail to disable regulator!\n", __func__); } regulator_put(ac10x->supplies[i].consumer); } kfree(ac10x); return 0; } static int ac10x_codec_suspend(struct snd_soc_codec *codec) { int i ,ret =0; struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); AC10X_DBG("[codec]:suspend\n"); ac10x_set_bias_level(codec, SND_SOC_BIAS_OFF); for (i = 0; i < ARRAY_SIZE(ac10x_supplies); i++){ ret = regulator_disable(ac10x->supplies[i].consumer); if (0 != ret) { pr_err("[ac10x] %s: some error happen, fail to disable regulator!\n", __func__); } } return 0; } static int ac10x_codec_resume(struct snd_soc_codec *codec) { struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); AC10X_DBG("[codec]:resume"); #ifndef CONFIG_SWITCH_DETECT_EXTERNAL ac10x->mode = HEADPHONE_IDLE; headphone_state = 0; ac10x->state = -1; #endif ac10x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); schedule_work(&ac10x->codec_resume); return 0; } static struct snd_soc_codec_driver soc_codec_dev_sndvir_audio = { .probe = ac10x_codec_probe, .remove = ac10x_codec_remove, .suspend = ac10x_codec_suspend, .resume = ac10x_codec_resume, .set_bias_level = ac10x_set_bias_level, .ignore_pmdown_time = 1, }; static int __devinit ac10x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { int ret = 0; struct ac10x_priv *ac10x; AC10X_DBG("%s,line:%d\n", __func__, __LINE__); ac10x = devm_kzalloc(&i2c->dev, sizeof(struct ac10x_priv), GFP_KERNEL); if (ac10x == NULL) { return -ENOMEM; } i2c_set_clientdata(i2c, ac10x); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_sndvir_audio, ac10x_dai, ARRAY_SIZE(ac10x_dai)); if (ret < 0) { dev_err(&i2c->dev, "Failed to register ac10x: %d\n", ret); } ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group); if (ret) { pr_err("failed to create attr group\n"); } return 0; } static void ac10x_shutdown(struct i2c_client *i2c) { int reg_val; struct snd_soc_codec *codec = NULL; struct ac10x_priv *ac10x = i2c_get_clientdata(i2c); if (ac10x->codec != NULL) codec = ac10x->codec; else{ pr_err("no sound card.\n"); return ; } /*set headphone volume to 0*/ reg_val = snd_soc_read(codec, HPOUT_CTRL); reg_val &= ~(0x3f<dev.kobj, &audio_debug_attr_group); snd_soc_unregister_codec(&i2c->dev); return 0; } int ac10x_driver_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; if (I2C_BUS == adapter->nr) { strlcpy(info->type, "ac10x-codec", I2C_NAME_SIZE); return 0; } else { return -ENODEV; } } static unsigned short normal_i2c[] = {0x1a, I2C_CLIENT_END }; static const struct i2c_device_id ac10x_id[] = { {"ac10x-codec", 0}, {}, }; static struct i2c_driver ac10x_codec_driver = { .class = I2C_CLASS_HWMON, .id_table = ac10x_id, .probe = ac10x_probe, .remove = __devexit_p(ac10x_remove), .driver = { .name = "ac10x-codec", .owner = THIS_MODULE, }, .address_list = normal_i2c, .detect = ac10x_driver_detect, .shutdown = ac10x_shutdown, }; static int __init ac10x_init(void){ int ret; pr_info("%s(%d): ac100 driver register!\n", __func__, __LINE__); ret = i2c_add_driver(&ac10x_codec_driver); return ret; } module_init(ac10x_init); static void __exit ac10x_exit(void) { pr_info("%s(%d): ac100 device unregister!\n", __func__, __LINE__); i2c_del_driver(&ac10x_codec_driver); } module_exit(ac10x_exit); MODULE_DESCRIPTION("ASoC ac10x driver"); MODULE_AUTHOR("huangxin,liushaohua"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ac10x-codec");