From 26d61ff64d9a61425d017846db61e9a06de07286 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Mon, 3 Feb 2020 17:13:59 +0100 Subject: [PATCH 19/23] Sync `rk3308_codec` to BSP tree --- .../bindings/sound/rockchip,rk3308-codec.txt | 78 + sound/soc/codecs/rk3308_codec.c | 5687 ++++++++++++++--- sound/soc/codecs/rk3308_codec.h | 217 +- sound/soc/codecs/rk3308_codec_provider.h | 28 + 4 files changed, 4894 insertions(+), 1116 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt create mode 100644 sound/soc/codecs/rk3308_codec_provider.h diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt new file mode 100644 index 000000000000..e20bbd73e37e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.txt @@ -0,0 +1,78 @@ +* Rockchip RK3308 Internal Codec + +Required properties: + +- compatible: "rockchip,rk3308-codec" +- reg: The physical base address of the controller and length of memory + mapped region. +- rockchip,grf: The phandle of the syscon node for GRF register. +- clocks: A list of phandle + clock-specifer pairs, one for each entry in + clock-names. +- clock-names: It should be "acodec". +- resets : Must contain an entry for each entry in reset-names. +- reset-names : Must include the following entries: "acodec-reset". + +Optional properties: +- rockchip,enable-all-adcs: This is a boolean type property, that shows whether + force enable all of ADCs. The following shows the relationship between grps + and ADC: + * grp 0 -- select ADC1 / ADC2 + * grp 1 -- select ADC3 / ADC4 + * grp 2 -- select ADC5 / ADC6 + * grp 3 -- select ADC7 / ADC8 + If the property is not used, the enabled ADC groups refer to needed channels + via configure hw_params. + +- rockchip,adc-grps-route: This is a variable length array, that shows the + mapping route of ACODEC sdo to I2S sdi. By default, they are one-to-one + mapping: + * sdi_0 <-- sdo_0 + * sdi_1 <-- sdo_1 + * sdi_2 <-- sdo_2 + * sdi_3 <-- sdo_3 + If you would like to change the route mapping like this: + * sdi_0 <-- sdo_3 + * sdi_1 <-- sdo_0 + * sdi_2 <-- sdo_2 + * sdi_3 <-- sdo_1 + You need to add the property on dts: + - rockchip,adc-grps-route = <3 0 2 1>; + +- rockchip,delay-loopback-handle-ms: This property points out that the delay for + handling ADC after enable PAs during loopback. +- rockchip,delay-start-play-ms: This property points out the delay ms of start + playback according to different amplifier performance. +- rockchip,en-always-grps: This property will keep the needed ADCs enabled + always after enabling once. +- rockchip,loopback-grp: It points out the ADC group which is the loopback used. +- rockchip,no-deep-low-power: The codec will not enter deep low power mode + during suspend. +- rockchip,no-hp-det: If there is no headphone on boards, we don't need to + enable headphone detection. +- rockchip,micbias1: Using internal micbias1 supply which are from codec. +- rockchip,micbias2: Using internal micbias2 supply which are from codec. +- rockchip,hp-jack-reversed;: To detect headphone via the reversed jack. +- hp-ctl-gpios: The gpio of head phone controller. +- pa-drv-gpios: The gpio of poweramplifier controller +- rockchip,delay-pa-drv-ms: This property points out that the delay for + power on amplifier +- spk-ctl-gpios: The gpio of speak controller. +- micbias-en-gpios: The GPIO to enable external micbias. +- vmicbias-supply: The phandle to the regulator to handle external micbias. + +Example for rk3308 internal codec: + +acodec: acodec@ff560000 { + compatible = "rockchip,rk3308-codec"; + reg = <0x0 0xff560000 0x0 0x10000>; + rockchip,grf = <&grf>; + clocks = <&cru PCLK_ACODEC>; + clock-names = "acodec"; + resets = <&cru SRST_ACODEC_P>; + reset-names = "acodec-reset"; + rockchip,loopback-grp = <0>; + hp-ctl-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + pa-drv-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>; + spk-ctl-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c index 106f09738dd0..815e22fc346c 100644 --- a/sound/soc/codecs/rk3308_codec.c +++ b/sound/soc/codecs/rk3308_codec.c @@ -29,1420 +29,4699 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include "rk3308_codec.h" +#include "rk3308_codec_provider.h" + +#if defined(CONFIG_DEBUG_FS) +#include +#include +#include +#endif + +#define CODEC_DRV_NAME "rk3308-acodec" + +#define ADC_GRP_SKIP_MAGIC 0x1001 +#define ADC_LR_GROUP_MAX 4 +#define ADC_STABLE_MS 200 +#define DEBUG_POP_ALWAYS 0 +#define HPDET_POLL_MS 2000 +#define NOT_USED 255 +#define LOOPBACK_HANDLE_MS 100 +#define PA_DRV_MS 5 + +#define GRF_SOC_CON1 0x304 +#define GRF_CHIP_ID 0x800 +#define GRF_I2S2_8CH_SDI_SFT 0 +#define GRF_I2S3_4CH_SDI_SFT 8 +#define GRF_I2S1_2CH_SDI_SFT 12 + +#define GRF_I2S2_8CH_SDI_R_MSK(i, v) ((v >> (i * 2 + GRF_I2S2_8CH_SDI_SFT)) & 0x3) +#define GRF_I2S2_8CH_SDI_W_MSK(i) (0x3 << (i * 2 + GRF_I2S2_8CH_SDI_SFT + 16)) +#define GRF_I2S2_8CH_SDI(i, v) (((v & 0x3) << (i * 2 + GRF_I2S2_8CH_SDI_SFT)) |\ + GRF_I2S2_8CH_SDI_W_MSK(i)) + +#define GRF_I2S3_4CH_SDI_W_MSK(i) (0x3 << (i * 2 + GRF_I2S3_4CH_SDI_SFT + 16)) +#define GRF_I2S3_4CH_SDI(i, v) (((v & 0x3) << (i * 2 + GRF_I2S3_4CH_SDI_SFT)) |\ + GRF_I2S3_4CH_SDI_W_MSK(i)) + +#define GRF_I2S1_2CH_SDI_W_MSK (0x3 << (GRF_I2S1_2CH_SDI_SFT + 16)) +#define GRF_I2S1_2CH_SDI(v) (((v & 0x3) << GRF_I2S1_2CH_SDI_SFT) |\ + GRF_I2S1_2CH_SDI_W_MSK) + +#define DETECT_GRF_ACODEC_HPDET_COUNTER 0x0030 +#define DETECT_GRF_ACODEC_HPDET_CON 0x0034 +#define DETECT_GRF_ACODEC_HPDET_STATUS 0x0038 +#define DETECT_GRF_ACODEC_HPDET_STATUS_CLR 0x003c + +/* 200ms based on pclk is 100MHz */ +#define DEFAULT_HPDET_COUNT 20000000 +#define HPDET_NEG_IRQ_SFT 1 +#define HPDET_POS_IRQ_SFT 0 +#define HPDET_BOTH_NEG_POS ((1 << HPDET_NEG_IRQ_SFT) |\ + (1 << HPDET_POS_IRQ_SFT)) + +#define ACODEC_VERSION_A 0xa +#define ACODEC_VERSION_B 0xb + +enum { + ACODEC_TO_I2S2_8CH = 0, + ACODEC_TO_I2S3_4CH, + ACODEC_TO_I2S1_2CH, +}; + +enum { + ADC_GRP0_MICIN = 0, + ADC_GRP0_LINEIN +}; + +enum { + ADC_TYPE_NORMAL = 0, + ADC_TYPE_LOOPBACK, + ADC_TYPE_DBG, + ADC_TYPE_ALL, +}; + +enum { + DAC_LINEOUT = 0, + DAC_HPOUT = 1, + DAC_LINEOUT_HPOUT = 11, +}; + +enum { + EXT_MICBIAS_NONE = 0, + EXT_MICBIAS_FUNC1, /* enable external micbias via GPIO */ + EXT_MICBIAS_FUNC2, /* enable external micbias via regulator */ +}; + +enum { + PATH_IDLE = 0, + PATH_BUSY, +}; + +enum { + PM_NORMAL = 0, + PM_LLP_DOWN, /* light low power down */ + PM_LLP_UP, + PM_DLP_DOWN, /* deep low power down */ + PM_DLP_UP, + PM_DLP_DOWN2, + PM_DLP_UP2, +}; struct rk3308_codec_priv { const struct device *plat_dev; struct device dev; struct reset_control *reset; struct regmap *regmap; + struct regmap *grf; + struct regmap *detect_grf; struct clk *pclk; + struct clk *mclk_rx; + struct clk *mclk_tx; + struct gpio_desc *micbias_en_gpio; + struct gpio_desc *hp_ctl_gpio; struct gpio_desc *spk_ctl_gpio; - int adc_ch; /* To select ADCs for channel */ - int adc_ch0_using_linein; + struct gpio_desc *pa_drv_gpio; + struct snd_soc_codec *codec; + struct snd_soc_jack *hpdet_jack; + struct regulator *vcc_micbias; + u32 codec_ver; + + /* + * To select ADCs for groups: + * + * grp 0 -- select ADC1 / ADC2 + * grp 1 -- select ADC3 / ADC4 + * grp 2 -- select ADC5 / ADC6 + * grp 3 -- select ADC7 / ADC8 + */ + u32 used_adc_grps; + /* The ADC group which is used for loop back */ + u32 loopback_grp; + u32 cur_dbg_grp; + u32 en_always_grps[ADC_LR_GROUP_MAX]; + u32 en_always_grps_num; + u32 skip_grps[ADC_LR_GROUP_MAX]; + u32 i2s_sdis[ADC_LR_GROUP_MAX]; + u32 to_i2s_grps; + u32 delay_loopback_handle_ms; + u32 delay_start_play_ms; + u32 delay_pa_drv_ms; + u32 micbias_num; + u32 micbias_volt; + int which_i2s; + int irq; + int adc_grp0_using_linein; + int adc_zerocross; + /* 0: line out, 1: hp out, 11: lineout and hpout */ + int dac_output; + int dac_path_state; + + int ext_micbias; + int pm_state; + + /* AGC L/R Off/on */ + unsigned int agc_l[ADC_LR_GROUP_MAX]; + unsigned int agc_r[ADC_LR_GROUP_MAX]; + + /* AGC L/R Approximate Sample Rate */ + unsigned int agc_asr_l[ADC_LR_GROUP_MAX]; + unsigned int agc_asr_r[ADC_LR_GROUP_MAX]; + + /* ADC MIC Mute/Work */ + unsigned int mic_mute_l[ADC_LR_GROUP_MAX]; + unsigned int mic_mute_r[ADC_LR_GROUP_MAX]; + + /* For the high pass filter */ + unsigned int hpf_cutoff[ADC_LR_GROUP_MAX]; + + /* Only hpout do fade-in and fade-out */ + unsigned int hpout_l_dgain; + unsigned int hpout_r_dgain; + + bool adc_grps_endisable[ADC_LR_GROUP_MAX]; + bool dac_endisable; + bool enable_all_adcs; + bool enable_micbias; + bool micbias1; + bool micbias2; + bool hp_jack_reversed; + bool hp_plugged; + bool loopback_dacs_enabled; + bool no_deep_low_power; + bool no_hp_det; + struct delayed_work hpdet_work; + struct delayed_work loopback_work; + +#if defined(CONFIG_DEBUG_FS) + struct dentry *dbg_codec; +#endif }; -static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_gain_tlv, +static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_gain_tlv, -1800, 150, 2850); -static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_max_gain_tlv, +static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_max_gain_tlv, -1350, 600, 2850); -static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_ch_min_gain_tlv, +static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_min_gain_tlv, -1800, 600, 2400); -static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_mic_gain_tlv, - 0, 600, 3000); static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, -1800, 150, 2850); -static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_gain_tlv, - 0, 150, 600); +static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_lineout_gain_tlv, + -600, 150, 0); static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, -3900, 150, 600); static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_RANGE(rk3308_codec_adc_mic_gain_tlv_a, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), +); + +static const DECLARE_TLV_DB_RANGE(rk3308_codec_adc_mic_gain_tlv_b, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(660, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(1300, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0), +); + +static bool handle_loopback(struct rk3308_codec_priv *rk3308); + +static int check_micbias(int micbias); + +static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, + int micbias); +static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308); + +static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpf_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_agc_asr_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_agc_asr_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_mic_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_mic_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_mic_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_main_micbias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_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_5 0 +#define MICBIAS_VREFx0_55 1 +#define MICBIAS_VREFx0_6 2 +#define MICBIAS_VREFx0_65 3 +#define MICBIAS_VREFx0_7 4 +#define MICBIAS_VREFx0_75 5 +#define MICBIAS_VREFx0_8 6 +#define MICBIAS_VREFx0_85 7 + +static const char *micbias_volts_enum_array[MICBIAS_VOLT_NUM] = { + [MICBIAS_VREFx0_5] = "VREFx0_5", + [MICBIAS_VREFx0_55] = "VREFx0_55", + [MICBIAS_VREFx0_6] = "VREFx0_6", + [MICBIAS_VREFx0_65] = "VREFx0_65", + [MICBIAS_VREFx0_7] = "VREFx0_7", + [MICBIAS_VREFx0_75] = "VREFx0_75", + [MICBIAS_VREFx0_8] = "VREFx0_8", + [MICBIAS_VREFx0_85] = "VREFx0_85", +}; + +static const struct soc_enum rk3308_micbias_volts_enum_array[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(micbias_volts_enum_array), micbias_volts_enum_array), +}; + +/* ADC MICBIAS1 and MICBIAS2 Main Switch */ +static const struct soc_enum rk3308_main_micbias_enum_array[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), +}; + +static const struct soc_enum rk3308_hpf_enum_array[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(offon_text), offon_text), +}; + +/* ALC AGC Switch */ +static const struct soc_enum rk3308_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), + SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(3, 1, ARRAY_SIZE(offon_text), offon_text), +}; + +/* ADC MIC Mute/Work Switch */ +static const struct soc_enum rk3308_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), + SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(mute_text), mute_text), + SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(mute_text), mute_text), + SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(mute_text), mute_text), + SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(mute_text), mute_text), + SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(mute_text), mute_text), + SOC_ENUM_SINGLE(3, 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 rk3308_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), + SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), + SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), + SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), + SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), + SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(agc_asr_text), agc_asr_text), + SOC_ENUM_SINGLE(3, 1, ARRAY_SIZE(agc_asr_text), agc_asr_text), +}; + +static const struct snd_kcontrol_new mic_gains_a[] = { + /* ADC MIC */ + SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Left Volume", + RK3308_ADC_ANA_CON01(0), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Right Volume", + RK3308_ADC_ANA_CON01(0), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Left Volume", + RK3308_ADC_ANA_CON01(1), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Right Volume", + RK3308_ADC_ANA_CON01(1), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Left Volume", + RK3308_ADC_ANA_CON01(2), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Right Volume", + RK3308_ADC_ANA_CON01(2), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Left Volume", + RK3308_ADC_ANA_CON01(3), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), + SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Right Volume", + RK3308_ADC_ANA_CON01(3), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_a), +}; + +static const struct snd_kcontrol_new mic_gains_b[] = { + /* ADC MIC */ + SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Left Volume", + RK3308_ADC_ANA_CON01(0), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 0 Right Volume", + RK3308_ADC_ANA_CON01(0), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Left Volume", + RK3308_ADC_ANA_CON01(1), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 1 Right Volume", + RK3308_ADC_ANA_CON01(1), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Left Volume", + RK3308_ADC_ANA_CON01(2), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 2 Right Volume", + RK3308_ADC_ANA_CON01(2), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Left Volume", + RK3308_ADC_ANA_CON01(3), + RK3308_ADC_CH1_MIC_GAIN_SFT, + RK3308_ADC_CH1_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), + SOC_SINGLE_EXT_TLV("ADC MIC Group 3 Right Volume", + RK3308_ADC_ANA_CON01(3), + RK3308_ADC_CH2_MIC_GAIN_SFT, + RK3308_ADC_CH2_MIC_GAIN_MAX, + 0, + rk3308_codec_mic_gain_get, + rk3308_codec_mic_gain_put, + rk3308_codec_adc_mic_gain_tlv_b), +}; + static const struct snd_kcontrol_new rk3308_codec_dapm_controls[] = { - /* ALC AGC Channel*/ - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Volume", + /* ALC AGC Group */ + SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Left Volume", RK3308_ALC_L_DIG_CON03(0), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Right Volume", RK3308_ALC_R_DIG_CON03(0), RK3308_AGC_PGA_GAIN_SFT, - RK3308_AGC_PGA_GAIN_NDB_18, - RK3308_AGC_PGA_GAIN_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Volume", + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Left Volume", RK3308_ALC_L_DIG_CON03(1), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Right Volume", RK3308_ALC_R_DIG_CON03(1), RK3308_AGC_PGA_GAIN_SFT, - RK3308_AGC_PGA_GAIN_NDB_18, - RK3308_AGC_PGA_GAIN_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Volume", + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Left Volume", RK3308_ALC_L_DIG_CON03(2), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Right Volume", RK3308_ALC_R_DIG_CON03(2), RK3308_AGC_PGA_GAIN_SFT, - RK3308_AGC_PGA_GAIN_NDB_18, - RK3308_AGC_PGA_GAIN_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Volume", + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Left Volume", RK3308_ALC_L_DIG_CON03(3), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Right Volume", RK3308_ALC_R_DIG_CON03(3), RK3308_AGC_PGA_GAIN_SFT, - RK3308_AGC_PGA_GAIN_NDB_18, - RK3308_AGC_PGA_GAIN_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_gain_tlv), + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), /* ALC AGC MAX */ - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Max Volume", + SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Left Max Volume", RK3308_ALC_L_DIG_CON09(0), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Right Max Volume", RK3308_ALC_R_DIG_CON09(0), RK3308_AGC_MAX_GAIN_PGA_SFT, - RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, - RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_max_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Max Volume", + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Left Max Volume", RK3308_ALC_L_DIG_CON09(1), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Right Max Volume", RK3308_ALC_R_DIG_CON09(1), RK3308_AGC_MAX_GAIN_PGA_SFT, - RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, - RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_max_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Max Volume", + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Left Max Volume", RK3308_ALC_L_DIG_CON09(2), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Right Max Volume", RK3308_ALC_R_DIG_CON09(2), RK3308_AGC_MAX_GAIN_PGA_SFT, - RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, - RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_max_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Max Volume", + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Left Max Volume", RK3308_ALC_L_DIG_CON09(3), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Right Max Volume", RK3308_ALC_R_DIG_CON09(3), RK3308_AGC_MAX_GAIN_PGA_SFT, - RK3308_AGC_MAX_GAIN_PGA_NDB_13_5, - RK3308_AGC_MAX_GAIN_PGA_PDB_28_5, - 0, rk3308_codec_alc_agc_ch_max_gain_tlv), + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), /* ALC AGC MIN */ - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 0 Min Volume", + SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Left Min Volume", RK3308_ALC_L_DIG_CON09(0), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 0 Right Min Volume", RK3308_ALC_R_DIG_CON09(0), RK3308_AGC_MIN_GAIN_PGA_SFT, - RK3308_AGC_MIN_GAIN_PGA_NDB_18, - RK3308_AGC_MIN_GAIN_PGA_PDB_24, - 0, rk3308_codec_alc_agc_ch_min_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 1 Min Volume", + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Left Min Volume", RK3308_ALC_L_DIG_CON09(1), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 1 Right Min Volume", RK3308_ALC_R_DIG_CON09(1), RK3308_AGC_MIN_GAIN_PGA_SFT, - RK3308_AGC_MIN_GAIN_PGA_NDB_18, - RK3308_AGC_MIN_GAIN_PGA_PDB_24, - 0, rk3308_codec_alc_agc_ch_min_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 2 Min Volume", + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Left Min Volume", RK3308_ALC_L_DIG_CON09(2), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 2 Right Min Volume", RK3308_ALC_R_DIG_CON09(2), RK3308_AGC_MIN_GAIN_PGA_SFT, - RK3308_AGC_MIN_GAIN_PGA_NDB_18, - RK3308_AGC_MIN_GAIN_PGA_PDB_24, - 0, rk3308_codec_alc_agc_ch_min_gain_tlv), - SOC_DOUBLE_R_RANGE_TLV("ALC AGC Channel 3 Min Volume", + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + + SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Left Min Volume", RK3308_ALC_L_DIG_CON09(3), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC AGC Group 3 Right Min Volume", RK3308_ALC_R_DIG_CON09(3), RK3308_AGC_MIN_GAIN_PGA_SFT, - RK3308_AGC_MIN_GAIN_PGA_NDB_18, - RK3308_AGC_MIN_GAIN_PGA_PDB_24, - 0, rk3308_codec_alc_agc_ch_min_gain_tlv), - - /* ADC MIC */ - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 0 Left Volume", - RK3308_ADC_ANA_CON01(0), - RK3308_ADC_CH1_MIC_GAIN_SFT, - RK3308_ADC_CH1_MIC_GAIN_0DB, - RK3308_ADC_CH1_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 0 Right Volume", - RK3308_ADC_ANA_CON01(0), - RK3308_ADC_CH2_MIC_GAIN_SFT, - RK3308_ADC_CH2_MIC_GAIN_0DB, - RK3308_ADC_CH2_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 1 Left Volume", - RK3308_ADC_ANA_CON01(1), - RK3308_ADC_CH1_MIC_GAIN_SFT, - RK3308_ADC_CH1_MIC_GAIN_0DB, - RK3308_ADC_CH1_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 1 Right Volume", - RK3308_ADC_ANA_CON01(1), - RK3308_ADC_CH2_MIC_GAIN_SFT, - RK3308_ADC_CH2_MIC_GAIN_0DB, - RK3308_ADC_CH2_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 2 Left Volume", - RK3308_ADC_ANA_CON01(2), - RK3308_ADC_CH1_MIC_GAIN_SFT, - RK3308_ADC_CH1_MIC_GAIN_0DB, - RK3308_ADC_CH1_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 2 Right Volume", - RK3308_ADC_ANA_CON01(2), - RK3308_ADC_CH2_MIC_GAIN_SFT, - RK3308_ADC_CH2_MIC_GAIN_0DB, - RK3308_ADC_CH2_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 3 Left Volume", - RK3308_ADC_ANA_CON01(3), - RK3308_ADC_CH1_MIC_GAIN_SFT, - RK3308_ADC_CH1_MIC_GAIN_0DB, - RK3308_ADC_CH1_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC MIC Channel 3 Right Volume", - RK3308_ADC_ANA_CON01(3), - RK3308_ADC_CH2_MIC_GAIN_SFT, - RK3308_ADC_CH2_MIC_GAIN_0DB, - RK3308_ADC_CH2_MIC_GAIN_30DB, - 0, rk3308_codec_adc_mic_gain_tlv), + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + + /* ALC AGC Switch */ + SOC_ENUM_EXT("ALC AGC Group 0 Left Switch", rk3308_agc_enum_array[0], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 0 Right Switch", rk3308_agc_enum_array[1], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 1 Left Switch", rk3308_agc_enum_array[2], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 1 Right Switch", rk3308_agc_enum_array[3], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 2 Left Switch", rk3308_agc_enum_array[4], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 2 Right Switch", rk3308_agc_enum_array[5], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 3 Left Switch", rk3308_agc_enum_array[6], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("ALC AGC Group 3 Right Switch", rk3308_agc_enum_array[7], + rk3308_codec_agc_get, rk3308_codec_agc_put), + + /* ALC AGC Approximate Sample Rate */ + SOC_ENUM_EXT("AGC Group 0 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[0], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 0 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[1], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 1 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[2], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 1 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[3], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 2 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[4], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 2 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[5], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 3 Left Approximate Sample Rate", rk3308_agc_asr_enum_array[6], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + SOC_ENUM_EXT("AGC Group 3 Right Approximate Sample Rate", rk3308_agc_asr_enum_array[7], + rk3308_codec_agc_asr_get, rk3308_codec_agc_asr_put), + + /* ADC MICBIAS Voltage */ + SOC_ENUM_EXT("ADC MICBIAS Voltage", rk3308_micbias_volts_enum_array[0], + rk3308_codec_micbias_volts_get, rk3308_codec_micbias_volts_put), + + /* ADC Main MICBIAS Switch */ + SOC_ENUM_EXT("ADC Main MICBIAS", rk3308_main_micbias_enum_array[0], + rk3308_codec_main_micbias_get, rk3308_codec_main_micbias_put), + + /* ADC MICBIAS1 and MICBIAS2 Switch */ + SOC_SINGLE("ADC MICBIAS1", RK3308_ADC_ANA_CON07(1), + RK3308_ADC_MIC_BIAS_BUF_SFT, 1, 0), + SOC_SINGLE("ADC MICBIAS2", RK3308_ADC_ANA_CON07(2), + RK3308_ADC_MIC_BIAS_BUF_SFT, 1, 0), + + /* ADC MIC Mute/Work Switch */ + SOC_ENUM_EXT("ADC MIC Group 0 Left Switch", rk3308_mic_mute_enum_array[0], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 0 Right Switch", rk3308_mic_mute_enum_array[1], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 1 Left Switch", rk3308_mic_mute_enum_array[2], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 1 Right Switch", rk3308_mic_mute_enum_array[3], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 2 Left Switch", rk3308_mic_mute_enum_array[4], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 2 Right Switch", rk3308_mic_mute_enum_array[5], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 3 Left Switch", rk3308_mic_mute_enum_array[6], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), + SOC_ENUM_EXT("ADC MIC Group 3 Right Switch", rk3308_mic_mute_enum_array[7], + rk3308_codec_mic_mute_get, rk3308_codec_mic_mute_put), /* ADC ALC */ - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 0 Left Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 0 Left Volume", RK3308_ADC_ANA_CON03(0), RK3308_ADC_CH1_ALC_GAIN_SFT, - RK3308_ADC_CH1_ALC_GAIN_NDB_18, - RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 0 Right Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 0 Right Volume", RK3308_ADC_ANA_CON04(0), RK3308_ADC_CH2_ALC_GAIN_SFT, - RK3308_ADC_CH2_ALC_GAIN_NDB_18, - RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 1 Left Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 1 Left Volume", RK3308_ADC_ANA_CON03(1), RK3308_ADC_CH1_ALC_GAIN_SFT, - RK3308_ADC_CH1_ALC_GAIN_NDB_18, - RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 1 Right Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 1 Right Volume", RK3308_ADC_ANA_CON04(1), RK3308_ADC_CH2_ALC_GAIN_SFT, - RK3308_ADC_CH2_ALC_GAIN_NDB_18, - RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 2 Left Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 2 Left Volume", RK3308_ADC_ANA_CON03(2), RK3308_ADC_CH1_ALC_GAIN_SFT, - RK3308_ADC_CH1_ALC_GAIN_NDB_18, - RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 2 Right Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 2 Right Volume", RK3308_ADC_ANA_CON04(2), RK3308_ADC_CH2_ALC_GAIN_SFT, - RK3308_ADC_CH2_ALC_GAIN_NDB_18, - RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 3 Left Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 3 Left Volume", RK3308_ADC_ANA_CON03(3), RK3308_ADC_CH1_ALC_GAIN_SFT, - RK3308_ADC_CH1_ALC_GAIN_NDB_18, - RK3308_ADC_CH1_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - SOC_SINGLE_RANGE_TLV("ADC ALC Channel 3 Right Volume", + SOC_SINGLE_RANGE_TLV("ADC ALC Group 3 Right Volume", RK3308_ADC_ANA_CON04(3), RK3308_ADC_CH2_ALC_GAIN_SFT, - RK3308_ADC_CH2_ALC_GAIN_NDB_18, - RK3308_ADC_CH2_ALC_GAIN_PDB_28_5, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, 0, rk3308_codec_adc_alc_gain_tlv), - /* DAC */ - SOC_SINGLE_RANGE_TLV("DAC Left Volume", - RK3308_DAC_ANA_CON04, - RK3308_DAC_L_GAIN_SFT, - RK3308_DAC_L_GAIN_0DB, - RK3308_DAC_L_GAIN_PDB_6, - 0, rk3308_codec_dac_gain_tlv), - SOC_SINGLE_RANGE_TLV("DAC Right Volume", - RK3308_DAC_ANA_CON04, - RK3308_DAC_R_GAIN_SFT, - RK3308_DAC_R_GAIN_0DB, - RK3308_DAC_R_GAIN_PDB_6, - 0, rk3308_codec_dac_gain_tlv), + /* ADC High Pass Filter */ + SOC_ENUM_EXT("ADC Group 0 HPF Cut-off", rk3308_hpf_enum_array[0], + rk3308_codec_hpf_get, rk3308_codec_hpf_put), + SOC_ENUM_EXT("ADC Group 1 HPF Cut-off", rk3308_hpf_enum_array[1], + rk3308_codec_hpf_get, rk3308_codec_hpf_put), + SOC_ENUM_EXT("ADC Group 2 HPF Cut-off", rk3308_hpf_enum_array[2], + rk3308_codec_hpf_get, rk3308_codec_hpf_put), + SOC_ENUM_EXT("ADC Group 3 HPF Cut-off", rk3308_hpf_enum_array[3], + rk3308_codec_hpf_get, rk3308_codec_hpf_put), + + /* DAC LINEOUT */ + SOC_SINGLE_TLV("DAC LINEOUT Left Volume", + RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_GAIN_SFT, + RK3308_DAC_L_LINEOUT_GAIN_MAX, + 0, rk3308_codec_dac_lineout_gain_tlv), + SOC_SINGLE_TLV("DAC LINEOUT Right Volume", + RK3308_DAC_ANA_CON04, + RK3308_DAC_R_LINEOUT_GAIN_SFT, + RK3308_DAC_R_LINEOUT_GAIN_MAX, + 0, rk3308_codec_dac_lineout_gain_tlv), /* DAC HPOUT */ - SOC_SINGLE_RANGE_TLV("DAC HPOUT Left Volume", - RK3308_DAC_ANA_CON05, - RK3308_DAC_L_HPOUT_GAIN_SFT, - RK3308_DAC_L_HPOUT_GAIN_NDB_39, - RK3308_DAC_L_HPOUT_GAIN_PDB_6, - 0, rk3308_codec_dac_hpout_gain_tlv), - SOC_SINGLE_RANGE_TLV("DAC HPOUT Right Volume", - RK3308_DAC_ANA_CON06, - RK3308_DAC_R_HPOUT_GAIN_SFT, - RK3308_DAC_R_HPOUT_GAIN_NDB_39, - RK3308_DAC_R_HPOUT_GAIN_PDB_6, - 0, rk3308_codec_dac_hpout_gain_tlv), + SOC_SINGLE_EXT_TLV("DAC HPOUT Left Volume", + RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_SFT, + RK3308_DAC_L_HPOUT_GAIN_MAX, + 0, + rk3308_codec_hpout_l_get_tlv, + rk3308_codec_hpout_l_put_tlv, + rk3308_codec_dac_hpout_gain_tlv), + SOC_SINGLE_EXT_TLV("DAC HPOUT Right Volume", + RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_SFT, + RK3308_DAC_R_HPOUT_GAIN_MAX, + 0, + rk3308_codec_hpout_r_get_tlv, + rk3308_codec_hpout_r_put_tlv, + rk3308_codec_dac_hpout_gain_tlv), /* DAC HPMIX */ SOC_SINGLE_RANGE_TLV("DAC HPMIX Left Volume", - RK3308_DAC_ANA_CON05, + RK3308_DAC_ANA_CON12, RK3308_DAC_L_HPMIX_GAIN_SFT, - RK3308_DAC_L_HPMIX_GAIN_NDB_6, - RK3308_DAC_L_HPMIX_GAIN_0DB, + RK3308_DAC_L_HPMIX_GAIN_MIN, + RK3308_DAC_L_HPMIX_GAIN_MAX, 0, rk3308_codec_dac_hpmix_gain_tlv), SOC_SINGLE_RANGE_TLV("DAC HPMIX Right Volume", - RK3308_DAC_ANA_CON05, + RK3308_DAC_ANA_CON12, RK3308_DAC_R_HPMIX_GAIN_SFT, - RK3308_DAC_R_HPMIX_GAIN_NDB_6, - RK3308_DAC_R_HPMIX_GAIN_0DB, + RK3308_DAC_R_HPMIX_GAIN_MIN, + RK3308_DAC_R_HPMIX_GAIN_MAX, 0, rk3308_codec_dac_hpmix_gain_tlv), }; -static void rk3308_speaker_ctl(struct rk3308_codec_priv *rk3308, int on) +static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - gpiod_direction_output(rk3308->spk_ctl_gpio, on); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } + + if (e->shift_l) + ucontrol->value.integer.value[0] = rk3308->agc_r[e->reg]; + else + ucontrol->value.integer.value[0] = rk3308->agc_l[e->reg]; + + return 0; } -static int rk3308_codec_reset(struct snd_soc_codec *codec) +static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.integer.value[0]; + int grp = e->reg; - reset_control_assert(rk3308->reset); - usleep_range(200, 300); /* estimated value */ - reset_control_deassert(rk3308->reset); + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } - regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00); - usleep_range(200, 300); /* estimated value */ - regmap_write(rk3308->regmap, RK3308_GLB_CON, - RK3308_SYS_WORK | - RK3308_DAC_DIG_WORK | - RK3308_ADC_DIG_WORK); + if (value) { + /* ALC AGC On */ + if (e->shift_l) { + /* ALC AGC Right On */ + regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK, + RK3308_AGC_FUNC_SEL_EN); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, + RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); + + rk3308->agc_r[e->reg] = 1; + } else { + /* ALC AGC Left On */ + regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK, + RK3308_AGC_FUNC_SEL_EN); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK, + RK3308_ADC_ALCL_CON_GAIN_PGAL_EN); + + rk3308->agc_l[e->reg] = 1; + } + } else { + /* ALC AGC Off */ + if (e->shift_l) { + /* ALC AGC Right Off */ + regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK, + RK3308_AGC_FUNC_SEL_DIS); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, + RK3308_ADC_ALCR_CON_GAIN_PGAR_DIS); + + rk3308->agc_r[e->reg] = 0; + } else { + /* ALC AGC Left Off */ + regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK, + RK3308_AGC_FUNC_SEL_DIS); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK, + RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS); + + rk3308->agc_l[e->reg] = 0; + } + } return 0; } -static int rk3308_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) +static int rk3308_codec_agc_asr_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - switch (level) { - case SND_SOC_BIAS_ON: - break; - - case SND_SOC_BIAS_PREPARE: - break; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; - case SND_SOC_BIAS_STANDBY: - case SND_SOC_BIAS_OFF: - break; + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; } - snd_soc_codec_force_bias_level(codec, level); + if (e->shift_l) { + regmap_read(rk3308->regmap, RK3308_ALC_R_DIG_CON04(grp), &value); + rk3308->agc_asr_r[e->reg] = value >> RK3308_AGC_APPROX_RATE_SFT; + ucontrol->value.integer.value[0] = rk3308->agc_asr_r[e->reg]; + } else { + regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON04(grp), &value); + rk3308->agc_asr_l[e->reg] = value >> RK3308_AGC_APPROX_RATE_SFT; + ucontrol->value.integer.value[0] = rk3308->agc_asr_l[e->reg]; + } return 0; } -static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) +static int rk3308_codec_agc_asr_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; - int ch = rk3308->adc_ch; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - adc_aif2 |= RK3308_ADC_IO_MODE_SLAVE; - adc_aif2 |= RK3308_ADC_MODE_SLAVE; - dac_aif2 |= RK3308_DAC_IO_MODE_SLAVE; - dac_aif2 |= RK3308_DAC_MODE_SLAVE; - break; - case SND_SOC_DAIFMT_CBM_CFM: - adc_aif2 |= RK3308_ADC_IO_MODE_MASTER; - adc_aif2 |= RK3308_ADC_MODE_MASTER; - dac_aif2 |= RK3308_DAC_IO_MODE_MASTER; - dac_aif2 |= RK3308_DAC_MODE_MASTER; - break; - default: + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_DSP_A: - adc_aif1 |= RK3308_ADC_I2S_MODE_PCM; - dac_aif1 |= RK3308_DAC_I2S_MODE_PCM; - break; - case SND_SOC_DAIFMT_I2S: - adc_aif1 |= RK3308_ADC_I2S_MODE_I2S; - dac_aif1 |= RK3308_DAC_I2S_MODE_I2S; - break; - case SND_SOC_DAIFMT_RIGHT_J: - adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; - dac_aif1 |= RK3308_DAC_I2S_MODE_RJ; - break; - case SND_SOC_DAIFMT_LEFT_J: - adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; - dac_aif1 |= RK3308_DAC_I2S_MODE_LJ; - break; - default: - return -EINVAL; + value = ucontrol->value.integer.value[0] << RK3308_AGC_APPROX_RATE_SFT; + + if (e->shift_l) { + /* ALC AGC Right Approximate Sample Rate */ + regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON04(grp), + RK3308_AGC_APPROX_RATE_MSK, + value); + rk3308->agc_asr_r[e->reg] = ucontrol->value.integer.value[0]; + } else { + /* ALC AGC Left Approximate Sample Rate */ + regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON04(grp), + RK3308_AGC_APPROX_RATE_MSK, + value); + rk3308->agc_asr_l[e->reg] = ucontrol->value.integer.value[0]; } - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; - adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; - dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; - dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; - break; - case SND_SOC_DAIFMT_IB_IF: - adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; - adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; - dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; - dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; - break; - case SND_SOC_DAIFMT_IB_NF: - adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; - adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; - dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; - dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; - break; - case SND_SOC_DAIFMT_NB_IF: - adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; - adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; - dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; - dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; - break; - default: + return 0; +} + +static int rk3308_codec_mic_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; + + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); return -EINVAL; } - regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(ch), - RK3308_ADC_I2S_LRC_POL_MSK | - RK3308_ADC_I2S_MODE_MSK, - adc_aif1); - regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(ch), - RK3308_ADC_IO_MODE_MSK | - RK3308_ADC_MODE_MSK | - RK3308_ADC_I2S_BIT_CLK_POL_MSK, - adc_aif2); - - regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, - RK3308_DAC_I2S_LRC_POL_MSK | - RK3308_DAC_I2S_MODE_MSK, - dac_aif1); - regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, - RK3308_DAC_IO_MODE_MSK | - RK3308_DAC_MODE_MSK | - RK3308_DAC_I2S_BIT_CLK_POL_MSK, - dac_aif2); + if (e->shift_l) { + /* ADC MIC Right Mute/Work Infos */ + regmap_read(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), &value); + rk3308->mic_mute_r[e->reg] = (value & RK3308_ADC_R_CH_BIST_SINE) >> + RK3308_ADC_R_CH_BIST_SFT; + ucontrol->value.integer.value[0] = rk3308->mic_mute_r[e->reg]; + } else { + /* ADC MIC Left Mute/Work Infos */ + regmap_read(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), &value); + rk3308->mic_mute_l[e->reg] = (value & RK3308_ADC_L_CH_BIST_SINE) >> + RK3308_ADC_L_CH_BIST_SFT; + ucontrol->value.integer.value[0] = rk3308->mic_mute_l[e->reg]; + } return 0; } -static int rk3308_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int rk3308_codec_mic_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = dai->codec; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; - int ch = rk3308->adc_ch; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; + int grp = e->reg; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS; - dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS; - dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS; - break; - case SNDRV_PCM_FORMAT_S24_LE: - adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS; - dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS; - break; - case SNDRV_PCM_FORMAT_S32_LE: - adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS; - dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS; - break; - default: + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); return -EINVAL; } - switch (params_channels(params)) { - case 1: - adc_aif1 |= RK3308_ADC_I2S_MONO; - break; - case 2: - adc_aif1 |= RK3308_ADC_I2S_STEREO; - break; - default: - return -EINVAL; + if (e->shift_l) { + /* ADC MIC Right Mute/Work Configuration */ + value = ucontrol->value.integer.value[0] << RK3308_ADC_R_CH_BIST_SFT; + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_SINE, + value); + rk3308->mic_mute_r[e->reg] = ucontrol->value.integer.value[0]; + } else { + /* ADC MIC Left Mute/Work Configuration */ + value = ucontrol->value.integer.value[0] << RK3308_ADC_L_CH_BIST_SFT; + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_SINE, + value); + rk3308->mic_mute_l[e->reg] = ucontrol->value.integer.value[0]; } - adc_aif1 |= RK3308_ADC_I2S_LR_NORMAL; - adc_aif2 |= RK3308_ADC_I2S_WORK; - dac_aif1 |= RK3308_DAC_I2S_LR_NORMAL; - dac_aif2 |= RK3308_DAC_I2S_WORK; + return 0; +} - regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(ch), - RK3308_ADC_I2S_VALID_LEN_MSK | - RK3308_ADC_I2S_LR_MSK | - RK3308_ADC_I2S_TYPE_MSK, - adc_aif1); - regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(ch), - RK3308_ADC_I2S_MSK, - adc_aif2); +static int rk3308_codec_micbias_volts_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, - RK3308_DAC_I2S_VALID_LEN_MSK | - RK3308_DAC_I2S_LR_MSK, - dac_aif1); - regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, - RK3308_DAC_I2S_MSK, - dac_aif2); + ucontrol->value.integer.value[0] = rk3308->micbias_volt; return 0; } -static int rk3308_digital_mute(struct snd_soc_dai *dai, int mute) +static int rk3308_codec_micbias_volts_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + unsigned int volt = ucontrol->value.integer.value[0]; + int ret; + + ret = check_micbias(volt); + if (ret < 0) { + dev_err(rk3308->plat_dev, "The invalid micbias volt: %d\n", + volt); + return ret; + } + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), + RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, + volt); + + rk3308->micbias_volt = volt; + return 0; } -static int rk3308_codec_dac_enable(struct rk3308_codec_priv *rk3308) +static int rk3308_codec_main_micbias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - /* Step 01 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, - RK3308_DAC_CURRENT_MSK, - RK3308_DAC_CURRENT_EN); - - /* Step 02 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, - RK3308_DAC_BUF_REF_L_MSK | - RK3308_DAC_BUF_REF_R_MSK, - RK3308_DAC_BUF_REF_L_EN | - RK3308_DAC_BUF_REF_R_EN); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - /* Step 03 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, - RK3308_DAC_POP_SOUND_L_MSK | - RK3308_DAC_POP_SOUND_R_MSK, - RK3308_DAC_POP_SOUND_L_WORK | - RK3308_DAC_POP_SOUND_R_WORK); + ucontrol->value.integer.value[0] = rk3308->enable_micbias; - /* Step 04 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, - RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN, - RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN); + return 0; +} - /* Step 05 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, - RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK, - RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK); +static int rk3308_codec_main_micbias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + unsigned int on = ucontrol->value.integer.value[0]; + + if (on) { + if (!rk3308->enable_micbias) + rk3308_codec_micbias_enable(rk3308, rk3308->micbias_volt); + } else { + if (rk3308->enable_micbias) + rk3308_codec_micbias_disable(rk3308); + } - /* Step 06 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, - RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN, - RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN); + return 0; +} - /* Step 07 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, - RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN, - RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN); +static int rk3308_codec_mic_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return snd_soc_get_volsw_range(kcontrol, ucontrol); +} - /* Step 08 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, - RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK, - RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK); +static int rk3308_codec_mic_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + unsigned int gain = ucontrol->value.integer.value[0]; - /* Step 09 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN, - RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN); + if (gain > RK3308_ADC_CH1_MIC_GAIN_MAX) { + dev_err(rk3308->plat_dev, "%s: invalid mic gain: %d\n", + __func__, gain); + return -EINVAL; + } - /* Step 10 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN, - RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN); + if (rk3308->codec_ver == ACODEC_VERSION_A) { + /* + * From the TRM, there are only suupport 0dB(gain==0) and + * 20dB(gain==3) on the codec version A. + */ + if (!(gain == 0 || gain == RK3308_ADC_CH1_MIC_GAIN_MAX)) { + dev_err(rk3308->plat_dev, + "version A doesn't supported: %d, expect: 0,%d\n", + gain, RK3308_ADC_CH1_MIC_GAIN_MAX); + return 0; + } + } - /* Step 11 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN, - RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN); + return snd_soc_put_volsw_range(kcontrol, ucontrol); +} - /* Step 12 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK, - RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK); +static int rk3308_codec_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value; - /* Step 13 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, - RK3308_DAC_L_HPMIX_SEL_MSK | - RK3308_DAC_R_HPMIX_SEL_MSK, - RK3308_DAC_L_HPMIX_I2S | - RK3308_DAC_R_HPMIX_I2S); + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } - /* Step 14 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, - RK3308_DAC_L_HPMIX_UNMUTE | - RK3308_DAC_R_HPMIX_UNMUTE, - RK3308_DAC_L_HPMIX_UNMUTE | - RK3308_DAC_R_HPMIX_UNMUTE); + regmap_read(rk3308->regmap, RK3308_ADC_DIG_CON04(e->reg), &value); + if (value & RK3308_ADC_HPF_PATH_MSK) + rk3308->hpf_cutoff[e->reg] = 0; + else + rk3308->hpf_cutoff[e->reg] = 1; - /* Step 15 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, - RK3308_DAC_L_HPMIX_GAIN_MSK | - RK3308_DAC_R_HPMIX_GAIN_MSK, - RK3308_DAC_L_HPMIX_GAIN_0DB | - RK3308_DAC_R_HPMIX_GAIN_0DB); + ucontrol->value.integer.value[0] = rk3308->hpf_cutoff[e->reg]; - /* Step 16 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, - RK3308_DAC_L_HPOUT_UNMUTE | - RK3308_DAC_R_HPOUT_UNMUTE, - RK3308_DAC_L_HPOUT_UNMUTE | - RK3308_DAC_R_HPOUT_UNMUTE); + return 0; +} - /* Step 17 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, - RK3308_DAC_L_LINEOUT_UNMUTE | - RK3308_DAC_R_LINEOUT_UNMUTE, - RK3308_DAC_L_LINEOUT_UNMUTE | - RK3308_DAC_R_LINEOUT_UNMUTE); +static int rk3308_codec_hpf_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.integer.value[0]; - /* Step 18 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, - RK3308_DAC_L_HPOUT_GAIN_MSK, - RK3308_DAC_L_HPOUT_GAIN_0DB); + if (e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "%s: Invalid ADC grp: %d\n", __func__, e->reg); + return -EINVAL; + } - /* Step 18 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, - RK3308_DAC_R_HPOUT_GAIN_MSK, - RK3308_DAC_R_HPOUT_GAIN_0DB); + if (value) { + /* Enable high pass filter for ADCs */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON04(e->reg), + RK3308_ADC_HPF_PATH_MSK, + RK3308_ADC_HPF_PATH_EN); + } else { + /* Disable high pass filter for ADCs. */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON04(e->reg), + RK3308_ADC_HPF_PATH_MSK, + RK3308_ADC_HPF_PATH_DIS); + } - /* Step 19 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, - RK3308_DAC_L_GAIN_MSK | RK3308_DAC_R_GAIN_MSK, - RK3308_DAC_L_GAIN_0DB | RK3308_DAC_R_GAIN_0DB); + rk3308->hpf_cutoff[e->reg] = value; return 0; } -static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308) +static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - /* Step 01 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, - RK3308_DAC_L_GAIN_MSK | RK3308_DAC_R_GAIN_MSK, - RK3308_DAC_L_GAIN_0DB | RK3308_DAC_R_GAIN_0DB); - - /* - * Step 02 - * - * Note1. In the step2, adjusting the register step by step to the - * appropriate value and taking 20ms as time step - */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, - RK3308_DAC_L_HPOUT_GAIN_MSK, - RK3308_DAC_L_HPOUT_GAIN_NDB_39); - - /* Step 02 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, - RK3308_DAC_R_HPOUT_GAIN_MSK, - RK3308_DAC_R_HPOUT_GAIN_NDB_39); + return snd_soc_get_volsw_range(kcontrol, ucontrol); +} - /* Step 03 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, - RK3308_DAC_L_HPMIX_UNMUTE | - RK3308_DAC_R_HPMIX_UNMUTE, - RK3308_DAC_L_HPMIX_MUTE | - RK3308_DAC_R_HPMIX_MUTE); +static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + unsigned int dgain = ucontrol->value.integer.value[0]; - /* Step 04 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, - RK3308_DAC_L_HPMIX_SEL_MSK | - RK3308_DAC_R_HPMIX_SEL_MSK, - RK3308_DAC_L_HPMIX_NONE | - RK3308_DAC_R_HPMIX_NONE); + if (dgain > RK3308_DAC_L_HPOUT_GAIN_MAX) { + dev_err(rk3308->plat_dev, "%s: invalid l_dgain: %d\n", + __func__, dgain); + return -EINVAL; + } - /* Step 05 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, - RK3308_DAC_L_HPOUT_UNMUTE | - RK3308_DAC_R_HPOUT_UNMUTE, - RK3308_DAC_L_HPOUT_MUTE | - RK3308_DAC_R_HPOUT_MUTE); + rk3308->hpout_l_dgain = dgain; - /* Step 06 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK, - RK3308_DAC_L_DAC_INIT | RK3308_DAC_R_DAC_INIT); + return snd_soc_put_volsw_range(kcontrol, ucontrol); +} - /* Step 07 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, - RK3308_DAC_L_HPOUT_EN | RK3308_DAC_R_HPOUT_EN, - RK3308_DAC_L_HPOUT_DIS | RK3308_DAC_R_HPOUT_DIS); +static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return snd_soc_get_volsw_range(kcontrol, ucontrol); +} - /* Step 08 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, - RK3308_DAC_L_LINEOUT_UNMUTE | - RK3308_DAC_R_LINEOUT_UNMUTE, - RK3308_DAC_L_LINEOUT_MUTE | - RK3308_DAC_R_LINEOUT_MUTE); +static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + unsigned int dgain = ucontrol->value.integer.value[0]; - /* Step 09 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, - RK3308_DAC_L_LINEOUT_EN | RK3308_DAC_R_LINEOUT_EN, - RK3308_DAC_L_LINEOUT_DIS | RK3308_DAC_R_LINEOUT_DIS); + if (dgain > RK3308_DAC_R_HPOUT_GAIN_MAX) { + dev_err(rk3308->plat_dev, "%s: invalid r_dgain: %d\n", + __func__, dgain); + return -EINVAL; + } - /* Step 10 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, - RK3308_DAC_L_HPMIX_EN | RK3308_DAC_R_HPMIX_EN, - RK3308_DAC_L_HPMIX_DIS | RK3308_DAC_R_HPMIX_DIS); + rk3308->hpout_r_dgain = dgain; - /* Step 11 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN, - RK3308_DAC_L_DAC_DIS | RK3308_DAC_R_DAC_DIS); + return snd_soc_put_volsw_range(kcontrol, ucontrol); +} - /* Step 12 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN, - RK3308_DAC_L_CLK_DIS | RK3308_DAC_R_CLK_DIS); +static u32 to_mapped_grp(struct rk3308_codec_priv *rk3308, int idx) +{ + return rk3308->i2s_sdis[idx]; +} - /* Step 13 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, - RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN, - RK3308_DAC_L_REF_DIS | RK3308_DAC_R_REF_DIS); +static bool adc_for_each_grp(struct rk3308_codec_priv *rk3308, + int type, int idx, u32 *grp) +{ + if (type == ADC_TYPE_NORMAL) { + u32 mapped_grp = to_mapped_grp(rk3308, idx); + int max_grps; + + if (rk3308->enable_all_adcs) + max_grps = ADC_LR_GROUP_MAX; + else + max_grps = rk3308->used_adc_grps; + + if (idx >= max_grps) + return false; + + if ((!rk3308->loopback_dacs_enabled) && + handle_loopback(rk3308) && + rk3308->loopback_grp == mapped_grp) { + /* + * Ths loopback DACs are closed, and specify the + * loopback ADCs. + */ + *grp = ADC_GRP_SKIP_MAGIC; + } else if (rk3308->en_always_grps_num && + rk3308->skip_grps[mapped_grp]) { + /* To set the skip flag if the ADC GRP is enabled. */ + *grp = ADC_GRP_SKIP_MAGIC; + } else { + *grp = mapped_grp; + } - /* Step 14 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, - RK3308_DAC_POP_SOUND_L_MSK | - RK3308_DAC_POP_SOUND_R_MSK, - RK3308_DAC_POP_SOUND_L_INIT | - RK3308_DAC_POP_SOUND_R_INIT); + dev_dbg(rk3308->plat_dev, + "ADC_TYPE_NORMAL, idx: %d, mapped_grp: %d, get grp: %d,\n", + idx, mapped_grp, *grp); + } else if (type == ADC_TYPE_ALL) { + if (idx >= ADC_LR_GROUP_MAX) + return false; + + *grp = idx; + dev_dbg(rk3308->plat_dev, + "ADC_TYPE_ALL, idx: %d, get grp: %d\n", + idx, *grp); + } else if (type == ADC_TYPE_DBG) { + if (idx >= ADC_LR_GROUP_MAX) + return false; + + if (idx == (int)rk3308->cur_dbg_grp) + *grp = idx; + else + *grp = ADC_GRP_SKIP_MAGIC; + + dev_dbg(rk3308->plat_dev, + "ADC_TYPE_DBG, idx: %d, get grp: %d\n", + idx, *grp); + } else { + if (idx >= 1) + return false; + + *grp = rk3308->loopback_grp; + dev_dbg(rk3308->plat_dev, + "ADC_TYPE_LOOPBACK, idx: %d, get grp: %d\n", + idx, *grp); + } - /* Step 15 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, - RK3308_DAC_BUF_REF_L_EN | RK3308_DAC_BUF_REF_R_EN, - RK3308_DAC_BUF_REF_L_DIS | RK3308_DAC_BUF_REF_R_DIS); + return true; +} - /* Step 16 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, - RK3308_DAC_CURRENT_EN, - RK3308_DAC_CURRENT_DIS); +static int rk3308_codec_get_dac_path_state(struct rk3308_codec_priv *rk3308) +{ + return rk3308->dac_path_state; +} - /* Step 17 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, - RK3308_DAC_L_HPOUT_WORK | RK3308_DAC_R_HPOUT_WORK, - RK3308_DAC_L_HPOUT_INIT | RK3308_DAC_R_HPOUT_INIT); +static void rk3308_codec_set_dac_path_state(struct rk3308_codec_priv *rk3308, + int state) +{ + rk3308->dac_path_state = state; +} - /* Step 18 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, - RK3308_DAC_L_HPMIX_WORK | RK3308_DAC_R_HPMIX_WORK, - RK3308_DAC_L_HPMIX_INIT | RK3308_DAC_R_HPMIX_INIT); +static void rk3308_headphone_ctl(struct rk3308_codec_priv *rk3308, int on) +{ + if (rk3308->hp_ctl_gpio) + gpiod_direction_output(rk3308->hp_ctl_gpio, on); +} - /* Step 19 */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, - RK3308_DAC_L_HPMIX_GAIN_MSK | - RK3308_DAC_R_HPMIX_GAIN_MSK, - RK3308_DAC_L_HPMIX_GAIN_NDB_6 | - RK3308_DAC_R_HPMIX_GAIN_NDB_6); +static void rk3308_speaker_ctl(struct rk3308_codec_priv *rk3308, int on) +{ + if (on) { + if (rk3308->pa_drv_gpio) { + gpiod_direction_output(rk3308->pa_drv_gpio, on); + msleep(rk3308->delay_pa_drv_ms); + } - /* - * Note2. If the ACODEC_DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] - * is set to 0x1, add the steps from the section Disable DAC - * Configuration Standard Usage Flow after complete the step 19 - */ + if (rk3308->spk_ctl_gpio) + gpiod_direction_output(rk3308->spk_ctl_gpio, on); + } else { + if (rk3308->spk_ctl_gpio) + gpiod_direction_output(rk3308->spk_ctl_gpio, on); - return 0; + if (rk3308->pa_drv_gpio) { + msleep(rk3308->delay_pa_drv_ms); + gpiod_direction_output(rk3308->pa_drv_gpio, on); + } + } } -static int rk3308_codec_power_on(struct snd_soc_codec *codec) +static int rk3308_codec_reset(struct snd_soc_codec *codec) { struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - /* 1. Supply the power of digital part and reset the Audio Codec */ - /* Do nothing */ + reset_control_assert(rk3308->reset); + usleep_range(2000, 2500); /* estimated value */ + reset_control_deassert(rk3308->reset); - /* - * 2. Configure ACODEC_DAC_ANA_CON1[1:0] and ACODEC_DAC_ANA_CON1[5:4] - * to 0x1, to setup dc voltage of the DAC channel output - */ - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, - RK3308_DAC_POP_SOUND_L_MSK, RK3308_DAC_POP_SOUND_L_INIT); - regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, - RK3308_DAC_POP_SOUND_R_MSK, RK3308_DAC_POP_SOUND_R_INIT); + regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00); + usleep_range(200, 300); /* estimated value */ + regmap_write(rk3308->regmap, RK3308_GLB_CON, + RK3308_SYS_WORK | + RK3308_DAC_DIG_WORK | + RK3308_ADC_DIG_WORK); - /* - * 3. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 0x1 - * - * Note: Only the reg (ADC_ANA_CON10+0x0)[6:0] represent the control - * signal to select current to pre-charge/dis_charge - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), - RK3308_ADC_CURRENT_CHARGE_MSK, RK3308_ADC_SEL_I_64(1)); + return 0; +} - /* 4. Supply the power of the analog part(AVDD,AVDDRV) */ +static int rk3308_codec_adc_dig_reset(struct rk3308_codec_priv *rk3308) +{ + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_ADC_DIG_WORK, + RK3308_ADC_DIG_RESET); + udelay(50); + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_ADC_DIG_WORK, + RK3308_ADC_DIG_WORK); - /* - * 5. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x1 to setup - * reference voltage - * - * Note: Only the reg (ADC_ANA_CON10+0x0)[7] represent the enable - * signal of reference voltage module - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), - RK3308_ADC_REF_EN, RK3308_ADC_REF_EN); + return 0; +} - /* - * 6. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to - * 0x7f step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to - * 0x7f directly. The suggestion slot time of the step is 20ms. - */ - mdelay(20); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), - RK3308_ADC_CURRENT_CHARGE_MSK, - RK3308_ADC_DONT_SEL_ALL); +static int rk3308_codec_dac_dig_reset(struct rk3308_codec_priv *rk3308) +{ + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_DAC_DIG_WORK, + RK3308_DAC_DIG_RESET); + udelay(50); + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_DAC_DIG_WORK, + RK3308_DAC_DIG_WORK); - /* 7. Wait until the voltage of VCM keeps stable at the AVDD/2 */ - usleep_range(200, 300); /* estimated value */ + return 0; +} - /* - * 8. Configure the register ACODEC_ADC_ANA_CON10[6:0] to the - * appropriate value(expect 0x0) for reducing power. - */ +static int rk3308_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - /* TODO: choose an appropriate charge value */ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); + break; + case SND_SOC_BIAS_OFF: + break; + } return 0; } -static int rk3308_codec_power_off(struct snd_soc_codec *codec) +static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) { + struct snd_soc_codec *codec = codec_dai->codec; struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; + int idx, grp, is_master; + int type = ADC_TYPE_ALL; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + adc_aif2 |= RK3308_ADC_IO_MODE_SLAVE; + adc_aif2 |= RK3308_ADC_MODE_SLAVE; + dac_aif2 |= RK3308_DAC_IO_MODE_SLAVE; + dac_aif2 |= RK3308_DAC_MODE_SLAVE; + is_master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + adc_aif2 |= RK3308_ADC_IO_MODE_MASTER; + adc_aif2 |= RK3308_ADC_MODE_MASTER; + dac_aif2 |= RK3308_DAC_IO_MODE_MASTER; + dac_aif2 |= RK3308_DAC_MODE_MASTER; + is_master = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + adc_aif1 |= RK3308_ADC_I2S_MODE_PCM; + dac_aif1 |= RK3308_DAC_I2S_MODE_PCM; + break; + case SND_SOC_DAIFMT_I2S: + adc_aif1 |= RK3308_ADC_I2S_MODE_I2S; + dac_aif1 |= RK3308_DAC_I2S_MODE_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; + dac_aif1 |= RK3308_DAC_I2S_MODE_RJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + adc_aif1 |= RK3308_ADC_I2S_MODE_LJ; + dac_aif1 |= RK3308_DAC_I2S_MODE_LJ; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; + adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; + dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; + dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; + break; + case SND_SOC_DAIFMT_IB_IF: + adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; + adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; + dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; + dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; + break; + case SND_SOC_DAIFMT_IB_NF: + adc_aif1 |= RK3308_ADC_I2S_LRC_POL_NORMAL; + adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; + dac_aif1 |= RK3308_DAC_I2S_LRC_POL_NORMAL; + dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; + break; + case SND_SOC_DAIFMT_NB_IF: + adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; + adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_NORMAL; + dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; + dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_NORMAL; + break; + default: + return -EINVAL; + } /* - * 1. Keep the power on and disable the DAC and ADC path according to - * the section power on configuration standard usage flow. + * Hold ADC Digital registers start at master mode + * + * There are 8 ADCs and use the same SCLK and LRCK internal for master + * mode, We need to make sure that they are in effect at the same time, + * otherwise they will cause the abnormal clocks. */ + if (is_master) + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_ADC_DIG_WORK, + RK3308_ADC_DIG_RESET); + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp), + RK3308_ADC_I2S_LRC_POL_MSK | + RK3308_ADC_I2S_MODE_MSK, + adc_aif1); + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), + RK3308_ADC_IO_MODE_MSK | + RK3308_ADC_MODE_MSK | + RK3308_ADC_I2S_BIT_CLK_POL_MSK, + adc_aif2); + } - /* 2. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 0x1 */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), - RK3308_ADC_CURRENT_CHARGE_MSK, RK3308_ADC_SEL_I_64(1)); + /* Hold ADC Digital registers end at master mode */ + if (is_master) + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_ADC_DIG_WORK, + RK3308_ADC_DIG_WORK); - /* 3. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x0 */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), - RK3308_ADC_REF_EN, RK3308_ADC_REF_DIS); + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, + RK3308_DAC_I2S_LRC_POL_MSK | + RK3308_DAC_I2S_MODE_MSK, + dac_aif1); + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, + RK3308_DAC_IO_MODE_MSK | + RK3308_DAC_MODE_MSK | + RK3308_DAC_I2S_BIT_CLK_POL_MSK, + dac_aif2); + + return 0; +} + +static int rk3308_codec_dac_dig_config(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + unsigned int dac_aif1 = 0, dac_aif2 = 0; + + /* Clear the status of DAC DIG Digital reigisters */ + rk3308_codec_dac_dig_reset(rk3308); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS; + break; + default: + return -EINVAL; + } + + dac_aif1 |= RK3308_DAC_I2S_LR_NORMAL; + dac_aif2 |= RK3308_DAC_I2S_WORK; + + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, + RK3308_DAC_I2S_VALID_LEN_MSK | + RK3308_DAC_I2S_LR_MSK, + dac_aif1); + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, + RK3308_DAC_I2S_MSK, + dac_aif2); + + return 0; +} + +static int rk3308_codec_adc_dig_config(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + unsigned int adc_aif1 = 0, adc_aif2 = 0; + int type = ADC_TYPE_NORMAL; + int idx, grp; + + /* Clear the status of ADC DIG Digital reigisters */ + rk3308_codec_adc_dig_reset(rk3308); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS; + break; + default: + return -EINVAL; + } + + switch (params_channels(params)) { + case 1: + adc_aif1 |= RK3308_ADC_I2S_MONO; + break; + case 2: + case 4: + case 6: + case 8: + adc_aif1 |= RK3308_ADC_I2S_STEREO; + break; + default: + return -EINVAL; + } + + adc_aif1 |= RK3308_ADC_I2S_LR_NORMAL; + adc_aif2 |= RK3308_ADC_I2S_WORK; + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp), + RK3308_ADC_I2S_VALID_LEN_MSK | + RK3308_ADC_I2S_LR_MSK | + RK3308_ADC_I2S_TYPE_MSK, + adc_aif1); + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), + RK3308_ADC_I2S_MSK, + adc_aif2); + } + + return 0; +} + +static int rk3308_codec_update_adc_grps(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + switch (params_channels(params)) { + case 1: + rk3308->used_adc_grps = 1; + break; + case 2: + case 4: + case 6: + case 8: + rk3308->used_adc_grps = params_channels(params) / 2; + break; + default: + dev_err(rk3308->plat_dev, "Invalid channels: %d\n", + params_channels(params)); + return -EINVAL; + } + + return 0; +} + +static int rk3308_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + int dgain; + + if (mute) { + for (dgain = 0x2; dgain <= 0x7; dgain++) { + /* + * Keep the max -> min digital CIC interpolation + * filter gain step by step. + * + * loud: 0x2; whisper: 0x7 + */ + regmap_update_bits(rk3308->regmap, + RK3308_DAC_DIG_CON04, + RK3308_DAC_CIC_IF_GAIN_MSK, + dgain); + usleep_range(200, 300); /* estimated value */ + } + +#if !DEBUG_POP_ALWAYS + rk3308_headphone_ctl(rk3308, 0); + rk3308_speaker_ctl(rk3308, 0); +#endif + } else { +#if !DEBUG_POP_ALWAYS + if (rk3308->dac_output == DAC_LINEOUT) + rk3308_speaker_ctl(rk3308, 1); + else if (rk3308->dac_output == DAC_HPOUT) + rk3308_headphone_ctl(rk3308, 1); + + if (rk3308->delay_start_play_ms) + msleep(rk3308->delay_start_play_ms); +#endif + for (dgain = 0x7; dgain >= 0x2; dgain--) { + /* + * Keep the min -> max digital CIC interpolation + * filter gain step by step + * + * loud: 0x2; whisper: 0x7 + */ + regmap_update_bits(rk3308->regmap, + RK3308_DAC_DIG_CON04, + RK3308_DAC_CIC_IF_GAIN_MSK, + dgain); + usleep_range(200, 300); /* estimated value */ + } + } + } + + return 0; +} + +static int rk3308_codec_digital_fadein(struct rk3308_codec_priv *rk3308) +{ + unsigned int dgain, dgain_ref; + + if (rk3308->hpout_l_dgain != rk3308->hpout_r_dgain) { + pr_warn("HPOUT l_dgain: 0x%x != r_dgain: 0x%x\n", + rk3308->hpout_l_dgain, rk3308->hpout_r_dgain); + dgain_ref = min(rk3308->hpout_l_dgain, rk3308->hpout_r_dgain); + } else { + dgain_ref = rk3308->hpout_l_dgain; + } /* - * 4.Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to 0x7f - * step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to 0x7f - * directly. The suggestion slot time of the step is 20ms + * We'd better change the gain of the left and right channels + * at the same time to avoid different listening */ - mdelay(20); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), - RK3308_ADC_CURRENT_CHARGE_MSK, - RK3308_ADC_DONT_SEL_ALL); + for (dgain = RK3308_DAC_L_HPOUT_GAIN_NDB_39; + dgain <= dgain_ref; dgain++) { + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_MSK, + dgain); + + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_MSK, + dgain); + } + + return 0; +} + +static int rk3308_codec_digital_fadeout(struct rk3308_codec_priv *rk3308) +{ + unsigned int l_dgain, r_dgain; + + /* + * Note. In the step2, adjusting the register step by step to + * the appropriate value and taking 20ms as time step + */ + regmap_read(rk3308->regmap, RK3308_DAC_ANA_CON05, &l_dgain); + l_dgain &= RK3308_DAC_L_HPOUT_GAIN_MSK; + + regmap_read(rk3308->regmap, RK3308_DAC_ANA_CON06, &r_dgain); + r_dgain &= RK3308_DAC_R_HPOUT_GAIN_MSK; + + if (l_dgain != r_dgain) { + pr_warn("HPOUT l_dgain: 0x%x != r_dgain: 0x%x\n", + l_dgain, r_dgain); + l_dgain = min(l_dgain, r_dgain); + } + + /* + * We'd better change the gain of the left and right channels + * at the same time to avoid different listening + */ + while (l_dgain >= RK3308_DAC_L_HPOUT_GAIN_NDB_39) { + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_MSK, + l_dgain); + + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_MSK, + l_dgain); + + usleep_range(200, 300); /* estimated value */ + + if (l_dgain == RK3308_DAC_L_HPOUT_GAIN_NDB_39) + break; + + l_dgain--; + } + + return 0; +} + +static int rk3308_codec_dac_lineout_enable(struct rk3308_codec_priv *rk3308) +{ + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* Step 04 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_DC_FROM_INTERNAL | + RK3308_DAC_R_SEL_DC_FROM_INTERNAL); + } + + /* Step 07 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN); + + udelay(20); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* Step 10 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL | + RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL); + + udelay(20); + } + + /* Step 19 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE); + udelay(20); + + return 0; +} + +static int rk3308_codec_dac_lineout_disable(struct rk3308_codec_priv *rk3308) +{ + /* Step 08 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE, + RK3308_DAC_L_LINEOUT_MUTE | + RK3308_DAC_R_LINEOUT_MUTE); + + /* Step 09 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN, + RK3308_DAC_L_LINEOUT_DIS | + RK3308_DAC_R_LINEOUT_DIS); + + return 0; +} + +static int rk3308_codec_dac_hpout_enable(struct rk3308_codec_priv *rk3308) +{ + /* Step 03 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_WORK | + RK3308_DAC_HPOUT_POP_SOUND_R_WORK); + + udelay(20); + + /* Step 07 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN); + + udelay(20); + + /* Step 08 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK); + + udelay(20); + + /* Step 16 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE); + + udelay(20); + + return 0; +} + +static int rk3308_codec_dac_hpout_disable(struct rk3308_codec_priv *rk3308) +{ + /* Step 03 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_INIT | + RK3308_DAC_HPOUT_POP_SOUND_R_INIT); + + /* Step 07 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN, + RK3308_DAC_L_HPOUT_DIS | + RK3308_DAC_R_HPOUT_DIS); + + /* Step 08 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK, + RK3308_DAC_L_HPOUT_INIT | + RK3308_DAC_R_HPOUT_INIT); + + /* Step 16 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE, + RK3308_DAC_L_HPOUT_MUTE | + RK3308_DAC_R_HPOUT_MUTE); + + return 0; +} + +static int rk3308_codec_dac_switch(struct rk3308_codec_priv *rk3308, + int dac_output) +{ int ret = 0; + + if (rk3308->dac_output == dac_output) { + dev_info(rk3308->plat_dev, + "Don't need to change dac_output: %d\n", dac_output); + goto out; + } + + switch (dac_output) { + case DAC_LINEOUT: + case DAC_HPOUT: + case DAC_LINEOUT_HPOUT: + break; + default: + dev_err(rk3308->plat_dev, "Unknown value: %d\n", dac_output); + ret = -EINVAL; + goto out; + } + + if (rk3308_codec_get_dac_path_state(rk3308) == PATH_BUSY) { + /* + * We can only switch the audio path to LINEOUT or HPOUT on + * codec during playbacking, otherwise, just update the + * dac_output flag. + */ + switch (dac_output) { + case DAC_LINEOUT: + rk3308_headphone_ctl(rk3308, 0); + rk3308_speaker_ctl(rk3308, 1); + rk3308_codec_dac_hpout_disable(rk3308); + rk3308_codec_dac_lineout_enable(rk3308); + break; + case DAC_HPOUT: + rk3308_speaker_ctl(rk3308, 0); + rk3308_headphone_ctl(rk3308, 1); + rk3308_codec_dac_lineout_disable(rk3308); + rk3308_codec_dac_hpout_enable(rk3308); + break; + case DAC_LINEOUT_HPOUT: + rk3308_speaker_ctl(rk3308, 1); + rk3308_headphone_ctl(rk3308, 1); + rk3308_codec_dac_lineout_enable(rk3308); + rk3308_codec_dac_hpout_enable(rk3308); + break; + default: + break; + } + } + + rk3308->dac_output = dac_output; +out: + dev_dbg(rk3308->plat_dev, "switch dac_output to: %d\n", + rk3308->dac_output); + + return ret; +} + +static int rk3308_codec_dac_enable(struct rk3308_codec_priv *rk3308) +{ + /* + * Note1. If the ACODEC_DAC_ANA_CON12[6] or ACODEC_DAC_ANA_CON12[2] + * is set to 0x1, ignoring the step9~12. + */ + + /* + * Note2. If the ACODEC_ DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] + * is set to 0x1, the ADC0 or ADC1 should be enabled firstly, and + * please refer to Enable ADC Configuration Standard Usage Flow(expect + * step7~step9,step14). + */ + + /* + * Note3. If no opening the line out, ignoring the step6, step17 and + * step19. + */ + + /* + * Note4. If no opening the headphone out, ignoring the step3,step7~8, + * step16 and step18. + */ + + /* + * Note5. In the step18, adjust the register step by step to the + * appropriate value and taking 10ms as one time step + */ + + /* + * 1. Set the ACODEC_DAC_ANA_CON0[0] to 0x1, to enable the current + * source of DAC + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, + RK3308_DAC_CURRENT_MSK, + RK3308_DAC_CURRENT_EN); + + udelay(20); + + /* + * 2. Set the ACODEC_DAC_ANA_CON1[6] and ACODEC_DAC_ANA_CON1[2] to 0x1, + * to enable the reference voltage buffer + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_BUF_REF_L_MSK | + RK3308_DAC_BUF_REF_R_MSK, + RK3308_DAC_BUF_REF_L_EN | + RK3308_DAC_BUF_REF_R_EN); + + /* Waiting the stable reference voltage */ + mdelay(1); + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 03 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_WORK | + RK3308_DAC_HPOUT_POP_SOUND_R_WORK); + + udelay(20); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B && + (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT)) { + /* Step 04 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_DC_FROM_INTERNAL | + RK3308_DAC_R_SEL_DC_FROM_INTERNAL); + + udelay(20); + } + + /* Step 05 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_EN | + RK3308_DAC_R_HPMIX_EN, + RK3308_DAC_L_HPMIX_EN | + RK3308_DAC_R_HPMIX_EN); + + /* Waiting the stable HPMIX */ + mdelay(1); + + /* Step 06. Reset HPMIX and recover HPMIX gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK, + RK3308_DAC_L_HPMIX_INIT | + RK3308_DAC_R_HPMIX_INIT); + udelay(50); + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK); + + udelay(20); + + if (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 07 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN); + + udelay(20); + } + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 08 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN); + + udelay(20); + + /* Step 09 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK); + + udelay(20); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* Step 10 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL | + RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL); + + udelay(20); + } + + /* Step 11 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_REF_EN | + RK3308_DAC_R_REF_EN, + RK3308_DAC_L_REF_EN | + RK3308_DAC_R_REF_EN); + + udelay(20); + + /* Step 12 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_CLK_EN | + RK3308_DAC_R_CLK_EN, + RK3308_DAC_L_CLK_EN | + RK3308_DAC_R_CLK_EN); + + udelay(20); + + /* Step 13 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_EN | + RK3308_DAC_R_DAC_EN, + RK3308_DAC_L_DAC_EN | + RK3308_DAC_R_DAC_EN); + + udelay(20); + + /* Step 14 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_WORK | + RK3308_DAC_R_DAC_WORK, + RK3308_DAC_L_DAC_WORK | + RK3308_DAC_R_DAC_WORK); + + udelay(20); + + /* Step 15 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_SEL_MSK | + RK3308_DAC_R_HPMIX_SEL_MSK, + RK3308_DAC_L_HPMIX_I2S | + RK3308_DAC_R_HPMIX_I2S); + + udelay(20); + + /* Step 16 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_UNMUTE | + RK3308_DAC_R_HPMIX_UNMUTE, + RK3308_DAC_L_HPMIX_UNMUTE | + RK3308_DAC_R_HPMIX_UNMUTE); + + udelay(20); + + /* Step 17: Put configuration HPMIX Gain to DAPM */ + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 18 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE); + + udelay(20); + } + + if (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 19 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE); + udelay(20); + } + + /* Step 20, put configuration HPOUT gain to DAPM control */ + /* Step 21, put configuration LINEOUT gain to DAPM control */ + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Just for HPOUT */ + rk3308_codec_digital_fadein(rk3308); + } + + rk3308->dac_endisable = true; + + /* TODO: TRY TO TEST DRIVE STRENGTH */ + + return 0; +} + +static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308) +{ + /* + * Step 00 skipped. Keep the DAC channel work and input the mute signal. + */ + + /* Step 01 skipped. May set the min gain for LINEOUT. */ + + /* Step 02 skipped. May set the min gain for HPOUT. */ + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Just for HPOUT */ + rk3308_codec_digital_fadeout(rk3308); + } + + /* Step 03 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_UNMUTE | + RK3308_DAC_R_HPMIX_UNMUTE, + RK3308_DAC_L_HPMIX_UNMUTE | + RK3308_DAC_R_HPMIX_UNMUTE); + + /* Step 04 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_SEL_MSK | + RK3308_DAC_R_HPMIX_SEL_MSK, + RK3308_DAC_L_HPMIX_NONE | + RK3308_DAC_R_HPMIX_NONE); + /* Step 05 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE, + RK3308_DAC_L_HPOUT_MUTE | + RK3308_DAC_R_HPOUT_MUTE); + + /* Step 06 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_WORK | + RK3308_DAC_R_DAC_WORK, + RK3308_DAC_L_DAC_INIT | + RK3308_DAC_R_DAC_INIT); + + /* Step 07 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN, + RK3308_DAC_L_HPOUT_DIS | + RK3308_DAC_R_HPOUT_DIS); + + /* Step 08 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE, + RK3308_DAC_L_LINEOUT_MUTE | + RK3308_DAC_R_LINEOUT_MUTE); + + /* Step 09 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN, + RK3308_DAC_L_LINEOUT_DIS | + RK3308_DAC_R_LINEOUT_DIS); + + /* Step 10 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_EN | + RK3308_DAC_R_HPMIX_EN, + RK3308_DAC_L_HPMIX_DIS | + RK3308_DAC_R_HPMIX_DIS); + + /* Step 11 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_EN | + RK3308_DAC_R_DAC_EN, + RK3308_DAC_L_DAC_DIS | + RK3308_DAC_R_DAC_DIS); + + /* Step 12 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_CLK_EN | + RK3308_DAC_R_CLK_EN, + RK3308_DAC_L_CLK_DIS | + RK3308_DAC_R_CLK_DIS); + + /* Step 13 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_REF_EN | + RK3308_DAC_R_REF_EN, + RK3308_DAC_L_REF_DIS | + RK3308_DAC_R_REF_DIS); + + /* Step 14 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_INIT | + RK3308_DAC_HPOUT_POP_SOUND_R_INIT); + + /* Step 15 */ + if (rk3308->codec_ver == ACODEC_VERSION_B && + (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT)) { + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_DC_FROM_VCM | + RK3308_DAC_R_SEL_DC_FROM_VCM); + } + + /* Step 16 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_BUF_REF_L_EN | + RK3308_DAC_BUF_REF_R_EN, + RK3308_DAC_BUF_REF_L_DIS | + RK3308_DAC_BUF_REF_R_DIS); + + /* Step 17 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, + RK3308_DAC_CURRENT_EN, + RK3308_DAC_CURRENT_DIS); + + /* Step 18 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK, + RK3308_DAC_L_HPOUT_INIT | + RK3308_DAC_R_HPOUT_INIT); + + /* Step 19 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK); + + /* Step 20 skipped, may set the min gain for HPOUT. */ + + /* + * Note2. If the ACODEC_DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] + * is set to 0x1, add the steps from the section Disable ADC + * Configuration Standard Usage Flow after complete the step 19 + * + * IF USING LINE-IN + * rk3308_codec_adc_ana_disable(rk3308, type); + */ + + rk3308->dac_endisable = false; + + return 0; +} + +static int rk3308_codec_power_on(struct rk3308_codec_priv *rk3308) +{ + unsigned int v; + + /* 0. Supply the power of digital part and reset the Audio Codec */ + /* Do nothing */ + + /* + * 1. Configure ACODEC_DAC_ANA_CON1[1:0] and ACODEC_DAC_ANA_CON1[5:4] + * to 0x1, to setup dc voltage of the DAC channel output. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_INIT); + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_R_INIT); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 2. Configure ACODEC_DAC_ANA_CON15[1:0] and + * ACODEC_DAC_ANA_CON15[5:4] to 0x1, to setup dc voltage of + * the DAC channel output. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK, + RK3308_DAC_L_SEL_DC_FROM_VCM); + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_R_SEL_DC_FROM_VCM); + } + + /* + * 3. Configure the register ACODEC_ADC_ANA_CON10[3:0] to 7’b000_0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + RK3308_ADC_SEL_I(0x1)); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 4. Configure the register ACODEC_ADC_ANA_CON14[3:0] to + * 4’b0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, + RK3308_DAC_SEL_I(0x1)); + } + + /* 5. Supply the power of the analog part(AVDD,AVDDRV) */ + + /* + * 6. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x1 to setup + * reference voltage + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_REF_EN, RK3308_ADC_REF_EN); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 7. Configure the register ACODEC_ADC_ANA_CON14[4] to 0x1 to + * setup reference voltage + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_VCM_LINEOUT_EN, + RK3308_DAC_VCM_LINEOUT_EN); + } + + /* + * 8. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to + * 0x7f step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to + * 0x7f directly. Here the slot time of the step is 200us. + */ + for (v = 0x1; v <= 0x7f; v++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + v); + udelay(200); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 9. Change the register ACODEC_ADC_ANA_CON14[3:0] from the 0x1 + * to 0xf step by step or configure the + * ACODEC_ADC_ANA_CON14[3:0] to 0xf directly. Here the slot + * time of the step is 200us. + */ + for (v = 0x1; v <= 0xf; v++) { + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, + v); + udelay(200); + } + } + + /* 10. Wait until the voltage of VCM keeps stable at the AVDD/2 */ + msleep(20); /* estimated value */ + + /* + * 11. Configure the register ACODEC_ADC_ANA_CON10[6:0] to the + * appropriate value(expect 0x0) for reducing power. + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, 0x7c); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 12. Configure the register ACODEC_DAC_ANA_CON14[6:0] to the + * appropriate value(expect 0x0) for reducing power. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, 0xf); + } + + return 0; +} + +static int rk3308_codec_power_off(struct rk3308_codec_priv *rk3308) +{ + unsigned int v; + + /* + * 0. Keep the power on and disable the DAC and ADC path according to + * the section power on configuration standard usage flow. + */ + + /* + * 1. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 7’b000_0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + RK3308_ADC_SEL_I(0x1)); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 2. Configure the register ACODEC_DAC_ANA_CON14[3:0] to + * 4’b0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, + RK3308_DAC_SEL_I(0x1)); + } + + /* 3. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x0 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_REF_EN, + RK3308_ADC_REF_DIS); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* 4. Configure the register ACODEC_DAC_ANA_CON14[7] to 0x0 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_VCM_LINEOUT_EN, + RK3308_DAC_VCM_LINEOUT_DIS); + } + + /* + * 5. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to 0x7f + * step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to 0x7f + * directly. Here the slot time of the step is 200us. + */ + for (v = 0x1; v <= 0x7f; v++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + v); + udelay(200); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 6. Change the register ACODEC_DAC_ANA_CON14[3:0] from the 0x1 + * to 0xf step by step or configure the + * ACODEC_DAC_ANA_CON14[3:0] to 0xf directly. Here the slot + * time of the step is 200us. + */ + for (v = 0x1; v <= 0x7f; v++) { + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + v); + udelay(200); + } + } + + /* 7. Wait until the voltage of VCM keeps stable at the AGND */ + msleep(20); /* estimated value */ + + /* 8. Power off the analog power supply */ + /* 9. Power off the digital power supply */ + + /* Do something via hardware */ + + return 0; +} + +static int rk3308_codec_headset_detect_enable(struct rk3308_codec_priv *rk3308) +{ + /* + * Set ACODEC_DAC_ANA_CON0[1] to 0x1, to enable the headset insert + * detection + * + * Note. When the voltage of PAD HPDET> 8*AVDD/9, the output value of + * the pin_hpdet will be set to 0x1 and assert a interrupt + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, + RK3308_DAC_HEADPHONE_DET_MSK, + RK3308_DAC_HEADPHONE_DET_EN); + + return 0; +} + +static int rk3308_codec_headset_detect_disable(struct rk3308_codec_priv *rk3308) +{ + /* + * Set ACODEC_DAC_ANA_CON0[1] to 0x0, to disable the headset insert + * detection + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, + RK3308_DAC_HEADPHONE_DET_MSK, + RK3308_DAC_HEADPHONE_DET_DIS); + + return 0; +} + +static int rk3308_codec_check_i2s_sdis(struct rk3308_codec_priv *rk3308, + int num) +{ + int i, j, ret = 0; + + switch (num) { + case 1: + rk3308->which_i2s = ACODEC_TO_I2S1_2CH; + break; + case 2: + rk3308->which_i2s = ACODEC_TO_I2S3_4CH; + break; + case 4: + rk3308->which_i2s = ACODEC_TO_I2S2_8CH; + break; + default: + dev_err(rk3308->plat_dev, "Invalid i2s sdis num: %d\n", num); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < num; i++) { + if (rk3308->i2s_sdis[i] > ADC_LR_GROUP_MAX - 1) { + dev_err(rk3308->plat_dev, + "i2s_sdis[%d]: %d is overflow\n", + i, rk3308->i2s_sdis[i]); + ret = -EINVAL; + goto err; + } + + for (j = 0; j < num; j++) { + if (i == j) + continue; + + if (rk3308->i2s_sdis[i] == rk3308->i2s_sdis[j]) { + dev_err(rk3308->plat_dev, + "Invalid i2s_sdis: [%d]%d == [%d]%d\n", + i, rk3308->i2s_sdis[i], + j, rk3308->i2s_sdis[j]); + ret = -EINVAL; + goto err; + } + } + } + +err: + return ret; +} + +static int rk3308_codec_adc_grps_route_config(struct rk3308_codec_priv *rk3308) +{ + int idx = 0; + + if (rk3308->which_i2s == ACODEC_TO_I2S2_8CH) { + for (idx = 0; idx < rk3308->to_i2s_grps; idx++) { + regmap_write(rk3308->grf, GRF_SOC_CON1, + GRF_I2S2_8CH_SDI(idx, rk3308->i2s_sdis[idx])); + } + } else if (rk3308->which_i2s == ACODEC_TO_I2S3_4CH) { + for (idx = 0; idx < rk3308->to_i2s_grps; idx++) { + regmap_write(rk3308->grf, GRF_SOC_CON1, + GRF_I2S3_4CH_SDI(idx, rk3308->i2s_sdis[idx])); + } + } else if (rk3308->which_i2s == ACODEC_TO_I2S1_2CH) { + regmap_write(rk3308->grf, GRF_SOC_CON1, + GRF_I2S1_2CH_SDI(rk3308->i2s_sdis[idx])); + } + + return 0; +} + +/* Put default one-to-one mapping */ +static int rk3308_codec_adc_grps_route_default(struct rk3308_codec_priv *rk3308) +{ + unsigned int idx; + + /* + * The GRF values may be kept the previous status after hot reboot, + * if the property 'rockchip,adc-grps-route' is not set, we need to + * recover default the order of sdi/sdo for i2s2_8ch/i2s3_8ch/i2s1_2ch. + */ + regmap_write(rk3308->grf, GRF_SOC_CON1, + GRF_I2S1_2CH_SDI(0)); + + for (idx = 0; idx < 2; idx++) { + regmap_write(rk3308->grf, GRF_SOC_CON1, + GRF_I2S3_4CH_SDI(idx, idx)); + } + + /* Using i2s2_8ch by default. */ + rk3308->which_i2s = ACODEC_TO_I2S2_8CH; + rk3308->to_i2s_grps = ADC_LR_GROUP_MAX; + + for (idx = 0; idx < ADC_LR_GROUP_MAX; idx++) { + rk3308->i2s_sdis[idx] = idx; + regmap_write(rk3308->grf, GRF_SOC_CON1, + GRF_I2S2_8CH_SDI(idx, idx)); + } + + return 0; +} + +static int rk3308_codec_adc_grps_route(struct rk3308_codec_priv *rk3308, + struct device_node *np) +{ + int num, ret; + + num = of_count_phandle_with_args(np, "rockchip,adc-grps-route", NULL); + if (num < 0) { + if (num == -ENOENT) { + /* Not use 'rockchip,adc-grps-route' property here */ + rk3308_codec_adc_grps_route_default(rk3308); + ret = 0; + } else { + dev_err(rk3308->plat_dev, + "Failed to read 'rockchip,adc-grps-route' num: %d\n", + num); + ret = num; + } + return ret; + } + + ret = of_property_read_u32_array(np, "rockchip,adc-grps-route", + rk3308->i2s_sdis, num); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to read 'rockchip,adc-grps-route': %d\n", + ret); + return ret; + } + + ret = rk3308_codec_check_i2s_sdis(rk3308, num); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to check i2s_sdis: %d\n", ret); + return ret; + } + + rk3308->to_i2s_grps = num; + + rk3308_codec_adc_grps_route_config(rk3308); + + return 0; +} + +static int check_micbias(int micbias) +{ + switch (micbias) { + case RK3308_ADC_MICBIAS_VOLT_0_85: + case RK3308_ADC_MICBIAS_VOLT_0_8: + case RK3308_ADC_MICBIAS_VOLT_0_75: + case RK3308_ADC_MICBIAS_VOLT_0_7: + case RK3308_ADC_MICBIAS_VOLT_0_65: + case RK3308_ADC_MICBIAS_VOLT_0_6: + case RK3308_ADC_MICBIAS_VOLT_0_55: + case RK3308_ADC_MICBIAS_VOLT_0_5: + return 0; + } + + return -EINVAL; +} + +static bool handle_loopback(struct rk3308_codec_priv *rk3308) +{ + /* The version B doesn't need to handle loopback. */ + if (rk3308->codec_ver == ACODEC_VERSION_B) + return false; + + switch (rk3308->loopback_grp) { + case 0: + case 1: + case 2: + case 3: + return true; + } + + return false; +} + +static bool has_en_always_grps(struct rk3308_codec_priv *rk3308) +{ + int idx; + + if (rk3308->en_always_grps_num) { + for (idx = 0; idx < ADC_LR_GROUP_MAX; idx++) { + if (rk3308->en_always_grps[idx] >= 0 && + rk3308->en_always_grps[idx] <= ADC_LR_GROUP_MAX - 1) + return true; + } + } + + return false; +} + +static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, + int micbias) +{ + int ret; + + if (rk3308->ext_micbias != EXT_MICBIAS_NONE) + return 0; + + /* 0. Power up the ACODEC and keep the AVDDH stable */ + + /* Step 1. Configure ACODEC_ADC_ANA_CON7[2:0] to the certain value */ + ret = check_micbias(micbias); + if (ret < 0) { + dev_err(rk3308->plat_dev, "This is an invalid micbias: %d\n", + micbias); + return ret; + } + + /* + * Note: Only the reg (ADC_ANA_CON7+0x0)[2:0] represent the level range + * control signal of MICBIAS voltage + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), + RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, + micbias); + + /* Step 2. Wait until the VCMH keep stable */ + msleep(20); /* estimated value */ + + /* + * Step 3. Configure ACODEC_ADC_ANA_CON8[4] to 0x1 + * + * Note: Only the reg (ADC_ANA_CON8+0x0)[4] represent the enable + * signal of current source for MICBIAS + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), + RK3308_ADC_MICBIAS_CURRENT_MSK, + RK3308_ADC_MICBIAS_CURRENT_EN); + + /* + * Step 4. Configure the (ADC_ANA_CON7+0x40)[3] or + * (ADC_ANA_CON7+0x80)[3] to 0x1. + * + * (ADC_ANA_CON7+0x40)[3] used to control the MICBIAS1, and + * (ADC_ANA_CON7+0x80)[3] used to control the MICBIAS2 + */ + if (rk3308->micbias1) + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(1), + RK3308_ADC_MIC_BIAS_BUF_EN, + RK3308_ADC_MIC_BIAS_BUF_EN); + + if (rk3308->micbias2) + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(2), + RK3308_ADC_MIC_BIAS_BUF_EN, + RK3308_ADC_MIC_BIAS_BUF_EN); + + /* waiting micbias stabled*/ + mdelay(50); + + rk3308->enable_micbias = true; + + return 0; +} + +static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308) +{ + if (rk3308->ext_micbias != EXT_MICBIAS_NONE) + return 0; + + /* Step 0. Enable the MICBIAS and keep the Audio Codec stable */ + /* Do nothing */ + + /* + * Step 1. Configure the (ADC_ANA_CON7+0x40)[3] or + * (ADC_ANA_CON7+0x80)[3] to 0x0 + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(1), + RK3308_ADC_MIC_BIAS_BUF_EN, + RK3308_ADC_MIC_BIAS_BUF_DIS); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(2), + RK3308_ADC_MIC_BIAS_BUF_EN, + RK3308_ADC_MIC_BIAS_BUF_DIS); + + /* + * Step 2. Configure ACODEC_ADC_ANA_CON8[4] to 0x0 + * + * Note: Only the reg (ADC_ANA_CON8+0x0)[4] represent the enable + * signal of current source for MICBIAS + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), + RK3308_ADC_MICBIAS_CURRENT_MSK, + RK3308_ADC_MICBIAS_CURRENT_DIS); + + rk3308->enable_micbias = false; + + return 0; +} + +static int rk3308_codec_adc_reinit_mics(struct rk3308_codec_priv *rk3308, + int type) +{ + int idx, grp; + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 1 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK, + RK3308_ADC_CH1_ADC_INIT | + RK3308_ADC_CH2_ADC_INIT); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 2 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK, + RK3308_ADC_CH1_ALC_INIT | + RK3308_ADC_CH2_ALC_INIT); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 3 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK, + RK3308_ADC_CH1_MIC_INIT | + RK3308_ADC_CH2_MIC_INIT); + } + + usleep_range(200, 250); /* estimated value */ + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 1 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK, + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 2 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK, + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 3 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK, + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK); + } + + return 0; +} + +static int rk3308_codec_adc_ana_enable(struct rk3308_codec_priv *rk3308, + int type) +{ + unsigned int agc_func_en; + int idx, grp; + + /* + * 1. Set the ACODEC_ADC_ANA_CON7[7:6] and ACODEC_ADC_ANA_CON7[5:4], + * to select the line-in or microphone as input of ADC + * + * Note1. Please ignore the step1 for enabling ADC3, ADC4, ADC5, + * ADC6, ADC7, and ADC8 + */ + if (rk3308->adc_grp0_using_linein) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), + RK3308_ADC_CH1_IN_SEL_MSK | + RK3308_ADC_CH2_IN_SEL_MSK, + RK3308_ADC_CH1_IN_LINEIN | + RK3308_ADC_CH2_IN_LINEIN); + + /* Keep other ADCs as MIC-IN */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + /* The groups without line-in are >= 1 */ + if (grp < 1 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON07(grp), + RK3308_ADC_CH1_IN_SEL_MSK | + RK3308_ADC_CH2_IN_SEL_MSK, + RK3308_ADC_CH1_IN_MIC | + RK3308_ADC_CH2_IN_MIC); + } + } else { + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON07(grp), + RK3308_ADC_CH1_IN_SEL_MSK | + RK3308_ADC_CH2_IN_SEL_MSK, + RK3308_ADC_CH1_IN_MIC | + RK3308_ADC_CH2_IN_MIC); + } + } + + /* + * 2. Set ACODEC_ADC_ANA_CON0[7] and [3] to 0x1, to end the mute station + * of ADC, to enable the MIC module, to enable the reference voltage + * buffer, and to end the initialization of MIC + */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_UNMUTE | + RK3308_ADC_CH2_MIC_UNMUTE, + RK3308_ADC_CH1_MIC_UNMUTE | + RK3308_ADC_CH2_MIC_UNMUTE); + } + + /* + * 3. Set ACODEC_ADC_ANA_CON6[0] to 0x1, to enable the current source + * of audio + */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(grp), + RK3308_ADC_CURRENT_MSK, + RK3308_ADC_CURRENT_EN); + } + + /* + * This is mainly used for BIST mode that wait ADCs are stable. + * + * By tested results, the type delay is >40us, but we need to leave + * enough delay margin. + */ + usleep_range(400, 500); + + /* vendor step 4*/ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_BUF_REF_EN | + RK3308_ADC_CH2_BUF_REF_EN, + RK3308_ADC_CH1_BUF_REF_EN | + RK3308_ADC_CH2_BUF_REF_EN); + } + + /* vendor step 5 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_EN | + RK3308_ADC_CH2_MIC_EN, + RK3308_ADC_CH1_MIC_EN | + RK3308_ADC_CH2_MIC_EN); + } + + /* vendor step 6 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_EN | + RK3308_ADC_CH2_ALC_EN, + RK3308_ADC_CH1_ALC_EN | + RK3308_ADC_CH2_ALC_EN); + } + + /* vendor step 7 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_CLK_EN | + RK3308_ADC_CH2_CLK_EN, + RK3308_ADC_CH1_CLK_EN | + RK3308_ADC_CH2_CLK_EN); + } + + /* vendor step 8 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_EN | + RK3308_ADC_CH2_ADC_EN, + RK3308_ADC_CH1_ADC_EN | + RK3308_ADC_CH2_ADC_EN); + } + + /* vendor step 9 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK, + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK); + } + + /* vendor step 10 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK, + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK); + } + + /* vendor step 11 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK, + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK); + } + + /* vendor step 12 */ + + /* vendor step 13 */ + + /* vendor step 14 */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), + &agc_func_en); + if (rk3308->adc_zerocross || + agc_func_en & RK3308_AGC_FUNC_SEL_EN) { + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ZEROCROSS_DET_EN, + RK3308_ADC_CH1_ZEROCROSS_DET_EN); + } + regmap_read(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), + &agc_func_en); + if (rk3308->adc_zerocross || + agc_func_en & RK3308_AGC_FUNC_SEL_EN) { + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH2_ZEROCROSS_DET_EN, + RK3308_ADC_CH2_ZEROCROSS_DET_EN); + } + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + rk3308->adc_grps_endisable[grp] = true; + } + + return 0; +} + +static int rk3308_codec_adc_ana_disable(struct rk3308_codec_priv *rk3308, + int type) +{ + int idx, grp; + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 1 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ZEROCROSS_DET_EN | + RK3308_ADC_CH2_ZEROCROSS_DET_EN, + RK3308_ADC_CH1_ZEROCROSS_DET_DIS | + RK3308_ADC_CH2_ZEROCROSS_DET_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 2 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_EN | + RK3308_ADC_CH2_ADC_EN, + RK3308_ADC_CH1_ADC_DIS | + RK3308_ADC_CH2_ADC_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 3 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_CLK_EN | + RK3308_ADC_CH2_CLK_EN, + RK3308_ADC_CH1_CLK_DIS | + RK3308_ADC_CH2_CLK_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 4 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_EN | + RK3308_ADC_CH2_ALC_EN, + RK3308_ADC_CH1_ALC_DIS | + RK3308_ADC_CH2_ALC_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 5 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_EN | + RK3308_ADC_CH2_MIC_EN, + RK3308_ADC_CH1_MIC_DIS | + RK3308_ADC_CH2_MIC_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 6 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_BUF_REF_EN | + RK3308_ADC_CH2_BUF_REF_EN, + RK3308_ADC_CH1_BUF_REF_DIS | + RK3308_ADC_CH2_BUF_REF_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 7 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(grp), + RK3308_ADC_CURRENT_MSK, + RK3308_ADC_CURRENT_DIS); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 8 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK, + RK3308_ADC_CH1_ADC_INIT | + RK3308_ADC_CH2_ADC_INIT); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 9 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK, + RK3308_ADC_CH1_ALC_INIT | + RK3308_ADC_CH2_ALC_INIT); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + /* vendor step 10 */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK, + RK3308_ADC_CH1_MIC_INIT | + RK3308_ADC_CH2_MIC_INIT); + } + + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + rk3308->adc_grps_endisable[grp] = false; + } + + return 0; +} + +static int rk3308_codec_open_capture(struct rk3308_codec_priv *rk3308) +{ + int idx, grp = 0; + int type = ADC_TYPE_NORMAL; + + rk3308_codec_adc_ana_enable(rk3308, type); + rk3308_codec_adc_reinit_mics(rk3308, type); + + if (rk3308->adc_grp0_using_linein) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(0), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_NORMAL_RIGHT); + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(0), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_NORMAL_LEFT); + } else { + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (handle_loopback(rk3308) && + idx == rk3308->loopback_grp && + grp == ADC_GRP_SKIP_MAGIC) { + /* + * Switch to dummy BIST mode (BIST keep reset + * now) to keep the zero input data in I2S bus. + * + * It may cause the glitch if we hold the ADC + * digtital i2s module in codec. + * + * Then, the grp which is set from loopback_grp. + */ + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(rk3308->loopback_grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_BIST_SINE); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(rk3308->loopback_grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_BIST_SINE); + } else { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_NORMAL_LEFT); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_NORMAL_RIGHT); + } + } + } + + return 0; +} + +static void rk3308_codec_adc_mclk_disable(struct rk3308_codec_priv *rk3308) +{ + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_ADC_MCLK_MSK, + RK3308_ADC_MCLK_DIS); +} + +static void rk3308_codec_adc_mclk_enable(struct rk3308_codec_priv *rk3308) +{ + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_ADC_MCLK_MSK, + RK3308_ADC_MCLK_EN); + udelay(20); +} + +static void rk3308_codec_dac_mclk_disable(struct rk3308_codec_priv *rk3308) +{ + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_DAC_MCLK_MSK, + RK3308_DAC_MCLK_DIS); +} + +static void rk3308_codec_dac_mclk_enable(struct rk3308_codec_priv *rk3308) +{ + regmap_update_bits(rk3308->regmap, RK3308_GLB_CON, + RK3308_DAC_MCLK_MSK, + RK3308_DAC_MCLK_EN); + udelay(20); +} + +static int rk3308_codec_open_dbg_capture(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_ana_enable(rk3308, ADC_TYPE_DBG); + + return 0; +} + +static int rk3308_codec_close_dbg_capture(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_DBG); + + return 0; +} + +static int rk3308_codec_close_all_capture(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_ALL); + + return 0; +} + +static int rk3308_codec_close_capture(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_NORMAL); + + return 0; +} + +static int rk3308_codec_open_playback(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_dac_enable(rk3308); + + return 0; +} + +static int rk3308_codec_close_playback(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_dac_disable(rk3308); + + return 0; +} + +static int rk3308_codec_llp_down(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_mclk_disable(rk3308); + rk3308_codec_dac_mclk_disable(rk3308); + + return 0; +} + +static int rk3308_codec_llp_up(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_mclk_enable(rk3308); + rk3308_codec_dac_mclk_enable(rk3308); + + return 0; +} + +static int rk3308_codec_dlp_down(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_micbias_disable(rk3308); + rk3308_codec_power_off(rk3308); + + return 0; +} + +static int rk3308_codec_dlp_up(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_power_on(rk3308); + rk3308_codec_micbias_enable(rk3308, rk3308->micbias_volt); + + return 0; +} + +/* Just used for debug and trace power state */ +static void rk3308_codec_set_pm_state(struct rk3308_codec_priv *rk3308, + int pm_state) +{ + int ret; + + switch (pm_state) { + case PM_LLP_DOWN: + rk3308_codec_llp_down(rk3308); + break; + case PM_LLP_UP: + rk3308_codec_llp_up(rk3308); + break; + case PM_DLP_DOWN: + rk3308_codec_dlp_down(rk3308); + break; + case PM_DLP_UP: + rk3308_codec_dlp_up(rk3308); + break; + case PM_DLP_DOWN2: + clk_disable_unprepare(rk3308->mclk_rx); + clk_disable_unprepare(rk3308->mclk_tx); + clk_disable_unprepare(rk3308->pclk); + break; + case PM_DLP_UP2: + ret = clk_prepare_enable(rk3308->pclk); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to enable acodec pclk: %d\n", ret); + goto err; + } + + ret = clk_prepare_enable(rk3308->mclk_rx); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to enable i2s mclk_rx: %d\n", ret); + goto err; + } + + ret = clk_prepare_enable(rk3308->mclk_tx); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to enable i2s mclk_tx: %d\n", ret); + goto err; + } + break; + default: + dev_err(rk3308->plat_dev, "Invalid pm_state: %d\n", pm_state); + goto err; + } + + rk3308->pm_state = pm_state; + +err: + return; +} + +static void rk3308_codec_update_adcs_status(struct rk3308_codec_priv *rk3308, + int state) +{ + int idx, grp; + + /* Update skip_grps flags if the ADCs need to be enabled always. */ + if (state == PATH_BUSY) { + for (idx = 0; idx < rk3308->used_adc_grps; idx++) { + u32 mapped_grp = to_mapped_grp(rk3308, idx); + + for (grp = 0; grp < rk3308->en_always_grps_num; grp++) { + u32 en_always_grp = rk3308->en_always_grps[grp]; + + if (mapped_grp == en_always_grp) + rk3308->skip_grps[en_always_grp] = 1; + } + } + } +} + +static int rk3308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct snd_pcm_str *playback_str = + &substream->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]; + int type = ADC_TYPE_LOOPBACK; + int idx, grp; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* DAC only supports 2 channels */ + rk3308_codec_dac_mclk_enable(rk3308); + rk3308_codec_open_playback(rk3308); + rk3308_codec_dac_dig_config(rk3308, params); + rk3308_codec_set_dac_path_state(rk3308, PATH_BUSY); + } else { + if (rk3308->micbias_num && + !rk3308->enable_micbias) + rk3308_codec_micbias_enable(rk3308, rk3308->micbias_volt); + + rk3308_codec_adc_mclk_enable(rk3308); + ret = rk3308_codec_update_adc_grps(rk3308, params); + if (ret < 0) + return ret; + + if (handle_loopback(rk3308)) { + if (rk3308->micbias_num && + (params_channels(params) == 2) && + to_mapped_grp(rk3308, 0) == rk3308->loopback_grp) + rk3308_codec_micbias_disable(rk3308); + + /* Check the DACs are opened */ + if (playback_str->substream_opened) { + rk3308->loopback_dacs_enabled = true; + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_NORMAL_LEFT); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_NORMAL_RIGHT); + } + } else { + rk3308->loopback_dacs_enabled = false; + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_BIST_SINE); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_BIST_SINE); + } + } + } + + rk3308_codec_open_capture(rk3308); + rk3308_codec_adc_dig_config(rk3308, params); + rk3308_codec_update_adcs_status(rk3308, PATH_BUSY); + } + + return 0; +} + +static int rk3308_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + int type = ADC_TYPE_LOOPBACK; + int idx, grp; + + if (handle_loopback(rk3308) && + rk3308->dac_output == DAC_LINEOUT && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (cmd == SNDRV_PCM_TRIGGER_START) { + struct snd_pcm_str *capture_str = + &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + + if (capture_str->substream_opened) + queue_delayed_work(system_power_efficient_wq, + &rk3308->loopback_work, + msecs_to_jiffies(rk3308->delay_loopback_handle_ms)); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + /* + * Switch to dummy bist mode to kick the glitch during disable + * ADCs and keep zero input data + */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_BIST_SINE); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_BIST_SINE); + } + rk3308_codec_adc_ana_disable(rk3308, ADC_TYPE_LOOPBACK); + } + } + + return 0; +} + +static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rk3308_codec_close_playback(rk3308); + rk3308_codec_dac_mclk_disable(rk3308); + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); + rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); + } else { + rk3308_codec_close_capture(rk3308); + if (!has_en_always_grps(rk3308)) { + rk3308_codec_adc_mclk_disable(rk3308); + rk3308_codec_update_adcs_status(rk3308, PATH_IDLE); + if (rk3308->micbias_num && + rk3308->enable_micbias) + rk3308_codec_micbias_disable(rk3308); + } + + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); + } +} + +static struct snd_soc_dai_ops rk3308_dai_ops = { + .hw_params = rk3308_hw_params, + .set_fmt = rk3308_set_dai_fmt, + .mute_stream = rk3308_mute_stream, + .trigger = rk3308_pcm_trigger, + .shutdown = rk3308_pcm_shutdown, +}; + +static struct snd_soc_dai_driver rk3308_dai[] = { + { + .name = "rk3308-hifi", + .id = RK3308_HIFI, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .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 = 8, + .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 = &rk3308_dai_ops, + }, +}; + +static int rk3308_suspend(struct snd_soc_codec *codec) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + + if (rk3308->no_deep_low_power) + goto out; + + rk3308_codec_dlp_down(rk3308); + clk_disable_unprepare(rk3308->mclk_rx); + clk_disable_unprepare(rk3308->mclk_tx); + clk_disable_unprepare(rk3308->pclk); + +out: + rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int rk3308_resume(struct snd_soc_codec *codec) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (rk3308->no_deep_low_power) + goto out; + + ret = clk_prepare_enable(rk3308->pclk); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to enable acodec pclk: %d\n", ret); + goto out; + } + + ret = clk_prepare_enable(rk3308->mclk_rx); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to enable i2s mclk_rx: %d\n", ret); + goto out; + } + + ret = clk_prepare_enable(rk3308->mclk_tx); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to enable i2s mclk_tx: %d\n", ret); + goto out; + } + + rk3308_codec_dlp_up(rk3308); +out: + rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return ret; +} + +static int rk3308_codec_default_gains(struct rk3308_codec_priv *rk3308) +{ + int grp; + + /* Prepare ADC gains */ + /* vendor step 12, set MIC PGA default gains */ + for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON01(grp), + RK3308_ADC_CH1_MIC_GAIN_MSK | + RK3308_ADC_CH2_MIC_GAIN_MSK, + RK3308_ADC_CH1_MIC_GAIN_0DB | + RK3308_ADC_CH2_MIC_GAIN_0DB); + } + + /* vendor step 13, set ALC default gains */ + for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON03(grp), + RK3308_ADC_CH1_ALC_GAIN_MSK, + RK3308_ADC_CH1_ALC_GAIN_0DB); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON04(grp), + RK3308_ADC_CH2_ALC_GAIN_MSK, + RK3308_ADC_CH2_ALC_GAIN_0DB); + } + + /* Prepare DAC gains */ + /* Step 15, set HPMIX default gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_GAIN_MSK | + RK3308_DAC_R_HPMIX_GAIN_MSK, + RK3308_DAC_L_HPMIX_GAIN_NDB_6 | + RK3308_DAC_R_HPMIX_GAIN_NDB_6); + + /* Step 18, set HPOUT default gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_MSK, + RK3308_DAC_L_HPOUT_GAIN_NDB_39); + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_MSK, + RK3308_DAC_R_HPOUT_GAIN_NDB_39); + + /* Using the same gain to HPOUT LR channels */ + rk3308->hpout_l_dgain = RK3308_DAC_L_HPOUT_GAIN_NDB_39; + + /* Step 19, set LINEOUT default gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_GAIN_MSK | + RK3308_DAC_R_LINEOUT_GAIN_MSK, + RK3308_DAC_L_LINEOUT_GAIN_NDB_6 | + RK3308_DAC_R_LINEOUT_GAIN_NDB_6); + + return 0; +} + +static int rk3308_codec_setup_en_always_adcs(struct rk3308_codec_priv *rk3308, + struct device_node *np) +{ + int num, ret; + + num = of_count_phandle_with_args(np, "rockchip,en-always-grps", NULL); + if (num < 0) { + if (num == -ENOENT) { + /* + * If there is note use 'rockchip,en-always-grps' + * property, return 0 is also right. + */ + ret = 0; + } else { + dev_err(rk3308->plat_dev, + "Failed to read 'rockchip,adc-grps-route' num: %d\n", + num); + ret = num; + } + + rk3308->en_always_grps_num = 0; + return ret; + } + + rk3308->en_always_grps_num = num; + + ret = of_property_read_u32_array(np, "rockchip,en-always-grps", + rk3308->en_always_grps, num); + if (ret < 0) { + dev_err(rk3308->plat_dev, + "Failed to read 'rockchip,en-always-grps': %d\n", + ret); + return ret; + } + + /* Clear all of skip_grps flags. */ + for (num = 0; num < ADC_LR_GROUP_MAX; num++) + rk3308->skip_grps[num] = 0; + + /* The loopback grp should not be enabled always. */ + for (num = 0; num < rk3308->en_always_grps_num; num++) { + if (rk3308->en_always_grps[num] == rk3308->loopback_grp) { + dev_err(rk3308->plat_dev, + "loopback_grp: %d should not be enabled always!\n", + rk3308->loopback_grp); + ret = -EINVAL; + return ret; + } + } + + return 0; +} + +static int rk3308_codec_dapm_mic_gains(struct rk3308_codec_priv *rk3308) +{ + int ret; + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + ret = snd_soc_add_codec_controls(rk3308->codec, + mic_gains_b, + ARRAY_SIZE(mic_gains_b)); + if (ret) { + dev_err(rk3308->plat_dev, + "%s: add mic_gains_b failed: %d\n", + __func__, ret); + return ret; + } + } else { + ret = snd_soc_add_codec_controls(rk3308->codec, + mic_gains_a, + ARRAY_SIZE(mic_gains_a)); + if (ret) { + dev_err(rk3308->plat_dev, + "%s: add mic_gains_a failed: %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static int rk3308_codec_check_micbias(struct rk3308_codec_priv *rk3308, + struct device_node *np) +{ + struct device *dev = (struct device *)rk3308->plat_dev; + int num = 0, ret; + + /* Check internal micbias */ + rk3308->micbias1 = + of_property_read_bool(np, "rockchip,micbias1"); + if (rk3308->micbias1) + num++; + + rk3308->micbias2 = + of_property_read_bool(np, "rockchip,micbias2"); + if (rk3308->micbias2) + num++; + + rk3308->micbias_volt = RK3308_ADC_MICBIAS_VOLT_0_85; /* by default */ + rk3308->micbias_num = num; + + /* Check external micbias */ + rk3308->ext_micbias = EXT_MICBIAS_NONE; + + rk3308->micbias_en_gpio = devm_gpiod_get_optional(dev, + "micbias-en", + GPIOD_IN); + if (!rk3308->micbias_en_gpio) { + dev_info(dev, "Don't need micbias-en gpio\n"); + } else if (IS_ERR(rk3308->micbias_en_gpio)) { + ret = PTR_ERR(rk3308->micbias_en_gpio); + dev_err(dev, "Unable to claim gpio micbias-en\n"); + return ret; + } else if (gpiod_get_value(rk3308->micbias_en_gpio)) { + rk3308->ext_micbias = EXT_MICBIAS_FUNC1; + } + + rk3308->vcc_micbias = devm_regulator_get_optional(dev, + "vmicbias"); + if (IS_ERR(rk3308->vcc_micbias)) { + if (PTR_ERR(rk3308->vcc_micbias) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "no vmicbias regulator found\n"); + } else { + ret = regulator_enable(rk3308->vcc_micbias); + if (ret) { + dev_err(dev, "Can't enable vmicbias: %d\n", ret); + return ret; + } + rk3308->ext_micbias = EXT_MICBIAS_FUNC2; + } + + dev_info(dev, "Check ext_micbias: %d\n", rk3308->ext_micbias); + + return 0; +} + +static int rk3308_codec_dapm_controls_prepare(struct rk3308_codec_priv *rk3308) +{ + int grp; + + for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { + rk3308->hpf_cutoff[grp] = 0; + rk3308->agc_l[grp] = 0; + rk3308->agc_r[grp] = 0; + rk3308->agc_asr_l[grp] = AGC_ASR_96KHZ; + rk3308->agc_asr_r[grp] = AGC_ASR_96KHZ; + } + + rk3308_codec_dapm_mic_gains(rk3308); + + return 0; +} + +static int rk3308_codec_prepare(struct rk3308_codec_priv *rk3308) +{ + /* Clear registers for ADC and DAC */ + rk3308_codec_close_playback(rk3308); + rk3308_codec_close_all_capture(rk3308); + rk3308_codec_default_gains(rk3308); + rk3308_codec_llp_down(rk3308); + rk3308_codec_dapm_controls_prepare(rk3308); + + return 0; +} + +static int rk3308_probe(struct snd_soc_codec *codec) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + int ext_micbias; + + rk3308->codec = codec; + rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); + + rk3308_codec_reset(codec); + rk3308_codec_power_on(rk3308); + + /* From vendor recommend, disable micbias at first. */ + ext_micbias = rk3308->ext_micbias; + rk3308->ext_micbias = EXT_MICBIAS_NONE; + rk3308_codec_micbias_disable(rk3308); + rk3308->ext_micbias = ext_micbias; + + rk3308_codec_prepare(rk3308); + if (!rk3308->no_hp_det) + rk3308_codec_headset_detect_enable(rk3308); + + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); + + return 0; +} - /* 5. Wait until the voltage of VCM keeps stable at the AGND */ - usleep_range(200, 300); /* estimated value */ +static int rk3308_remove(struct snd_soc_codec *codec) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + + rk3308_headphone_ctl(rk3308, 0); + rk3308_speaker_ctl(rk3308, 0); + if (!rk3308->no_hp_det) + rk3308_codec_headset_detect_disable(rk3308); + rk3308_codec_micbias_disable(rk3308); + rk3308_codec_power_off(rk3308); - /* 6. Power off the analog power supply */ - /* 7. Power off the digital power supply */ + rk3308_codec_set_dac_path_state(rk3308, PATH_IDLE); - /* Do something via hardware */ + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); return 0; } -static int check_micbias(int micbias) +static struct snd_soc_codec_driver soc_codec_dev_rk3308 = { + .probe = rk3308_probe, + .remove = rk3308_remove, + .suspend = rk3308_suspend, + .resume = rk3308_resume, + .set_bias_level = rk3308_set_bias_level, + .controls = rk3308_codec_dapm_controls, + .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), +}; + +static const struct reg_default rk3308_codec_reg_defaults[] = { + { RK3308_GLB_CON, 0x07 }, +}; + +static bool rk3308_codec_write_read_reg(struct device *dev, unsigned int reg) { - switch (micbias) { - case RK3308_ADC_MICBIAS_VOLT_0_85: - case RK3308_ADC_MICBIAS_VOLT_0_8: - case RK3308_ADC_MICBIAS_VOLT_0_75: - case RK3308_ADC_MICBIAS_VOLT_0_7: - case RK3308_ADC_MICBIAS_VOLT_0_65: - case RK3308_ADC_MICBIAS_VOLT_0_6: - case RK3308_ADC_MICBIAS_VOLT_0_55: - case RK3308_ADC_MICBIAS_VOLT_0_5: - return 0; - } + /* All registers can be read / write */ + return true; +} - return -EINVAL; +static bool rk3308_codec_volatile_reg(struct device *dev, unsigned int reg) +{ + return true; } -static int rk3308_codec_micbias_enable(struct rk3308_codec_priv *rk3308, - int micbias) +static void rk3308_codec_hpdetect_work(struct work_struct *work) { - int ch = rk3308->adc_ch; - int ret; + struct rk3308_codec_priv *rk3308 = + container_of(work, struct rk3308_codec_priv, hpdet_work.work); + unsigned int val; + int need_poll = 0, need_irq = 0; + int need_report = 0, report_type = 0; + int dac_output = DAC_LINEOUT; + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* Check headphone plugged/unplugged directly. */ + regmap_read(rk3308->detect_grf, + DETECT_GRF_ACODEC_HPDET_STATUS, &val); + regmap_write(rk3308->detect_grf, + DETECT_GRF_ACODEC_HPDET_STATUS_CLR, val); + + if (rk3308->hp_jack_reversed) { + switch (val) { + case 0x0: + case 0x2: + dac_output = DAC_HPOUT; + report_type = SND_JACK_HEADPHONE; + break; + default: + break; + } + } else { + switch (val) { + case 0x1: + dac_output = DAC_HPOUT; + report_type = SND_JACK_HEADPHONE; + break; + default: + /* Includes val == 2 or others. */ + break; + } + } - if (ch != 1 && ch != 2) { - dev_err(rk3308->plat_dev, - "%s: currnet ch: %d, only ch1/2 control MICBIAS1/2\n", - __func__, ch); - return -EINVAL; - } + rk3308_codec_dac_switch(rk3308, dac_output); + if (rk3308->hpdet_jack) + snd_soc_jack_report(rk3308->hpdet_jack, + report_type, + SND_JACK_HEADPHONE); - /* 1. Power up the ACODEC and keep the AVDDH stable */ + enable_irq(rk3308->irq); - /* 2. Configure ACODEC_ADC_ANA_CON7[2:0] to the certain value */ - ret = check_micbias(micbias); - if (ret < 0) { - dev_err(rk3308->plat_dev, "This is an invalid micbias: %d\n", - micbias); - return ret; + return; } - /* - * Note: Only the reg (ADC_ANA_CON7+0x0)[2:0] represent the level range - * control signal of MICBIAS voltage - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), - RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, - micbias); + /* Check headphone unplugged via poll. */ + regmap_read(rk3308->regmap, RK3308_DAC_DIG_CON14, &val); - /* 3. Wait until the VCMH keep stable */ - usleep_range(200, 300); /* estimated value */ + if (rk3308->hp_jack_reversed) { + if (!val) { + rk3308->hp_plugged = true; + report_type = SND_JACK_HEADPHONE; - /* 4. Configure ACODEC_ADC_ANA_CON8[4] to 0x1 */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(ch), - RK3308_ADC_MICBIAS_CURRENT_MSK, - RK3308_ADC_MICBIAS_CURRENT_EN); + need_report = 1; + need_irq = 1; + } else { + if (rk3308->hp_plugged) { + rk3308->hp_plugged = false; + need_report = 1; + } + need_poll = 1; + } + } else { + if (!val) { + rk3308->hp_plugged = false; - /* - * 5. Configure the (ADC_ANA_CON7+0x40)[3] or (ADC_ANA_CON7+0x80)[3] - * to 0x1. - * (ADC_ANA_CON7+0x40)[3] used to control the MICBIAS1, and - * (ADC_ANA_CON7+0x80)[3] used to control the MICBIAS2 - */ + need_report = 1; + need_irq = 1; + } else { + if (!rk3308->hp_plugged) { + rk3308->hp_plugged = true; + report_type = SND_JACK_HEADPHONE; + need_report = 1; + } + need_poll = 1; + } + } - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), - RK3308_ADC_MIC_BIAS_BUF_EN, - RK3308_ADC_MIC_BIAS_BUF_EN); + if (need_poll) + queue_delayed_work(system_power_efficient_wq, + &rk3308->hpdet_work, + msecs_to_jiffies(HPDET_POLL_MS)); - return 0; -} + if (need_report) { + if (report_type) + dac_output = DAC_HPOUT; -static int rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308) -{ - int ch = rk3308->adc_ch; + rk3308_codec_dac_switch(rk3308, dac_output); - if (ch != 1 && ch != 2) { - dev_err(rk3308->plat_dev, - "%s: currnet ch: %d, only ch1/2 control MICBIAS1/2\n", - __func__, ch); - return -EINVAL; + if (rk3308->hpdet_jack) + snd_soc_jack_report(rk3308->hpdet_jack, + report_type, + SND_JACK_HEADPHONE); } - /* 1. Enable the MICBIAS and keep the Audio Codec stable */ - /* Do nothing */ - - /* - * 2. Configure the (ADC_ANA_CON7+0x40)[3] or - * (ADC_ANA_CON7+0x80)[3] to 0x0 - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), - RK3308_ADC_MIC_BIAS_BUF_EN, - RK3308_ADC_MIC_BIAS_BUF_DIS); - - /* 3. Configure ACODEC_ADC_ANA_CON8[4] to 0x0 */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(ch), - RK3308_ADC_MICBIAS_CURRENT_MSK, - RK3308_ADC_MICBIAS_CURRENT_DIS); + if (need_irq) + enable_irq(rk3308->irq); +} - return 0; +static void rk3308_codec_loopback_work(struct work_struct *work) +{ + struct rk3308_codec_priv *rk3308 = + container_of(work, struct rk3308_codec_priv, loopback_work.work); + int type = ADC_TYPE_LOOPBACK; + int idx, grp; + + /* Prepare loopback ADCs */ + rk3308_codec_adc_ana_enable(rk3308, type); + + /* Waiting ADCs are stable */ + msleep(ADC_STABLE_MS); + + /* Recover normal mode after enable ADCs */ + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) { + if (grp < 0 || grp > ADC_LR_GROUP_MAX - 1) + continue; + + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_NORMAL_LEFT); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_NORMAL_RIGHT); + } } -static int rk3308_codec_alc_enable(struct rk3308_codec_priv *rk3308) +static irqreturn_t rk3308_codec_hpdet_isr(int irq, void *data) { - int ch = rk3308->adc_ch; + struct rk3308_codec_priv *rk3308 = data; /* - * 1. Set he max level and min level of the ALC need to control. - * - * These values are estimated + * For the high level irq trigger, disable irq and avoid a lot of + * repeated irq handlers entry. */ - regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON05(ch), - RK3308_AGC_LO_8BITS_AGC_MIN_MSK, - 0x16); - regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON05(ch), - RK3308_AGC_HI_8BITS_AGC_MIN_MSK, - 0x40); - - regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON05(ch), - RK3308_AGC_LO_8BITS_AGC_MAX_MSK, - 0x26); - regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON05(ch), - RK3308_AGC_HI_8BITS_AGC_MAX_MSK, - 0x40); + disable_irq_nosync(rk3308->irq); + queue_delayed_work(system_power_efficient_wq, + &rk3308->hpdet_work, msecs_to_jiffies(10)); - /* - * 2. Set ACODEC_ALC_DIG_CON4[2:0] according to the sample rate - * - * By default is 44.1KHz for sample. - */ - regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON04(ch), - RK3308_AGC_APPROX_RATE_MSK, - RK3308_AGC_APPROX_RATE_44_1K); - regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON04(ch), - RK3308_AGC_APPROX_RATE_MSK, - RK3308_AGC_APPROX_RATE_44_1K); - - /* 3. Set ACODEC_ALC_DIG_CON9[6] to 0x1, to enable the ALC module */ - regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), - RK3308_AGC_FUNC_SEL_MSK, - RK3308_AGC_FUNC_SEL_EN); - regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(ch), - RK3308_AGC_FUNC_SEL_MSK, - RK3308_AGC_FUNC_SEL_EN); + return IRQ_HANDLED; +} - /* - * 4. Set ACODEC_ADC_ANA_CON11[1:0], (ACODEC_ADC_ANA_CON11+0x40)[1:0], - * (ACODEC_ADC_ANA_CON11+0x80)[1:0] and (ACODEC_ADC_ANA_CON11+0xc0)[1:0] - * to 0x3, to enable the ALC module to control the gain of PGA. - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(ch), - RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK | - RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, - RK3308_ADC_ALCL_CON_GAIN_PGAL_EN | - RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); +void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_codec *codec, + struct snd_soc_jack *hpdet_jack); +EXPORT_SYMBOL_GPL(rk3308_codec_set_jack_detect_cb); - /* - * 5.Observe the current ALC output gain by reading - * ACODEC_ALC_DIG_CON12[4:0] - * - * The default GAIN is 0x0c - */ - regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON12(ch), - RK3308_AGC_GAIN_MSK, - 0x0c); - regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON12(ch), - RK3308_AGC_GAIN_MSK, - 0x0c); +static void rk3308_codec_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hpdet_jack) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - return 0; -} + rk3308->hpdet_jack = hpdet_jack; -static int rk3308_codec_alc_disable(struct rk3308_codec_priv *rk3308) -{ - int ch = rk3308->adc_ch; + /* To detect jack once during startup */ + disable_irq_nosync(rk3308->irq); + queue_delayed_work(system_power_efficient_wq, + &rk3308->hpdet_work, msecs_to_jiffies(10)); - /* - * 1. Set ACODEC_ALC_DIG_CON9[6] to 0x0, to disable the ALC module, - * then the ALC output gain will keep to the last value - */ - regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), - RK3308_AGC_FUNC_SEL_MSK, - RK3308_AGC_FUNC_SEL_DIS); - regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(ch), - RK3308_AGC_FUNC_SEL_MSK, - RK3308_AGC_FUNC_SEL_DIS); + dev_info(rk3308->plat_dev, "%s: Request detect hp jack once\n", + __func__); +} - /* - * 2. Set ACODEC_ADC_ANA_CON11[1:0], (ACODEC_ADC_ANA_CON11+0x40)[1:0], - * (ACODEC_ADC_ANA_CON11+0x80)[1:0] and (ACODEC_ADC_ANA_CON11+0xc0)[1:0] - * to 0x0, to disable the ALC module to control the gain of PGA. - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(ch), - RK3308_ADC_ALCL_CON_GAIN_PGAL_MSK | - RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK, - RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS | - RK3308_ADC_ALCR_CON_GAIN_PGAR_DIS); +static const struct regmap_config rk3308_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = RK3308_DAC_ANA_CON15, + .writeable_reg = rk3308_codec_write_read_reg, + .readable_reg = rk3308_codec_write_read_reg, + .volatile_reg = rk3308_codec_volatile_reg, + .reg_defaults = rk3308_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rk3308_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; - return 0; +static ssize_t pm_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + + return sprintf(buf, "pm_state: %d\n", rk3308->pm_state); } -static int rk3308_codec_adc_ana_enable(struct rk3308_codec_priv *rk3308) +static ssize_t pm_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - unsigned int adc_aif1 = 0, adc_aif2 = 0; - unsigned int agc_func_en; - int ch = rk3308->adc_ch; + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + unsigned long pm_state; + int ret = kstrtoul(buf, 10, &pm_state); - /* - * 1. Set the ACODEC_ADC_ANA_CON7[7:6] and ACODEC_ADC_ANA_CON7[5:4], - * to select the line-in or microphone as input of ADC - * - * Note1. Please ignore the step1 for enabling ADC3, ADC4, ADC5, - * ADC6, ADC7, and ADC8 - */ - if (ch == 0) { - if (rk3308->adc_ch0_using_linein) { - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), - RK3308_ADC_CH1_IN_SEL_MSK, - RK3308_ADC_CH1_IN_LINEIN); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), - RK3308_ADC_CH2_IN_SEL_MSK, - RK3308_ADC_CH2_IN_LINEIN); - } else { - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), - RK3308_ADC_CH1_IN_SEL_MSK, - RK3308_ADC_CH1_IN_MIC); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(ch), - RK3308_ADC_CH2_IN_SEL_MSK, - RK3308_ADC_CH2_IN_MIC); - } + if (ret < 0) { + dev_err(dev, "Invalid pm_state: %ld, ret: %d\n", + pm_state, ret); + return -EINVAL; } - /* - * 2. Set ACODEC_ADC_ANA_CON0[7:0] to 0xff, to end the mute station - * of ADC, to enable the MIC module, to enable the reference voltage - * buffer, and to end the initialization of MIC - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(ch), - RK3308_ADC_CH1_CH2_MIC_ALL_MSK, - RK3308_ADC_CH1_CH2_MIC_ALL); + rk3308_codec_set_pm_state(rk3308, pm_state); - /* - * 3. Set ACODEC_ADC_ANA_CON6[0] to 0x1, to enable the current source - * of audio - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(ch), - RK3308_ADC_CURRENT_MSK, - RK3308_ADC_CURRENT_EN); + dev_info(dev, "Store pm_state: %d\n", rk3308->pm_state); - /* - * 4. Set ACODEC_ADC_ANA_CON2[7:0] to 0x77, to enable the ALC module, - * to enable the zero-crossing detection function, and to end the - * initialization of ALC - * - * Note2. Please set ACODEC_ADC_ANA_CON2[7:0] to 0x33 in step4 - * if the AGC function is closed - */ + return count; +} - adc_aif1 = RK3308_ADC_CH1_ALC_EN | RK3308_ADC_CH1_ALC_WORK; - regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), &agc_func_en); - if (agc_func_en & RK3308_AGC_FUNC_SEL_EN) - adc_aif1 |= RK3308_ADC_CH1_ZEROCROSS_DET_EN; +static ssize_t adc_grps_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + u32 grp; + int type = ADC_TYPE_NORMAL, count = 0; + int idx; + + count += sprintf(buf + count, "current used adc_grps:\n"); + count += sprintf(buf + count, "- normal:"); + for (idx = 0; adc_for_each_grp(rk3308, type, idx, &grp); idx++) + count += sprintf(buf + count, " %d", grp); + count += sprintf(buf + count, "\n"); + count += sprintf(buf + count, "- loopback: %d\n", + rk3308->loopback_grp); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), - RK3308_ADC_CH1_ALC_ZC_MSK, - adc_aif1); + return count; +} - adc_aif2 = RK3308_ADC_CH2_ALC_EN | RK3308_ADC_CH2_ALC_WORK; - regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(ch), &agc_func_en); - if (agc_func_en & RK3308_AGC_FUNC_SEL_EN) - adc_aif2 |= RK3308_ADC_CH2_ZEROCROSS_DET_EN; +static ssize_t adc_grps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + char adc_type; + int grps, ret; + + ret = sscanf(buf, "%c,%d", &adc_type, &grps); + if (ret != 2) { + dev_err(rk3308->plat_dev, "%s sscanf failed: %d\n", + __func__, ret); + return -EFAULT; + } - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), - RK3308_ADC_CH2_ALC_ZC_MSK, - adc_aif2); + if (adc_type == 'n') + rk3308->used_adc_grps = grps; + else if (adc_type == 'l') + rk3308->loopback_grp = grps; - /* - * 5. Set ACODEC_ADC_ANA_CON5[7:0] to 0x77, to enable the clock and - * ADC module, and to end the initialization of ADC - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), - RK3308_ADC_CH1_ADC_CLK_MSK, - RK3308_ADC_CH1_CLK_EN | - RK3308_ADC_CH1_ADC_EN | - RK3308_ADC_CH1_ADC_WORK); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), - RK3308_ADC_CH2_ADC_CLK_MSK, - RK3308_ADC_CH2_CLK_EN | - RK3308_ADC_CH2_ADC_EN | - RK3308_ADC_CH2_ADC_WORK); + return count; +} - /* - * 6. Set ACODEC_ADC_ANA_CON1[5:4] and ACODEC_ADC_ANA_CON1[1:0], - * to select the gain of the MIC - * - * By default is 0db. - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), - RK3308_ADC_CH1_MIC_GAIN_MSK, - RK3308_ADC_CH1_MIC_GAIN_0DB); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), - RK3308_ADC_CH2_MIC_GAIN_MSK, - RK3308_ADC_CH2_MIC_GAIN_0DB); +static ssize_t adc_grps_route_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + char which_i2s[32] = {0}; + int count = 0; + u32 grp; - /* - * 7.Set ACODEC_ADC_ANA_CON3[4:0] and ACODEC_ADC_ANA_CON4[3:0] to - * select the gain of ALC - * - * By default is 0db. - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON03(ch), - RK3308_ADC_CH1_ALC_GAIN_MSK, - RK3308_ADC_CH1_ALC_GAIN_0DB); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON04(ch), - RK3308_ADC_CH2_ALC_GAIN_MSK, - RK3308_ADC_CH2_ALC_GAIN_0DB); + switch (rk3308->which_i2s) { + case ACODEC_TO_I2S1_2CH: + strcpy(which_i2s, "i2s1_2ch"); + break; + case ACODEC_TO_I2S3_4CH: + strcpy(which_i2s, "i2s3_4ch"); + break; + default: + strcpy(which_i2s, "i2s2_8ch"); + break; + } - /* 8.Begin recording */ + count += sprintf(buf + count, "%s from acodec route mapping:\n", + which_i2s); + for (grp = 0; grp < rk3308->to_i2s_grps; grp++) { + count += sprintf(buf + count, "* sdi_%d <-- sdo_%d\n", + grp, rk3308->i2s_sdis[grp]); + } - return 0; + return count; } -static int rk3308_codec_adc_ana_disable(struct rk3308_codec_priv *rk3308) +static ssize_t adc_grps_route_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - int ch = rk3308->adc_ch; + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + int which_i2s, idx, i2s_sdis[ADC_LR_GROUP_MAX]; + int ret; - /* - * 1. Set ACODEC_ADC_ANA_CON2[7:0] to 0x0, to disable the ALC module, - * to disable the zero-crossing detection function, and to begin the - * initialization of ALC - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), - RK3308_ADC_CH1_ALC_ZC_MSK, - 0); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(ch), - RK3308_ADC_CH2_ALC_ZC_MSK, - 0); + ret = sscanf(buf, "%d,%d,%d,%d,%d", &which_i2s, + &i2s_sdis[0], &i2s_sdis[1], &i2s_sdis[2], &i2s_sdis[3]); + if (ret != 5) { + dev_err(rk3308->plat_dev, "%s sscanf failed: %d\n", + __func__, ret); + goto err; + } - /* - * 2. Set ACODEC_ADC_ANA_CON5[7:0] to 0x0, to disable the clock and - * ADC module, and to begin the initialization of ADC - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), - RK3308_ADC_CH1_ADC_CLK_MSK, - 0); - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(ch), - RK3308_ADC_CH2_ADC_CLK_MSK, - 0); + if (which_i2s < ACODEC_TO_I2S2_8CH || + which_i2s > ACODEC_TO_I2S1_2CH) { + dev_err(rk3308->plat_dev, "Invalid i2s type: %d\n", which_i2s); + goto err; + } - /* - * 3. Set ACODEC_ADC_ANA_CON0[7:0] to 0x88, to disable the MIC module, - * to disable the reference voltage buffer, and to begin the - * initialization of MIC - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(ch), - RK3308_ADC_CH1_CH2_MIC_ALL_MSK, - RK3308_ADC_CH1_MIC_UNMUTE | - RK3308_ADC_CH2_MIC_UNMUTE); + rk3308->which_i2s = which_i2s; - /* - * 4. Set ACODEC_ADC_ANA_CON6[0] to 0x0, to disable the current - * source of audio - */ - regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(ch), - RK3308_ADC_CURRENT_MSK, - RK3308_ADC_CURRENT_DIS); + switch (rk3308->which_i2s) { + case ACODEC_TO_I2S1_2CH: + rk3308->to_i2s_grps = 1; + break; + case ACODEC_TO_I2S3_4CH: + rk3308->to_i2s_grps = 2; + break; + default: + rk3308->to_i2s_grps = 4; + break; + } - return 0; + for (idx = 0; idx < rk3308->to_i2s_grps; idx++) + rk3308->i2s_sdis[idx] = i2s_sdis[idx]; + + rk3308_codec_adc_grps_route_config(rk3308); + +err: + return count; } -static int rk3308_codec_open_capture(struct snd_soc_codec *codec) +static ssize_t adc_grp0_in_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - - rk3308_codec_alc_enable(rk3308); - rk3308_codec_adc_ana_enable(rk3308); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); - return 0; + return sprintf(buf, "adc ch0 using: %s\n", + rk3308->adc_grp0_using_linein ? "line in" : "mic in"); } -static int rk3308_codec_close_capture(struct snd_soc_codec *codec) +static ssize_t adc_grp0_in_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + unsigned long using_linein; + int ret = kstrtoul(buf, 10, &using_linein); + + if (ret < 0 || using_linein > 1) { + dev_err(dev, "Invalid input status: %ld, ret: %d\n", + using_linein, ret); + return -EINVAL; + } - rk3308_codec_alc_disable(rk3308); - rk3308_codec_adc_ana_disable(rk3308); + rk3308->adc_grp0_using_linein = using_linein; - return 0; + dev_info(dev, "store using_linein: %d\n", + rk3308->adc_grp0_using_linein); + + return count; } -static int rk3308_codec_open_playback(struct snd_soc_codec *codec) +static ssize_t adc_zerocross_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); - - rk3308_codec_dac_enable(rk3308); - rk3308_speaker_ctl(rk3308, 1); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); - return 0; + return sprintf(buf, "adc zerocross: %s\n", + rk3308->adc_zerocross ? "enabled" : "disabled"); } -static int rk3308_codec_close_playback(struct snd_soc_codec *codec) +static ssize_t adc_zerocross_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + unsigned long zerocross; + int ret = kstrtoul(buf, 10, &zerocross); - rk3308_speaker_ctl(rk3308, 0); - rk3308_codec_dac_disable(rk3308); + if (ret < 0 || zerocross > 1) { + dev_err(dev, "Invalid zerocross: %ld, ret: %d\n", + zerocross, ret); + return -EINVAL; + } - return 0; + rk3308->adc_zerocross = zerocross; + + dev_info(dev, "store adc zerocross: %d\n", rk3308->adc_zerocross); + + return count; } -static int rk3308_pcm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static ssize_t adc_grps_endisable_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct snd_soc_codec *codec = dai->codec; - int ret = 0; + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + int count = 0, i; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ret = rk3308_codec_open_playback(codec); - else - ret = rk3308_codec_open_capture(codec); + count += sprintf(buf + count, "enabled adc grps:"); + for (i = 0; i < ADC_LR_GROUP_MAX; i++) + count += sprintf(buf + count, "%d ", + rk3308->adc_grps_endisable[i]); - return ret; + count += sprintf(buf + count, "\n"); + return count; } -static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static ssize_t adc_grps_endisable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct snd_soc_codec *codec = dai->codec; + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + int grp, endisable, ret; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rk3308_codec_close_playback(codec); - else - rk3308_codec_close_capture(codec); -} + ret = sscanf(buf, "%d,%d", &grp, &endisable); + if (ret != 2) { + dev_err(rk3308->plat_dev, "%s sscanf failed: %d\n", + __func__, ret); + return -EFAULT; + } -static struct snd_soc_dai_ops rk3308_dai_ops = { - .hw_params = rk3308_hw_params, - .set_fmt = rk3308_set_dai_fmt, - .digital_mute = rk3308_digital_mute, - .startup = rk3308_pcm_startup, - .shutdown = rk3308_pcm_shutdown, -}; + rk3308->cur_dbg_grp = grp; -static struct snd_soc_dai_driver rk3308_dai[] = { - { - .name = "rk3308-hifi", - .id = RK3308_HIFI, - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .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 = 8, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), - }, - .ops = &rk3308_dai_ops, - }, -}; + if (endisable) + rk3308_codec_open_dbg_capture(rk3308); + else + rk3308_codec_close_dbg_capture(rk3308); -static int rk3308_suspend(struct snd_soc_codec *codec) -{ - rk3308_set_bias_level(codec, SND_SOC_BIAS_OFF); + dev_info(dev, "ADC grp %d endisable: %d\n", grp, endisable); - return 0; + return count; } -static int rk3308_resume(struct snd_soc_codec *codec) +static ssize_t dac_endisable_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - rk3308_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); - return 0; + return sprintf(buf, "%d\n", rk3308->dac_endisable); } -static int rk3308_probe(struct snd_soc_codec *codec) +static ssize_t dac_endisable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + unsigned long endisable; + int ret = kstrtoul(buf, 10, &endisable); - rk3308_codec_reset(codec); - rk3308_codec_power_on(codec); + if (ret < 0) { + dev_err(dev, "Invalid endisable: %ld, ret: %d\n", + endisable, ret); + return -EINVAL; + } + + if (endisable) + rk3308_codec_open_playback(rk3308); + else + rk3308_codec_close_playback(rk3308); - rk3308_codec_micbias_enable(rk3308, RK3308_ADC_MICBIAS_VOLT_0_7); + dev_info(dev, "DAC endisable: %ld\n", endisable); - return 0; + return count; } -static int rk3308_remove(struct snd_soc_codec *codec) +static ssize_t dac_output_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct rk3308_codec_priv *rk3308 = snd_soc_codec_get_drvdata(codec); + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + ssize_t ret = 0; - rk3308_speaker_ctl(rk3308, 0); - rk3308_codec_micbias_disable(rk3308); - rk3308_codec_power_off(codec); + switch (rk3308->dac_output) { + case DAC_LINEOUT: + ret = sprintf(buf, "dac path: %s\n", "line out"); + break; + case DAC_HPOUT: + ret = sprintf(buf, "dac path: %s\n", "hp out"); + break; + case DAC_LINEOUT_HPOUT: + ret = sprintf(buf, "dac path: %s\n", + "both line out and hp out"); + break; + default: + pr_err("Invalid dac path: %d ?\n", rk3308->dac_output); + break; + } - return 0; + return ret; } -static struct snd_soc_codec_driver soc_codec_dev_rk3308 = { - .probe = rk3308_probe, - .remove = rk3308_remove, - .suspend = rk3308_suspend, - .resume = rk3308_resume, - .set_bias_level = rk3308_set_bias_level, - .controls = rk3308_codec_dapm_controls, - .num_controls = ARRAY_SIZE(rk3308_codec_dapm_controls), -}; - -static const struct reg_default rk3308_codec_reg_defaults[] = { - { RK3308_GLB_CON, 0x07 }, -}; - -static bool rk3308_codec_write_read_reg(struct device *dev, unsigned int reg) +static ssize_t dac_output_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - /* All registers can be read / write */ - return true; -} + struct rk3308_codec_priv *rk3308 = + container_of(dev, struct rk3308_codec_priv, dev); + unsigned long dac_output; + int ret = kstrtoul(buf, 10, &dac_output); -static bool rk3308_codec_volatile_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case RK3308_GLB_CON: - return true; - default: - return false; + if (ret < 0) { + dev_err(dev, "Invalid input status: %ld, ret: %d\n", + dac_output, ret); + return -EINVAL; } -} -static const struct regmap_config rk3308_codec_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = RK3308_DAC_ANA_CON13, - .writeable_reg = rk3308_codec_write_read_reg, - .readable_reg = rk3308_codec_write_read_reg, - .volatile_reg = rk3308_codec_volatile_reg, - .reg_defaults = rk3308_codec_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(rk3308_codec_reg_defaults), - .cache_type = REGCACHE_FLAT, -}; + rk3308_codec_dac_switch(rk3308, dac_output); -static ssize_t adc_ch_show(struct device *dev, - struct device_attribute *attr, - char *buf) + dev_info(dev, "Store dac_output: %d\n", rk3308->dac_output); + + return count; +} + +static ssize_t enable_all_adcs_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct rk3308_codec_priv *rk3308 = container_of(dev, struct rk3308_codec_priv, dev); - return sprintf(buf, "adc_ch: %d\n", rk3308->adc_ch); + return sprintf(buf, "%d\n", rk3308->enable_all_adcs); } -static ssize_t adc_ch_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t enable_all_adcs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct rk3308_codec_priv *rk3308 = container_of(dev, struct rk3308_codec_priv, dev); - unsigned long ch; - int ret = kstrtoul(buf, 10, &ch); + unsigned long enable; + int ret = kstrtoul(buf, 10, &enable); - if (ret < 0 || ch > 4) { - dev_err(dev, "Invalid ch: %ld, ret: %d\n", ch, ret); + if (ret < 0) { + dev_err(dev, "Invalid enable value: %ld, ret: %d\n", + enable, ret); return -EINVAL; } - rk3308->adc_ch = ch; - - dev_info(dev, "Store ch: %d\n", rk3308->adc_ch); + rk3308->enable_all_adcs = enable; return count; } -static const struct device_attribute adc_ch_attrs[] = { - __ATTR(adc_ch, 0644, adc_ch_show, adc_ch_store), +static const struct device_attribute acodec_attrs[] = { + __ATTR_RW(adc_grps), + __ATTR_RW(adc_grps_endisable), + __ATTR_RW(adc_grps_route), + __ATTR_RW(adc_grp0_in), + __ATTR_RW(adc_zerocross), + __ATTR_RW(dac_endisable), + __ATTR_RW(dac_output), + __ATTR_RW(enable_all_adcs), + __ATTR_RW(pm_state), }; static void rk3308_codec_device_release(struct device *dev) @@ -1468,8 +4747,8 @@ static int rk3308_codec_sysfs_init(struct platform_device *pdev, return -ENOMEM; } - for (i = 0; i < ARRAY_SIZE(adc_ch_attrs); i++) { - if (device_create_file(dev, &adc_ch_attrs[i])) { + for (i = 0; i < ARRAY_SIZE(acodec_attrs); i++) { + if (device_create_file(dev, &acodec_attrs[i])) { dev_err(&pdev->dev, "Create 'rk3308-acodec-dev' attr failed\n"); device_unregister(dev); @@ -1480,32 +4759,136 @@ static int rk3308_codec_sysfs_init(struct platform_device *pdev, return 0; } +#if defined(CONFIG_DEBUG_FS) +static int rk3308_codec_debugfs_reg_show(struct seq_file *s, void *v) +{ + struct rk3308_codec_priv *rk3308 = s->private; + unsigned int i; + unsigned int val; + + for (i = RK3308_GLB_CON; i <= RK3308_DAC_ANA_CON13; i += 4) { + regmap_read(rk3308->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 rk3308_codec_debugfs_reg_operate(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct rk3308_codec_priv *rk3308 = + ((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(rk3308->regmap, reg, val); + regcache_cache_only(rk3308->regmap, false); + regcache_sync(rk3308->regmap); + pr_info("Read back reg: 0x%04x with val: 0x%08x\n", reg, val); + } else if (op == 'r') { + regmap_read(rk3308->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 rk3308_codec_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, + rk3308_codec_debugfs_reg_show, inode->i_private); +} + +static const struct file_operations rk3308_codec_reg_debugfs_fops = { + .owner = THIS_MODULE, + .open = rk3308_codec_debugfs_open, + .read = seq_read, + .write = rk3308_codec_debugfs_reg_operate, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* CONFIG_DEBUG_FS */ + +static int rk3308_codec_get_version(struct rk3308_codec_priv *rk3308) +{ + unsigned int chip_id; + + regmap_read(rk3308->grf, GRF_CHIP_ID, &chip_id); + switch (chip_id) { + case 3306: + rk3308->codec_ver = ACODEC_VERSION_A; + break; + case 0x3308: + rk3308->codec_ver = ACODEC_VERSION_B; + break; + default: + pr_err("Unknown chip_id: %d / 0x%x\n", chip_id, chip_id); + return -EFAULT; + } + + pr_info("The acodec version is: %x\n", rk3308->codec_ver); + return 0; +} + static int rk3308_platform_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct rk3308_codec_priv *rk3308; struct resource *res; void __iomem *base; - int ret = 0; - struct regmap *grf; - - grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); - if (IS_ERR(grf)) { - dev_err(&pdev->dev, - "Missing 'rockchip,grf' property\n"); - return PTR_ERR(grf); - } + int ret; rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL); if (!rk3308) return -ENOMEM; + rk3308->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(rk3308->grf)) { + dev_err(&pdev->dev, + "Missing 'rockchip,grf' property\n"); + return PTR_ERR(rk3308->grf); + } + ret = rk3308_codec_sysfs_init(pdev, rk3308); if (ret < 0) { dev_err(&pdev->dev, "Sysfs init failed\n"); return ret; } +#if defined(CONFIG_DEBUG_FS) + rk3308->dbg_codec = debugfs_create_dir(CODEC_DRV_NAME, NULL); + if (IS_ERR(rk3308->dbg_codec)) + dev_err(&pdev->dev, + "Failed to create debugfs dir for rk3308!\n"); + else + debugfs_create_file("reg", 0644, rk3308->dbg_codec, + rk3308, &rk3308_codec_reg_debugfs_fops); +#endif rk3308->plat_dev = &pdev->dev; rk3308->reset = devm_reset_control_get(&pdev->dev, "acodec-reset"); @@ -1518,27 +4901,146 @@ static int rk3308_platform_probe(struct platform_device *pdev) rk3308->reset = NULL; } - /* GPIO0_A5 control speaker on RK3308 EVB */ - rk3308->spk_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "spk_ctl", - GPIOD_OUT_HIGH); - if (IS_ERR(rk3308->spk_ctl_gpio)) { + rk3308->hp_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "hp-ctl", + GPIOD_OUT_LOW); + if (!rk3308->hp_ctl_gpio) { + dev_info(&pdev->dev, "Don't need hp-ctl gpio\n"); + } else if (IS_ERR(rk3308->hp_ctl_gpio)) { + ret = PTR_ERR(rk3308->hp_ctl_gpio); + dev_err(&pdev->dev, "Unable to claim gpio hp-ctl\n"); + return ret; + } + + rk3308->spk_ctl_gpio = devm_gpiod_get_optional(&pdev->dev, "spk-ctl", + GPIOD_OUT_LOW); + + if (!rk3308->spk_ctl_gpio) { + dev_info(&pdev->dev, "Don't need spk-ctl gpio\n"); + } else if (IS_ERR(rk3308->spk_ctl_gpio)) { ret = PTR_ERR(rk3308->spk_ctl_gpio); - dev_err(&pdev->dev, "Unable to claim gpio spk_ctl\n"); + dev_err(&pdev->dev, "Unable to claim gpio spk-ctl\n"); + return ret; + } + + rk3308->pa_drv_gpio = devm_gpiod_get_optional(&pdev->dev, "pa-drv", + GPIOD_OUT_LOW); + + if (!rk3308->pa_drv_gpio) { + dev_info(&pdev->dev, "Don't need pa-drv gpio\n"); + } else if (IS_ERR(rk3308->pa_drv_gpio)) { + ret = PTR_ERR(rk3308->pa_drv_gpio); + dev_err(&pdev->dev, "Unable to claim gpio pa-drv\n"); return ret; } + if (rk3308->pa_drv_gpio) { + rk3308->delay_pa_drv_ms = PA_DRV_MS; + ret = of_property_read_u32(np, "rockchip,delay-pa-drv-ms", + &rk3308->delay_pa_drv_ms); + } + +#if DEBUG_POP_ALWAYS + dev_info(&pdev->dev, "Enable all ctl gpios always for debugging pop\n"); + rk3308_headphone_ctl(rk3308, 1); + rk3308_speaker_ctl(rk3308, 1); +#else + dev_info(&pdev->dev, "De-pop as much as possible\n"); + rk3308_headphone_ctl(rk3308, 0); + rk3308_speaker_ctl(rk3308, 0); +#endif + rk3308->pclk = devm_clk_get(&pdev->dev, "acodec"); if (IS_ERR(rk3308->pclk)) { dev_err(&pdev->dev, "Can't get acodec pclk\n"); return PTR_ERR(rk3308->pclk); } + rk3308->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx"); + if (IS_ERR(rk3308->mclk_rx)) { + dev_err(&pdev->dev, "Can't get acodec mclk_rx\n"); + return PTR_ERR(rk3308->mclk_rx); + } + + rk3308->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); + if (IS_ERR(rk3308->mclk_tx)) { + dev_err(&pdev->dev, "Can't get acodec mclk_tx\n"); + return PTR_ERR(rk3308->mclk_tx); + } + ret = clk_prepare_enable(rk3308->pclk); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable acodec pclk: %d\n", ret); return ret; } + ret = clk_prepare_enable(rk3308->mclk_rx); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable i2s mclk_rx: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(rk3308->mclk_tx); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable i2s mclk_tx: %d\n", ret); + return ret; + } + + rk3308_codec_check_micbias(rk3308, np); + + rk3308->enable_all_adcs = + of_property_read_bool(np, "rockchip,enable-all-adcs"); + + rk3308->hp_jack_reversed = + of_property_read_bool(np, "rockchip,hp-jack-reversed"); + + rk3308->no_deep_low_power = + of_property_read_bool(np, "rockchip,no-deep-low-power"); + + rk3308->no_hp_det = + of_property_read_bool(np, "rockchip,no-hp-det"); + + rk3308->delay_loopback_handle_ms = LOOPBACK_HANDLE_MS; + ret = of_property_read_u32(np, "rockchip,delay-loopback-handle-ms", + &rk3308->delay_loopback_handle_ms); + + rk3308->delay_start_play_ms = 0; + ret = of_property_read_u32(np, "rockchip,delay-start-play-ms", + &rk3308->delay_start_play_ms); + + rk3308->loopback_grp = NOT_USED; + ret = of_property_read_u32(np, "rockchip,loopback-grp", + &rk3308->loopback_grp); + /* + * If there is no loopback on some board, the -EINVAL indicates that + * we don't need add the node, and it is not an error. + */ + if (ret < 0 && ret != -EINVAL) { + dev_err(&pdev->dev, "Failed to read loopback property: %d\n", + ret); + return ret; + } + + ret = rk3308_codec_adc_grps_route(rk3308, np); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to route ADC groups: %d\n", + ret); + return ret; + } + + ret = rk3308_codec_setup_en_always_adcs(rk3308, np); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to setup enabled always ADCs: %d\n", + ret); + return ret; + } + + ret = rk3308_codec_get_version(rk3308); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get acodec version: %d\n", + ret); + return ret; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { @@ -1555,10 +5057,65 @@ static int rk3308_platform_probe(struct platform_device *pdev) goto failed; } + if (!rk3308->no_hp_det) { + int index = 0; + + if (rk3308->codec_ver == ACODEC_VERSION_B) + index = 1; + + rk3308->irq = platform_get_irq(pdev, index); + if (rk3308->irq < 0) { + dev_err(&pdev->dev, "Can not get codec irq\n"); + goto failed; + } + + INIT_DELAYED_WORK(&rk3308->hpdet_work, rk3308_codec_hpdetect_work); + + ret = devm_request_irq(&pdev->dev, rk3308->irq, + rk3308_codec_hpdet_isr, + 0, + "acodec-hpdet", + rk3308); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request IRQ: %d\n", ret); + goto failed; + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + rk3308->detect_grf = + syscon_regmap_lookup_by_phandle(np, "rockchip,detect-grf"); + if (IS_ERR(rk3308->detect_grf)) { + dev_err(&pdev->dev, + "Missing 'rockchip,detect-grf' property\n"); + return PTR_ERR(rk3308->detect_grf); + } + + /* Configure filter count and enable hpdet irq. */ + regmap_write(rk3308->detect_grf, + DETECT_GRF_ACODEC_HPDET_COUNTER, + DEFAULT_HPDET_COUNT); + regmap_write(rk3308->detect_grf, + DETECT_GRF_ACODEC_HPDET_CON, + (HPDET_BOTH_NEG_POS << 16) | + HPDET_BOTH_NEG_POS); + } + + rk3308_codec_set_jack_detect_cb = rk3308_codec_set_jack_detect; + } + + if (rk3308->codec_ver == ACODEC_VERSION_A) + INIT_DELAYED_WORK(&rk3308->loopback_work, + rk3308_codec_loopback_work); + + rk3308->adc_grp0_using_linein = ADC_GRP0_MICIN; + rk3308->dac_output = DAC_LINEOUT; + rk3308->adc_zerocross = 1; + rk3308->pm_state = PM_NORMAL; + platform_set_drvdata(pdev, rk3308); ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_rk3308, - rk3308_dai, ARRAY_SIZE(rk3308_dai)); + rk3308_dai, ARRAY_SIZE(rk3308_dai)); if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); goto failed; @@ -1567,7 +5124,10 @@ static int rk3308_platform_probe(struct platform_device *pdev) return ret; failed: + clk_disable_unprepare(rk3308->mclk_rx); + clk_disable_unprepare(rk3308->mclk_tx); clk_disable_unprepare(rk3308->pclk); + device_unregister(&rk3308->dev); return ret; } @@ -1577,8 +5137,11 @@ static int rk3308_platform_remove(struct platform_device *pdev) struct rk3308_codec_priv *rk3308 = (struct rk3308_codec_priv *)platform_get_drvdata(pdev); + clk_disable_unprepare(rk3308->mclk_rx); + clk_disable_unprepare(rk3308->mclk_tx); clk_disable_unprepare(rk3308->pclk); snd_soc_unregister_codec(&pdev->dev); + device_unregister(&rk3308->dev); return 0; } @@ -1591,7 +5154,7 @@ MODULE_DEVICE_TABLE(of, rk3308codec_of_match); static struct platform_driver rk3308_codec_driver = { .driver = { - .name = "rk3308-acodec", + .name = CODEC_DRV_NAME, .of_match_table = of_match_ptr(rk3308codec_of_match), }, .probe = rk3308_platform_probe, diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h index 6cfa69157785..93e089dae081 100644 --- a/sound/soc/codecs/rk3308_codec.h +++ b/sound/soc/codecs/rk3308_codec.h @@ -26,7 +26,8 @@ #define ACODEC_ADC_I2S_CTL0 0x04 /* REG 0x01 */ #define ACODEC_ADC_I2S_CTL1 0x08 /* REG 0x02 */ #define ACODEC_ADC_BIST_MODE_SEL 0x0c /* REG 0x03 */ -/* Resevred REG 0x04 ~ 0x06 */ +#define ACODEC_ADC_HPF_PATH 0x10 /* REG 0x04 */ +/* Resevred REG 0x05 ~ 0x06 */ #define ACODEC_ADC_DATA_PATH 0x1c /* REG 0x07 */ /* Resevred REG 0x08 ~ 0x0f */ @@ -62,12 +63,15 @@ #define ACODEC_DAC_I2S_CTL0 0x04 /* REG 0x01 */ #define ACODEC_DAC_I2S_CTL1 0x08 /* REG 0x02 */ #define ACODEC_DAC_BIST_MODE_SEL 0x0c /* REG 0x03 */ -/* Resevred REG 0x04 */ +#define ACODEC_DAC_DIGITAL_GAIN 0x10 /* REG 0x04 */ #define ACODEC_DAC_DATA_SEL 0x14 /* REG 0x05 */ /* Resevred REG 0x06 ~ 0x09 */ #define ACODEC_DAC_DATA_HI 0x28 /* REG 0x0a */ #define ACODEC_DAC_DATA_LO 0x2c /* REG 0x0b */ -/* Resevred REG 0x0c ~ 0x0f */ +/* Resevred REG 0x0c */ +#define ACODEC_DAC_HPDET_DELAYTIME 0x34 /* REG 0x0d */ +#define ACODEC_DAC_HPDET_STATUS 0x38 /* REG 0x0e, Read-only */ +/* Resevred REG 0x0f */ /* ADC ANALOG REGISTERS */ #define ACODEC_ADC_ANA_MIC_CTL 0x00 /* REG 0x00 */ @@ -92,10 +96,13 @@ #define ACODEC_DAC_ANA_LINEOUT 0x10 /* REG 0x04 */ #define ACODEC_DAC_ANA_L_HPOUT_GAIN 0x14 /* REG 0x05 */ #define ACODEC_DAC_ANA_R_HPOUT_GAIN 0x18 /* REG 0x06 */ +#define ACODEC_DAC_ANA_DRV_HPOUT 0x1c /* REG 0x07 */ +#define ACODEC_DAC_ANA_DRV_LINEOUT 0x20 /* REG 0x08 */ /* Resevred REG 0x07 ~ 0x0b */ #define ACODEC_DAC_ANA_HPMIX_CTL0 0x30 /* REG 0x0c */ #define ACODEC_DAC_ANA_HPMIX_CTL1 0x34 /* REG 0x0d */ -/* Resevred REG 0x0e ~ 0x0f */ +#define ACODEC_DAC_ANA_LINEOUT_CTL0 0x38 /* REG 0x0e */ +#define ACODEC_DAC_ANA_LINEOUT_CTL1 0x3c /* REG 0x0f */ /* * These registers are referenced by codec driver @@ -106,7 +113,7 @@ /* ADC DIGITAL REGISTERS */ /* - * The ADC chanel are 0 ~ 3, that control: + * The ADC group are 0 ~ 3, that control: * * CH0: left_0(ADC1) and right_0(ADC2) * CH1: left_1(ADC3) and right_1(ADC4) @@ -118,6 +125,7 @@ #define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_I2S_CTL0) #define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_I2S_CTL1) #define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_BIST_MODE_SEL) +#define RK3308_ADC_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_HPF_PATH) #define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_DATA_PATH) #define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET(ch) + ACODEC_ADC_PGA_AGC_L_CTL0) @@ -150,13 +158,16 @@ #define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_I2S_CTL0) #define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_I2S_CTL1) #define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_BIST_MODE_SEL) +#define RK3308_DAC_DIG_CON04 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DIGITAL_GAIN) #define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_SEL) #define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_HI) #define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_DATA_LO) +#define RK3308_DAC_DIG_CON13 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_HPDET_DELAYTIME) +#define RK3308_DAC_DIG_CON14 (RK3308_DAC_DIG_OFFSET + ACODEC_DAC_HPDET_STATUS) /* ADC ANALOG REGISTERS */ /* - * The ADC chanel are 0 ~ 3, that control: + * The ADC group are 0 ~ 3, that control: * * CH0: left_0(ADC1) and right_0(ADC2) * CH1: left_1(ADC3) and right_1(ADC4) @@ -179,7 +190,6 @@ /* DAC ANALOG REGISTERS */ #define RK3308_DAC_ANA_OFFSET 0x440 - #define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_CTL0) #define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_POP_VOLT) #define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_CTL1) @@ -187,8 +197,12 @@ #define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT) #define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_L_HPOUT_GAIN) #define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_R_HPOUT_GAIN) +#define RK3308_DAC_ANA_CON07 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_DRV_HPOUT) +#define RK3308_DAC_ANA_CON08 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_DRV_LINEOUT) #define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPMIX_CTL0) #define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_HPMIX_CTL1) +#define RK3308_DAC_ANA_CON14 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT_CTL0) +#define RK3308_DAC_ANA_CON15 (RK3308_DAC_ANA_OFFSET + ACODEC_DAC_ANA_LINEOUT_CTL1) /* * These are the bits for registers @@ -199,6 +213,12 @@ #define RK3308_ADC_BIST_RESET (0 << 7) #define RK3308_DAC_BIST_WORK (1 << 6) #define RK3308_DAC_BIST_RESET (0 << 6) +#define RK3308_ADC_MCLK_MSK (1 << 5) +#define RK3308_ADC_MCLK_DIS (1 << 5) +#define RK3308_ADC_MCLK_EN (0 << 5) +#define RK3308_DAC_MCLK_MSK (1 << 4) +#define RK3308_DAC_MCLK_DIS (1 << 4) +#define RK3308_DAC_MCLK_EN (0 << 4) #define RK3308_CODEC_RST_MSK (0x7 << 0) #define RK3308_ADC_DIG_WORK (1 << 2) #define RK3308_ADC_DIG_RESET (0 << 2) @@ -253,16 +273,27 @@ /* RK3308_ADC_DIG_CON03 - REG: 0x000c */ #define RK3308_ADC_L_CH_BIST_SFT 2 #define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT) -#define RK3308_ADC_L_CH_BIST_LEFT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ -#define RK3308_ADC_L_CH_BIST_SINE (0x2 << RK3308_ADC_L_CH_BIST_SFT) -#define RK3308_ADC_L_CH_BIST_CUBE (0x1 << RK3308_ADC_L_CH_BIST_SFT) -#define RK3308_ADC_L_CH_BIST_RIGHT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_L_CH_NORMAL_RIGHT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_L_CH_BIST_CUBE (0x2 << RK3308_ADC_L_CH_BIST_SFT) +#define RK3308_ADC_L_CH_BIST_SINE (0x1 << RK3308_ADC_L_CH_BIST_SFT) +#define RK3308_ADC_L_CH_NORMAL_LEFT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ #define RK3308_ADC_R_CH_BIST_SFT 0 #define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT) -#define RK3308_ADC_R_CH_BIST_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ -#define RK3308_ADC_R_CH_BIST_SINE (0x2 << RK3308_ADC_R_CH_BIST_SFT) -#define RK3308_ADC_R_CH_BIST_CUBE (0x1 << RK3308_ADC_R_CH_BIST_SFT) -#define RK3308_ADC_R_CH_BIST_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_R_CH_NORMAL_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_R_CH_BIST_CUBE (0x2 << RK3308_ADC_R_CH_BIST_SFT) +#define RK3308_ADC_R_CH_BIST_SINE (0x1 << RK3308_ADC_R_CH_BIST_SFT) +#define RK3308_ADC_R_CH_NORMAL_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ + +/* RK3308_ADC_DIG_CON04 - REG: 0x0010 */ +#define RK3308_ADC_HPF_PATH_SFT 2 +#define RK3308_ADC_HPF_PATH_MSK (1 << RK3308_ADC_HPF_PATH_SFT) +#define RK3308_ADC_HPF_PATH_DIS (1 << RK3308_ADC_HPF_PATH_SFT) +#define RK3308_ADC_HPF_PATH_EN (0 << RK3308_ADC_HPF_PATH_SFT) +#define RK3308_ADC_HPF_CUTOFF_SFT 0 +#define RK3308_ADC_HPF_CUTOFF_MSK (0x3 << RK3308_ADC_HPF_CUTOFF_SFT) +#define RK3308_ADC_HPF_CUTOFF_612HZ (0x2 << RK3308_ADC_HPF_CUTOFF_SFT) +#define RK3308_ADC_HPF_CUTOFF_245HZ (0x1 << RK3308_ADC_HPF_CUTOFF_SFT) +#define RK3308_ADC_HPF_CUTOFF_20HZ (0x0 << RK3308_ADC_HPF_CUTOFF_SFT) /* RK3308_ADC_DIG_CON07 - REG: 0x001c */ #define RK3308_ADCL_DATA_SFT 4 @@ -391,6 +422,8 @@ */ #define RK3308_AGC_PGA_ZERO_CRO_EN (0x1 << 5) #define RK3308_AGC_PGA_ZERO_CRO_DIS (0x0 << 5) +#define RK3308_AGC_PGA_GAIN_MAX 0x1f +#define RK3308_AGC_PGA_GAIN_MIN 0 #define RK3308_AGC_PGA_GAIN_SFT 0 #define RK3308_AGC_PGA_GAIN_MSK (0x1f << RK3308_AGC_PGA_GAIN_SFT) #define RK3308_AGC_PGA_GAIN_PDB_28_5 (0x1f << RK3308_AGC_PGA_GAIN_SFT) @@ -474,6 +507,8 @@ #define RK3308_AGC_FUNC_SEL_MSK (0x1 << 6) #define RK3308_AGC_FUNC_SEL_EN (0x1 << 6) #define RK3308_AGC_FUNC_SEL_DIS (0x0 << 6) +#define RK3308_AGC_MAX_GAIN_PGA_MAX 0x7 +#define RK3308_AGC_MAX_GAIN_PGA_MIN 0 #define RK3308_AGC_MAX_GAIN_PGA_SFT 3 #define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) #define RK3308_AGC_MAX_GAIN_PGA_PDB_28_5 (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) @@ -484,6 +519,8 @@ #define RK3308_AGC_MAX_GAIN_PGA_NDB_1_5 (0x2 << RK3308_AGC_MAX_GAIN_PGA_SFT) #define RK3308_AGC_MAX_GAIN_PGA_NDB_7_5 (0x1 << RK3308_AGC_MAX_GAIN_PGA_SFT) #define RK3308_AGC_MAX_GAIN_PGA_NDB_13_5 (0x0 << RK3308_AGC_MAX_GAIN_PGA_SFT) +#define RK3308_AGC_MIN_GAIN_PGA_MAX 0x7 +#define RK3308_AGC_MIN_GAIN_PGA_MIN 0 #define RK3308_AGC_MIN_GAIN_PGA_SFT 0 #define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) #define RK3308_AGC_MIN_GAIN_PGA_PDB_24 (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) @@ -555,6 +592,18 @@ #define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT) #define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */ +/* RK3308_DAC_DIG_CON04 - REG: 0x0310 */ +#define RK3308_DAC_MODULATOR_GAIN_SFT 4 +#define RK3308_DAC_MODULATOR_GAIN_MSK (0x7 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_MODULATOR_GAIN_4_8DB (0x5 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_MODULATOR_GAIN_4_2DB (0x4 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_MODULATOR_GAIN_3_5DB (0x3 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_MODULATOR_GAIN_2_8DB (0x2 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_MODULATOR_GAIN_2DB (0x1 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_MODULATOR_GAIN_0DB (0x0 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_CIC_IF_GAIN_SFT 0 +#define RK3308_DAC_CIC_IF_GAIN_MSK (0x7 << RK3308_DAC_CIC_IF_GAIN_SFT) + /* RK3308_DAC_DIG_CON05 - REG: 0x0314 */ #define RK3308_DAC_L_REG_CTL_INDATA (0x1 << 2) #define RK3308_DAC_L_NORMAL_DATA (0x0 << 2) @@ -587,18 +636,30 @@ #define RK3308_ADC_CH1_BUF_REF_EN (0x1 << 0) #define RK3308_ADC_CH1_BUF_REF_DIS (0x0 << 0) -/* RK3308_ADC_ANA_CON01 - REG: 0x0344 */ +/* RK3308_ADC_ANA_CON01 - REG: 0x0344 + * + * The PGA of MIC-INs: + * 0x0 - MIC1~MIC8 0dB + * 0x1 - MIC1~MIC8 6.6dB + * 0x2 - MIC1~MIC8 13dB + * 0x3 - MIC1~MIC8 20dB + */ +#define RK3308_ADC_CH2_MIC_GAIN_MAX 0x3 +#define RK3308_ADC_CH2_MIC_GAIN_MIN 0 #define RK3308_ADC_CH2_MIC_GAIN_SFT 4 #define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) -#define RK3308_ADC_CH2_MIC_GAIN_30DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) -#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT) -#define RK3308_ADC_CH2_MIC_GAIN_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT) +#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) +#define RK3308_ADC_CH2_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT) /* TRM: only used for version B */ +#define RK3308_ADC_CH2_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT) /* TRM: only used for version B */ #define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT) + +#define RK3308_ADC_CH1_MIC_GAIN_MAX 0x3 +#define RK3308_ADC_CH1_MIC_GAIN_MIN 0 #define RK3308_ADC_CH1_MIC_GAIN_SFT 0 #define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) -#define RK3308_ADC_CH1_MIC_GAIN_30DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) -#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT) -#define RK3308_ADC_CH1_MIC_GAIN_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT) +#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) +#define RK3308_ADC_CH1_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT) /* TRM: only used for version B */ +#define RK3308_ADC_CH1_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT) /* TRM: only used for version B */ #define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT) /* RK3308_ADC_ANA_CON02 - REG: 0x0348 */ @@ -619,6 +680,8 @@ #define RK3308_ADC_CH1_ALC_DIS (0x0 << 0) /* RK3308_ADC_ANA_CON03 - REG: 0x034c */ +#define RK3308_ADC_CH1_ALC_GAIN_MAX 0x1f +#define RK3308_ADC_CH1_ALC_GAIN_MIN 0 #define RK3308_ADC_CH1_ALC_GAIN_SFT 0 #define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) #define RK3308_ADC_CH1_ALC_GAIN_PDB_28_5 (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) @@ -655,6 +718,8 @@ #define RK3308_ADC_CH1_ALC_GAIN_NDB_18 (0x00 << RK3308_ADC_CH1_ALC_GAIN_SFT) /* RK3308_ADC_ANA_CON04 - REG: 0x0350 */ +#define RK3308_ADC_CH2_ALC_GAIN_MAX 0x1f +#define RK3308_ADC_CH2_ALC_GAIN_MIN 0 #define RK3308_ADC_CH2_ALC_GAIN_SFT 0 #define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) #define RK3308_ADC_CH2_ALC_GAIN_PDB_28_5 (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) @@ -728,10 +793,16 @@ #define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT) #define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT) -#define RK3308_ADC_MIC_BIAS_BUF_EN (0x1 << 3) -#define RK3308_ADC_MIC_BIAS_BUF_DIS (0x0 << 3) +#define RK3308_ADC_MIC_BIAS_BUF_SFT 3 +#define RK3308_ADC_MIC_BIAS_BUF_EN (0x1 << RK3308_ADC_MIC_BIAS_BUF_SFT) +#define RK3308_ADC_MIC_BIAS_BUF_DIS (0x0 << RK3308_ADC_MIC_BIAS_BUF_SFT) #define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0 #define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) +/* + * The follow MICBIAS_VOLTs are based on the external reference voltage(Vref). + * For example, the Vref == 3.3V, the MICBIAS_VOLT_0_85 is equal: + * 3.3V * 0.85 = 2.805V. + */ #define RK3308_ADC_MICBIAS_VOLT_0_85 (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) #define RK3308_ADC_MICBIAS_VOLT_0_8 (0x6 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) #define RK3308_ADC_MICBIAS_VOLT_0_75 (0x5 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) @@ -751,18 +822,11 @@ #define RK3308_ADC_REF_DIS (0x0 << 7) #define RK3308_ADC_CURRENT_CHARGE_SFT 0 #define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) -#define RK3308_ADC_DONT_SEL_ALL (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) /* - * 0: Choose the current I - * 1: Don't choose the current I + * 1: Choose the current I + * 0: Don't choose the current I */ -#define RK3308_ADC_SEL_I_1(x) ((x & 0x1) << 6) -#define RK3308_ADC_SEL_I_2(x) ((x & 0x1) << 5) -#define RK3308_ADC_SEL_I_4(x) ((x & 0x1) << 4) -#define RK3308_ADC_SEL_I_8(x) ((x & 0x1) << 3) -#define RK3308_ADC_SEL_I_16(x) ((x & 0x1) << 2) -#define RK3308_ADC_SEL_I_32(x) ((x & 0x1) << 1) -#define RK3308_ADC_SEL_I_64(x) ((x & 0x1) << 0) +#define RK3308_ADC_SEL_I(x) (x & 0x7f) /* RK3308_ADC_ANA_CON11 - REG: 0x036c */ #define RK3308_ADC_ALCR_CON_GAIN_PGAR_MSK (0x1 << 1) @@ -773,6 +837,7 @@ #define RK3308_ADC_ALCL_CON_GAIN_PGAL_DIS (0x0 << 0) /* RK3308_DAC_ANA_CON00 - REG: 0x0440 */ +#define RK3308_DAC_HEADPHONE_DET_MSK (0x1 << 1) #define RK3308_DAC_HEADPHONE_DET_EN (0x1 << 1) #define RK3308_DAC_HEADPHONE_DET_DIS (0x0 << 1) #define RK3308_DAC_CURRENT_MSK (0x1 << 0) @@ -783,17 +848,17 @@ #define RK3308_DAC_BUF_REF_R_MSK (0x1 << 6) #define RK3308_DAC_BUF_REF_R_EN (0x1 << 6) #define RK3308_DAC_BUF_REF_R_DIS (0x0 << 6) -#define RK3308_DAC_POP_SOUND_R_SFT 4 -#define RK3308_DAC_POP_SOUND_R_MSK (0x3 << RK3308_DAC_POP_SOUND_R_SFT) -#define RK3308_DAC_POP_SOUND_R_WORK (0x2 << RK3308_DAC_POP_SOUND_R_SFT) -#define RK3308_DAC_POP_SOUND_R_INIT (0x1 << RK3308_DAC_POP_SOUND_R_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_R_SFT 4 +#define RK3308_DAC_HPOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_R_WORK (0x2 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_R_INIT (0x1 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) #define RK3308_DAC_BUF_REF_L_MSK (0x1 << 2) #define RK3308_DAC_BUF_REF_L_EN (0x1 << 2) #define RK3308_DAC_BUF_REF_L_DIS (0x0 << 2) -#define RK3308_DAC_POP_SOUND_L_SFT 0 -#define RK3308_DAC_POP_SOUND_L_MSK (0x3 << RK3308_DAC_POP_SOUND_L_SFT) -#define RK3308_DAC_POP_SOUND_L_WORK (0x2 << RK3308_DAC_POP_SOUND_L_SFT) -#define RK3308_DAC_POP_SOUND_L_INIT (0x1 << RK3308_DAC_POP_SOUND_L_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_L_SFT 0 +#define RK3308_DAC_HPOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_L_WORK (0x2 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_L_INIT (0x1 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) /* RK3308_DAC_ANA_CON02 - REG: 0x0448 */ #define RK3308_DAC_R_DAC_WORK (0x1 << 7) @@ -828,28 +893,31 @@ #define RK3308_DAC_L_HPOUT_MUTE (0x0 << 0) /* RK3308_DAC_ANA_CON04 - REG: 0x0450 */ -#define RK3308_DAC_R_GAIN_SFT 6 -#define RK3308_DAC_R_GAIN_MSK (0x3 << RK3308_DAC_R_GAIN_SFT) -#define RK3308_DAC_R_GAIN_0DB (0x3 << RK3308_DAC_R_GAIN_SFT) -#define RK3308_DAC_R_GAIN_PDB_1_5 (0x2 << RK3308_DAC_R_GAIN_SFT) -#define RK3308_DAC_R_GAIN_PDB_3 (0x1 << RK3308_DAC_R_GAIN_SFT) -#define RK3308_DAC_R_GAIN_PDB_6 (0x0 << RK3308_DAC_R_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_MAX 0x3 +#define RK3308_DAC_R_LINEOUT_GAIN_SFT 6 +#define RK3308_DAC_R_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_R_LINEOUT_GAIN_SFT) #define RK3308_DAC_R_LINEOUT_UNMUTE (0x1 << 5) #define RK3308_DAC_R_LINEOUT_MUTE (0x0 << 5) #define RK3308_DAC_R_LINEOUT_EN (0x1 << 4) #define RK3308_DAC_R_LINEOUT_DIS (0x0 << 4) -#define RK3308_DAC_L_GAIN_SFT 2 -#define RK3308_DAC_L_GAIN_MSK (0x3 << RK3308_DAC_L_GAIN_SFT) -#define RK3308_DAC_L_GAIN_0DB (0x3 << RK3308_DAC_L_GAIN_SFT) -#define RK3308_DAC_L_GAIN_PDB_1_5 (0x2 << RK3308_DAC_L_GAIN_SFT) -#define RK3308_DAC_L_GAIN_PDB_3 (0x1 << RK3308_DAC_L_GAIN_SFT) -#define RK3308_DAC_L_GAIN_PDB_6 (0x0 << RK3308_DAC_L_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_MAX 0x3 +#define RK3308_DAC_L_LINEOUT_GAIN_SFT 2 +#define RK3308_DAC_L_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_L_LINEOUT_GAIN_SFT) #define RK3308_DAC_L_LINEOUT_UNMUTE (0x1 << 1) #define RK3308_DAC_L_LINEOUT_MUTE (0x0 << 1) #define RK3308_DAC_L_LINEOUT_EN (0x1 << 0) #define RK3308_DAC_L_LINEOUT_DIS (0x0 << 0) /* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */ +#define RK3308_DAC_L_HPOUT_GAIN_MAX 0x1e #define RK3308_DAC_L_HPOUT_GAIN_SFT 0 #define RK3308_DAC_L_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_L_HPOUT_GAIN_SFT) #define RK3308_DAC_L_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_L_HPOUT_GAIN_SFT) @@ -885,6 +953,7 @@ #define RK3308_DAC_L_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_L_HPOUT_GAIN_SFT) /* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */ +#define RK3308_DAC_R_HPOUT_GAIN_MAX 0x1e #define RK3308_DAC_R_HPOUT_GAIN_SFT 0 #define RK3308_DAC_R_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_R_HPOUT_GAIN_SFT) #define RK3308_DAC_R_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_R_HPOUT_GAIN_SFT) @@ -919,6 +988,18 @@ #define RK3308_DAC_R_HPOUT_GAIN_NDB_37_5 (0x01 << RK3308_DAC_R_HPOUT_GAIN_SFT) #define RK3308_DAC_R_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_R_HPOUT_GAIN_SFT) +/* RK3308_DAC_ANA_CON07 - REG: 0x045c */ +#define RK3308_DAC_R_HPOUT_DRV_SFT 4 +#define RK3308_DAC_R_HPOUT_DRV_MSK (0xf << RK3308_DAC_R_HPOUT_DRV_SFT) +#define RK3308_DAC_L_HPOUT_DRV_SFT 0 +#define RK3308_DAC_L_HPOUT_DRV_MSK (0xf << RK3308_DAC_L_HPOUT_DRV_SFT) + +/* RK3308_DAC_ANA_CON08 - REG: 0x0460 */ +#define RK3308_DAC_R_LINEOUT_DRV_SFT 4 +#define RK3308_DAC_R_LINEOUT_DRV_MSK (0xf << RK3308_DAC_R_LINEOUT_DRV_SFT) +#define RK3308_DAC_L_LINEOUT_DRV_SFT 0 +#define RK3308_DAC_L_LINEOUT_DRV_MSK (0xf << RK3308_DAC_L_LINEOUT_DRV_SFT) + /* RK3308_DAC_ANA_CON12 - REG: 0x0470 */ #define RK3308_DAC_R_HPMIX_SEL_SFT 6 #define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT) @@ -926,6 +1007,8 @@ #define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT) #define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT) #define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT) +#define RK3308_DAC_R_HPMIX_GAIN_MIN 0x1 +#define RK3308_DAC_R_HPMIX_GAIN_MAX 0x2 #define RK3308_DAC_R_HPMIX_GAIN_SFT 4 #define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT) #define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT) @@ -936,6 +1019,8 @@ #define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT) #define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT) #define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT) +#define RK3308_DAC_L_HPMIX_GAIN_MIN 0x1 +#define RK3308_DAC_L_HPMIX_GAIN_MAX 0x2 #define RK3308_DAC_L_HPMIX_GAIN_SFT 0 #define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT) #define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT) @@ -955,6 +1040,30 @@ #define RK3308_DAC_L_HPMIX_EN (0x1 << 0) #define RK3308_DAC_L_HPMIX_DIS (0x0 << 0) +/* RK3308_DAC_ANA_CON14 - REG: 0x0478 */ +#define RK3308_DAC_VCM_LINEOUT_EN (0x1 << 4) +#define RK3308_DAC_VCM_LINEOUT_DIS (0x0 << 4) +#define RK3308_DAC_CURRENT_CHARGE_SFT 0 +#define RK3308_DAC_CURRENT_CHARGE_MSK (0xf << RK3308_DAC_CURRENT_CHARGE_SFT) + +/* + * 1: Choose the current I + * 0: Don't choose the current I + */ +#define RK3308_DAC_SEL_I(x) (x & 0xf) + +/* RK3308_DAC_ANA_CON15 - REG: 0x047C */ +#define RK3308_DAC_LINEOUT_POP_SOUND_R_SFT 4 +#define RK3308_DAC_LINEOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_R_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_R_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_LINEOUT_POP_SOUND_L_SFT 0 +#define RK3308_DAC_LINEOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_L_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_L_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) + #define RK3308_HIFI 0x0 #endif /* __RK3308_CODEC_H__ */ diff --git a/sound/soc/codecs/rk3308_codec_provider.h b/sound/soc/codecs/rk3308_codec_provider.h new file mode 100644 index 000000000000..68042b1328dc --- /dev/null +++ b/sound/soc/codecs/rk3308_codec_provider.h @@ -0,0 +1,28 @@ +/* + * rk3308_codec_provider.h -- RK3308 ALSA Soc Audio Driver + * + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __RK3308_CODEC_PROVIDER_H__ +#define __RK3308_CODEC_PROVIDER_H__ + +#ifdef CONFIG_SND_SOC_RK3308 +extern void (*rk3308_codec_set_jack_detect_cb)(struct snd_soc_codec *codec, + struct snd_soc_jack *hpdet_jack); +#endif + +#endif /* __RK3308_CODEC_PROVIDER_H__ */ -- 2.25.1