diff options
author | Mark Brown <broonie@linaro.org> | 2013-11-08 05:43:37 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-11-08 05:43:37 -0500 |
commit | be529a5b7f3490c9c57295cc8c9891a3ab94b250 (patch) | |
tree | e63e7fe82322fc2a0d822ede210f1672b57976e9 /sound | |
parent | 6c61bbf0e844a035736272bd863d724d5f112ac5 (diff) | |
parent | 285d00c11b0a8d0ef63c176f88caab5071c9e80d (diff) |
Merge remote-tracking branch 'asoc/topic/tas5086' into asoc-next
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/tas5086.c | 171 |
1 files changed, 109 insertions, 62 deletions
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 6d31d88f7204..fe4d29d88564 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/i2c.h> | 37 | #include <linux/i2c.h> |
38 | #include <linux/regmap.h> | 38 | #include <linux/regmap.h> |
39 | #include <linux/spi/spi.h> | 39 | #include <linux/spi/spi.h> |
40 | #include <linux/of.h> | ||
40 | #include <linux/of_device.h> | 41 | #include <linux/of_device.h> |
41 | #include <linux/of_gpio.h> | 42 | #include <linux/of_gpio.h> |
42 | #include <sound/pcm.h> | 43 | #include <sound/pcm.h> |
@@ -244,6 +245,8 @@ struct tas5086_private { | |||
244 | unsigned int mclk, sclk; | 245 | unsigned int mclk, sclk; |
245 | unsigned int format; | 246 | unsigned int format; |
246 | bool deemph; | 247 | bool deemph; |
248 | unsigned int charge_period; | ||
249 | unsigned int pwm_start_mid_z; | ||
247 | /* Current sample rate for de-emphasis control */ | 250 | /* Current sample rate for de-emphasis control */ |
248 | int rate; | 251 | int rate; |
249 | /* GPIO driving Reset pin, if any */ | 252 | /* GPIO driving Reset pin, if any */ |
@@ -456,6 +459,75 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream) | |||
456 | return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val); | 459 | return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val); |
457 | } | 460 | } |
458 | 461 | ||
462 | static void tas5086_reset(struct tas5086_private *priv) | ||
463 | { | ||
464 | if (gpio_is_valid(priv->gpio_nreset)) { | ||
465 | /* Reset codec - minimum assertion time is 400ns */ | ||
466 | gpio_direction_output(priv->gpio_nreset, 0); | ||
467 | udelay(1); | ||
468 | gpio_set_value(priv->gpio_nreset, 1); | ||
469 | |||
470 | /* Codec needs ~15ms to wake up */ | ||
471 | msleep(15); | ||
472 | } | ||
473 | } | ||
474 | |||
475 | /* charge period values in microseconds */ | ||
476 | static const int tas5086_charge_period[] = { | ||
477 | 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200, | ||
478 | 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000, | ||
479 | 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000, | ||
480 | }; | ||
481 | |||
482 | static int tas5086_init(struct device *dev, struct tas5086_private *priv) | ||
483 | { | ||
484 | int ret, i; | ||
485 | |||
486 | /* | ||
487 | * If any of the channels is configured to start in Mid-Z mode, | ||
488 | * configure 'part 1' of the PWM starts to use Mid-Z, and tell | ||
489 | * all configured mid-z channels to start start under 'part 1'. | ||
490 | */ | ||
491 | if (priv->pwm_start_mid_z) | ||
492 | regmap_write(priv->regmap, TAS5086_PWM_START, | ||
493 | TAS5086_PWM_START_MIDZ_FOR_START_1 | | ||
494 | priv->pwm_start_mid_z); | ||
495 | |||
496 | /* lookup and set split-capacitor charge period */ | ||
497 | if (priv->charge_period == 0) { | ||
498 | regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); | ||
499 | } else { | ||
500 | i = index_in_array(tas5086_charge_period, | ||
501 | ARRAY_SIZE(tas5086_charge_period), | ||
502 | priv->charge_period); | ||
503 | if (i >= 0) | ||
504 | regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, | ||
505 | i + 0x08); | ||
506 | else | ||
507 | dev_warn(dev, | ||
508 | "Invalid split-cap charge period of %d ns.\n", | ||
509 | priv->charge_period); | ||
510 | } | ||
511 | |||
512 | /* enable factory trim */ | ||
513 | ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00); | ||
514 | if (ret < 0) | ||
515 | return ret; | ||
516 | |||
517 | /* start all channels */ | ||
518 | ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20); | ||
519 | if (ret < 0) | ||
520 | return ret; | ||
521 | |||
522 | /* mute all channels for now */ | ||
523 | ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE, | ||
524 | TAS5086_SOFT_MUTE_ALL); | ||
525 | if (ret < 0) | ||
526 | return ret; | ||
527 | |||
528 | return 0; | ||
529 | } | ||
530 | |||
459 | /* TAS5086 controls */ | 531 | /* TAS5086 controls */ |
460 | static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1); | 532 | static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1); |
461 | 533 | ||
@@ -691,14 +763,39 @@ static struct snd_soc_dai_driver tas5086_dai = { | |||
691 | }; | 763 | }; |
692 | 764 | ||
693 | #ifdef CONFIG_PM | 765 | #ifdef CONFIG_PM |
766 | static int tas5086_soc_suspend(struct snd_soc_codec *codec) | ||
767 | { | ||
768 | struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); | ||
769 | int ret; | ||
770 | |||
771 | /* Shut down all channels */ | ||
772 | ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x60); | ||
773 | if (ret < 0) | ||
774 | return ret; | ||
775 | |||
776 | return 0; | ||
777 | } | ||
778 | |||
694 | static int tas5086_soc_resume(struct snd_soc_codec *codec) | 779 | static int tas5086_soc_resume(struct snd_soc_codec *codec) |
695 | { | 780 | { |
696 | struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); | 781 | struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); |
782 | int ret; | ||
783 | |||
784 | tas5086_reset(priv); | ||
785 | regcache_mark_dirty(priv->regmap); | ||
786 | |||
787 | ret = tas5086_init(codec->dev, priv); | ||
788 | if (ret < 0) | ||
789 | return ret; | ||
790 | |||
791 | ret = regcache_sync(priv->regmap); | ||
792 | if (ret < 0) | ||
793 | return ret; | ||
697 | 794 | ||
698 | /* Restore codec state */ | 795 | return 0; |
699 | return regcache_sync(priv->regmap); | ||
700 | } | 796 | } |
701 | #else | 797 | #else |
798 | #define tas5086_soc_suspend NULL | ||
702 | #define tas5086_soc_resume NULL | 799 | #define tas5086_soc_resume NULL |
703 | #endif /* CONFIG_PM */ | 800 | #endif /* CONFIG_PM */ |
704 | 801 | ||
@@ -710,23 +807,19 @@ static const struct of_device_id tas5086_dt_ids[] = { | |||
710 | MODULE_DEVICE_TABLE(of, tas5086_dt_ids); | 807 | MODULE_DEVICE_TABLE(of, tas5086_dt_ids); |
711 | #endif | 808 | #endif |
712 | 809 | ||
713 | /* charge period values in microseconds */ | ||
714 | static const int tas5086_charge_period[] = { | ||
715 | 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200, | ||
716 | 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000, | ||
717 | 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000, | ||
718 | }; | ||
719 | |||
720 | static int tas5086_probe(struct snd_soc_codec *codec) | 810 | static int tas5086_probe(struct snd_soc_codec *codec) |
721 | { | 811 | { |
722 | struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); | 812 | struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); |
723 | int charge_period = 1300000; /* hardware default is 1300 ms */ | ||
724 | u8 pwm_start_mid_z = 0; | ||
725 | int i, ret; | 813 | int i, ret; |
726 | 814 | ||
815 | priv->pwm_start_mid_z = 0; | ||
816 | priv->charge_period = 1300000; /* hardware default is 1300 ms */ | ||
817 | |||
727 | if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) { | 818 | if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) { |
728 | struct device_node *of_node = codec->dev->of_node; | 819 | struct device_node *of_node = codec->dev->of_node; |
729 | of_property_read_u32(of_node, "ti,charge-period", &charge_period); | 820 | |
821 | of_property_read_u32(of_node, "ti,charge-period", | ||
822 | &priv->charge_period); | ||
730 | 823 | ||
731 | for (i = 0; i < 6; i++) { | 824 | for (i = 0; i < 6; i++) { |
732 | char name[25]; | 825 | char name[25]; |
@@ -735,43 +828,11 @@ static int tas5086_probe(struct snd_soc_codec *codec) | |||
735 | "ti,mid-z-channel-%d", i + 1); | 828 | "ti,mid-z-channel-%d", i + 1); |
736 | 829 | ||
737 | if (of_get_property(of_node, name, NULL) != NULL) | 830 | if (of_get_property(of_node, name, NULL) != NULL) |
738 | pwm_start_mid_z |= 1 << i; | 831 | priv->pwm_start_mid_z |= 1 << i; |
739 | } | 832 | } |
740 | } | 833 | } |
741 | 834 | ||
742 | /* | 835 | ret = tas5086_init(codec->dev, priv); |
743 | * If any of the channels is configured to start in Mid-Z mode, | ||
744 | * configure 'part 1' of the PWM starts to use Mid-Z, and tell | ||
745 | * all configured mid-z channels to start start under 'part 1'. | ||
746 | */ | ||
747 | if (pwm_start_mid_z) | ||
748 | regmap_write(priv->regmap, TAS5086_PWM_START, | ||
749 | TAS5086_PWM_START_MIDZ_FOR_START_1 | | ||
750 | pwm_start_mid_z); | ||
751 | |||
752 | /* lookup and set split-capacitor charge period */ | ||
753 | if (charge_period == 0) { | ||
754 | regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); | ||
755 | } else { | ||
756 | i = index_in_array(tas5086_charge_period, | ||
757 | ARRAY_SIZE(tas5086_charge_period), | ||
758 | charge_period); | ||
759 | if (i >= 0) | ||
760 | regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, | ||
761 | i + 0x08); | ||
762 | else | ||
763 | dev_warn(codec->dev, | ||
764 | "Invalid split-cap charge period of %d ns.\n", | ||
765 | charge_period); | ||
766 | } | ||
767 | |||
768 | /* enable factory trim */ | ||
769 | ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00); | ||
770 | if (ret < 0) | ||
771 | return ret; | ||
772 | |||
773 | /* start all channels */ | ||
774 | ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20); | ||
775 | if (ret < 0) | 836 | if (ret < 0) |
776 | return ret; | 837 | return ret; |
777 | 838 | ||
@@ -780,12 +841,6 @@ static int tas5086_probe(struct snd_soc_codec *codec) | |||
780 | if (ret < 0) | 841 | if (ret < 0) |
781 | return ret; | 842 | return ret; |
782 | 843 | ||
783 | /* mute all channels for now */ | ||
784 | ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE, | ||
785 | TAS5086_SOFT_MUTE_ALL); | ||
786 | if (ret < 0) | ||
787 | return ret; | ||
788 | |||
789 | return 0; | 844 | return 0; |
790 | } | 845 | } |
791 | 846 | ||
@@ -803,6 +858,7 @@ static int tas5086_remove(struct snd_soc_codec *codec) | |||
803 | static struct snd_soc_codec_driver soc_codec_dev_tas5086 = { | 858 | static struct snd_soc_codec_driver soc_codec_dev_tas5086 = { |
804 | .probe = tas5086_probe, | 859 | .probe = tas5086_probe, |
805 | .remove = tas5086_remove, | 860 | .remove = tas5086_remove, |
861 | .suspend = tas5086_soc_suspend, | ||
806 | .resume = tas5086_soc_resume, | 862 | .resume = tas5086_soc_resume, |
807 | .controls = tas5086_controls, | 863 | .controls = tas5086_controls, |
808 | .num_controls = ARRAY_SIZE(tas5086_controls), | 864 | .num_controls = ARRAY_SIZE(tas5086_controls), |
@@ -862,17 +918,8 @@ static int tas5086_i2c_probe(struct i2c_client *i2c, | |||
862 | if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) | 918 | if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) |
863 | gpio_nreset = -EINVAL; | 919 | gpio_nreset = -EINVAL; |
864 | 920 | ||
865 | if (gpio_is_valid(gpio_nreset)) { | ||
866 | /* Reset codec - minimum assertion time is 400ns */ | ||
867 | gpio_direction_output(gpio_nreset, 0); | ||
868 | udelay(1); | ||
869 | gpio_set_value(gpio_nreset, 1); | ||
870 | |||
871 | /* Codec needs ~15ms to wake up */ | ||
872 | msleep(15); | ||
873 | } | ||
874 | |||
875 | priv->gpio_nreset = gpio_nreset; | 921 | priv->gpio_nreset = gpio_nreset; |
922 | tas5086_reset(priv); | ||
876 | 923 | ||
877 | /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */ | 924 | /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */ |
878 | ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i); | 925 | ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i); |