diff options
Diffstat (limited to 'sound/soc/codecs/wm8770.c')
-rw-r--r-- | sound/soc/codecs/wm8770.c | 223 |
1 files changed, 110 insertions, 113 deletions
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index c7c0034d3966..89a18d82f303 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/of_device.h> | 17 | #include <linux/of_device.h> |
18 | #include <linux/pm.h> | 18 | #include <linux/pm.h> |
19 | #include <linux/spi/spi.h> | 19 | #include <linux/spi/spi.h> |
20 | #include <linux/regmap.h> | ||
20 | #include <linux/regulator/consumer.h> | 21 | #include <linux/regulator/consumer.h> |
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
22 | #include <sound/core.h> | 23 | #include <sound/core.h> |
@@ -35,19 +36,52 @@ static const char *wm8770_supply_names[WM8770_NUM_SUPPLIES] = { | |||
35 | "DVDD" | 36 | "DVDD" |
36 | }; | 37 | }; |
37 | 38 | ||
38 | static const u16 wm8770_reg_defs[WM8770_CACHEREGNUM] = { | 39 | static const struct reg_default wm8770_reg_defaults[] = { |
39 | 0x7f, 0x7f, 0x7f, 0x7f, | 40 | { 0, 0x7f }, |
40 | 0x7f, 0x7f, 0x7f, 0x7f, | 41 | { 1, 0x7f }, |
41 | 0x7f, 0xff, 0xff, 0xff, | 42 | { 2, 0x7f }, |
42 | 0xff, 0xff, 0xff, 0xff, | 43 | { 3, 0x7f }, |
43 | 0xff, 0xff, 0, 0x90, 0, | 44 | { 4, 0x7f }, |
44 | 0, 0x22, 0x22, 0x3e, | 45 | { 5, 0x7f }, |
45 | 0xc, 0xc, 0x100, 0x189, | 46 | { 6, 0x7f }, |
46 | 0x189, 0x8770 | 47 | { 7, 0x7f }, |
48 | { 8, 0x7f }, | ||
49 | { 9, 0xff }, | ||
50 | { 10, 0xff }, | ||
51 | { 11, 0xff }, | ||
52 | { 12, 0xff }, | ||
53 | { 13, 0xff }, | ||
54 | { 14, 0xff }, | ||
55 | { 15, 0xff }, | ||
56 | { 16, 0xff }, | ||
57 | { 17, 0xff }, | ||
58 | { 18, 0 }, | ||
59 | { 19, 0x90 }, | ||
60 | { 20, 0 }, | ||
61 | { 21, 0 }, | ||
62 | { 22, 0x22 }, | ||
63 | { 23, 0x22 }, | ||
64 | { 24, 0x3e }, | ||
65 | { 25, 0xc }, | ||
66 | { 26, 0xc }, | ||
67 | { 27, 0x100 }, | ||
68 | { 28, 0x189 }, | ||
69 | { 29, 0x189 }, | ||
70 | { 30, 0x8770 }, | ||
47 | }; | 71 | }; |
48 | 72 | ||
73 | static bool wm8770_volatile_reg(struct device *dev, unsigned int reg) | ||
74 | { | ||
75 | switch (reg) { | ||
76 | case WM8770_RESET: | ||
77 | return true; | ||
78 | default: | ||
79 | return false; | ||
80 | } | ||
81 | } | ||
82 | |||
49 | struct wm8770_priv { | 83 | struct wm8770_priv { |
50 | enum snd_soc_control_type control_type; | 84 | struct regmap *regmap; |
51 | struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES]; | 85 | struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES]; |
52 | struct notifier_block disable_nb[WM8770_NUM_SUPPLIES]; | 86 | struct notifier_block disable_nb[WM8770_NUM_SUPPLIES]; |
53 | struct snd_soc_codec *codec; | 87 | struct snd_soc_codec *codec; |
@@ -71,7 +105,7 @@ static int wm8770_regulator_event_##n(struct notifier_block *nb, \ | |||
71 | struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \ | 105 | struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \ |
72 | disable_nb[n]); \ | 106 | disable_nb[n]); \ |
73 | if (event & REGULATOR_EVENT_DISABLE) { \ | 107 | if (event & REGULATOR_EVENT_DISABLE) { \ |
74 | wm8770->codec->cache_sync = 1; \ | 108 | regcache_mark_dirty(wm8770->regmap); \ |
75 | } \ | 109 | } \ |
76 | return 0; \ | 110 | return 0; \ |
77 | } | 111 | } |
@@ -466,24 +500,6 @@ static int wm8770_set_sysclk(struct snd_soc_dai *dai, | |||
466 | return 0; | 500 | return 0; |
467 | } | 501 | } |
468 | 502 | ||
469 | static void wm8770_sync_cache(struct snd_soc_codec *codec) | ||
470 | { | ||
471 | int i; | ||
472 | u16 *cache; | ||
473 | |||
474 | if (!codec->cache_sync) | ||
475 | return; | ||
476 | |||
477 | codec->cache_only = 0; | ||
478 | cache = codec->reg_cache; | ||
479 | for (i = 0; i < codec->driver->reg_cache_size; i++) { | ||
480 | if (i == WM8770_RESET || cache[i] == wm8770_reg_defs[i]) | ||
481 | continue; | ||
482 | snd_soc_write(codec, i, cache[i]); | ||
483 | } | ||
484 | codec->cache_sync = 0; | ||
485 | } | ||
486 | |||
487 | static int wm8770_set_bias_level(struct snd_soc_codec *codec, | 503 | static int wm8770_set_bias_level(struct snd_soc_codec *codec, |
488 | enum snd_soc_bias_level level) | 504 | enum snd_soc_bias_level level) |
489 | { | 505 | { |
@@ -507,7 +523,9 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec, | |||
507 | ret); | 523 | ret); |
508 | return ret; | 524 | return ret; |
509 | } | 525 | } |
510 | wm8770_sync_cache(codec); | 526 | |
527 | regcache_sync(wm8770->regmap); | ||
528 | |||
511 | /* global powerup */ | 529 | /* global powerup */ |
512 | snd_soc_write(codec, WM8770_PWDNCTRL, 0); | 530 | snd_soc_write(codec, WM8770_PWDNCTRL, 0); |
513 | } | 531 | } |
@@ -554,68 +572,25 @@ static struct snd_soc_dai_driver wm8770_dai = { | |||
554 | .symmetric_rates = 1 | 572 | .symmetric_rates = 1 |
555 | }; | 573 | }; |
556 | 574 | ||
557 | #ifdef CONFIG_PM | ||
558 | static int wm8770_suspend(struct snd_soc_codec *codec) | ||
559 | { | ||
560 | wm8770_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static int wm8770_resume(struct snd_soc_codec *codec) | ||
565 | { | ||
566 | wm8770_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
567 | return 0; | ||
568 | } | ||
569 | #else | ||
570 | #define wm8770_suspend NULL | ||
571 | #define wm8770_resume NULL | ||
572 | #endif | ||
573 | |||
574 | static int wm8770_probe(struct snd_soc_codec *codec) | 575 | static int wm8770_probe(struct snd_soc_codec *codec) |
575 | { | 576 | { |
576 | struct wm8770_priv *wm8770; | 577 | struct wm8770_priv *wm8770; |
577 | int ret; | 578 | int ret; |
578 | int i; | ||
579 | 579 | ||
580 | wm8770 = snd_soc_codec_get_drvdata(codec); | 580 | wm8770 = snd_soc_codec_get_drvdata(codec); |
581 | wm8770->codec = codec; | 581 | wm8770->codec = codec; |
582 | 582 | ||
583 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8770->control_type); | 583 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); |
584 | if (ret < 0) { | 584 | if (ret < 0) { |
585 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 585 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
586 | return ret; | 586 | return ret; |
587 | } | 587 | } |
588 | 588 | ||
589 | for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) | ||
590 | wm8770->supplies[i].supply = wm8770_supply_names[i]; | ||
591 | |||
592 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8770->supplies), | ||
593 | wm8770->supplies); | ||
594 | if (ret) { | ||
595 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); | ||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0; | ||
600 | wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1; | ||
601 | wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2; | ||
602 | |||
603 | /* This should really be moved into the regulator core */ | ||
604 | for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { | ||
605 | ret = regulator_register_notifier(wm8770->supplies[i].consumer, | ||
606 | &wm8770->disable_nb[i]); | ||
607 | if (ret) { | ||
608 | dev_err(codec->dev, | ||
609 | "Failed to register regulator notifier: %d\n", | ||
610 | ret); | ||
611 | } | ||
612 | } | ||
613 | |||
614 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), | 589 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), |
615 | wm8770->supplies); | 590 | wm8770->supplies); |
616 | if (ret) { | 591 | if (ret) { |
617 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | 592 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); |
618 | goto err_reg_get; | 593 | return ret; |
619 | } | 594 | } |
620 | 595 | ||
621 | ret = wm8770_reset(codec); | 596 | ret = wm8770_reset(codec); |
@@ -624,8 +599,6 @@ static int wm8770_probe(struct snd_soc_codec *codec) | |||
624 | goto err_reg_enable; | 599 | goto err_reg_enable; |
625 | } | 600 | } |
626 | 601 | ||
627 | wm8770_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
628 | |||
629 | /* latch the volume update bits */ | 602 | /* latch the volume update bits */ |
630 | snd_soc_update_bits(codec, WM8770_MSDIGVOL, 0x100, 0x100); | 603 | snd_soc_update_bits(codec, WM8770_MSDIGVOL, 0x100, 0x100); |
631 | snd_soc_update_bits(codec, WM8770_MSALGVOL, 0x100, 0x100); | 604 | snd_soc_update_bits(codec, WM8770_MSALGVOL, 0x100, 0x100); |
@@ -641,46 +614,22 @@ static int wm8770_probe(struct snd_soc_codec *codec) | |||
641 | /* mute all DACs */ | 614 | /* mute all DACs */ |
642 | snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10, 0x10); | 615 | snd_soc_update_bits(codec, WM8770_DACMUTE, 0x10, 0x10); |
643 | 616 | ||
644 | snd_soc_add_codec_controls(codec, wm8770_snd_controls, | ||
645 | ARRAY_SIZE(wm8770_snd_controls)); | ||
646 | snd_soc_dapm_new_controls(&codec->dapm, wm8770_dapm_widgets, | ||
647 | ARRAY_SIZE(wm8770_dapm_widgets)); | ||
648 | snd_soc_dapm_add_routes(&codec->dapm, wm8770_intercon, | ||
649 | ARRAY_SIZE(wm8770_intercon)); | ||
650 | return 0; | ||
651 | |||
652 | err_reg_enable: | 617 | err_reg_enable: |
653 | regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); | 618 | regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); |
654 | err_reg_get: | ||
655 | regulator_bulk_free(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); | ||
656 | return ret; | 619 | return ret; |
657 | } | 620 | } |
658 | 621 | ||
659 | static int wm8770_remove(struct snd_soc_codec *codec) | ||
660 | { | ||
661 | struct wm8770_priv *wm8770; | ||
662 | int i; | ||
663 | |||
664 | wm8770 = snd_soc_codec_get_drvdata(codec); | ||
665 | wm8770_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
666 | |||
667 | for (i = 0; i < ARRAY_SIZE(wm8770->supplies); ++i) | ||
668 | regulator_unregister_notifier(wm8770->supplies[i].consumer, | ||
669 | &wm8770->disable_nb[i]); | ||
670 | regulator_bulk_free(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static struct snd_soc_codec_driver soc_codec_dev_wm8770 = { | 622 | static struct snd_soc_codec_driver soc_codec_dev_wm8770 = { |
675 | .probe = wm8770_probe, | 623 | .probe = wm8770_probe, |
676 | .remove = wm8770_remove, | ||
677 | .suspend = wm8770_suspend, | ||
678 | .resume = wm8770_resume, | ||
679 | .set_bias_level = wm8770_set_bias_level, | 624 | .set_bias_level = wm8770_set_bias_level, |
680 | .idle_bias_off = true, | 625 | .idle_bias_off = true, |
681 | .reg_cache_size = ARRAY_SIZE(wm8770_reg_defs), | 626 | |
682 | .reg_word_size = sizeof (u16), | 627 | .controls = wm8770_snd_controls, |
683 | .reg_cache_default = wm8770_reg_defs | 628 | .num_controls = ARRAY_SIZE(wm8770_snd_controls), |
629 | .dapm_widgets = wm8770_dapm_widgets, | ||
630 | .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets), | ||
631 | .dapm_routes = wm8770_intercon, | ||
632 | .num_dapm_routes = ARRAY_SIZE(wm8770_intercon), | ||
684 | }; | 633 | }; |
685 | 634 | ||
686 | static const struct of_device_id wm8770_of_match[] = { | 635 | static const struct of_device_id wm8770_of_match[] = { |
@@ -689,17 +638,57 @@ static const struct of_device_id wm8770_of_match[] = { | |||
689 | }; | 638 | }; |
690 | MODULE_DEVICE_TABLE(of, wm8770_of_match); | 639 | MODULE_DEVICE_TABLE(of, wm8770_of_match); |
691 | 640 | ||
692 | static int __devinit wm8770_spi_probe(struct spi_device *spi) | 641 | static const struct regmap_config wm8770_regmap = { |
642 | .reg_bits = 7, | ||
643 | .val_bits = 9, | ||
644 | .max_register = WM8770_RESET, | ||
645 | |||
646 | .reg_defaults = wm8770_reg_defaults, | ||
647 | .num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults), | ||
648 | .cache_type = REGCACHE_RBTREE, | ||
649 | |||
650 | .volatile_reg = wm8770_volatile_reg, | ||
651 | }; | ||
652 | |||
653 | static int wm8770_spi_probe(struct spi_device *spi) | ||
693 | { | 654 | { |
694 | struct wm8770_priv *wm8770; | 655 | struct wm8770_priv *wm8770; |
695 | int ret; | 656 | int ret, i; |
696 | 657 | ||
697 | wm8770 = devm_kzalloc(&spi->dev, sizeof(struct wm8770_priv), | 658 | wm8770 = devm_kzalloc(&spi->dev, sizeof(struct wm8770_priv), |
698 | GFP_KERNEL); | 659 | GFP_KERNEL); |
699 | if (!wm8770) | 660 | if (!wm8770) |
700 | return -ENOMEM; | 661 | return -ENOMEM; |
701 | 662 | ||
702 | wm8770->control_type = SND_SOC_SPI; | 663 | for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) |
664 | wm8770->supplies[i].supply = wm8770_supply_names[i]; | ||
665 | |||
666 | ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8770->supplies), | ||
667 | wm8770->supplies); | ||
668 | if (ret) { | ||
669 | dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); | ||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0; | ||
674 | wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1; | ||
675 | wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2; | ||
676 | |||
677 | /* This should really be moved into the regulator core */ | ||
678 | for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { | ||
679 | ret = regulator_register_notifier(wm8770->supplies[i].consumer, | ||
680 | &wm8770->disable_nb[i]); | ||
681 | if (ret) { | ||
682 | dev_err(&spi->dev, | ||
683 | "Failed to register regulator notifier: %d\n", | ||
684 | ret); | ||
685 | } | ||
686 | } | ||
687 | |||
688 | wm8770->regmap = devm_regmap_init_spi(spi, &wm8770_regmap); | ||
689 | if (IS_ERR(wm8770->regmap)) | ||
690 | return PTR_ERR(wm8770->regmap); | ||
691 | |||
703 | spi_set_drvdata(spi, wm8770); | 692 | spi_set_drvdata(spi, wm8770); |
704 | 693 | ||
705 | ret = snd_soc_register_codec(&spi->dev, | 694 | ret = snd_soc_register_codec(&spi->dev, |
@@ -708,9 +697,17 @@ static int __devinit wm8770_spi_probe(struct spi_device *spi) | |||
708 | return ret; | 697 | return ret; |
709 | } | 698 | } |
710 | 699 | ||
711 | static int __devexit wm8770_spi_remove(struct spi_device *spi) | 700 | static int wm8770_spi_remove(struct spi_device *spi) |
712 | { | 701 | { |
702 | struct wm8770_priv *wm8770 = spi_get_drvdata(spi); | ||
703 | int i; | ||
704 | |||
705 | for (i = 0; i < ARRAY_SIZE(wm8770->supplies); ++i) | ||
706 | regulator_unregister_notifier(wm8770->supplies[i].consumer, | ||
707 | &wm8770->disable_nb[i]); | ||
708 | |||
713 | snd_soc_unregister_codec(&spi->dev); | 709 | snd_soc_unregister_codec(&spi->dev); |
710 | |||
714 | return 0; | 711 | return 0; |
715 | } | 712 | } |
716 | 713 | ||
@@ -721,7 +718,7 @@ static struct spi_driver wm8770_spi_driver = { | |||
721 | .of_match_table = wm8770_of_match, | 718 | .of_match_table = wm8770_of_match, |
722 | }, | 719 | }, |
723 | .probe = wm8770_spi_probe, | 720 | .probe = wm8770_spi_probe, |
724 | .remove = __devexit_p(wm8770_spi_remove) | 721 | .remove = wm8770_spi_remove |
725 | }; | 722 | }; |
726 | 723 | ||
727 | module_spi_driver(wm8770_spi_driver); | 724 | module_spi_driver(wm8770_spi_driver); |