summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/rt5663.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/rt5663.c')
-rw-r--r--sound/soc/codecs/rt5663.c272
1 files changed, 265 insertions, 7 deletions
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index e45b895d8279..b036c9dc0c8c 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -38,13 +38,24 @@ enum {
CODEC_VER_0,
};
+struct impedance_mapping_table {
+ unsigned int imp_min;
+ unsigned int imp_max;
+ unsigned int vol;
+ unsigned int dc_offset_l_manual;
+ unsigned int dc_offset_r_manual;
+ unsigned int dc_offset_l_manual_mic;
+ unsigned int dc_offset_r_manual_mic;
+};
+
struct rt5663_priv {
struct snd_soc_codec *codec;
struct rt5663_platform_data pdata;
struct regmap *regmap;
- struct delayed_work jack_detect_work;
+ struct delayed_work jack_detect_work, jd_unplug_work;
struct snd_soc_jack *hs_jack;
struct timer_list btn_check_timer;
+ struct impedance_mapping_table *imp_table;
int codec_ver;
int sysclk;
@@ -1575,6 +1586,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
rt5663->jack_type = SND_JACK_HEADSET;
rt5663_enable_push_button_irq(codec, true);
+ if (rt5663->pdata.impedance_sensing_num)
+ break;
+
if (rt5663->pdata.dc_offset_l_manual_mic) {
regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2,
rt5663->pdata.dc_offset_l_manual_mic >>
@@ -1596,6 +1610,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
default:
rt5663->jack_type = SND_JACK_HEADPHONE;
+ if (rt5663->pdata.impedance_sensing_num)
+ break;
+
if (rt5663->pdata.dc_offset_l_manual) {
regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2,
rt5663->pdata.dc_offset_l_manual >> 16);
@@ -1623,6 +1640,177 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
return rt5663->jack_type;
}
+static int rt5663_impedance_sensing(struct snd_soc_codec *codec)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80;
+
+ for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) {
+ if (rt5663->imp_table[i].vol == 7)
+ break;
+ }
+
+ if (rt5663->jack_type == SND_JACK_HEADSET) {
+ snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual_mic >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff);
+ snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual_mic >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff);
+ } else {
+ snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual & 0xffff);
+ snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual & 0xffff);
+ }
+
+ reg84 = snd_soc_read(codec, RT5663_ASRC_2);
+ reg26 = snd_soc_read(codec, RT5663_STO1_ADC_MIXER);
+ reg2fa = snd_soc_read(codec, RT5663_DUMMY_1);
+ reg91 = snd_soc_read(codec, RT5663_HP_CHARGE_PUMP_1);
+ reg10 = snd_soc_read(codec, RT5663_RECMIX);
+ reg80 = snd_soc_read(codec, RT5663_GLB_CLK);
+
+ snd_soc_update_bits(codec, RT5663_STO_DRE_1, 0x8000, 0);
+ snd_soc_write(codec, RT5663_ASRC_2, 0);
+ snd_soc_write(codec, RT5663_STO1_ADC_MIXER, 0x4040);
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK |
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_VREF1 | RT5663_PWR_VREF2);
+ usleep_range(10000, 10005);
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK,
+ RT5663_PWR_FV1 | RT5663_PWR_FV2);
+ snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK,
+ RT5663_SCLK_SRC_RCCLK);
+ snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
+ RT5663_DIG_25M_CLK_EN);
+ snd_soc_update_bits(codec, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0);
+ snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00);
+ snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc);
+ snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, 0x1232);
+ snd_soc_write(codec, RT5663_HP_LOGIC_2, 0x0005);
+ snd_soc_write(codec, RT5663_DEPOP_2, 0x3003);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0030);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0x0003);
+ snd_soc_update_bits(codec, RT5663_PWR_DIG_2,
+ RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F,
+ RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F);
+ snd_soc_update_bits(codec, RT5663_PWR_DIG_1,
+ RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
+ RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 |
+ RT5663_PWR_ADC_R1,
+ RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
+ RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 |
+ RT5663_PWR_ADC_R1);
+ msleep(40);
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2,
+ RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2);
+ msleep(30);
+ snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ snd_soc_write(codec, RT5663_STO_DAC_MIXER, 0);
+ snd_soc_write(codec, RT5663_BYPASS_STO_DAC, 0x000c);
+ snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa);
+ snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224);
+ snd_soc_write(codec, RT5663_HP_OUT_EN, 0x8088);
+ snd_soc_write(codec, RT5663_CHOP_ADC, 0x3000);
+ snd_soc_write(codec, RT5663_ADDA_RST, 0xc000);
+ snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0x3320);
+ snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c9);
+ snd_soc_write(codec, RT5663_DUMMY_1, 0x004c);
+ snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7733);
+ snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777);
+ snd_soc_write(codec, RT5663_STO_DRE_9, 0x0007);
+ snd_soc_write(codec, RT5663_STO_DRE_10, 0x0007);
+ snd_soc_write(codec, RT5663_DUMMY_2, 0x02a4);
+ snd_soc_write(codec, RT5663_RECMIX, 0x0005);
+ snd_soc_write(codec, RT5663_HP_IMP_SEN_1, 0x4334);
+ snd_soc_update_bits(codec, RT5663_IRQ_3, 0x0004, 0x0004);
+ snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x2200);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x3000);
+ snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x6200);
+
+ for (i = 0; i < 100; i++) {
+ msleep(20);
+ if (snd_soc_read(codec, RT5663_INT_ST_1) & 0x2)
+ break;
+ }
+
+ value = snd_soc_read(codec, RT5663_HP_IMP_SEN_4);
+
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0);
+ snd_soc_write(codec, RT5663_INT_ST_1, 0);
+ snd_soc_write(codec, RT5663_HP_LOGIC_1, 0);
+ snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK,
+ RT5663_DIG_25M_CLK_DIS);
+ snd_soc_write(codec, RT5663_GLB_CLK, reg80);
+ snd_soc_write(codec, RT5663_RECMIX, reg10);
+ snd_soc_write(codec, RT5663_DUMMY_2, 0x00a4);
+ snd_soc_write(codec, RT5663_DUMMY_1, reg2fa);
+ snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c8);
+ snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0xb320);
+ snd_soc_write(codec, RT5663_ADDA_RST, 0xe400);
+ snd_soc_write(codec, RT5663_CHOP_ADC, 0x2000);
+ snd_soc_write(codec, RT5663_HP_OUT_EN, 0x0008);
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0);
+ snd_soc_update_bits(codec, RT5663_PWR_DIG_1,
+ RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 |
+ RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 |
+ RT5663_PWR_ADC_R1, 0);
+ snd_soc_update_bits(codec, RT5663_PWR_DIG_2,
+ RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0);
+ snd_soc_write(codec, RT5663_HP_LOGIC_2, 0);
+ snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, reg91);
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0);
+ snd_soc_write(codec, RT5663_STO1_ADC_MIXER, reg26);
+ snd_soc_write(codec, RT5663_ASRC_2, reg84);
+
+ for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) {
+ if (value >= rt5663->imp_table[i].imp_min &&
+ value <= rt5663->imp_table[i].imp_max)
+ break;
+ }
+
+ snd_soc_update_bits(codec, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK,
+ rt5663->imp_table[i].vol);
+ snd_soc_update_bits(codec, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK,
+ rt5663->imp_table[i].vol);
+
+ if (rt5663->jack_type == SND_JACK_HEADSET) {
+ snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual_mic >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff);
+ snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual_mic >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff);
+ } else {
+ snd_soc_write(codec, RT5663_MIC_DECRO_2,
+ rt5663->imp_table[i].dc_offset_l_manual >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_3,
+ rt5663->imp_table[i].dc_offset_l_manual & 0xffff);
+ snd_soc_write(codec, RT5663_MIC_DECRO_5,
+ rt5663->imp_table[i].dc_offset_r_manual >> 16);
+ snd_soc_write(codec, RT5663_MIC_DECRO_6,
+ rt5663->imp_table[i].dc_offset_r_manual & 0xffff);
+ }
+
+ return 0;
+}
+
static int rt5663_button_detect(struct snd_soc_codec *codec)
{
int btn_type, val;
@@ -1702,6 +1890,8 @@ static void rt5663_jack_detect_work(struct work_struct *work)
break;
case CODEC_VER_0:
report = rt5663_jack_detect(rt5663->codec, 1);
+ if (rt5663->pdata.impedance_sensing_num)
+ rt5663_impedance_sensing(rt5663->codec);
break;
default:
dev_err(codec->dev, "Unknown CODEC Version\n");
@@ -1751,8 +1941,15 @@ static void rt5663_jack_detect_work(struct work_struct *work)
break;
}
/* button release or spurious interrput*/
- if (btn_type == 0)
+ if (btn_type == 0) {
report = rt5663->jack_type;
+ cancel_delayed_work_sync(
+ &rt5663->jd_unplug_work);
+ } else {
+ queue_delayed_work(system_wq,
+ &rt5663->jd_unplug_work,
+ msecs_to_jiffies(500));
+ }
}
} else {
/* jack out */
@@ -1773,6 +1970,37 @@ static void rt5663_jack_detect_work(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3);
}
+static void rt5663_jd_unplug_work(struct work_struct *work)
+{
+ struct rt5663_priv *rt5663 =
+ container_of(work, struct rt5663_priv, jd_unplug_work.work);
+ struct snd_soc_codec *codec = rt5663->codec;
+
+ if (!codec)
+ return;
+
+ if (!rt5663_check_jd_status(codec)) {
+ /* jack out */
+ switch (rt5663->codec_ver) {
+ case CODEC_VER_1:
+ rt5663_v2_jack_detect(rt5663->codec, 0);
+ break;
+ case CODEC_VER_0:
+ rt5663_jack_detect(rt5663->codec, 0);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC Version\n");
+ }
+
+ snd_soc_jack_report(rt5663->hs_jack, 0, SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+ } else {
+ queue_delayed_work(system_wq, &rt5663->jd_unplug_work,
+ msecs_to_jiffies(500));
+ }
+}
+
static const struct snd_kcontrol_new rt5663_snd_controls[] = {
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL,
@@ -1797,10 +2025,6 @@ static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = {
};
static const struct snd_kcontrol_new rt5663_specific_controls[] = {
- /* Headphone Output Volume */
- SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9,
- RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1,
- rt5663_hp_vol_tlv),
/* Mic Boost Volume*/
SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2,
RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv),
@@ -1808,6 +2032,13 @@ static const struct snd_kcontrol_new rt5663_specific_controls[] = {
SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum),
};
+static const struct snd_kcontrol_new rt5663_hpvol_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9,
+ RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1,
+ rt5663_hp_vol_tlv),
+};
+
static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_widget *sink)
{
@@ -2890,6 +3121,10 @@ static int rt5663_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(rt5663_specific_dapm_routes));
snd_soc_add_codec_controls(codec, rt5663_specific_controls,
ARRAY_SIZE(rt5663_specific_controls));
+
+ if (!rt5663->imp_table)
+ snd_soc_add_codec_controls(codec, rt5663_hpvol_controls,
+ ARRAY_SIZE(rt5663_hpvol_controls));
break;
}
@@ -3178,6 +3413,8 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663)
static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
{
+ int table_size;
+
device_property_read_u32(dev, "realtek,dc_offset_l_manual",
&rt5663->pdata.dc_offset_l_manual);
device_property_read_u32(dev, "realtek,dc_offset_r_manual",
@@ -3186,6 +3423,17 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev)
&rt5663->pdata.dc_offset_l_manual_mic);
device_property_read_u32(dev, "realtek,dc_offset_r_manual_mic",
&rt5663->pdata.dc_offset_r_manual_mic);
+ device_property_read_u32(dev, "realtek,impedance_sensing_num",
+ &rt5663->pdata.impedance_sensing_num);
+
+ if (rt5663->pdata.impedance_sensing_num) {
+ table_size = sizeof(struct impedance_mapping_table) *
+ rt5663->pdata.impedance_sensing_num;
+ rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL);
+ device_property_read_u32_array(dev,
+ "realtek,impedance_sensing_table",
+ (u32 *)rt5663->imp_table, table_size);
+ }
return 0;
}
@@ -3219,7 +3467,16 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
ret);
return ret;
}
- regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
+
+ ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
+ if (ret || (val != RT5663_DEVICE_ID_2 && val != RT5663_DEVICE_ID_1)) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5663, retry one time.\n",
+ val);
+ msleep(100);
+ regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
+ }
+
switch (val) {
case RT5663_DEVICE_ID_2:
rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_v2_regmap);
@@ -3338,6 +3595,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
}
INIT_DELAYED_WORK(&rt5663->jack_detect_work, rt5663_jack_detect_work);
+ INIT_DELAYED_WORK(&rt5663->jd_unplug_work, rt5663_jd_unplug_work);
if (i2c->irq) {
ret = request_irq(i2c->irq, rt5663_irq,