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