aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-02-09 15:14:42 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-02-10 05:45:05 -0500
commitc5b6a9feaeb0fa0e39e3fc10f9bf8cc8de498739 (patch)
treec8f8fb62bfec14bc807d97c93441097d2db3c42b
parentfa9879edebdaad4cfcd2dbe3eaa2ba0dc4f0a262 (diff)
ASoC: Actively manage WM8903 DC servo configuration
Explicitly cache the DC servo offsets for digital paths in the driver, allowing them to be preserved over suspend and resume, and ensure that we recalibrate analogue outputs paths when they are in use so that we cover any changes in the input offset. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-rw-r--r--sound/soc/codecs/wm8903.c123
-rw-r--r--sound/soc/codecs/wm8903.h8
2 files changed, 127 insertions, 4 deletions
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 119abceb1d85..2f912276a8f5 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -223,6 +223,9 @@ struct wm8903_priv {
223 int fs; 223 int fs;
224 int deemph; 224 int deemph;
225 225
226 int dcs_pending;
227 int dcs_cache[4];
228
226 /* Reference count */ 229 /* Reference count */
227 int class_w_users; 230 int class_w_users;
228 231
@@ -248,6 +251,10 @@ static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int re
248 case WM8903_WRITE_SEQUENCER_4: 251 case WM8903_WRITE_SEQUENCER_4:
249 case WM8903_POWER_MANAGEMENT_3: 252 case WM8903_POWER_MANAGEMENT_3:
250 case WM8903_POWER_MANAGEMENT_2: 253 case WM8903_POWER_MANAGEMENT_2:
254 case WM8903_DC_SERVO_READBACK_1:
255 case WM8903_DC_SERVO_READBACK_2:
256 case WM8903_DC_SERVO_READBACK_3:
257 case WM8903_DC_SERVO_READBACK_4:
251 return 1; 258 return 1;
252 259
253 default: 260 default:
@@ -315,6 +322,103 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
315 return 0; 322 return 0;
316} 323}
317 324
325static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
326 struct snd_kcontrol *kcontrol, int event)
327{
328 struct snd_soc_codec *codec = w->codec;
329 struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
330
331 switch (event) {
332 case SND_SOC_DAPM_POST_PMU:
333 wm8903->dcs_pending |= 1 << w->shift;
334 break;
335 case SND_SOC_DAPM_PRE_PMD:
336 snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
337 1 << w->shift, 0);
338 break;
339 }
340
341 return 0;
342}
343
344#define WM8903_DCS_MODE_WRITE_STOP 0
345#define WM8903_DCS_MODE_START_STOP 2
346
347static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
348 enum snd_soc_dapm_type event, int subseq)
349{
350 struct snd_soc_codec *codec = container_of(dapm,
351 struct snd_soc_codec, dapm);
352 struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
353 int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
354 int i, val;
355
356 /* Complete any pending DC servo starts */
357 if (wm8903->dcs_pending) {
358 dev_dbg(codec->dev, "Starting DC servo for %x\n",
359 wm8903->dcs_pending);
360
361 /* If we've no cached values then we need to do startup */
362 for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
363 if (!(wm8903->dcs_pending & (1 << i)))
364 continue;
365
366 if (wm8903->dcs_cache[i]) {
367 dev_dbg(codec->dev,
368 "Restore DC servo %d value %x\n",
369 3 - i, wm8903->dcs_cache[i]);
370
371 snd_soc_write(codec, WM8903_DC_SERVO_4 + i,
372 wm8903->dcs_cache[i] & 0xff);
373 } else {
374 dev_dbg(codec->dev,
375 "Calibrate DC servo %d\n", 3 - i);
376 dcs_mode = WM8903_DCS_MODE_START_STOP;
377 }
378 }
379
380 /* Don't trust the cache for analogue */
381 if (wm8903->class_w_users)
382 dcs_mode = WM8903_DCS_MODE_START_STOP;
383
384 snd_soc_update_bits(codec, WM8903_DC_SERVO_2,
385 WM8903_DCS_MODE_MASK, dcs_mode);
386
387 snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
388 WM8903_DCS_ENA_MASK, wm8903->dcs_pending);
389
390 switch (dcs_mode) {
391 case WM8903_DCS_MODE_WRITE_STOP:
392 break;
393
394 case WM8903_DCS_MODE_START_STOP:
395 msleep(270);
396
397 /* Cache the measured offsets for digital */
398 if (wm8903->class_w_users)
399 break;
400
401 for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
402 if (!(wm8903->dcs_pending & (1 << i)))
403 continue;
404
405 val = snd_soc_read(codec,
406 WM8903_DC_SERVO_READBACK_1 + i);
407 dev_dbg(codec->dev, "DC servo %d: %x\n",
408 3 - i, val);
409 wm8903->dcs_cache[i] = val;
410 }
411 break;
412
413 default:
414 pr_warn("DCS mode %d delay not set\n", dcs_mode);
415 break;
416 }
417
418 wm8903->dcs_pending = 0;
419 }
420}
421
318/* 422/*
319 * When used with DAC outputs only the WM8903 charge pump supports 423 * When used with DAC outputs only the WM8903 charge pump supports
320 * operation in class W mode, providing very low power consumption 424 * operation in class W mode, providing very low power consumption
@@ -847,10 +951,15 @@ SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0,
847SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0, 951SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0,
848 NULL, 0), 952 NULL, 0),
849 953
850SND_SOC_DAPM_PGA_S("HPL_DCS", 3, WM8903_DC_SERVO_0, 3, 0, NULL, 0), 954SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0),
851SND_SOC_DAPM_PGA_S("HPR_DCS", 3, WM8903_DC_SERVO_0, 2, 0, NULL, 0), 955SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event,
852SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, WM8903_DC_SERVO_0, 1, 0, NULL, 0), 956 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
853SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, WM8903_DC_SERVO_0, 0, 0, NULL, 0), 957SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event,
958 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
959SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event,
960 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
961SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event,
962 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
854 963
855SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, 964SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
856 NULL, 0), 965 NULL, 0),
@@ -979,6 +1088,11 @@ static const struct snd_soc_dapm_route intercon[] = {
979 { "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" }, 1088 { "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" },
980 { "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" }, 1089 { "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" },
981 1090
1091 { "HPL_DCS", NULL, "DCS Master" },
1092 { "HPR_DCS", NULL, "DCS Master" },
1093 { "LINEOUTL_DCS", NULL, "DCS Master" },
1094 { "LINEOUTR_DCS", NULL, "DCS Master" },
1095
982 { "HPL_DCS", NULL, "HPL_ENA_DLY" }, 1096 { "HPL_DCS", NULL, "HPL_ENA_DLY" },
983 { "HPR_DCS", NULL, "HPR_ENA_DLY" }, 1097 { "HPR_DCS", NULL, "HPR_ENA_DLY" },
984 { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" }, 1098 { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" },
@@ -1901,6 +2015,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
1901 .reg_word_size = sizeof(u16), 2015 .reg_word_size = sizeof(u16),
1902 .reg_cache_default = wm8903_reg_defaults, 2016 .reg_cache_default = wm8903_reg_defaults,
1903 .volatile_register = wm8903_volatile_register, 2017 .volatile_register = wm8903_volatile_register,
2018 .seq_notifier = wm8903_seq_notifier,
1904}; 2019};
1905 2020
1906#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) 2021#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
index e8490f3edd03..9ef394ae0dd3 100644
--- a/sound/soc/codecs/wm8903.h
+++ b/sound/soc/codecs/wm8903.h
@@ -75,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec,
75#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41 75#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41
76#define WM8903_DC_SERVO_0 0x43 76#define WM8903_DC_SERVO_0 0x43
77#define WM8903_DC_SERVO_2 0x45 77#define WM8903_DC_SERVO_2 0x45
78#define WM8903_DC_SERVO_4 0x47
79#define WM8903_DC_SERVO_5 0x48
80#define WM8903_DC_SERVO_6 0x49
81#define WM8903_DC_SERVO_7 0x4A
82#define WM8903_DC_SERVO_READBACK_1 0x51
83#define WM8903_DC_SERVO_READBACK_2 0x52
84#define WM8903_DC_SERVO_READBACK_3 0x53
85#define WM8903_DC_SERVO_READBACK_4 0x54
78#define WM8903_ANALOGUE_HP_0 0x5A 86#define WM8903_ANALOGUE_HP_0 0x5A
79#define WM8903_ANALOGUE_LINEOUT_0 0x5E 87#define WM8903_ANALOGUE_LINEOUT_0 0x5E
80#define WM8903_CHARGE_PUMP_0 0x62 88#define WM8903_CHARGE_PUMP_0 0x62