Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+105 −40
Original line number Original line Diff line number Diff line
@@ -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) {
			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)


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)
	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)
	/* 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))
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)


	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,
		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));