ASoC: cs4270: add power management support
authorDaniel Mack <daniel@caiaq.de>
Tue, 5 May 2009 23:26:01 +0000 (01:26 +0200)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Thu, 7 May 2009 08:00:27 +0000 (09:00 +0100)
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Acked-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
sound/soc/codecs/cs4270.c

index 153124b2e3b130c5de65a36d71d0bb416e96e433..a32b8226c8a43bc19a2f3852ef24b3e802238c05 100644 (file)
@@ -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,
 };
 
 /*