diff options
author | Vasily Khoruzhick <anarsoul@gmail.com> | 2010-08-30 04:28:07 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-08-31 08:25:17 -0400 |
commit | 8614d310a2ba78cfc73ab12da112c3115801f94e (patch) | |
tree | c5a4b21a132d0931393bf74e59bde1c24453d897 /sound/soc/codecs/uda1380.c | |
parent | 4e48541676f019145b555761d89bf4f8607d3de0 (diff) |
ASoC: uda1380: make driver more powersave-friendly
Disable some codec modules in standby mode, completely disable
codec in off mode to save some power.
Fix suspend/resume: mark mixer regs as dirty on resume to
restore mixer values, otherwise driver produces no sound
(master is muted by default).
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Acked-by: Marek Vasut <marek.vasut@gmail.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/uda1380.c')
-rw-r--r-- | sound/soc/codecs/uda1380.c | 145 |
1 files changed, 105 insertions, 40 deletions
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 1a51c816e542..488f8010e405 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c | |||
@@ -39,6 +39,7 @@ struct uda1380_priv { | |||
39 | u16 reg_cache[UDA1380_CACHEREGNUM]; | 39 | u16 reg_cache[UDA1380_CACHEREGNUM]; |
40 | unsigned int dac_clk; | 40 | unsigned int dac_clk; |
41 | struct work_struct work; | 41 | struct work_struct work; |
42 | void *control_data; | ||
42 | }; | 43 | }; |
43 | 44 | ||
44 | /* | 45 | /* |
@@ -129,7 +130,46 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, | |||
129 | return -EIO; | 130 | return -EIO; |
130 | } | 131 | } |
131 | 132 | ||
132 | #define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0) | 133 | static void uda1380_sync_cache(struct snd_soc_codec *codec) |
134 | { | ||
135 | int reg; | ||
136 | u8 data[3]; | ||
137 | u16 *cache = codec->reg_cache; | ||
138 | |||
139 | /* Sync reg_cache with the hardware */ | ||
140 | for (reg = 0; reg < UDA1380_MVOL; reg++) { | ||
141 | data[0] = reg; | ||
142 | data[1] = (cache[reg] & 0xff00) >> 8; | ||
143 | data[2] = cache[reg] & 0x00ff; | ||
144 | if (codec->hw_write(codec->control_data, data, 3) != 3) | ||
145 | dev_err(codec->dev, "%s: write to reg 0x%x failed\n", | ||
146 | __func__, reg); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static int uda1380_reset(struct snd_soc_codec *codec) | ||
151 | { | ||
152 | struct uda1380_platform_data *pdata = codec->dev->platform_data; | ||
153 | |||
154 | if (gpio_is_valid(pdata->gpio_reset)) { | ||
155 | gpio_set_value(pdata->gpio_reset, 1); | ||
156 | mdelay(1); | ||
157 | gpio_set_value(pdata->gpio_reset, 0); | ||
158 | } else { | ||
159 | u8 data[3]; | ||
160 | |||
161 | data[0] = UDA1380_RESET; | ||
162 | data[1] = 0; | ||
163 | data[2] = 0; | ||
164 | |||
165 | if (codec->hw_write(codec->control_data, data, 3) != 3) { | ||
166 | dev_err(codec->dev, "%s: failed\n", __func__); | ||
167 | return -EIO; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
133 | 173 | ||
134 | static void uda1380_flush_work(struct work_struct *work) | 174 | static void uda1380_flush_work(struct work_struct *work) |
135 | { | 175 | { |
@@ -560,18 +600,40 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, | |||
560 | enum snd_soc_bias_level level) | 600 | enum snd_soc_bias_level level) |
561 | { | 601 | { |
562 | int pm = uda1380_read_reg_cache(codec, UDA1380_PM); | 602 | int pm = uda1380_read_reg_cache(codec, UDA1380_PM); |
603 | int reg; | ||
604 | struct uda1380_platform_data *pdata = codec->dev->platform_data; | ||
605 | |||
606 | if (codec->bias_level == level) | ||
607 | return 0; | ||
563 | 608 | ||
564 | switch (level) { | 609 | switch (level) { |
565 | case SND_SOC_BIAS_ON: | 610 | case SND_SOC_BIAS_ON: |
566 | case SND_SOC_BIAS_PREPARE: | 611 | case SND_SOC_BIAS_PREPARE: |
612 | /* ADC, DAC on */ | ||
567 | uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm); | 613 | uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm); |
568 | break; | 614 | break; |
569 | case SND_SOC_BIAS_STANDBY: | 615 | case SND_SOC_BIAS_STANDBY: |
570 | uda1380_write(codec, UDA1380_PM, R02_PON_BIAS); | 616 | if (codec->bias_level == SND_SOC_BIAS_OFF) { |
571 | break; | 617 | if (gpio_is_valid(pdata->gpio_power)) { |
572 | case SND_SOC_BIAS_OFF: | 618 | gpio_set_value(pdata->gpio_power, 1); |
619 | uda1380_reset(codec); | ||
620 | } | ||
621 | |||
622 | uda1380_sync_cache(codec); | ||
623 | } | ||
573 | uda1380_write(codec, UDA1380_PM, 0x0); | 624 | uda1380_write(codec, UDA1380_PM, 0x0); |
574 | break; | 625 | break; |
626 | case SND_SOC_BIAS_OFF: | ||
627 | if (!gpio_is_valid(pdata->gpio_power)) | ||
628 | break; | ||
629 | |||
630 | gpio_set_value(pdata->gpio_power, 0); | ||
631 | |||
632 | /* Mark mixer regs cache dirty to sync them with | ||
633 | * codec regs on power on. | ||
634 | */ | ||
635 | for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++) | ||
636 | set_bit(reg - 0x10, &uda1380_cache_dirty); | ||
575 | } | 637 | } |
576 | codec->bias_level = level; | 638 | codec->bias_level = level; |
577 | return 0; | 639 | return 0; |
@@ -651,16 +713,6 @@ static int uda1380_suspend(struct snd_soc_codec *codec, pm_message_t state) | |||
651 | 713 | ||
652 | static int uda1380_resume(struct snd_soc_codec *codec) | 714 | static int uda1380_resume(struct snd_soc_codec *codec) |
653 | { | 715 | { |
654 | int i; | ||
655 | u8 data[2]; | ||
656 | u16 *cache = codec->reg_cache; | ||
657 | |||
658 | /* Sync reg_cache with the hardware */ | ||
659 | for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) { | ||
660 | data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); | ||
661 | data[1] = cache[i] & 0x00ff; | ||
662 | codec->hw_write(codec->control_data, data, 2); | ||
663 | } | ||
664 | uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 716 | uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
665 | return 0; | 717 | return 0; |
666 | } | 718 | } |
@@ -671,29 +723,36 @@ static int uda1380_probe(struct snd_soc_codec *codec) | |||
671 | struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); | 723 | struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); |
672 | int ret; | 724 | int ret; |
673 | 725 | ||
726 | uda1380->codec = codec; | ||
727 | |||
674 | codec->hw_write = (hw_write_t)i2c_master_send; | 728 | codec->hw_write = (hw_write_t)i2c_master_send; |
729 | codec->control_data = uda1380->control_data; | ||
675 | 730 | ||
676 | if (!pdata || !pdata->gpio_power || !pdata->gpio_reset) | 731 | if (!pdata) |
677 | return -EINVAL; | 732 | return -EINVAL; |
678 | 733 | ||
679 | ret = gpio_request(pdata->gpio_power, "uda1380 power"); | 734 | if (gpio_is_valid(pdata->gpio_reset)) { |
680 | if (ret) | 735 | ret = gpio_request(pdata->gpio_reset, "uda1380 reset"); |
681 | return ret; | 736 | if (ret) |
682 | ret = gpio_request(pdata->gpio_reset, "uda1380 reset"); | 737 | goto err_out; |
683 | if (ret) | 738 | ret = gpio_direction_output(pdata->gpio_reset, 0); |
684 | goto err_gpio; | 739 | if (ret) |
685 | 740 | goto err_gpio_reset_conf; | |
686 | gpio_direction_output(pdata->gpio_power, 1); | 741 | } |
687 | |||
688 | /* we may need to have the clock running here - pH5 */ | ||
689 | gpio_direction_output(pdata->gpio_reset, 1); | ||
690 | udelay(5); | ||
691 | gpio_set_value(pdata->gpio_reset, 0); | ||
692 | 742 | ||
693 | ret = uda1380_reset(codec); | 743 | if (gpio_is_valid(pdata->gpio_power)) { |
694 | if (ret < 0) { | 744 | ret = gpio_request(pdata->gpio_power, "uda1380 power"); |
695 | dev_err(codec->dev, "Failed to issue reset\n"); | 745 | if (ret) |
696 | goto err_reset; | 746 | goto err_gpio; |
747 | ret = gpio_direction_output(pdata->gpio_power, 0); | ||
748 | if (ret) | ||
749 | goto err_gpio_power_conf; | ||
750 | } else { | ||
751 | ret = uda1380_reset(codec); | ||
752 | if (ret) { | ||
753 | dev_err(codec->dev, "Failed to issue reset\n"); | ||
754 | goto err_reset; | ||
755 | } | ||
697 | } | 756 | } |
698 | 757 | ||
699 | INIT_WORK(&uda1380->work, uda1380_flush_work); | 758 | INIT_WORK(&uda1380->work, uda1380_flush_work); |
@@ -703,10 +762,11 @@ static int uda1380_probe(struct snd_soc_codec *codec) | |||
703 | /* set clock input */ | 762 | /* set clock input */ |
704 | switch (pdata->dac_clk) { | 763 | switch (pdata->dac_clk) { |
705 | case UDA1380_DAC_CLK_SYSCLK: | 764 | case UDA1380_DAC_CLK_SYSCLK: |
706 | uda1380_write(codec, UDA1380_CLK, 0); | 765 | uda1380_write_reg_cache(codec, UDA1380_CLK, 0); |
707 | break; | 766 | break; |
708 | case UDA1380_DAC_CLK_WSPLL: | 767 | case UDA1380_DAC_CLK_WSPLL: |
709 | uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK); | 768 | uda1380_write_reg_cache(codec, UDA1380_CLK, |
769 | R00_DAC_CLK); | ||
710 | break; | 770 | break; |
711 | } | 771 | } |
712 | 772 | ||
@@ -717,10 +777,15 @@ static int uda1380_probe(struct snd_soc_codec *codec) | |||
717 | return 0; | 777 | return 0; |
718 | 778 | ||
719 | err_reset: | 779 | err_reset: |
720 | gpio_set_value(pdata->gpio_power, 0); | 780 | err_gpio_power_conf: |
721 | gpio_free(pdata->gpio_reset); | 781 | if (gpio_is_valid(pdata->gpio_power)) |
782 | gpio_free(pdata->gpio_power); | ||
783 | |||
784 | err_gpio_reset_conf: | ||
722 | err_gpio: | 785 | err_gpio: |
723 | gpio_free(pdata->gpio_power); | 786 | if (gpio_is_valid(pdata->gpio_reset)) |
787 | gpio_free(pdata->gpio_reset); | ||
788 | err_out: | ||
724 | return ret; | 789 | return ret; |
725 | } | 790 | } |
726 | 791 | ||
@@ -731,7 +796,6 @@ static int uda1380_remove(struct snd_soc_codec *codec) | |||
731 | 796 | ||
732 | uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); | 797 | uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); |
733 | 798 | ||
734 | gpio_set_value(pdata->gpio_power, 0); | ||
735 | gpio_free(pdata->gpio_reset); | 799 | gpio_free(pdata->gpio_reset); |
736 | gpio_free(pdata->gpio_power); | 800 | gpio_free(pdata->gpio_power); |
737 | 801 | ||
@@ -743,8 +807,8 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { | |||
743 | .remove = uda1380_remove, | 807 | .remove = uda1380_remove, |
744 | .suspend = uda1380_suspend, | 808 | .suspend = uda1380_suspend, |
745 | .resume = uda1380_resume, | 809 | .resume = uda1380_resume, |
746 | .read = uda1380_read_reg_cache, | 810 | .read = uda1380_read_reg_cache, |
747 | .write = uda1380_write, | 811 | .write = uda1380_write, |
748 | .set_bias_level = uda1380_set_bias_level, | 812 | .set_bias_level = uda1380_set_bias_level, |
749 | .reg_cache_size = ARRAY_SIZE(uda1380_reg), | 813 | .reg_cache_size = ARRAY_SIZE(uda1380_reg), |
750 | .reg_word_size = sizeof(u16), | 814 | .reg_word_size = sizeof(u16), |
@@ -764,6 +828,7 @@ static __devinit int uda1380_i2c_probe(struct i2c_client *i2c, | |||
764 | return -ENOMEM; | 828 | return -ENOMEM; |
765 | 829 | ||
766 | i2c_set_clientdata(i2c, uda1380); | 830 | i2c_set_clientdata(i2c, uda1380); |
831 | uda1380->control_data = i2c; | ||
767 | 832 | ||
768 | ret = snd_soc_register_codec(&i2c->dev, | 833 | ret = snd_soc_register_codec(&i2c->dev, |
769 | &soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai)); | 834 | &soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai)); |