[alsa-devel] [PATCH 1/2] ASoC: uda1380: make driver more powersave-friendly
Vasily Khoruzhick
anarsoul at gmail.com
Sat Aug 28 09:49:35 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 | 135 +++++++++++++++++++++++++++++++------------
1 files changed, 97 insertions(+), 38 deletions(-)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 2f925a2..c53514b 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -131,7 +131,52 @@ 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);
+ }
+
+ /* Make mixer regs as dirty, they can be modified only
+ * when i2s clock is applied.
+ */
+ for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++)
+ set_bit(reg - 0x10, &uda1380_cache_dirty);
+}
+
+static int uda1380_reset(struct snd_soc_codec *codec)
+{
+ struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+ if (pdata->gpio_reset != -EINVAL) {
+ gpio_set_value(pdata->gpio_reset, 1);
+ mdelay(1);
+ gpio_set_value(pdata->gpio_reset, 0);
+ } else {
+ 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)
{
@@ -562,17 +607,31 @@ 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) {
+ if (pdata->gpio_power != -EINVAL) {
+ gpio_set_value(pdata->gpio_power, 1);
+ uda1380_reset(codec);
+ }
+
+ uda1380_sync_cache(codec);
+ }
+ uda1380_write(codec, UDA1380_PM, 0x0);
break;
case SND_SOC_BIAS_OFF:
- uda1380_write(codec, UDA1380_PM, 0x0);
+ if (pdata->gpio_power != -EINVAL)
+ gpio_set_value(pdata->gpio_power, 0);
break;
}
codec->bias_level = level;
@@ -659,16 +718,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;
}
@@ -698,16 +748,18 @@ 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);
+ uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
break;
case UDA1380_DAC_CLK_WSPLL:
- uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
+ uda1380_write_reg_cache(codec, UDA1380_CLK,
+ R00_DAC_CLK);
break;
}
+
snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec);
@@ -752,22 +804,22 @@ static int uda1380_register(struct uda1380_priv *uda1380)
return -EINVAL;
}
- if (!pdata || !pdata->gpio_power || !pdata->gpio_reset)
+ if (!pdata)
return -EINVAL;
- ret = gpio_request(pdata->gpio_power, "uda1380 power");
- if (ret)
- goto err_out;
- ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
- if (ret)
- goto err_gpio;
-
- gpio_direction_output(pdata->gpio_power, 1);
+ if (pdata->gpio_power != -EINVAL) {
+ ret = gpio_request(pdata->gpio_power, "uda1380 power");
+ if (ret)
+ goto err_out;
+ gpio_direction_output(pdata->gpio_power, 0);
+ }
- /* we may need to have the clock running here - pH5 */
- gpio_direction_output(pdata->gpio_reset, 1);
- udelay(5);
- gpio_set_value(pdata->gpio_reset, 0);
+ if (pdata->gpio_reset != -EINVAL) {
+ ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
+ if (ret)
+ goto err_gpio;
+ gpio_direction_output(pdata->gpio_reset, 0);
+ }
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -788,10 +840,12 @@ 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;
+ if (pdata->gpio_power == -EINVAL) {
+ 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);
@@ -818,10 +872,11 @@ static int uda1380_register(struct uda1380_priv *uda1380)
err_dai:
snd_soc_unregister_codec(codec);
err_reset:
- gpio_set_value(pdata->gpio_power, 0);
- gpio_free(pdata->gpio_reset);
+ if (pdata->gpio_reset != -EINVAL)
+ gpio_free(pdata->gpio_reset);
err_gpio:
- gpio_free(pdata->gpio_power);
+ if (pdata->gpio_power != -EINVAL)
+ gpio_free(pdata->gpio_power);
err_out:
return ret;
}
@@ -834,9 +889,13 @@ static void uda1380_unregister(struct uda1380_priv *uda1380)
snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
snd_soc_unregister_codec(&uda1380->codec);
- gpio_set_value(pdata->gpio_power, 0);
- gpio_free(pdata->gpio_reset);
- gpio_free(pdata->gpio_power);
+ if (pdata->gpio_power != -EINVAL) {
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_power);
+ }
+
+ if (pdata->gpio_reset != -EINVAL)
+ gpio_free(pdata->gpio_reset);
kfree(uda1380);
uda1380_codec = NULL;
--
1.7.2
More information about the Alsa-devel
mailing list