diff options
Diffstat (limited to 'sound/soc/codecs/wm_hubs.c')
-rw-r--r-- | sound/soc/codecs/wm_hubs.c | 69 |
1 files changed, 46 insertions, 23 deletions
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2cb81538cd91..31c2a5724d85 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -94,6 +94,18 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); u16 reg, reg_l, reg_r, dcs_cfg; + /* If we're using a digital only path and have a previously + * callibrated DC servo offset stored then use that. */ + if (hubs->class_w && hubs->class_w_dcs) { + dev_dbg(codec->dev, "Using cached DC servo offset %x\n", + hubs->class_w_dcs); + snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs); + wait_for_dc_servo(codec, + WM8993_DCS_TRIG_DAC_WR_0 | + WM8993_DCS_TRIG_DAC_WR_1); + return; + } + /* Set for 32 series updates */ snd_soc_update_bits(codec, WM8993_DC_SERVO_1, WM8993_DCS_SERIES_NO_01_MASK, @@ -101,34 +113,34 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) wait_for_dc_servo(codec, WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1); + /* Different chips in the family support different readback + * methods. + */ + switch (hubs->dcs_readback_mode) { + case 0: + reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) + & WM8993_DCS_INTEG_CHAN_0_MASK;; + reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) + & WM8993_DCS_INTEG_CHAN_1_MASK; + break; + case 1: + reg = snd_soc_read(codec, WM8993_DC_SERVO_3); + reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) + >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; + reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; + break; + default: + WARN(1, "Unknown DCS readback method"); + break; + } + + dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r); + /* Apply correction to DC servo result */ if (hubs->dcs_codes) { dev_dbg(codec->dev, "Applying %d code DC servo correction\n", hubs->dcs_codes); - /* Different chips in the family support different - * readback methods. - */ - switch (hubs->dcs_readback_mode) { - case 0: - reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) - & WM8993_DCS_INTEG_CHAN_0_MASK;; - reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) - & WM8993_DCS_INTEG_CHAN_1_MASK; - break; - case 1: - reg = snd_soc_read(codec, WM8993_DC_SERVO_3); - reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) - >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; - reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; - break; - default: - WARN(1, "Unknown DCS readback method"); - break; - } - - dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r); - /* HPOUT1L */ if (reg_l + hubs->dcs_codes > 0 && reg_l + hubs->dcs_codes < 0xff) @@ -148,7 +160,15 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) wait_for_dc_servo(codec, WM8993_DCS_TRIG_DAC_WR_0 | WM8993_DCS_TRIG_DAC_WR_1); + } else { + dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + dcs_cfg |= reg_r; } + + /* Save the callibrated offset if we're in class W mode and + * therefore don't have any analogue signal mixed in. */ + if (hubs->class_w) + hubs->class_w_dcs = dcs_cfg; } /* @@ -163,6 +183,9 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + /* Updating the analogue gains invalidates the DC servo cache */ + hubs->class_w_dcs = 0; + /* If we're applying an offset correction then updating the * callibration would be likely to introduce further offsets. */ if (hubs->dcs_codes) |