// SPDX-License-Identifier: GPL-2.0+ /* * rv1106_codec.c - Rockchip RV1106 SoC Codec Driver * * Copyright (C) 2022 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rv1106_codec.h" #if defined(CONFIG_DEBUG_FS) #include #include #include #endif #define CODEC_DRV_NAME "rv1106-acodec" #define PERI_GRF_PERI_CON1 0x004 #define ACODEC_AD2DA_LOOP_MSK (1 << 23) #define ACODEC_AD2DA_LOOP_EN (1 << 7) #define ACODEC_AD2DA_LOOP_DIS (1 << 7) /* Control the i2s sdo sdi interface that connect to internal acodec * 1: connect to internal acodec * 0: connect to external acodec */ #define ACODEC_MSK (1 << 22) #define ACODEC_EN (1 << 6) #define ACODEC_DIS (0 << 6) #define LR(b, x, v) (((1 << b) & x) ? v : 0) #define L(x, v) LR(0, x, v) #define R(x, v) LR(1, x, v) #define ADCL (1 << 0) #define ADCR (1 << 1) #define NOT_SPECIFIED (-1) enum soc_id_e { SOC_RV1103 = 0x1103, SOC_RV1106 = 0x1106, }; enum adc_mode_e { DIFF_ADCL = 0, /* Differential ADCL, the ADCR is not used */ SING_ADCL, /* Single-end ADCL, the ADCR is not used */ DIFF_ADCR, /* Differential ADCR, the ADCL is not used */ SING_ADCR, /* Single-end ADCR, the ADCL is not used */ SING_ADCLR, /* Single-end ADCL and ADCR */ DIFF_ADCLR, /* Differential ADCL and ADCR (Not supported on rv1103 codec) */ ADC_MODE_NUM, }; struct rv1106_codec_priv { const struct device *plat_dev; struct device dev; struct reset_control *reset; struct regmap *regmap; struct regmap *grf; struct clk *pclk_acodec; struct clk *mclk_acodec; struct clk *mclk_cpu; struct gpio_desc *pa_ctl_gpio; struct snd_soc_component *component; enum adc_mode_e adc_mode; enum soc_id_e soc_id; u32 pa_ctl_delay_ms; u32 micbias_volt; /* AGC L/R Off/on */ unsigned int agc_l; unsigned int agc_r; /* AGC L/R Approximate Sample Rate */ unsigned int agc_asr_l; unsigned int agc_asr_r; /* ADC MIC Mute/Work */ unsigned int mic_mute_l; unsigned int mic_mute_r; /* For the high pass filter */ unsigned int hpf_cutoff; /* Specify init gains after codec startup */ unsigned int init_mic_gain; unsigned int init_alc_gain; unsigned int init_lineout_gain; bool adc_enable; bool dac_enable; bool micbias_enable; bool micbias_used; #if defined(CONFIG_DEBUG_FS) struct dentry *dbg_codec; #endif }; static const DECLARE_TLV_DB_SCALE(rv1106_codec_alc_agc_gain_tlv, -1800, 150, 2850); static const DECLARE_TLV_DB_SCALE(rv1106_codec_alc_agc_max_gain_tlv, -1350, 600, 2850); static const DECLARE_TLV_DB_SCALE(rv1106_codec_alc_agc_min_gain_tlv, -1800, 600, 2400); static const DECLARE_TLV_DB_SCALE(rv1106_codec_adc_alc_gain_tlv, -900, 150, 3750); static const DECLARE_TLV_DB_SCALE(rv1106_codec_adc_dig_gain_tlv, -9750, 50, 3000); static const DECLARE_TLV_DB_SCALE(rv1106_codec_dac_lineout_gain_tlv, -3900, 150, 600); static const DECLARE_TLV_DB_RANGE(rv1106_codec_adc_mic_gain_tlv, 1, 1, TLV_DB_SCALE_ITEM(0, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0), 3, 3, TLV_DB_SCALE_ITEM(1200, 0, 0), ); static const DECLARE_TLV_DB_RANGE(rv1106_codec_dac_hpmix_gain_tlv, 1, 2, TLV_DB_SCALE_ITEM(0, 600, 0), ); static int check_micbias(int volt); static int rv1106_codec_adc_enable(struct rv1106_codec_priv *rv1106); static int rv1106_codec_adc_disable(struct rv1106_codec_priv *rv1106); static int rv1106_codec_micbias_enable(struct rv1106_codec_priv *rv1106, int micbias_volt); static int rv1106_codec_hpmix_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_hpmix_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_micbias_disable(struct rv1106_codec_priv *rv1106); static int rv1106_codec_hpf_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_hpf_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_adc_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_adc_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_agc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_agc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_agc_asr_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_agc_asr_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_mic_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_mic_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_mic_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_mic_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_main_micbias_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int rv1106_codec_main_micbias_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static const char *offon_text[2] = { [0] = "Off", [1] = "On", }; static const char *mute_text[2] = { [0] = "Work", [1] = "Mute", }; /* ADC MICBIAS Volt */ #define MICBIAS_VOLT_NUM 8 #define MICBIAS_VREFx0_8 0 #define MICBIAS_VREFx0_825 1 #define MICBIAS_VREFx0_85 2 #define MICBIAS_VREFx0_875 3 #define MICBIAS_VREFx0_9 4 #define MICBIAS_VREFx0_925 5 #define MICBIAS_VREFx0_95 6 #define MICBIAS_VREFx0_975 7 static const char *micbias_volts_enum_array[MICBIAS_VOLT_NUM] = { [MICBIAS_VREFx0_8] = "VREFx0_8", [MICBIAS_VREFx0_825] = "VREFx0_825", [MICBIAS_VREFx0_85] = "VREFx0_85", [MICBIAS_VREFx0_875] = "VREFx0_875", [MICBIAS_VREFx0_9] = "VREFx0_9", [MICBIAS_VREFx0_925] = "VREFx0_925", [MICBIAS_VREFx0_95] = "VREFx0_95", [MICBIAS_VREFx0_975] = "VREFx0_975", }; static const char *adc_mode_enum_array[ADC_MODE_NUM] = { [DIFF_ADCL] = "DiffadcL", [SING_ADCL] = "SingadcL", [DIFF_ADCR] = "DiffadcR", [SING_ADCR] = "SingadcR", [SING_ADCLR] = "SingadcLR", [DIFF_ADCLR] = "DiffadcLR", }; static const struct soc_enum rv1106_adc_mode_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adc_mode_enum_array), adc_mode_enum_array), }; static const struct soc_enum rv1106_micbias_volts_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(micbias_volts_enum_array), micbias_volts_enum_array), }; /* ADC MICBIAS Main Switch */ static const struct soc_enum rv1106_main_micbias_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), }; static const struct soc_enum rv1106_hpf_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), }; /* ALC AGC Switch */ static const struct soc_enum rv1106_agc_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(offon_text), offon_text), }; /* ADC MIC Mute/Work Switch */ static const struct soc_enum rv1106_mic_mute_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(mute_text), mute_text), SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(mute_text), mute_text), }; /* ALC AGC Approximate Sample Rate */ #define AGC_ASR_NUM 8 #define AGC_ASR_96KHZ 0 #define AGC_ASR_48KHZ 1 #define AGC_ASR_44_1KHZ 2 #define AGC_ASR_32KHZ 3 #define AGC_ASR_24KHZ 4 #define AGC_ASR_16KHZ 5 #define AGC_ASR_12KHZ 6 #define AGC_ASR_8KHZ 7 static const char *agc_asr_text[AGC_ASR_NUM] = { [AGC_ASR_96KHZ] = "96KHz", [AGC_ASR_48KHZ] = "48KHz", [AGC_ASR_44_1KHZ] = "44.1KHz", [AGC_ASR_32KHZ] = "32KHz", [AGC_ASR_24KHZ] = "24KHz", [AGC_ASR_16KHZ] = "16KHz", [AGC_ASR_12KHZ] = "12KHz", [AGC_ASR_8KHZ] = "8KHz", }; static const struct soc_enum rv1106_agc_asr_enum_array[] = { SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), }; static const struct snd_kcontrol_new rv1106_codec_dapm_controls[] = { /* ADC MIC */ SOC_SINGLE_EXT_TLV("ADC MIC Left Gain", ACODEC_ADC_ANA_CTL2, ACODEC_ADC_L_MIC_GAIN_SFT, ACODEC_ADC_MIC_GAIN_MAX, 0, rv1106_codec_mic_gain_get, rv1106_codec_mic_gain_put, rv1106_codec_adc_mic_gain_tlv), SOC_SINGLE_EXT_TLV("ADC MIC Right Gain", ACODEC_ADC_ANA_CTL2, ACODEC_ADC_R_MIC_GAIN_SFT, ACODEC_ADC_MIC_GAIN_MAX, 0, rv1106_codec_mic_gain_get, rv1106_codec_mic_gain_put, rv1106_codec_adc_mic_gain_tlv), /* ADC ALC */ SOC_SINGLE_RANGE_TLV("ADC ALC Left Volume", ACODEC_ADC_ANA_CTL4, ACODEC_ADC_L_ALC_GAIN_SFT, ACODEC_ADC_L_ALC_GAIN_MIN, ACODEC_ADC_L_ALC_GAIN_MAX, 0, rv1106_codec_adc_alc_gain_tlv), SOC_SINGLE_RANGE_TLV("ADC ALC Right Volume", ACODEC_ADC_ANA_CTL5, ACODEC_ADC_R_ALC_GAIN_SFT, ACODEC_ADC_R_ALC_GAIN_MIN, ACODEC_ADC_R_ALC_GAIN_MAX, 0, rv1106_codec_adc_alc_gain_tlv), /* ADC Digital Volume */ SOC_SINGLE_RANGE_TLV("ADC Digital Left Volume", ACODEC_ADC_L_DIG_VOL, ACODEC_ADC_L_DIG_VOL_SFT, ACODEC_ADC_L_DIG_VOL_MIN, ACODEC_ADC_L_DIG_VOL_MAX, 0, rv1106_codec_adc_dig_gain_tlv), SOC_SINGLE_RANGE_TLV("ADC Digital Right Volume", ACODEC_ADC_R_DIG_VOL, ACODEC_ADC_R_DIG_VOL_SFT, ACODEC_ADC_R_DIG_VOL_MIN, ACODEC_ADC_R_DIG_VOL_MAX, 0, rv1106_codec_adc_dig_gain_tlv), /* ADC High Pass Filter */ SOC_ENUM_EXT("ADC HPF Cut-off", rv1106_hpf_enum_array[0], rv1106_codec_hpf_get, rv1106_codec_hpf_put), /* ALC AGC Group */ SOC_SINGLE_RANGE_TLV("ALC AGC Left Volume", ACODEC_ADC_PGA_AGC_L_CTL3, ACODEC_AGC_PGA_GAIN_SFT, ACODEC_AGC_PGA_GAIN_MIN, ACODEC_AGC_PGA_GAIN_MAX, 0, rv1106_codec_alc_agc_gain_tlv), SOC_SINGLE_RANGE_TLV("ALC AGC Right Volume", ACODEC_ADC_PGA_AGC_R_CTL3, ACODEC_AGC_PGA_GAIN_SFT, ACODEC_AGC_PGA_GAIN_MIN, ACODEC_AGC_PGA_GAIN_MAX, 0, rv1106_codec_alc_agc_gain_tlv), /* ALC AGC MAX */ SOC_SINGLE_RANGE_TLV("ALC AGC Left Max Volume", ACODEC_ADC_PGA_AGC_L_CTL9, ACODEC_AGC_MAX_GAIN_PGA_SFT, ACODEC_AGC_MAX_GAIN_PGA_MIN, ACODEC_AGC_MAX_GAIN_PGA_MAX, 0, rv1106_codec_alc_agc_max_gain_tlv), SOC_SINGLE_RANGE_TLV("ALC AGC Right Max Volume", ACODEC_ADC_PGA_AGC_R_CTL9, ACODEC_AGC_MAX_GAIN_PGA_SFT, ACODEC_AGC_MAX_GAIN_PGA_MIN, ACODEC_AGC_MAX_GAIN_PGA_MAX, 0, rv1106_codec_alc_agc_max_gain_tlv), /* ALC AGC MIN */ SOC_SINGLE_RANGE_TLV("ALC AGC Left Min Volume", ACODEC_ADC_PGA_AGC_L_CTL9, ACODEC_AGC_MIN_GAIN_PGA_SFT, ACODEC_AGC_MIN_GAIN_PGA_MIN, ACODEC_AGC_MIN_GAIN_PGA_MAX, 0, rv1106_codec_alc_agc_min_gain_tlv), SOC_SINGLE_RANGE_TLV("ALC AGC Right Min Volume", ACODEC_ADC_PGA_AGC_R_CTL9, ACODEC_AGC_MIN_GAIN_PGA_SFT, ACODEC_AGC_MIN_GAIN_PGA_MIN, ACODEC_AGC_MIN_GAIN_PGA_MAX, 0, rv1106_codec_alc_agc_min_gain_tlv), /* ALC AGC Switch */ SOC_ENUM_EXT("ALC AGC Left Switch", rv1106_agc_enum_array[0], rv1106_codec_agc_get, rv1106_codec_agc_put), SOC_ENUM_EXT("ALC AGC Right Switch", rv1106_agc_enum_array[1], rv1106_codec_agc_get, rv1106_codec_agc_put), /* ALC AGC Approximate Sample Rate */ SOC_ENUM_EXT("AGC Left Approximate Sample Rate", rv1106_agc_asr_enum_array[0], rv1106_codec_agc_asr_get, rv1106_codec_agc_asr_put), SOC_ENUM_EXT("AGC Right Approximate Sample Rate", rv1106_agc_asr_enum_array[1], rv1106_codec_agc_asr_get, rv1106_codec_agc_asr_put), /* ADC Mode */ SOC_ENUM_EXT("ADC Mode", rv1106_adc_mode_enum_array[0], rv1106_codec_adc_mode_get, rv1106_codec_adc_mode_put), /* ADC MICBIAS Voltage */ SOC_ENUM_EXT("ADC MICBIAS Voltage", rv1106_micbias_volts_enum_array[0], rv1106_codec_micbias_volts_get, rv1106_codec_micbias_volts_put), /* ADC Main MICBIAS Switch */ SOC_ENUM_EXT("ADC Main MICBIAS", rv1106_main_micbias_enum_array[0], rv1106_codec_main_micbias_get, rv1106_codec_main_micbias_put), /* ADC MIC Mute/Work Switch */ SOC_ENUM_EXT("ADC MIC Left Switch", rv1106_mic_mute_enum_array[0], rv1106_codec_mic_mute_get, rv1106_codec_mic_mute_put), SOC_ENUM_EXT("ADC MIC Right Switch", rv1106_mic_mute_enum_array[1], rv1106_codec_mic_mute_get, rv1106_codec_mic_mute_put), /* DAC LINEOUT */ SOC_SINGLE_RANGE_TLV("DAC LINEOUT Volume", ACODEC_DAC_ANA_CTL2, ACODEC_DAC_LINEOUT_GAIN_SFT, ACODEC_DAC_LINEOUT_GAIN_MIN, ACODEC_DAC_LINEOUT_GAIN_MAX, 0, rv1106_codec_dac_lineout_gain_tlv), /* DAC HPMIX */ SOC_SINGLE_EXT_TLV("DAC HPMIX Volume", ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_GAIN_SFT, ACODEC_DAC_HPMIX_GAIN_MAX, 0, rv1106_codec_hpmix_gain_get, rv1106_codec_hpmix_gain_put, rv1106_codec_dac_hpmix_gain_tlv), }; static unsigned int using_adc_lr(enum adc_mode_e adc_mode) { if (adc_mode >= SING_ADCLR && adc_mode <= DIFF_ADCLR) return (ADCL | ADCR); else if (adc_mode >= DIFF_ADCR && adc_mode <= SING_ADCR) return ADCR; else return ADCL; } static bool using_adc_diff(enum adc_mode_e adc_mode) { if (adc_mode == DIFF_ADCL || adc_mode == DIFF_ADCR || adc_mode == DIFF_ADCLR) return true; else return false; } static int check_adc_mode(struct rv1106_codec_priv *rv1106) { if (rv1106->soc_id == SOC_RV1103 && (rv1106->adc_mode == DIFF_ADCLR || rv1106->adc_mode == DIFF_ADCR)) { dev_err(rv1106->plat_dev, "%s: Differential mode rv1103 only supports 'DiffadcL'\n", __func__); return -EINVAL; } return 0; } static int rv1106_codec_adc_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = rv1106->adc_mode; return 0; } static int rv1106_codec_adc_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int last_mode = rv1106->adc_mode; rv1106->adc_mode = ucontrol->value.integer.value[0]; if (check_adc_mode(rv1106)) { dev_err(rv1106->plat_dev, "%s - something error checking ADC mode\n", __func__); rv1106->adc_mode = last_mode; return 0; } return 0; } static int rv1106_codec_agc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; if (e->shift_l) ucontrol->value.integer.value[0] = rv1106->agc_r; else ucontrol->value.integer.value[0] = rv1106->agc_l; return 0; } static int rv1106_codec_agc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int value = ucontrol->value.integer.value[0]; if (value) { /* ALC AGC On */ if (e->shift_l) { /* ALC AGC Right On */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_PGA_AGC_R_CTL9, ACODEC_AGC_FUNC_SEL_MSK, ACODEC_AGC_FUNC_SEL_EN); regmap_update_bits(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, ACODEC_ADC_R_PGA_MSK, ACODEC_ADC_PGA_ALCR_EN); rv1106->agc_r = 1; } else { /* ALC AGC Left On */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_PGA_AGC_L_CTL9, ACODEC_AGC_FUNC_SEL_MSK, ACODEC_AGC_FUNC_SEL_EN); regmap_update_bits(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, ACODEC_ADC_L_PGA_MSK, ACODEC_ADC_PGA_ALCL_EN); rv1106->agc_l = 1; } } else { /* ALC AGC Off */ if (e->shift_l) { /* ALC AGC Right Off */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_PGA_AGC_R_CTL9, ACODEC_AGC_FUNC_SEL_MSK, ACODEC_AGC_FUNC_SEL_DIS); regmap_update_bits(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, ACODEC_ADC_R_PGA_MSK, ACODEC_ADC_PGA_ALCR_DIS); rv1106->agc_r = 0; } else { /* ALC AGC Left Off */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_PGA_AGC_L_CTL9, ACODEC_AGC_FUNC_SEL_MSK, ACODEC_AGC_FUNC_SEL_DIS); regmap_update_bits(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, ACODEC_ADC_L_PGA_MSK, ACODEC_ADC_PGA_ALCL_DIS); rv1106->agc_l = 0; } } return 0; } static int rv1106_codec_agc_asr_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int value; if (e->shift_l) { regmap_read(rv1106->regmap, ACODEC_ADC_PGA_AGC_R_CTL4, &value); rv1106->agc_asr_r = value >> ACODEC_AGC_APPROX_RATE_SFT; ucontrol->value.integer.value[0] = rv1106->agc_asr_r; } else { regmap_read(rv1106->regmap, ACODEC_ADC_PGA_AGC_L_CTL4, &value); rv1106->agc_asr_l = value >> ACODEC_AGC_APPROX_RATE_SFT; ucontrol->value.integer.value[0] = rv1106->agc_asr_l; } return 0; } static int rv1106_codec_agc_asr_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int value; value = ucontrol->value.integer.value[0] << ACODEC_AGC_APPROX_RATE_SFT; if (e->shift_l) { /* ALC AGC Right Approximate Sample Rate */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_PGA_AGC_R_CTL4, ACODEC_AGC_APPROX_RATE_MSK, value); rv1106->agc_asr_r = ucontrol->value.integer.value[0]; } else { /* ALC AGC Left Approximate Sample Rate */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_PGA_AGC_L_CTL4, ACODEC_AGC_APPROX_RATE_MSK, value); rv1106->agc_asr_l = ucontrol->value.integer.value[0]; } return 0; } static int rv1106_codec_mic_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int value; if (e->shift_l) { /* ADC MIC Right Mute/Work Infos */ regmap_read(rv1106->regmap, ACODEC_ADC_BIST_MODE_SEL, &value); rv1106->mic_mute_r = (value & ACODEC_ADC_R_BIST_SINE) >> ACODEC_ADC_R_BIST_SFT; ucontrol->value.integer.value[0] = rv1106->mic_mute_r; } else { /* ADC MIC Left Mute/Work Infos */ regmap_read(rv1106->regmap, ACODEC_ADC_BIST_MODE_SEL, &value); rv1106->mic_mute_l = (value & ACODEC_ADC_L_BIST_SINE) >> ACODEC_ADC_L_BIST_SFT; ucontrol->value.integer.value[0] = rv1106->mic_mute_l; } return 0; } static int rv1106_codec_mic_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int value; if (e->shift_l) { /* ADC MIC Right Mute/Work Configuration */ value = ucontrol->value.integer.value[0] << ACODEC_ADC_R_BIST_SFT; regmap_update_bits(rv1106->regmap, ACODEC_ADC_BIST_MODE_SEL, ACODEC_ADC_R_BIST_SINE, value); rv1106->mic_mute_r = ucontrol->value.integer.value[0]; } else { /* ADC MIC Left Mute/Work Configuration */ value = ucontrol->value.integer.value[0] << ACODEC_ADC_L_BIST_SFT; regmap_update_bits(rv1106->regmap, ACODEC_ADC_BIST_MODE_SEL, ACODEC_ADC_L_BIST_SINE, value); rv1106->mic_mute_l = ucontrol->value.integer.value[0]; } return 0; } static int rv1106_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = rv1106->micbias_volt; return 0; } static int rv1106_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int volt = ucontrol->value.integer.value[0]; int ret; ret = check_micbias(volt); if (ret < 0) { dev_err(rv1106->plat_dev, "Invalid micbias volt: %d\n", volt); return ret; } regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_ADC_LEVEL_RANGE_MICBIAS_MSK, volt); rv1106->micbias_volt = volt; return 0; } static int rv1106_codec_main_micbias_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = rv1106->micbias_enable; return 0; } static int rv1106_codec_main_micbias_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int on = ucontrol->value.integer.value[0]; if (on) { if (!rv1106->micbias_enable) rv1106_codec_micbias_enable(rv1106, rv1106->micbias_volt); } else { if (rv1106->micbias_enable) rv1106_codec_micbias_disable(rv1106); } return 0; } static int rv1106_codec_mic_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return snd_soc_get_volsw_range(kcontrol, ucontrol); } static int rv1106_codec_mic_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int index = ucontrol->value.integer.value[0]; /* * From the TRM, the gain of MIC Boost only supports: * 0dB (index == 1) * 20dB(index == 2) * 12dB(index == 3) */ if ((index < ACODEC_ADC_MIC_GAIN_MIN) || (index > ACODEC_ADC_MIC_GAIN_MAX)) { dev_err(rv1106->plat_dev, "%s: invalid mic gain index: %d\n", __func__, index); return -EINVAL; } return snd_soc_put_volsw_range(kcontrol, ucontrol); } static int rv1106_codec_hpf_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int value; regmap_read(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, &value); if (value & ACODEC_ADC_HPF_MSK) rv1106->hpf_cutoff = 1; else rv1106->hpf_cutoff = 0; ucontrol->value.integer.value[0] = rv1106->hpf_cutoff; return 0; } static int rv1106_codec_hpf_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int value = ucontrol->value.integer.value[0]; if (value) { /* Enable high pass filter for ADCs */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, ACODEC_ADC_HPF_MSK, ACODEC_ADC_HPF_EN); } else { /* Disable high pass filter for ADCs. */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_HPF_PGA_CTL, ACODEC_ADC_HPF_MSK, ACODEC_ADC_HPF_DIS); } rv1106->hpf_cutoff = value; return 0; } static void rv1106_codec_pa_ctrl(struct rv1106_codec_priv *rv1106, bool on) { if (!rv1106->pa_ctl_gpio) return; if (on) { gpiod_direction_output(rv1106->pa_ctl_gpio, on); msleep(rv1106->pa_ctl_delay_ms); } else { gpiod_direction_output(rv1106->pa_ctl_gpio, on); } } static int rv1106_codec_reset(struct snd_soc_component *component) { struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); reset_control_assert(rv1106->reset); usleep_range(10000, 11000); /* estimated value */ reset_control_deassert(rv1106->reset); regmap_write(rv1106->regmap, ACODEC_GLB_CON, 0x00); usleep_range(10000, 11000); /* estimated value */ regmap_write(rv1106->regmap, ACODEC_GLB_CON, ACODEC_CODEC_SYS_WORK | ACODEC_CODEC_CORE_WORK); return 0; } static int rv1106_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: regcache_cache_only(rv1106->regmap, false); regcache_sync(rv1106->regmap); break; case SND_SOC_BIAS_OFF: break; } return 0; } static int rv1106_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: adc_aif2 |= ACODEC_ADC_IO_MODE_SLAVE; adc_aif2 |= ACODEC_ADC_MODE_SLAVE; dac_aif2 |= ACODEC_DAC_IO_MODE_SLAVE; dac_aif2 |= ACODEC_DAC_MODE_SLAVE; break; case SND_SOC_DAIFMT_CBM_CFM: adc_aif2 |= ACODEC_ADC_IO_MODE_MASTER; adc_aif2 |= ACODEC_ADC_MODE_MASTER; dac_aif2 |= ACODEC_DAC_IO_MODE_MASTER; dac_aif2 |= ACODEC_DAC_MODE_MASTER; break; default: return -EINVAL; } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: adc_aif1 |= ACODEC_ADC_I2S_MODE_PCM; dac_aif1 |= ACODEC_DAC_I2S_MODE_PCM; break; case SND_SOC_DAIFMT_I2S: adc_aif1 |= ACODEC_ADC_I2S_MODE_I2S; dac_aif1 |= ACODEC_DAC_I2S_MODE_I2S; break; case SND_SOC_DAIFMT_RIGHT_J: adc_aif1 |= ACODEC_ADC_I2S_MODE_RJ; dac_aif1 |= ACODEC_DAC_I2S_MODE_RJ; break; case SND_SOC_DAIFMT_LEFT_J: adc_aif1 |= ACODEC_ADC_I2S_MODE_LJ; dac_aif1 |= ACODEC_DAC_I2S_MODE_LJ; break; default: return -EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: adc_aif1 |= ACODEC_ADC_I2S_LRC_POL_NORMAL; adc_aif2 |= ACODEC_ADC_I2S_BIT_CLK_POL_NORMAL; dac_aif1 |= ACODEC_DAC_I2S_LRC_POL_NORMAL; dac_aif2 |= ACODEC_DAC_I2S_BIT_CLK_POL_NORMAL; break; case SND_SOC_DAIFMT_IB_IF: adc_aif1 |= ACODEC_ADC_I2S_LRC_POL_REVERSAL; adc_aif2 |= ACODEC_ADC_I2S_BIT_CLK_POL_REVERSAL; dac_aif1 |= ACODEC_DAC_I2S_LRC_POL_REVERSAL; dac_aif2 |= ACODEC_DAC_I2S_BIT_CLK_POL_REVERSAL; break; case SND_SOC_DAIFMT_IB_NF: adc_aif1 |= ACODEC_ADC_I2S_LRC_POL_NORMAL; adc_aif2 |= ACODEC_ADC_I2S_BIT_CLK_POL_REVERSAL; dac_aif1 |= ACODEC_DAC_I2S_LRC_POL_NORMAL; dac_aif2 |= ACODEC_DAC_I2S_BIT_CLK_POL_REVERSAL; break; case SND_SOC_DAIFMT_NB_IF: adc_aif1 |= ACODEC_ADC_I2S_LRC_POL_REVERSAL; adc_aif2 |= ACODEC_ADC_I2S_BIT_CLK_POL_NORMAL; dac_aif1 |= ACODEC_DAC_I2S_LRC_POL_REVERSAL; dac_aif2 |= ACODEC_DAC_I2S_BIT_CLK_POL_NORMAL; break; default: return -EINVAL; } regmap_update_bits(rv1106->regmap, ACODEC_ADC_I2S_CTL0, ACODEC_ADC_I2S_LRC_POL_MSK | ACODEC_ADC_I2S_MODE_MSK, adc_aif1); regmap_update_bits(rv1106->regmap, ACODEC_ADC_I2S_CTL1, ACODEC_ADC_IO_MODE_MSK | ACODEC_ADC_MODE_MSK | ACODEC_ADC_I2S_BIT_CLK_POL_MSK, adc_aif2); regmap_update_bits(rv1106->regmap, ACODEC_DAC_I2S_CTL0, ACODEC_DAC_I2S_LRC_POL_MSK | ACODEC_DAC_I2S_MODE_MSK, dac_aif1); regmap_update_bits(rv1106->regmap, ACODEC_ADC_I2S_CTL1, ACODEC_DAC_IO_MODE_MSK | ACODEC_DAC_MODE_MSK | ACODEC_DAC_I2S_BIT_CLK_POL_MSK, dac_aif2); return 0; } static int rv1106_codec_dac_dig_config(struct rv1106_codec_priv *rv1106, struct snd_pcm_hw_params *params) { unsigned int dac_aif1 = 0, dac_aif2 = 0; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dac_aif1 |= ACODEC_DAC_I2S_VALID_LEN_16BITS; break; case SNDRV_PCM_FORMAT_S20_3LE: dac_aif1 |= ACODEC_DAC_I2S_VALID_LEN_20BITS; break; case SNDRV_PCM_FORMAT_S24_LE: dac_aif1 |= ACODEC_DAC_I2S_VALID_LEN_24BITS; break; case SNDRV_PCM_FORMAT_S32_LE: dac_aif1 |= ACODEC_DAC_I2S_VALID_LEN_32BITS; break; default: return -EINVAL; } dac_aif1 |= ACODEC_DAC_I2S_LR_NORMAL; dac_aif2 |= ACODEC_DAC_I2S_WORK; regmap_update_bits(rv1106->regmap, ACODEC_DAC_I2S_CTL0, ACODEC_DAC_I2S_VALID_LEN_MSK | ACODEC_DAC_I2S_LR_MSK, dac_aif1); regmap_update_bits(rv1106->regmap, ACODEC_DAC_I2S_CTL1, ACODEC_DAC_I2S_MSK, dac_aif2); return 0; } static int rv1106_codec_adc_dig_config(struct rv1106_codec_priv *rv1106, struct snd_pcm_hw_params *params) { unsigned int adc_aif1 = 0, adc_aif2 = 0; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: adc_aif1 |= ACODEC_ADC_I2S_VALID_LEN_16BITS; break; case SNDRV_PCM_FORMAT_S20_3LE: adc_aif1 |= ACODEC_ADC_I2S_VALID_LEN_20BITS; break; case SNDRV_PCM_FORMAT_S24_LE: adc_aif1 |= ACODEC_ADC_I2S_VALID_LEN_24BITS; break; case SNDRV_PCM_FORMAT_S32_LE: adc_aif1 |= ACODEC_ADC_I2S_VALID_LEN_32BITS; break; default: return -EINVAL; } adc_aif1 |= ACODEC_ADC_I2S_DATA_SEL_NORMAL; adc_aif2 |= ACODEC_ADC_I2S_WORK; regmap_update_bits(rv1106->regmap, ACODEC_ADC_I2S_CTL0, ACODEC_ADC_I2S_VALID_LEN_MSK | ACODEC_ADC_I2S_DATA_SEL_MSK, adc_aif1); regmap_update_bits(rv1106->regmap, ACODEC_ADC_I2S_CTL1, ACODEC_ADC_I2S_MSK, adc_aif2); return 0; } static int rv1106_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { if (mute) { /* Mute DAC HPMIX/LINEOUT */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_MUTE_MSK, ACODEC_DAC_L_LINEOUT_MUTE); regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MUTE_MSK, ACODEC_DAC_HPMIX_MUTE); rv1106_codec_pa_ctrl(rv1106, false); } else { /* Unmute DAC HPMIX/LINEOUT */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MUTE_MSK, ACODEC_DAC_HPMIX_WORK); regmap_update_bits(rv1106->regmap, ACODEC_DAC_L_LINEOUT_MUTE_MSK, ACODEC_DAC_MUTE_MSK, ACODEC_DAC_L_LINEOUT_WORK); rv1106_codec_pa_ctrl(rv1106, true); } } return 0; } static int rv1106_codec_dac_enable(struct rv1106_codec_priv *rv1106) { /* Step 01 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_IBIAS_MSK, ACODEC_DAC_IBIAS_EN); udelay(20); /* Step 02 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_REF_VOL_BUF_MSK, ACODEC_DAC_L_REF_VOL_BUF_EN); /* Waiting the stable reference voltage */ usleep_range(1000, 2000); /* Step 03 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_MSK, ACODEC_DAC_L_LINEOUT_EN); udelay(20); /* Step 04 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_SIGNAL_MSK, ACODEC_DAC_L_LINEOUT_SIGNAL_WORK); udelay(20); /* Step 05 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MSK, ACODEC_DAC_HPMIX_EN); udelay(20); /* Step 06 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MDL_MSK, ACODEC_DAC_HPMIX_MDL_WORK); udelay(20); /* Step 07 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_SEL_MSK, ACODEC_DAC_HPMIX_I2S); /* Waiting HPMIX be stable */ usleep_range(18000, 20000); /* Step 08 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_REF_VOL_MSK, ACODEC_DAC_L_REF_VOL_EN); udelay(20); /* Step 09 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_CLK_MSK, ACODEC_DAC_L_CLK_EN); udelay(20); /* Step 10 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_SRC_SIGNAL_MSK, ACODEC_DAC_SRC_SIGNAL_EN); udelay(20); /* Step 11 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_SIGNAL_MSK, ACODEC_DAC_L_SIGNAL_WORK); udelay(20); /* Step 12 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MUTE_MSK, ACODEC_DAC_HPMIX_WORK); udelay(20); /* Step 13 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_MUTE_MSK, ACODEC_DAC_L_LINEOUT_WORK); /* Skip setting gains that Step 14/15 */ rv1106->dac_enable = true; return 0; } static int rv1106_codec_dac_disable(struct rv1106_codec_priv *rv1106) { /* Step 01 */ /* Skip cleaning the gain to GAIN_LINEOUTL */ /* Step 02 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_MUTE_MSK, ACODEC_DAC_L_LINEOUT_MUTE); /* Step 03 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_SIGNAL_MSK, ACODEC_DAC_L_LINEOUT_SIGNAL_INIT); /* Step 04 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL1, ACODEC_DAC_L_LINEOUT_MSK, ACODEC_DAC_L_LINEOUT_DIS); /* Step 05 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MUTE_MSK, ACODEC_DAC_HPMIX_MUTE); /* Step 06 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MDL_MSK, ACODEC_DAC_HPMIX_MDL_INIT); /* Step 07 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_HPMIX_CTL, ACODEC_DAC_HPMIX_MSK, ACODEC_DAC_HPMIX_DIS); /* Step 08 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_SRC_SIGNAL_MSK, ACODEC_DAC_SRC_SIGNAL_DIS); /* Step 09 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_CLK_MSK, ACODEC_DAC_L_CLK_DIS); /* Step 10 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_REF_VOL_MSK, ACODEC_DAC_L_REF_VOL_DIS); /* Step 11 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_REF_VOL_BUF_MSK, ACODEC_DAC_L_REF_VOL_BUF_DIS); /* Step 12 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_IBIAS_MSK, ACODEC_DAC_IBIAS_DIS); /* Step 13 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_SIGNAL_MSK, ACODEC_DAC_L_SIGNAL_INIT); rv1106->dac_enable = false; return 0; } static int rv1106_codec_power_on(struct rv1106_codec_priv *rv1106) { /* vendor step 1 */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL0, ACODEC_DAC_L_REF_POP_SOUND_MSK, ACODEC_DAC_L_REF_POP_SOUND_WORK); /* vendor step 2. Charging */ regmap_update_bits(rv1106->regmap, ACODEC_CURRENT_CHARGE_CTL, ACODEC_ADC_CURRENT_CHARGE_MSK, ACODEC_ADC_SEL_I(0xff)); /* vendor step 3. Supply the power of the analog part. */ /* vendor step 4 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_ADC_REF_VOL_MSK, ACODEC_ADC_REF_VOL_EN); /* vendor step 5. Wait charging completed */ msleep(20); /* vendor step 6 */ regmap_update_bits(rv1106->regmap, ACODEC_CURRENT_CHARGE_CTL, ACODEC_ADC_CURRENT_CHARGE_MSK, ACODEC_ADC_SEL_I(0x02)); return 0; } static int rv1106_codec_power_off(struct rv1106_codec_priv *rv1106) { /* * 0. Keep the power on and disable the DAC and ADC path. */ /* vendor step 1 */ regmap_update_bits(rv1106->regmap, ACODEC_CURRENT_CHARGE_CTL, ACODEC_ADC_CURRENT_CHARGE_MSK, ACODEC_ADC_SEL_I(0xff)); /* vendor step 3 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_ADC_REF_VOL_MSK, ACODEC_ADC_REF_VOL_DIS); /* vendor step 3. Wait until the voltage of VCM keep stable at AGND. */ msleep(20); return 0; } static int rv1106_codec_adc_i2s_route(struct rv1106_codec_priv *rv1106) { regmap_write(rv1106->grf, PERI_GRF_PERI_CON1, ACODEC_MSK | ACODEC_EN); return 0; } static int check_micbias(int volt) { switch (volt) { case ACODEC_ADC_MICBIAS_VOLT_0_975: case ACODEC_ADC_MICBIAS_VOLT_0_95: case ACODEC_ADC_MICBIAS_VOLT_0_925: case ACODEC_ADC_MICBIAS_VOLT_0_9: case ACODEC_ADC_MICBIAS_VOLT_0_875: case ACODEC_ADC_MICBIAS_VOLT_0_85: case ACODEC_ADC_MICBIAS_VOLT_0_825: case ACODEC_ADC_MICBIAS_VOLT_0_8: return 0; } return -EINVAL; } static int rv1106_codec_micbias_enable(struct rv1106_codec_priv *rv1106, int volt) { int ret; if (!rv1106->micbias_used) return 0; /* 0. Power up the ACODEC and keep the AVDDH stable */ /* vendor step 1 */ ret = check_micbias(volt); if (ret < 0) { dev_err(rv1106->plat_dev, "This is an invalid volt: %d\n", volt); return ret; } regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_ADC_LEVEL_RANGE_MICBIAS_MSK, volt); /* vendor step 4 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_MICBIAS_MSK, ACODEC_MICBIAS_WORK); /* waiting micbias stabled*/ mdelay(20); rv1106->micbias_enable = true; return 0; } static int rv1106_codec_micbias_disable(struct rv1106_codec_priv *rv1106) { if (!rv1106->micbias_used) return 0; /* Step 0. Enable the MICBIAS and keep the Audio Codec stable */ /* Do nothing */ /* vendor step 1 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_MICBIAS_MSK, ACODEC_MICBIAS_RST); rv1106->micbias_enable = false; return 0; } static int rv1106_codec_hpmix_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return snd_soc_get_volsw_range(kcontrol, ucontrol); } static int rv1106_codec_hpmix_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); unsigned int index = ucontrol->value.integer.value[0]; if ((index < ACODEC_DAC_HPMIX_GAIN_MIN) || (index > ACODEC_DAC_HPMIX_GAIN_MAX)) { dev_err(rv1106->plat_dev, "%s: invalid gain index: %d\n", __func__, index); return -EINVAL; } return snd_soc_put_volsw_range(kcontrol, ucontrol); } static int rv1106_codec_adc_enable(struct rv1106_codec_priv *rv1106) { unsigned int lr = using_adc_lr(rv1106->adc_mode); bool is_diff = using_adc_diff(rv1106->adc_mode); unsigned int agc_func_en; int ret; dev_dbg(rv1106->plat_dev, "%s: soc_id: 0x%x lr: %d is_diff: %d\n", __func__, rv1106->soc_id, lr, is_diff); ret = check_adc_mode(rv1106); if (ret < 0) { dev_err(rv1106->plat_dev, "%s - something error checking ADC mode: %d\n", __func__, ret); return ret; } /* vendor step 00 */ if (rv1106->soc_id == SOC_RV1103 && rv1106->adc_mode == DIFF_ADCL) { /* The ADCL is differential mode on rv1103 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, ACODEC_ADC_L_MODE_SEL_MSK, ACODEC_ADC_L_FULL_DIFFER2); } else if (rv1106->soc_id == SOC_RV1106 && is_diff) { /* The differential mode on rv1106 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, L(lr, ACODEC_ADC_L_MODE_SEL_MSK) | R(lr, ACODEC_ADC_R_MODE_SEL_MSK), L(lr, ACODEC_ADC_L_FULL_DIFFER) | R(lr, ACODEC_ADC_R_FULL_DIFFER)); } else { /* The single-end mode */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, L(lr, ACODEC_ADC_L_MODE_SEL_MSK) | R(lr, ACODEC_ADC_R_MODE_SEL_MSK), L(lr, ACODEC_ADC_L_SINGLE_END) | R(lr, ACODEC_ADC_R_SINGLE_END)); } /* vendor step 01 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, L(lr, ACODEC_ADC_L_MIC_MSK) | R(lr, ACODEC_ADC_R_MIC_MSK), L(lr, ACODEC_ADC_L_MIC_WORK) | R(lr, ACODEC_ADC_R_MIC_WORK)); /* vendor step 02 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_ADC_IBIAS_MSK, ACODEC_ADC_IBIAS_EN); /* vendor step 03 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, L(lr, ACODEC_ADC_L_REF_VOL_BUF_MSK) | R(lr, ACODEC_ADC_R_REF_VOL_BUF_MSK), L(lr, ACODEC_ADC_L_REF_VOL_BUF_EN) | R(lr, ACODEC_ADC_R_REF_VOL_BUF_EN)); /* waiting VREF be stable */ msleep(100); /* vendor step 04 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, L(lr, ACODEC_MIC_L_MSK) | R(lr, ACODEC_MIC_R_MSK), L(lr, ACODEC_MIC_L_EN) | R(lr, ACODEC_MIC_R_EN)); /* vendor step 05 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, L(lr, ACODEC_ADC_L_MSK) | R(lr, ACODEC_ADC_R_MSK), L(lr, ACODEC_ADC_L_EN) | R(lr, ACODEC_ADC_R_EN)); /* vendor step 06 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, L(lr, ACODEC_ADC_L_CLK_MSK) | R(lr, ACODEC_ADC_R_CLK_MSK), L(lr, ACODEC_ADC_L_CLK_WORK) | R(lr, ACODEC_ADC_R_CLK_WORK)); /* vendor step 07 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, L(lr, ACODEC_ADC_L_WORK) | R(lr, ACODEC_ADC_R_WORK), L(lr, ACODEC_ADC_L_WORK) | R(lr, ACODEC_ADC_R_WORK)); /* vendor step 08 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, L(lr, ACODEC_ADC_L_SIGNAL_EN) | R(lr, ACODEC_ADC_R_SIGNAL_EN), L(lr, ACODEC_ADC_L_SIGNAL_EN) | R(lr, ACODEC_ADC_R_SIGNAL_EN)); /* vendor step 09 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, L(lr, ACODEC_ADC_L_ALC_MSK) | R(lr, ACODEC_ADC_R_ALC_MSK), L(lr, ACODEC_ADC_L_ALC_WORK) | R(lr, ACODEC_ADC_R_ALC_WORK)); /* vendor step 10 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, L(lr, ACODEC_ADC_L_MIC_SIGNAL_MSK) | R(lr, ACODEC_ADC_R_MIC_SIGNAL_MSK), L(lr, ACODEC_ADC_L_MIC_SIGNAL_WORK) | R(lr, ACODEC_ADC_R_MIC_SIGNAL_WORK)); /* vendor step 11, configure GAIN_MICL/R by user */ /* vendor step 12, configure GAIN_ALCL/R by user */ /* vendor step 13 */ regmap_read(rv1106->regmap, ACODEC_ADC_ANA_CTL1, &agc_func_en); if (agc_func_en & ACODEC_AGC_FUNC_SEL_EN) { regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, L(lr, ACODEC_ADC_L_ZERO_CROSS_DET_MSK) | R(lr, ACODEC_ADC_R_ZERO_CROSS_DET_MSK), L(lr, ACODEC_ADC_L_ZERO_CROSS_DET_EN) | R(lr, ACODEC_ADC_R_ZERO_CROSS_DET_EN)); } rv1106->adc_enable = true; return 0; } static int rv1106_codec_adc_disable(struct rv1106_codec_priv *rv1106) { /* vendor step 1 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, ACODEC_ADC_L_ZERO_CROSS_DET_MSK | ACODEC_ADC_R_ZERO_CROSS_DET_MSK, ACODEC_ADC_L_ZERO_CROSS_DET_DIS | ACODEC_ADC_R_ZERO_CROSS_DET_DIS); /* vendor step 2 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, ACODEC_ADC_L_WORK | ACODEC_ADC_R_WORK, ACODEC_ADC_L_INIT | ACODEC_ADC_R_INIT); /* vendor step 3 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, ACODEC_ADC_L_CLK_WORK | ACODEC_ADC_R_CLK_WORK, ACODEC_ADC_L_CLK_RST | ACODEC_ADC_R_CLK_RST); /* vendor step 4 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, ACODEC_ADC_L_MSK | ACODEC_ADC_R_MSK, ACODEC_ADC_L_DIS | ACODEC_ADC_R_DIS); /* vendor step 5 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL3, ACODEC_MIC_L_MSK | ACODEC_MIC_R_MSK, ACODEC_MIC_L_DIS | ACODEC_MIC_R_DIS); /* vendor step 6 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, ACODEC_ADC_L_REF_VOL_BUF_MSK | ACODEC_ADC_R_REF_VOL_BUF_MSK, ACODEC_ADC_L_REF_VOL_BUF_DIS | ACODEC_ADC_R_REF_VOL_BUF_DIS); /* vendor step 7 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL0, ACODEC_ADC_IBIAS_MSK, ACODEC_ADC_IBIAS_DIS); /* vendor step 8 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, ACODEC_ADC_L_SIGNAL_EN | ACODEC_ADC_R_SIGNAL_EN, ACODEC_ADC_L_SIGNAL_DIS | ACODEC_ADC_R_SIGNAL_DIS); /* vendor step 9 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL6, ACODEC_ADC_L_ALC_MSK | ACODEC_ADC_R_ALC_MSK, ACODEC_ADC_L_ALC_INIT | ACODEC_ADC_R_ALC_INIT); /* vendor step 10 */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL1, ACODEC_ADC_L_MIC_SIGNAL_MSK | ACODEC_ADC_R_MIC_SIGNAL_MSK, ACODEC_ADC_L_MIC_SIGNAL_INIT | ACODEC_ADC_R_MIC_SIGNAL_INIT); rv1106->adc_enable = false; return 0; } static int rv1106_codec_open_capture(struct rv1106_codec_priv *rv1106) { rv1106_codec_adc_enable(rv1106); return 0; } static int rv1106_codec_close_capture(struct rv1106_codec_priv *rv1106) { rv1106_codec_adc_disable(rv1106); return 0; } static int rv1106_codec_open_playback(struct rv1106_codec_priv *rv1106) { rv1106_codec_dac_enable(rv1106); return 0; } static int rv1106_codec_close_playback(struct rv1106_codec_priv *rv1106) { rv1106_codec_dac_disable(rv1106); return 0; } static int rv1106_codec_dlp_down(struct rv1106_codec_priv *rv1106) { rv1106_codec_micbias_disable(rv1106); rv1106_codec_power_off(rv1106); return 0; } static int rv1106_codec_dlp_up(struct rv1106_codec_priv *rv1106) { rv1106_codec_power_on(rv1106); rv1106_codec_micbias_enable(rv1106, rv1106->micbias_volt); return 0; } static int rv1106_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { rv1106_codec_open_playback(rv1106); rv1106_codec_dac_dig_config(rv1106, params); } else { rv1106_codec_open_capture(rv1106); rv1106_codec_adc_dig_config(rv1106, params); } return 0; } static void rv1106_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rv1106_codec_close_playback(rv1106); else rv1106_codec_close_capture(rv1106); regcache_cache_only(rv1106->regmap, false); regcache_sync(rv1106->regmap); } static const struct snd_soc_dai_ops rv1106_dai_ops = { .hw_params = rv1106_hw_params, .set_fmt = rv1106_set_dai_fmt, .mute_stream = rv1106_mute_stream, .shutdown = rv1106_pcm_shutdown, }; static struct snd_soc_dai_driver rv1106_dai[] = { { .name = "rv1106-hifi", .id = ACODEC_HIFI, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE), }, .capture = { .stream_name = "HiFi Capture", .channels_min = 1, .channels_max = 4, .rates = SNDRV_PCM_RATE_8000_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE), }, .ops = &rv1106_dai_ops, }, }; static int rv1106_suspend(struct snd_soc_component *component) { struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); rv1106_codec_dlp_down(rv1106); clk_disable_unprepare(rv1106->mclk_acodec); clk_disable_unprepare(rv1106->pclk_acodec); rv1106_set_bias_level(component, SND_SOC_BIAS_OFF); return 0; } static int rv1106_resume(struct snd_soc_component *component) { struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); int ret = 0; ret = clk_prepare_enable(rv1106->pclk_acodec); if (ret < 0) { dev_err(rv1106->plat_dev, "Failed to enable acodec pclk_acodec: %d\n", ret); goto out; } ret = clk_prepare_enable(rv1106->mclk_acodec); if (ret < 0) { dev_err(rv1106->plat_dev, "Failed to enable acodec mclk_acodec: %d\n", ret); goto out; } rv1106_codec_dlp_up(rv1106); out: rv1106_set_bias_level(component, SND_SOC_BIAS_STANDBY); return ret; } static int rv1106_codec_default_gains(struct rv1106_codec_priv *rv1106) { int gainl, gainr; /** * MIC Gain * 0dB (0x01) * 20dB (0x02) * 12dB (0x03) */ if (rv1106->init_mic_gain == NOT_SPECIFIED) { gainl = ACODEC_ADC_L_MIC_GAIN_0DB; gainr = ACODEC_ADC_R_MIC_GAIN_0DB; } else { gainl = ((rv1106->init_mic_gain >> 4) & 0x03) << ACODEC_ADC_L_MIC_GAIN_SFT; gainr = ((rv1106->init_mic_gain >> 0) & 0x03) << ACODEC_ADC_R_MIC_GAIN_SFT; } /* Prepare ADC gains */ /* vendor step 12, set MIC PGA default gains */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL2, ACODEC_ADC_L_MIC_GAIN_MSK | ACODEC_ADC_R_MIC_GAIN_MSK, gainl | gainr); /** * ALC Gain (0dB: 0x06) * min: -9.0dB (0x00) * max: +37.5dB (0x1f) * step: +1.5dB */ if (rv1106->init_alc_gain == NOT_SPECIFIED) { gainl = ACODEC_ADC_L_ALC_GAIN_0DB; gainr = ACODEC_ADC_R_ALC_GAIN_0DB; } else { gainl = ((rv1106->init_alc_gain >> 4) & 0x1f); gainr = ((rv1106->init_alc_gain >> 0) & 0x1f); } /* vendor step 13, set ALC default gains */ regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL4, ACODEC_ADC_L_ALC_GAIN_MSK, gainl); regmap_update_bits(rv1106->regmap, ACODEC_ADC_ANA_CTL5, ACODEC_ADC_R_ALC_GAIN_MSK, gainr); /* Prepare DAC gains */ /* Step 19, set LINEOUT default gains */ regmap_update_bits(rv1106->regmap, ACODEC_DAC_GAIN_SEL, ACODEC_DAC_DIG_GAIN_MSK, ACODEC_DAC_DIG_GAIN(ACODEC_DAC_DIG_0DB)); /* The calibrated fixed gain */ /** * Lineout Gain (0dB: 0x1a) * min: -39.0dB (0x00) * max: +6.0dB (0x1f) * step: +1.5dB */ if (rv1106->init_lineout_gain == NOT_SPECIFIED) gainl = ACODEC_DAC_LINEOUT_GAIN_0DB; else gainl = rv1106->init_lineout_gain & 0x1f; regmap_update_bits(rv1106->regmap, ACODEC_DAC_ANA_CTL2, ACODEC_DAC_LINEOUT_GAIN_MSK, gainl); return 0; } static int rv1106_codec_check_micbias(struct rv1106_codec_priv *rv1106, struct device_node *np) { /* Check internal of acodec micbias */ rv1106->micbias_used = of_property_read_bool(np, "acodec,micbias"); /* Using 0.9*AVDD by default */ rv1106->micbias_volt = ACODEC_ADC_MICBIAS_VOLT_0_9; return 0; } static int rv1106_codec_dapm_controls_prepare(struct rv1106_codec_priv *rv1106) { rv1106->adc_mode = DIFF_ADCL; rv1106->hpf_cutoff = 0; rv1106->agc_l = 0; rv1106->agc_r = 0; rv1106->agc_asr_l = AGC_ASR_96KHZ; rv1106->agc_asr_r = AGC_ASR_96KHZ; return 0; } static int rv1106_codec_prepare(struct rv1106_codec_priv *rv1106) { /* Clear registers for ADC and DAC */ rv1106_codec_close_playback(rv1106); rv1106_codec_close_capture(rv1106); rv1106_codec_default_gains(rv1106); rv1106_codec_dapm_controls_prepare(rv1106); return 0; } static int rv1106_probe(struct snd_soc_component *component) { struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); rv1106->component = component; rv1106_codec_reset(component); rv1106_codec_dlp_up(rv1106); rv1106_codec_prepare(rv1106); regcache_cache_only(rv1106->regmap, false); regcache_sync(rv1106->regmap); return 0; } static void rv1106_remove(struct snd_soc_component *component) { struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component); rv1106_codec_pa_ctrl(rv1106, false); rv1106_codec_micbias_disable(rv1106); rv1106_codec_power_off(rv1106); regcache_cache_only(rv1106->regmap, false); regcache_sync(rv1106->regmap); } static const struct snd_soc_component_driver soc_codec_dev_rv1106 = { .probe = rv1106_probe, .remove = rv1106_remove, .suspend = rv1106_suspend, .resume = rv1106_resume, .set_bias_level = rv1106_set_bias_level, .controls = rv1106_codec_dapm_controls, .num_controls = ARRAY_SIZE(rv1106_codec_dapm_controls), }; /* Set the default value or reset value */ static const struct reg_default rv1106_codec_reg_defaults[] = { { ACODEC_RESET_CTL, 0x03 }, }; static bool rv1106_codec_write_read_reg(struct device *dev, unsigned int reg) { /* All registers can be read / write */ return true; } static bool rv1106_codec_volatile_reg(struct device *dev, unsigned int reg) { /* All registers can be read / write */ return true; } static const struct regmap_config rv1106_codec_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = ACODEC_REG_MAX, .writeable_reg = rv1106_codec_write_read_reg, .readable_reg = rv1106_codec_write_read_reg, .volatile_reg = rv1106_codec_volatile_reg, .reg_defaults = rv1106_codec_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rv1106_codec_reg_defaults), .cache_type = REGCACHE_FLAT, }; static ssize_t adc_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rv1106_codec_priv *rv1106 = container_of(dev, struct rv1106_codec_priv, dev); return sprintf(buf, "%d\n", rv1106->adc_enable); } static ssize_t adc_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct rv1106_codec_priv *rv1106 = container_of(dev, struct rv1106_codec_priv, dev); unsigned long enable; int ret = kstrtoul(buf, 10, &enable); if (ret < 0) { dev_err(dev, "Invalid enable: %ld, ret: %d\n", enable, ret); return -EINVAL; } if (enable) rv1106_codec_open_capture(rv1106); else rv1106_codec_close_capture(rv1106); dev_info(dev, "ADC enable: %ld\n", enable); return count; } static ssize_t dac_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rv1106_codec_priv *rv1106 = container_of(dev, struct rv1106_codec_priv, dev); return sprintf(buf, "%d\n", rv1106->dac_enable); } static ssize_t dac_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct rv1106_codec_priv *rv1106 = container_of(dev, struct rv1106_codec_priv, dev); unsigned long enable; int ret = kstrtoul(buf, 10, &enable); if (ret < 0) { dev_err(dev, "Invalid enable: %ld, ret: %d\n", enable, ret); return -EINVAL; } if (enable) rv1106_codec_open_playback(rv1106); else rv1106_codec_close_playback(rv1106); dev_info(dev, "DAC enable: %ld\n", enable); return count; } static const struct device_attribute acodec_attrs[] = { __ATTR_RW(adc_enable), __ATTR_RW(dac_enable), }; static void rv1106_codec_device_release(struct device *dev) { /* Do nothing */ } static int rv1106_codec_sysfs_init(struct platform_device *pdev, struct rv1106_codec_priv *rv1106) { struct device *dev = &rv1106->dev; int i; dev->release = rv1106_codec_device_release; dev->parent = &pdev->dev; set_dev_node(dev, dev_to_node(&pdev->dev)); dev_set_name(dev, "acodec_attrs"); if (device_register(dev)) { dev_err(&pdev->dev, "Register 'acodec_attrs' failed\n"); dev->parent = NULL; return -ENOMEM; } for (i = 0; i < ARRAY_SIZE(acodec_attrs); i++) { if (device_create_file(dev, &acodec_attrs[i])) { dev_err(&pdev->dev, "Create 'acodec_attrs' failed\n"); device_unregister(dev); return -ENOMEM; } } return 0; } static int rv1106_codec_sysfs_exit(struct rv1106_codec_priv *rv1106) { struct device *dev = &rv1106->dev; int i; for (i = 0; i < ARRAY_SIZE(acodec_attrs); i++) device_remove_file(dev, &acodec_attrs[i]); device_unregister(dev); return 0; } #if defined(CONFIG_DEBUG_FS) static int rv1106_codec_debugfs_reg_show(struct seq_file *s, void *v) { struct rv1106_codec_priv *rv1106 = s->private; unsigned int i; unsigned int val; for (i = ACODEC_RESET_CTL; i <= ACODEC_ADC_PGA_AGC_R_CTL9; i += 4) { regmap_read(rv1106->regmap, i, &val); if (!(i % 16)) seq_printf(s, "\nR:%04x: ", i); seq_printf(s, "%08x ", val); } seq_puts(s, "\n"); return 0; } static ssize_t rv1106_codec_debugfs_reg_operate(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct rv1106_codec_priv *rv1106 = ((struct seq_file *)file->private_data)->private; unsigned int reg, val; char op; char kbuf[32]; int ret; if (count >= sizeof(kbuf)) return -EINVAL; if (copy_from_user(kbuf, buf, count)) return -EFAULT; kbuf[count] = '\0'; ret = sscanf(kbuf, "%c,%x,%x", &op, ®, &val); if (ret != 3) { pr_err("sscanf failed: %d\n", ret); return -EFAULT; } if (op == 'w') { pr_info("Write reg: 0x%04x with val: 0x%08x\n", reg, val); regmap_write(rv1106->regmap, reg, val); regcache_cache_only(rv1106->regmap, false); regcache_sync(rv1106->regmap); pr_info("Read back reg: 0x%04x with val: 0x%08x\n", reg, val); } else if (op == 'r') { regmap_read(rv1106->regmap, reg, &val); pr_info("Read reg: 0x%04x with val: 0x%08x\n", reg, val); } else { pr_err("This is an invalid operation: %c\n", op); } return count; } static int rv1106_codec_debugfs_open(struct inode *inode, struct file *file) { return single_open(file, rv1106_codec_debugfs_reg_show, inode->i_private); } static const struct file_operations rv1106_codec_reg_debugfs_fops = { .owner = THIS_MODULE, .open = rv1106_codec_debugfs_open, .read = seq_read, .write = rv1106_codec_debugfs_reg_operate, .llseek = seq_lseek, .release = single_release, }; #endif /* CONFIG_DEBUG_FS */ static const struct of_device_id rv1106_codec_of_match[] = { { .compatible = "rockchip,rv1103-codec", .data = (void *)SOC_RV1103}, { .compatible = "rockchip,rv1106-codec", .data = (void *)SOC_RV1106}, {}, }; MODULE_DEVICE_TABLE(of, rv1106_codec_of_match); static int rv1106_platform_probe(struct platform_device *pdev) { const struct of_device_id *of_id; struct device_node *np = pdev->dev.of_node; struct rv1106_codec_priv *rv1106; struct resource *res; void __iomem *base; int ret; rv1106 = devm_kzalloc(&pdev->dev, sizeof(*rv1106), GFP_KERNEL); if (!rv1106) return -ENOMEM; of_id = of_match_device(rv1106_codec_of_match, &pdev->dev); if (of_id) rv1106->soc_id = (enum soc_id_e)of_id->data; dev_info(&pdev->dev, "current soc_id: rv%x\n", rv1106->soc_id); rv1106->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(rv1106->grf)) return dev_err_probe(&pdev->dev, PTR_ERR(rv1106->grf), "Missing 'rockchip,grf' property\n"); rv1106->plat_dev = &pdev->dev; rv1106->reset = devm_reset_control_get(&pdev->dev, "acodec-reset"); if (IS_ERR(rv1106->reset)) { ret = PTR_ERR(rv1106->reset); if (ret != -ENOENT) return ret; dev_dbg(&pdev->dev, "No reset control found\n"); rv1106->reset = NULL; } rv1106->init_mic_gain = NOT_SPECIFIED; of_property_read_u32(np, "init-mic-gain", &rv1106->init_mic_gain); rv1106->init_alc_gain = NOT_SPECIFIED; of_property_read_u32(np, "init-alc-gain", &rv1106->init_alc_gain); rv1106->init_lineout_gain = NOT_SPECIFIED; of_property_read_u32(np, "init-lineout-gain", &rv1106->init_lineout_gain); rv1106->pa_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "pa-ctl", GPIOD_OUT_LOW); if (IS_ERR(rv1106->pa_ctl_gpio)) return dev_err_probe(&pdev->dev, PTR_ERR(rv1106->pa_ctl_gpio), "Unable to claim gpio pa-ctl\n"); if (rv1106->pa_ctl_gpio) of_property_read_u32(np, "pa-ctl-delay-ms", &rv1106->pa_ctl_delay_ms); dev_info(&pdev->dev, "%s pa_ctl_gpio and pa_ctl_delay_ms: %d\n", rv1106->pa_ctl_gpio ? "Use" : "No use", rv1106->pa_ctl_delay_ms); /* Close external PA during startup. */ rv1106_codec_pa_ctrl(rv1106, false); rv1106->pclk_acodec = devm_clk_get(&pdev->dev, "pclk_acodec"); if (IS_ERR(rv1106->pclk_acodec)) return dev_err_probe(&pdev->dev, PTR_ERR(rv1106->pclk_acodec), "Can't get acodec pclk_acodec\n"); rv1106->mclk_acodec = devm_clk_get(&pdev->dev, "mclk_acodec"); if (IS_ERR(rv1106->mclk_acodec)) return dev_err_probe(&pdev->dev, PTR_ERR(rv1106->mclk_acodec), "Can't get acodec mclk_acodec\n"); rv1106->mclk_cpu = devm_clk_get(&pdev->dev, "mclk_cpu"); if (IS_ERR(rv1106->mclk_cpu)) return dev_err_probe(&pdev->dev, PTR_ERR(rv1106->mclk_cpu), "Can't get acodec mclk_cpu\n"); ret = rv1106_codec_sysfs_init(pdev, rv1106); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Sysfs init failed\n"); #if defined(CONFIG_DEBUG_FS) rv1106->dbg_codec = debugfs_create_dir(CODEC_DRV_NAME, NULL); if (IS_ERR(rv1106->dbg_codec)) dev_err(&pdev->dev, "Failed to create debugfs dir for rv1106!\n"); else debugfs_create_file("reg", 0644, rv1106->dbg_codec, rv1106, &rv1106_codec_reg_debugfs_fops); #endif ret = clk_prepare_enable(rv1106->pclk_acodec); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable acodec pclk_acodec: %d\n", ret); goto failed_2; } ret = clk_prepare_enable(rv1106->mclk_acodec); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable acodec mclk_acodec: %d\n", ret); goto failed_1; } /** * In PERICRU_PERICLKSEL_CON08, the mclk_acodec_t/rx_div are div 4 * by default, we need to calibrate once, make the div is 1 and keep * the rate of mclk_acodec is the same with mclk_i2s. * * FIXME: need to handle div dynamically if the DSMAUDIO is enabled. */ clk_set_rate(rv1106->mclk_acodec, clk_get_rate(rv1106->mclk_cpu)); rv1106_codec_check_micbias(rv1106, np); ret = rv1106_codec_adc_i2s_route(rv1106); if (ret < 0) { dev_err(&pdev->dev, "Failed to route ADC to i2s: %d\n", ret); goto failed; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { ret = PTR_ERR(base); dev_err(&pdev->dev, "Failed to ioremap resource\n"); goto failed; } rv1106->regmap = devm_regmap_init_mmio(&pdev->dev, base, &rv1106_codec_regmap_config); if (IS_ERR(rv1106->regmap)) { ret = PTR_ERR(rv1106->regmap); dev_err(&pdev->dev, "Failed to regmap mmio\n"); goto failed; } platform_set_drvdata(pdev, rv1106); ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rv1106, rv1106_dai, ARRAY_SIZE(rv1106_dai)); if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); goto failed; } return ret; failed: clk_disable_unprepare(rv1106->mclk_acodec); failed_1: clk_disable_unprepare(rv1106->pclk_acodec); failed_2: #if defined(CONFIG_DEBUG_FS) debugfs_remove_recursive(rv1106->dbg_codec); #endif rv1106_codec_sysfs_exit(rv1106); return ret; } static int rv1106_platform_remove(struct platform_device *pdev) { struct rv1106_codec_priv *rv1106 = (struct rv1106_codec_priv *)platform_get_drvdata(pdev); clk_disable_unprepare(rv1106->mclk_acodec); clk_disable_unprepare(rv1106->pclk_acodec); #if defined(CONFIG_DEBUG_FS) debugfs_remove_recursive(rv1106->dbg_codec); #endif rv1106_codec_sysfs_exit(rv1106); return 0; } static struct platform_driver rv1106_codec_driver = { .driver = { .name = CODEC_DRV_NAME, .of_match_table = of_match_ptr(rv1106_codec_of_match), }, .probe = rv1106_platform_probe, .remove = rv1106_platform_remove, }; module_platform_driver(rv1106_codec_driver); MODULE_DESCRIPTION("ASoC RV1106 Codec Driver"); MODULE_AUTHOR("Jason Zhu "); MODULE_LICENSE("GPL");