|
@@ -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)
|