aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/wm8960.h2
-rw-r--r--sound/soc/codecs/wm8960.c206
2 files changed, 179 insertions, 29 deletions
diff --git a/include/sound/wm8960.h b/include/sound/wm8960.h
index 808a258e696c..74e9a95529c5 100644
--- a/include/sound/wm8960.h
+++ b/include/sound/wm8960.h
@@ -16,6 +16,8 @@
16#define WM8960_DRES_MAX 3 16#define WM8960_DRES_MAX 3
17 17
18struct wm8960_data { 18struct wm8960_data {
19 bool capless; /* Headphone outputs configured in capless mode */
20
19 int dres; /* Discharge resistance for headphone outputs */ 21 int dres; /* Discharge resistance for headphone outputs */
20}; 22};
21 23
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index cf5cb3f73b69..c2960d3ec6df 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -31,8 +31,14 @@
31struct snd_soc_codec_device soc_codec_dev_wm8960; 31struct snd_soc_codec_device soc_codec_dev_wm8960;
32 32
33/* R25 - Power 1 */ 33/* R25 - Power 1 */
34#define WM8960_VMID_MASK 0x180
34#define WM8960_VREF 0x40 35#define WM8960_VREF 0x40
35 36
37/* R26 - Power 2 */
38#define WM8960_PWR2_LOUT1 0x40
39#define WM8960_PWR2_ROUT1 0x20
40#define WM8960_PWR2_OUT3 0x02
41
36/* R28 - Anti-pop 1 */ 42/* R28 - Anti-pop 1 */
37#define WM8960_POBCTRL 0x80 43#define WM8960_POBCTRL 0x80
38#define WM8960_BUFDCOPEN 0x10 44#define WM8960_BUFDCOPEN 0x10
@@ -42,6 +48,7 @@ struct snd_soc_codec_device soc_codec_dev_wm8960;
42 48
43/* R29 - Anti-pop 2 */ 49/* R29 - Anti-pop 2 */
44#define WM8960_DISOP 0x40 50#define WM8960_DISOP 0x40
51#define WM8960_DRES_MASK 0x30
45 52
46/* 53/*
47 * wm8960 register cache 54 * wm8960 register cache
@@ -68,6 +75,9 @@ static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
68struct wm8960_priv { 75struct wm8960_priv {
69 u16 reg_cache[WM8960_CACHEREGNUM]; 76 u16 reg_cache[WM8960_CACHEREGNUM];
70 struct snd_soc_codec codec; 77 struct snd_soc_codec codec;
78 struct snd_soc_dapm_widget *lout1;
79 struct snd_soc_dapm_widget *rout1;
80 struct snd_soc_dapm_widget *out3;
71}; 81};
72 82
73#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) 83#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)
@@ -226,10 +236,6 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
226 &wm8960_routput_mixer[0], 236 &wm8960_routput_mixer[0],
227 ARRAY_SIZE(wm8960_routput_mixer)), 237 ARRAY_SIZE(wm8960_routput_mixer)),
228 238
229SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
230 &wm8960_mono_out[0],
231 ARRAY_SIZE(wm8960_mono_out)),
232
233SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), 239SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
234SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), 240SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
235 241
@@ -248,6 +254,17 @@ SND_SOC_DAPM_OUTPUT("SPK_RN"),
248SND_SOC_DAPM_OUTPUT("OUT3"), 254SND_SOC_DAPM_OUTPUT("OUT3"),
249}; 255};
250 256
257static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
258SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
259 &wm8960_mono_out[0],
260 ARRAY_SIZE(wm8960_mono_out)),
261};
262
263/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
264static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
265SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
266};
267
251static const struct snd_soc_dapm_route audio_paths[] = { 268static const struct snd_soc_dapm_route audio_paths[] = {
252 { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, 269 { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
253 { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, 270 { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
@@ -278,9 +295,6 @@ static const struct snd_soc_dapm_route audio_paths[] = {
278 { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } , 295 { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
279 { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, 296 { "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
280 297
281 { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
282 { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
283
284 { "LOUT1 PGA", NULL, "Left Output Mixer" }, 298 { "LOUT1 PGA", NULL, "Left Output Mixer" },
285 { "ROUT1 PGA", NULL, "Right Output Mixer" }, 299 { "ROUT1 PGA", NULL, "Right Output Mixer" },
286 300
@@ -297,17 +311,65 @@ static const struct snd_soc_dapm_route audio_paths[] = {
297 { "SPK_LP", NULL, "Left Speaker Output" }, 311 { "SPK_LP", NULL, "Left Speaker Output" },
298 { "SPK_RN", NULL, "Right Speaker Output" }, 312 { "SPK_RN", NULL, "Right Speaker Output" },
299 { "SPK_RP", NULL, "Right Speaker Output" }, 313 { "SPK_RP", NULL, "Right Speaker Output" },
314};
315
316static const struct snd_soc_dapm_route audio_paths_out3[] = {
317 { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
318 { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
300 319
301 { "OUT3", NULL, "Mono Output Mixer", } 320 { "OUT3", NULL, "Mono Output Mixer", }
302}; 321};
303 322
323static const struct snd_soc_dapm_route audio_paths_capless[] = {
324 { "HP_L", NULL, "OUT3 VMID" },
325 { "HP_R", NULL, "OUT3 VMID" },
326
327 { "OUT3 VMID", NULL, "Left Output Mixer" },
328 { "OUT3 VMID", NULL, "Right Output Mixer" },
329};
330
304static int wm8960_add_widgets(struct snd_soc_codec *codec) 331static int wm8960_add_widgets(struct snd_soc_codec *codec)
305{ 332{
333 struct wm8960_data *pdata = codec->dev->platform_data;
334 struct wm8960_priv *wm8960 = codec->private_data;
335 struct snd_soc_dapm_widget *w;
336
306 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets, 337 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
307 ARRAY_SIZE(wm8960_dapm_widgets)); 338 ARRAY_SIZE(wm8960_dapm_widgets));
308 339
309 snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); 340 snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
310 341
342 /* In capless mode OUT3 is used to provide VMID for the
343 * headphone outputs, otherwise it is used as a mono mixer.
344 */
345 if (pdata && pdata->capless) {
346 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_capless,
347 ARRAY_SIZE(wm8960_dapm_widgets_capless));
348
349 snd_soc_dapm_add_routes(codec, audio_paths_capless,
350 ARRAY_SIZE(audio_paths_capless));
351 } else {
352 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_out3,
353 ARRAY_SIZE(wm8960_dapm_widgets_out3));
354
355 snd_soc_dapm_add_routes(codec, audio_paths_out3,
356 ARRAY_SIZE(audio_paths_out3));
357 }
358
359 /* We need to power up the headphone output stage out of
360 * sequence for capless mode. To save scanning the widget
361 * list each time to find the desired power state do so now
362 * and save the result.
363 */
364 list_for_each_entry(w, &codec->dapm_widgets, list) {
365 if (strcmp(w->name, "LOUT1 PGA") == 0)
366 wm8960->lout1 = w;
367 if (strcmp(w->name, "ROUT1 PGA") == 0)
368 wm8960->rout1 = w;
369 if (strcmp(w->name, "OUT3 VMID") == 0)
370 wm8960->out3 = w;
371 }
372
311 return 0; 373 return 0;
312} 374}
313 375
@@ -408,10 +470,9 @@ static int wm8960_mute(struct snd_soc_dai *dai, int mute)
408 return 0; 470 return 0;
409} 471}
410 472
411static int wm8960_set_bias_level(struct snd_soc_codec *codec, 473static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
412 enum snd_soc_bias_level level) 474 enum snd_soc_bias_level level)
413{ 475{
414 struct wm8960_data *pdata = codec->dev->platform_data;
415 u16 reg; 476 u16 reg;
416 477
417 switch (level) { 478 switch (level) {
@@ -430,18 +491,8 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
430 if (codec->bias_level == SND_SOC_BIAS_OFF) { 491 if (codec->bias_level == SND_SOC_BIAS_OFF) {
431 /* Enable anti-pop features */ 492 /* Enable anti-pop features */
432 snd_soc_write(codec, WM8960_APOP1, 493 snd_soc_write(codec, WM8960_APOP1,
433 WM8960_POBCTRL | WM8960_SOFT_ST | 494 WM8960_POBCTRL | WM8960_SOFT_ST |
434 WM8960_BUFDCOPEN | WM8960_BUFIOEN); 495 WM8960_BUFDCOPEN | WM8960_BUFIOEN);
435
436 /* Discharge HP output */
437 reg = WM8960_DISOP;
438 if (pdata)
439 reg |= pdata->dres << 4;
440 snd_soc_write(codec, WM8960_APOP2, reg);
441
442 msleep(400);
443
444 snd_soc_write(codec, WM8960_APOP2, 0);
445 496
446 /* Enable & ramp VMID at 2x50k */ 497 /* Enable & ramp VMID at 2x50k */
447 reg = snd_soc_read(codec, WM8960_POWER1); 498 reg = snd_soc_read(codec, WM8960_POWER1);
@@ -472,8 +523,101 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
472 /* Disable VMID and VREF, let them discharge */ 523 /* Disable VMID and VREF, let them discharge */
473 snd_soc_write(codec, WM8960_POWER1, 0); 524 snd_soc_write(codec, WM8960_POWER1, 0);
474 msleep(600); 525 msleep(600);
526 break;
527 }
528
529 codec->bias_level = level;
530
531 return 0;
532}
533
534static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
535 enum snd_soc_bias_level level)
536{
537 struct wm8960_priv *wm8960 = codec->private_data;
538 int reg;
539
540 switch (level) {
541 case SND_SOC_BIAS_ON:
542 break;
543
544 case SND_SOC_BIAS_PREPARE:
545 switch (codec->bias_level) {
546 case SND_SOC_BIAS_STANDBY:
547 /* Enable anti pop mode */
548 snd_soc_update_bits(codec, WM8960_APOP1,
549 WM8960_POBCTRL | WM8960_SOFT_ST |
550 WM8960_BUFDCOPEN,
551 WM8960_POBCTRL | WM8960_SOFT_ST |
552 WM8960_BUFDCOPEN);
553
554 /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
555 reg = 0;
556 if (wm8960->lout1 && wm8960->lout1->power)
557 reg |= WM8960_PWR2_LOUT1;
558 if (wm8960->rout1 && wm8960->rout1->power)
559 reg |= WM8960_PWR2_ROUT1;
560 if (wm8960->out3 && wm8960->out3->power)
561 reg |= WM8960_PWR2_OUT3;
562 snd_soc_update_bits(codec, WM8960_POWER2,
563 WM8960_PWR2_LOUT1 |
564 WM8960_PWR2_ROUT1 |
565 WM8960_PWR2_OUT3, reg);
566
567 /* Enable VMID at 2*50k */
568 snd_soc_update_bits(codec, WM8960_POWER1,
569 WM8960_VMID_MASK, 0x80);
570
571 /* Ramp */
572 msleep(100);
573
574 /* Enable VREF */
575 snd_soc_update_bits(codec, WM8960_POWER1,
576 WM8960_VREF, WM8960_VREF);
577
578 msleep(100);
579 break;
580
581 case SND_SOC_BIAS_ON:
582 /* Enable anti-pop mode */
583 snd_soc_update_bits(codec, WM8960_APOP1,
584 WM8960_POBCTRL | WM8960_SOFT_ST |
585 WM8960_BUFDCOPEN,
586 WM8960_POBCTRL | WM8960_SOFT_ST |
587 WM8960_BUFDCOPEN);
588
589 /* Disable VMID and VREF */
590 snd_soc_update_bits(codec, WM8960_POWER1,
591 WM8960_VREF | WM8960_VMID_MASK, 0);
592 break;
593
594 default:
595 break;
596 }
597 break;
598
599 case SND_SOC_BIAS_STANDBY:
600 switch (codec->bias_level) {
601 case SND_SOC_BIAS_PREPARE:
602 /* Disable HP discharge */
603 snd_soc_update_bits(codec, WM8960_APOP2,
604 WM8960_DISOP | WM8960_DRES_MASK,
605 0);
606
607 /* Disable anti-pop features */
608 snd_soc_update_bits(codec, WM8960_APOP1,
609 WM8960_POBCTRL | WM8960_SOFT_ST |
610 WM8960_BUFDCOPEN,
611 WM8960_POBCTRL | WM8960_SOFT_ST |
612 WM8960_BUFDCOPEN);
613 break;
614
615 default:
616 break;
617 }
618 break;
475 619
476 snd_soc_write(codec, WM8960_APOP1, 0); 620 case SND_SOC_BIAS_OFF:
477 break; 621 break;
478 } 622 }
479 623
@@ -663,7 +807,7 @@ static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
663 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 807 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
664 struct snd_soc_codec *codec = socdev->card->codec; 808 struct snd_soc_codec *codec = socdev->card->codec;
665 809
666 wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF); 810 codec->set_bias_level(codec, SND_SOC_BIAS_OFF);
667 return 0; 811 return 0;
668} 812}
669 813
@@ -682,8 +826,8 @@ static int wm8960_resume(struct platform_device *pdev)
682 codec->hw_write(codec->control_data, data, 2); 826 codec->hw_write(codec->control_data, data, 2);
683 } 827 }
684 828
685 wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 829 codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
686 wm8960_set_bias_level(codec, codec->suspend_bias_level); 830 codec->set_bias_level(codec, codec->suspend_bias_level);
687 return 0; 831 return 0;
688} 832}
689 833
@@ -753,6 +897,8 @@ static int wm8960_register(struct wm8960_priv *wm8960,
753 goto err; 897 goto err;
754 } 898 }
755 899
900 codec->set_bias_level = wm8960_set_bias_level_out3;
901
756 if (!pdata) { 902 if (!pdata) {
757 dev_warn(codec->dev, "No platform data supplied\n"); 903 dev_warn(codec->dev, "No platform data supplied\n");
758 } else { 904 } else {
@@ -760,6 +906,9 @@ static int wm8960_register(struct wm8960_priv *wm8960,
760 dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres); 906 dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
761 pdata->dres = 0; 907 pdata->dres = 0;
762 } 908 }
909
910 if (pdata->capless)
911 codec->set_bias_level = wm8960_set_bias_level_capless;
763 } 912 }
764 913
765 mutex_init(&codec->mutex); 914 mutex_init(&codec->mutex);
@@ -770,7 +919,6 @@ static int wm8960_register(struct wm8960_priv *wm8960,
770 codec->name = "WM8960"; 919 codec->name = "WM8960";
771 codec->owner = THIS_MODULE; 920 codec->owner = THIS_MODULE;
772 codec->bias_level = SND_SOC_BIAS_OFF; 921 codec->bias_level = SND_SOC_BIAS_OFF;
773 codec->set_bias_level = wm8960_set_bias_level;
774 codec->dai = &wm8960_dai; 922 codec->dai = &wm8960_dai;
775 codec->num_dai = 1; 923 codec->num_dai = 1;
776 codec->reg_cache_size = WM8960_CACHEREGNUM; 924 codec->reg_cache_size = WM8960_CACHEREGNUM;
@@ -792,7 +940,7 @@ static int wm8960_register(struct wm8960_priv *wm8960,
792 940
793 wm8960_dai.dev = codec->dev; 941 wm8960_dai.dev = codec->dev;
794 942
795 wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 943 codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
796 944
797 /* Latch the update bits */ 945 /* Latch the update bits */
798 reg = snd_soc_read(codec, WM8960_LINVOL); 946 reg = snd_soc_read(codec, WM8960_LINVOL);
@@ -841,7 +989,7 @@ err:
841 989
842static void wm8960_unregister(struct wm8960_priv *wm8960) 990static void wm8960_unregister(struct wm8960_priv *wm8960)
843{ 991{
844 wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF); 992 wm8960->codec.set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
845 snd_soc_unregister_dai(&wm8960_dai); 993 snd_soc_unregister_dai(&wm8960_dai);
846 snd_soc_unregister_codec(&wm8960->codec); 994 snd_soc_unregister_codec(&wm8960->codec);
847 kfree(wm8960); 995 kfree(wm8960);