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.c216
1 files changed, 181 insertions, 35 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index d07bcc1e1c60..7233cc68435a 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -15,6 +15,7 @@
15#include <linux/pm.h> 15#include <linux/pm.h>
16#include <linux/i2c.h> 16#include <linux/i2c.h>
17#include <linux/platform_device.h> 17#include <linux/platform_device.h>
18#include <linux/slab.h>
18#include <sound/core.h> 19#include <sound/core.h>
19#include <sound/pcm.h> 20#include <sound/pcm.h>
20#include <sound/pcm_params.h> 21#include <sound/pcm_params.h>
@@ -22,6 +23,7 @@
22#include <sound/soc-dapm.h> 23#include <sound/soc-dapm.h>
23#include <sound/initval.h> 24#include <sound/initval.h>
24#include <sound/tlv.h> 25#include <sound/tlv.h>
26#include <sound/wm8960.h>
25 27
26#include "wm8960.h" 28#include "wm8960.h"
27 29
@@ -30,8 +32,14 @@
30struct snd_soc_codec_device soc_codec_dev_wm8960; 32struct snd_soc_codec_device soc_codec_dev_wm8960;
31 33
32/* R25 - Power 1 */ 34/* R25 - Power 1 */
35#define WM8960_VMID_MASK 0x180
33#define WM8960_VREF 0x40 36#define WM8960_VREF 0x40
34 37
38/* R26 - Power 2 */
39#define WM8960_PWR2_LOUT1 0x40
40#define WM8960_PWR2_ROUT1 0x20
41#define WM8960_PWR2_OUT3 0x02
42
35/* R28 - Anti-pop 1 */ 43/* R28 - Anti-pop 1 */
36#define WM8960_POBCTRL 0x80 44#define WM8960_POBCTRL 0x80
37#define WM8960_BUFDCOPEN 0x10 45#define WM8960_BUFDCOPEN 0x10
@@ -41,6 +49,7 @@ struct snd_soc_codec_device soc_codec_dev_wm8960;
41 49
42/* R29 - Anti-pop 2 */ 50/* R29 - Anti-pop 2 */
43#define WM8960_DISOP 0x40 51#define WM8960_DISOP 0x40
52#define WM8960_DRES_MASK 0x30
44 53
45/* 54/*
46 * wm8960 register cache 55 * wm8960 register cache
@@ -67,6 +76,9 @@ static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
67struct wm8960_priv { 76struct wm8960_priv {
68 u16 reg_cache[WM8960_CACHEREGNUM]; 77 u16 reg_cache[WM8960_CACHEREGNUM];
69 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;
70}; 82};
71 83
72#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) 84#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)
@@ -225,10 +237,6 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
225 &wm8960_routput_mixer[0], 237 &wm8960_routput_mixer[0],
226 ARRAY_SIZE(wm8960_routput_mixer)), 238 ARRAY_SIZE(wm8960_routput_mixer)),
227 239
228SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
229 &wm8960_mono_out[0],
230 ARRAY_SIZE(wm8960_mono_out)),
231
232SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), 240SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
233SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), 241SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
234 242
@@ -247,6 +255,17 @@ SND_SOC_DAPM_OUTPUT("SPK_RN"),
247SND_SOC_DAPM_OUTPUT("OUT3"), 255SND_SOC_DAPM_OUTPUT("OUT3"),
248}; 256};
249 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
250static const struct snd_soc_dapm_route audio_paths[] = { 269static const struct snd_soc_dapm_route audio_paths[] = {
251 { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, 270 { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
252 { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, 271 { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
@@ -277,9 +296,6 @@ static const struct snd_soc_dapm_route audio_paths[] = {
277 { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } , 296 { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
278 { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, 297 { "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
279 298
280 { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
281 { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
282
283 { "LOUT1 PGA", NULL, "Left Output Mixer" }, 299 { "LOUT1 PGA", NULL, "Left Output Mixer" },
284 { "ROUT1 PGA", NULL, "Right Output Mixer" }, 300 { "ROUT1 PGA", NULL, "Right Output Mixer" },
285 301
@@ -296,17 +312,65 @@ static const struct snd_soc_dapm_route audio_paths[] = {
296 { "SPK_LP", NULL, "Left Speaker Output" }, 312 { "SPK_LP", NULL, "Left Speaker Output" },
297 { "SPK_RN", NULL, "Right Speaker Output" }, 313 { "SPK_RN", NULL, "Right Speaker Output" },
298 { "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" },
299 320
300 { "OUT3", NULL, "Mono Output Mixer", } 321 { "OUT3", NULL, "Mono Output Mixer", }
301}; 322};
302 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
303static int wm8960_add_widgets(struct snd_soc_codec *codec) 332static int wm8960_add_widgets(struct snd_soc_codec *codec)
304{ 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
305 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets, 338 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
306 ARRAY_SIZE(wm8960_dapm_widgets)); 339 ARRAY_SIZE(wm8960_dapm_widgets));
307 340
308 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));
309 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
310 return 0; 374 return 0;
311} 375}
312 376
@@ -407,10 +471,9 @@ static int wm8960_mute(struct snd_soc_dai *dai, int mute)
407 return 0; 471 return 0;
408} 472}
409 473
410static int wm8960_set_bias_level(struct snd_soc_codec *codec, 474static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
411 enum snd_soc_bias_level level) 475 enum snd_soc_bias_level level)
412{ 476{
413 struct wm8960_data *pdata = codec->dev->platform_data;
414 u16 reg; 477 u16 reg;
415 478
416 switch (level) { 479 switch (level) {
@@ -429,18 +492,8 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
429 if (codec->bias_level == SND_SOC_BIAS_OFF) { 492 if (codec->bias_level == SND_SOC_BIAS_OFF) {
430 /* Enable anti-pop features */ 493 /* Enable anti-pop features */
431 snd_soc_write(codec, WM8960_APOP1, 494 snd_soc_write(codec, WM8960_APOP1,
432 WM8960_POBCTRL | WM8960_SOFT_ST | 495 WM8960_POBCTRL | WM8960_SOFT_ST |
433 WM8960_BUFDCOPEN | WM8960_BUFIOEN); 496 WM8960_BUFDCOPEN | WM8960_BUFIOEN);
434
435 /* Discharge HP output */
436 reg = WM8960_DISOP;
437 if (pdata)
438 reg |= pdata->dres << 4;
439 snd_soc_write(codec, WM8960_APOP2, reg);
440
441 msleep(400);
442
443 snd_soc_write(codec, WM8960_APOP2, 0);
444 497
445 /* Enable & ramp VMID at 2x50k */ 498 /* Enable & ramp VMID at 2x50k */
446 reg = snd_soc_read(codec, WM8960_POWER1); 499 reg = snd_soc_read(codec, WM8960_POWER1);
@@ -471,8 +524,101 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
471 /* Disable VMID and VREF, let them discharge */ 524 /* Disable VMID and VREF, let them discharge */
472 snd_soc_write(codec, WM8960_POWER1, 0); 525 snd_soc_write(codec, WM8960_POWER1, 0);
473 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;
474 599
475 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:
476 break; 622 break;
477 } 623 }
478 624
@@ -593,10 +739,6 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
593 u16 reg; 739 u16 reg;
594 740
595 switch (div_id) { 741 switch (div_id) {
596 case WM8960_SYSCLKSEL:
597 reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1fe;
598 snd_soc_write(codec, WM8960_CLOCK1, reg | div);
599 break;
600 case WM8960_SYSCLKDIV: 742 case WM8960_SYSCLKDIV:
601 reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; 743 reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
602 snd_soc_write(codec, WM8960_CLOCK1, reg | div); 744 snd_soc_write(codec, WM8960_CLOCK1, reg | div);
@@ -662,7 +804,7 @@ static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
662 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 804 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
663 struct snd_soc_codec *codec = socdev->card->codec; 805 struct snd_soc_codec *codec = socdev->card->codec;
664 806
665 wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF); 807 codec->set_bias_level(codec, SND_SOC_BIAS_OFF);
666 return 0; 808 return 0;
667} 809}
668 810
@@ -681,8 +823,8 @@ static int wm8960_resume(struct platform_device *pdev)
681 codec->hw_write(codec->control_data, data, 2); 823 codec->hw_write(codec->control_data, data, 2);
682 } 824 }
683 825
684 wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 826 codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
685 wm8960_set_bias_level(codec, codec->suspend_bias_level); 827
686 return 0; 828 return 0;
687} 829}
688 830
@@ -752,6 +894,8 @@ static int wm8960_register(struct wm8960_priv *wm8960,
752 goto err; 894 goto err;
753 } 895 }
754 896
897 codec->set_bias_level = wm8960_set_bias_level_out3;
898
755 if (!pdata) { 899 if (!pdata) {
756 dev_warn(codec->dev, "No platform data supplied\n"); 900 dev_warn(codec->dev, "No platform data supplied\n");
757 } else { 901 } else {
@@ -759,17 +903,19 @@ static int wm8960_register(struct wm8960_priv *wm8960,
759 dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres); 903 dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
760 pdata->dres = 0; 904 pdata->dres = 0;
761 } 905 }
906
907 if (pdata->capless)
908 codec->set_bias_level = wm8960_set_bias_level_capless;
762 } 909 }
763 910
764 mutex_init(&codec->mutex); 911 mutex_init(&codec->mutex);
765 INIT_LIST_HEAD(&codec->dapm_widgets); 912 INIT_LIST_HEAD(&codec->dapm_widgets);
766 INIT_LIST_HEAD(&codec->dapm_paths); 913 INIT_LIST_HEAD(&codec->dapm_paths);
767 914
768 codec->private_data = wm8960; 915 snd_soc_codec_set_drvdata(codec, wm8960);
769 codec->name = "WM8960"; 916 codec->name = "WM8960";
770 codec->owner = THIS_MODULE; 917 codec->owner = THIS_MODULE;
771 codec->bias_level = SND_SOC_BIAS_OFF; 918 codec->bias_level = SND_SOC_BIAS_OFF;
772 codec->set_bias_level = wm8960_set_bias_level;
773 codec->dai = &wm8960_dai; 919 codec->dai = &wm8960_dai;
774 codec->num_dai = 1; 920 codec->num_dai = 1;
775 codec->reg_cache_size = WM8960_CACHEREGNUM; 921 codec->reg_cache_size = WM8960_CACHEREGNUM;
@@ -791,7 +937,7 @@ static int wm8960_register(struct wm8960_priv *wm8960,
791 937
792 wm8960_dai.dev = codec->dev; 938 wm8960_dai.dev = codec->dev;
793 939
794 wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 940 codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
795 941
796 /* Latch the update bits */ 942 /* Latch the update bits */
797 reg = snd_soc_read(codec, WM8960_LINVOL); 943 reg = snd_soc_read(codec, WM8960_LINVOL);
@@ -840,7 +986,7 @@ err:
840 986
841static void wm8960_unregister(struct wm8960_priv *wm8960) 987static void wm8960_unregister(struct wm8960_priv *wm8960)
842{ 988{
843 wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF); 989 wm8960->codec.set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
844 snd_soc_unregister_dai(&wm8960_dai); 990 snd_soc_unregister_dai(&wm8960_dai);
845 snd_soc_unregister_codec(&wm8960->codec); 991 snd_soc_unregister_codec(&wm8960->codec);
846 kfree(wm8960); 992 kfree(wm8960);
@@ -882,7 +1028,7 @@ MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
882 1028
883static struct i2c_driver wm8960_i2c_driver = { 1029static struct i2c_driver wm8960_i2c_driver = {
884 .driver = { 1030 .driver = {
885 .name = "WM8960 I2C Codec", 1031 .name = "wm8960",
886 .owner = THIS_MODULE, 1032 .owner = THIS_MODULE,
887 }, 1033 },
888 .probe = wm8960_i2c_probe, 1034 .probe = wm8960_i2c_probe,