diff options
author | Charles Keepax <ckeepax@opensource.wolfsonmicro.com> | 2015-07-07 10:28:14 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-07-08 15:23:07 -0400 |
commit | d1acd31883d78f905a930493ff145ca4a25ad680 (patch) | |
tree | 99e838c01c77b8121f448c052848d00f9f23f102 /sound/soc | |
parent | 81207880cef207cd89db863f9aa1d65f22b4f2a2 (diff) |
ASoC: wm5110: Add special DRE on/off handling for the headphone path
For the best performance the headphone path enable/disable must be
handled specially for the situations of DRE on and DRE off.
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/codecs/wm5110.c | 285 |
1 files changed, 276 insertions, 9 deletions
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 709fcc6169d8..b5c201b1e454 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c | |||
@@ -131,6 +131,25 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = { | |||
131 | { 0x33fb, 0xfe00 }, | 131 | { 0x33fb, 0xfe00 }, |
132 | }; | 132 | }; |
133 | 133 | ||
134 | static const struct reg_default wm5110_sysclk_reve_patch[] = { | ||
135 | { 0x3270, 0xE410 }, | ||
136 | { 0x3271, 0x3078 }, | ||
137 | { 0x3272, 0xE410 }, | ||
138 | { 0x3273, 0x3070 }, | ||
139 | { 0x3274, 0xE410 }, | ||
140 | { 0x3275, 0x3066 }, | ||
141 | { 0x3276, 0xE410 }, | ||
142 | { 0x3277, 0x3056 }, | ||
143 | { 0x327A, 0xE414 }, | ||
144 | { 0x327B, 0x3078 }, | ||
145 | { 0x327C, 0xE414 }, | ||
146 | { 0x327D, 0x3070 }, | ||
147 | { 0x327E, 0xE414 }, | ||
148 | { 0x327F, 0x3066 }, | ||
149 | { 0x3280, 0xE414 }, | ||
150 | { 0x3281, 0x3056 }, | ||
151 | }; | ||
152 | |||
134 | static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, | 153 | static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, |
135 | struct snd_kcontrol *kcontrol, int event) | 154 | struct snd_kcontrol *kcontrol, int event) |
136 | { | 155 | { |
@@ -146,7 +165,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, | |||
146 | patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch); | 165 | patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch); |
147 | break; | 166 | break; |
148 | default: | 167 | default: |
149 | return 0; | 168 | patch = wm5110_sysclk_reve_patch; |
169 | patch_size = ARRAY_SIZE(wm5110_sysclk_reve_patch); | ||
170 | break; | ||
150 | } | 171 | } |
151 | 172 | ||
152 | switch (event) { | 173 | switch (event) { |
@@ -164,6 +185,249 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, | |||
164 | return 0; | 185 | return 0; |
165 | } | 186 | } |
166 | 187 | ||
188 | static const struct reg_default wm5110_no_dre_left_enable[] = { | ||
189 | { 0x3024, 0xE410 }, | ||
190 | { 0x3025, 0x0056 }, | ||
191 | { 0x301B, 0x0224 }, | ||
192 | { 0x301F, 0x4263 }, | ||
193 | { 0x3021, 0x5291 }, | ||
194 | { 0x3030, 0xE410 }, | ||
195 | { 0x3031, 0x3066 }, | ||
196 | { 0x3032, 0xE410 }, | ||
197 | { 0x3033, 0x3070 }, | ||
198 | { 0x3034, 0xE410 }, | ||
199 | { 0x3035, 0x3078 }, | ||
200 | { 0x3036, 0xE410 }, | ||
201 | { 0x3037, 0x3080 }, | ||
202 | { 0x3038, 0xE410 }, | ||
203 | { 0x3039, 0x3080 }, | ||
204 | }; | ||
205 | |||
206 | static const struct reg_default wm5110_dre_left_enable[] = { | ||
207 | { 0x3024, 0x0231 }, | ||
208 | { 0x3025, 0x0B00 }, | ||
209 | { 0x301B, 0x0227 }, | ||
210 | { 0x301F, 0x4266 }, | ||
211 | { 0x3021, 0x5294 }, | ||
212 | { 0x3030, 0xE231 }, | ||
213 | { 0x3031, 0x0266 }, | ||
214 | { 0x3032, 0x8231 }, | ||
215 | { 0x3033, 0x4B15 }, | ||
216 | { 0x3034, 0x8231 }, | ||
217 | { 0x3035, 0x0B15 }, | ||
218 | { 0x3036, 0xE231 }, | ||
219 | { 0x3037, 0x5294 }, | ||
220 | { 0x3038, 0x0231 }, | ||
221 | { 0x3039, 0x0B00 }, | ||
222 | }; | ||
223 | |||
224 | static const struct reg_default wm5110_no_dre_right_enable[] = { | ||
225 | { 0x3074, 0xE414 }, | ||
226 | { 0x3075, 0x0056 }, | ||
227 | { 0x306B, 0x0224 }, | ||
228 | { 0x306F, 0x4263 }, | ||
229 | { 0x3071, 0x5291 }, | ||
230 | { 0x3080, 0xE414 }, | ||
231 | { 0x3081, 0x3066 }, | ||
232 | { 0x3082, 0xE414 }, | ||
233 | { 0x3083, 0x3070 }, | ||
234 | { 0x3084, 0xE414 }, | ||
235 | { 0x3085, 0x3078 }, | ||
236 | { 0x3086, 0xE414 }, | ||
237 | { 0x3087, 0x3080 }, | ||
238 | { 0x3088, 0xE414 }, | ||
239 | { 0x3089, 0x3080 }, | ||
240 | }; | ||
241 | |||
242 | static const struct reg_default wm5110_dre_right_enable[] = { | ||
243 | { 0x3074, 0x0231 }, | ||
244 | { 0x3075, 0x0B00 }, | ||
245 | { 0x306B, 0x0227 }, | ||
246 | { 0x306F, 0x4266 }, | ||
247 | { 0x3071, 0x5294 }, | ||
248 | { 0x3080, 0xE231 }, | ||
249 | { 0x3081, 0x0266 }, | ||
250 | { 0x3082, 0x8231 }, | ||
251 | { 0x3083, 0x4B17 }, | ||
252 | { 0x3084, 0x8231 }, | ||
253 | { 0x3085, 0x0B17 }, | ||
254 | { 0x3086, 0xE231 }, | ||
255 | { 0x3087, 0x5294 }, | ||
256 | { 0x3088, 0x0231 }, | ||
257 | { 0x3089, 0x0B00 }, | ||
258 | }; | ||
259 | |||
260 | static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) | ||
261 | { | ||
262 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
263 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
264 | struct arizona *arizona = priv->arizona; | ||
265 | unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); | ||
266 | const struct reg_default *wseq; | ||
267 | int nregs; | ||
268 | |||
269 | switch (w->shift) { | ||
270 | case ARIZONA_OUT1L_ENA_SHIFT: | ||
271 | if (val & ARIZONA_DRE1L_ENA_MASK) { | ||
272 | wseq = wm5110_dre_left_enable; | ||
273 | nregs = ARRAY_SIZE(wm5110_dre_left_enable); | ||
274 | } else { | ||
275 | wseq = wm5110_no_dre_left_enable; | ||
276 | nregs = ARRAY_SIZE(wm5110_no_dre_left_enable); | ||
277 | priv->out_up_delay += 10; | ||
278 | } | ||
279 | break; | ||
280 | case ARIZONA_OUT1R_ENA_SHIFT: | ||
281 | if (val & ARIZONA_DRE1R_ENA_MASK) { | ||
282 | wseq = wm5110_dre_right_enable; | ||
283 | nregs = ARRAY_SIZE(wm5110_dre_right_enable); | ||
284 | } else { | ||
285 | wseq = wm5110_no_dre_right_enable; | ||
286 | nregs = ARRAY_SIZE(wm5110_no_dre_right_enable); | ||
287 | priv->out_up_delay += 10; | ||
288 | } | ||
289 | break; | ||
290 | default: | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | return regmap_multi_reg_write(arizona->regmap, wseq, nregs); | ||
295 | } | ||
296 | |||
297 | static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w) | ||
298 | { | ||
299 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
300 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
301 | unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); | ||
302 | |||
303 | switch (w->shift) { | ||
304 | case ARIZONA_OUT1L_ENA_SHIFT: | ||
305 | if (!(val & ARIZONA_DRE1L_ENA_MASK)) { | ||
306 | snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, | ||
307 | ARIZONA_WS_TRG1, ARIZONA_WS_TRG1); | ||
308 | snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, | ||
309 | ARIZONA_WS_TRG1, 0); | ||
310 | priv->out_down_delay += 27; | ||
311 | } | ||
312 | break; | ||
313 | case ARIZONA_OUT1R_ENA_SHIFT: | ||
314 | if (!(val & ARIZONA_DRE1R_ENA_MASK)) { | ||
315 | snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, | ||
316 | ARIZONA_WS_TRG2, ARIZONA_WS_TRG2); | ||
317 | snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, | ||
318 | ARIZONA_WS_TRG2, 0); | ||
319 | priv->out_down_delay += 27; | ||
320 | } | ||
321 | break; | ||
322 | default: | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int wm5110_hp_ev(struct snd_soc_dapm_widget *w, | ||
330 | struct snd_kcontrol *kcontrol, int event) | ||
331 | { | ||
332 | struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); | ||
333 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
334 | |||
335 | switch (priv->arizona->rev) { | ||
336 | case 0 ... 3: | ||
337 | break; | ||
338 | default: | ||
339 | switch (event) { | ||
340 | case SND_SOC_DAPM_PRE_PMU: | ||
341 | wm5110_hp_pre_enable(w); | ||
342 | break; | ||
343 | case SND_SOC_DAPM_PRE_PMD: | ||
344 | wm5110_hp_pre_disable(w); | ||
345 | break; | ||
346 | default: | ||
347 | break; | ||
348 | } | ||
349 | break; | ||
350 | } | ||
351 | |||
352 | return arizona_hp_ev(w, kcontrol, event); | ||
353 | } | ||
354 | |||
355 | static int wm5110_clear_pga_volume(struct arizona *arizona, int output) | ||
356 | { | ||
357 | struct reg_default clear_pga = { | ||
358 | ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80 | ||
359 | }; | ||
360 | int ret; | ||
361 | |||
362 | ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1); | ||
363 | if (ret) | ||
364 | dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n", | ||
365 | clear_pga.reg, ret); | ||
366 | |||
367 | return ret; | ||
368 | } | ||
369 | |||
370 | static int wm5110_put_dre(struct snd_kcontrol *kcontrol, | ||
371 | struct snd_ctl_elem_value *ucontrol) | ||
372 | { | ||
373 | struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); | ||
374 | struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); | ||
375 | struct arizona *arizona = dev_get_drvdata(codec->dev->parent); | ||
376 | struct soc_mixer_control *mc = | ||
377 | (struct soc_mixer_control *)kcontrol->private_value; | ||
378 | unsigned int ena, dre; | ||
379 | unsigned int mask = (0x1 << mc->shift) | (0x1 << mc->rshift); | ||
380 | unsigned int lnew = (!!ucontrol->value.integer.value[0]) << mc->shift; | ||
381 | unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift; | ||
382 | unsigned int lold, rold; | ||
383 | unsigned int lena, rena; | ||
384 | int ret; | ||
385 | |||
386 | snd_soc_dapm_mutex_lock(dapm); | ||
387 | |||
388 | ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &ena); | ||
389 | if (ret) { | ||
390 | dev_err(arizona->dev, "Failed to read output state: %d\n", ret); | ||
391 | goto err; | ||
392 | } | ||
393 | ret = regmap_read(arizona->regmap, ARIZONA_DRE_ENABLE, &dre); | ||
394 | if (ret) { | ||
395 | dev_err(arizona->dev, "Failed to read DRE state: %d\n", ret); | ||
396 | goto err; | ||
397 | } | ||
398 | |||
399 | lold = dre & (1 << mc->shift); | ||
400 | rold = dre & (1 << mc->rshift); | ||
401 | /* Enables are channel wise swapped from the DRE enables */ | ||
402 | lena = ena & (1 << mc->rshift); | ||
403 | rena = ena & (1 << mc->shift); | ||
404 | |||
405 | if ((lena && lnew != lold) || (rena && rnew != rold)) { | ||
406 | dev_err(arizona->dev, "Can't change DRE on active outputs\n"); | ||
407 | ret = -EBUSY; | ||
408 | goto err; | ||
409 | } | ||
410 | |||
411 | ret = regmap_update_bits(arizona->regmap, ARIZONA_DRE_ENABLE, | ||
412 | mask, lnew | rnew); | ||
413 | if (ret) { | ||
414 | dev_err(arizona->dev, "Failed to set DRE: %d\n", ret); | ||
415 | goto err; | ||
416 | } | ||
417 | |||
418 | /* Force reset of PGA volumes, if turning DRE off */ | ||
419 | if (!lnew && lold) | ||
420 | wm5110_clear_pga_volume(arizona, mc->shift); | ||
421 | |||
422 | if (!rnew && rold) | ||
423 | wm5110_clear_pga_volume(arizona, mc->rshift); | ||
424 | |||
425 | err: | ||
426 | snd_soc_dapm_mutex_unlock(dapm); | ||
427 | |||
428 | return ret; | ||
429 | } | ||
430 | |||
167 | static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); | 431 | static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); |
168 | static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); | 432 | static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); |
169 | static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); | 433 | static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); |
@@ -409,12 +673,15 @@ SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, | |||
409 | SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, | 673 | SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, |
410 | ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), | 674 | ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), |
411 | 675 | ||
412 | SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, | 676 | SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, |
413 | ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0), | 677 | ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0, |
414 | SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, | 678 | snd_soc_get_volsw, wm5110_put_dre), |
415 | ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0), | 679 | SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, |
416 | SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, | 680 | ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0, |
417 | ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0), | 681 | snd_soc_get_volsw, wm5110_put_dre), |
682 | SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, | ||
683 | ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0, | ||
684 | snd_soc_get_volsw, wm5110_put_dre), | ||
418 | 685 | ||
419 | SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), | 686 | SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), |
420 | SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), | 687 | SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), |
@@ -904,11 +1171,11 @@ SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, | |||
904 | ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), | 1171 | ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), |
905 | 1172 | ||
906 | SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, | 1173 | SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, |
907 | ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, | 1174 | ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, |
908 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | | 1175 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | |
909 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), | 1176 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), |
910 | SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, | 1177 | SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, |
911 | ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, | 1178 | ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, |
912 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | | 1179 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | |
913 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), | 1180 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), |
914 | SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, | 1181 | SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, |