diff options
Diffstat (limited to 'sound/soc/codecs/wm8804.c')
| -rw-r--r-- | sound/soc/codecs/wm8804.c | 281 |
1 files changed, 79 insertions, 202 deletions
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index b2b0e68f707e..1bd4ace29594 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c | |||
| @@ -15,10 +15,7 @@ | |||
| 15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
| 16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 17 | #include <linux/pm.h> | 17 | #include <linux/pm.h> |
| 18 | #include <linux/i2c.h> | ||
| 19 | #include <linux/of_device.h> | 18 | #include <linux/of_device.h> |
| 20 | #include <linux/spi/spi.h> | ||
| 21 | #include <linux/regmap.h> | ||
| 22 | #include <linux/regulator/consumer.h> | 19 | #include <linux/regulator/consumer.h> |
| 23 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
| 24 | #include <sound/core.h> | 21 | #include <sound/core.h> |
| @@ -185,9 +182,9 @@ static bool wm8804_volatile(struct device *dev, unsigned int reg) | |||
| 185 | } | 182 | } |
| 186 | } | 183 | } |
| 187 | 184 | ||
| 188 | static int wm8804_reset(struct snd_soc_codec *codec) | 185 | static int wm8804_reset(struct wm8804_priv *wm8804) |
| 189 | { | 186 | { |
| 190 | return snd_soc_write(codec, WM8804_RST_DEVID1, 0x0); | 187 | return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); |
| 191 | } | 188 | } |
| 192 | 189 | ||
| 193 | static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | 190 | static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
| @@ -518,100 +515,6 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec, | |||
| 518 | return 0; | 515 | return 0; |
| 519 | } | 516 | } |
| 520 | 517 | ||
| 521 | static int wm8804_remove(struct snd_soc_codec *codec) | ||
| 522 | { | ||
| 523 | struct wm8804_priv *wm8804; | ||
| 524 | int i; | ||
| 525 | |||
| 526 | wm8804 = snd_soc_codec_get_drvdata(codec); | ||
| 527 | |||
| 528 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) | ||
| 529 | regulator_unregister_notifier(wm8804->supplies[i].consumer, | ||
| 530 | &wm8804->disable_nb[i]); | ||
| 531 | return 0; | ||
| 532 | } | ||
| 533 | |||
| 534 | static int wm8804_probe(struct snd_soc_codec *codec) | ||
| 535 | { | ||
| 536 | struct wm8804_priv *wm8804; | ||
| 537 | int i, id1, id2, ret; | ||
| 538 | |||
| 539 | wm8804 = snd_soc_codec_get_drvdata(codec); | ||
| 540 | |||
| 541 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) | ||
| 542 | wm8804->supplies[i].supply = wm8804_supply_names[i]; | ||
| 543 | |||
| 544 | ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies), | ||
| 545 | wm8804->supplies); | ||
| 546 | if (ret) { | ||
| 547 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); | ||
| 548 | return ret; | ||
| 549 | } | ||
| 550 | |||
| 551 | wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; | ||
| 552 | wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; | ||
| 553 | |||
| 554 | /* This should really be moved into the regulator core */ | ||
| 555 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { | ||
| 556 | ret = regulator_register_notifier(wm8804->supplies[i].consumer, | ||
| 557 | &wm8804->disable_nb[i]); | ||
| 558 | if (ret != 0) { | ||
| 559 | dev_err(codec->dev, | ||
| 560 | "Failed to register regulator notifier: %d\n", | ||
| 561 | ret); | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), | ||
| 566 | wm8804->supplies); | ||
| 567 | if (ret) { | ||
| 568 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | ||
| 569 | return ret; | ||
| 570 | } | ||
| 571 | |||
| 572 | id1 = snd_soc_read(codec, WM8804_RST_DEVID1); | ||
| 573 | if (id1 < 0) { | ||
| 574 | dev_err(codec->dev, "Failed to read device ID: %d\n", id1); | ||
| 575 | ret = id1; | ||
| 576 | goto err_reg_enable; | ||
| 577 | } | ||
| 578 | |||
| 579 | id2 = snd_soc_read(codec, WM8804_DEVID2); | ||
| 580 | if (id2 < 0) { | ||
| 581 | dev_err(codec->dev, "Failed to read device ID: %d\n", id2); | ||
| 582 | ret = id2; | ||
| 583 | goto err_reg_enable; | ||
| 584 | } | ||
| 585 | |||
| 586 | id2 = (id2 << 8) | id1; | ||
| 587 | |||
| 588 | if (id2 != 0x8805) { | ||
| 589 | dev_err(codec->dev, "Invalid device ID: %#x\n", id2); | ||
| 590 | ret = -EINVAL; | ||
| 591 | goto err_reg_enable; | ||
| 592 | } | ||
| 593 | |||
| 594 | ret = snd_soc_read(codec, WM8804_DEVREV); | ||
| 595 | if (ret < 0) { | ||
| 596 | dev_err(codec->dev, "Failed to read device revision: %d\n", | ||
| 597 | ret); | ||
| 598 | goto err_reg_enable; | ||
| 599 | } | ||
| 600 | dev_info(codec->dev, "revision %c\n", ret + 'A'); | ||
| 601 | |||
| 602 | ret = wm8804_reset(codec); | ||
| 603 | if (ret < 0) { | ||
| 604 | dev_err(codec->dev, "Failed to issue reset: %d\n", ret); | ||
| 605 | goto err_reg_enable; | ||
| 606 | } | ||
| 607 | |||
| 608 | return 0; | ||
| 609 | |||
| 610 | err_reg_enable: | ||
| 611 | regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); | ||
| 612 | return ret; | ||
| 613 | } | ||
| 614 | |||
| 615 | static const struct snd_soc_dai_ops wm8804_dai_ops = { | 518 | static const struct snd_soc_dai_ops wm8804_dai_ops = { |
| 616 | .hw_params = wm8804_hw_params, | 519 | .hw_params = wm8804_hw_params, |
| 617 | .set_fmt = wm8804_set_fmt, | 520 | .set_fmt = wm8804_set_fmt, |
| @@ -649,8 +552,6 @@ static struct snd_soc_dai_driver wm8804_dai = { | |||
| 649 | }; | 552 | }; |
| 650 | 553 | ||
| 651 | static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { | 554 | static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { |
| 652 | .probe = wm8804_probe, | ||
| 653 | .remove = wm8804_remove, | ||
| 654 | .set_bias_level = wm8804_set_bias_level, | 555 | .set_bias_level = wm8804_set_bias_level, |
| 655 | .idle_bias_off = true, | 556 | .idle_bias_off = true, |
| 656 | 557 | ||
| @@ -658,13 +559,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { | |||
| 658 | .num_controls = ARRAY_SIZE(wm8804_snd_controls), | 559 | .num_controls = ARRAY_SIZE(wm8804_snd_controls), |
| 659 | }; | 560 | }; |
| 660 | 561 | ||
| 661 | static const struct of_device_id wm8804_of_match[] = { | 562 | const struct regmap_config wm8804_regmap_config = { |
| 662 | { .compatible = "wlf,wm8804", }, | ||
| 663 | { } | ||
| 664 | }; | ||
| 665 | MODULE_DEVICE_TABLE(of, wm8804_of_match); | ||
| 666 | |||
| 667 | static const struct regmap_config wm8804_regmap_config = { | ||
| 668 | .reg_bits = 8, | 563 | .reg_bits = 8, |
| 669 | .val_bits = 8, | 564 | .val_bits = 8, |
| 670 | 565 | ||
| @@ -675,128 +570,110 @@ static const struct regmap_config wm8804_regmap_config = { | |||
| 675 | .reg_defaults = wm8804_reg_defaults, | 570 | .reg_defaults = wm8804_reg_defaults, |
| 676 | .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), | 571 | .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), |
| 677 | }; | 572 | }; |
| 573 | EXPORT_SYMBOL_GPL(wm8804_regmap_config); | ||
| 678 | 574 | ||
| 679 | #if defined(CONFIG_SPI_MASTER) | 575 | int wm8804_probe(struct device *dev, struct regmap *regmap) |
| 680 | static int wm8804_spi_probe(struct spi_device *spi) | ||
| 681 | { | 576 | { |
| 682 | struct wm8804_priv *wm8804; | 577 | struct wm8804_priv *wm8804; |
| 683 | int ret; | 578 | unsigned int id1, id2; |
| 579 | int i, ret; | ||
| 684 | 580 | ||
| 685 | wm8804 = devm_kzalloc(&spi->dev, sizeof *wm8804, GFP_KERNEL); | 581 | wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL); |
| 686 | if (!wm8804) | 582 | if (!wm8804) |
| 687 | return -ENOMEM; | 583 | return -ENOMEM; |
| 688 | 584 | ||
| 689 | wm8804->regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); | 585 | dev_set_drvdata(dev, wm8804); |
| 690 | if (IS_ERR(wm8804->regmap)) { | 586 | |
| 691 | ret = PTR_ERR(wm8804->regmap); | 587 | wm8804->regmap = regmap; |
| 588 | |||
| 589 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) | ||
| 590 | wm8804->supplies[i].supply = wm8804_supply_names[i]; | ||
| 591 | |||
| 592 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies), | ||
| 593 | wm8804->supplies); | ||
| 594 | if (ret) { | ||
| 595 | dev_err(dev, "Failed to request supplies: %d\n", ret); | ||
| 692 | return ret; | 596 | return ret; |
| 693 | } | 597 | } |
| 694 | 598 | ||
| 695 | spi_set_drvdata(spi, wm8804); | 599 | wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; |
| 600 | wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; | ||
| 696 | 601 | ||
| 697 | ret = snd_soc_register_codec(&spi->dev, | 602 | /* This should really be moved into the regulator core */ |
| 698 | &soc_codec_dev_wm8804, &wm8804_dai, 1); | 603 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { |
| 604 | ret = regulator_register_notifier(wm8804->supplies[i].consumer, | ||
| 605 | &wm8804->disable_nb[i]); | ||
| 606 | if (ret != 0) { | ||
| 607 | dev_err(dev, | ||
| 608 | "Failed to register regulator notifier: %d\n", | ||
| 609 | ret); | ||
| 610 | } | ||
| 611 | } | ||
| 699 | 612 | ||
| 700 | return ret; | 613 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), |
| 701 | } | 614 | wm8804->supplies); |
| 615 | if (ret) { | ||
| 616 | dev_err(dev, "Failed to enable supplies: %d\n", ret); | ||
| 617 | goto err_reg_enable; | ||
| 618 | } | ||
| 702 | 619 | ||
| 703 | static int wm8804_spi_remove(struct spi_device *spi) | 620 | ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); |
| 704 | { | 621 | if (ret < 0) { |
| 705 | snd_soc_unregister_codec(&spi->dev); | 622 | dev_err(dev, "Failed to read device ID: %d\n", ret); |
| 706 | return 0; | 623 | goto err_reg_enable; |
| 707 | } | 624 | } |
| 708 | 625 | ||
| 709 | static struct spi_driver wm8804_spi_driver = { | 626 | ret = regmap_read(regmap, WM8804_DEVID2, &id2); |
| 710 | .driver = { | 627 | if (ret < 0) { |
| 711 | .name = "wm8804", | 628 | dev_err(dev, "Failed to read device ID: %d\n", ret); |
| 712 | .owner = THIS_MODULE, | 629 | goto err_reg_enable; |
| 713 | .of_match_table = wm8804_of_match, | 630 | } |
| 714 | }, | ||
| 715 | .probe = wm8804_spi_probe, | ||
| 716 | .remove = wm8804_spi_remove | ||
| 717 | }; | ||
| 718 | #endif | ||
| 719 | 631 | ||
| 720 | #if IS_ENABLED(CONFIG_I2C) | 632 | id2 = (id2 << 8) | id1; |
| 721 | static int wm8804_i2c_probe(struct i2c_client *i2c, | ||
| 722 | const struct i2c_device_id *id) | ||
| 723 | { | ||
| 724 | struct wm8804_priv *wm8804; | ||
| 725 | int ret; | ||
| 726 | 633 | ||
| 727 | wm8804 = devm_kzalloc(&i2c->dev, sizeof *wm8804, GFP_KERNEL); | 634 | if (id2 != 0x8805) { |
| 728 | if (!wm8804) | 635 | dev_err(dev, "Invalid device ID: %#x\n", id2); |
| 729 | return -ENOMEM; | 636 | ret = -EINVAL; |
| 637 | goto err_reg_enable; | ||
| 638 | } | ||
| 730 | 639 | ||
| 731 | wm8804->regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); | 640 | ret = regmap_read(regmap, WM8804_DEVREV, &id1); |
| 732 | if (IS_ERR(wm8804->regmap)) { | 641 | if (ret < 0) { |
| 733 | ret = PTR_ERR(wm8804->regmap); | 642 | dev_err(dev, "Failed to read device revision: %d\n", |
| 734 | return ret; | 643 | ret); |
| 644 | goto err_reg_enable; | ||
| 735 | } | 645 | } |
| 646 | dev_info(dev, "revision %c\n", id1 + 'A'); | ||
| 736 | 647 | ||
| 737 | i2c_set_clientdata(i2c, wm8804); | 648 | ret = wm8804_reset(wm8804); |
| 649 | if (ret < 0) { | ||
| 650 | dev_err(dev, "Failed to issue reset: %d\n", ret); | ||
| 651 | goto err_reg_enable; | ||
| 652 | } | ||
| 738 | 653 | ||
| 739 | ret = snd_soc_register_codec(&i2c->dev, | 654 | return snd_soc_register_codec(dev, &soc_codec_dev_wm8804, |
| 740 | &soc_codec_dev_wm8804, &wm8804_dai, 1); | 655 | &wm8804_dai, 1); |
| 656 | |||
| 657 | err_reg_enable: | ||
| 658 | regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); | ||
| 741 | return ret; | 659 | return ret; |
| 742 | } | 660 | } |
| 661 | EXPORT_SYMBOL_GPL(wm8804_probe); | ||
| 743 | 662 | ||
| 744 | static int wm8804_i2c_remove(struct i2c_client *i2c) | 663 | void wm8804_remove(struct device *dev) |
| 745 | { | 664 | { |
| 746 | snd_soc_unregister_codec(&i2c->dev); | 665 | struct wm8804_priv *wm8804; |
| 747 | return 0; | 666 | int i; |
| 748 | } | ||
| 749 | |||
| 750 | static const struct i2c_device_id wm8804_i2c_id[] = { | ||
| 751 | { "wm8804", 0 }, | ||
| 752 | { } | ||
| 753 | }; | ||
| 754 | MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); | ||
| 755 | |||
| 756 | static struct i2c_driver wm8804_i2c_driver = { | ||
| 757 | .driver = { | ||
| 758 | .name = "wm8804", | ||
| 759 | .owner = THIS_MODULE, | ||
| 760 | .of_match_table = wm8804_of_match, | ||
| 761 | }, | ||
| 762 | .probe = wm8804_i2c_probe, | ||
| 763 | .remove = wm8804_i2c_remove, | ||
| 764 | .id_table = wm8804_i2c_id | ||
| 765 | }; | ||
| 766 | #endif | ||
| 767 | 667 | ||
| 768 | static int __init wm8804_modinit(void) | 668 | wm8804 = dev_get_drvdata(dev); |
| 769 | { | ||
| 770 | int ret = 0; | ||
| 771 | 669 | ||
| 772 | #if IS_ENABLED(CONFIG_I2C) | 670 | for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) |
| 773 | ret = i2c_add_driver(&wm8804_i2c_driver); | 671 | regulator_unregister_notifier(wm8804->supplies[i].consumer, |
| 774 | if (ret) { | 672 | &wm8804->disable_nb[i]); |
| 775 | printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n", | ||
| 776 | ret); | ||
| 777 | } | ||
| 778 | #endif | ||
| 779 | #if defined(CONFIG_SPI_MASTER) | ||
| 780 | ret = spi_register_driver(&wm8804_spi_driver); | ||
| 781 | if (ret != 0) { | ||
| 782 | printk(KERN_ERR "Failed to register wm8804 SPI driver: %d\n", | ||
| 783 | ret); | ||
| 784 | } | ||
| 785 | #endif | ||
| 786 | return ret; | ||
| 787 | } | ||
| 788 | module_init(wm8804_modinit); | ||
| 789 | 673 | ||
| 790 | static void __exit wm8804_exit(void) | 674 | snd_soc_unregister_codec(dev); |
| 791 | { | ||
| 792 | #if IS_ENABLED(CONFIG_I2C) | ||
| 793 | i2c_del_driver(&wm8804_i2c_driver); | ||
| 794 | #endif | ||
| 795 | #if defined(CONFIG_SPI_MASTER) | ||
| 796 | spi_unregister_driver(&wm8804_spi_driver); | ||
| 797 | #endif | ||
| 798 | } | 675 | } |
| 799 | module_exit(wm8804_exit); | 676 | EXPORT_SYMBOL_GPL(wm8804_remove); |
| 800 | 677 | ||
| 801 | MODULE_DESCRIPTION("ASoC WM8804 driver"); | 678 | MODULE_DESCRIPTION("ASoC WM8804 driver"); |
| 802 | MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); | 679 | MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); |
