aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm_hubs.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-10-27 16:48:36 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-10-28 14:34:03 -0400
commitfec6dd833e733b5d9588a1f1e4d81118b79b5774 (patch)
treefd321d8252fa61dd6df15166c41e262f4c8955ae /sound/soc/codecs/wm_hubs.c
parentd906401114861585c990ff0290c002b5d22fc71a (diff)
ASoC: Store DC offset correction for wm_hubs devices in class W mode
Providing the analogue configuration of the output path remains the same the DC offset corrected by the DC servo will remain identical so we can skip the callibration, reducing the startup time for the headphone output. Implement this for the wm_hubs devices as has been done for several other CODECs. Don't do this if we have any analogue paths enabled since offsets may be being introduced by the analogue paths which could vary outside the control of the driver. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm_hubs.c')
-rw-r--r--sound/soc/codecs/wm_hubs.c69
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)
94 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); 94 struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
95 u16 reg, reg_l, reg_r, dcs_cfg; 95 u16 reg, reg_l, reg_r, dcs_cfg;
96 96
97 /* If we're using a digital only path and have a previously
98 * callibrated DC servo offset stored then use that. */
99 if (hubs->class_w && hubs->class_w_dcs) {
100 dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
101 hubs->class_w_dcs);
102 snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs);
103 wait_for_dc_servo(codec,
104 WM8993_DCS_TRIG_DAC_WR_0 |
105 WM8993_DCS_TRIG_DAC_WR_1);
106 return;
107 }
108
97 /* Set for 32 series updates */ 109 /* Set for 32 series updates */
98 snd_soc_update_bits(codec, WM8993_DC_SERVO_1, 110 snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
99 WM8993_DCS_SERIES_NO_01_MASK, 111 WM8993_DCS_SERIES_NO_01_MASK,
@@ -101,34 +113,34 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
101 wait_for_dc_servo(codec, 113 wait_for_dc_servo(codec,
102 WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1); 114 WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1);
103 115
116 /* Different chips in the family support different readback
117 * methods.
118 */
119 switch (hubs->dcs_readback_mode) {
120 case 0:
121 reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
122 & WM8993_DCS_INTEG_CHAN_0_MASK;;
123 reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
124 & WM8993_DCS_INTEG_CHAN_1_MASK;
125 break;
126 case 1:
127 reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
128 reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
129 >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
130 reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
131 break;
132 default:
133 WARN(1, "Unknown DCS readback method");
134 break;
135 }
136
137 dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
138
104 /* Apply correction to DC servo result */ 139 /* Apply correction to DC servo result */
105 if (hubs->dcs_codes) { 140 if (hubs->dcs_codes) {
106 dev_dbg(codec->dev, "Applying %d code DC servo correction\n", 141 dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
107 hubs->dcs_codes); 142 hubs->dcs_codes);
108 143
109 /* Different chips in the family support different
110 * readback methods.
111 */
112 switch (hubs->dcs_readback_mode) {
113 case 0:
114 reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
115 & WM8993_DCS_INTEG_CHAN_0_MASK;;
116 reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
117 & WM8993_DCS_INTEG_CHAN_1_MASK;
118 break;
119 case 1:
120 reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
121 reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
122 >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
123 reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
124 break;
125 default:
126 WARN(1, "Unknown DCS readback method");
127 break;
128 }
129
130 dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
131
132 /* HPOUT1L */ 144 /* HPOUT1L */
133 if (reg_l + hubs->dcs_codes > 0 && 145 if (reg_l + hubs->dcs_codes > 0 &&
134 reg_l + hubs->dcs_codes < 0xff) 146 reg_l + hubs->dcs_codes < 0xff)
@@ -148,7 +160,15 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
148 wait_for_dc_servo(codec, 160 wait_for_dc_servo(codec,
149 WM8993_DCS_TRIG_DAC_WR_0 | 161 WM8993_DCS_TRIG_DAC_WR_0 |
150 WM8993_DCS_TRIG_DAC_WR_1); 162 WM8993_DCS_TRIG_DAC_WR_1);
163 } else {
164 dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
165 dcs_cfg |= reg_r;
151 } 166 }
167
168 /* Save the callibrated offset if we're in class W mode and
169 * therefore don't have any analogue signal mixed in. */
170 if (hubs->class_w)
171 hubs->class_w_dcs = dcs_cfg;
152} 172}
153 173
154/* 174/*
@@ -163,6 +183,9 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
163 183
164 ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); 184 ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
165 185
186 /* Updating the analogue gains invalidates the DC servo cache */
187 hubs->class_w_dcs = 0;
188
166 /* If we're applying an offset correction then updating the 189 /* If we're applying an offset correction then updating the
167 * callibration would be likely to introduce further offsets. */ 190 * callibration would be likely to introduce further offsets. */
168 if (hubs->dcs_codes) 191 if (hubs->dcs_codes)