[ALSA] hda-codec - optimize resume using caches

So far, the driver looked the table of snd_kcontrol_new used for creating
mixer elements and forces to call each of its put callbacks in PM resume
code.  This is too ugly and hackish.
Now, the resume is simplified using the codec amp and command register
caches.  The driver simply restores the values that have been written
in the cache table.  With this simplification, most codec support codes
don't require any special resume callback.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 6652a53..1d31da4 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -836,12 +836,13 @@
 		return 0;
 	val &= mask;
 	val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
-	if (info->vol[ch] == val && !codec->in_resume)
+	if (info->vol[ch] == val)
 		return 0;
 	put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	return 1;
 }
 
+#ifdef CONFIG_PM
 /* resume the all amp commands from the cache */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
@@ -865,6 +866,7 @@
 		}
 	}
 }
+#endif /* CONFIG_PM */
 
 /*
  * AMP control callbacks
@@ -1272,11 +1274,13 @@
 	change = codec->spdif_ctls != val;
 	codec->spdif_ctls = val;
 
-	if (change || codec->in_resume) {
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    val & 0xff);
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2,
-				    val >> 8);
+	if (change) {
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_DIGI_CONVERT_1,
+					  val & 0xff);
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_DIGI_CONVERT_2,
+					  val >> 8);
 	}
 
 	mutex_unlock(&codec->spdif_mutex);
@@ -1307,17 +1311,19 @@
 	if (ucontrol->value.integer.value[0])
 		val |= AC_DIG1_ENABLE;
 	change = codec->spdif_ctls != val;
-	if (change || codec->in_resume) {
+	if (change) {
 		codec->spdif_ctls = val;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    val & 0xff);
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_DIGI_CONVERT_1,
+					  val & 0xff);
 		/* unmute amp switch (if any) */
 		if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
-		    (val & AC_DIG1_ENABLE))
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
-					    AC_AMP_SET_OUTPUT);
+		    (val & AC_DIG1_ENABLE)) {
+			snd_hda_codec_amp_update(codec, nid, 0, HDA_OUTPUT, 0,
+						 0x80, 0x00);
+			snd_hda_codec_amp_update(codec, nid, 1, HDA_OUTPUT, 0,
+						 0x80, 0x00);
+		}
 	}
 	mutex_unlock(&codec->spdif_mutex);
 	return change;
@@ -1409,10 +1415,10 @@
 
 	mutex_lock(&codec->spdif_mutex);
 	change = codec->spdif_in_enable != val;
-	if (change || codec->in_resume) {
+	if (change) {
 		codec->spdif_in_enable = val;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    val);
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_DIGI_CONVERT_1, val);
 	}
 	mutex_unlock(&codec->spdif_mutex);
 	return change;
@@ -1482,6 +1488,10 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+/*
+ * command cache
+ */
 
 /* build a 32bit cache key with the widget id and the command parameter */
 #define build_cmd_cache_key(nid, verb)	((verb << 8) | nid)
@@ -1548,6 +1558,7 @@
 		snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
 					  seq->param);
 }
+#endif /* CONFIG_PM */
 
 /*
  * set power state of the codec
@@ -2122,12 +2133,12 @@
 
 	mode = ucontrol->value.enumerated.item[0];
 	snd_assert(mode < num_chmodes, return -EINVAL);
-	if (*max_channelsp == chmode[mode].channels && !codec->in_resume)
+	if (*max_channelsp == chmode[mode].channels)
 		return 0;
 	/* change the current channel setting */
 	*max_channelsp = chmode[mode].channels;
 	if (chmode[mode].sequence)
-		snd_hda_sequence_write(codec, chmode[mode].sequence);
+		snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
 	return 1;
 }
 
@@ -2160,10 +2171,10 @@
 	idx = ucontrol->value.enumerated.item[0];
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (*cur_val == idx && !codec->in_resume)
+	if (*cur_val == idx)
 		return 0;
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
-			    imux->items[idx].index);
+	snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+				  imux->items[idx].index);
 	*cur_val = idx;
 	return 1;
 }
@@ -2608,65 +2619,13 @@
 				    AC_PWRST_D0);
 		if (codec->patch_ops.resume)
 			codec->patch_ops.resume(codec);
-	}
-	return 0;
-}
-
-/**
- * snd_hda_resume_ctls - resume controls in the new control list
- * @codec: the HDA codec
- * @knew: the array of struct snd_kcontrol_new
- *
- * This function resumes the mixer controls in the struct snd_kcontrol_new array,
- * originally for snd_hda_add_new_ctls().
- * The array must be terminated with an empty entry as terminator.
- */
-int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
-{
-	struct snd_ctl_elem_value *val;
-
-	val = kmalloc(sizeof(*val), GFP_KERNEL);
-	if (!val)
-		return -ENOMEM;
-	codec->in_resume = 1;
-	for (; knew->name; knew++) {
-		int i, count;
-		count = knew->count ? knew->count : 1;
-		for (i = 0; i < count; i++) {
-			memset(val, 0, sizeof(*val));
-			val->id.iface = knew->iface;
-			val->id.device = knew->device;
-			val->id.subdevice = knew->subdevice;
-			strcpy(val->id.name, knew->name);
-			val->id.index = knew->index ? knew->index : i;
-			/* Assume that get callback reads only from cache,
-			 * not accessing to the real hardware
-			 */
-			if (snd_ctl_elem_read(codec->bus->card, val) < 0)
-				continue;
-			snd_ctl_elem_write(codec->bus->card, NULL, val);
+		else {
+			codec->patch_ops.init(codec);
+			snd_hda_codec_resume_amp(codec);
+			snd_hda_codec_resume_cache(codec);
 		}
 	}
-	codec->in_resume = 0;
-	kfree(val);
 	return 0;
 }
 
-/**
- * snd_hda_resume_spdif_out - resume the digital out
- * @codec: the HDA codec
- */
-int snd_hda_resume_spdif_out(struct hda_codec *codec)
-{
-	return snd_hda_resume_ctls(codec, dig_mixes);
-}
-
-/**
- * snd_hda_resume_spdif_in - resume the digital in
- * @codec: the HDA codec
- */
-int snd_hda_resume_spdif_in(struct hda_codec *codec)
-{
-	return snd_hda_resume_ctls(codec, dig_in_ctls);
-}
 #endif
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index ef94c91..92938d2 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -552,11 +552,6 @@
 	/* set by patch */
 	struct hda_codec_ops patch_ops;
 
-	/* resume phase - all controls should update even if
-	 * the values are not changed
-	 */
-	unsigned int in_resume;
-
 	/* PCM to create, set by patch_ops.build_pcms callback */
 	unsigned int num_pcms;
 	struct hda_pcm *pcm_info;
@@ -622,11 +617,16 @@
 int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
 
 /* cached write */
+#ifdef CONFIG_PM
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm);
 void snd_hda_sequence_write_cache(struct hda_codec *codec,
 				  const struct hda_verb *seq);
 void snd_hda_codec_resume_cache(struct hda_codec *codec);
+#else
+#define snd_hda_codec_write_cache	snd_hda_codec_write
+#define snd_hda_sequence_write_cache	snd_hda_sequence_write
+#endif
 
 /*
  * Mixer
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 000287f..d5f11801 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -218,9 +218,9 @@
 	ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
 	if (val >= ofs)
 		val -= ofs;
-	val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
-	val |= AC_AMP_SET_OUTPUT;
-	return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+	snd_hda_codec_amp_update(codec, node->nid, 0, HDA_OUTPUT, 0, 0xff, val);
+	snd_hda_codec_amp_update(codec, node->nid, 0, HDA_OUTPUT, 1, 0xff, val);
+	return 0;
 }
 
 /*
@@ -234,11 +234,11 @@
 	ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
 	if (val >= ofs)
 		val -= ofs;
-	val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
-	val |= AC_AMP_SET_INPUT;
-	// awk added - fixed to allow unmuting of indexed amps
-	val |= index << AC_AMP_SET_INDEX_SHIFT;
-	return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+	snd_hda_codec_amp_update(codec, node->nid, 0, HDA_INPUT, index,
+				 0xff, val);
+	snd_hda_codec_amp_update(codec, node->nid, 1, HDA_INPUT, index,
+				 0xff, val);
+	return 0;
 }
 
 /*
@@ -248,7 +248,8 @@
 				   unsigned int index)
 {
 	snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
-	return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_CONNECT_SEL, index);
+	return snd_hda_codec_write_cache(codec, node->nid, 0,
+					 AC_VERB_SET_CONNECT_SEL, index);
 }
 
 /*
@@ -379,7 +380,7 @@
 			/* unmute the PIN output */
 			unmute_output(codec, node);
 			/* set PIN-Out enable */
-			snd_hda_codec_write(codec, node->nid, 0,
+			snd_hda_codec_write_cache(codec, node->nid, 0,
 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
 					    AC_PINCTL_OUT_EN |
 					    ((node->pin_caps & AC_PINCAP_HP_DRV) ?
@@ -570,7 +571,8 @@
 	/* unmute the PIN external input */
 	unmute_input(codec, node, 0); /* index = 0? */
 	/* set PIN-In enable */
-	snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+	snd_hda_codec_write_cache(codec, node->nid, 0,
+				  AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
 
 	return 1; /* found */
 }
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5120897..8dec32cf 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -84,7 +84,9 @@
 			   int direction, int index);
 int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 			     int direction, int idx, int mask, int val);
+#ifdef CONFIG_PM
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
+#endif
 
 /* mono switch binding multiple inputs */
 #define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
@@ -257,15 +259,6 @@
 			 struct snd_kcontrol_new *knew);
 
 /*
- * power management
- */
-#ifdef CONFIG_PM
-int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew);
-int snd_hda_resume_spdif_out(struct hda_codec *codec);
-int snd_hda_resume_spdif_in(struct hda_codec *codec);
-#endif
-
-/*
  * unsolicited event handler
  */
 
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index cc2e944..f20ddd8 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -318,31 +318,11 @@
 	kfree(codec->spec);
 }
 
-#ifdef CONFIG_PM
-static int ad198x_resume(struct hda_codec *codec)
-{
-	struct ad198x_spec *spec = codec->spec;
-	int i;
-
-	codec->patch_ops.init(codec);
-	for (i = 0; i < spec->num_mixers; i++)
-		snd_hda_resume_ctls(codec, spec->mixers[i]);
-	if (spec->multiout.dig_out_nid)
-		snd_hda_resume_spdif_out(codec);
-	if (spec->dig_in_nid)
-		snd_hda_resume_spdif_in(codec);
-	return 0;
-}
-#endif
-
 static struct hda_codec_ops ad198x_patch_ops = {
 	.build_controls = ad198x_build_controls,
 	.build_pcms = ad198x_build_pcms,
 	.init = ad198x_init,
 	.free = ad198x_free,
-#ifdef CONFIG_PM
-	.resume = ad198x_resume,
-#endif
 };
 
 
@@ -376,12 +356,12 @@
 	eapd = ucontrol->value.integer.value[0];
 	if (invert)
 		eapd = !eapd;
-	if (eapd == spec->cur_eapd && ! codec->in_resume)
+	if (eapd == spec->cur_eapd)
 		return 0;
 	spec->cur_eapd = eapd;
-	snd_hda_codec_write(codec, nid,
-			    0, AC_VERB_SET_EAPD_BTLENABLE,
-			    eapd ? 0x02 : 0x00);
+	snd_hda_codec_write_cache(codec, nid,
+				  0, AC_VERB_SET_EAPD_BTLENABLE,
+				  eapd ? 0x02 : 0x00);
 	return 1;
 }
 
@@ -882,8 +862,9 @@
 
 	if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
 		spec->spdif_route = ucontrol->value.enumerated.item[0];
-		snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
-				    AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
+		snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  spec->spdif_route);
 		return 1;
 	}
 	return 0;
@@ -1824,33 +1805,34 @@
 					 AC_VERB_GET_AMP_GAIN_MUTE,
 					 AC_AMP_GET_INPUT);
 		change = sel & 0x80;
-		if (change || codec->in_resume) {
-			snd_hda_codec_write(codec, 0x1d, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_UNMUTE(0));
-			snd_hda_codec_write(codec, 0x1d, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_MUTE(1));
+		if (change) {
+			snd_hda_codec_write_cache(codec, 0x1d, 0,
+						  AC_VERB_SET_AMP_GAIN_MUTE,
+						  AMP_IN_UNMUTE(0));
+			snd_hda_codec_write_cache(codec, 0x1d, 0,
+						  AC_VERB_SET_AMP_GAIN_MUTE,
+						  AMP_IN_MUTE(1));
 		}
 	} else {
 		sel = snd_hda_codec_read(codec, 0x1d, 0,
 					 AC_VERB_GET_AMP_GAIN_MUTE,
 					 AC_AMP_GET_INPUT | 0x01);
 		change = sel & 0x80;
-		if (change || codec->in_resume) {
-			snd_hda_codec_write(codec, 0x1d, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_MUTE(0));
-			snd_hda_codec_write(codec, 0x1d, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_UNMUTE(1));
+		if (change) {
+			snd_hda_codec_write_cache(codec, 0x1d, 0,
+						  AC_VERB_SET_AMP_GAIN_MUTE,
+						  AMP_IN_MUTE(0));
+			snd_hda_codec_write_cache(codec, 0x1d, 0,
+						  AC_VERB_SET_AMP_GAIN_MUTE,
+						  AMP_IN_UNMUTE(1));
 		}
 		sel = snd_hda_codec_read(codec, 0x0b, 0,
 					 AC_VERB_GET_CONNECT_SEL, 0) + 1;
 		change |= sel != val;
-		if (change || codec->in_resume)
-			snd_hda_codec_write(codec, 0x0b, 0,
-					    AC_VERB_SET_CONNECT_SEL, val - 1);
+		if (change)
+			snd_hda_codec_write_cache(codec, 0x0b, 0,
+						  AC_VERB_SET_CONNECT_SEL,
+						  val - 1);
 	}
 	return change;
 }
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index 72d3ab9..fbb8969 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -62,19 +62,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-/*
- * resume
- */
-static int atihdmi_resume(struct hda_codec *codec)
-{
-	atihdmi_init(codec);
-	snd_hda_resume_spdif_out(codec);
-
-	return 0;
-}
-#endif
-
 /*
  * Digital out
  */
@@ -141,9 +128,6 @@
 	.build_pcms = atihdmi_build_pcms,
 	.init = atihdmi_init,
 	.free = atihdmi_free,
-#ifdef CONFIG_PM
-	.resume = atihdmi_resume,
-#endif
 };
 
 static int patch_atihdmi(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 3c722e6..2468f31 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -427,27 +427,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-/*
- * resume
- */
-static int cmi9880_resume(struct hda_codec *codec)
-{
-	struct cmi_spec *spec = codec->spec;
-
-	cmi9880_init(codec);
-	snd_hda_resume_ctls(codec, cmi9880_basic_mixer);
-	if (spec->channel_modes)
-		snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer);
-	if (spec->multiout.dig_out_nid)
-		snd_hda_resume_spdif_out(codec);
-	if (spec->dig_in_nid)
-		snd_hda_resume_spdif_in(codec);
-
-	return 0;
-}
-#endif
-
 /*
  * Analog playback callbacks
  */
@@ -635,9 +614,6 @@
 	.build_pcms = cmi9880_build_pcms,
 	.init = cmi9880_init,
 	.free = cmi9880_free,
-#ifdef CONFIG_PM
-	.resume = cmi9880_resume,
-#endif
 };
 
 static int patch_cmi9880(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2603431..f1b6d0e 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -311,23 +311,6 @@
 	kfree(codec->spec);
 }
 
-#ifdef CONFIG_PM
-static int conexant_resume(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	int i;
-
-	codec->patch_ops.init(codec);
-	for (i = 0; i < spec->num_mixers; i++)
-		snd_hda_resume_ctls(codec, spec->mixers[i]);
-	if (spec->multiout.dig_out_nid)
-		snd_hda_resume_spdif_out(codec);
-	if (spec->dig_in_nid)
-		snd_hda_resume_spdif_in(codec);
-	return 0;
-}
-#endif
-
 static int conexant_build_controls(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
@@ -358,9 +341,6 @@
 	.build_pcms = conexant_build_pcms,
 	.init = conexant_init,
 	.free = conexant_free,
-#ifdef CONFIG_PM
-	.resume = conexant_resume,
-#endif
 };
 
 /*
@@ -396,13 +376,13 @@
 	eapd = ucontrol->value.integer.value[0];
 	if (invert)
 		eapd = !eapd;
-	if (eapd == spec->cur_eapd && !codec->in_resume)
+	if (eapd == spec->cur_eapd)
 		return 0;
 	
 	spec->cur_eapd = eapd;
-	snd_hda_codec_write(codec, nid,
-			    0, AC_VERB_SET_EAPD_BTLENABLE,
-			    eapd ? 0x02 : 0x00);
+	snd_hda_codec_write_cache(codec, nid,
+				  0, AC_VERB_SET_EAPD_BTLENABLE,
+				  eapd ? 0x02 : 0x00);
 	return 1;
 }
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 39c08bb..6301113 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -442,8 +442,9 @@
 	change = pinctl != alc_pin_mode_values[val];
 	if (change) {
 		/* Set pin mode to that requested */
-		snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    alc_pin_mode_values[val]);
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_PIN_WIDGET_CONTROL,
+					  alc_pin_mode_values[val]);
 
 		/* Also enable the retasking pin's input/output as required 
 		 * for the requested pin mode.  Enum values of 2 or less are
@@ -456,19 +457,23 @@
 		 * this turns out to be necessary in the future.
 		 */
 		if (val <= 2) {
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_OUT_MUTE);
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_UNMUTE(0));
+			snd_hda_codec_amp_update(codec, nid, 0, HDA_OUTPUT, 0,
+						 0x80, 0x80);
+			snd_hda_codec_amp_update(codec, nid, 1, HDA_OUTPUT, 0,
+						 0x80, 0x80);
+			snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT, 0,
+						 0x80, 0x00);
+			snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT, 0,
+						 0x80, 0x00);
 		} else {
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_MUTE(0));
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_OUT_UNMUTE);
+			snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT, 0,
+						 0x80, 0x80);
+			snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT, 0,
+						 0x80, 0x80);
+			snd_hda_codec_amp_update(codec, nid, 0, HDA_OUTPUT, 0,
+						 0x80, 0x00);
+			snd_hda_codec_amp_update(codec, nid, 1, HDA_OUTPUT, 0,
+						 0x80, 0x00);
 		}
 	}
 	return change;
@@ -520,7 +525,8 @@
 		gpio_data &= ~mask;
 	else
 		gpio_data |= mask;
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
+	snd_hda_codec_write_cache(codec, nid, 0,
+				  AC_VERB_SET_GPIO_DATA, gpio_data);
 
 	return change;
 }
@@ -573,8 +579,8 @@
 		ctrl_data &= ~mask;
 	else
 		ctrl_data |= mask;
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-			    ctrl_data);
+	snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+				  ctrl_data);
 
 	return change;
 }
@@ -2026,27 +2032,6 @@
 		spec->unsol_event(codec, res);
 }
 
-#ifdef CONFIG_PM
-/*
- * resume
- */
-static int alc_resume(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	alc_init(codec);
-	for (i = 0; i < spec->num_mixers; i++)
-		snd_hda_resume_ctls(codec, spec->mixers[i]);
-	if (spec->multiout.dig_out_nid)
-		snd_hda_resume_spdif_out(codec);
-	if (spec->dig_in_nid)
-		snd_hda_resume_spdif_in(codec);
-
-	return 0;
-}
-#endif
-
 /*
  * Analog playback callbacks
  */
@@ -2278,9 +2263,6 @@
 	.init = alc_init,
 	.free = alc_free,
 	.unsol_event = alc_unsol_event,
-#ifdef CONFIG_PM
-	.resume = alc_resume,
-#endif
 };
 
 
@@ -2377,11 +2359,15 @@
 				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 	new_ctl = ctls[ucontrol->value.enumerated.item[0]];
 	if (old_ctl != new_ctl) {
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    (ucontrol->value.enumerated.item[0] >= 3 ?
-				     0xb080 : 0xb000));
+		int val;
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_PIN_WIDGET_CONTROL,
+					  new_ctl);
+		val = ucontrol->value.enumerated.item[0] >= 3 ? 0x80 : 0x00;
+		snd_hda_codec_amp_update(codec, nid, 0, HDA_OUTPUT, 0,
+					 0x80, val);
+		snd_hda_codec_amp_update(codec, nid, 1, HDA_OUTPUT, 0,
+					 0x80, val);
 		return 1;
 	}
 	return 0;
@@ -2424,7 +2410,8 @@
 	sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
 	if (ucontrol->value.enumerated.item[0] != sel) {
 		sel = ucontrol->value.enumerated.item[0] & 3;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel);
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_CONNECT_SEL, sel);
 		return 1;
 	}
 	return 0;
@@ -4054,13 +4041,17 @@
         present = snd_hda_codec_read(codec, 0x0f, 0,
                                      AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
 	if (present) {
-		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 1);
-		snd_hda_codec_write(codec, 0x0f, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+		snd_hda_codec_write_cache(codec, 0x01, 0,
+					  AC_VERB_SET_GPIO_DATA, 1);
+		snd_hda_codec_write_cache(codec, 0x0f, 0,
+					  AC_VERB_SET_PIN_WIDGET_CONTROL,
+					  PIN_HP);
 	} else {
-		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
-		snd_hda_codec_write(codec, 0x0f, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_codec_write_cache(codec, 0x01, 0,
+					  AC_VERB_SET_GPIO_DATA, 0);
+		snd_hda_codec_write_cache(codec, 0x0f, 0,
+					  AC_VERB_SET_PIN_WIDGET_CONTROL,
+					  PIN_OUT);
 	}
 }
 
@@ -4797,12 +4788,16 @@
 	idx = ucontrol->value.enumerated.item[0];
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (*cur_val == idx && !codec->in_resume)
+	if (*cur_val == idx)
 		return 0;
 	for (i = 0; i < imux->num_items; i++) {
-		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    v | (imux->items[i].index << 8));
+		unsigned int v = (i == idx) ? 0x00 : 0x80;
+		snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT,
+					 imux->items[i].index,
+					 0x80, v);
+		snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT,
+					 imux->items[i].index,
+					 0x80, v);
 	}
 	*cur_val = idx;
 	return 1;
@@ -5187,7 +5182,8 @@
 				 0x80, present ? 0x80 : 0);
 	snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
 				 0x80, present ? 0x80 : 0);
-	snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA, present ? 1 : 3);
+	snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
+				  present ? 1 : 3);
 }
 
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -5777,12 +5773,16 @@
 	idx = ucontrol->value.enumerated.item[0];
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (*cur_val == idx && !codec->in_resume)
+	if (*cur_val == idx)
 		return 0;
 	for (i = 0; i < imux->num_items; i++) {
-		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    v | (imux->items[i].index << 8));
+		unsigned int v = (i == idx) ? 0x00 : 0x80;
+		snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT,
+					 imux->items[i].index,
+					 0x80, v);
+		snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT,
+					 imux->items[i].index,
+					 0x80, v);
 	}
 	*cur_val = idx;
 	return 1;
@@ -6509,8 +6509,8 @@
 				 0x80, bits);
 	snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
 				 0x80, bits);
-	snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
-			    present ? 1 : 3);
+	snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
+				  present ? 1 : 3);
 }
 
 static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -7510,8 +7510,8 @@
 					  0x80, valp[0] ? 0 : 0x80);
 	change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
 					   0x80, valp[1] ? 0 : 0x80);
-	if (change || codec->in_resume)
-		alc262_fujitsu_automute(codec, codec->in_resume);
+	if (change)
+		alc262_fujitsu_automute(codec, 0);
 	return change;
 }
 
@@ -8328,14 +8328,17 @@
 	idx = ucontrol->value.enumerated.item[0];
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (*cur_val == idx && !codec->in_resume)
+	if (*cur_val == idx)
 		return 0;
 	for (i = 0; i < imux->num_items; i++) {
-		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    v | (imux->items[i].index << 8));
-                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
-				    idx );
+		unsigned int v = (i == idx) ? 0x00 : 0x80;
+		snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT,
+					 imux->items[i].index, 0x80, v);
+		snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT,
+					 imux->items[i].index, 0x80, v);
+                snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  idx );
 	}
 	*cur_val = idx;
 	return 1;
@@ -9916,12 +9919,14 @@
 	idx = ucontrol->value.enumerated.item[0];
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (*cur_val == idx && !codec->in_resume)
+	if (*cur_val == idx)
 		return 0;
 	for (i = 0; i < imux->num_items; i++) {
-		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    v | (imux->items[i].index << 8));
+		unsigned int v = (i == idx) ? 0x00 : 0x80;
+		snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT,
+					 imux->items[i].index, 0x80, v);
+		snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT,
+					 imux->items[i].index, 0x80, v);
 	}
 	*cur_val = idx;
 	return 1;
@@ -10847,12 +10852,14 @@
 	idx = ucontrol->value.enumerated.item[0];
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (*cur_val == idx && !codec->in_resume)
+	if (*cur_val == idx)
 		return 0;
 	for (i = 0; i < imux->num_items; i++) {
-		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    v | (imux->items[i].index << 8));
+		unsigned int v = (i == idx) ? 0x00 : 0x80;
+		snd_hda_codec_amp_update(codec, nid, 0, HDA_INPUT,
+					 imux->items[i].index, 0x80, v);
+		snd_hda_codec_amp_update(codec, nid, 1, HDA_INPUT,
+					 imux->items[i].index, 0x80, v);
 	}
 	*cur_val = idx;
 	return 1;
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 9838eac..2a4b960 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -78,6 +78,8 @@
 /* si3054 codec registers (nodes) access macros */
 #define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0))
 #define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val))
+#define SET_REG_CACHE(codec,reg,val) \
+	snd_hda_codec_write_cache(codec,reg,0,SI3054_VERB_WRITE_NODE,val)
 
 
 struct si3054_spec {
@@ -113,9 +115,9 @@
 	u16 reg  = PRIVATE_REG(kcontrol->private_value);
 	u16 mask = PRIVATE_MASK(kcontrol->private_value);
 	if (uvalue->value.integer.value[0])
-		SET_REG(codec, reg, (GET_REG(codec, reg)) | mask);
+		SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) | mask);
 	else
-		SET_REG(codec, reg, (GET_REG(codec, reg)) & ~mask);
+		SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) & ~mask);
 	return 0;
 }
 
@@ -267,10 +269,6 @@
 	.build_pcms = si3054_build_pcms,
 	.init = si3054_init,
 	.free = si3054_free,
-#ifdef CONFIG_PM
-	//.suspend = si3054_suspend,
-	.resume = si3054_init,
-#endif
 };
 
 static int patch_si3054(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 87a36e9..145a5f3 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -874,16 +874,16 @@
 {
 	struct sigmatel_spec *spec = codec->spec;
 	/* Configure GPIOx as output */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
+	snd_hda_codec_write_cache(codec, codec->afg, 0,
+				  AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
 	/* Configure GPIOx as CMOS */
-	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
+	snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000);
 	/* Assert GPIOx */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+	snd_hda_codec_write_cache(codec, codec->afg, 0,
+				  AC_VERB_SET_GPIO_DATA, spec->gpio_data);
 	/* Enable GPIOx */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
+	snd_hda_codec_write_cache(codec, codec->afg, 0,
+				  AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
 }
 
 /*
@@ -1082,7 +1082,8 @@
 static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
 
 {
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_codec_write_cache(codec, nid, 0,
+				  AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
 }
 
 #define stac92xx_io_switch_info		snd_ctl_boolean_mono_info
@@ -1291,8 +1292,8 @@
 		spec->multiout.num_dacs++;
 		if (conn_len > 1) {
 			/* select this DAC in the pin's input mux */
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_CONNECT_SEL, j);
+			snd_hda_codec_write_cache(codec, nid, 0,
+						  AC_VERB_SET_CONNECT_SEL, j);
 
 		}
 	}
@@ -1545,9 +1546,9 @@
 		 * NID lists.  Hopefully this won't get confused.
 		 */
 		for (i = 0; i < spec->num_muxes; i++) {
-			snd_hda_codec_write(codec, spec->mux_nids[i], 0,
-					    AC_VERB_SET_CONNECT_SEL,
-					    imux->items[0].index);
+			snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
+						  AC_VERB_SET_CONNECT_SEL,
+						  imux->items[0].index);
 		}
 	}
 
@@ -1879,7 +1880,7 @@
 	if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
 		pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
 	
-	snd_hda_codec_write(codec, nid, 0,
+	snd_hda_codec_write_cache(codec, nid, 0,
 			AC_VERB_SET_PIN_WIDGET_CONTROL,
 			pin_ctl | flag);
 }
@@ -1889,7 +1890,7 @@
 {
 	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
 			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-	snd_hda_codec_write(codec, nid, 0,
+	snd_hda_codec_write_cache(codec, nid, 0,
 			AC_VERB_SET_PIN_WIDGET_CONTROL,
 			pin_ctl & ~flag);
 }
@@ -1948,21 +1949,10 @@
 #ifdef CONFIG_PM
 static int stac92xx_resume(struct hda_codec *codec)
 {
-	struct sigmatel_spec *spec = codec->spec;
-	int i;
-
 	stac92xx_set_config_regs(codec);
-	if (spec->gpio_mask && spec->gpio_data)
-		stac92xx_enable_gpio_mask(codec);
 	stac92xx_init(codec);
-	snd_hda_resume_ctls(codec, spec->mixer);
-	for (i = 0; i < spec->num_mixers; i++)
-		snd_hda_resume_ctls(codec, spec->mixers[i]);
-	if (spec->multiout.dig_out_nid)
-		snd_hda_resume_spdif_out(codec);
-	if (spec->dig_in_nid)
-		snd_hda_resume_spdif_in(codec);
-
+	snd_hda_codec_resume_amp(codec);
+	snd_hda_codec_resume_cache(codec);
 	return 0;
 }
 #endif
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index ba32d1e..6c734f0 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -543,27 +543,6 @@
  	return 0;
 }
 
-#ifdef CONFIG_PM
-/*
- * resume
- */
-static int via_resume(struct hda_codec *codec)
-{
-	struct via_spec *spec = codec->spec;
-	int i;
-
-	via_init(codec);
-	for (i = 0; i < spec->num_mixers; i++)
-		snd_hda_resume_ctls(codec, spec->mixers[i]);
-	if (spec->multiout.dig_out_nid)
-		snd_hda_resume_spdif_out(codec);
-	if (spec->dig_in_nid)
-		snd_hda_resume_spdif_in(codec);
-
-	return 0;
-}
-#endif
-
 /*
  */
 static struct hda_codec_ops via_patch_ops = {
@@ -571,9 +550,6 @@
 	.build_pcms = via_build_pcms,
 	.init = via_init,
 	.free = via_free,
-#ifdef CONFIG_PM
-	.resume = via_resume,
-#endif
 };
 
 /* fill in the dac_nids table from the parsed pin configuration */