summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
authorVasily Khoruzhick <anarsoul@gmail.com>2010-08-30 11:28:07 +0300
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-08-31 13:25:17 +0100
commit8614d310a2ba78cfc73ab12da112c3115801f94e (patch)
treec5a4b21a132d0931393bf74e59bde1c24453d897 /sound/soc
parent4e48541676f019145b555761d89bf4f8607d3de0 (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')
-rw-r--r--sound/soc/codecs/uda1380.c145
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 {
u16 reg_cache[UDA1380_CACHEREGNUM];
unsigned int dac_clk;
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;
}
-#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)
{
@@ -560,18 +600,40 @@ 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);
+ int reg;
+ 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);
- break;
- case SND_SOC_BIAS_OFF:
+ 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;
+ 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;
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)
{
- 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;
}
@@ -671,29 +723,36 @@ static int uda1380_probe(struct snd_soc_codec *codec)
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int ret;
+ uda1380->codec = codec;
+
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;
- ret = gpio_request(pdata->gpio_power, "uda1380 power");
- if (ret)
- return ret;
- ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
- if (ret)
- goto err_gpio;
-
- gpio_direction_output(pdata->gpio_power, 1);
-
- /* 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_reset)) {
+ ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
+ if (ret)
+ goto err_out;
+ ret = gpio_direction_output(pdata->gpio_reset, 0);
+ if (ret)
+ goto err_gpio_reset_conf;
+ }
- ret = uda1380_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- goto err_reset;
+ 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);
+ if (ret) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_reset;
+ }
}
INIT_WORK(&uda1380->work, uda1380_flush_work);
@@ -703,10 +762,11 @@ static int uda1380_probe(struct snd_soc_codec *codec)
/* 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;
}
@@ -717,10 +777,15 @@ static int uda1380_probe(struct snd_soc_codec *codec)
return 0;
err_reset:
- gpio_set_value(pdata->gpio_power, 0);
- gpio_free(pdata->gpio_reset);
+err_gpio_power_conf:
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+
+err_gpio_reset_conf:
err_gpio:
- gpio_free(pdata->gpio_power);
+ if (gpio_is_valid(pdata->gpio_reset))
+ gpio_free(pdata->gpio_reset);
+err_out:
return ret;
}
@@ -731,7 +796,6 @@ static int uda1380_remove(struct snd_soc_codec *codec)
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_power);
@@ -743,8 +807,8 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
.remove = uda1380_remove,
.suspend = uda1380_suspend,
.resume = uda1380_resume,
- .read = uda1380_read_reg_cache,
- .write = uda1380_write,
+ .read = uda1380_read_reg_cache,
+ .write = uda1380_write,
.set_bias_level = uda1380_set_bias_level,
.reg_cache_size = ARRAY_SIZE(uda1380_reg),
.reg_word_size = sizeof(u16),
@@ -764,6 +828,7 @@ static __devinit int uda1380_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, uda1380);
+ uda1380->control_data = i2c;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));