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