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>
This commit is contained in:
parent
4e48541676
commit
8614d310a2
1 changed files with 106 additions and 41 deletions
|
@ -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,
|
||||||
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,
|
||||||
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) {
|
||||||
break;
|
if (gpio_is_valid(pdata->gpio_power)) {
|
||||||
case SND_SOC_BIAS_OFF:
|
gpio_set_value(pdata->gpio_power, 1);
|
||||||
|
uda1380_reset(codec);
|
||||||
|
}
|
||||||
|
|
||||||
|
uda1380_sync_cache(codec);
|
||||||
|
}
|
||||||
uda1380_write(codec, UDA1380_PM, 0x0);
|
uda1380_write(codec, UDA1380_PM, 0x0);
|
||||||
break;
|
break;
|
||||||
|
case SND_SOC_BIAS_OFF:
|
||||||
|
if (!gpio_is_valid(pdata->gpio_power))
|
||||||
|
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)
|
||||||
|
|
||||||
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,29 +723,36 @@ 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;
|
||||||
|
|
||||||
codec->hw_write = (hw_write_t)i2c_master_send;
|
uda1380->codec = codec;
|
||||||
|
|
||||||
if (!pdata || !pdata->gpio_power || !pdata->gpio_reset)
|
codec->hw_write = (hw_write_t)i2c_master_send;
|
||||||
|
codec->control_data = uda1380->control_data;
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = gpio_request(pdata->gpio_power, "uda1380 power");
|
if (gpio_is_valid(pdata->gpio_reset)) {
|
||||||
if (ret)
|
ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
|
||||||
return ret;
|
if (ret)
|
||||||
ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
|
goto err_out;
|
||||||
if (ret)
|
ret = gpio_direction_output(pdata->gpio_reset, 0);
|
||||||
goto err_gpio;
|
if (ret)
|
||||||
|
goto err_gpio_reset_conf;
|
||||||
|
}
|
||||||
|
|
||||||
gpio_direction_output(pdata->gpio_power, 1);
|
if (gpio_is_valid(pdata->gpio_power)) {
|
||||||
|
ret = gpio_request(pdata->gpio_power, "uda1380 power");
|
||||||
/* we may need to have the clock running here - pH5 */
|
if (ret)
|
||||||
gpio_direction_output(pdata->gpio_reset, 1);
|
goto err_gpio;
|
||||||
udelay(5);
|
ret = gpio_direction_output(pdata->gpio_power, 0);
|
||||||
gpio_set_value(pdata->gpio_reset, 0);
|
if (ret)
|
||||||
|
goto err_gpio_power_conf;
|
||||||
ret = uda1380_reset(codec);
|
} else {
|
||||||
if (ret < 0) {
|
ret = uda1380_reset(codec);
|
||||||
dev_err(codec->dev, "Failed to issue reset\n");
|
if (ret) {
|
||||||
goto err_reset;
|
dev_err(codec->dev, "Failed to issue reset\n");
|
||||||
|
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)
|
||||||
/* 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)
|
||||||
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))
|
||||||
|
gpio_free(pdata->gpio_power);
|
||||||
|
|
||||||
|
err_gpio_reset_conf:
|
||||||
err_gpio:
|
err_gpio:
|
||||||
gpio_free(pdata->gpio_power);
|
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)
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -743,8 +807,8 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
|
||||||
.remove = uda1380_remove,
|
.remove = uda1380_remove,
|
||||||
.suspend = uda1380_suspend,
|
.suspend = uda1380_suspend,
|
||||||
.resume = uda1380_resume,
|
.resume = uda1380_resume,
|
||||||
.read = uda1380_read_reg_cache,
|
.read = uda1380_read_reg_cache,
|
||||||
.write = uda1380_write,
|
.write = uda1380_write,
|
||||||
.set_bias_level = uda1380_set_bias_level,
|
.set_bias_level = uda1380_set_bias_level,
|
||||||
.reg_cache_size = ARRAY_SIZE(uda1380_reg),
|
.reg_cache_size = ARRAY_SIZE(uda1380_reg),
|
||||||
.reg_word_size = sizeof(u16),
|
.reg_word_size = sizeof(u16),
|
||||||
|
@ -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));
|
||||||
|
|
Loading…
Reference in a new issue