aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-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