Commit 8614d310 authored by Vasily Khoruzhick's avatar Vasily Khoruzhick Committed by Mark Brown

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: default avatarVasily Khoruzhick <anarsoul@gmail.com>
Acked-by: default avatarMarek Vasut <marek.vasut@gmail.com>
Acked-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 4e485416
...@@ -39,6 +39,7 @@ struct uda1380_priv { ...@@ -39,6 +39,7 @@ struct uda1380_priv {
u16 reg_cache[UDA1380_CACHEREGNUM]; u16 reg_cache[UDA1380_CACHEREGNUM];
unsigned int dac_clk; unsigned int dac_clk;
struct work_struct work; struct work_struct work;
void *control_data;
}; };
/* /*
...@@ -129,7 +130,46 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, ...@@ -129,7 +130,46 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO; 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);
}
}
static int uda1380_reset(struct snd_soc_codec *codec)
{
struct uda1380_platform_data *pdata = codec->dev->platform_data;
if (gpio_is_valid(pdata->gpio_reset)) {
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) static void uda1380_flush_work(struct work_struct *work)
{ {
...@@ -560,18 +600,40 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, ...@@ -560,18 +600,40 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
int pm = uda1380_read_reg_cache(codec, UDA1380_PM); int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
int reg;
struct uda1380_platform_data *pdata = codec->dev->platform_data;
if (codec->bias_level == level)
return 0;
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
/* ADC, DAC on */
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm); uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
break; break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS); if (codec->bias_level == SND_SOC_BIAS_OFF) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 1);
uda1380_reset(codec);
}
uda1380_sync_cache(codec);
}
uda1380_write(codec, UDA1380_PM, 0x0);
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
uda1380_write(codec, UDA1380_PM, 0x0); if (!gpio_is_valid(pdata->gpio_power))
break; break;
gpio_set_value(pdata->gpio_power, 0);
/* Mark mixer regs cache dirty to sync them with
* codec regs on power on.
*/
for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++)
set_bit(reg - 0x10, &uda1380_cache_dirty);
} }
codec->bias_level = level; codec->bias_level = level;
return 0; return 0;
...@@ -651,16 +713,6 @@ static int uda1380_suspend(struct snd_soc_codec *codec, pm_message_t state) ...@@ -651,16 +713,6 @@ static int uda1380_suspend(struct snd_soc_codec *codec, pm_message_t state)
static int uda1380_resume(struct snd_soc_codec *codec) static int uda1380_resume(struct snd_soc_codec *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); uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; return 0;
} }
...@@ -671,30 +723,37 @@ static int uda1380_probe(struct snd_soc_codec *codec) ...@@ -671,30 +723,37 @@ static int uda1380_probe(struct snd_soc_codec *codec)
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec); struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int ret; int ret;
uda1380->codec = codec;
codec->hw_write = (hw_write_t)i2c_master_send; codec->hw_write = (hw_write_t)i2c_master_send;
codec->control_data = uda1380->control_data;
if (!pdata || !pdata->gpio_power || !pdata->gpio_reset) if (!pdata)
return -EINVAL; return -EINVAL;
ret = gpio_request(pdata->gpio_power, "uda1380 power"); if (gpio_is_valid(pdata->gpio_reset)) {
if (ret)
return ret;
ret = gpio_request(pdata->gpio_reset, "uda1380 reset"); ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
if (ret) if (ret)
goto err_gpio; goto err_out;
ret = gpio_direction_output(pdata->gpio_reset, 0);
gpio_direction_output(pdata->gpio_power, 1); if (ret)
goto err_gpio_reset_conf;
/* 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 (gpio_is_valid(pdata->gpio_power)) {
ret = gpio_request(pdata->gpio_power, "uda1380 power");
if (ret)
goto err_gpio;
ret = gpio_direction_output(pdata->gpio_power, 0);
if (ret)
goto err_gpio_power_conf;
} else {
ret = uda1380_reset(codec); ret = uda1380_reset(codec);
if (ret < 0) { if (ret) {
dev_err(codec->dev, "Failed to issue reset\n"); dev_err(codec->dev, "Failed to issue reset\n");
goto err_reset; goto err_reset;
} }
}
INIT_WORK(&uda1380->work, uda1380_flush_work); INIT_WORK(&uda1380->work, uda1380_flush_work);
...@@ -703,10 +762,11 @@ static int uda1380_probe(struct snd_soc_codec *codec) ...@@ -703,10 +762,11 @@ static int uda1380_probe(struct snd_soc_codec *codec)
/* set clock input */ /* set clock input */
switch (pdata->dac_clk) { switch (pdata->dac_clk) {
case UDA1380_DAC_CLK_SYSCLK: case UDA1380_DAC_CLK_SYSCLK:
uda1380_write(codec, UDA1380_CLK, 0); uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
break; break;
case UDA1380_DAC_CLK_WSPLL: case UDA1380_DAC_CLK_WSPLL:
uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK); uda1380_write_reg_cache(codec, UDA1380_CLK,
R00_DAC_CLK);
break; break;
} }
...@@ -717,10 +777,15 @@ static int uda1380_probe(struct snd_soc_codec *codec) ...@@ -717,10 +777,15 @@ static int uda1380_probe(struct snd_soc_codec *codec)
return 0; return 0;
err_reset: err_reset:
gpio_set_value(pdata->gpio_power, 0); err_gpio_power_conf:
gpio_free(pdata->gpio_reset); if (gpio_is_valid(pdata->gpio_power))
err_gpio:
gpio_free(pdata->gpio_power); gpio_free(pdata->gpio_power);
err_gpio_reset_conf:
err_gpio:
if (gpio_is_valid(pdata->gpio_reset))
gpio_free(pdata->gpio_reset);
err_out:
return ret; return ret;
} }
...@@ -731,7 +796,6 @@ static int uda1380_remove(struct snd_soc_codec *codec) ...@@ -731,7 +796,6 @@ static int uda1380_remove(struct snd_soc_codec *codec)
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
gpio_set_value(pdata->gpio_power, 0);
gpio_free(pdata->gpio_reset); gpio_free(pdata->gpio_reset);
gpio_free(pdata->gpio_power); gpio_free(pdata->gpio_power);
...@@ -764,6 +828,7 @@ static __devinit int uda1380_i2c_probe(struct i2c_client *i2c, ...@@ -764,6 +828,7 @@ static __devinit int uda1380_i2c_probe(struct i2c_client *i2c,
return -ENOMEM; return -ENOMEM;
i2c_set_clientdata(i2c, uda1380); i2c_set_clientdata(i2c, uda1380);
uda1380->control_data = i2c;
ret = snd_soc_register_codec(&i2c->dev, ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai)); &soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment