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

Commit 5e7c0344 authored by Daniel Mack's avatar Daniel Mack Committed by Mark Brown
Browse files

ASoC: cs4270: add power management support

parent 80ab8817
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@
 * - The machine driver's 'startup' function must call
 *   cs4270_set_dai_sysclk() with the value of MCLK.
 * - Only I2S and left-justified modes are supported
 * - Power management is not supported
 * - Power management is supported
 */

#include <linux/module.h>
@@ -27,6 +27,7 @@
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/i2c.h>
#include <linux/delay.h>

#include "cs4270.h"

@@ -65,6 +66,8 @@
#define CS4270_PWRCTL_PDN_ADC	0x20
#define CS4270_PWRCTL_PDN_DAC	0x02
#define CS4270_PWRCTL_PDN	0x01
#define CS4270_PWRCTL_PDN_ALL	\
	(CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN)
#define CS4270_MODE_SPEED_MASK	0x30
#define CS4270_MODE_1X		0x00
#define CS4270_MODE_2X		0x10
@@ -788,6 +791,57 @@ static struct i2c_device_id cs4270_id[] = {
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);

#ifdef CONFIG_PM

/* This suspend/resume implementation can handle both - a simple standby
 * where the codec remains powered, and a full suspend, where the voltage
 * domain the codec is connected to is teared down and/or any other hardware
 * reset condition is asserted.
 *
 * The codec's own power saving features are enabled in the suspend callback,
 * and all registers are written back to the hardware when resuming.
 */

static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
{
	struct cs4270_private *cs4270 = i2c_get_clientdata(client);
	struct snd_soc_codec *codec = &cs4270->codec;
	int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;

	return snd_soc_write(codec, CS4270_PWRCTL, reg);
}

static int cs4270_i2c_resume(struct i2c_client *client)
{
	struct cs4270_private *cs4270 = i2c_get_clientdata(client);
	struct snd_soc_codec *codec = &cs4270->codec;
	int reg;

	/* In case the device was put to hard reset during sleep, we need to
	 * wait 500ns here before any I2C communication. */
	ndelay(500);

	/* first restore the entire register cache ... */
	for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) {
		u8 val = snd_soc_read(codec, reg);

		if (i2c_smbus_write_byte_data(client, reg, val)) {
			dev_err(codec->dev, "i2c write failed\n");
			return -EIO;
		}
	}

	/* ... then disable the power-down bits */
	reg = snd_soc_read(codec, CS4270_PWRCTL);
	reg &= ~CS4270_PWRCTL_PDN_ALL;

	return snd_soc_write(codec, CS4270_PWRCTL, reg);
}
#else
#define cs4270_i2c_suspend	NULL
#define cs4270_i2c_resume	NULL
#endif /* CONFIG_PM */

/*
 * cs4270_i2c_driver - I2C device identification
 *
@@ -802,6 +856,8 @@ static struct i2c_driver cs4270_i2c_driver = {
	.id_table = cs4270_id,
	.probe = cs4270_i2c_probe,
	.remove = cs4270_i2c_remove,
	.suspend = cs4270_i2c_suspend,
	.resume = cs4270_i2c_resume,
};

/*