Browse Source

Merge branch 'topic/hda' into for-linus

Takashi Iwai 16 years ago
parent
commit
e7bfbb0215

+ 3 - 0
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -741,6 +741,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     model	- force the model name
     model	- force the model name
     position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
+    		  When the bit 8 (0x100) is set, the lower 8 bits are used
+		  as the "fixed" codec slots; i.e. the driver probes the
+		  slots regardless what hardware reports back
     probe_only	- Only probing and no codec initialization (default=off);
     probe_only	- Only probing and no codec initialization (default=off);
 		  Useful to check the initial codec status for debugging
 		  Useful to check the initial codec status for debugging
     bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples.
     bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples.

+ 18 - 3
Documentation/sound/alsa/HD-Audio-Models.txt

@@ -56,6 +56,7 @@ ALC262
   sony-assamd	Sony ASSAMD
   sony-assamd	Sony ASSAMD
   toshiba-s06	Toshiba S06
   toshiba-s06	Toshiba S06
   toshiba-rx1	Toshiba RX1
   toshiba-rx1	Toshiba RX1
+  tyan		Tyan Thunder n6650W (S2915-E)
   ultra		Samsung Q1 Ultra Vista model
   ultra		Samsung Q1 Ultra Vista model
   lenovo-3000	Lenovo 3000 y410
   lenovo-3000	Lenovo 3000 y410
   nec		NEC Versa S9100
   nec		NEC Versa S9100
@@ -261,6 +262,8 @@ Conexant 5051
 =============
 =============
   laptop	Basic Laptop config (default)
   laptop	Basic Laptop config (default)
   hp		HP Spartan laptop
   hp		HP Spartan laptop
+  hp-dv6736	HP dv6736
+  lenovo-x200	Lenovo X200 laptop
 
 
 STAC9200
 STAC9200
 ========
 ========
@@ -278,6 +281,7 @@ STAC9200
   gateway-m4	Gateway laptops with EAPD control
   gateway-m4	Gateway laptops with EAPD control
   gateway-m4-2	Gateway laptops with EAPD control
   gateway-m4-2	Gateway laptops with EAPD control
   panasonic	Panasonic CF-74
   panasonic	Panasonic CF-74
+  auto		BIOS setup (default)
 
 
 STAC9205/9254
 STAC9205/9254
 =============
 =============
@@ -285,6 +289,8 @@ STAC9205/9254
   dell-m42	Dell (unknown)
   dell-m42	Dell (unknown)
   dell-m43	Dell Precision
   dell-m43	Dell Precision
   dell-m44	Dell Inspiron
   dell-m44	Dell Inspiron
+  eapd		Keep EAPD on (e.g. Gateway T1616)
+  auto		BIOS setup (default)
 
 
 STAC9220/9221
 STAC9220/9221
 =============
 =============
@@ -308,6 +314,7 @@ STAC9220/9221
   dell-d82	Dell (unknown)
   dell-d82	Dell (unknown)
   dell-m81	Dell (unknown)
   dell-m81	Dell (unknown)
   dell-m82	Dell XPS M1210
   dell-m82	Dell XPS M1210
+  auto		BIOS setup (default)
 
 
 STAC9202/9250/9251
 STAC9202/9250/9251
 ==================
 ==================
@@ -319,6 +326,7 @@ STAC9202/9250/9251
   m3		Some Gateway MX series laptops
   m3		Some Gateway MX series laptops
   m5		Some Gateway MX series laptops (MP6954)
   m5		Some Gateway MX series laptops (MP6954)
   m6		Some Gateway NX series laptops
   m6		Some Gateway NX series laptops
+  auto		BIOS setup (default)
 
 
 STAC9227/9228/9229/927x
 STAC9227/9228/9229/927x
 =======================
 =======================
@@ -328,6 +336,7 @@ STAC9227/9228/9229/927x
   5stack	D965 5stack + SPDIF
   5stack	D965 5stack + SPDIF
   dell-3stack	Dell Dimension E520
   dell-3stack	Dell Dimension E520
   dell-bios	Fixes with Dell BIOS setup
   dell-bios	Fixes with Dell BIOS setup
+  auto		BIOS setup (default)
 
 
 STAC92HD71B*
 STAC92HD71B*
 ============
 ============
@@ -335,7 +344,10 @@ STAC92HD71B*
   dell-m4-1	Dell desktops
   dell-m4-1	Dell desktops
   dell-m4-2	Dell desktops
   dell-m4-2	Dell desktops
   dell-m4-3	Dell desktops
   dell-m4-3	Dell desktops
-  hp-m4		HP dv laptops
+  hp-m4		HP mini 1000
+  hp-dv5	HP dv series
+  hp-hdx	HP HDX series
+  auto		BIOS setup (default)
 
 
 STAC92HD73*
 STAC92HD73*
 ===========
 ===========
@@ -345,13 +357,16 @@ STAC92HD73*
   dell-m6-dmic	Dell desktops/laptops with digital mics
   dell-m6-dmic	Dell desktops/laptops with digital mics
   dell-m6	Dell desktops/laptops with both type of mics
   dell-m6	Dell desktops/laptops with both type of mics
   dell-eq	Dell desktops/laptops
   dell-eq	Dell desktops/laptops
+  auto		BIOS setup (default)
 
 
 STAC92HD83*
 STAC92HD83*
 ===========
 ===========
   ref		Reference board
   ref		Reference board
   mic-ref	Reference board with power managment for ports
   mic-ref	Reference board with power managment for ports
+  dell-s14	Dell laptop
+  auto		BIOS setup (default)
 
 
 STAC9872
 STAC9872
 ========
 ========
-  vaio		Setup for VAIO FE550G/SZ110
-  vaio-ar Setup for VAIO AR
+  vaio		VAIO laptop without SPDIF
+  auto		BIOS setup (default)

+ 44 - 3
Documentation/sound/alsa/HD-Audio.txt

@@ -109,6 +109,13 @@ slot, pass `probe_mask=1`.  For the first and the third slots, pass
 Since 2.6.29 kernel, the driver has a more robust probing method, so
 Since 2.6.29 kernel, the driver has a more robust probing method, so
 this error might happen rarely, though.
 this error might happen rarely, though.
 
 
+On a machine with a broken BIOS, sometimes you need to force the
+driver to probe the codec slots the hardware doesn't report for use.
+In such a case, turn the bit 8 (0x100) of `probe_mask` option on.
+Then the rest 8 bits are passed as the codec slots to probe
+unconditionally.  For example, `probe_mask=0x103` will force to probe
+the codec slots 0 and 1 no matter what the hardware reports.
+
 
 
 Interrupt Handling
 Interrupt Handling
 ~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~
@@ -358,10 +365,26 @@ modelname::
   to this file.
   to this file.
 init_verbs::
 init_verbs::
   The extra verbs to execute at initialization.  You can add a verb by
   The extra verbs to execute at initialization.  You can add a verb by
-  writing to this file.  Pass tree numbers, nid, verb and parameter.
+  writing to this file.  Pass three numbers: nid, verb and parameter
+  (separated with a space).
 hints::
 hints::
-  Shows hint strings for codec parsers for any use.  Right now it's
-  not used.
+  Shows / stores hint strings for codec parsers for any use.
+  Its format is `key = value`.  For example, passing `hp_detect = yes`
+  to IDT/STAC codec parser will result in the disablement of the
+  headphone detection.
+init_pin_configs::
+  Shows the initial pin default config values set by BIOS.
+driver_pin_configs::
+  Shows the pin default values set by the codec parser explicitly.
+  This doesn't show all pin values but only the changed values by
+  the parser.  That is, if the parser doesn't change the pin default
+  config values by itself, this will contain nothing.
+user_pin_configs::
+  Shows the pin default config values to override the BIOS setup.
+  Writing this (with two numbers, NID and value) appends the new
+  value.  The given will be used instead of the initial BIOS value at
+  the next reconfiguration time.  Note that this config will override
+  even the driver pin configs, too.
 reconfig::
 reconfig::
   Triggers the codec re-configuration.  When any value is written to
   Triggers the codec re-configuration.  When any value is written to
   this file, the driver re-initialize and parses the codec tree
   this file, the driver re-initialize and parses the codec tree
@@ -371,6 +394,14 @@ clear::
   Resets the codec, removes the mixer elements and PCM stuff of the
   Resets the codec, removes the mixer elements and PCM stuff of the
   specified codec, and clear all init verbs and hints.
   specified codec, and clear all init verbs and hints.
 
 
+For example, when you want to change the pin default configuration
+value of the pin widget 0x14 to 0x9993013f, and let the driver
+re-configure based on that state, run like below:
+------------------------------------------------------------------------
+  # echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs
+  # echo 1 > /sys/class/sound/hwC0D0/reconfig  
+------------------------------------------------------------------------
+
 
 
 Power-Saving
 Power-Saving
 ~~~~~~~~~~~~
 ~~~~~~~~~~~~
@@ -461,6 +492,16 @@ run with `--no-upload` option, and attach the generated file.
 There are some other useful options.  See `--help` option output for
 There are some other useful options.  See `--help` option output for
 details.
 details.
 
 
+When a probe error occurs or when the driver obviously assigns a
+mismatched model, it'd be helpful to load the driver with
+`probe_only=1` option (at best after the cold reboot) and run
+alsa-info at this state.  With this option, the driver won't configure
+the mixer and PCM but just tries to probe the codec slot.  After
+probing, the proc file is available, so you can get the raw codec
+information before modified by the driver.  Of course, the driver
+isn't usable with `probe_only=1`.  But you can continue the
+configuration via hwdep sysfs file if hda-reconfig option is enabled.
+
 
 
 hda-verb
 hda-verb
 ~~~~~~~~
 ~~~~~~~~

+ 2 - 0
include/linux/pci_ids.h

@@ -2112,6 +2112,8 @@
 #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
 #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
 #define PCI_DEVICE_ID_MELLANOX_SINAI	0x6274
 #define PCI_DEVICE_ID_MELLANOX_SINAI	0x6274
 
 
+#define PCI_VENDOR_ID_DFI		0x15bd
+
 #define PCI_VENDOR_ID_QUICKNET		0x15e2
 #define PCI_VENDOR_ID_QUICKNET		0x15e2
 #define PCI_DEVICE_ID_QUICKNET_XJ	0x0500
 #define PCI_DEVICE_ID_QUICKNET_XJ	0x0500
 
 

+ 1 - 0
sound/pci/hda/hda_beep.c

@@ -138,6 +138,7 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
 
 
 		input_unregister_device(beep->dev);
 		input_unregister_device(beep->dev);
 		kfree(beep);
 		kfree(beep);
+		codec->beep = NULL;
 	}
 	}
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
 EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);

+ 1 - 1
sound/pci/hda/hda_beep.h

@@ -39,7 +39,7 @@ struct hda_beep {
 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
 void snd_hda_detach_beep_device(struct hda_codec *codec);
 void snd_hda_detach_beep_device(struct hda_codec *codec);
 #else
 #else
-#define snd_hda_attach_beep_device(...)
+#define snd_hda_attach_beep_device(...)		0
 #define snd_hda_detach_beep_device(...)
 #define snd_hda_detach_beep_device(...)
 #endif
 #endif
 #endif
 #endif

+ 359 - 68
sound/pci/hda/hda_codec.c

@@ -647,9 +647,9 @@ static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
 
 
 	total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
 	total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
 	for (i = 0; i < total_nodes; i++, nid++) {
 	for (i = 0; i < total_nodes; i++, nid++) {
-		unsigned int func;
-		func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE);
-		switch (func & 0xff) {
+		codec->function_id = snd_hda_param_read(codec, nid,
+						AC_PAR_FUNCTION_TYPE) & 0xff;
+		switch (codec->function_id) {
 		case AC_GRP_AUDIO_FUNCTION:
 		case AC_GRP_AUDIO_FUNCTION:
 			codec->afg = nid;
 			codec->afg = nid;
 			break;
 			break;
@@ -682,11 +682,140 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
 	return 0;
 	return 0;
 }
 }
 
 
+/* read all pin default configurations and save codec->init_pins */
+static int read_pin_defaults(struct hda_codec *codec)
+{
+	int i;
+	hda_nid_t nid = codec->start_nid;
+
+	for (i = 0; i < codec->num_nodes; i++, nid++) {
+		struct hda_pincfg *pin;
+		unsigned int wcaps = get_wcaps(codec, nid);
+		unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
+				AC_WCAP_TYPE_SHIFT;
+		if (wid_type != AC_WID_PIN)
+			continue;
+		pin = snd_array_new(&codec->init_pins);
+		if (!pin)
+			return -ENOMEM;
+		pin->nid = nid;
+		pin->cfg = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_CONFIG_DEFAULT, 0);
+	}
+	return 0;
+}
+
+/* look up the given pin config list and return the item matching with NID */
+static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
+					 struct snd_array *array,
+					 hda_nid_t nid)
+{
+	int i;
+	for (i = 0; i < array->used; i++) {
+		struct hda_pincfg *pin = snd_array_elem(array, i);
+		if (pin->nid == nid)
+			return pin;
+	}
+	return NULL;
+}
+
+/* write a config value for the given NID */
+static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
+		       unsigned int cfg)
+{
+	int i;
+	for (i = 0; i < 4; i++) {
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
+				    cfg & 0xff);
+		cfg >>= 8;
+	}
+}
+
+/* set the current pin config value for the given NID.
+ * the value is cached, and read via snd_hda_codec_get_pincfg()
+ */
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+		       hda_nid_t nid, unsigned int cfg)
+{
+	struct hda_pincfg *pin;
+	unsigned int oldcfg;
+
+	oldcfg = snd_hda_codec_get_pincfg(codec, nid);
+	pin = look_up_pincfg(codec, list, nid);
+	if (!pin) {
+		pin = snd_array_new(list);
+		if (!pin)
+			return -ENOMEM;
+		pin->nid = nid;
+	}
+	pin->cfg = cfg;
+
+	/* change only when needed; e.g. if the pincfg is already present
+	 * in user_pins[], don't write it
+	 */
+	cfg = snd_hda_codec_get_pincfg(codec, nid);
+	if (oldcfg != cfg)
+		set_pincfg(codec, nid, cfg);
+	return 0;
+}
+
+int snd_hda_codec_set_pincfg(struct hda_codec *codec,
+			     hda_nid_t nid, unsigned int cfg)
+{
+	return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
+
+/* get the current pin config value of the given pin NID */
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct hda_pincfg *pin;
+
+#ifdef CONFIG_SND_HDA_HWDEP
+	pin = look_up_pincfg(codec, &codec->user_pins, nid);
+	if (pin)
+		return pin->cfg;
+#endif
+	pin = look_up_pincfg(codec, &codec->driver_pins, nid);
+	if (pin)
+		return pin->cfg;
+	pin = look_up_pincfg(codec, &codec->init_pins, nid);
+	if (pin)
+		return pin->cfg;
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
+
+/* restore all current pin configs */
+static void restore_pincfgs(struct hda_codec *codec)
+{
+	int i;
+	for (i = 0; i < codec->init_pins.used; i++) {
+		struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+		set_pincfg(codec, pin->nid,
+			   snd_hda_codec_get_pincfg(codec, pin->nid));
+	}
+}
 
 
 static void init_hda_cache(struct hda_cache_rec *cache,
 static void init_hda_cache(struct hda_cache_rec *cache,
 			   unsigned int record_size);
 			   unsigned int record_size);
 static void free_hda_cache(struct hda_cache_rec *cache);
 static void free_hda_cache(struct hda_cache_rec *cache);
 
 
+/* restore the initial pin cfgs and release all pincfg lists */
+static void restore_init_pincfgs(struct hda_codec *codec)
+{
+	/* first free driver_pins and user_pins, then call restore_pincfg
+	 * so that only the values in init_pins are restored
+	 */
+	snd_array_free(&codec->driver_pins);
+#ifdef CONFIG_SND_HDA_HWDEP
+	snd_array_free(&codec->user_pins);
+#endif
+	restore_pincfgs(codec);
+	snd_array_free(&codec->init_pins);
+}
+
 /*
 /*
  * codec destructor
  * codec destructor
  */
  */
@@ -694,6 +823,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 {
 {
 	if (!codec)
 	if (!codec)
 		return;
 		return;
+	restore_init_pincfgs(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	cancel_delayed_work(&codec->power_work);
 	cancel_delayed_work(&codec->power_work);
 	flush_workqueue(codec->bus->workq);
 	flush_workqueue(codec->bus->workq);
@@ -712,6 +842,9 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 	kfree(codec);
 	kfree(codec);
 }
 }
 
 
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+				unsigned int power_state);
+
 /**
 /**
  * snd_hda_codec_new - create a HDA codec
  * snd_hda_codec_new - create a HDA codec
  * @bus: the bus to assign
  * @bus: the bus to assign
@@ -751,6 +884,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 	snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
 	snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+	snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
+	snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
 	if (codec->bus->modelname) {
 	if (codec->bus->modelname) {
 		codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
 		codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
 		if (!codec->modelname) {
 		if (!codec->modelname) {
@@ -787,15 +922,18 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 	setup_fg_nodes(codec);
 	setup_fg_nodes(codec);
 	if (!codec->afg && !codec->mfg) {
 	if (!codec->afg && !codec->mfg) {
 		snd_printdd("hda_codec: no AFG or MFG node found\n");
 		snd_printdd("hda_codec: no AFG or MFG node found\n");
-		snd_hda_codec_free(codec);
-		return -ENODEV;
+		err = -ENODEV;
+		goto error;
 	}
 	}
 
 
-	if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {
+	err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
+	if (err < 0) {
 		snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
 		snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
-		snd_hda_codec_free(codec);
-		return -ENOMEM;
+		goto error;
 	}
 	}
+	err = read_pin_defaults(codec);
+	if (err < 0)
+		goto error;
 
 
 	if (!codec->subsystem_id) {
 	if (!codec->subsystem_id) {
 		hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
 		hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
@@ -806,12 +944,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 	if (bus->modelname)
 	if (bus->modelname)
 		codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
 		codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
 
 
+	/* power-up all before initialization */
+	hda_set_power_state(codec,
+			    codec->afg ? codec->afg : codec->mfg,
+			    AC_PWRST_D0);
+
 	if (do_init) {
 	if (do_init) {
 		err = snd_hda_codec_configure(codec);
 		err = snd_hda_codec_configure(codec);
-		if (err < 0) {
-			snd_hda_codec_free(codec);
-			return err;
-		}
+		if (err < 0)
+			goto error;
 	}
 	}
 	snd_hda_codec_proc_new(codec);
 	snd_hda_codec_proc_new(codec);
 
 
@@ -824,6 +965,10 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 	if (codecp)
 	if (codecp)
 		*codecp = codec;
 		*codecp = codec;
 	return 0;
 	return 0;
+
+ error:
+	snd_hda_codec_free(codec);
+	return err;
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 
 
@@ -907,6 +1052,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 
 
 /* FIXME: more better hash key? */
 /* FIXME: more better hash key? */
 #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
 #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
+#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
 #define INFO_AMP_CAPS	(1<<0)
 #define INFO_AMP_CAPS	(1<<0)
 #define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))
 #define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))
 
 
@@ -997,6 +1143,21 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
 
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct hda_amp_info *info;
+
+	info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
+	if (!info)
+		return 0;
+	if (!info->head.val) {
+		info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+		info->head.val |= INFO_AMP_CAPS;
+	}
+	return info->amp_caps;
+}
+EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+
 /*
 /*
  * read the current volume to info
  * read the current volume to info
  * if the cache exists, read the cache value.
  * if the cache exists, read the cache value.
@@ -1120,6 +1281,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 	u16 nid = get_amp_nid(kcontrol);
 	u16 nid = get_amp_nid(kcontrol);
 	u8 chs = get_amp_channels(kcontrol);
 	u8 chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int dir = get_amp_direction(kcontrol);
+	unsigned int ofs = get_amp_offset(kcontrol);
 	u32 caps;
 	u32 caps;
 
 
 	caps = query_amp_caps(codec, nid, dir);
 	caps = query_amp_caps(codec, nid, dir);
@@ -1131,6 +1293,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 		       kcontrol->id.name);
 		       kcontrol->id.name);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
+	if (ofs < caps)
+		caps -= ofs;
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = chs == 3 ? 2 : 1;
 	uinfo->count = chs == 3 ? 2 : 1;
 	uinfo->value.integer.min = 0;
 	uinfo->value.integer.min = 0;
@@ -1139,6 +1303,32 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
 
 
+
+static inline unsigned int
+read_amp_value(struct hda_codec *codec, hda_nid_t nid,
+	       int ch, int dir, int idx, unsigned int ofs)
+{
+	unsigned int val;
+	val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
+	val &= HDA_AMP_VOLMASK;
+	if (val >= ofs)
+		val -= ofs;
+	else
+		val = 0;
+	return val;
+}
+
+static inline int
+update_amp_value(struct hda_codec *codec, hda_nid_t nid,
+		 int ch, int dir, int idx, unsigned int ofs,
+		 unsigned int val)
+{
+	if (val > 0)
+		val += ofs;
+	return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
+					HDA_AMP_VOLMASK, val);
+}
+
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
 {
 {
@@ -1147,14 +1337,13 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 	int chs = get_amp_channels(kcontrol);
 	int chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int idx = get_amp_index(kcontrol);
 	int idx = get_amp_index(kcontrol);
+	unsigned int ofs = get_amp_offset(kcontrol);
 	long *valp = ucontrol->value.integer.value;
 	long *valp = ucontrol->value.integer.value;
 
 
 	if (chs & 1)
 	if (chs & 1)
-		*valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx)
-			& HDA_AMP_VOLMASK;
+		*valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
 	if (chs & 2)
 	if (chs & 2)
-		*valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx)
-			& HDA_AMP_VOLMASK;
+		*valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
@@ -1167,18 +1356,17 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 	int chs = get_amp_channels(kcontrol);
 	int chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int idx = get_amp_index(kcontrol);
 	int idx = get_amp_index(kcontrol);
+	unsigned int ofs = get_amp_offset(kcontrol);
 	long *valp = ucontrol->value.integer.value;
 	long *valp = ucontrol->value.integer.value;
 	int change = 0;
 	int change = 0;
 
 
 	snd_hda_power_up(codec);
 	snd_hda_power_up(codec);
 	if (chs & 1) {
 	if (chs & 1) {
-		change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
-						  0x7f, *valp);
+		change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
 		valp++;
 		valp++;
 	}
 	}
 	if (chs & 2)
 	if (chs & 2)
-		change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
-						   0x7f, *valp);
+		change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 	return change;
 	return change;
 }
 }
@@ -1190,6 +1378,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	hda_nid_t nid = get_amp_nid(kcontrol);
 	hda_nid_t nid = get_amp_nid(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	int dir = get_amp_direction(kcontrol);
+	unsigned int ofs = get_amp_offset(kcontrol);
 	u32 caps, val1, val2;
 	u32 caps, val1, val2;
 
 
 	if (size < 4 * sizeof(unsigned int))
 	if (size < 4 * sizeof(unsigned int))
@@ -1198,6 +1387,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 	val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
 	val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
 	val2 = (val2 + 1) * 25;
 	val2 = (val2 + 1) * 25;
 	val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
 	val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+	val1 += ofs;
 	val1 = ((int)val1) * ((int)val2);
 	val1 = ((int)val1) * ((int)val2);
 	if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
 	if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
 		return -EFAULT;
 		return -EFAULT;
@@ -1268,7 +1458,6 @@ int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
 }
 }
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
 
-#ifdef CONFIG_SND_HDA_RECONFIG
 /* Clear all controls assigned to the given codec */
 /* Clear all controls assigned to the given codec */
 void snd_hda_ctls_clear(struct hda_codec *codec)
 void snd_hda_ctls_clear(struct hda_codec *codec)
 {
 {
@@ -1279,9 +1468,52 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
 	snd_array_free(&codec->mixers);
 	snd_array_free(&codec->mixers);
 }
 }
 
 
-void snd_hda_codec_reset(struct hda_codec *codec)
+/* pseudo device locking
+ * toggle card->shutdown to allow/disallow the device access (as a hack)
+ */
+static int hda_lock_devices(struct snd_card *card)
 {
 {
-	int i;
+	spin_lock(&card->files_lock);
+	if (card->shutdown) {
+		spin_unlock(&card->files_lock);
+		return -EINVAL;
+	}
+	card->shutdown = 1;
+	spin_unlock(&card->files_lock);
+	return 0;
+}
+
+static void hda_unlock_devices(struct snd_card *card)
+{
+	spin_lock(&card->files_lock);
+	card->shutdown = 0;
+	spin_unlock(&card->files_lock);
+}
+
+int snd_hda_codec_reset(struct hda_codec *codec)
+{
+	struct snd_card *card = codec->bus->card;
+	int i, pcm;
+
+	if (hda_lock_devices(card) < 0)
+		return -EBUSY;
+	/* check whether the codec isn't used by any mixer or PCM streams */
+	if (!list_empty(&card->ctl_files)) {
+		hda_unlock_devices(card);
+		return -EBUSY;
+	}
+	for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+		struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+		if (!cpcm->pcm)
+			continue;
+		if (cpcm->pcm->streams[0].substream_opened ||
+		    cpcm->pcm->streams[1].substream_opened) {
+			hda_unlock_devices(card);
+			return -EBUSY;
+		}
+	}
+
+	/* OK, let it free */
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	cancel_delayed_work(&codec->power_work);
 	cancel_delayed_work(&codec->power_work);
@@ -1291,8 +1523,7 @@ void snd_hda_codec_reset(struct hda_codec *codec)
 	/* relase PCMs */
 	/* relase PCMs */
 	for (i = 0; i < codec->num_pcms; i++) {
 	for (i = 0; i < codec->num_pcms; i++) {
 		if (codec->pcm_info[i].pcm) {
 		if (codec->pcm_info[i].pcm) {
-			snd_device_free(codec->bus->card,
-					codec->pcm_info[i].pcm);
+			snd_device_free(card, codec->pcm_info[i].pcm);
 			clear_bit(codec->pcm_info[i].device,
 			clear_bit(codec->pcm_info[i].device,
 				  codec->bus->pcm_dev_bits);
 				  codec->bus->pcm_dev_bits);
 		}
 		}
@@ -1305,13 +1536,22 @@ void snd_hda_codec_reset(struct hda_codec *codec)
 	free_hda_cache(&codec->cmd_cache);
 	free_hda_cache(&codec->cmd_cache);
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+	/* free only driver_pins so that init_pins + user_pins are restored */
+	snd_array_free(&codec->driver_pins);
+	restore_pincfgs(codec);
 	codec->num_pcms = 0;
 	codec->num_pcms = 0;
 	codec->pcm_info = NULL;
 	codec->pcm_info = NULL;
 	codec->preset = NULL;
 	codec->preset = NULL;
+	memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+	codec->slave_dig_outs = NULL;
+	codec->spdif_status_reset = 0;
 	module_put(codec->owner);
 	module_put(codec->owner);
 	codec->owner = NULL;
 	codec->owner = NULL;
+
+	/* allow device access again */
+	hda_unlock_devices(card);
+	return 0;
 }
 }
-#endif /* CONFIG_SND_HDA_RECONFIG */
 
 
 /* create a virtual master control and add slaves */
 /* create a virtual master control and add slaves */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@@ -1336,15 +1576,20 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 	
 	
 	for (s = slaves; *s; s++) {
 	for (s = slaves; *s; s++) {
 		struct snd_kcontrol *sctl;
 		struct snd_kcontrol *sctl;
-
-		sctl = snd_hda_find_mixer_ctl(codec, *s);
-		if (!sctl) {
-			snd_printdd("Cannot find slave %s, skipped\n", *s);
-			continue;
+		int i = 0;
+		for (;;) {
+			sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
+			if (!sctl) {
+				if (!i)
+					snd_printdd("Cannot find slave %s, "
+						    "skipped\n", *s);
+				break;
+			}
+			err = snd_ctl_add_slave(kctl, sctl);
+			if (err < 0)
+				return err;
+			i++;
 		}
 		}
-		err = snd_ctl_add_slave(kctl, sctl);
-		if (err < 0)
-			return err;
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -1955,6 +2200,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 	}
 	}
 	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
 	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
 		kctl = snd_ctl_new1(dig_mix, codec);
 		kctl = snd_ctl_new1(dig_mix, codec);
+		if (!kctl)
+			return -ENOMEM;
 		kctl->private_value = nid;
 		kctl->private_value = nid;
 		err = snd_hda_ctl_add(codec, kctl);
 		err = snd_hda_ctl_add(codec, kctl);
 		if (err < 0)
 		if (err < 0)
@@ -2074,8 +2321,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 				 * don't power down the widget if it controls
 				 * don't power down the widget if it controls
 				 * eapd and EAPD_BTLENABLE is set.
 				 * eapd and EAPD_BTLENABLE is set.
 				 */
 				 */
-				pincap = snd_hda_param_read(codec, nid,
-							    AC_PAR_PIN_CAP);
+				pincap = snd_hda_query_pin_caps(codec, nid);
 				if (pincap & AC_PINCAP_EAPD) {
 				if (pincap & AC_PINCAP_EAPD) {
 					int eapd = snd_hda_codec_read(codec,
 					int eapd = snd_hda_codec_read(codec,
 						nid, 0,
 						nid, 0,
@@ -2144,6 +2390,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
 	hda_set_power_state(codec,
 	hda_set_power_state(codec,
 			    codec->afg ? codec->afg : codec->mfg,
 			    codec->afg ? codec->afg : codec->mfg,
 			    AC_PWRST_D0);
 			    AC_PWRST_D0);
+	restore_pincfgs(codec); /* restore all current pin configs */
 	hda_exec_init_verbs(codec);
 	hda_exec_init_verbs(codec);
 	if (codec->patch_ops.resume)
 	if (codec->patch_ops.resume)
 		codec->patch_ops.resume(codec);
 		codec->patch_ops.resume(codec);
@@ -2171,8 +2418,16 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
 
 
 	list_for_each_entry(codec, &bus->codec_list, list) {
 	list_for_each_entry(codec, &bus->codec_list, list) {
 		int err = snd_hda_codec_build_controls(codec);
 		int err = snd_hda_codec_build_controls(codec);
-		if (err < 0)
-			return err;
+		if (err < 0) {
+			printk(KERN_ERR "hda_codec: cannot build controls"
+			       "for #%d (error %d)\n", codec->addr, err); 
+			err = snd_hda_codec_reset(codec);
+			if (err < 0) {
+				printk(KERN_ERR
+				       "hda_codec: cannot revert codec\n");
+				return err;
+			}
+		}
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -2181,19 +2436,12 @@ EXPORT_SYMBOL_HDA(snd_hda_build_controls);
 int snd_hda_codec_build_controls(struct hda_codec *codec)
 int snd_hda_codec_build_controls(struct hda_codec *codec)
 {
 {
 	int err = 0;
 	int err = 0;
-	/* fake as if already powered-on */
-	hda_keep_power_on(codec);
-	/* then fire up */
-	hda_set_power_state(codec,
-			    codec->afg ? codec->afg : codec->mfg,
-			    AC_PWRST_D0);
 	hda_exec_init_verbs(codec);
 	hda_exec_init_verbs(codec);
 	/* continue to initialize... */
 	/* continue to initialize... */
 	if (codec->patch_ops.init)
 	if (codec->patch_ops.init)
 		err = codec->patch_ops.init(codec);
 		err = codec->patch_ops.init(codec);
 	if (!err && codec->patch_ops.build_controls)
 	if (!err && codec->patch_ops.build_controls)
 		err = codec->patch_ops.build_controls(codec);
 		err = codec->patch_ops.build_controls(codec);
-	snd_hda_power_down(codec);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	return 0;
 	return 0;
@@ -2306,12 +2554,11 @@ EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 				u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 				u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 {
 {
-	int i;
-	unsigned int val, streams;
+	unsigned int i, val, wcaps;
 
 
 	val = 0;
 	val = 0;
-	if (nid != codec->afg &&
-	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
+	wcaps = get_wcaps(codec, nid);
+	if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) {
 		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
 		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
 		if (val == -1)
 		if (val == -1)
 			return -EIO;
 			return -EIO;
@@ -2325,15 +2572,20 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 			if (val & (1 << i))
 			if (val & (1 << i))
 				rates |= rate_bits[i].alsa_bits;
 				rates |= rate_bits[i].alsa_bits;
 		}
 		}
+		if (rates == 0) {
+			snd_printk(KERN_ERR "hda_codec: rates == 0 "
+				   "(nid=0x%x, val=0x%x, ovrd=%i)\n",
+					nid, val,
+					(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+			return -EIO;
+		}
 		*ratesp = rates;
 		*ratesp = rates;
 	}
 	}
 
 
 	if (formatsp || bpsp) {
 	if (formatsp || bpsp) {
 		u64 formats = 0;
 		u64 formats = 0;
-		unsigned int bps;
-		unsigned int wcaps;
+		unsigned int streams, bps;
 
 
-		wcaps = get_wcaps(codec, nid);
 		streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
 		streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
 		if (streams == -1)
 		if (streams == -1)
 			return -EIO;
 			return -EIO;
@@ -2386,6 +2638,15 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 			formats |= SNDRV_PCM_FMTBIT_U8;
 			formats |= SNDRV_PCM_FMTBIT_U8;
 			bps = 8;
 			bps = 8;
 		}
 		}
+		if (formats == 0) {
+			snd_printk(KERN_ERR "hda_codec: formats == 0 "
+				   "(nid=0x%x, val=0x%x, ovrd=%i, "
+				   "streams=0x%x)\n",
+					nid, val,
+					(wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+					streams);
+			return -EIO;
+		}
 		if (formatsp)
 		if (formatsp)
 			*formatsp = formats;
 			*formatsp = formats;
 		if (bpsp)
 		if (bpsp)
@@ -2501,12 +2762,16 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
 static int set_pcm_default_values(struct hda_codec *codec,
 static int set_pcm_default_values(struct hda_codec *codec,
 				  struct hda_pcm_stream *info)
 				  struct hda_pcm_stream *info)
 {
 {
+	int err;
+
 	/* query support PCM information from the given NID */
 	/* query support PCM information from the given NID */
 	if (info->nid && (!info->rates || !info->formats)) {
 	if (info->nid && (!info->rates || !info->formats)) {
-		snd_hda_query_supported_pcm(codec, info->nid,
+		err = snd_hda_query_supported_pcm(codec, info->nid,
 				info->rates ? NULL : &info->rates,
 				info->rates ? NULL : &info->rates,
 				info->formats ? NULL : &info->formats,
 				info->formats ? NULL : &info->formats,
 				info->maxbps ? NULL : &info->maxbps);
 				info->maxbps ? NULL : &info->maxbps);
+		if (err < 0)
+			return err;
 	}
 	}
 	if (info->ops.open == NULL)
 	if (info->ops.open == NULL)
 		info->ops.open = hda_pcm_default_open_close;
 		info->ops.open = hda_pcm_default_open_close;
@@ -2549,13 +2814,10 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
 		for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
 		for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
 			dev = audio_idx[i];
 			dev = audio_idx[i];
 			if (!test_bit(dev, bus->pcm_dev_bits))
 			if (!test_bit(dev, bus->pcm_dev_bits))
-				break;
-		}
-		if (i >= ARRAY_SIZE(audio_idx)) {
-			snd_printk(KERN_WARNING "Too many audio devices\n");
-			return -EAGAIN;
+				goto ok;
 		}
 		}
-		break;
+		snd_printk(KERN_WARNING "Too many audio devices\n");
+		return -EAGAIN;
 	case HDA_PCM_TYPE_SPDIF:
 	case HDA_PCM_TYPE_SPDIF:
 	case HDA_PCM_TYPE_HDMI:
 	case HDA_PCM_TYPE_HDMI:
 	case HDA_PCM_TYPE_MODEM:
 	case HDA_PCM_TYPE_MODEM:
@@ -2570,6 +2832,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
 		snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
 		snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
+ ok:
 	set_bit(dev, bus->pcm_dev_bits);
 	set_bit(dev, bus->pcm_dev_bits);
 	return dev;
 	return dev;
 }
 }
@@ -2606,24 +2869,36 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
 		if (!codec->patch_ops.build_pcms)
 		if (!codec->patch_ops.build_pcms)
 			return 0;
 			return 0;
 		err = codec->patch_ops.build_pcms(codec);
 		err = codec->patch_ops.build_pcms(codec);
-		if (err < 0)
-			return err;
+		if (err < 0) {
+			printk(KERN_ERR "hda_codec: cannot build PCMs"
+			       "for #%d (error %d)\n", codec->addr, err); 
+			err = snd_hda_codec_reset(codec);
+			if (err < 0) {
+				printk(KERN_ERR
+				       "hda_codec: cannot revert codec\n");
+				return err;
+			}
+		}
 	}
 	}
 	for (pcm = 0; pcm < codec->num_pcms; pcm++) {
 	for (pcm = 0; pcm < codec->num_pcms; pcm++) {
 		struct hda_pcm *cpcm = &codec->pcm_info[pcm];
 		struct hda_pcm *cpcm = &codec->pcm_info[pcm];
 		int dev;
 		int dev;
 
 
 		if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
 		if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
-			return 0; /* no substreams assigned */
+			continue; /* no substreams assigned */
 
 
 		if (!cpcm->pcm) {
 		if (!cpcm->pcm) {
 			dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
 			dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
 			if (dev < 0)
 			if (dev < 0)
-				return 0;
+				continue; /* no fatal error */
 			cpcm->device = dev;
 			cpcm->device = dev;
 			err = snd_hda_attach_pcm(codec, cpcm);
 			err = snd_hda_attach_pcm(codec, cpcm);
-			if (err < 0)
-				return err;
+			if (err < 0) {
+				printk(KERN_ERR "hda_codec: cannot attach "
+				       "PCM stream %d for codec #%d\n",
+				       dev, codec->addr);
+				continue; /* no fatal error */
+			}
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
@@ -3324,8 +3599,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 		if (ignore_nids && is_in_nid_list(nid, ignore_nids))
 		if (ignore_nids && is_in_nid_list(nid, ignore_nids))
 			continue;
 			continue;
 
 
-		def_conf = snd_hda_codec_read(codec, nid, 0,
-					      AC_VERB_GET_CONFIG_DEFAULT, 0);
+		def_conf = snd_hda_codec_get_pincfg(codec, nid);
 		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
 		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
 			continue;
 			continue;
 		loc = get_defcfg_location(def_conf);
 		loc = get_defcfg_location(def_conf);
@@ -3401,10 +3675,22 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 			cfg->input_pins[AUTO_PIN_AUX] = nid;
 			cfg->input_pins[AUTO_PIN_AUX] = nid;
 			break;
 			break;
 		case AC_JACK_SPDIF_OUT:
 		case AC_JACK_SPDIF_OUT:
-			cfg->dig_out_pin = nid;
+		case AC_JACK_DIG_OTHER_OUT:
+			if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+				continue;
+			cfg->dig_out_pins[cfg->dig_outs] = nid;
+			cfg->dig_out_type[cfg->dig_outs] =
+				(loc == AC_JACK_LOC_HDMI) ?
+				HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+			cfg->dig_outs++;
 			break;
 			break;
 		case AC_JACK_SPDIF_IN:
 		case AC_JACK_SPDIF_IN:
+		case AC_JACK_DIG_OTHER_IN:
 			cfg->dig_in_pin = nid;
 			cfg->dig_in_pin = nid;
+			if (loc == AC_JACK_LOC_HDMI)
+				cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+			else
+				cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -3510,6 +3796,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 		   cfg->hp_pins[1], cfg->hp_pins[2],
 		   cfg->hp_pins[1], cfg->hp_pins[2],
 		   cfg->hp_pins[3], cfg->hp_pins[4]);
 		   cfg->hp_pins[3], cfg->hp_pins[4]);
 	snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
 	snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+	if (cfg->dig_outs)
+		snd_printd("   dig-out=0x%x/0x%x\n",
+			   cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
 	snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
 	snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
 		   " cd=0x%x, aux=0x%x\n",
 		   " cd=0x%x, aux=0x%x\n",
 		   cfg->input_pins[AUTO_PIN_MIC],
 		   cfg->input_pins[AUTO_PIN_MIC],
@@ -3518,6 +3807,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 		   cfg->input_pins[AUTO_PIN_FRONT_LINE],
 		   cfg->input_pins[AUTO_PIN_FRONT_LINE],
 		   cfg->input_pins[AUTO_PIN_CD],
 		   cfg->input_pins[AUTO_PIN_CD],
 		   cfg->input_pins[AUTO_PIN_AUX]);
 		   cfg->input_pins[AUTO_PIN_AUX]);
+	if (cfg->dig_in_pin)
+		snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
 
 
 	return 0;
 	return 0;
 }
 }

+ 19 - 0
sound/pci/hda/hda_codec.h

@@ -739,6 +739,7 @@ struct hda_codec {
 	hda_nid_t mfg;	/* MFG node id */
 	hda_nid_t mfg;	/* MFG node id */
 
 
 	/* ids */
 	/* ids */
+	u32 function_id;
 	u32 vendor_id;
 	u32 vendor_id;
 	u32 subsystem_id;
 	u32 subsystem_id;
 	u32 revision_id;
 	u32 revision_id;
@@ -778,11 +779,14 @@ struct hda_codec {
 	unsigned short spdif_ctls;	/* SPDIF control bits */
 	unsigned short spdif_ctls;	/* SPDIF control bits */
 	unsigned int spdif_in_enable;	/* SPDIF input enable? */
 	unsigned int spdif_in_enable;	/* SPDIF input enable? */
 	hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
 	hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
+	struct snd_array init_pins;	/* initial (BIOS) pin configurations */
+	struct snd_array driver_pins;	/* pin configs set by codec parser */
 
 
 #ifdef CONFIG_SND_HDA_HWDEP
 #ifdef CONFIG_SND_HDA_HWDEP
 	struct snd_hwdep *hwdep;	/* assigned hwdep device */
 	struct snd_hwdep *hwdep;	/* assigned hwdep device */
 	struct snd_array init_verbs;	/* additional init verbs */
 	struct snd_array init_verbs;	/* additional init verbs */
 	struct snd_array hints;		/* additional hints */
 	struct snd_array hints;		/* additional hints */
+	struct snd_array user_pins;	/* default pin configs to override */
 #endif
 #endif
 
 
 	/* misc flags */
 	/* misc flags */
@@ -790,6 +794,9 @@ struct hda_codec {
 					     * status change
 					     * status change
 					     * (e.g. Realtek codecs)
 					     * (e.g. Realtek codecs)
 					     */
 					     */
+	unsigned int pin_amp_workaround:1; /* pin out-amp takes index
+					    * (e.g. Conexant codecs)
+					    */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	unsigned int power_on :1;	/* current (global) power-state */
 	unsigned int power_on :1;	/* current (global) power-state */
 	unsigned int power_transition :1; /* power-state in transition */
 	unsigned int power_transition :1; /* power-state in transition */
@@ -855,6 +862,18 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
 #define snd_hda_sequence_write_cache	snd_hda_sequence_write
 #define snd_hda_sequence_write_cache	snd_hda_sequence_write
 #endif
 #endif
 
 
+/* the struct for codec->pin_configs */
+struct hda_pincfg {
+	hda_nid_t nid;
+	unsigned int cfg;
+};
+
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
+			     unsigned int cfg);
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+		       hda_nid_t nid, unsigned int cfg); /* for hwdep */
+
 /*
 /*
  * Mixer
  * Mixer
  */
  */

+ 2 - 2
sound/pci/hda/hda_generic.c

@@ -144,9 +144,9 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
 	node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 	node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 
 
 	if (node->type == AC_WID_PIN) {
 	if (node->type == AC_WID_PIN) {
-		node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP);
+		node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
 		node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-		node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+		node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
 	}
 	}
 
 
 	if (node->wid_caps & AC_WCAP_OUT_AMP) {
 	if (node->wid_caps & AC_WCAP_OUT_AMP) {

+ 219 - 21
sound/pci/hda/hda_hwdep.c

@@ -30,6 +30,12 @@
 #include <sound/hda_hwdep.h>
 #include <sound/hda_hwdep.h>
 #include <sound/minors.h>
 #include <sound/minors.h>
 
 
+/* hint string pair */
+struct hda_hint {
+	const char *key;
+	const char *val;	/* contained in the same alloc as key */
+};
+
 /*
 /*
  * write/read an out-of-bound verb
  * write/read an out-of-bound verb
  */
  */
@@ -99,16 +105,17 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
 
 
 static void clear_hwdep_elements(struct hda_codec *codec)
 static void clear_hwdep_elements(struct hda_codec *codec)
 {
 {
-	char **head;
 	int i;
 	int i;
 
 
 	/* clear init verbs */
 	/* clear init verbs */
 	snd_array_free(&codec->init_verbs);
 	snd_array_free(&codec->init_verbs);
 	/* clear hints */
 	/* clear hints */
-	head = codec->hints.list;
-	for (i = 0; i < codec->hints.used; i++, head++)
-		kfree(*head);
+	for (i = 0; i < codec->hints.used; i++) {
+		struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+		kfree(hint->key); /* we don't need to free hint->val */
+	}
 	snd_array_free(&codec->hints);
 	snd_array_free(&codec->hints);
+	snd_array_free(&codec->user_pins);
 }
 }
 
 
 static void hwdep_free(struct snd_hwdep *hwdep)
 static void hwdep_free(struct snd_hwdep *hwdep)
@@ -140,7 +147,8 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
 #endif
 #endif
 
 
 	snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
 	snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
-	snd_array_init(&codec->hints, sizeof(char *), 32);
+	snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
+	snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -153,7 +161,13 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
 
 
 static int clear_codec(struct hda_codec *codec)
 static int clear_codec(struct hda_codec *codec)
 {
 {
-	snd_hda_codec_reset(codec);
+	int err;
+
+	err = snd_hda_codec_reset(codec);
+	if (err < 0) {
+		snd_printk(KERN_ERR "The codec is being used, can't free.\n");
+		return err;
+	}
 	clear_hwdep_elements(codec);
 	clear_hwdep_elements(codec);
 	return 0;
 	return 0;
 }
 }
@@ -162,20 +176,29 @@ static int reconfig_codec(struct hda_codec *codec)
 {
 {
 	int err;
 	int err;
 
 
+	snd_hda_power_up(codec);
 	snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
 	snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
-	snd_hda_codec_reset(codec);
+	err = snd_hda_codec_reset(codec);
+	if (err < 0) {
+		snd_printk(KERN_ERR
+			   "The codec is being used, can't reconfigure.\n");
+		goto error;
+	}
 	err = snd_hda_codec_configure(codec);
 	err = snd_hda_codec_configure(codec);
 	if (err < 0)
 	if (err < 0)
-		return err;
+		goto error;
 	/* rebuild PCMs */
 	/* rebuild PCMs */
 	err = snd_hda_codec_build_pcms(codec);
 	err = snd_hda_codec_build_pcms(codec);
 	if (err < 0)
 	if (err < 0)
-		return err;
+		goto error;
 	/* rebuild mixers */
 	/* rebuild mixers */
 	err = snd_hda_codec_build_controls(codec);
 	err = snd_hda_codec_build_controls(codec);
 	if (err < 0)
 	if (err < 0)
-		return err;
-	return snd_card_register(codec->bus->card);
+		goto error;
+	err = snd_card_register(codec->bus->card);
+ error:
+	snd_hda_power_down(codec);
+	return err;
 }
 }
 
 
 /*
 /*
@@ -271,6 +294,22 @@ static ssize_t type##_store(struct device *dev,			\
 CODEC_ACTION_STORE(reconfig);
 CODEC_ACTION_STORE(reconfig);
 CODEC_ACTION_STORE(clear);
 CODEC_ACTION_STORE(clear);
 
 
+static ssize_t init_verbs_show(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	int i, len = 0;
+	for (i = 0; i < codec->init_verbs.used; i++) {
+		struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
+		len += snprintf(buf + len, PAGE_SIZE - len,
+				"0x%02x 0x%03x 0x%04x\n",
+				v->nid, v->verb, v->param);
+	}
+	return len;
+}
+
 static ssize_t init_verbs_store(struct device *dev,
 static ssize_t init_verbs_store(struct device *dev,
 				struct device_attribute *attr,
 				struct device_attribute *attr,
 				const char *buf, size_t count)
 				const char *buf, size_t count)
@@ -293,26 +332,157 @@ static ssize_t init_verbs_store(struct device *dev,
 	return count;
 	return count;
 }
 }
 
 
+static ssize_t hints_show(struct device *dev,
+			  struct device_attribute *attr,
+			  char *buf)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	int i, len = 0;
+	for (i = 0; i < codec->hints.used; i++) {
+		struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+		len += snprintf(buf + len, PAGE_SIZE - len,
+				"%s = %s\n", hint->key, hint->val);
+	}
+	return len;
+}
+
+static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
+{
+	int i;
+
+	for (i = 0; i < codec->hints.used; i++) {
+		struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+		if (!strcmp(hint->key, key))
+			return hint;
+	}
+	return NULL;
+}
+
+static void remove_trail_spaces(char *str)
+{
+	char *p;
+	if (!*str)
+		return;
+	p = str + strlen(str) - 1;
+	for (; isspace(*p); p--) {
+		*p = 0;
+		if (p == str)
+			return;
+	}
+}
+
+#define MAX_HINTS	1024
+
 static ssize_t hints_store(struct device *dev,
 static ssize_t hints_store(struct device *dev,
 			   struct device_attribute *attr,
 			   struct device_attribute *attr,
 			   const char *buf, size_t count)
 			   const char *buf, size_t count)
 {
 {
 	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
 	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
 	struct hda_codec *codec = hwdep->private_data;
 	struct hda_codec *codec = hwdep->private_data;
-	char *p;
-	char **hint;
+	char *key, *val;
+	struct hda_hint *hint;
 
 
-	if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+	while (isspace(*buf))
+		buf++;
+	if (!*buf || *buf == '#' || *buf == '\n')
 		return count;
 		return count;
-	p = kstrndup_noeol(buf, 1024);
-	if (!p)
+	if (*buf == '=')
+		return -EINVAL;
+	key = kstrndup_noeol(buf, 1024);
+	if (!key)
 		return -ENOMEM;
 		return -ENOMEM;
-	hint = snd_array_new(&codec->hints);
+	/* extract key and val */
+	val = strchr(key, '=');
+	if (!val) {
+		kfree(key);
+		return -EINVAL;
+	}
+	*val++ = 0;
+	while (isspace(*val))
+		val++;
+	remove_trail_spaces(key);
+	remove_trail_spaces(val);
+	hint = get_hint(codec, key);
+	if (hint) {
+		/* replace */
+		kfree(hint->key);
+		hint->key = key;
+		hint->val = val;
+		return count;
+	}
+	/* allocate a new hint entry */
+	if (codec->hints.used >= MAX_HINTS)
+		hint = NULL;
+	else
+		hint = snd_array_new(&codec->hints);
 	if (!hint) {
 	if (!hint) {
-		kfree(p);
+		kfree(key);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	*hint = p;
+	hint->key = key;
+	hint->val = val;
+	return count;
+}
+
+static ssize_t pin_configs_show(struct hda_codec *codec,
+				struct snd_array *list,
+				char *buf)
+{
+	int i, len = 0;
+	for (i = 0; i < list->used; i++) {
+		struct hda_pincfg *pin = snd_array_elem(list, i);
+		len += sprintf(buf + len, "0x%02x 0x%08x\n",
+			       pin->nid, pin->cfg);
+	}
+	return len;
+}
+
+static ssize_t init_pin_configs_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	return pin_configs_show(codec, &codec->init_pins, buf);
+}
+
+static ssize_t user_pin_configs_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	return pin_configs_show(codec, &codec->user_pins, buf);
+}
+
+static ssize_t driver_pin_configs_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	return pin_configs_show(codec, &codec->driver_pins, buf);
+}
+
+#define MAX_PIN_CONFIGS		32
+
+static ssize_t user_pin_configs_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	int nid, cfg;
+	int err;
+
+	if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
+		return -EINVAL;
+	if (!nid)
+		return -EINVAL;
+	err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+	if (err < 0)
+		return err;
 	return count;
 	return count;
 }
 }
 
 
@@ -331,8 +501,11 @@ static struct device_attribute codec_attrs[] = {
 	CODEC_ATTR_RO(mfg),
 	CODEC_ATTR_RO(mfg),
 	CODEC_ATTR_RW(name),
 	CODEC_ATTR_RW(name),
 	CODEC_ATTR_RW(modelname),
 	CODEC_ATTR_RW(modelname),
-	CODEC_ATTR_WO(init_verbs),
-	CODEC_ATTR_WO(hints),
+	CODEC_ATTR_RW(init_verbs),
+	CODEC_ATTR_RW(hints),
+	CODEC_ATTR_RO(init_pin_configs),
+	CODEC_ATTR_RW(user_pin_configs),
+	CODEC_ATTR_RO(driver_pin_configs),
 	CODEC_ATTR_WO(reconfig),
 	CODEC_ATTR_WO(reconfig),
 	CODEC_ATTR_WO(clear),
 	CODEC_ATTR_WO(clear),
 };
 };
@@ -351,4 +524,29 @@ int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * Look for hint string
+ */
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+	struct hda_hint *hint = get_hint(codec, key);
+	return hint ? hint->val : NULL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_hint);
+
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+	const char *p = snd_hda_get_hint(codec, key);
+	if (!p || !*p)
+		return -ENOENT;
+	switch (toupper(*p)) {
+	case 'T': /* true */
+	case 'Y': /* yes */
+	case '1':
+		return 1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
+
 #endif /* CONFIG_SND_HDA_RECONFIG */
 #endif /* CONFIG_SND_HDA_RECONFIG */

+ 77 - 38
sound/pci/hda/hda_intel.c

@@ -381,6 +381,7 @@ struct azx {
 
 
 	/* HD codec */
 	/* HD codec */
 	unsigned short codec_mask;
 	unsigned short codec_mask;
+	int  codec_probe_mask; /* copied from probe_mask option */
 	struct hda_bus *bus;
 	struct hda_bus *bus;
 
 
 	/* CORB/RIRB */
 	/* CORB/RIRB */
@@ -858,13 +859,18 @@ static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
 		      SD_CTL_DMA_START | SD_INT_MASK);
 		      SD_CTL_DMA_START | SD_INT_MASK);
 }
 }
 
 
-/* stop a stream */
-static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+/* stop DMA */
+static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
 {
 {
-	/* stop DMA */
 	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
 	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
 		      ~(SD_CTL_DMA_START | SD_INT_MASK));
 		      ~(SD_CTL_DMA_START | SD_INT_MASK));
 	azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
 	azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+}
+
+/* stop a stream */
+static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+{
+	azx_stream_clear(chip, azx_dev);
 	/* disable SIE */
 	/* disable SIE */
 	azx_writeb(chip, INTCTL,
 	azx_writeb(chip, INTCTL,
 		   azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
 		   azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
@@ -1075,8 +1081,7 @@ static int azx_setup_periods(struct azx *chip,
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 
 
-	period_bytes = snd_pcm_lib_period_bytes(substream);
-	azx_dev->period_bytes = period_bytes;
+	period_bytes = azx_dev->period_bytes;
 	periods = azx_dev->bufsize / period_bytes;
 	periods = azx_dev->bufsize / period_bytes;
 
 
 	/* program the initial BDL entries */
 	/* program the initial BDL entries */
@@ -1123,24 +1128,17 @@ static int azx_setup_periods(struct azx *chip,
  error:
  error:
 	snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
 	snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
 		   azx_dev->bufsize, period_bytes);
 		   azx_dev->bufsize, period_bytes);
-	/* reset */
-	azx_sd_writel(azx_dev, SD_BDLPL, 0);
-	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 	return -EINVAL;
 	return -EINVAL;
 }
 }
 
 
-/*
- * set up the SD for streaming
- */
-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+/* reset stream */
+static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
 {
 {
 	unsigned char val;
 	unsigned char val;
 	int timeout;
 	int timeout;
 
 
-	/* make sure the run bit is zero for SD */
-	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
-		      ~SD_CTL_DMA_START);
-	/* reset stream */
+	azx_stream_clear(chip, azx_dev);
+
 	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
 	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
 		      SD_CTL_STREAM_RESET);
 		      SD_CTL_STREAM_RESET);
 	udelay(3);
 	udelay(3);
@@ -1157,7 +1155,15 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
 	while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
 	while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
 	       --timeout)
 	       --timeout)
 		;
 		;
+}
 
 
+/*
+ * set up the SD for streaming
+ */
+static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+{
+	/* make sure the run bit is zero for SD */
+	azx_stream_clear(chip, azx_dev);
 	/* program the stream_tag */
 	/* program the stream_tag */
 	azx_sd_writel(azx_dev, SD_CTL,
 	azx_sd_writel(azx_dev, SD_CTL,
 		      (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
 		      (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
@@ -1228,7 +1234,6 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
 };
 };
 
 
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
-				      unsigned int codec_probe_mask,
 				      int no_init)
 				      int no_init)
 {
 {
 	struct hda_bus_template bus_temp;
 	struct hda_bus_template bus_temp;
@@ -1261,7 +1266,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
 
 
 	/* First try to probe all given codec slots */
 	/* First try to probe all given codec slots */
 	for (c = 0; c < max_slots; c++) {
 	for (c = 0; c < max_slots; c++) {
-		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+		if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
 			if (probe_codec(chip, c) < 0) {
 			if (probe_codec(chip, c) < 0) {
 				/* Some BIOSen give you wrong codec addresses
 				/* Some BIOSen give you wrong codec addresses
 				 * that don't exist
 				 * that don't exist
@@ -1285,7 +1290,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
 
 
 	/* Then create codec instances */
 	/* Then create codec instances */
 	for (c = 0; c < max_slots; c++) {
 	for (c = 0; c < max_slots; c++) {
-		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+		if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
 			struct hda_codec *codec;
 			struct hda_codec *codec;
 			err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
 			err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
 			if (err < 0)
 			if (err < 0)
@@ -1403,6 +1408,8 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
 	runtime->private_data = azx_dev;
 	runtime->private_data = azx_dev;
 	snd_pcm_set_sync(substream);
 	snd_pcm_set_sync(substream);
 	mutex_unlock(&chip->open_mutex);
 	mutex_unlock(&chip->open_mutex);
+
+	azx_stream_reset(chip, azx_dev);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1429,6 +1436,11 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
 static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
 static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *hw_params)
 			     struct snd_pcm_hw_params *hw_params)
 {
 {
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+
+	azx_dev->bufsize = 0;
+	azx_dev->period_bytes = 0;
+	azx_dev->format_val = 0;
 	return snd_pcm_lib_malloc_pages(substream,
 	return snd_pcm_lib_malloc_pages(substream,
 					params_buffer_bytes(hw_params));
 					params_buffer_bytes(hw_params));
 }
 }
@@ -1443,6 +1455,9 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 	azx_sd_writel(azx_dev, SD_CTL, 0);
 	azx_sd_writel(azx_dev, SD_CTL, 0);
+	azx_dev->bufsize = 0;
+	azx_dev->period_bytes = 0;
+	azx_dev->format_val = 0;
 
 
 	hinfo->ops.cleanup(hinfo, apcm->codec, substream);
 	hinfo->ops.cleanup(hinfo, apcm->codec, substream);
 
 
@@ -1456,23 +1471,37 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
 	struct azx_dev *azx_dev = get_azx_dev(substream);
 	struct azx_dev *azx_dev = get_azx_dev(substream);
 	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
 	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int bufsize, period_bytes, format_val;
+	int err;
 
 
-	azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
-	azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
-							 runtime->channels,
-							 runtime->format,
-							 hinfo->maxbps);
-	if (!azx_dev->format_val) {
+	format_val = snd_hda_calc_stream_format(runtime->rate,
+						runtime->channels,
+						runtime->format,
+						hinfo->maxbps);
+	if (!format_val) {
 		snd_printk(KERN_ERR SFX
 		snd_printk(KERN_ERR SFX
 			   "invalid format_val, rate=%d, ch=%d, format=%d\n",
 			   "invalid format_val, rate=%d, ch=%d, format=%d\n",
 			   runtime->rate, runtime->channels, runtime->format);
 			   runtime->rate, runtime->channels, runtime->format);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	bufsize = snd_pcm_lib_buffer_bytes(substream);
+	period_bytes = snd_pcm_lib_period_bytes(substream);
+
 	snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
 	snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
-		    azx_dev->bufsize, azx_dev->format_val);
-	if (azx_setup_periods(chip, substream, azx_dev) < 0)
-		return -EINVAL;
+		    bufsize, format_val);
+
+	if (bufsize != azx_dev->bufsize ||
+	    period_bytes != azx_dev->period_bytes ||
+	    format_val != azx_dev->format_val) {
+		azx_dev->bufsize = bufsize;
+		azx_dev->period_bytes = period_bytes;
+		azx_dev->format_val = format_val;
+		err = azx_setup_periods(chip, substream, azx_dev);
+		if (err < 0)
+			return err;
+	}
+
 	azx_setup_controller(chip, azx_dev);
 	azx_setup_controller(chip, azx_dev);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
 		azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
@@ -2100,25 +2129,36 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
 	SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
 	SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
 	/* including bogus ALC268 in slot#2 that conflicts with ALC888 */
 	/* including bogus ALC268 in slot#2 that conflicts with ALC888 */
 	SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
 	SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
-	/* conflict of ALC268 in slot#3 (digital I/O); a temporary fix */
-	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba laptop", 0x03),
+	/* forced codec slots */
+	SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
 	{}
 	{}
 };
 };
 
 
+#define AZX_FORCE_CODEC_MASK	0x100
+
 static void __devinit check_probe_mask(struct azx *chip, int dev)
 static void __devinit check_probe_mask(struct azx *chip, int dev)
 {
 {
 	const struct snd_pci_quirk *q;
 	const struct snd_pci_quirk *q;
 
 
-	if (probe_mask[dev] == -1) {
+	chip->codec_probe_mask = probe_mask[dev];
+	if (chip->codec_probe_mask == -1) {
 		q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
 		q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
 		if (q) {
 		if (q) {
 			printk(KERN_INFO
 			printk(KERN_INFO
 			       "hda_intel: probe_mask set to 0x%x "
 			       "hda_intel: probe_mask set to 0x%x "
 			       "for device %04x:%04x\n",
 			       "for device %04x:%04x\n",
 			       q->value, q->subvendor, q->subdevice);
 			       q->value, q->subvendor, q->subdevice);
-			probe_mask[dev] = q->value;
+			chip->codec_probe_mask = q->value;
 		}
 		}
 	}
 	}
+
+	/* check forced option */
+	if (chip->codec_probe_mask != -1 &&
+	    (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
+		chip->codec_mask = chip->codec_probe_mask & 0xff;
+		printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n",
+		       chip->codec_mask);
+	}
 }
 }
 
 
 
 
@@ -2359,8 +2399,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
 	card->private_data = chip;
 	card->private_data = chip;
 
 
 	/* create codec instances */
 	/* create codec instances */
-	err = azx_codec_create(chip, model[dev], probe_mask[dev],
-			       probe_only[dev]);
+	err = azx_codec_create(chip, model[dev], probe_only[dev]);
 	if (err < 0)
 	if (err < 0)
 		goto out_free;
 		goto out_free;
 
 
@@ -2457,10 +2496,10 @@ static struct pci_device_id azx_ids[] = {
 	{ PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
-	{ PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA },
-	{ PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
-	{ PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
-	{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
+	{ PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA },
+	{ PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA },
+	{ PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA },
+	{ PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
 	/* Teradici */
 	/* Teradici */
 	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
 	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
 	/* AMD Generic, PCI class code and Vendor ID for HD Audio */
 	/* AMD Generic, PCI class code and Vendor ID for HD Audio */

+ 29 - 4
sound/pci/hda/hda_local.h

@@ -26,8 +26,10 @@
 /*
 /*
  * for mixer controls
  * for mixer controls
  */
  */
+#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs)		\
+	((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
 #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
 #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
-	((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19))
+	HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
 /* mono volume with index (index=0,1,...) (channel=1,2) */
 /* mono volume with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
 #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
@@ -96,7 +98,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 					    const char *name);
 					    const char *name);
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 			unsigned int *tlv, const char **slaves);
 			unsigned int *tlv, const char **slaves);
-void snd_hda_codec_reset(struct hda_codec *codec);
+int snd_hda_codec_reset(struct hda_codec *codec);
 int snd_hda_codec_configure(struct hda_codec *codec);
 int snd_hda_codec_configure(struct hda_codec *codec);
 
 
 /* amp value bits */
 /* amp value bits */
@@ -134,7 +136,7 @@ extern struct hda_ctl_ops snd_hda_bind_sw;	/* for bind-switch */
 
 
 struct hda_bind_ctls {
 struct hda_bind_ctls {
 	struct hda_ctl_ops *ops;
 	struct hda_ctl_ops *ops;
-	long values[];
+	unsigned long values[];
 };
 };
 
 
 int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
@@ -227,6 +229,7 @@ struct hda_multi_out {
 	hda_nid_t hp_nid;	/* optional DAC for HP, 0 when not exists */
 	hda_nid_t hp_nid;	/* optional DAC for HP, 0 when not exists */
 	hda_nid_t extra_out_nid[3];	/* optional DACs, 0 when not exists */
 	hda_nid_t extra_out_nid[3];	/* optional DACs, 0 when not exists */
 	hda_nid_t dig_out_nid;	/* digital out audio widget */
 	hda_nid_t dig_out_nid;	/* digital out audio widget */
+	hda_nid_t *slave_dig_outs;
 	int max_channels;	/* currently supported analog channels */
 	int max_channels;	/* currently supported analog channels */
 	int dig_out_used;	/* current usage of digital out (HDA_DIG_XXX) */
 	int dig_out_used;	/* current usage of digital out (HDA_DIG_XXX) */
 	int no_share_stream;	/* don't share a stream with multiple pins */
 	int no_share_stream;	/* don't share a stream with multiple pins */
@@ -354,9 +357,12 @@ struct auto_pin_cfg {
 	int line_out_type;	/* AUTO_PIN_XXX_OUT */
 	int line_out_type;	/* AUTO_PIN_XXX_OUT */
 	hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
 	hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
 	hda_nid_t input_pins[AUTO_PIN_LAST];
 	hda_nid_t input_pins[AUTO_PIN_LAST];
-	hda_nid_t dig_out_pin;
+	int dig_outs;
+	hda_nid_t dig_out_pins[2];
 	hda_nid_t dig_in_pin;
 	hda_nid_t dig_in_pin;
 	hda_nid_t mono_out_pin;
 	hda_nid_t mono_out_pin;
+	int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+	int dig_in_type; /* HDA_PCM_TYPE_XXX */
 };
 };
 
 
 #define get_defcfg_connect(cfg) \
 #define get_defcfg_connect(cfg) \
@@ -405,6 +411,7 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 			      unsigned int caps);
 			      unsigned int caps);
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
 
 
 int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
 int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
 void snd_hda_ctls_clear(struct hda_codec *codec);
 void snd_hda_ctls_clear(struct hda_codec *codec);
@@ -427,6 +434,23 @@ static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
 }
 }
 #endif
 #endif
 
 
+#ifdef CONFIG_SND_HDA_RECONFIG
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
+#else
+static inline
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+	return NULL;
+}
+
+static inline
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+	return -ENOENT;
+}
+#endif
+
 /*
 /*
  * power-management
  * power-management
  */
  */
@@ -458,6 +482,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 #define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
 #define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
 #define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
 #define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
 #define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
+#define get_amp_offset(kc)	(((kc)->private_value >> 23) & 0x3f)
 
 
 /*
 /*
  * CEA Short Audio Descriptor data
  * CEA Short Audio Descriptor data

+ 15 - 6
sound/pci/hda/hda_proc.c

@@ -399,8 +399,10 @@ static void print_conn_list(struct snd_info_buffer *buffer,
 {
 {
 	int c, curr = -1;
 	int c, curr = -1;
 
 
-	if (conn_len > 1 && wid_type != AC_WID_AUD_MIX &&
-	    wid_type != AC_WID_VOL_KNB)
+	if (conn_len > 1 &&
+	    wid_type != AC_WID_AUD_MIX &&
+	    wid_type != AC_WID_VOL_KNB &&
+	    wid_type != AC_WID_POWER)
 		curr = snd_hda_codec_read(codec, nid, 0,
 		curr = snd_hda_codec_read(codec, nid, 0,
 					  AC_VERB_GET_CONNECT_SEL, 0);
 					  AC_VERB_GET_CONNECT_SEL, 0);
 	snd_iprintf(buffer, "  Connection: %d\n", conn_len);
 	snd_iprintf(buffer, "  Connection: %d\n", conn_len);
@@ -467,8 +469,9 @@ static void print_codec_info(struct snd_info_entry *entry,
 	snd_iprintf(buffer, "Codec: %s\n",
 	snd_iprintf(buffer, "Codec: %s\n",
 		    codec->name ? codec->name : "Not Set");
 		    codec->name ? codec->name : "Not Set");
 	snd_iprintf(buffer, "Address: %d\n", codec->addr);
 	snd_iprintf(buffer, "Address: %d\n", codec->addr);
-	snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
-	snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
+	snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
+	snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
+	snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
 	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
 	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
 
 
 	if (codec->mfg)
 	if (codec->mfg)
@@ -554,8 +557,14 @@ static void print_codec_info(struct snd_info_entry *entry,
 			snd_iprintf(buffer, "  Amp-Out caps: ");
 			snd_iprintf(buffer, "  Amp-Out caps: ");
 			print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
 			print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
 			snd_iprintf(buffer, "  Amp-Out vals: ");
 			snd_iprintf(buffer, "  Amp-Out vals: ");
-			print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
-				       wid_caps & AC_WCAP_STEREO, 1);
+			if (wid_type == AC_WID_PIN &&
+			    codec->pin_amp_workaround)
+				print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+					       wid_caps & AC_WCAP_STEREO,
+					       conn_len);
+			else
+				print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+					       wid_caps & AC_WCAP_STEREO, 1);
 		}
 		}
 
 
 		switch (wid_type) {
 		switch (wid_type) {

+ 112 - 83
sound/pci/hda/patch_analog.c

@@ -27,11 +27,12 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
+#include "hda_beep.h"
 
 
 struct ad198x_spec {
 struct ad198x_spec {
 	struct snd_kcontrol_new *mixers[5];
 	struct snd_kcontrol_new *mixers[5];
 	int num_mixers;
 	int num_mixers;
-
+	unsigned int beep_amp;	/* beep amp value, set via set_beep_amp() */
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
 						 * don't forget NULL termination!
 						 * don't forget NULL termination!
 						 */
 						 */
@@ -154,6 +155,16 @@ static const char *ad_slave_sws[] = {
 
 
 static void ad198x_free_kctls(struct hda_codec *codec);
 static void ad198x_free_kctls(struct hda_codec *codec);
 
 
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new ad_beep_mixer[] = {
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+#define set_beep_amp(spec, nid, idx, dir) \
+	((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+
 static int ad198x_build_controls(struct hda_codec *codec)
 static int ad198x_build_controls(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec = codec->spec;
 	struct ad198x_spec *spec = codec->spec;
@@ -181,6 +192,21 @@ static int ad198x_build_controls(struct hda_codec *codec)
 			return err;
 			return err;
 	}
 	}
 
 
+	/* create beep controls if needed */
+	if (spec->beep_amp) {
+		struct snd_kcontrol_new *knew;
+		for (knew = ad_beep_mixer; knew->name; knew++) {
+			struct snd_kcontrol *kctl;
+			kctl = snd_ctl_new1(knew, codec);
+			if (!kctl)
+				return -ENOMEM;
+			kctl->private_value = spec->beep_amp;
+			err = snd_hda_ctl_add(codec, kctl);
+			if (err < 0)
+				return err;
+		}
+	}
+
 	/* if we have no master control, let's create it */
 	/* if we have no master control, let's create it */
 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
 		unsigned int vmaster_tlv[4];
 		unsigned int vmaster_tlv[4];
@@ -406,7 +432,8 @@ static void ad198x_free(struct hda_codec *codec)
 		return;
 		return;
 
 
 	ad198x_free_kctls(codec);
 	ad198x_free_kctls(codec);
-	kfree(codec->spec);
+	kfree(spec);
+	snd_hda_detach_beep_device(codec);
 }
 }
 
 
 static struct hda_codec_ops ad198x_patch_ops = {
 static struct hda_codec_ops ad198x_patch_ops = {
@@ -545,8 +572,6 @@ static struct snd_kcontrol_new ad1986a_mixers[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -610,8 +635,7 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
-	/* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
-	   HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+	/* 
 	   HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	   HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
 	   HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
 	   HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -809,8 +833,6 @@ static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
 	{
 	{
@@ -1002,10 +1024,8 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
 	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
-	SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
-	SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
-	SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
 	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
 	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+	SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
 	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
 	SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
 	SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
@@ -1027,15 +1047,14 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
 
 
 static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
 static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
 {
 {
-	unsigned int conf = snd_hda_codec_read(codec, nid, 0,
-					       AC_VERB_GET_CONFIG_DEFAULT, 0);
+	unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
 	return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
 	return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
 }
 }
 
 
 static int patch_ad1986a(struct hda_codec *codec)
 static int patch_ad1986a(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
-	int board_config;
+	int err, board_config;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -1043,6 +1062,13 @@ static int patch_ad1986a(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
+	err = snd_hda_attach_beep_device(codec, 0x19);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
+
 	spec->multiout.max_channels = 6;
 	spec->multiout.max_channels = 6;
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
 	spec->multiout.dac_nids = ad1986a_dac_nids;
 	spec->multiout.dac_nids = ad1986a_dac_nids;
@@ -1222,8 +1248,6 @@ static struct snd_kcontrol_new ad1983_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
@@ -1294,6 +1318,7 @@ static struct hda_amp_list ad1983_loopbacks[] = {
 static int patch_ad1983(struct hda_codec *codec)
 static int patch_ad1983(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
+	int err;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -1301,6 +1326,13 @@ static int patch_ad1983(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
+	err = snd_hda_attach_beep_device(codec, 0x10);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
 	spec->multiout.dac_nids = ad1983_dac_nids;
 	spec->multiout.dac_nids = ad1983_dac_nids;
@@ -1370,8 +1402,6 @@ static struct snd_kcontrol_new ad1981_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
@@ -1416,8 +1446,8 @@ static struct hda_verb ad1981_init_verbs[] = {
 	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
 	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
 	/* Mic boost: 0dB */
 	/* Mic boost: 0dB */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Record selector: Front mic */
 	/* Record selector: Front mic */
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
@@ -1682,10 +1712,10 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
 	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
 	SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
 	SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
 	/* All HP models */
 	/* All HP models */
-	SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
+	SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
 	/* Lenovo Thinkpad T60/X60/Z6xx */
 	/* Lenovo Thinkpad T60/X60/Z6xx */
-	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
+	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
 	/* HP nx6320 (reversed SSID, H/W bug) */
 	/* HP nx6320 (reversed SSID, H/W bug) */
 	SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
 	SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
 	{}
 	{}
@@ -1694,7 +1724,7 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
 static int patch_ad1981(struct hda_codec *codec)
 static int patch_ad1981(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
-	int board_config;
+	int err, board_config;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -1702,6 +1732,13 @@ static int patch_ad1981(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
+	err = snd_hda_attach_beep_device(codec, 0x10);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
+
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
 	spec->multiout.dac_nids = ad1981_dac_nids;
 	spec->multiout.dac_nids = ad1981_dac_nids;
@@ -1988,9 +2025,6 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
 
 
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
 
@@ -2034,9 +2068,6 @@ static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
 
 
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
 
@@ -2066,9 +2097,6 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
 
 
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
 
@@ -2297,10 +2325,6 @@ static struct hda_verb ad1988_capture_init_verbs[] = {
 	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-	/* ADCs; muted */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 
 
 	{ }
 	{ }
 };
 };
@@ -2408,10 +2432,6 @@ static struct hda_verb ad1988_3stack_init_verbs[] = {
 	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-	/* ADCs; muted */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	/* Analog Mix output amp */
 	/* Analog Mix output amp */
 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 	{ }
 	{ }
@@ -2483,10 +2503,6 @@ static struct hda_verb ad1988_laptop_init_verbs[] = {
 	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-	/* ADCs; muted */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	/* Analog Mix output amp */
 	/* Analog Mix output amp */
 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 	{ }
 	{ }
@@ -2890,7 +2906,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
 
-	if (spec->autocfg.dig_out_pin)
+	if (spec->autocfg.dig_outs)
 		spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
 		spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = AD1988_SPDIF_IN;
 		spec->dig_in_nid = AD1988_SPDIF_IN;
@@ -2940,7 +2956,7 @@ static struct snd_pci_quirk ad1988_cfg_tbl[] = {
 static int patch_ad1988(struct hda_codec *codec)
 static int patch_ad1988(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
-	int board_config;
+	int err, board_config;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -2960,7 +2976,7 @@ static int patch_ad1988(struct hda_codec *codec)
 
 
 	if (board_config == AD1988_AUTO) {
 	if (board_config == AD1988_AUTO) {
 		/* automatic parse from the BIOS config */
 		/* automatic parse from the BIOS config */
-		int err = ad1988_parse_auto_config(codec);
+		err = ad1988_parse_auto_config(codec);
 		if (err < 0) {
 		if (err < 0) {
 			ad198x_free(codec);
 			ad198x_free(codec);
 			return err;
 			return err;
@@ -2970,6 +2986,13 @@ static int patch_ad1988(struct hda_codec *codec)
 		}
 		}
 	}
 	}
 
 
+	err = snd_hda_attach_beep_device(codec, 0x10);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
 	switch (board_config) {
 	switch (board_config) {
 	case AD1988_6STACK:
 	case AD1988_6STACK:
 	case AD1988_6STACK_DIG:
 	case AD1988_6STACK_DIG:
@@ -3126,12 +3149,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
-	/*
-	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-	*/
 	HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3204,10 +3221,10 @@ static struct hda_verb ad1884_init_verbs[] = {
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
 	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
 	/* Port-B (front mic) pin */
 	/* Port-B (front mic) pin */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Port-C (rear mic) pin */
 	/* Port-C (rear mic) pin */
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Analog mixer; mute as default */
 	/* Analog mixer; mute as default */
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
@@ -3240,7 +3257,7 @@ static const char *ad1884_slave_vols[] = {
 	"CD Playback Volume",
 	"CD Playback Volume",
 	"Internal Mic Playback Volume",
 	"Internal Mic Playback Volume",
 	"Docking Mic Playback Volume"
 	"Docking Mic Playback Volume"
-	"Beep Playback Volume",
+	/* "Beep Playback Volume", */
 	"IEC958 Playback Volume",
 	"IEC958 Playback Volume",
 	NULL
 	NULL
 };
 };
@@ -3248,6 +3265,7 @@ static const char *ad1884_slave_vols[] = {
 static int patch_ad1884(struct hda_codec *codec)
 static int patch_ad1884(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
+	int err;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -3255,6 +3273,13 @@ static int patch_ad1884(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
+	err = snd_hda_attach_beep_device(codec, 0x10);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
 	spec->multiout.dac_nids = ad1884_dac_nids;
 	spec->multiout.dac_nids = ad1884_dac_nids;
@@ -3321,8 +3346,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
 	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -3358,7 +3381,7 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	/* docking mic boost */
 	/* docking mic boost */
-	{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	/* Analog mixer - docking mic; mute as default */
 	/* Analog mixer - docking mic; mute as default */
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	/* enable EAPD bit */
 	/* enable EAPD bit */
@@ -3379,10 +3402,6 @@ static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
-	/*
-	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
-	*/
 	HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3468,7 +3487,7 @@ static const char *ad1984_models[AD1984_MODELS] = {
 
 
 static struct snd_pci_quirk ad1984_cfg_tbl[] = {
 static struct snd_pci_quirk ad1984_cfg_tbl[] = {
 	/* Lenovo Thinkpad T61/X61 */
 	/* Lenovo Thinkpad T61/X61 */
-	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
 	SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
 	SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
 	{}
 	{}
 };
 };
@@ -3561,8 +3580,6 @@ static struct snd_kcontrol_new ad1884a_base_mixers[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
@@ -3622,10 +3639,10 @@ static struct hda_verb ad1884a_init_verbs[] = {
 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	/* Port-B (front mic) pin */
 	/* Port-B (front mic) pin */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Port-C (rear line-in) pin */
 	/* Port-C (rear line-in) pin */
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Port-E (rear mic) pin */
 	/* Port-E (rear mic) pin */
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
@@ -3695,8 +3712,6 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
 	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
@@ -3724,8 +3739,6 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
 	HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3836,8 +3849,6 @@ static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3911,9 +3922,9 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
-	SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
-	SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
-	SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
+	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
+	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
+	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
 	{}
 	{}
 };
 };
@@ -3921,7 +3932,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
 static int patch_ad1884a(struct hda_codec *codec)
 static int patch_ad1884a(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
-	int board_config;
+	int err, board_config;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -3929,6 +3940,13 @@ static int patch_ad1884a(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
+	err = snd_hda_attach_beep_device(codec, 0x10);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
 	spec->multiout.dac_nids = ad1884a_dac_nids;
 	spec->multiout.dac_nids = ad1884a_dac_nids;
@@ -3966,6 +3984,14 @@ static int patch_ad1884a(struct hda_codec *codec)
 		spec->multiout.dig_out_nid = 0;
 		spec->multiout.dig_out_nid = 0;
 		codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
 		codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
 		codec->patch_ops.init = ad1884a_hp_init;
 		codec->patch_ops.init = ad1884a_hp_init;
+		/* set the upper-limit for mixer amp to 0dB for avoiding the
+		 * possible damage by overloading
+		 */
+		snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+					  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+					  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+					  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+					  (1 << AC_AMPCAP_MUTE_SHIFT));
 		break;
 		break;
 	case AD1884A_THINKPAD:
 	case AD1884A_THINKPAD:
 		spec->mixers[0] = ad1984a_thinkpad_mixers;
 		spec->mixers[0] = ad1984a_thinkpad_mixers;
@@ -4083,8 +4109,6 @@ static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -4097,8 +4121,6 @@ static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
-	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
 	HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -4257,7 +4279,7 @@ static const char *ad1882_models[AD1986A_MODELS] = {
 static int patch_ad1882(struct hda_codec *codec)
 static int patch_ad1882(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec;
 	struct ad198x_spec *spec;
-	int board_config;
+	int err, board_config;
 
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -4265,6 +4287,13 @@ static int patch_ad1882(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
+	err = snd_hda_attach_beep_device(codec, 0x10);
+	if (err < 0) {
+		ad198x_free(codec);
+		return err;
+	}
+	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
 	spec->multiout.max_channels = 6;
 	spec->multiout.max_channels = 6;
 	spec->multiout.num_dacs = 3;
 	spec->multiout.num_dacs = 3;
 	spec->multiout.dac_nids = ad1882_dac_nids;
 	spec->multiout.dac_nids = ad1882_dac_nids;

+ 6 - 6
sound/pci/hda/patch_cmedia.c

@@ -680,13 +680,13 @@ static int patch_cmi9880(struct hda_codec *codec)
 		struct auto_pin_cfg cfg;
 		struct auto_pin_cfg cfg;
 
 
 		/* collect pin default configuration */
 		/* collect pin default configuration */
-		port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-		port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+		port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
+		port_f = snd_hda_codec_get_pincfg(codec, 0x10);
 		spec->front_panel = 1;
 		spec->front_panel = 1;
 		if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
 		if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
 		    get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
 		    get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
-			port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-			port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+			port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
+			port_h = snd_hda_codec_get_pincfg(codec, 0x20);
 			spec->channel_modes = cmi9880_channel_modes;
 			spec->channel_modes = cmi9880_channel_modes;
 			/* no front panel */
 			/* no front panel */
 			if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
 			if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
@@ -703,8 +703,8 @@ static int patch_cmi9880(struct hda_codec *codec)
 			spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
 			spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
 		} else {
 		} else {
 			spec->input_mux = &cmi9880_basic_mux;
 			spec->input_mux = &cmi9880_basic_mux;
-			port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-			port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+			port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
+			port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
 			if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
 			if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
 				spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
 				spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
 			if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
 			if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)

+ 188 - 180
sound/pci/hda/patch_conexant.c

@@ -58,6 +58,7 @@ struct conexant_spec {
 
 
 	struct snd_kcontrol_new *mixers[5];
 	struct snd_kcontrol_new *mixers[5];
 	int num_mixers;
 	int num_mixers;
+	hda_nid_t vmaster_nid;
 
 
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
 						 * don't forget NULL
 						 * don't forget NULL
@@ -72,6 +73,7 @@ struct conexant_spec {
 					 */
 					 */
 	unsigned int cur_eapd;
 	unsigned int cur_eapd;
 	unsigned int hp_present;
 	unsigned int hp_present;
+	unsigned int no_auto_mic;
 	unsigned int need_dac_fix;
 	unsigned int need_dac_fix;
 
 
 	/* capture */
 	/* capture */
@@ -461,6 +463,29 @@ static void conexant_free(struct hda_codec *codec)
 	kfree(codec->spec);
 	kfree(codec->spec);
 }
 }
 
 
+static struct snd_kcontrol_new cxt_capture_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = conexant_mux_enum_info,
+		.get = conexant_mux_enum_get,
+		.put = conexant_mux_enum_put
+	},
+	{}
+};
+
+static const char *slave_vols[] = {
+	"Headphone Playback Volume",
+	"Speaker Playback Volume",
+	NULL
+};
+
+static const char *slave_sws[] = {
+	"Headphone Playback Switch",
+	"Speaker Playback Switch",
+	NULL
+};
+
 static int conexant_build_controls(struct hda_codec *codec)
 static int conexant_build_controls(struct hda_codec *codec)
 {
 {
 	struct conexant_spec *spec = codec->spec;
 	struct conexant_spec *spec = codec->spec;
@@ -488,6 +513,32 @@ static int conexant_build_controls(struct hda_codec *codec)
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
+
+	/* if we have no master control, let's create it */
+	if (spec->vmaster_nid &&
+	    !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+		unsigned int vmaster_tlv[4];
+		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+					HDA_OUTPUT, vmaster_tlv);
+		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+					  vmaster_tlv, slave_vols);
+		if (err < 0)
+			return err;
+	}
+	if (spec->vmaster_nid &&
+	    !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+					  NULL, slave_sws);
+		if (err < 0)
+			return err;
+	}
+
+	if (spec->input_mux) {
+		err = snd_hda_add_new_ctls(codec, cxt_capture_mixers);
+		if (err < 0)
+			return err;
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -719,13 +770,6 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec,
 }
 }
 
 
 static struct snd_kcontrol_new cxt5045_mixers[] = {
 static struct snd_kcontrol_new cxt5045_mixers[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.info = conexant_mux_enum_info,
-		.get = conexant_mux_enum_get,
-		.put = conexant_mux_enum_put
-	},
 	HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
@@ -759,13 +803,6 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
 };
 };
 
 
 static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
 static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.info = conexant_mux_enum_info,
-		.get = conexant_mux_enum_get,
-		.put = conexant_mux_enum_put
-	},
 	HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
@@ -1002,15 +1039,9 @@ static const char *cxt5045_models[CXT5045_MODELS] = {
 };
 };
 
 
 static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
 static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
-	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
-	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
-	SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
-	SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
-	SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
-	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
 	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
 	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
-	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
+			   CXT5045_LAPTOP_HPSENSE),
 	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
 	SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
@@ -1020,8 +1051,8 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
-	SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
-	SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+	SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell",
+			   CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
 	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
 	{}
 	{}
 };
 };
@@ -1035,6 +1066,7 @@ static int patch_cxt5045(struct hda_codec *codec)
 	if (!spec)
 	if (!spec)
 		return -ENOMEM;
 		return -ENOMEM;
 	codec->spec = spec;
 	codec->spec = spec;
+	codec->pin_amp_workaround = 1;
 
 
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
@@ -1134,7 +1166,7 @@ static int patch_cxt5045(struct hda_codec *codec)
 /* Conexant 5047 specific */
 /* Conexant 5047 specific */
 #define CXT5047_SPDIF_OUT	0x11
 #define CXT5047_SPDIF_OUT	0x11
 
 
-static hda_nid_t cxt5047_dac_nids[2] = { 0x10, 0x1c };
+static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
 static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
 static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
 static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
 static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
 
 
@@ -1142,20 +1174,6 @@ static struct hda_channel_mode cxt5047_modes[1] = {
 	{ 2, NULL },
 	{ 2, NULL },
 };
 };
 
 
-static struct hda_input_mux cxt5047_capture_source = {
-	.num_items = 1,
-	.items = {
-		{ "Mic", 0x2 },
-	}
-};
-
-static struct hda_input_mux cxt5047_hp_capture_source = {
-	.num_items = 1,
-	.items = {
-		{ "ExtMic", 0x2 },
-	}
-};
-
 static struct hda_input_mux cxt5047_toshiba_capture_source = {
 static struct hda_input_mux cxt5047_toshiba_capture_source = {
 	.num_items = 2,
 	.num_items = 2,
 	.items = {
 	.items = {
@@ -1179,7 +1197,11 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 	 * the headphone jack
 	 * the headphone jack
 	 */
 	 */
 	bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
 	bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
-	snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
+	/* NOTE: Conexat codec needs the index for *OUTPUT* amp of
+	 * pin widgets unlike other codecs.  In this case, we need to
+	 * set index 0x01 for the volume from the mixer amp 0x19.
+	 */
+	snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
 				 HDA_AMP_MUTE, bits);
 				 HDA_AMP_MUTE, bits);
 	bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
 	bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
 	snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
 	snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
@@ -1187,16 +1209,6 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 	return 1;
 	return 1;
 }
 }
 
 
-/* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */
-static struct hda_bind_ctls cxt5047_bind_master_vol = {
-	.ops = &snd_hda_bind_vol,
-	.values = {
-		HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT),
-		HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-		0
-	},
-};
-
 /* mute internal speaker if HP is plugged */
 /* mute internal speaker if HP is plugged */
 static void cxt5047_hp_automute(struct hda_codec *codec)
 static void cxt5047_hp_automute(struct hda_codec *codec)
 {
 {
@@ -1207,27 +1219,8 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
 
 
 	bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
 	bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	/* Mute/Unmute PCM 2 for good measure - some systems need this */
-	snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
-
-/* mute internal speaker if HP is plugged */
-static void cxt5047_hp2_automute(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	unsigned int bits;
-
-	spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-
-	bits = spec->hp_present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	/* Mute/Unmute PCM 2 for good measure - some systems need this */
-	snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0,
+	/* See the note in cxt5047_hp_master_sw_put */
+	snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
 				 HDA_AMP_MUTE, bits);
 				 HDA_AMP_MUTE, bits);
 }
 }
 
 
@@ -1268,55 +1261,14 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec,
 	}
 	}
 }
 }
 
 
-/* unsolicited event for HP jack sensing - non-EAPD systems */
-static void cxt5047_hp2_unsol_event(struct hda_codec *codec,
-				  unsigned int res)
-{
-	res >>= 26;
-	switch (res) {
-	case CONEXANT_HP_EVENT:
-		cxt5047_hp2_automute(codec);
-		break;
-	case CONEXANT_MIC_EVENT:
-		cxt5047_hp_automic(codec);
-		break;
-	}
-}
-
-static struct snd_kcontrol_new cxt5047_mixers[] = {
-	HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Gain Volume", 0x1a, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Mic Gain Switch", 0x1a, 0x0, HDA_OUTPUT),
+static struct snd_kcontrol_new cxt5047_base_mixers[] = {
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
 	HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
 	HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("PCM-2 Volume", 0x1c, 0x00, HDA_OUTPUT),
-	HDA_CODEC_MUTE("PCM-2 Switch", 0x1c, 0x00, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x00, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x00, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Headphone Playback Switch", 0x13, 0x00, HDA_OUTPUT),
-
-	{}
-};
-
-static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.info = conexant_mux_enum_info,
-		.get = conexant_mux_enum_get,
-		.put = conexant_mux_enum_put
-	},
-	HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
-	HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
-	HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
-	HDA_BIND_VOL("Master Playback Volume", &cxt5047_bind_master_vol),
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Master Playback Switch",
 		.name = "Master Playback Switch",
@@ -1329,29 +1281,15 @@ static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = {
 	{}
 	{}
 };
 };
 
 
-static struct snd_kcontrol_new cxt5047_hp_mixers[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.info = conexant_mux_enum_info,
-		.get = conexant_mux_enum_get,
-		.put = conexant_mux_enum_put
-	},
-	HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
-	HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
-	HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
+static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
+	/* See the note in cxt5047_hp_master_sw_put */
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
+	{}
+};
+
+static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = cxt_eapd_info,
-		.get = cxt_eapd_get,
-		.put = cxt5047_hp_master_sw_put,
-		.private_value = 0x13,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -1362,8 +1300,8 @@ static struct hda_verb cxt5047_init_verbs[] = {
 	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
 	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
 	/* HP, Speaker  */
 	/* HP, Speaker  */
 	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
 	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
-	{0x13, AC_VERB_SET_CONNECT_SEL,0x1},
-	{0x1d, AC_VERB_SET_CONNECT_SEL,0x0},
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */
+	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */
 	/* Record selector: Mic */
 	/* Record selector: Mic */
 	{0x12, AC_VERB_SET_CONNECT_SEL,0x03},
 	{0x12, AC_VERB_SET_CONNECT_SEL,0x03},
 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE,
 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -1383,30 +1321,7 @@ static struct hda_verb cxt5047_init_verbs[] = {
 
 
 /* configuration for Toshiba Laptops */
 /* configuration for Toshiba Laptops */
 static struct hda_verb cxt5047_toshiba_init_verbs[] = {
 static struct hda_verb cxt5047_toshiba_init_verbs[] = {
-	{0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */
-	/* pin sensing on HP and Mic jacks */
-	{0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
-	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
-	/* Speaker routing */
-	{0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
-	{}
-};
-
-/* configuration for HP Laptops */
-static struct hda_verb cxt5047_hp_init_verbs[] = {
-	/* pin sensing on HP jack */
-	{0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
-	/* 0x13 is actually shared by both HP and speaker;
-	 * setting the connection to 0 (=0x19) makes the master volume control
-	 * working mysteriouslly...
-	 */
-	{0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
-	/* Record selector: Ext Mic */
-	{0x12, AC_VERB_SET_CONNECT_SEL,0x03},
-	{0x19, AC_VERB_SET_AMP_GAIN_MUTE,
-	 AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
-	/* Speaker routing */
-	{0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
+	{0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
 	{}
 	{}
 };
 };
 
 
@@ -1571,11 +1486,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
 };
 };
 
 
 static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
 static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
 	SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
-	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
-	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
-	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6700", CXT5047_LAPTOP),
+	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
+			   CXT5047_LAPTOP),
 	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
 	{}
 	{}
 };
 };
@@ -1589,6 +1502,7 @@ static int patch_cxt5047(struct hda_codec *codec)
 	if (!spec)
 	if (!spec)
 		return -ENOMEM;
 		return -ENOMEM;
 	codec->spec = spec;
 	codec->spec = spec;
+	codec->pin_amp_workaround = 1;
 
 
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
 	spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
 	spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
@@ -1597,9 +1511,8 @@ static int patch_cxt5047(struct hda_codec *codec)
 	spec->num_adc_nids = 1;
 	spec->num_adc_nids = 1;
 	spec->adc_nids = cxt5047_adc_nids;
 	spec->adc_nids = cxt5047_adc_nids;
 	spec->capsrc_nids = cxt5047_capsrc_nids;
 	spec->capsrc_nids = cxt5047_capsrc_nids;
-	spec->input_mux = &cxt5047_capture_source;
 	spec->num_mixers = 1;
 	spec->num_mixers = 1;
-	spec->mixers[0] = cxt5047_mixers;
+	spec->mixers[0] = cxt5047_base_mixers;
 	spec->num_init_verbs = 1;
 	spec->num_init_verbs = 1;
 	spec->init_verbs[0] = cxt5047_init_verbs;
 	spec->init_verbs[0] = cxt5047_init_verbs;
 	spec->spdif_route = 0;
 	spec->spdif_route = 0;
@@ -1613,21 +1526,22 @@ static int patch_cxt5047(struct hda_codec *codec)
 						  cxt5047_cfg_tbl);
 						  cxt5047_cfg_tbl);
 	switch (board_config) {
 	switch (board_config) {
 	case CXT5047_LAPTOP:
 	case CXT5047_LAPTOP:
-		codec->patch_ops.unsol_event = cxt5047_hp2_unsol_event;
+		spec->num_mixers = 2;
+		spec->mixers[1] = cxt5047_hp_spk_mixers;
+		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 		break;
 		break;
 	case CXT5047_LAPTOP_HP:
 	case CXT5047_LAPTOP_HP:
-		spec->input_mux = &cxt5047_hp_capture_source;
-		spec->num_init_verbs = 2;
-		spec->init_verbs[1] = cxt5047_hp_init_verbs;
-		spec->mixers[0] = cxt5047_hp_mixers;
+		spec->num_mixers = 2;
+		spec->mixers[1] = cxt5047_hp_only_mixers;
 		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 		codec->patch_ops.init = cxt5047_hp_init;
 		codec->patch_ops.init = cxt5047_hp_init;
 		break;
 		break;
 	case CXT5047_LAPTOP_EAPD:
 	case CXT5047_LAPTOP_EAPD:
 		spec->input_mux = &cxt5047_toshiba_capture_source;
 		spec->input_mux = &cxt5047_toshiba_capture_source;
+		spec->num_mixers = 2;
+		spec->mixers[1] = cxt5047_hp_spk_mixers;
 		spec->num_init_verbs = 2;
 		spec->num_init_verbs = 2;
 		spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
 		spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
-		spec->mixers[0] = cxt5047_toshiba_mixers;
 		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 		break;
 		break;
 #ifdef CONFIG_SND_DEBUG
 #ifdef CONFIG_SND_DEBUG
@@ -1638,6 +1552,7 @@ static int patch_cxt5047(struct hda_codec *codec)
 		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 		codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 #endif	
 #endif	
 	}
 	}
+	spec->vmaster_nid = 0x13;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1673,8 +1588,11 @@ static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 /* toggle input of built-in and mic jack appropriately */
 /* toggle input of built-in and mic jack appropriately */
 static void cxt5051_portb_automic(struct hda_codec *codec)
 static void cxt5051_portb_automic(struct hda_codec *codec)
 {
 {
+	struct conexant_spec *spec = codec->spec;
 	unsigned int present;
 	unsigned int present;
 
 
+	if (spec->no_auto_mic)
+		return;
 	present = snd_hda_codec_read(codec, 0x17, 0,
 	present = snd_hda_codec_read(codec, 0x17, 0,
 				     AC_VERB_GET_PIN_SENSE, 0) &
 				     AC_VERB_GET_PIN_SENSE, 0) &
 		AC_PINSENSE_PRESENCE;
 		AC_PINSENSE_PRESENCE;
@@ -1690,6 +1608,8 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
 	unsigned int present;
 	unsigned int present;
 	hda_nid_t new_adc;
 	hda_nid_t new_adc;
 
 
+	if (spec->no_auto_mic)
+		return;
 	present = snd_hda_codec_read(codec, 0x18, 0,
 	present = snd_hda_codec_read(codec, 0x18, 0,
 				     AC_VERB_GET_PIN_SENSE, 0) &
 				     AC_VERB_GET_PIN_SENSE, 0) &
 		AC_PINSENSE_PRESENCE;
 		AC_PINSENSE_PRESENCE;
@@ -1776,6 +1696,22 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
 	{}
 	{}
 };
 };
 
 
+static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
+	HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = cxt_eapd_info,
+		.get = cxt_eapd_get,
+		.put = cxt5051_hp_master_sw_put,
+		.private_value = 0x1a,
+	},
+
+	{}
+};
+
 static struct hda_verb cxt5051_init_verbs[] = {
 static struct hda_verb cxt5051_init_verbs[] = {
 	/* Line in, Mic */
 	/* Line in, Mic */
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
@@ -1806,6 +1742,66 @@ static struct hda_verb cxt5051_init_verbs[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
+static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
+	/* Line in, Mic */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+	/* SPK  */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* HP, Amp  */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* DAC1 */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Record selector: Int mic */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* SPDIF route: PCM */
+	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* EAPD */
+	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+	{ } /* end */
+};
+
+static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
+	/* Line in, Mic */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+	/* SPK  */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* HP, Amp  */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Docking HP */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* DAC1 */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Record selector: Int mic */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+	/* SPDIF route: PCM */
+	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* EAPD */
+	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+	{ } /* end */
+};
+
 /* initialize jack-sensing, too */
 /* initialize jack-sensing, too */
 static int cxt5051_init(struct hda_codec *codec)
 static int cxt5051_init(struct hda_codec *codec)
 {
 {
@@ -1823,18 +1819,24 @@ static int cxt5051_init(struct hda_codec *codec)
 enum {
 enum {
 	CXT5051_LAPTOP,	 /* Laptops w/ EAPD support */
 	CXT5051_LAPTOP,	 /* Laptops w/ EAPD support */
 	CXT5051_HP,	/* no docking */
 	CXT5051_HP,	/* no docking */
+	CXT5051_HP_DV6736,	/* HP without mic switch */
+	CXT5051_LENOVO_X200,	/* Lenovo X200 laptop */
 	CXT5051_MODELS
 	CXT5051_MODELS
 };
 };
 
 
 static const char *cxt5051_models[CXT5051_MODELS] = {
 static const char *cxt5051_models[CXT5051_MODELS] = {
 	[CXT5051_LAPTOP]	= "laptop",
 	[CXT5051_LAPTOP]	= "laptop",
 	[CXT5051_HP]		= "hp",
 	[CXT5051_HP]		= "hp",
+	[CXT5051_HP_DV6736]	= "hp-dv6736",
+	[CXT5051_LENOVO_X200]	= "lenovo-x200",
 };
 };
 
 
 static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
 static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
 	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
 	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
 		      CXT5051_LAPTOP),
 		      CXT5051_LAPTOP),
 	SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
 	SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
 	{}
 	{}
 };
 };
 
 
@@ -1847,6 +1849,7 @@ static int patch_cxt5051(struct hda_codec *codec)
 	if (!spec)
 	if (!spec)
 		return -ENOMEM;
 		return -ENOMEM;
 	codec->spec = spec;
 	codec->spec = spec;
+	codec->pin_amp_workaround = 1;
 
 
 	codec->patch_ops = conexant_patch_ops;
 	codec->patch_ops = conexant_patch_ops;
 	codec->patch_ops.init = cxt5051_init;
 	codec->patch_ops.init = cxt5051_init;
@@ -1867,17 +1870,22 @@ static int patch_cxt5051(struct hda_codec *codec)
 	spec->cur_adc = 0;
 	spec->cur_adc = 0;
 	spec->cur_adc_idx = 0;
 	spec->cur_adc_idx = 0;
 
 
+	codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+
 	board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
 	board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
 						  cxt5051_models,
 						  cxt5051_models,
 						  cxt5051_cfg_tbl);
 						  cxt5051_cfg_tbl);
 	switch (board_config) {
 	switch (board_config) {
 	case CXT5051_HP:
 	case CXT5051_HP:
-		codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
 		spec->mixers[0] = cxt5051_hp_mixers;
 		spec->mixers[0] = cxt5051_hp_mixers;
 		break;
 		break;
-	default:
-	case CXT5051_LAPTOP:
-		codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+	case CXT5051_HP_DV6736:
+		spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
+		spec->mixers[0] = cxt5051_hp_dv6736_mixers;
+		spec->no_auto_mic = 1;
+		break;
+	case CXT5051_LENOVO_X200:
+		spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
 		break;
 		break;
 	}
 	}
 
 

File diff suppressed because it is too large
+ 324 - 171
sound/pci/hda/patch_realtek.c


File diff suppressed because it is too large
+ 247 - 228
sound/pci/hda/patch_sigmatel.c


+ 7 - 10
sound/pci/hda/patch_via.c

@@ -1308,16 +1308,13 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
 	unsigned int def_conf;
 	unsigned int def_conf;
 	unsigned char seqassoc;
 	unsigned char seqassoc;
 
 
-	def_conf = snd_hda_codec_read(codec, nid, 0,
-				      AC_VERB_GET_CONFIG_DEFAULT, 0);
+	def_conf = snd_hda_codec_get_pincfg(codec, nid);
 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
 		if (seqassoc == 0xff) {
 		if (seqassoc == 0xff) {
 			def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
 			def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
-					    def_conf >> 24);
+			snd_hda_codec_set_pincfg(codec, nid, def_conf);
 		}
 		}
 	}
 	}
 
 
@@ -1354,7 +1351,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
 
-	if (spec->autocfg.dig_out_pin)
+	if (spec->autocfg.dig_outs)
 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = VT1708_DIGIN_NID;
 		spec->dig_in_nid = VT1708_DIGIN_NID;
@@ -1827,7 +1824,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
 
-	if (spec->autocfg.dig_out_pin)
+	if (spec->autocfg.dig_outs)
 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = VT1709_DIGIN_NID;
 		spec->dig_in_nid = VT1709_DIGIN_NID;
@@ -2371,7 +2368,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
 
-	if (spec->autocfg.dig_out_pin)
+	if (spec->autocfg.dig_outs)
 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = VT1708B_DIGIN_NID;
 		spec->dig_in_nid = VT1708B_DIGIN_NID;
@@ -2836,7 +2833,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
 
-	if (spec->autocfg.dig_out_pin)
+	if (spec->autocfg.dig_outs)
 		spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
 		spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
 
 
 	spec->extra_dig_out_nid = 0x15;
 	spec->extra_dig_out_nid = 0x15;
@@ -3155,7 +3152,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
 
-	if (spec->autocfg.dig_out_pin)
+	if (spec->autocfg.dig_outs)
 		spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
 		spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
 
 
 	spec->extra_dig_out_nid = 0x1B;
 	spec->extra_dig_out_nid = 0x1B;

Some files were not shown because too many files changed in this diff