Merge branch 'for-2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc-2.6 into for-2.6.37
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index b317586..fc68779 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -61,15 +61,25 @@
 	"DRVDD",	/* ADC Analog and Output Driver Voltage */
 };
 
+struct aic3x_priv;
+
+struct aic3x_disable_nb {
+	struct notifier_block nb;
+	struct aic3x_priv *aic3x;
+};
+
 /* codec private data */
 struct aic3x_priv {
+	struct snd_soc_codec *codec;
 	struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
+	struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
 	enum snd_soc_control_type control_type;
 	struct aic3x_setup_data *setup;
 	void *control_data;
 	unsigned int sysclk;
 	int master;
 	int gpio_reset;
+	int power;
 #define AIC3X_MODEL_3X 0
 #define AIC3X_MODEL_33 1
 #define AIC3X_MODEL_3007 2
@@ -112,62 +122,23 @@
 };
 
 /*
- * read aic3x register cache
- */
-static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec,
-						unsigned int reg)
-{
-	u8 *cache = codec->reg_cache;
-	if (reg >= AIC3X_CACHEREGNUM)
-		return -1;
-	return cache[reg];
-}
-
-/*
- * write aic3x register cache
- */
-static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec,
-					 u8 reg, u8 value)
-{
-	u8 *cache = codec->reg_cache;
-	if (reg >= AIC3X_CACHEREGNUM)
-		return;
-	cache[reg] = value;
-}
-
-/*
- * write to the aic3x register space
- */
-static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
-		       unsigned int value)
-{
-	u8 data[2];
-
-	/* data is
-	 *   D15..D8 aic3x register offset
-	 *   D7...D0 register data
-	 */
-	data[0] = reg & 0xff;
-	data[1] = value & 0xff;
-
-	aic3x_write_reg_cache(codec, data[0], data[1]);
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-/*
- * read from the aic3x register space
+ * read from the aic3x register space. Only use for this function is if
+ * wanting to read volatile bits from those registers that has both read-only
+ * and read/write bits. All other cases should use snd_soc_read.
  */
 static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
 		      u8 *value)
 {
-	*value = reg & 0xff;
+	u8 *cache = codec->reg_cache;
 
-	value[0] = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+	if (codec->cache_only)
+		return -EINVAL;
+	if (reg >= AIC3X_CACHEREGNUM)
+		return -1;
 
-	aic3x_write_reg_cache(codec, reg, *value);
+	*value = codec->hw_read(codec, reg);
+	cache[reg] = *value;
+
 	return 0;
 }
 
@@ -646,6 +617,14 @@
 	SND_SOC_DAPM_INPUT("LINE1R"),
 	SND_SOC_DAPM_INPUT("LINE2L"),
 	SND_SOC_DAPM_INPUT("LINE2R"),
+
+	/*
+	 * Virtual output pin to detection block inside codec. This can be
+	 * used to keep codec bias on if gpio or detection features are needed.
+	 * Force pin on or construct a path with an input jack and mic bias
+	 * widgets.
+	 */
+	SND_SOC_DAPM_OUTPUT("Detection"),
 };
 
 static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = {
@@ -839,8 +818,7 @@
 	int clk;
 
 	/* select data word length */
-	data =
-	    aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
+	data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
 		break;
@@ -854,7 +832,7 @@
 		data |= (0x03 << 4);
 		break;
 	}
-	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, data);
 
 	/* Fsref can be 44100 or 48000 */
 	fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
@@ -869,17 +847,17 @@
 
 	if (bypass_pll) {
 		pll_q &= 0xf;
-		aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
-		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+		snd_soc_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+		snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
 		/* disable PLL if it is bypassed */
-		reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-		aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE);
+		reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
+		snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE);
 
 	} else {
-		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+		snd_soc_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
 		/* enable PLL when it is used */
-		reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-		aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE);
+		reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
+		snd_soc_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE);
 	}
 
 	/* Route Left DAC to left channel input and
@@ -888,7 +866,7 @@
 	data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
 	if (params_rate(params) >= 64000)
 		data |= DUAL_RATE_MODE;
-	aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
+	snd_soc_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
 
 	/* codec sample rate select */
 	data = (fsref * 20) / params_rate(params);
@@ -897,7 +875,7 @@
 	data /= 5;
 	data -= 2;
 	data |= (data << 4);
-	aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
+	snd_soc_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
 
 	if (bypass_pll)
 		return 0;
@@ -966,13 +944,16 @@
 	}
 
 found:
-	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
-	aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
-	aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
-	aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
-	aic3x_write(codec, AIC3X_PLL_PROGD_REG,
-		    (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+	data = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
+	snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
+		      data | (pll_p << PLLP_SHIFT));
+	snd_soc_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG,
+		      pll_r << PLLR_SHIFT);
+	snd_soc_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
+	snd_soc_write(codec, AIC3X_PLL_PROGC_REG,
+		      (pll_d >> 6) << PLLD_MSB_SHIFT);
+	snd_soc_write(codec, AIC3X_PLL_PROGD_REG,
+		      (pll_d & 0x3F) << PLLD_LSB_SHIFT);
 
 	return 0;
 }
@@ -980,15 +961,15 @@
 static int aic3x_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
-	u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON;
+	u8 ldac_reg = snd_soc_read(codec, LDAC_VOL) & ~MUTE_ON;
+	u8 rdac_reg = snd_soc_read(codec, RDAC_VOL) & ~MUTE_ON;
 
 	if (mute) {
-		aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
-		aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
+		snd_soc_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
+		snd_soc_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
 	} else {
-		aic3x_write(codec, LDAC_VOL, ldac_reg);
-		aic3x_write(codec, RDAC_VOL, rdac_reg);
+		snd_soc_write(codec, LDAC_VOL, ldac_reg);
+		snd_soc_write(codec, RDAC_VOL, rdac_reg);
 	}
 
 	return 0;
@@ -1012,8 +993,8 @@
 	u8 iface_areg, iface_breg;
 	int delay = 0;
 
-	iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
-	iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
+	iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+	iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1052,13 +1033,98 @@
 	}
 
 	/* set iface */
-	aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
-	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
-	aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
+	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
+	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
+	snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
 
 	return 0;
 }
 
+static int aic3x_init_3007(struct snd_soc_codec *codec)
+{
+	u8 tmp1, tmp2, *cache = codec->reg_cache;
+
+	/*
+	 * There is no need to cache writes to undocumented page 0xD but
+	 * respective page 0 register cache entries must be preserved
+	 */
+	tmp1 = cache[0xD];
+	tmp2 = cache[0x8];
+	/* Class-D speaker driver init; datasheet p. 46 */
+	snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x0D);
+	snd_soc_write(codec, 0xD, 0x0D);
+	snd_soc_write(codec, 0x8, 0x5C);
+	snd_soc_write(codec, 0x8, 0x5D);
+	snd_soc_write(codec, 0x8, 0x5C);
+	snd_soc_write(codec, AIC3X_PAGE_SELECT, 0x00);
+	cache[0xD] = tmp1;
+	cache[0x8] = tmp2;
+
+	return 0;
+}
+
+static int aic3x_regulator_event(struct notifier_block *nb,
+				 unsigned long event, void *data)
+{
+	struct aic3x_disable_nb *disable_nb =
+		container_of(nb, struct aic3x_disable_nb, nb);
+	struct aic3x_priv *aic3x = disable_nb->aic3x;
+
+	if (event & REGULATOR_EVENT_DISABLE) {
+		/*
+		 * Put codec to reset and require cache sync as at least one
+		 * of the supplies was disabled
+		 */
+		if (aic3x->gpio_reset >= 0)
+			gpio_set_value(aic3x->gpio_reset, 0);
+		aic3x->codec->cache_sync = 1;
+	}
+
+	return 0;
+}
+
+static int aic3x_set_power(struct snd_soc_codec *codec, int power)
+{
+	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+	int i, ret;
+	u8 *cache = codec->reg_cache;
+
+	if (power) {
+		ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
+					    aic3x->supplies);
+		if (ret)
+			goto out;
+		aic3x->power = 1;
+		/*
+		 * Reset release and cache sync is necessary only if some
+		 * supply was off or if there were cached writes
+		 */
+		if (!codec->cache_sync)
+			goto out;
+
+		if (aic3x->gpio_reset >= 0) {
+			udelay(1);
+			gpio_set_value(aic3x->gpio_reset, 1);
+		}
+
+		/* Sync reg_cache with the hardware */
+		codec->cache_only = 0;
+		for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++)
+			snd_soc_write(codec, i, cache[i]);
+		if (aic3x->model == AIC3X_MODEL_3007)
+			aic3x_init_3007(codec);
+		codec->cache_sync = 0;
+	} else {
+		aic3x->power = 0;
+		/* HW writes are needless when bias is off */
+		codec->cache_only = 1;
+		ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),
+					     aic3x->supplies);
+	}
+out:
+	return ret;
+}
+
 static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 				enum snd_soc_bias_level level)
 {
@@ -1069,23 +1135,29 @@
 	case SND_SOC_BIAS_ON:
 		break;
 	case SND_SOC_BIAS_PREPARE:
-		if (aic3x->master) {
+		if (codec->bias_level == SND_SOC_BIAS_STANDBY &&
+		    aic3x->master) {
 			/* enable pll */
-			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
-				    reg | PLL_ENABLE);
+			reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
+			snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
+				      reg | PLL_ENABLE);
 		}
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		/* fall through and disable pll */
-	case SND_SOC_BIAS_OFF:
-		if (aic3x->master) {
+		if (!aic3x->power)
+			aic3x_set_power(codec, 1);
+		if (codec->bias_level == SND_SOC_BIAS_PREPARE &&
+		    aic3x->master) {
 			/* disable pll */
-			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
-				    reg & ~PLL_ENABLE);
+			reg = snd_soc_read(codec, AIC3X_PLL_PROGA_REG);
+			snd_soc_write(codec, AIC3X_PLL_PROGA_REG,
+				      reg & ~PLL_ENABLE);
 		}
 		break;
+	case SND_SOC_BIAS_OFF:
+		if (aic3x->power)
+			aic3x_set_power(codec, 0);
+		break;
 	}
 	codec->bias_level = level;
 
@@ -1096,8 +1168,8 @@
 {
 	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
 	u8 bit = gpio ? 3: 0;
-	u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
-	aic3x_write(codec, reg, val | (!!state << bit));
+	u8 val = snd_soc_read(codec, reg) & ~(1 << bit);
+	snd_soc_write(codec, reg, val | (!!state << bit));
 }
 EXPORT_SYMBOL_GPL(aic3x_set_gpio);
 
@@ -1126,7 +1198,7 @@
 	if (detect & AIC3X_HEADSET_DETECT_MASK)
 		val |= AIC3X_HEADSET_DETECT_ENABLED;
 
-	aic3x_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
+	snd_soc_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
 }
 EXPORT_SYMBOL_GPL(aic3x_set_headset_detection);
 
@@ -1184,17 +1256,6 @@
 
 static int aic3x_resume(struct snd_soc_codec *codec)
 {
-	int i;
-	u8 data[2];
-	u8 *cache = codec->reg_cache;
-
-	/* Sync reg_cache with the hardware */
-	for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) {
-		data[0] = i;
-		data[1] = cache[i];
-		codec->hw_write(codec->control_data, data, 2);
-	}
-
 	aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return 0;
@@ -1209,104 +1270,133 @@
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
 	int reg;
 
-	aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
-	aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
+	snd_soc_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
+	snd_soc_write(codec, AIC3X_RESET, SOFT_RESET);
 
 	/* DAC default volume and mute */
-	aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
-	aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
+	snd_soc_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
+	snd_soc_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
 
 	/* DAC to HP default volume and route to Output mixer */
-	aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
-	aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
-	aic3x_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
-	aic3x_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
 	/* DAC to Line Out default volume and route to Output mixer */
-	aic3x_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
-	aic3x_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
 	/* DAC to Mono Line Out default volume and route to Output mixer */
-	aic3x_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
-	aic3x_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+	snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
 
 	/* unmute all outputs */
-	reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
-	aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE);
-	reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
-	aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE);
-	reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
-	aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
-	reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
-	aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE);
-	reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
-	aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE);
-	reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
-	aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE);
-	reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
-	aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, LLOPM_CTRL);
+	snd_soc_write(codec, LLOPM_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, RLOPM_CTRL);
+	snd_soc_write(codec, RLOPM_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, MONOLOPM_CTRL);
+	snd_soc_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, HPLOUT_CTRL);
+	snd_soc_write(codec, HPLOUT_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, HPROUT_CTRL);
+	snd_soc_write(codec, HPROUT_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, HPLCOM_CTRL);
+	snd_soc_write(codec, HPLCOM_CTRL, reg | UNMUTE);
+	reg = snd_soc_read(codec, HPRCOM_CTRL);
+	snd_soc_write(codec, HPRCOM_CTRL, reg | UNMUTE);
 
 	/* ADC default volume and unmute */
-	aic3x_write(codec, LADC_VOL, DEFAULT_GAIN);
-	aic3x_write(codec, RADC_VOL, DEFAULT_GAIN);
+	snd_soc_write(codec, LADC_VOL, DEFAULT_GAIN);
+	snd_soc_write(codec, RADC_VOL, DEFAULT_GAIN);
 	/* By default route Line1 to ADC PGA mixer */
-	aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0);
-	aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0);
+	snd_soc_write(codec, LINE1L_2_LADC_CTRL, 0x0);
+	snd_soc_write(codec, LINE1R_2_RADC_CTRL, 0x0);
 
 	/* PGA to HP Bypass default volume, disconnect from Output Mixer */
-	aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
-	aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
-	aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
-	aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
 	/* PGA to Line Out default volume, disconnect from Output Mixer */
-	aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
-	aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
 	/* PGA to Mono Line Out default volume, disconnect from Output Mixer */
-	aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
-	aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
 
 	/* Line2 to HP Bypass default volume, disconnect from Output Mixer */
-	aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
-	aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
-	aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
-	aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
 	/* Line2 Line Out default volume, disconnect from Output Mixer */
-	aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
-	aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
 	/* Line2 to Mono Out default volume, disconnect from Output Mixer */
-	aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
-	aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
+	snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
 
 	if (aic3x->model == AIC3X_MODEL_3007) {
-		/* Class-D speaker driver init; datasheet p. 46 */
-		aic3x_write(codec, AIC3X_PAGE_SELECT, 0x0D);
-		aic3x_write(codec, 0xD, 0x0D);
-		aic3x_write(codec, 0x8, 0x5C);
-		aic3x_write(codec, 0x8, 0x5D);
-		aic3x_write(codec, 0x8, 0x5C);
-		aic3x_write(codec, AIC3X_PAGE_SELECT, 0x00);
-		aic3x_write(codec, CLASSD_CTRL, 0);
+		aic3x_init_3007(codec);
+		snd_soc_write(codec, CLASSD_CTRL, 0);
 	}
 
-	/* off, with power on */
-	aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	return 0;
 }
 
 static int aic3x_probe(struct snd_soc_codec *codec)
 {
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+	int ret, i;
 
-	codec->hw_write = (hw_write_t) i2c_master_send;
 	codec->control_data = aic3x->control_data;
+	aic3x->codec = codec;
+	codec->idle_bias_off = 1;
 
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	if (aic3x->gpio_reset >= 0) {
+		ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
+		if (ret != 0)
+			goto err_gpio;
+		gpio_direction_output(aic3x->gpio_reset, 0);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
+		aic3x->supplies[i].supply = aic3x_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies),
+				 aic3x->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err_get;
+	}
+	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
+		aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
+		aic3x->disable_nb[i].aic3x = aic3x;
+		ret = regulator_register_notifier(aic3x->supplies[i].consumer,
+						  &aic3x->disable_nb[i].nb);
+		if (ret) {
+			dev_err(codec->dev,
+				"Failed to request regulator notifier: %d\n",
+				 ret);
+			goto err_notif;
+		}
+	}
+
+	codec->cache_only = 1;
 	aic3x_init(codec);
 
 	if (aic3x->setup) {
 		/* setup GPIO functions */
-		aic3x_write(codec, AIC3X_GPIO1_REG,
-			    (aic3x->setup->gpio_func[0] & 0xf) << 4);
-		aic3x_write(codec, AIC3X_GPIO2_REG,
-			    (aic3x->setup->gpio_func[1] & 0xf) << 4);
+		snd_soc_write(codec, AIC3X_GPIO1_REG,
+			      (aic3x->setup->gpio_func[0] & 0xf) << 4);
+		snd_soc_write(codec, AIC3X_GPIO2_REG,
+			      (aic3x->setup->gpio_func[1] & 0xf) << 4);
 	}
 
 	snd_soc_add_controls(codec, aic3x_snd_controls,
@@ -1317,17 +1407,39 @@
 	aic3x_add_widgets(codec);
 
 	return 0;
+
+err_notif:
+	while (i--)
+		regulator_unregister_notifier(aic3x->supplies[i].consumer,
+					      &aic3x->disable_nb[i].nb);
+	regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
+err_get:
+	if (aic3x->gpio_reset >= 0)
+		gpio_free(aic3x->gpio_reset);
+err_gpio:
+	kfree(aic3x);
+	return ret;
 }
 
 static int aic3x_remove(struct snd_soc_codec *codec)
 {
+	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+	int i;
+
 	aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	if (aic3x->gpio_reset >= 0) {
+		gpio_set_value(aic3x->gpio_reset, 0);
+		gpio_free(aic3x->gpio_reset);
+	}
+	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
+		regulator_unregister_notifier(aic3x->supplies[i].consumer,
+					      &aic3x->disable_nb[i].nb);
+	regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
+
 	return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
-	.read = aic3x_read_reg_cache,
-	.write = aic3x_write,
 	.set_bias_level = aic3x_set_bias_level,
 	.reg_cache_size = ARRAY_SIZE(aic3x_reg),
 	.reg_word_size = sizeof(u8),
@@ -1361,7 +1473,7 @@
 {
 	struct aic3x_pdata *pdata = i2c->dev.platform_data;
 	struct aic3x_priv *aic3x;
-	int ret, i;
+	int ret;
 	const struct i2c_device_id *tbl;
 
 	aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
@@ -1371,6 +1483,8 @@
 	}
 
 	aic3x->control_data = i2c;
+	aic3x->control_type = SND_SOC_I2C;
+
 	i2c_set_clientdata(i2c, aic3x);
 	if (pdata) {
 		aic3x->gpio_reset = pdata->gpio_reset;
@@ -1379,68 +1493,21 @@
 		aic3x->gpio_reset = -1;
 	}
 
-	if (aic3x->gpio_reset >= 0) {
-		ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
-		if (ret != 0)
-			goto err_gpio;
-		gpio_direction_output(aic3x->gpio_reset, 0);
-	}
-
 	for (tbl = aic3x_i2c_id; tbl->name[0]; tbl++) {
 		if (!strcmp(tbl->name, id->name))
 			break;
 	}
 	aic3x->model = tbl - aic3x_i2c_id;
 
-	for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
-		aic3x->supplies[i].supply = aic3x_supply_names[i];
-
-	ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(aic3x->supplies),
-				 aic3x->supplies);
-	if (ret != 0) {
-		dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
-		goto err_get;
-	}
-
-	ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
-				    aic3x->supplies);
-	if (ret != 0) {
-		dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
-		goto err_enable;
-	}
-
-	if (aic3x->gpio_reset >= 0) {
-		udelay(1);
-		gpio_set_value(aic3x->gpio_reset, 1);
-	}
-
 	ret = snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_aic3x, &aic3x_dai, 1);
 	if (ret < 0)
-		goto err_enable;
-	return ret;
-
-err_enable:
-	regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
-err_get:
-	if (aic3x->gpio_reset >= 0)
-		gpio_free(aic3x->gpio_reset);
-err_gpio:
-	kfree(aic3x);
+		kfree(aic3x);
 	return ret;
 }
 
 static int aic3x_i2c_remove(struct i2c_client *client)
 {
-	struct aic3x_priv *aic3x = i2c_get_clientdata(client);
-
-	if (aic3x->gpio_reset >= 0) {
-		gpio_set_value(aic3x->gpio_reset, 0);
-		gpio_free(aic3x->gpio_reset);
-	}
-	regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
-	regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
-
 	snd_soc_unregister_codec(&client->dev);
 	kfree(i2c_get_clientdata(client));
 	return 0;
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 0cd5909..0c47c78 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -193,6 +193,9 @@
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
 
+	if (wl1273->mode == ucontrol->value.integer.value[0])
+		return 0;
+
 	/* Do not allow changes while stream is running */
 	if (codec->active)
 		return -EPERM;
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 7ba5690..9969618 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -759,7 +759,7 @@
 	.ops = &mcbsp_dai_ops,
 };
 
-int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
+static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
 			struct snd_ctl_elem_info *uinfo)
 {
 	struct soc_mixer_control *mc =