[alsa-devel] [PATCH RFC 3/3] uda1380: make driver more powersave-friendly
Vasily Khoruzhick
anarsoul at gmail.com
Sat Jun 26 17:14:45 CEST 2010
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 at gmail.com>
---
sound/soc/codecs/uda1380.c | 92 ++++++++++++++++++++++++++++---------------
1 files changed, 60 insertions(+), 32 deletions(-)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 2b7f200..a30f809 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -130,7 +130,41 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
+static void uda1380_sync_cache(struct snd_soc_codec *codec)
+{
+ int reg;
+ u8 data[3];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (reg = 0; reg < UDA1380_MVOL; reg++) {
+ data[0] = reg;
+ data[1] = (cache[reg] & 0xff00) >> 8;
+ data[2] = cache[reg] & 0x00ff;
+ if (codec->hw_write(codec->control_data, data, 3) != 3)
+ dev_err(codec->dev, "%s: write to reg 0x%x failed\n",
+ __func__, reg);
+ }
+
+ for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++)
+ set_bit(reg - 0x10, &uda1380_cache_dirty);
+}
+
+static int uda1380_reset(struct snd_soc_codec *codec)
+{
+ u8 data[3];
+
+ data[0] = UDA1380_RESET;
+ data[1] = 0;
+ data[2] = 0;
+
+ if (codec->hw_write(codec->control_data, data, 3) != 3) {
+ dev_err(codec->dev, "%s: failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
static void uda1380_flush_work(struct work_struct *work)
{
@@ -481,13 +515,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ uda1380_write(codec, UDA1380_MIXER,
mixer & ~R14_SILENCE);
schedule_work(&uda1380->work);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ uda1380_write(codec, UDA1380_MIXER,
mixer | R14_SILENCE);
schedule_work(&uda1380->work);
break;
@@ -561,17 +595,38 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+ struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+ if (codec->bias_level == level)
+ return 0;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
+ /* ADC, DAC on */
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
break;
case SND_SOC_BIAS_STANDBY:
- uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ pdata->set_power(1);
+ uda1380_reset(codec);
+
+ switch (pdata->dac_clk) {
+ case UDA1380_DAC_CLK_SYSCLK:
+ uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
+ break;
+ case UDA1380_DAC_CLK_WSPLL:
+ uda1380_write_reg_cache(codec, UDA1380_CLK,
+ R00_DAC_CLK);
+ break;
+ }
+
+ uda1380_sync_cache(codec);
+ }
+ uda1380_write(codec, UDA1380_PM, 0x0);
break;
case SND_SOC_BIAS_OFF:
- uda1380_write(codec, UDA1380_PM, 0x0);
+ pdata->set_power(0);
break;
}
codec->bias_level = level;
@@ -658,16 +713,7 @@ static int uda1380_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- int i;
- u8 data[2];
- u16 *cache = codec->reg_cache;
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
- data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
- data[1] = cache[i] & 0x00ff;
- codec->hw_write(codec->control_data, data, 2);
- }
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -697,15 +743,6 @@ static int uda1380_probe(struct platform_device *pdev)
/* power on device */
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- /* set clock input */
- switch (pdata->dac_clk) {
- case UDA1380_DAC_CLK_SYSCLK:
- uda1380_write(codec, UDA1380_CLK, 0);
- break;
- case UDA1380_DAC_CLK_WSPLL:
- uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
- break;
- }
snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls));
@@ -754,9 +791,6 @@ static int uda1380_register(struct uda1380_priv *uda1380)
if (!pdata || !pdata->set_power)
return -EINVAL;
- /* we may need to have the clock running here - pH5 */
- pdata->set_power(1);
-
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -776,12 +810,6 @@ static int uda1380_register(struct uda1380_priv *uda1380)
memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg));
- ret = uda1380_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- goto err_reset;
- }
-
INIT_WORK(&uda1380->work, uda1380_flush_work);
for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++)
--
1.7.1
More information about the Alsa-devel
mailing list