diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8903.c | 123 | ||||
-rw-r--r-- | sound/soc/codecs/wm8903.h | 8 |
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 | ||
325 | static 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 | |||
347 | static 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, | |||
847 | SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0, | 951 | SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0, |
848 | NULL, 0), | 952 | NULL, 0), |
849 | 953 | ||
850 | SND_SOC_DAPM_PGA_S("HPL_DCS", 3, WM8903_DC_SERVO_0, 3, 0, NULL, 0), | 954 | SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0), |
851 | SND_SOC_DAPM_PGA_S("HPR_DCS", 3, WM8903_DC_SERVO_0, 2, 0, NULL, 0), | 955 | SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event, |
852 | SND_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), |
853 | SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, WM8903_DC_SERVO_0, 0, 0, NULL, 0), | 957 | SND_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), | ||
959 | SND_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), | ||
961 | SND_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 | ||
855 | SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, | 964 | SND_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 |