diff -drupN a/sound/soc/codecs/ac102.c b/sound/soc/codecs/ac102.c --- a/sound/soc/codecs/ac102.c 1970-01-01 03:00:00.000000000 +0300 +++ b/sound/soc/codecs/ac102.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,1185 @@ +/* + * ac102.c -- ac102 ALSA Soc Audio driver + * + * Version: 1.0 + * + * Author: panjunwen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ac102.h" + + +#define AC102_DEBUG_EN 1 + +#if AC102_DEBUG_EN +#define AC102_DEBUG(...) printk(__VA_ARGS__) +#else +#define AC102_DEBUG(...) +#endif + + +/*test config*/ +#define AC102_ADC_PATTERN_SEL ADC_PTN_NORMAL /*0:ADC normal, 1:0x5A5A5A, 2:0x123456, 3:ADC_PTN_RX_MIX_DATA*/ +#define AC102_DAC_PATTERN_SEL DAC_PTN_NORMAL /*0:DAC normal, 1:-6dB sin, 2:-60dB sin, 3:zero data*/ + + +/*AC102 config*/ +#define AC102_CHIP_NUMS 1 /*range[1, 4]*/ +#define AC102_SLOT_WIDTH 32 /*8/12/16/20/24/28/32bit Slot Width*/ +#define AC102_LRCK_PERIOD (AC102_SLOT_WIDTH * (AC102_CHIP_NUMS < 2 ? 2 : AC102_CHIP_NUMS)) /*range[1, 1024], default PCM mode, I2S/LJ/RJ mode shall divide by 2*/ +#define AC102_MATCH_DTS_EN 0 /*AC102 match method select: 0: i2c_detect, 1:devices tree*/ + +#define AC102_ADC_CLK_DIV ADC_DAC_CLK_DIV_1 /*AC102 ADC CLK divide ratio*/ +#define AC102_DAC_CLK_DIV ADC_DAC_CLK_DIV_1 /*AC102 DAC CLK divide ratio*/ +#define AC102_AGC_EN 0 /*AC102 AGC enable*/ +#define AC102_EQ_EN 0 /*AC102 EQ enable*/ + +#define AC102_DAPM_EN 1 +#define AC102_CODEC_RW_USER_EN 1 +#define AC102_PGA_GAIN ADC_PGA_GAIN_30dB /*0~31dB,1dB step*/ +#define AC102_LINEOUT_GAIN DAC_LINEOUT_GAIN_MINUS_21dB /*-45~0dB,3dB step*/ +#define AC102_DMIC_EN 0 /*0:ADC 1:DMIC*/ + + +#define AC102_REGULATOR_NAME "ac102_vcc_3v3" +#define AC102_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) +#define AC102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + + +struct i2c_client *i2c_ctrl[AC102_CHIP_NUMS]; +int regulator_en; + +struct voltage_supply { + struct regulator *vcc; +}; + +struct ac102_priv { + struct i2c_client *i2c; + struct snd_soc_codec *codec; + struct voltage_supply vol_supply; +}; + +static const struct regmap_config ac102_regmap_config = { + .reg_bits = 8, /*Number of bits in a register address*/ + .val_bits = 8, /*Number of bits in a register value*/ +}; + + +struct real_val_to_reg_val { + unsigned int real_val; + unsigned int reg_val; +}; + + + +static const struct real_val_to_reg_val ac102_bclk_div[] = { + {0, 0}, + {1, 1}, + {2, 2}, + {4, 3}, + {6, 4}, + {8, 5}, + {12, 6}, + {16, 7}, + {24, 8}, + {32, 9}, + {48, 10}, + {64, 11}, + {96, 12}, + {128, 13}, + {176, 14}, + {192, 15}, +}; + + +static const DECLARE_TLV_DB_SCALE(adc_pga_gain_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(adc_dig_vol_tlv, -6450, 50, 0); +static const DECLARE_TLV_DB_SCALE(dac_lineout_gain_tlv, -4500, 300, 0); +static const DECLARE_TLV_DB_SCALE(dac_dig_vol_tlv, -6450, 50, 0); +static const DECLARE_TLV_DB_SCALE(mixer_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(agc_hysteresis_tlv, 100, 100, 0); +static const DECLARE_TLV_DB_SCALE(agc_target_level_tlv, -3000, 100, 0); +static const DECLARE_TLV_DB_SCALE(agc_max_gain_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(agc_noise_threshold_tlv, -3000, 200, 0); +static const DECLARE_TLV_DB_SCALE(agc_gain_hysteresis_tlv, 0, 100, 0); + + +/*************************************** General(volume) controls *******************************************/ +/*ac102 common controls*/ +static const struct snd_kcontrol_new ac102_controls[] = { + SOC_SINGLE_TLV("ADC PGA Gain", ADC_ANA_CTRL1, PGA_GAIN_CTRL, 0x1F, 0, adc_pga_gain_tlv), + SOC_SINGLE_TLV("ADC Digital Vol", ADC_DVC, 0, 0xFF, 0, adc_dig_vol_tlv), + SOC_SINGLE_TLV("DAC LineOut Gain", DAC_ANA_CTRL2, LINE_OUT_AMP_GAIN, 0x0F, 1, dac_lineout_gain_tlv), + SOC_SINGLE_TLV("DAC Digital Vol", DAC_DVC, 0, 0xFF, 0, dac_dig_vol_tlv), + + SOC_SINGLE_TLV("TX Mixer_L RECD_DAT Gain", I2S_TX_MIX_SRC, TX_MIXL_RECD_GAIN, 0x1, 0, mixer_gain_tlv), + SOC_SINGLE_TLV("TX Mixer_L PLAY_DAT Gain", I2S_TX_MIX_SRC, TX_MIXL_PLAY_GAIN, 0x1, 0, mixer_gain_tlv), + SOC_SINGLE_TLV("TX Mixer_R RECD_DAT Gain", I2S_TX_MIX_SRC, TX_MIXR_RECD_GAIN, 0x1, 0, mixer_gain_tlv), + SOC_SINGLE_TLV("TX Mixer_R RXM_DAT Gain", I2S_TX_MIX_SRC, TX_MIXR_RXM_GAIN, 0x1, 0, mixer_gain_tlv), + + SOC_SINGLE_TLV("RX Mixer RXL Gain", I2S_RX_MIX_SRC, RX_MIX_RXL_GAIN, 0x1, 0, mixer_gain_tlv), + SOC_SINGLE_TLV("RX Mixer RXR Gain", I2S_RX_MIX_SRC, RX_MIX_RXR_GAIN, 0x1, 0, mixer_gain_tlv), + + SOC_SINGLE_TLV("DAC Mixer RXM_DAT Gain", DAC_MIX_SRC, DAC_MIX_RXM_GAIN, 0x1, 0, mixer_gain_tlv), + SOC_SINGLE_TLV("DAC Mixer RECD_DAT Gain", DAC_MIX_SRC, DAC_MIX_RECD_GAIN, 0x1, 0, mixer_gain_tlv), + + SOC_SINGLE_TLV("AGC Max Gain", AGC_MAXG, 0, 0x50, 0, agc_max_gain_tlv), + SOC_SINGLE_TLV("AGC Hysteresis", AGC_CTRL, AGC_HYS_SET, 0x3, 0, agc_hysteresis_tlv), + SOC_SINGLE_TLV("AGC Gain Hysteresis", AGC_OPT, AGC_GAIN_HYS_SET, 0x3, 0, agc_gain_hysteresis_tlv), + SOC_SINGLE_TLV("AGC Noise Threshold", AGC_NTH, AGC_NOISE_THRES, 0x1F, 0, agc_noise_threshold_tlv), + SOC_SINGLE_RANGE_TLV("AGC Target Level", AGC_TGLVL, AGC_TGLVL_SET, 0x22, 0x3f, 0, agc_target_level_tlv), + + /*debug control*/ + SOC_SINGLE("ADC HPF Switch", AGC_CTRL, HPF_EN, 0x1, 0), + SOC_SINGLE("ADC Pattern Sel", ADC_DIG_CTRL, ADC_PTN_SEL, 0x03, 0), + SOC_SINGLE("DAC Pattern Sel", DAC_DIG_CTRL, DAC_PTN_SEL, 0x03, 0), +}; + + +/*************************************** DAPM controls *******************************************/ +/*ADC DMIC Source Select Mux*/ +static const char *adc_dmic_src_mux_text[] = { + "ADC switch", "DMIC switch" +}; +static const struct soc_enum adc_dmic_src_mux_enum = + SOC_ENUM_SINGLE(ADC_DIG_CTRL, DIG_MIC_EN, 2, adc_dmic_src_mux_text); +static const struct snd_kcontrol_new adc_dmic_src_mux = + SOC_DAPM_ENUM("ADC DMIC MUX", adc_dmic_src_mux_enum); + +/*TX Left Mixer source control*/ +static const struct snd_kcontrol_new tx_left_src_mixer[] = { + SOC_DAPM_SINGLE("RECD_DAT switch", I2S_TX_MIX_SRC, TX_MIXL_RECD_SRC, 1, 0), + SOC_DAPM_SINGLE("PLAY_DAT switch", I2S_TX_MIX_SRC, TX_MIXL_PLAY_SRC, 1, 0), +}; + +/*TX Right Mixer source control*/ +static const struct snd_kcontrol_new tx_right_src_mixer[] = { + SOC_DAPM_SINGLE("RECD_DAT switch", I2S_TX_MIX_SRC, TX_MIXR_RECD_SRC, 1, 0), + SOC_DAPM_SINGLE("RXM switch", I2S_TX_MIX_SRC, TX_MIXR_RXM_SRC, 1, 0), +}; + +/*RX Mixer source control*/ +static const struct snd_kcontrol_new rx_src_mixer[] = { + SOC_DAPM_SINGLE("RXL switch", I2S_RX_MIX_SRC, RX_MIX_RXL_SRC, 1, 0), + SOC_DAPM_SINGLE("RXR switch", I2S_RX_MIX_SRC, RX_MIX_RXR_SRC, 1, 0), +}; + +/*DAC Mixer source control*/ +static const struct snd_kcontrol_new dac_src_mixer[] = { + SOC_DAPM_SINGLE("RXM switch", DAC_MIX_SRC, DAC_MIX_RXM_SRC, 1, 0), + SOC_DAPM_SINGLE("RECD_DAT switch", DAC_MIX_SRC, DAC_MIX_RECD_SRC, 1, 0), +}; + + +/*************************************** DAPM widgets *******************************************/ +/*ac102 dapm widgets*/ +static const struct snd_soc_dapm_widget ac102_dapm_widgets[] = { + /*input widgets*/ + SND_SOC_DAPM_INPUT("MICP"), + SND_SOC_DAPM_INPUT("MICN"), + SND_SOC_DAPM_INPUT("DMIC"), + + /*output widgets*/ + SND_SOC_DAPM_OUTPUT("LINEOUTP"), + SND_SOC_DAPM_OUTPUT("LINEOUTN"), + + /*MIC PGA*/ + SND_SOC_DAPM_PGA("MIC PGA", ADC_ANA_CTRL1, ADC_PGA_GEN, 0, NULL, 0), + + /*ADC DMIC MUX*/ + SND_SOC_DAPM_MUX("ADC DMIC MUX", ADC_DIG_CTRL, ADC_DIG_EN, 0, &adc_dmic_src_mux), + + /*TX MIXER Left*/ + SND_SOC_DAPM_MIXER("TX MIXL", SND_SOC_NOPM, 0, 0, tx_left_src_mixer, ARRAY_SIZE(tx_left_src_mixer)), + + /*TX MIXER Right*/ + SND_SOC_DAPM_MIXER("TX MIXR", SND_SOC_NOPM, 0, 0, tx_right_src_mixer, ARRAY_SIZE(tx_right_src_mixer)), + + /*RX MIXER*/ + SND_SOC_DAPM_MIXER("RX MIXER", SND_SOC_NOPM, 0, 0, rx_src_mixer, ARRAY_SIZE(rx_src_mixer)), + + /*DAC MIXER*/ + SND_SOC_DAPM_MIXER("DAC MIXER", DAC_DIG_CTRL, DAC_DIG_EN, 0, dac_src_mixer, ARRAY_SIZE(dac_src_mixer)), + + /*LINEOUT PGA*/ + SND_SOC_DAPM_PGA("LINEOUT PGA", SYS_FUNC_CTRL, DAC_ANA_OUT_EN, 0, NULL, 0), + + /*AIF OUT -> (stream widget, stname must be same with codec dai_driver stream_name, which will be used to build dai widget)*/ + SND_SOC_DAPM_AIF_OUT("AIF ADC OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /*AIF IN -> (stream widget, stname must be same with codec dai_driver stream_name, which will be used to build dai widget)*/ + SND_SOC_DAPM_AIF_IN("AIF DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), +}; + + +/*************************************** DAPM routes *******************************************/ +/*ac102 dapm routes*/ +static const struct snd_soc_dapm_route ac102_dapm_routes[] = { + /*MIC PGA*/ + {"MIC PGA", NULL, "MICP"}, + {"MIC PGA", NULL, "MICN"}, + + /*ADC DMIC MUX*/ + {"ADC DMIC MUX", "ADC switch", "MIC PGA"}, + {"ADC DMIC MUX", "DMIC switch", "DMIC"}, + + /*TX MIXL*/ + {"TX MIXL", "RECD_DAT switch", "ADC DMIC MUX"}, + {"TX MIXL", "PLAY_DAT switch", "DAC MIXER"}, + + /*TX MIXR*/ + {"TX MIXR", "RECD_DAT switch", "ADC DMIC MUX"}, + {"TX MIXR", "RXM switch", "RX MIXER"}, + + /*RX MIXER*/ + {"RX MIXER", "RXL switch", "AIF DAC IN"}, + {"RX MIXER", "RXR switch", "AIF DAC IN"}, + + /*DAC MIXER*/ + {"DAC MIXER", "RXM switch", "RX MIXER"}, + {"DAC MIXER", "RECD_DAT switch", "ADC DMIC MUX"}, + + /*LINEOUT PGA*/ + {"LINEOUT PGA", NULL, "DAC MIXER"}, + + /*LINEOUT*/ + {"LINEOUTP", NULL, "LINEOUT PGA"}, + {"LINEOUTN", NULL, "LINEOUT PGA"}, + + /*AIF ADC OUT*/ + {"AIF ADC OUT", NULL, "TX MIXL"}, + {"AIF ADC OUT", NULL, "TX MIXR"}, +}; + + +static int ac102_read(u8 reg, u8 *rt_value, struct i2c_client *client) +{ + int ret; + u8 read_cmd[3] = {0}; + u8 cmd_len = 0; + + read_cmd[0] = reg; + cmd_len = 1; + + if (client == NULL || client->adapter == NULL) { + pr_err("ac102_read client or client->adapter is NULL\n"); + return -1; + } + + ret = i2c_master_send(client, read_cmd, cmd_len); + if (ret != cmd_len) { + pr_err("ac102_read error1->[REG-0x%02x]\n", reg); + return -1; + } + + ret = i2c_master_recv(client, rt_value, 1); + if (ret != 1) { + pr_err("ac102_read error2->[REG-0x%02x], ret=%d\n", reg, ret); + return -1; + } + + return 0; +} + +static int ac102_write(u8 reg, unsigned char value, struct i2c_client *client) +{ + int ret = 0; + u8 write_cmd[2] = {0}; + + write_cmd[0] = reg; + write_cmd[1] = value; + + if (client == NULL || client->adapter == NULL) { + pr_err("ac102_write client or client->adapter is NULL\n"); + return -1; + } + + ret = i2c_master_send(client, write_cmd, 2); + if (ret != 2) { + pr_err("ac102_write error->[REG-0x%02x,val-0x%02x]\n", reg, value); + return -1; + } + + return 0; +} + +static int ac102_update_bits(u8 reg, u8 mask, u8 value, struct i2c_client *client) +{ + u8 val_old, val_new; + + ac102_read(reg, &val_old, client); + val_new = (val_old & ~mask) | (value & mask); + if (val_new != val_old) { + ac102_write(reg, val_new, client); + } + + return 0; +} + +#if 0 +static int ac102_multi_chips_read(u8 reg, unsigned char *rt_value) +{ + u8 i; + + for (i = 0; i < AC102_CHIP_NUMS; i++) { + ac102_read(reg, rt_value++, i2c_ctrl[i]); + } + return 0; +} +#endif + +static int ac102_multi_chips_write(u8 reg, unsigned char value) +{ + u8 i; + + for (i = 0; i < AC102_CHIP_NUMS; i++) { + ac102_write(reg, value, i2c_ctrl[i]); + } + + return 0; +} + +static int ac102_multi_chips_update_bits(u8 reg, u8 mask, u8 value) +{ + u8 i; + + for (i = 0; i < AC102_CHIP_NUMS; i++) { + ac102_update_bits(reg, mask, value, i2c_ctrl[i]); + } + + return 0; +} + + +static void ac102_hw_init(int stream, struct i2c_client *i2c) +{ + /*** Chip reset ***/ + //ac102_write(CHIP_AUDIO_RST, 0x34, i2c); /*0x00=0x34: resets all registers and all digital modules to their default state*/ + + /*** Analog voltage enable ***/ + ac102_write(PWR_CTRL1, 0x73, i2c); /*0x01=0x73: ALDOOUT=1.8V, DLDOOUT=1.2V, MICBISA=2.39V*/ + ac102_write(PWR_CTRL2, 0x1F, i2c); /*0x02=0x1F: ALDO/DLDO/MICBIAS/VREF/IREF Enable*/ + + /*** SYSCLK Config ***/ + ac102_update_bits(SYS_CLK_ENA, 0x1<> 8)< 1 + if (i2c->addr == 0x33) { + ac102_write(I2S_RX_MIX_SRC, 0x01, i2c); /*0x18=0x01: RX Mixer Source select RXL, and Mixer Gain set 0dB*/ + } else if (i2c->addr == 0x30) { + ac102_write(I2S_RX_MIX_SRC, 0x02, i2c); /*0x18=0x02: RX Mixer Source select RXR, and Mixer Gain set 0dB*/ + } +#else + ac102_write(I2S_RX_MIX_SRC, 0x0F, i2c); /*0x18=0x0F: RX Mixer Source select RXL&RXR, and Mixer Gain set -6dB*/ +#endif + + ac102_write(DAC_MIX_SRC, 0x01, i2c); /*0x1D=0x01: DAC Mixer Source select RXM, and Mixer Gain set 0dB*/ +#endif + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + /*** ADC Analog/Digital Config ***/ + /*DMIC config, ADC Delay Funtion Disable, ADC Digital Part Enable*/ + ac102_update_bits(ADC_DIG_CTRL, !AC102_DAPM_EN<%s\n", __func__); + + ac102_multi_chips_update_bits(SYS_CLK_ENA, 1 << SYSCLK_EN, 1 << SYSCLK_EN); + + return 0; +} + +static int ac102_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) +{ + return 0; +} + +static int ac102_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) +{ + u32 i; + u32 bclk_div; + u32 bclk_div_reg_val; + + AC102_DEBUG("\n--->%s\n", __func__); + + if (!div_id) { + /*use div_id to judge Master/Slave mode, 0: Slave mode, 1: Master mode*/ + AC102_DEBUG("AC102 work as Slave mode, don't need to config BCLK_DIV\n\n"); + return 0; + } + + bclk_div = div/(AC102_LRCK_PERIOD); /*default PCM mode*/ + /*bclk_div = div/(2*AC102_LRCK_PERIOD);*/ /*I2S/LJ/RJ mode*/ + + for (i = 0; i < ARRAY_SIZE(ac102_bclk_div); i++) { + if (ac102_bclk_div[i].real_val == bclk_div) { + bclk_div_reg_val = ac102_bclk_div[i].reg_val; + AC102_DEBUG("AC102 set BCLK_DIV_[%u]\n\n", bclk_div); + break; + } + } + + if (i == ARRAY_SIZE(ac102_bclk_div)) { + pr_err("AC102 don't support BCLK_DIV_[%u]\n\n", bclk_div); + return -EINVAL; + } + + /*AC102 set BCLK DIV*/ + ac102_multi_chips_update_bits(I2S_BCLK_CTRL, 0xf<dev); + + AC102_DEBUG("\n--->%s\n", __func__); + + /*AC102 config Master/Slave mode*/ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /*AC102 Master*/ + AC102_DEBUG("AC102 set to work as Master\n"); + ac102_update_bits(I2S_CTRL, 0x3<i2c); /*BCLK & LRCK output*/ + break; + case SND_SOC_DAIFMT_CBS_CFS: /*AC102 Slave*/ + AC102_DEBUG("AC102 set to work as Slave\n"); + ac102_update_bits(I2S_CTRL, 0x3<i2c); /*BCLK & LRCK input*/ + break; + default: + pr_err("AC102 Master/Slave mode config error:%u\n\n", (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12); + return -EINVAL; + } + for (i = 0; i < AC102_CHIP_NUMS; i++) { /*multi_chips: only one chip set as Master, and the others also need to set as Slave*/ + if (i2c_ctrl[i] == ac102->i2c) + continue; + ac102_update_bits(I2S_CTRL, 0x3<> 8); + return -EINVAL; + } + ac102_multi_chips_update_bits(I2S_BCLK_CTRL, 0x1 << BCLK_POLARITY, brck_polarity << BCLK_POLARITY); + ac102_multi_chips_update_bits(I2S_LRCK_CTRL1, 0x1 << LRCK_POLARITY, lrck_polarity << LRCK_POLARITY); + + return 0; +} + +static int ac102_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + u8 i; + u8 channels; + u8 channels_en; + u8 sample_resolution; + + AC102_DEBUG("\n--->%s\n", __func__); + + /*AC102 hw init*/ + for (i = 0; i < AC102_CHIP_NUMS; i++) { + ac102_hw_init(substream->stream, i2c_ctrl[i]); + } + + /*AC102 set channels*/ + channels = params_channels(params); + for (i = 0; i < (channels <= AC102_CHIP_NUMS ? channels : AC102_CHIP_NUMS); i++) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ac102_update_bits(I2S_SLOT_CTRL, 0x3 << RX_CHSEL, (channels-1) << RX_CHSEL, i2c_ctrl[i]); + ac102_update_bits(I2S_CTRL, 0x1 << RXEN, 0x1 << RXEN, i2c_ctrl[i]); + if (i < 2) + ac102_write(I2S_RX_CHMP_CTRL, 0xe4, i2c_ctrl[i]); + else + ac102_write(I2S_RX_CHMP_CTRL, 0x4e, i2c_ctrl[i]); + } else { + channels_en = AC102_CHIP_NUMS > 1 ? 1 << i : ((1 << channels) - 1) << i; + ac102_update_bits(I2S_SLOT_CTRL, 0x3 << TX_CHSEL, (channels - 1) << TX_CHSEL, i2c_ctrl[i]); + ac102_write(I2S_TX_CTRL, channels_en, i2c_ctrl[i]); + ac102_update_bits(I2S_CTRL, 0x1 << TXEN, 0x1 << TXEN, i2c_ctrl[i]); + } + } + for (; i < AC102_CHIP_NUMS; i++) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ac102_update_bits(I2S_SLOT_CTRL, 0x3 << RX_CHSEL, 0 << RX_CHSEL, i2c_ctrl[i]); + ac102_update_bits(I2S_CTRL, 0x1 << RXEN, 0x0 << RXEN, i2c_ctrl[i]); + } else { + ac102_update_bits(I2S_SLOT_CTRL, 0x3 << TX_CHSEL, 0 << TX_CHSEL, i2c_ctrl[i]); + ac102_write(I2S_TX_CTRL, 0, i2c_ctrl[i]); + ac102_update_bits(I2S_CTRL, 0x1 << TXEN, 0x0 << TXEN, i2c_ctrl[i]); + } + } + + /*AC102 set sample resorution*/ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + sample_resolution = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + sample_resolution = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + sample_resolution = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + sample_resolution = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + sample_resolution = 32; + break; + default: + dev_err(dai->dev, "AC102 don't supported the sample resolution: %u\n", params_format(params)); + return -EINVAL; + } + ac102_multi_chips_update_bits(I2S_FMT_CTRL2, 0x7 << SAMPLE_RESOLUTION, (sample_resolution / 4 - 1) << SAMPLE_RESOLUTION); + + /*AC102 Globle enable*/ + ac102_multi_chips_update_bits(I2S_CTRL, 0x1 << GEN, 0x1 << GEN); + + return 0; +} + +static int ac102_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + AC102_DEBUG("\n--->%s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + AC102_DEBUG("AC102 Capture path disable\n"); + ac102_multi_chips_update_bits(PWR_CTRL2, 0x1 << MBIAS_EN, 0x0 << MBIAS_EN); /*MICBIAS disable*/ + ac102_multi_chips_update_bits(ADC_DIG_CTRL, !AC102_DAPM_EN << ADC_DIG_EN, 0x0 << ADC_DIG_EN); /*ADC Digital disable*/ + ac102_multi_chips_update_bits(ADC_ANA_CTRL1, !AC102_DAPM_EN << ADC_PGA_GEN, 0x0 << ADC_PGA_GEN); /*ADC Analog(ADC&PGA) disable*/ + ac102_multi_chips_update_bits(AGC_CTRL, 0x1 << AGC_EN, 0x0 << AGC_EN); /*ADC AGC disable*/ + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + AC102_DEBUG("AC102 Playback path disable\n"); + ac102_multi_chips_update_bits(DAC_DIG_CTRL, !AC102_DAPM_EN << DAC_DIG_EN, 0x0 << DAC_DIG_EN); /*DAC Digital disable*/ + ac102_multi_chips_update_bits(SYS_FUNC_CTRL, !AC102_DAPM_EN << DAC_ANA_OUT_EN, 0 << DAC_ANA_OUT_EN); /*DAC Analog disable*/ + ac102_multi_chips_update_bits(EQ_CTRL, 0x1 << EQ_EN, 0x0 << EQ_EN); /*DAC EQ disable*/ + } + +#if 0 /*!AC102_DAPM_EN*/ + AC102_DEBUG("AC102 resets all registers and all digital modules to their default state\n\n"); + ac102_multi_chips_write(CHIP_AUDIO_RST, 0x34); +#endif + + return 0; +} + + +/*** define ac102 dai_ops struct ***/ +static const struct snd_soc_dai_ops ac102_dai_ops = { + /*DAI clocking configuration*/ + .set_sysclk = ac102_set_sysclk, + .set_pll = ac102_set_pll, + .set_clkdiv = ac102_set_clkdiv, + + /*ALSA PCM audio operations*/ + .hw_params = ac102_hw_params, + .hw_free = ac102_hw_free, + + /*DAI format configuration*/ + .set_fmt = ac102_set_fmt, +}; + +/*** define ac102 dai_driver struct ***/ +#if AC102_CHIP_NUMS > 0 +static struct snd_soc_dai_driver ac102_dai0 = { + .name = "ac102-pcm0", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .ops = &ac102_dai_ops, +}; +#endif + +#if AC102_CHIP_NUMS > 1 +static struct snd_soc_dai_driver ac102_dai1 = { + .name = "ac102-pcm1", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .ops = &ac102_dai_ops, +}; +#endif + +#if AC102_CHIP_NUMS > 2 +static struct snd_soc_dai_driver ac102_dai2 = { + .name = "ac102-pcm2", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .ops = &ac102_dai_ops, +}; +#endif + +#if AC102_CHIP_NUMS > 3 +static struct snd_soc_dai_driver ac102_dai3 = { + .name = "ac102-pcm3", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = (AC102_CHIP_NUMS > 1 ? AC102_CHIP_NUMS : 2), + .rates = AC102_RATES, + .formats = AC102_FORMATS, + }, + .ops = &ac102_dai_ops, +}; +#endif + +static struct snd_soc_dai_driver *ac102_dai[] = { +#if AC102_CHIP_NUMS > 0 + &ac102_dai0, +#endif + +#if AC102_CHIP_NUMS > 1 + &ac102_dai1, +#endif + +#if AC102_CHIP_NUMS > 2 + &ac102_dai2, +#endif + +#if AC102_CHIP_NUMS > 3 + &ac102_dai3, +#endif +}; + + +static int ac102_probe(struct snd_soc_codec *codec) +{ + struct ac102_priv *ac102 = dev_get_drvdata(codec->dev); + int ret = 0; + + codec->control_data = devm_regmap_init_i2c(ac102->i2c, &ac102_regmap_config); + ret = PTR_RET(codec->control_data); + if (ret) { + dev_err(codec->dev, "AC102 regmap init I2C Failed: %d\n", ret); + return ret; + } + ac102->codec = codec; + + return 0; +} + +static int ac102_remove(struct snd_soc_codec *codec) +{ + return 0; +} + + +static int ac102_suspend(struct snd_soc_codec *codec) +{ +#if AC102_MATCH_DTS_EN + struct ac102_priv *ac102 = dev_get_drvdata(codec->dev); + + if (regulator_en && !IS_ERR(ac102->vol_supply.vcc)) { + regulator_disable(ac102->vol_supply.vcc); + regulator_en = 0; + } +#endif + + return 0; +} + +static int ac102_resume(struct snd_soc_codec *codec) +{ +#if AC102_MATCH_DTS_EN + struct ac102_priv *ac102 = dev_get_drvdata(codec->dev); + int ret; + + if (!regulator_en && !IS_ERR(ac102->vol_supply.vcc)) { + ret = regulator_enable(ac102->vol_supply.vcc); + if (ret != 0) + pr_err("[AC102] %s: some error happen, fail to enable regulator!\n", __func__); + regulator_en = 1; + } +#endif + + return 0; +} + + +static unsigned int ac102_codec_read(struct snd_soc_codec *codec, unsigned int reg) +{ + /*AC102_DEBUG("\n--->%s\n",__FUNCTION__);*/ + u8 val_r; + struct ac102_priv *ac102 = dev_get_drvdata(codec->dev); + + ac102_read(reg, &val_r, ac102->i2c); + return val_r; +} + +static int ac102_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) +{ + /*AC102_DEBUG("\n--->%s\n",__FUNCTION__);*/ + ac102_multi_chips_write(reg, value); + return 0; +} + + +/*** define ac102 codec_driver struct ***/ +static const struct snd_soc_codec_driver ac102_soc_codec_driver = { + .probe = ac102_probe, + .remove = ac102_remove, + .suspend = ac102_suspend, + .resume = ac102_resume, + +#if AC102_CODEC_RW_USER_EN + .read = ac102_codec_read, + .write = ac102_codec_write, +#endif + +#if AC102_DAPM_EN + .component_driver = { + .controls = ac102_controls, + .num_controls = ARRAY_SIZE(ac102_controls), + .dapm_widgets = ac102_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ac102_dapm_widgets), + .dapm_routes = ac102_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ac102_dapm_routes), + }, +#endif +}; + + +static ssize_t ac102_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int val = 0, flag = 0; + u8 i = 0, reg, num, value_w, value_r; + + struct ac102_priv *ac102 = dev_get_drvdata(dev); + val = simple_strtol(buf, NULL, 16); + flag = (val >> 16) & 0xFF; + + if (flag) { + reg = (val >> 8) & 0xFF; + value_w = val & 0xFF; + printk("\nWrite: start REG:0x%02x,val:0x%02x,count:0x%02x\n", reg, value_w, flag); + while (flag--) { + ac102_write(reg, value_w, ac102->i2c); + printk("Write 0x%02x to REG:0x%02x\n", value_w, reg); + reg++; + } + } else { + reg = (val >> 8) & 0xFF; + num = val & 0xff; + printk("\nRead: start REG:0x%02x,count:0x%02x\n", reg, num); + + do { + value_r = 0; + ac102_read(reg, &value_r, ac102->i2c); + printk("REG[0x%02x]: 0x%02x; ", reg, value_r); + reg++; + i++; + if ((i == num) || (i % 4 == 0)) + printk("\n"); + } while (i < num); + } + + return count; +} + +static ssize_t ac102_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + printk("/*** AC102 driver version: V1.0 ***/\n"); + printk("echo flag|reg|val > ac102\n"); + printk("eg->read start addres=0x06,count=0x10: echo 0610 >ac102\n"); + printk("eg->write start addres=0x50,value=0x00,count=0x4: echo 45000 >ac102\n"); + return 0; +} + +static DEVICE_ATTR(ac102, 0644, ac102_show, ac102_store); + +static struct attribute *ac102_debug_attrs[] = { + &dev_attr_ac102.attr, + NULL, +}; + +static struct attribute_group ac102_debug_attr_group = { + .name = "ac102_debug", + .attrs = ac102_debug_attrs, +}; + + +static int ac102_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) +{ + struct ac102_priv *ac102; +#if AC102_MATCH_DTS_EN + struct device_node *np = i2c->dev.of_node; + char *regulator_name = NULL; +#endif + int ret = 0; + + ac102 = devm_kzalloc(&i2c->dev, sizeof(struct ac102_priv), GFP_KERNEL); + if (ac102 == NULL) { + dev_err(&i2c->dev, "Unable to allocate ac102 private data\n"); + return -ENOMEM; + } + + ac102->i2c = i2c; + dev_set_drvdata(&i2c->dev, ac102); + +#if AC102_MATCH_DTS_EN + if (!regulator_en) { + ret = of_property_read_string(np, AC102_REGULATOR_NAME, ®ulator_name); + if (ret) { + pr_err("get ac102 regulator name failed \n"); + } else { + ac102->vol_supply.vcc = regulator_get(NULL, regulator_name); + if (IS_ERR(ac102->vol_supply.vcc) || !ac102->vol_supply.vcc) { + pr_err("get ac102 audio-3v3 failed, return!\n"); + return -EFAULT; + } + regulator_set_voltage(ac102->vol_supply.vcc, 3300000, 3300000);/*1800000 uV*/ + ret = regulator_enable(ac102->vol_supply.vcc); + if (ret != 0) + pr_err("[AC102] %s: some error happen, fail to enable regulator!\n", __func__); + regulator_en = 1; + } + } +#endif + + if (i2c_id->driver_data < AC102_CHIP_NUMS) { + i2c_ctrl[i2c_id->driver_data] = i2c; + ret = snd_soc_register_codec(&i2c->dev, + &ac102_soc_codec_driver, + ac102_dai[i2c_id->driver_data], 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register ac102 codec: %d\n", ret); + } + ac102_update_bits(AGC_CTRL, 0x1<driver_data)); + } + + ret = sysfs_create_group(&i2c->dev.kobj, &ac102_debug_attr_group); + if (ret) { + pr_err("failed to create attr group\n"); + } + + return ret; +} + +static int ac102_i2c_remove(struct i2c_client *i2c) +{ + /*sysfs_remove_group(&i2c->dev.kobj, &ac102_debug_attr_group);*/ + snd_soc_unregister_codec(&i2c->dev); + return 0; +} + +/*I2C devices register method_3: i2c_detect*/ +static int ac102_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + u8 ac102_chip_id; + struct i2c_adapter *adapter = client->adapter; + + ac102_read(CHIP_AUDIO_RST, &ac102_chip_id, client); + AC102_DEBUG("\nAC102_Chip_ID on I2C-%d:0x%02X\n", adapter->nr, ac102_chip_id); + + if (ac102_chip_id == 0x12) { + if (client->addr == 0x33) { + strlcpy(info->type, "ac102_0", I2C_NAME_SIZE); + return 0; + } else if (client->addr == 0x30) { + strlcpy(info->type, "ac102_1", I2C_NAME_SIZE); + return 0; + } + } + + return -ENODEV; +} + + +/*I2C devices address used in register method_3*/ +static const unsigned short ac102_i2c_addr[] = { +#if AC102_CHIP_NUMS > 0 + 0x33, +#endif + +#if AC102_CHIP_NUMS > 1 + 0x30, +#endif + +#if AC102_CHIP_NUMS > 2 + 0x33, +#endif + +#if AC102_CHIP_NUMS > 3 + 0x30, +#endif + + I2C_CLIENT_END, +}; + +/*I2C devices register method_1: i2c_board_info (i2c_register_board_info)*/ +/*I2C devices register method_2: device tree source (in .dts file)*/ +static struct i2c_board_info const ac102_i2c_board_info[] = { +#if AC102_CHIP_NUMS > 0 + {I2C_BOARD_INFO("ac102_0", 0x33),}, +#endif + +#if AC102_CHIP_NUMS > 1 + {I2C_BOARD_INFO("ac102_1", 0x30),}, +#endif + +#if AC102_CHIP_NUMS > 2 + {I2C_BOARD_INFO("ac102_2", 0x33),}, +#endif + +#if AC102_CHIP_NUMS > 3 + {I2C_BOARD_INFO("ac102_3", 0x30),}, +#endif +}; + +/*I2C driver and devices match method_1: i2c_device_id*/ +static const struct i2c_device_id ac102_i2c_id[] = { +#if AC102_CHIP_NUMS > 0 + { "ac102_0", 0 }, +#endif + +#if AC102_CHIP_NUMS > 1 + { "ac102_1", 1 }, +#endif + +#if AC102_CHIP_NUMS > 2 + { "ac102_2", 2 }, +#endif + +#if AC102_CHIP_NUMS > 3 + { "ac102_3", 3 }, +#endif + {}, +}; +MODULE_DEVICE_TABLE(i2c, ac102_i2c_id); + +/*I2C driver and devices match method_2: of_device_id (devices tree)*/ +static const struct of_device_id ac102_dt_ids[] = { +#if AC102_CHIP_NUMS > 0 + { .compatible = "ac102_0", }, +#endif + +#if AC102_CHIP_NUMS > 1 + { .compatible = "ac102_1", }, +#endif + +#if AC102_CHIP_NUMS > 2 + { .compatible = "ac102_2", }, +#endif + +#if AC102_CHIP_NUMS > 3 + { .compatible = "ac102_3", }, +#endif + {}, +}; +MODULE_DEVICE_TABLE(of, ac102_dt_ids); + +static struct i2c_driver ac102_i2c_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ac102", + .owner = THIS_MODULE, +#if AC102_MATCH_DTS_EN + .of_match_table = ac102_dt_ids, +#endif + }, + .probe = ac102_i2c_probe, + .remove = ac102_i2c_remove, + .id_table = ac102_i2c_id, +#if !AC102_MATCH_DTS_EN + .address_list = ac102_i2c_addr, + .detect = ac102_i2c_detect, +#endif +}; + +static int __init ac102_init(void) +{ + int ret ; + ret = i2c_add_driver(&ac102_i2c_driver); + if (ret != 0) + pr_err("Failed to register ac102 i2c driver : %d \n", ret); + + return ret; +} +module_init(ac102_init); + +static void __exit ac102_exit(void) +{ + i2c_del_driver(&ac102_i2c_driver); +} +module_exit(ac102_exit); + +MODULE_DESCRIPTION("ASoC ac102 codec driver"); +MODULE_AUTHOR("panjunwen"); +MODULE_LICENSE("GPL"); +