Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 71ef049..2260130 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -615,9 +615,11 @@
   Module snd-hda-intel
   --------------------
 
-    Module for Intel HD Audio (ICH6, ICH6M, ICH7)
+    Module for Intel HD Audio (ICH6, ICH6M, ICH7), ATI SB450,
+	       VIA VT8251/VT8237A
 
     model	- force the model name
+    position_fix - Fix DMA pointer (0 = FIFO size, 1 = none, 2 = POSBUF)
 
     Module supports up to 8 cards.
 
@@ -635,6 +637,7 @@
 	  5stack	5-jack in back, 2-jack in front
 	  5stack-digout	5-jack in back, 2-jack in front, a SPDIF out
 	  w810		3-jack
+	  z71v		3-jack (HP shared SPDIF)
 
 	CMI9880
 	  minimal	3-jack in back
@@ -643,6 +646,14 @@
 	  full_dig	6-jack in back, 2-jack in front, SPDIF I/O
 	  allout	5-jack in back, 2-jack in front, SPDIF out
 
+    Note 2: If you get click noises on output, try the module option
+	    position_fix=1 or 2.  position_fix=1 will use the SD_LPIB
+	    register value without FIFO size correction as the current
+	    DMA pointer.  position_fix=2 will make the driver to use
+	    the position buffer instead of reading SD_LPIB register.
+	    (Usually SD_LPLIB register is more accurate than the
+	    position buffer.)
+
   Module snd-hdsp
   ---------------
 
@@ -677,15 +688,19 @@
                         * TerraTec EWS 88D
                         * TerraTec EWX 24/96
                         * TerraTec DMX 6Fire
+			* TerraTec Phase 88
                         * Hoontech SoundTrack DSP 24
                         * Hoontech SoundTrack DSP 24 Value
                         * Hoontech SoundTrack DSP 24 Media 7.1
+			* Event Electronics, EZ8
                         * Digigram VX442
+			* Lionstracs, Mediastaton
 
     model       - Use the given board model, one of the following:
 		  delta1010, dio2496, delta66, delta44, audiophile, delta410,
 		  delta1010lt, vx442, ewx2496, ews88mt, ews88mt_new, ews88d,
-		  dmx6fire, dsp24, dsp24_value, dsp24_71, ez8
+		  dmx6fire, dsp24, dsp24_value, dsp24_71, ez8,
+		  phase88, mediastation
     omni	- Omni I/O support for MidiMan M-Audio Delta44/66
     cs8427_timeout - reset timeout for the CS8427 chip (S/PDIF transciever)
                      in msec resolution, default value is 500 (0.5 sec)
@@ -694,20 +709,46 @@
     is not used with all Envy24 based cards (for example in the MidiMan Delta
     serie).
 
+    Note: The supported board is detected by reading EEPROM or PCI
+	  SSID (if EEPROM isn't available).  You can override the
+	  model by passing "model" module option in case that the
+	  driver isn't configured properly or you want to try another
+	  type for testing.
+
   Module snd-ice1724
   ------------------
 
-    Module for Envy24HT (VT/ICE1724) based PCI sound cards.
+    Module for Envy24HT (VT/ICE1724), Envy24PT (VT1720) based PCI sound cards.
 			* MidiMan M Audio Revolution 7.1
 			* AMP Ltd AUDIO2000
-			* TerraTec Aureon Sky-5.1, Space-7.1
+			* TerraTec Aureon 5.1 Sky
+			* TerraTec Aureon 7.1 Space
+			* TerraTec Aureon 7.1 Universe
+			* TerraTec Phase 22
+			* TerraTec Phase 28
+			* AudioTrak Prodigy 7.1
+			* AudioTrak Prodigy 192
+			* Pontis MS300
+			* Albatron K8X800 Pro II 
+			* Chaintech ZNF3-150
+			* Chaintech ZNF3-250
+			* Chaintech 9CJS
+			* Chaintech AV-710
+			* Shuttle SN25P
 
     model       - Use the given board model, one of the following:
-		  revo71, amp2000, prodigy71, aureon51, aureon71,
-		  k8x800
+		  revo71, amp2000, prodigy71, prodigy192, aureon51,
+		  aureon71, universe, k8x800, phase22, phase28, ms300,
+		  av710
 
     Module supports up to 8 cards and autoprobe.
 
+    Note: The supported board is detected by reading EEPROM or PCI
+	  SSID (if EEPROM isn't available).  You can override the
+	  model by passing "model" module option in case that the
+	  driver isn't configured properly or you want to try another
+	  type for testing.
+
   Module snd-intel8x0
   -------------------
 
@@ -1211,16 +1252,18 @@
   ------------------
 
     Module for AC'97 motherboards based on VIA 82C686A/686B, 8233,
-    8233A, 8233C, 8235 (south) bridge.
+    8233A, 8233C, 8235, 8237 (south) bridge.
 
     mpu_port	- 0x300,0x310,0x320,0x330, otherwise obtain BIOS setup
 		  [VIA686A/686B only]
     joystick	- Enable joystick (default off) [VIA686A/686B only]
     ac97_clock	- AC'97 codec clock base (default 48000Hz)
     dxs_support	- support DXS channels,
-		  0 = auto (defalut), 1 = enable, 2 = disable,
-		  3 = 48k only, 4 = no VRA
-		  [VIA8233/C,8235 only]
+		  0 = auto (default), 1 = enable, 2 = disable,
+		  3 = 48k only, 4 = no VRA, 5 = enable any sample
+		  rate and different sample rates on different
+		  channels
+		  [VIA8233/C, 8235, 8237 only]
     ac97_quirk  - AC'97 workaround for strange hardware
                   See the description of intel8x0 module for details.
 
@@ -1232,18 +1275,23 @@
           default value 1.4.  Then the interrupt number will be
           assigned under 15. You might also upgrade your BIOS.
     
-    Note: VIA8233/5 (not VIA8233A) can support DXS (direct sound)
+    Note: VIA8233/5/7 (not VIA8233A) can support DXS (direct sound)
 	  channels as the first PCM.  On these channels, up to 4
-	  streams can be played at the same time.
+	  streams can be played at the same time, and the controller
+	  can perform sample rate conversion with separate rates for
+	  each channel.
 	  As default (dxs_support = 0), 48k fixed rate is chosen
 	  except for the known devices since the output is often
 	  noisy except for 48k on some mother boards due to the
 	  bug of BIOS.
-	  Please try once dxs_support=1 and if it works on other
+	  Please try once dxs_support=5 and if it works on other
 	  sample rates (e.g. 44.1kHz of mp3 playback), please let us
 	  know the PCI subsystem vendor/device id's (output of
 	  "lspci -nv").
-	  If it doesn't work, try dxs_support=4.  If it still doesn't
+	  If dxs_support=5 does not work, try dxs_support=4; if it
+	  doesn't work too, try dxs_support=1.  (dxs_support=1 is
+	  usually for old motherboards.  The correct implementated
+	  board should work with 4 or 5.)  If it still doesn't
 	  work and the default setting is ok, dxs_support=3 is the
 	  right choice.  If the default setting doesn't work at all,
 	  try dxs_support=2 to disable the DXS channels.
diff --git a/Documentation/sound/alsa/CMIPCI.txt b/Documentation/sound/alsa/CMIPCI.txt
index 4a7df77..1872e24 100644
--- a/Documentation/sound/alsa/CMIPCI.txt
+++ b/Documentation/sound/alsa/CMIPCI.txt
@@ -89,19 +89,22 @@
 
 There are some control switchs affecting to the speaker connections:
 
-"Line-In As Rear"	- As mentioned above, the line-in jack is used
-	for the rear (3th and 4th channels) output.
-"Line-In As Bass"	- The line-in jack is used for the bass (5th
-	and 6th channels) output.
-"Mic As Center/LFE"	- The mic jack is used for the bass output.
-	If this switch is on, you cannot use a microphone as a capture
-	source, of course.
-
+"Line-In Mode"	- an enum control to change the behavior of line-in
+	jack.  Either "Line-In", "Rear Output" or "Bass Output" can
+	be selected.  The last item is available only with model 039
+	or newer. 
+	When "Rear Output" is chosen, the surround channels 3 and 4
+	are output to line-in jack.
+"Mic-In Mode"	- an enum control to change the behavior of mic-in
+	jack.  Either "Mic-In" or "Center/LFE Output" can be
+	selected. 
+	When "Center/LFE Output" is chosen, the center and bass
+	channels (channels 5 and 6) are output to mic-in jack. 
 
 Digital I/O
 -----------
 
-The CM8x38 provides the excellent SPDIF capability with very chip
+The CM8x38 provides the excellent SPDIF capability with very cheap
 price (yes, that's the reason I bought the card :)
 
 The SPDIF playback and capture are done via the third PCM device
@@ -122,8 +125,9 @@
 simultaneously.
 
 To enable SPDIF output, you need to turn on "IEC958 Output Switch"
-control via mixer or alsactl.  Then you'll see the red light on from
-the card so you know that's working obviously :)
+control via mixer or alsactl ("IEC958" is the official name of
+so-called S/PDIF).  Then you'll see the red light on from the card so
+you know that's working obviously :)
 The SPDIF input is always enabled, so you can hear SPDIF input data
 from line-out with "IEC958 In Monitor" switch at any time (see
 below).
@@ -205,9 +209,10 @@
 MIDI CONTROLLER
 ---------------
 
-The MPU401-UART interface is enabled as default only for the first
-(CMIPCI) card.  You need to set module option "midi_port" properly
-for the 2nd (CMIPCI) card.
+The MPU401-UART interface is disabled as default.  You need to set
+module option "mpu_port" with a valid I/O port address to enable the
+MIDI support.  The valid I/O ports are 0x300, 0x310, 0x320 and 0x330.
+Choose the value which doesn't conflict with other cards.
 
 There is _no_ hardware wavetable function on this chip (except for
 OPL3 synth below).
@@ -229,9 +234,11 @@
 Joystick and Modem
 ------------------
 
-The joystick and modem should be available by enabling the control
-switch "Joystick" and "Modem" respectively.  But I myself have never
-tested them yet.
+The legacy joystick is supported.  To enable the joystick support, pass
+joystick_port=1 module option.  The value 1 means the auto-detection.
+If the auto-detection fails, try to pass the exact I/O address.
+
+The modem is enabled dynamically via a card control switch "Modem".
 
 
 Debugging Information
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
index e789475..db0b7d2 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
@@ -371,7 +371,7 @@
           <listitem><para>create <function>probe()</function> callback.</para></listitem>
           <listitem><para>create <function>remove()</function> callback.</para></listitem>
           <listitem><para>create pci_driver table which contains the three pointers above.</para></listitem>
-          <listitem><para>create <function>init()</function> function just calling <function>pci_module_init()</function> to register the pci_driver table defined above.</para></listitem>
+          <listitem><para>create <function>init()</function> function just calling <function>pci_register_driver()</function> to register the pci_driver table defined above.</para></listitem>
           <listitem><para>create <function>exit()</function> function to call <function>pci_unregister_driver()</function> function.</para></listitem>
         </itemizedlist>
       </para>
@@ -1198,7 +1198,7 @@
   /* initialization of the module */
   static int __init alsa_card_mychip_init(void)
   {
-          return pci_module_init(&driver);
+          return pci_register_driver(&driver);
   }
 
   /* clean up the module */
@@ -1654,7 +1654,7 @@
 <![CDATA[
   static int __init alsa_card_mychip_init(void)
   {
-          return pci_module_init(&driver);
+          return pci_register_driver(&driver);
   }
 
   static void __exit alsa_card_mychip_exit(void)
diff --git a/Documentation/sound/alsa/emu10k1-jack.txt b/Documentation/sound/alsa/emu10k1-jack.txt
new file mode 100644
index 0000000..751d450
--- /dev/null
+++ b/Documentation/sound/alsa/emu10k1-jack.txt
@@ -0,0 +1,74 @@
+This document is a guide to using the emu10k1 based devices with JACK for low
+latency, multichannel recording functionality.  All of my recent work to allow
+Linux users to use the full capabilities of their hardware has been inspired 
+by the kX Project.  Without their work I never would have discovered the true
+power of this hardware.
+
+	http://www.kxproject.com
+						- Lee Revell, 2005.03.30
+
+Low latency, multichannel audio with JACK and the emu10k1/emu10k2
+-----------------------------------------------------------------
+
+Until recently, emu10k1 users on Linux did not have access to the same low
+latency, multichannel features offered by the "kX ASIO" feature of their
+Windows driver.  As of ALSA 1.0.9 this is no more!
+
+For those unfamiliar with kX ASIO, this consists of 16 capture and 16 playback
+channels.  With a post 2.6.9 Linux kernel, latencies down to 64 (1.33 ms) or
+even 32 (0.66ms) frames should work well.
+
+The configuration is slightly more involved than on Windows, as you have to
+select the correct device for JACK to use.  Actually, for qjackctl users it's
+fairly self explanatory - select Duplex, then for capture and playback select
+the multichannel devices, set the in and out channels to 16, and the sample
+rate to 48000Hz.  The command line looks like this:
+
+/usr/local/bin/jackd -R -dalsa -r48000 -p64 -n2 -D -Chw:0,2 -Phw:0,3 -S
+
+This will give you 16 input ports and 16 output ports.
+
+The 16 output ports map onto the 16 FX buses (or the first 16 of 64, for the
+Audigy).  The mapping from FX bus to physical output is described in
+SB-Live-mixer.txt (or Audigy-mixer.txt).
+
+The 16 input ports are connected to the 16 physical inputs.  Contrary to
+popular belief, all emu10k1 cards are multichannel cards.  Which of these
+input channels have physical inputs connected to them depends on the card
+model.  Trial and error is highly recommended; the pinout diagrams
+for the card have been reverse engineered by some enterprising kX users and are 
+available on the internet.  Meterbridge is helpful here, and the kX forums are
+packed with useful information.
+
+Each input port will either correspond to a digital (SPDIF) input, an analog
+input, or nothing.  The one exception is the SBLive! 5.1.  On these devices,
+the second and third input ports are wired to the center/LFE output.  You will
+still see 16 capture channels, but only 14 are available for recording inputs.
+
+This chart, borrowed from kxfxlib/da_asio51.cpp, describes the mapping of JACK
+ports to FXBUS2 (multitrack recording input) and EXTOUT (physical output)
+channels.
+
+/*JACK (& ASIO) mappings on 10k1 5.1 SBLive cards:
+--------------------------------------------
+JACK		Epilog		FXBUS2(nr)
+--------------------------------------------
+capture_1	asio14		FXBUS2(0xe)
+capture_2	asio15		FXBUS2(0xf)
+capture_3	asio0		FXBUS2(0x0)	
+~capture_4	Center		EXTOUT(0x11)	// mapped to by Center
+~capture_5	LFE		EXTOUT(0x12)	// mapped to by LFE
+capture_6	asio3		FXBUS2(0x3)
+capture_7	asio4		FXBUS2(0x4)
+capture_8	asio5		FXBUS2(0x5)
+capture_9	asio6		FXBUS2(0x6)
+capture_10	asio7		FXBUS2(0x7)
+capture_11	asio8		FXBUS2(0x8)
+capture_12	asio9		FXBUS2(0x9)
+capture_13	asio10		FXBUS2(0xa)
+capture_14	asio11		FXBUS2(0xb)
+capture_15	asio12		FXBUS2(0xc)
+capture_16	asio13		FXBUS2(0xd)
+*/
+
+TODO: describe use of ld10k1/qlo10k1 in conjunction with JACK
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 2433e27..996eeab 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -437,6 +437,7 @@
 	void (*suspend) (ac97_t *ac97);
 	void (*resume) (ac97_t *ac97);
 #endif
+	void (*update_jacks) (ac97_t *ac97);	/* for jack-sharing */
 };
 
 struct _snd_ac97_bus_ops {
@@ -516,6 +517,9 @@
 		} ad18xx;
 		unsigned int dev_flags;		/* device specific */
 	} spec;
+	/* jack-sharing info */
+	unsigned char indep_surround;
+	unsigned char channel_mode;
 };
 
 /* conditions */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index a4d149f..9974f83 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -113,9 +113,10 @@
 	SNDRV_HWDEP_IFACE_BLUETOOTH,	/* Bluetooth audio */
 	SNDRV_HWDEP_IFACE_USX2Y_PCM,	/* Tascam US122, US224 & US428 rawusb pcm */
 	SNDRV_HWDEP_IFACE_PCXHR,	/* Digigram PCXHR */
+	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_PCXHR
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
 };
 
 struct sndrv_hwdep_info {
@@ -344,7 +345,7 @@
 	SNDRV_PCM_HW_PARAM_LAST_INTERVAL = SNDRV_PCM_HW_PARAM_TICK_TIME
 };
 
-#define SNDRV_PCM_HW_PARAMS_RUNTIME		(1<<0)
+#define SNDRV_PCM_HW_PARAMS_NORESAMPLE		(1<<0)	/* avoid rate resampling */
 
 struct sndrv_interval {
 	unsigned int min, max;
@@ -559,7 +560,7 @@
  *  Timer section - /dev/snd/timer
  */
 
-#define SNDRV_TIMER_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 2)
+#define SNDRV_TIMER_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 4)
 
 enum sndrv_timer_class {
 	SNDRV_TIMER_CLASS_NONE = -1,
@@ -672,10 +673,11 @@
 	SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info),
 	SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params),
 	SNDRV_TIMER_IOCTL_STATUS = _IOR('T', 0x14, struct sndrv_timer_status),
-	SNDRV_TIMER_IOCTL_START = _IO('T', 0x20),
-	SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21),
-	SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22),
-	SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0x23),
+	/* The following four ioctls are changed since 1.0.9 due to confliction */
+	SNDRV_TIMER_IOCTL_START = _IO('T', 0xa0),
+	SNDRV_TIMER_IOCTL_STOP = _IO('T', 0xa1),
+	SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0xa2),
+	SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0xa3),
 };
 
 struct sndrv_timer_read {
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 43b6786..c50b919 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -83,7 +83,8 @@
 #define IPR			0x08		/* Global interrupt pending register		*/
 						/* Clear pending interrupts by writing a 1 to	*/
 						/* the relevant bits and zero to the other bits	*/
-
+#define IPR_P16V		0x80000000	/* Bit set when the CA0151 P16V chip wishes
+						   to interrupt */
 #define IPR_GPIOMSG		0x20000000	/* GPIO message interrupt (RE'd, still not sure 
 						   which INTE bits enable it)			*/
 
@@ -746,6 +747,7 @@
 						/* Assumes sample lock				*/
 
 /* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS.			*/
+#define SRCS_SPDIFVALID		0x04000000	/* SPDIF stream valid				*/
 #define SRCS_SPDIFLOCKED	0x02000000	/* SPDIF stream locked				*/
 #define SRCS_RATELOCKED		0x01000000	/* Sample rate locked				*/
 #define SRCS_ESTSAMPLERATE	0x0007ffff	/* Do not modify this field.			*/
@@ -803,10 +805,26 @@
 #define A_FXWC2			0x75		/* Selects 0x9f-0x80 for FX recording           */
 
 #define A_SPDIF_SAMPLERATE	0x76		/* Set the sample rate of SPDIF output		*/
-#define A_SPDIF_RATE_MASK	0x000000c0
+#define A_SAMPLE_RATE		0x76		/* Various sample rate settings. */
+#define A_SAMPLE_RATE_NOT_USED  0x0ffc111e	/* Bits that are not used and cannot be set. 	*/
+#define A_SAMPLE_RATE_UNKNOWN	0xf0030001	/* Bits that can be set, but have unknown use. 	*/
+#define A_SPDIF_RATE_MASK	0x000000e0	/* Any other values for rates, just use 48000	*/
 #define A_SPDIF_48000		0x00000000
-#define A_SPDIF_44100		0x00000080
+#define A_SPDIF_192000		0x00000020
 #define A_SPDIF_96000		0x00000040
+#define A_SPDIF_44100		0x00000080
+
+#define A_I2S_CAPTURE_RATE_MASK	0x00000e00	/* This sets the capture PCM rate, but it is    */
+#define A_I2S_CAPTURE_48000	0x00000000	/* unclear if this sets the ADC rate as well.	*/
+#define A_I2S_CAPTURE_192000	0x00000200
+#define A_I2S_CAPTURE_96000	0x00000400
+#define A_I2S_CAPTURE_44100	0x00000800
+
+#define A_PCM_RATE_MASK		0x0000e000	/* This sets the playback PCM rate on the P16V	*/
+#define A_PCM_48000		0x00000000
+#define A_PCM_192000		0x00002000
+#define A_PCM_96000		0x00004000
+#define A_PCM_44100		0x00008000
 
 /* 0x77,0x78,0x79 "something i2s-related" - default to 0x01080000 on my audigy 2 ZS --rlrevell	*/
 /* 0x7a, 0x7b - lookup tables */
@@ -1039,28 +1057,28 @@
 	u32 vendor;
 	u32 device;
 	u32 subsystem;
+	unsigned char revision;
 	unsigned char emu10k1_chip; /* Original SB Live. Not SB Live 24bit. */
 	unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
 	unsigned char ca0102_chip;  /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
 	unsigned char ca0108_chip;  /* Audigy 2 Value */
 	unsigned char ca0151_chip;  /* P16V */
 	unsigned char spk71;        /* Has 7.1 speakers */
+	unsigned char sblive51;	    /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
 	unsigned char spdif_bug;    /* Has Spdif phasing bug */
 	unsigned char ac97_chip;    /* Has an AC97 chip */
 	unsigned char ecard;        /* APS EEPROM */
-	char * driver;
-	char * name;
+	const char *driver;
+	const char *name;
+	const char *id;		/* for backward compatibility - can be NULL if not needed */
 } emu_chip_details_t;
 
 struct _snd_emu10k1 {
 	int irq;
 
 	unsigned long port;			/* I/O port number */
-	unsigned int APS: 1,			/* APS flag */
-	    no_ac97: 1,				/* no AC'97 */
-	    tos_link: 1,			/* tos link detected */
-	    rear_ac97: 1,			/* rear channels are on AC'97 */
-	    spk71:1;				/* 7.1 configuration (Audigy 2 ZS) */
+	unsigned int tos_link: 1,		/* tos link detected */
+	    rear_ac97: 1;			/* rear channels are on AC'97 */
 	const emu_chip_details_t *card_capabilities;	/* Contains profile of card capabilities */
 	unsigned int audigy;			/* is Audigy? */
 	unsigned int revision;			/* chip revision */
@@ -1109,7 +1127,10 @@
 
 	emu10k1_voice_t voices[NUM_G];
 	emu10k1_voice_t p16v_voices[4];
+	emu10k1_voice_t p16v_capture_voice;
 	int p16v_device_offset;
+	u32 p16v_capture_source;
+	u32 p16v_capture_channel;
 	emu10k1_pcm_mixer_t pcm_mixer[32];
 	emu10k1_pcm_mixer_t efx_pcm_mixer[NUM_EFX_PLAYBACK];
 	snd_kcontrol_t *ctl_send_routing;
@@ -1453,7 +1474,6 @@
 #endif
 
 typedef struct {
-	unsigned int card;			/* card type */
 	unsigned int internal_tram_size;	/* in samples */
 	unsigned int external_tram_size;	/* in samples */
 	char fxbus_names[16][32];		/* names of FXBUSes */
diff --git a/include/sound/gus.h b/include/sound/gus.h
index 8b6287a..b4b461c 100644
--- a/include/sound/gus.h
+++ b/include/sound/gus.h
@@ -526,9 +526,6 @@
 extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr);
 extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data);
 extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr);
-extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data);
-extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr);
-extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count);
 extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
 extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
 extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
@@ -544,9 +541,6 @@
 {
 	return snd_gf1_i_look16(gus, reg | 0x80);
 }
-extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
-extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
-extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
 
 extern void snd_gf1_select_active_voices(snd_gus_card_t * gus);
 
@@ -580,10 +574,6 @@
 
 void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup);
 int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block);
-snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
-				      unsigned int address);
-snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
-				       unsigned int *share_id);
 snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
 				       char *name, int size, int w_16,
 				       int align, unsigned int *share_id);
@@ -608,23 +598,13 @@
 /* gus_volume.c */
 
 unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol);
-unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol);
-unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
-				    unsigned short start,
-				    unsigned short end,
-				    unsigned int us);
 unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2);
-unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens);
-unsigned short snd_gf1_compute_freq(unsigned int freq,
-				    unsigned int rate,
-				    unsigned short mix_rate);
 
 /* gus_reset.c */
 
 void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what);
 void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice);
 void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice);
-void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
 void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
 snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port);
 void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice);
@@ -641,9 +621,6 @@
 
 #ifdef CONFIG_SND_DEBUG
 extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus);
-extern void snd_gf1_print_global_registers(snd_gus_card_t * gus);
-extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus);
-extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit);
 #endif
 
 /* gus.c */
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 53fc04d..d935417 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -848,23 +848,6 @@
 
 void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params);
 void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var);
-int snd_pcm_hw_param_min(snd_pcm_substream_t *substream, 
-			 snd_pcm_hw_params_t *params,
-			 snd_pcm_hw_param_t var,
-			 unsigned int val, int *dir);
-int snd_pcm_hw_param_max(snd_pcm_substream_t *substream, 
-			 snd_pcm_hw_params_t *params,
-			 snd_pcm_hw_param_t var,
-			 unsigned int val, int *dir);
-int snd_pcm_hw_param_setinteger(snd_pcm_substream_t *substream, 
-				snd_pcm_hw_params_t *params,
-				snd_pcm_hw_param_t var);
-int snd_pcm_hw_param_first(snd_pcm_substream_t *substream, 
-			   snd_pcm_hw_params_t *params,
-			   snd_pcm_hw_param_t var, int *dir);
-int snd_pcm_hw_param_last(snd_pcm_substream_t *substream, 
-			  snd_pcm_hw_params_t *params,
-			  snd_pcm_hw_param_t var, int *dir);
 int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, 
 			  snd_pcm_hw_params_t *params,
 			  snd_pcm_hw_param_t var, 
@@ -876,7 +859,6 @@
 int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
 
 int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
-int snd_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
 
 int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream);
 int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream);
@@ -922,8 +904,22 @@
 int snd_pcm_format_linear(snd_pcm_format_t format);
 int snd_pcm_format_little_endian(snd_pcm_format_t format);
 int snd_pcm_format_big_endian(snd_pcm_format_t format);
+/**
+ * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is CPU-endian, 0 if
+ * opposite, or a negative error code if endian not specified.
+ */
+/* int snd_pcm_format_cpu_endian(snd_pcm_format_t format); */
+#ifdef SNDRV_LITTLE_ENDIAN
+#define snd_pcm_format_cpu_endian	snd_pcm_format_little_endian
+#else
+#define snd_pcm_format_cpu_endian	snd_pcm_format_big_endian
+#endif
 int snd_pcm_format_width(snd_pcm_format_t format);			/* in bits */
 int snd_pcm_format_physical_width(snd_pcm_format_t format);		/* in bits */
+ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples);
 const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format);
 int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames);
 snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian);
diff --git a/include/sound/seq_midi_event.h b/include/sound/seq_midi_event.h
index 4357cac..8857e2b 100644
--- a/include/sound/seq_midi_event.h
+++ b/include/sound/seq_midi_event.h
@@ -41,9 +41,7 @@
 };
 
 int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev);
-int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize);
 void snd_midi_event_free(snd_midi_event_t *dev);
-void snd_midi_event_init(snd_midi_event_t *dev);
 void snd_midi_event_reset_encode(snd_midi_event_t *dev);
 void snd_midi_event_reset_decode(snd_midi_event_t *dev);
 void snd_midi_event_no_status(snd_midi_event_t *dev, int on);
diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h
index cf4e238..1ad27e8 100644
--- a/include/sound/seq_virmidi.h
+++ b/include/sound/seq_virmidi.h
@@ -79,6 +79,5 @@
 #define SNDRV_VIRMIDI_SEQ_DISPATCH	2
 
 int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi);
-int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev);
 
 #endif /* __SOUND_SEQ_VIRMIDI */
diff --git a/include/sound/timer.h b/include/sound/timer.h
index 57fde99..1898511 100644
--- a/include/sound/timer.h
+++ b/include/sound/timer.h
@@ -152,6 +152,4 @@
 
 extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left);
 
-extern unsigned int snd_timer_system_resolution(void);
-
 #endif /* __SOUND_TIMER_H */
diff --git a/include/sound/version.h b/include/sound/version.h
index 98b4230..46acfa8 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated by configure.  */
-#define CONFIG_SND_VERSION "1.0.9rc2"
-#define CONFIG_SND_DATE "  (Thu Mar 24 10:33:39 2005 UTC)"
+#define CONFIG_SND_VERSION "1.0.9"
+#define CONFIG_SND_DATE " (Sun May 29 07:31:02 2005 UTC)"
diff --git a/sound/Kconfig b/sound/Kconfig
index 047d59e..ee794ae 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -42,6 +42,11 @@
 config SND
 	tristate "Advanced Linux Sound Architecture"
 	depends on SOUND
+	help
+	  Say 'Y' or 'M' to enable ALSA (Advanced Linux Sound Architecture),
+	  the new base sound system.
+
+	  For more information, see <http://www.alsa-project.org/>
 
 source "sound/core/Kconfig"
 
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index cdacf4d..34c1740 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -14,5 +14,11 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sa11xx-uda1341.
 
+config SND_ARMAACI
+	tristate "ARM PrimeCell PL041 AC Link support"
+	depends on SND && ARM_AMBA
+	select SND_PCM
+	select SND_AC97_CODEC
+
 endmenu
 
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index d7e7dc0..f74ec28 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -6,3 +6,6 @@
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o 
+
+obj-$(CONFIG_SND_ARMAACI)	+= snd-aaci.o
+snd-aaci-objs			:= aaci.o devdma.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
new file mode 100644
index 0000000..08cc3dd
--- /dev/null
+++ b/sound/arm/aaci.c
@@ -0,0 +1,968 @@
+/*
+ *  linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Documentation: ARM DDI 0173B
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "aaci.h"
+#include "devdma.h"
+
+#define DRIVER_NAME	"aaci-pl041"
+
+/*
+ * PM support is not complete.  Turn it off.
+ */
+#undef CONFIG_PM
+
+static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97)
+{
+	u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
+
+	/*
+	 * Ensure that the slot 1/2 RX registers are empty.
+	 */
+	v = readl(aaci->base + AACI_SLFR);
+	if (v & SLFR_2RXV)
+		readl(aaci->base + AACI_SL2RX);
+	if (v & SLFR_1RXV)
+		readl(aaci->base + AACI_SL1RX);
+
+	writel(maincr, aaci->base + AACI_MAINCR);
+}
+
+/*
+ * P29:
+ *  The recommended use of programming the external codec through slot 1
+ *  and slot 2 data is to use the channels during setup routines and the
+ *  slot register at any other time.  The data written into slot 1, slot 2
+ *  and slot 12 registers is transmitted only when their corresponding
+ *  SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
+ *  register.
+ */
+static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	struct aaci *aaci = ac97->private_data;
+	u32 v;
+
+	if (ac97->num >= 4)
+		return;
+
+	down(&aaci->ac97_sem);
+
+	aaci_ac97_select_codec(aaci, ac97);
+
+	/*
+	 * P54: You must ensure that AACI_SL2TX is always written
+	 * to, if required, before data is written to AACI_SL1TX.
+	 */
+	writel(val << 4, aaci->base + AACI_SL2TX);
+	writel(reg << 12, aaci->base + AACI_SL1TX);
+
+	/*
+	 * Wait for the transmission of both slots to complete.
+	 */
+	do {
+		v = readl(aaci->base + AACI_SLFR);
+	} while (v & (SLFR_1TXB|SLFR_2TXB));
+
+	up(&aaci->ac97_sem);
+}
+
+/*
+ * Read an AC'97 register.
+ */
+static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	struct aaci *aaci = ac97->private_data;
+	u32 v;
+
+	if (ac97->num >= 4)
+		return ~0;
+
+	down(&aaci->ac97_sem);
+
+	aaci_ac97_select_codec(aaci, ac97);
+
+	/*
+	 * Write the register address to slot 1.
+	 */
+	writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
+
+	/*
+	 * Wait for the transmission to complete.
+	 */
+	do {
+		v = readl(aaci->base + AACI_SLFR);
+	} while (v & SLFR_1TXB);
+
+	/*
+	 * Give the AC'97 codec more than enough time
+	 * to respond. (42us = ~2 frames at 48kHz.)
+	 */
+	udelay(42);
+
+	/*
+	 * Wait for slot 2 to indicate data.
+	 */
+	do {
+		cond_resched();
+		v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
+	} while (v != (SLFR_1RXV|SLFR_2RXV));
+
+	v = readl(aaci->base + AACI_SL1RX) >> 12;
+	if (v == reg) {
+		v = readl(aaci->base + AACI_SL2RX) >> 4;
+	} else {
+		dev_err(&aaci->dev->dev,
+			"wrong ac97 register read back (%x != %x)\n",
+			v, reg);
+		v = ~0;
+	}
+
+	up(&aaci->ac97_sem);
+	return v;
+}
+
+static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun)
+{
+	u32 val;
+	int timeout = 5000;
+
+	do {
+		val = readl(aacirun->base + AACI_SR);
+	} while (val & (SR_TXB|SR_RXB) && timeout--);
+}
+
+
+
+/*
+ * Interrupt support.
+ */
+static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
+{
+	if (mask & ISR_URINTR) {
+		writel(ICLR_TXUEC1, aaci->base + AACI_INTCLR);
+	}
+
+	if (mask & ISR_TXINTR) {
+		struct aaci_runtime *aacirun = &aaci->playback;
+		void *ptr;
+
+		if (!aacirun->substream || !aacirun->start) {
+			dev_warn(&aaci->dev->dev, "TX interrupt???");
+			writel(0, aacirun->base + AACI_IE);
+			return;
+		}
+
+		ptr = aacirun->ptr;
+		do {
+			unsigned int len = aacirun->fifosz;
+			u32 val;
+
+			if (aacirun->bytes <= 0) {
+				aacirun->bytes += aacirun->period;
+				aacirun->ptr = ptr;
+				spin_unlock(&aaci->lock);
+				snd_pcm_period_elapsed(aacirun->substream);
+				spin_lock(&aaci->lock);
+			}
+			if (!(aacirun->cr & TXCR_TXEN))
+				break;
+
+			val = readl(aacirun->base + AACI_SR);
+			if (!(val & SR_TXHE))
+				break;
+			if (!(val & SR_TXFE))
+				len >>= 1;
+
+			aacirun->bytes -= len;
+
+			/* writing 16 bytes at a time */
+			for ( ; len > 0; len -= 16) {
+				asm(
+					"ldmia	%0!, {r0, r1, r2, r3}\n\t"
+					"stmia	%1, {r0, r1, r2, r3}"
+					: "+r" (ptr)
+					: "r" (aacirun->fifo)
+					: "r0", "r1", "r2", "r3", "cc");
+
+				if (ptr >= aacirun->end)
+					ptr = aacirun->start;
+			}
+		} while (1);
+
+		aacirun->ptr = ptr;
+	}
+}
+
+static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs)
+{
+	struct aaci *aaci = devid;
+	u32 mask;
+	int i;
+
+	spin_lock(&aaci->lock);
+	mask = readl(aaci->base + AACI_ALLINTS);
+	if (mask) {
+		u32 m = mask;
+		for (i = 0; i < 4; i++, m >>= 7) {
+			if (m & 0x7f) {
+				aaci_fifo_irq(aaci, m);
+			}
+		}
+	}
+	spin_unlock(&aaci->lock);
+
+	return mask ? IRQ_HANDLED : IRQ_NONE;
+}
+
+
+
+/*
+ * ALSA support.
+ */
+
+struct aaci_stream {
+	unsigned char codec_idx;
+	unsigned char rate_idx;
+};
+
+static struct aaci_stream aaci_streams[] = {
+	[ACSTREAM_FRONT] = {
+		.codec_idx	= 0,
+		.rate_idx	= AC97_RATES_FRONT_DAC,
+	},
+	[ACSTREAM_SURROUND] = {
+		.codec_idx	= 0,
+		.rate_idx	= AC97_RATES_SURR_DAC,
+	},
+	[ACSTREAM_LFE] = {
+		.codec_idx	= 0,
+		.rate_idx	= AC97_RATES_LFE_DAC,
+	},
+};
+
+static inline unsigned int aaci_rate_mask(struct aaci *aaci, int streamid)
+{
+	struct aaci_stream *s = aaci_streams + streamid;
+	return aaci->ac97_bus->codec[s->codec_idx]->rates[s->rate_idx];
+}
+
+static unsigned int rate_list[] = {
+	5512, 8000, 11025, 16000, 22050, 32000, 44100,
+	48000, 64000, 88200, 96000, 176400, 192000
+};
+
+/*
+ * Double-rate rule: we can support double rate iff channels == 2
+ *  (unimplemented)
+ */
+static int
+aaci_rule_rate_by_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+{
+	struct aaci *aaci = rule->private;
+	unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512;
+	snd_interval_t *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	switch (c->max) {
+	case 6:
+		rate_mask &= aaci_rate_mask(aaci, ACSTREAM_LFE);
+	case 4:
+		rate_mask &= aaci_rate_mask(aaci, ACSTREAM_SURROUND);
+	case 2:
+		rate_mask &= aaci_rate_mask(aaci, ACSTREAM_FRONT);
+	}
+
+	return snd_interval_list(hw_param_interval(p, rule->var),
+				 ARRAY_SIZE(rate_list), rate_list,
+				 rate_mask);
+}
+
+static snd_pcm_hardware_t aaci_hw_info = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				  SNDRV_PCM_INFO_RESUME,
+
+	/*
+	 * ALSA doesn't support 18-bit or 20-bit packed into 32-bit
+	 * words.  It also doesn't support 12-bit at all.
+	 */
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+
+	/* should this be continuous or knot? */
+	.rates			= SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_max		= 48000,
+	.rate_min		= 4000,
+	.channels_min		= 2,
+	.channels_max		= 6,
+	.buffer_bytes_max	= 64 * 1024,
+	.period_bytes_min	= 256,
+	.period_bytes_max	= PAGE_SIZE,
+	.periods_min		= 4,
+	.periods_max		= PAGE_SIZE / 16,
+};
+
+static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream,
+			 struct aaci_runtime *aacirun)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret;
+
+	aacirun->substream = substream;
+	runtime->private_data = aacirun;
+	runtime->hw = aaci_hw_info;
+
+	/*
+	 * FIXME: ALSA specifies fifo_size in bytes.  If we're in normal
+	 * mode, each 32-bit word contains one sample.  If we're in
+	 * compact mode, each 32-bit word contains two samples, effectively
+	 * halving the FIFO size.  However, we don't know for sure which
+	 * we'll be using at this point.  We set this to the lower limit.
+	 */
+	runtime->hw.fifo_size = aaci->fifosize * 2;
+
+	/*
+	 * Add rule describing hardware rate dependency
+	 * on the number of channels.
+	 */
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  aaci_rule_rate_by_channels, aaci,
+				  SNDRV_PCM_HW_PARAM_CHANNELS,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (ret)
+		goto out;
+
+	ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT,
+			  DRIVER_NAME, aaci);
+	if (ret)
+		goto out;
+
+	return 0;
+
+ out:
+	return ret;
+}
+
+
+/*
+ * Common ALSA stuff
+ */
+static int aaci_pcm_close(snd_pcm_substream_t *substream)
+{
+	struct aaci *aaci = substream->private_data;
+	struct aaci_runtime *aacirun = substream->runtime->private_data;
+
+	WARN_ON(aacirun->cr & TXCR_TXEN);
+
+	aacirun->substream = NULL;
+	free_irq(aaci->dev->irq[0], aaci);
+
+	return 0;
+}
+
+static int aaci_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+	struct aaci_runtime *aacirun = substream->runtime->private_data;
+
+	/*
+	 * This must not be called with the device enabled.
+	 */
+	WARN_ON(aacirun->cr & TXCR_TXEN);
+
+	if (aacirun->pcm_open)
+		snd_ac97_pcm_close(aacirun->pcm);
+	aacirun->pcm_open = 0;
+
+	/*
+	 * Clear out the DMA and any allocated buffers.
+	 */
+	devdma_hw_free(NULL, substream);
+
+	return 0;
+}
+
+static int aaci_pcm_hw_params(snd_pcm_substream_t *substream,
+			      struct aaci_runtime *aacirun,
+			      snd_pcm_hw_params_t *params)
+{
+	int err;
+
+	aaci_pcm_hw_free(substream);
+
+	err = devdma_hw_alloc(NULL, substream,
+			      params_buffer_bytes(params));
+	if (err < 0)
+		goto out;
+
+	err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+				params_channels(params),
+				aacirun->pcm->r[0].slots);
+	if (err)
+		goto out;
+
+	aacirun->pcm_open = 1;
+
+ out:
+	return err;
+}
+
+static int aaci_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct aaci_runtime *aacirun = runtime->private_data;
+
+	aacirun->start	= (void *)runtime->dma_area;
+	aacirun->end	= aacirun->start + runtime->dma_bytes;
+	aacirun->ptr	= aacirun->start;
+	aacirun->period	=
+	aacirun->bytes	= frames_to_bytes(runtime, runtime->period_size);
+
+	return 0;
+}
+
+static snd_pcm_uframes_t aaci_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct aaci_runtime *aacirun = runtime->private_data;
+	ssize_t bytes = aacirun->ptr - aacirun->start;
+
+	return bytes_to_frames(runtime, bytes);
+}
+
+static int aaci_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+{
+	return devdma_mmap(NULL, substream, vma);
+}
+
+
+/*
+ * Playback specific ALSA stuff
+ */
+static const u32 channels_to_txmask[] = {
+	[2] = TXCR_TX3 | TXCR_TX4,
+	[4] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8,
+	[6] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8 | TXCR_TX6 | TXCR_TX9,
+};
+
+/*
+ * We can support two and four channel audio.  Unfortunately
+ * six channel audio requires a non-standard channel ordering:
+ *   2 -> FL(3), FR(4)
+ *   4 -> FL(3), FR(4), SL(7), SR(8)
+ *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
+ *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
+ * This requires an ALSA configuration file to correct.
+ */
+static unsigned int channel_list[] = { 2, 4, 6 };
+
+static int
+aaci_rule_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+{
+	struct aaci *aaci = rule->private;
+	unsigned int chan_mask = 1 << 0, slots;
+
+	/*
+	 * pcms[0] is the our 5.1 PCM instance.
+	 */
+	slots = aaci->ac97_bus->pcms[0].r[0].slots;
+	if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+		chan_mask |= 1 << 1;
+		if (slots & (1 << AC97_SLOT_LFE))
+			chan_mask |= 1 << 2;
+	}
+
+	return snd_interval_list(hw_param_interval(p, rule->var),
+				 ARRAY_SIZE(channel_list), channel_list,
+				 chan_mask);
+}
+
+static int aaci_pcm_playback_open(snd_pcm_substream_t *substream)
+{
+	struct aaci *aaci = substream->private_data;
+	int ret;
+
+	/*
+	 * Add rule describing channel dependency.
+	 */
+	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+				  SNDRV_PCM_HW_PARAM_CHANNELS,
+				  aaci_rule_channels, aaci,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (ret)
+		return ret;
+
+	return aaci_pcm_open(aaci, substream, &aaci->playback);
+}
+
+static int aaci_pcm_playback_hw_params(snd_pcm_substream_t *substream,
+				       snd_pcm_hw_params_t *params)
+{
+	struct aaci *aaci = substream->private_data;
+	struct aaci_runtime *aacirun = substream->runtime->private_data;
+	unsigned int channels = params_channels(params);
+	int ret;
+
+	WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) ||
+		!channels_to_txmask[channels]);
+
+	ret = aaci_pcm_hw_params(substream, aacirun, params);
+
+	/*
+	 * Enable FIFO, compact mode, 16 bits per sample.
+	 * FIXME: double rate slots?
+	 */
+	if (ret >= 0) {
+		aacirun->cr = TXCR_FEN | TXCR_COMPACT | TXCR_TSZ16;
+		aacirun->cr |= channels_to_txmask[channels];
+
+		aacirun->fifosz	= aaci->fifosize * 4;
+		if (aacirun->cr & TXCR_COMPACT)
+			aacirun->fifosz >>= 1;
+	}
+	return ret;
+}
+
+static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
+{
+	u32 ie;
+
+	ie = readl(aacirun->base + AACI_IE);
+	ie &= ~(IE_URIE|IE_TXIE);
+	writel(ie, aacirun->base + AACI_IE);
+	aacirun->cr &= ~TXCR_TXEN;
+	aaci_chan_wait_ready(aacirun);
+	writel(aacirun->cr, aacirun->base + AACI_TXCR);
+}
+
+static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
+{
+	u32 ie;
+
+	aaci_chan_wait_ready(aacirun);
+	aacirun->cr |= TXCR_TXEN;
+
+	ie = readl(aacirun->base + AACI_IE);
+	ie |= IE_URIE | IE_TXIE;
+	writel(ie, aacirun->base + AACI_IE);
+	writel(aacirun->cr, aacirun->base + AACI_TXCR);
+}
+
+static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	struct aaci *aaci = substream->private_data;
+	struct aaci_runtime *aacirun = substream->runtime->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&aaci->lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		aaci_pcm_playback_start(aacirun);
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+		aaci_pcm_playback_start(aacirun);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		aaci_pcm_playback_stop(aacirun);
+		break;
+
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		aaci_pcm_playback_stop(aacirun);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&aaci->lock, flags);
+
+	return ret;
+}
+
+static snd_pcm_ops_t aaci_playback_ops = {
+	.open		= aaci_pcm_playback_open,
+	.close		= aaci_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= aaci_pcm_playback_hw_params,
+	.hw_free	= aaci_pcm_hw_free,
+	.prepare	= aaci_pcm_prepare,
+	.trigger	= aaci_pcm_playback_trigger,
+	.pointer	= aaci_pcm_pointer,
+	.mmap		= aaci_pcm_mmap,
+};
+
+
+
+/*
+ * Power Management.
+ */
+#ifdef CONFIG_PM
+static int aaci_do_suspend(snd_card_t *card, unsigned int state)
+{
+	struct aaci *aaci = card->private_data;
+	if (aaci->card->power_state != SNDRV_CTL_POWER_D3cold) {
+		snd_pcm_suspend_all(aaci->pcm);
+		snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D3cold);
+	}
+	return 0;
+}
+
+static int aaci_do_resume(snd_card_t *card, unsigned int state)
+{
+	struct aaci *aaci = card->private_data;
+	if (aaci->card->power_state != SNDRV_CTL_POWER_D0) {
+		snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D0);
+	}
+	return 0;
+}
+
+static int aaci_suspend(struct amba_device *dev, u32 state)
+{
+	snd_card_t *card = amba_get_drvdata(dev);
+	return card ? aaci_do_suspend(card) : 0;
+}
+
+static int aaci_resume(struct amba_device *dev)
+{
+	snd_card_t *card = amba_get_drvdata(dev);
+	return card ? aaci_do_resume(card) : 0;
+}
+#else
+#define aaci_do_suspend		NULL
+#define aaci_do_resume		NULL
+#define aaci_suspend		NULL
+#define aaci_resume		NULL
+#endif
+
+
+static struct ac97_pcm ac97_defs[] __devinitdata = {
+	[0] = {		/* Front PCM */
+		.exclusive = 1,
+		.r = {
+			[0] = {
+				.slots	= (1 << AC97_SLOT_PCM_LEFT) |
+					  (1 << AC97_SLOT_PCM_RIGHT) |
+					  (1 << AC97_SLOT_PCM_CENTER) |
+					  (1 << AC97_SLOT_PCM_SLEFT) |
+					  (1 << AC97_SLOT_PCM_SRIGHT) |
+					  (1 << AC97_SLOT_LFE),
+			},
+		},
+	},
+	[1] = {	/* PCM in */
+		.stream = 1,
+		.exclusive = 1,
+		.r = {
+			[0] = {
+				.slots	= (1 << AC97_SLOT_PCM_LEFT) |
+					  (1 << AC97_SLOT_PCM_RIGHT),
+			},
+		},
+	},
+	[2] = {	/* Mic in */
+		.stream = 1,
+		.exclusive = 1,
+		.r = {
+			[0] = {
+				.slots	= (1 << AC97_SLOT_MIC),
+			},
+		},
+	}
+};
+
+static ac97_bus_ops_t aaci_bus_ops = {
+	.write	= aaci_ac97_write,
+	.read	= aaci_ac97_read,
+};
+
+static int __devinit aaci_probe_ac97(struct aaci *aaci)
+{
+	ac97_template_t ac97_template;
+	ac97_bus_t *ac97_bus;
+	ac97_t *ac97;
+	int ret;
+
+	/*
+	 * Assert AACIRESET for 2us
+	 */
+	writel(0, aaci->base + AACI_RESET);
+	udelay(2);
+	writel(RESET_NRST, aaci->base + AACI_RESET);
+
+	/*
+	 * Give the AC'97 codec more than enough time
+	 * to wake up. (42us = ~2 frames at 48kHz.)
+	 */
+	udelay(42);
+
+	ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);
+	if (ret)
+		goto out;
+
+	ac97_bus->clock = 48000;
+	aaci->ac97_bus = ac97_bus;
+
+	memset(&ac97_template, 0, sizeof(ac97_template_t));
+	ac97_template.private_data = aaci;
+	ac97_template.num = 0;
+	ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
+
+	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
+	if (ret)
+		goto out;
+
+	/*
+	 * Disable AC97 PC Beep input on audio codecs.
+	 */
+	if (ac97_is_audio(ac97))
+		snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e);
+
+	ret = snd_ac97_pcm_assign(ac97_bus, ARRAY_SIZE(ac97_defs), ac97_defs);
+	if (ret)
+		goto out;
+
+	aaci->playback.pcm = &ac97_bus->pcms[0];
+
+ out:
+	return ret;
+}
+
+static void aaci_free_card(snd_card_t *card)
+{
+	struct aaci *aaci = card->private_data;
+	if (aaci->base)
+		iounmap(aaci->base);
+}
+
+static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
+{
+	struct aaci *aaci;
+	snd_card_t *card;
+
+	card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			    THIS_MODULE, sizeof(struct aaci));
+	if (card == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	card->private_free = aaci_free_card;
+	snd_card_set_pm_callback(card, aaci_do_suspend, aaci_do_resume, NULL);
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s at 0x%08lx, irq %d",
+		 card->shortname, dev->res.start, dev->irq[0]);
+
+	aaci = card->private_data;
+	init_MUTEX(&aaci->ac97_sem);
+	spin_lock_init(&aaci->lock);
+	aaci->card = card;
+	aaci->dev = dev;
+
+	/* Set MAINCR to allow slot 1 and 2 data IO */
+	aaci->maincr = MAINCR_IE | MAINCR_SL1RXEN | MAINCR_SL1TXEN |
+		       MAINCR_SL2RXEN | MAINCR_SL2TXEN;
+
+	return aaci;
+}
+
+static int __devinit aaci_init_pcm(struct aaci *aaci)
+{
+	snd_pcm_t *pcm;
+	int ret;
+
+	ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 0, &pcm);
+	if (ret == 0) {
+		aaci->pcm = pcm;
+		pcm->private_data = aaci;
+		pcm->info_flags = 0;
+
+		strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
+	}
+
+	return ret;
+}
+
+static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
+{
+	void *base = aaci->base + AACI_CSCH1;
+	int i;
+
+	writel(TXCR_FEN | TXCR_TSZ16 | TXCR_TXEN, base + AACI_TXCR);
+
+	for (i = 0; !(readl(base + AACI_SR) & SR_TXFF) && i < 4096; i++)
+		writel(0, aaci->base + AACI_DR1);
+
+	writel(0, base + AACI_TXCR);
+
+	/*
+	 * Re-initialise the AACI after the FIFO depth test, to
+	 * ensure that the FIFOs are empty.  Unfortunately, merely
+	 * disabling the channel doesn't clear the FIFO.
+	 */
+	writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);
+	writel(aaci->maincr, aaci->base + AACI_MAINCR);
+
+	/*
+	 * If we hit 4096, we failed.  Go back to the specified
+	 * fifo depth.
+	 */
+	if (i == 4096)
+		i = 8;
+
+	return i;
+}
+
+static int __devinit aaci_probe(struct amba_device *dev, void *id)
+{
+	struct aaci *aaci;
+	int ret, i;
+
+	ret = amba_request_regions(dev, NULL);
+	if (ret)
+		return ret;
+
+	aaci = aaci_init_card(dev);
+	if (IS_ERR(aaci)) {
+		ret = PTR_ERR(aaci);
+		goto out;
+	}
+
+	aaci->base = ioremap(dev->res.start, SZ_4K);
+	if (!aaci->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Playback uses AACI channel 0
+	 */
+	aaci->playback.base = aaci->base + AACI_CSCH1;
+	aaci->playback.fifo = aaci->base + AACI_DR1;
+
+	for (i = 0; i < 4; i++) {
+		void *base = aaci->base + i * 0x14;
+
+		writel(0, base + AACI_IE);
+		writel(0, base + AACI_TXCR);
+		writel(0, base + AACI_RXCR);
+	}
+
+	writel(0x1fff, aaci->base + AACI_INTCLR);
+	writel(aaci->maincr, aaci->base + AACI_MAINCR);
+
+	/*
+	 * Size the FIFOs.
+	 */
+	aaci->fifosize = aaci_size_fifo(aaci);
+
+	ret = aaci_probe_ac97(aaci);
+	if (ret)
+		goto out;
+
+	ret = aaci_init_pcm(aaci);
+	if (ret)
+		goto out;
+
+	ret = snd_card_register(aaci->card);
+	if (ret == 0) {
+		dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
+			aaci->fifosize);
+		amba_set_drvdata(dev, aaci->card);
+		return ret;
+	}
+
+ out:
+	if (aaci)
+		snd_card_free(aaci->card);
+	amba_release_regions(dev);
+	return ret;
+}
+
+static int __devexit aaci_remove(struct amba_device *dev)
+{
+	snd_card_t *card = amba_get_drvdata(dev);
+
+	amba_set_drvdata(dev, NULL);
+
+	if (card) {
+		struct aaci *aaci = card->private_data;
+		writel(0, aaci->base + AACI_MAINCR);
+
+		snd_card_free(card);
+		amba_release_regions(dev);
+	}
+
+	return 0;
+}
+
+static struct amba_id aaci_ids[] = {
+	{
+		.id	= 0x00041041,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 },
+};
+
+static struct amba_driver aaci_driver = {
+	.drv		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= aaci_probe,
+	.remove		= __devexit_p(aaci_remove),
+	.suspend	= aaci_suspend,
+	.resume		= aaci_resume,
+	.id_table	= aaci_ids,
+};
+
+static int __init aaci_init(void)
+{
+	return amba_driver_register(&aaci_driver);
+}
+
+static void __exit aaci_exit(void)
+{
+	amba_driver_unregister(&aaci_driver);
+}
+
+module_init(aaci_init);
+module_exit(aaci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
new file mode 100644
index 0000000..d752e64
--- /dev/null
+++ b/sound/arm/aaci.h
@@ -0,0 +1,246 @@
+/*
+ *  linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef AACI_H
+#define AACI_H
+
+/*
+ * Control and status register offsets
+ *  P39.
+ */
+#define AACI_CSCH1	0x000
+#define AACI_CSCH2	0x014
+#define AACI_CSCH3	0x028
+#define AACI_CSCH4	0x03c
+
+#define AACI_RXCR	0x000	/* 29 bits Control Rx FIFO */
+#define AACI_TXCR	0x004	/* 17 bits Control Tx FIFO */
+#define AACI_SR		0x008	/* 12 bits Status */
+#define AACI_ISR	0x00c	/* 7 bits  Int Status */
+#define AACI_IE 	0x010	/* 7 bits  Int Enable */
+
+/*
+ * Other registers
+ */
+#define AACI_SL1RX	0x050
+#define AACI_SL1TX	0x054
+#define AACI_SL2RX	0x058
+#define AACI_SL2TX	0x05c
+#define AACI_SL12RX	0x060
+#define AACI_SL12TX	0x064
+#define AACI_SLFR	0x068	/* slot flags */
+#define AACI_SLISTAT	0x06c	/* slot interrupt status */
+#define AACI_SLIEN	0x070	/* slot interrupt enable */
+#define AACI_INTCLR	0x074	/* interrupt clear */
+#define AACI_MAINCR	0x078	/* main control */
+#define AACI_RESET	0x07c	/* reset control */
+#define AACI_SYNC	0x080	/* sync control */
+#define AACI_ALLINTS	0x084	/* all fifo interrupt status */
+#define AACI_MAINFR	0x088	/* main flag register */
+#define AACI_DR1	0x090	/* data read/written fifo 1 */
+#define AACI_DR2	0x0b0	/* data read/written fifo 2 */
+#define AACI_DR3	0x0d0	/* data read/written fifo 3 */
+#define AACI_DR4	0x0f0	/* data read/written fifo 4 */
+
+/*
+ * transmit fifo control register. P48
+ */
+#define TXCR_FEN	(1 << 16)	/* fifo enable */
+#define TXCR_COMPACT	(1 << 15)	/* compact mode */
+#define TXCR_TSZ16	(0 << 13)	/* 16 bits */
+#define TXCR_TSZ18	(1 << 13)	/* 18 bits */
+#define TXCR_TSZ20	(2 << 13)	/* 20 bits */
+#define TXCR_TSZ12	(3 << 13)	/* 12 bits */
+#define TXCR_TX12	(1 << 12)	/* transmits slot 12 */
+#define TXCR_TX11	(1 << 11)	/* transmits slot 12 */
+#define TXCR_TX10	(1 << 10)	/* transmits slot 12 */
+#define TXCR_TX9	(1 << 9)	/* transmits slot 12 */
+#define TXCR_TX8	(1 << 8)	/* transmits slot 12 */
+#define TXCR_TX7	(1 << 7)	/* transmits slot 12 */
+#define TXCR_TX6	(1 << 6)	/* transmits slot 12 */
+#define TXCR_TX5	(1 << 5)	/* transmits slot 12 */
+#define TXCR_TX4	(1 << 4)	/* transmits slot 12 */
+#define TXCR_TX3	(1 << 3)	/* transmits slot 12 */
+#define TXCR_TX2	(1 << 2)	/* transmits slot 12 */
+#define TXCR_TX1	(1 << 1)	/* transmits slot 12 */
+#define TXCR_TXEN	(1 << 0)	/* transmit enable */
+
+/*
+ * status register bits. P49
+ */
+#define SR_RXTOFE	(1 << 11)	/* rx timeout fifo empty */
+#define SR_TXTO		(1 << 10)	/* rx timeout fifo nonempty */
+#define SR_TXU		(1 << 9)	/* tx underrun */
+#define SR_RXO		(1 << 8)	/* rx overrun */
+#define SR_TXB		(1 << 7)	/* tx busy */
+#define SR_RXB		(1 << 6)	/* rx busy */
+#define SR_TXFF		(1 << 5)	/* tx fifo full */
+#define SR_RXFF		(1 << 4)	/* rx fifo full */
+#define SR_TXHE		(1 << 3)	/* tx fifo half empty */
+#define SR_RXHF		(1 << 2)	/* rx fifo half full */
+#define SR_TXFE		(1 << 1)	/* tx fifo empty */
+#define SR_RXFE		(1 << 0)	/* rx fifo empty */
+
+/*
+ * interrupt status register bits.
+ */
+#define ISR_RXTOFEINTR	(1 << 6)	/* rx fifo empty */
+#define ISR_URINTR	(1 << 5)	/* tx underflow */
+#define ISR_ORINTR	(1 << 4)	/* rx overflow */
+#define ISR_RXINTR	(1 << 3)	/* rx fifo */
+#define ISR_TXINTR	(1 << 2)	/* tx fifo intr */
+#define ISR_RXTOINTR	(1 << 1)	/* tx timeout */
+#define ISR_TXCINTR	(1 << 0)	/* tx complete */
+
+/*
+ * interrupt enable register bits.
+ */
+#define IE_RXTOIE	(1 << 6)
+#define IE_URIE		(1 << 5)
+#define IE_ORIE		(1 << 4)
+#define IE_RXIE		(1 << 3)
+#define IE_TXIE		(1 << 2)
+#define IE_RXTIE	(1 << 1)
+#define IE_TXCIE	(1 << 0)
+
+/*
+ * interrupt status. P51
+ */
+#define ISR_RXTOFE	(1 << 6)	/* rx timeout fifo empty */
+#define ISR_UR		(1 << 5)	/* tx fifo underrun */
+#define ISR_OR		(1 << 4)	/* rx fifo overrun */
+#define ISR_RX		(1 << 3)	/* rx interrupt status */
+#define ISR_TX		(1 << 2)	/* tx interrupt status */
+#define ISR_RXTO	(1 << 1)	/* rx timeout */
+#define ISR_TXC		(1 << 0)	/* tx complete */
+
+/*
+ * interrupt enable. P52
+ */
+#define IE_RXTOFE	(1 << 6)	/* rx timeout fifo empty */
+#define IE_UR		(1 << 5)	/* tx fifo underrun */
+#define IE_OR		(1 << 4)	/* rx fifo overrun */
+#define IE_RX		(1 << 3)	/* rx interrupt status */
+#define IE_TX		(1 << 2)	/* tx interrupt status */
+#define IE_RXTO		(1 << 1)	/* rx timeout */
+#define IE_TXC		(1 << 0)	/* tx complete */
+
+/*
+ * slot flag register bits. P56
+ */
+#define SLFR_RWIS	(1 << 13)	/* raw wake-up interrupt status */
+#define SLFR_RGPIOINTR	(1 << 12)	/* raw gpio interrupt */
+#define SLFR_12TXE	(1 << 11)	/* slot 12 tx empty */
+#define SLFR_12RXV	(1 << 10)	/* slot 12 rx valid */
+#define SLFR_2TXE	(1 << 9)	/* slot 2 tx empty */
+#define SLFR_2RXV	(1 << 8)	/* slot 2 rx valid */
+#define SLFR_1TXE	(1 << 7)	/* slot 1 tx empty */
+#define SLFR_1RXV	(1 << 6)	/* slot 1 rx valid */
+#define SLFR_12TXB	(1 << 5)	/* slot 12 tx busy */
+#define SLFR_12RXB	(1 << 4)	/* slot 12 rx busy */
+#define SLFR_2TXB	(1 << 3)	/* slot 2 tx busy */
+#define SLFR_2RXB	(1 << 2)	/* slot 2 rx busy */
+#define SLFR_1TXB	(1 << 1)	/* slot 1 tx busy */
+#define SLFR_1RXB	(1 << 0)	/* slot 1 rx busy */
+
+/*
+ * Interrupt clear register.
+ */
+#define ICLR_RXTOFEC4	(1 << 12)
+#define ICLR_RXTOFEC3	(1 << 11)
+#define ICLR_RXTOFEC2	(1 << 10)
+#define ICLR_RXTOFEC1	(1 << 9)
+#define ICLR_TXUEC4	(1 << 8)
+#define ICLR_TXUEC3	(1 << 7)
+#define ICLR_TXUEC2	(1 << 6)
+#define ICLR_TXUEC1	(1 << 5)
+#define ICLR_RXOEC4	(1 << 4)
+#define ICLR_RXOEC3	(1 << 3)
+#define ICLR_RXOEC2	(1 << 2)
+#define ICLR_RXOEC1	(1 << 1)
+#define ICLR_WISC	(1 << 0)
+
+/*
+ * Main control register bits. P62
+ */
+#define MAINCR_SCRA(x)	((x) << 10)	/* secondary codec reg access */
+#define MAINCR_DMAEN	(1 << 9)	/* dma enable */
+#define MAINCR_SL12TXEN	(1 << 8)	/* slot 12 transmit enable */
+#define MAINCR_SL12RXEN	(1 << 7)	/* slot 12 receive enable */
+#define MAINCR_SL2TXEN	(1 << 6)	/* slot 2 transmit enable */
+#define MAINCR_SL2RXEN	(1 << 5)	/* slot 2 receive enable */
+#define MAINCR_SL1TXEN	(1 << 4)	/* slot 1 transmit enable */
+#define MAINCR_SL1RXEN	(1 << 3)	/* slot 1 receive enable */
+#define MAINCR_LPM	(1 << 2)	/* low power mode */
+#define MAINCR_LOOPBK	(1 << 1)	/* loopback */
+#define MAINCR_IE	(1 << 0)	/* aaci interface enable */
+
+/*
+ * Reset register bits. P65
+ */
+#define RESET_NRST	(1 << 0)
+
+/*
+ * Sync register bits. P65
+ */
+#define SYNC_FORCE	(1 << 0)
+
+/*
+ * Main flag register bits. P66
+ */
+#define MAINFR_TXB	(1 << 1)	/* transmit busy */
+#define MAINFR_RXB	(1 << 0)	/* receive busy */
+
+
+
+struct aaci_runtime {
+	void			*base;
+	void			*fifo;
+
+	struct ac97_pcm		*pcm;
+	int			pcm_open;
+
+	u32			cr;
+	snd_pcm_substream_t	*substream;
+
+	/*
+	 * PIO support
+	 */
+	void			*start;
+	void			*end;
+	void			*ptr;
+	int			bytes;
+	unsigned int		period;
+	unsigned int		fifosz;
+};
+
+struct aaci {
+	struct amba_device	*dev;
+	snd_card_t		*card;
+	void			*base;
+	unsigned int		fifosize;
+
+	/* AC'97 */
+	struct semaphore	ac97_sem;
+	ac97_bus_t		*ac97_bus;
+
+	u32			maincr;
+	spinlock_t		lock;
+
+	struct aaci_runtime	playback;
+	struct aaci_runtime	capture;
+
+	snd_pcm_t		*pcm;
+};
+
+#define ACSTREAM_FRONT		0
+#define ACSTREAM_SURROUND	1
+#define ACSTREAM_LFE		2
+
+#endif
diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c
new file mode 100644
index 0000000..60826a5
--- /dev/null
+++ b/sound/arm/devdma.c
@@ -0,0 +1,81 @@
+/*
+ *  linux/sound/arm/devdma.c
+ *
+ *  Copyright (C) 2003-2004 Russell King, All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  ARM DMA shim for ALSA.
+ */
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "devdma.h"
+
+void devdma_hw_free(struct device *dev, snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+
+	if (runtime->dma_area == NULL)
+		return;
+
+	if (buf != &substream->dma_buffer) {
+		dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
+		kfree(runtime->dma_buffer_p);
+	}
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+}
+
+int devdma_hw_alloc(struct device *dev, snd_pcm_substream_t *substream, size_t size)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+	int ret = 0;
+
+	if (buf) {
+		if (buf->bytes >= size)
+			goto out;
+		devdma_hw_free(dev, substream);
+	}
+
+	if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
+		buf = &substream->dma_buffer;
+	} else {
+		buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
+		if (!buf)
+			goto nomem;
+
+		buf->dev.type = SNDRV_DMA_TYPE_DEV;
+		buf->dev.dev = dev;
+		buf->area = dma_alloc_coherent(dev, size, &buf->addr, GFP_KERNEL);
+		buf->bytes = size;
+		buf->private_data = NULL;
+
+		if (!buf->area)
+			goto free;
+	}
+	snd_pcm_set_runtime_buffer(substream, buf);
+	ret = 1;
+ out:
+	runtime->dma_bytes = size;
+	return ret;
+
+ free:
+	kfree(buf);
+ nomem:
+	return -ENOMEM;
+}
+
+int devdma_mmap(struct device *dev, snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+}
diff --git a/sound/arm/devdma.h b/sound/arm/devdma.h
new file mode 100644
index 0000000..5a33b6b
--- /dev/null
+++ b/sound/arm/devdma.h
@@ -0,0 +1,3 @@
+void devdma_hw_free(struct device *dev, snd_pcm_substream_t *substream);
+int devdma_hw_alloc(struct device *dev, snd_pcm_substream_t *substream, size_t size);
+int devdma_mmap(struct device *dev, snd_pcm_substream_t *substream, struct vm_area_struct *vma);
diff --git a/sound/core/control.c b/sound/core/control.c
index f4ea6bf..4e39a21 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1102,7 +1102,7 @@
 		}
 	}
 	up_read(&snd_ioctl_rwsem);
-	snd_printd("unknown ioctl = 0x%x\n", cmd);
+	snd_printdd("unknown ioctl = 0x%x\n", cmd);
 	return -ENOTTY;
 }
 
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 1a80502..468fca8 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -125,17 +125,26 @@
 static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
-	frames = frames_to_bytes(runtime, frames);
+	long buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	long bytes = frames_to_bytes(runtime, frames);
 	if (buffer_size == runtime->oss.buffer_bytes)
-		return frames;
-	return (runtime->oss.buffer_bytes * frames) / buffer_size;
+		return bytes;
+#if BITS_PER_LONG >= 64
+	return runtime->oss.buffer_bytes * bytes / buffer_size;
+#else
+	{
+		u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
+		u32 rem;
+		div64_32(&bsize, buffer_size, &rem);
+		return (long)bsize;
+	}
+#endif
 }
 
 static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	long buffer_size = snd_pcm_lib_buffer_bytes(substream);
 	if (buffer_size == runtime->oss.buffer_bytes)
 		return bytes_to_frames(runtime, bytes);
 	return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
@@ -464,7 +473,8 @@
 	sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
 	sw_params->period_step = 1;
 	sw_params->sleep_min = 0;
-	sw_params->avail_min = 1;
+	sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		1 : runtime->period_size;
 	sw_params->xfer_align = 1;
 	if (atomic_read(&runtime->mmap_count) ||
 	    (substream->oss.setup && substream->oss.setup->nosilence)) {
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 6bb3100..6430410 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -663,10 +663,7 @@
 		bitset_t *dstmask = bs;
 		int err;
 		bitset_one(dstmask, schannels);
-		if (plugin == NULL) {
-			bitset_and(client_vmask, dstmask, schannels);
-			return 0;
-		}
+
 		while (1) {
 			err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
 			if (err < 0)
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 8d94325..9f4c920 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -451,6 +451,7 @@
 		entry->c.text.read = snd_pcm_xrun_debug_read;
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_pcm_xrun_debug_write;
+		entry->mode |= S_IWUSR;
 		entry->private_data = pstr;
 		if (snd_info_register(entry) < 0) {
 			snd_info_free_entry(entry);
@@ -1048,7 +1049,6 @@
 EXPORT_SYMBOL(snd_pcm_format_name);
   /* pcm_native.c */
 EXPORT_SYMBOL(snd_pcm_link_rwlock);
-EXPORT_SYMBOL(snd_pcm_start);
 #ifdef CONFIG_PM
 EXPORT_SYMBOL(snd_pcm_suspend);
 EXPORT_SYMBOL(snd_pcm_suspend_all);
@@ -1068,6 +1068,7 @@
 EXPORT_SYMBOL(snd_pcm_format_big_endian);
 EXPORT_SYMBOL(snd_pcm_format_width);
 EXPORT_SYMBOL(snd_pcm_format_physical_width);
+EXPORT_SYMBOL(snd_pcm_format_size);
 EXPORT_SYMBOL(snd_pcm_format_silence_64);
 EXPORT_SYMBOL(snd_pcm_format_set_silence);
 EXPORT_SYMBOL(snd_pcm_build_linear_format);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 151fd99..c5bfd09 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1143,7 +1143,8 @@
 #define INT_MIN ((int)((unsigned int)INT_MAX+1))
 #endif
 
-void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
+static void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params,
+				  snd_pcm_hw_param_t var)
 {
 	if (hw_is_mask(var)) {
 		snd_mask_any(hw_param_mask(params, var));
@@ -1160,6 +1161,7 @@
 	snd_BUG();
 }
 
+#if 0
 /**
  * snd_pcm_hw_param_any
  */
@@ -1169,6 +1171,7 @@
 	_snd_pcm_hw_param_any(params, var);
 	return snd_pcm_hw_refine(pcm, params);
 }
+#endif  /*  0  */
 
 void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
 {
@@ -1181,6 +1184,7 @@
 	params->info = ~0U;
 }
 
+#if 0
 /**
  * snd_pcm_hw_params_any
  *
@@ -1191,6 +1195,7 @@
 	_snd_pcm_hw_params_any(params);
 	return snd_pcm_hw_refine(pcm, params);
 }
+#endif  /*  0  */
 
 /**
  * snd_pcm_hw_param_value
@@ -1198,8 +1203,8 @@
  * Return the value for field PAR if it's fixed in configuration space 
  *  defined by PARAMS. Return -EINVAL otherwise
  */
-int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params,
-			   snd_pcm_hw_param_t var, int *dir)
+static int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params,
+				  snd_pcm_hw_param_t var, int *dir)
 {
 	if (hw_is_mask(var)) {
 		const snd_mask_t *mask = hw_param_mask_c(params, var);
@@ -1296,6 +1301,7 @@
 	return changed;
 }
 	
+#if 0
 /**
  * snd_pcm_hw_param_setinteger
  *
@@ -1317,9 +1323,10 @@
 	}
 	return 0;
 }
+#endif  /*  0  */
 
-int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
-			    snd_pcm_hw_param_t var)
+static int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
+				   snd_pcm_hw_param_t var)
 {
 	int changed;
 	if (hw_is_mask(var))
@@ -1345,9 +1352,9 @@
  * values > minimum. Reduce configuration space accordingly.
  * Return the minimum.
  */
-int snd_pcm_hw_param_first(snd_pcm_t *pcm, 
-			   snd_pcm_hw_params_t *params, 
-			   snd_pcm_hw_param_t var, int *dir)
+static int snd_pcm_hw_param_first(snd_pcm_t *pcm, 
+				  snd_pcm_hw_params_t *params, 
+				  snd_pcm_hw_param_t var, int *dir)
 {
 	int changed = _snd_pcm_hw_param_first(params, var);
 	if (changed < 0)
@@ -1359,8 +1366,8 @@
 	return snd_pcm_hw_param_value(params, var, dir);
 }
 
-int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
-			   snd_pcm_hw_param_t var)
+static int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
+				  snd_pcm_hw_param_t var)
 {
 	int changed;
 	if (hw_is_mask(var))
@@ -1386,9 +1393,9 @@
  * values < maximum. Reduce configuration space accordingly.
  * Return the maximum.
  */
-int snd_pcm_hw_param_last(snd_pcm_t *pcm, 
-			  snd_pcm_hw_params_t *params,
-			  snd_pcm_hw_param_t var, int *dir)
+static int snd_pcm_hw_param_last(snd_pcm_t *pcm, 
+				 snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, int *dir)
 {
 	int changed = _snd_pcm_hw_param_last(params, var);
 	if (changed < 0)
@@ -1437,8 +1444,9 @@
  * values < VAL. Reduce configuration space accordingly.
  * Return new minimum or -EINVAL if the configuration space is empty
  */
-int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
-			 snd_pcm_hw_param_t var, unsigned int val, int *dir)
+static int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+				snd_pcm_hw_param_t var, unsigned int val,
+				int *dir)
 {
 	int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
 	if (changed < 0)
@@ -1451,8 +1459,9 @@
 	return snd_pcm_hw_param_value_min(params, var, dir);
 }
 
-int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
-			   snd_pcm_hw_param_t var, unsigned int val, int dir)
+static int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
+				 snd_pcm_hw_param_t var, unsigned int val,
+				 int dir)
 {
 	int changed;
 	int open = 0;
@@ -1490,8 +1499,9 @@
  *  values >= VAL + 1. Reduce configuration space accordingly.
  *  Return new maximum or -EINVAL if the configuration space is empty
  */
-int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
-			  snd_pcm_hw_param_t var, unsigned int val, int *dir)
+static int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+				snd_pcm_hw_param_t var, unsigned int val,
+				int *dir)
 {
 	int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
 	if (changed < 0)
@@ -2564,9 +2574,6 @@
 EXPORT_SYMBOL(snd_interval_refine);
 EXPORT_SYMBOL(snd_interval_list);
 EXPORT_SYMBOL(snd_interval_ratnum);
-EXPORT_SYMBOL(snd_interval_muldivk);
-EXPORT_SYMBOL(snd_interval_mulkdiv);
-EXPORT_SYMBOL(snd_interval_div);
 EXPORT_SYMBOL(_snd_pcm_hw_params_any);
 EXPORT_SYMBOL(_snd_pcm_hw_param_min);
 EXPORT_SYMBOL(_snd_pcm_hw_param_set);
@@ -2580,7 +2587,6 @@
 EXPORT_SYMBOL(snd_pcm_hw_param_near);
 EXPORT_SYMBOL(snd_pcm_hw_param_set);
 EXPORT_SYMBOL(snd_pcm_hw_refine);
-EXPORT_SYMBOL(snd_pcm_hw_params);
 EXPORT_SYMBOL(snd_pcm_hw_constraints_init);
 EXPORT_SYMBOL(snd_pcm_hw_constraints_complete);
 EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index f1d5f7a..9a174fb 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -204,6 +204,7 @@
 		entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
+		entry->mode |= S_IWUSR;
 		entry->private_data = substream;
 		if (snd_info_register(entry) < 0) {
 			snd_info_free_entry(entry);
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index 422b8db..1453743 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -270,22 +270,6 @@
 }
 
 /**
- * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
- * @format: the format to check
- *
- * Returns 1 if the given PCM format is CPU-endian, 0 if
- * opposite, or a negative error code if endian not specified.
- */
-int snd_pcm_format_cpu_endian(snd_pcm_format_t format)
-{
-#ifdef SNDRV_LITTLE_ENDIAN
-	return snd_pcm_format_little_endian(format);
-#else
-	return snd_pcm_format_big_endian(format);
-#endif
-}
-
-/**
  * snd_pcm_format_width - return the bit-width of the format
  * @format: the format to check
  *
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index cad9bbd..4e58241 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -337,8 +337,8 @@
 	return err;
 }
 
-int snd_pcm_hw_params(snd_pcm_substream_t *substream,
-		      snd_pcm_hw_params_t *params)
+static int snd_pcm_hw_params(snd_pcm_substream_t *substream,
+			     snd_pcm_hw_params_t *params)
 {
 	snd_pcm_runtime_t *runtime;
 	int err;
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 18247db..57be915 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -414,6 +414,8 @@
 	if (newclient)
 		synths[card->number] = client;
 	up(&register_mutex);
+	kfree(info);
+	kfree(port);
 	return 0;	/* success */
 
       __nomem:
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index 21e5690..df1e2bb 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -171,11 +171,13 @@
 	spin_unlock_irqrestore(&dev->lock, flags);
 }
 
+#if 0
 void snd_midi_event_init(snd_midi_event_t *dev)
 {
 	snd_midi_event_reset_encode(dev);
 	snd_midi_event_reset_decode(dev);
 }
+#endif  /*  0  */
 
 void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
 {
@@ -185,6 +187,7 @@
 /*
  * resize buffer
  */
+#if 0
 int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
 {
 	unsigned char *new_buf, *old_buf;
@@ -204,6 +207,7 @@
 	kfree(old_buf);
 	return 0;
 }
+#endif  /*  0  */
 
 /*
  *  read bytes and encode to sequencer event if finished
@@ -517,8 +521,6 @@
  
 EXPORT_SYMBOL(snd_midi_event_new);
 EXPORT_SYMBOL(snd_midi_event_free);
-EXPORT_SYMBOL(snd_midi_event_resize_buffer);
-EXPORT_SYMBOL(snd_midi_event_init);
 EXPORT_SYMBOL(snd_midi_event_reset_encode);
 EXPORT_SYMBOL(snd_midi_event_reset_decode);
 EXPORT_SYMBOL(snd_midi_event_no_status);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 3afc7cc0..98de2e7 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -672,7 +672,8 @@
  * process a received queue-control event.
  * this function is exported for seq_sync.c.
  */
-void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
+static void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev,
+					int atomic, int hop)
 {
 	switch (ev->type) {
 	case SNDRV_SEQ_EVENT_START:
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
index b1bf551..ea3c542 100644
--- a/sound/core/seq/seq_queue.h
+++ b/sound/core/seq/seq_queue.h
@@ -111,7 +111,6 @@
 int snd_seq_queue_is_used(int queueid, int client);
 
 int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop);
-void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop);
 
 /*
  * 64bit division - for sync stuff..
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 753f1c0..a7f76fc 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -36,7 +36,8 @@
 
 #define SKEW_BASE	0x10000	/* 16bit shift */
 
-void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks)
+static void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick,
+					      int tempo, int ppq, int nticks)
 {
 	if (tempo < 1000000)
 		tick->resolution = (tempo * 1000) / ppq;
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 4c0872d..287ed68 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -64,8 +64,6 @@
 /* delete timer (destructor) */
 extern void snd_seq_timer_delete(seq_timer_t **tmr);
 
-void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks);
-
 /* */
 static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution)
 {
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 6b4e630..a66484b5 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -110,7 +110,7 @@
  * handler of a remote port which is attached to the virmidi via
  * SNDRV_VIRMIDI_SEQ_ATTACH.
  */
-/* exported */
+#if 0
 int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev)
 {
 	snd_virmidi_dev_t *rdev;
@@ -118,6 +118,7 @@
 	rdev = rmidi->private_data;
 	return snd_virmidi_dev_receive_event(rdev, ev);
 }
+#endif  /*  0  */
 
 /*
  * event handler of virmidi port
@@ -384,7 +385,7 @@
 	info->client = client;
 	info->type = KERNEL_CLIENT;
 	sprintf(info->name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
-	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info);
 
 	/* create a port */
 	memset(pinfo, 0, sizeof(*pinfo));
@@ -405,7 +406,7 @@
 	pcallbacks.unuse = snd_virmidi_unuse;
 	pcallbacks.event_input = snd_virmidi_event_input;
 	pinfo->kernel = &pcallbacks;
-	err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo);
+	err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, pinfo);
 	if (err < 0) {
 		snd_seq_delete_kernel_client(client);
 		rdev->client = -1;
@@ -548,4 +549,3 @@
 module_exit(alsa_virmidi_exit)
 
 EXPORT_SYMBOL(snd_virmidi_new);
-EXPORT_SYMBOL(snd_virmidi_receive);
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 33eaa5e..0815fad 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -431,7 +431,6 @@
 EXPORT_SYMBOL(snd_device_new);
 EXPORT_SYMBOL(snd_device_register);
 EXPORT_SYMBOL(snd_device_free);
-EXPORT_SYMBOL(snd_device_free_all);
   /* isadma.c */
 #ifdef CONFIG_ISA
 EXPORT_SYMBOL(snd_dma_program);
diff --git a/sound/core/timer.c b/sound/core/timer.c
index fa762ca..d67a5e9 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -69,6 +69,7 @@
 	struct timespec tstamp;		/* trigger tstamp */
 	wait_queue_head_t qchange_sleep;
 	struct fasync_struct *fasync;
+	struct semaphore tread_sem;
 } snd_timer_user_t;
 
 /* list of timers */
@@ -844,7 +845,7 @@
 	return 0;
 }
 
-int snd_timer_unregister(snd_timer_t *timer)
+static int snd_timer_unregister(snd_timer_t *timer)
 {
 	struct list_head *p, *n;
 	snd_timer_instance_t *ti;
@@ -945,11 +946,6 @@
 	unsigned long correction;
 };
 
-unsigned int snd_timer_system_resolution(void)
-{
-	return 1000000000L / HZ;
-}
-
 static void snd_timer_s_function(unsigned long data)
 {
 	snd_timer_t *timer = (snd_timer_t *)data;
@@ -1208,6 +1204,7 @@
 		return -ENOMEM;
 	spin_lock_init(&tu->qlock);
 	init_waitqueue_head(&tu->qchange_sleep);
+	init_MUTEX(&tu->tread_sem);
 	tu->ticks = 1;
 	tu->queue_size = 128;
 	tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
@@ -1454,18 +1451,23 @@
 	snd_timer_user_t *tu;
 	snd_timer_select_t tselect;
 	char str[32];
-	int err;
+	int err = 0;
 	
 	tu = file->private_data;
-	if (tu->timeri)
+	down(&tu->tread_sem);
+	if (tu->timeri) {
 		snd_timer_close(tu->timeri);
-	if (copy_from_user(&tselect, _tselect, sizeof(tselect)))
-		return -EFAULT;
+		tu->timeri = NULL;
+	}
+	if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
+		err = -EFAULT;
+		goto __err;
+	}
 	sprintf(str, "application %i", current->pid);
 	if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
 		tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
 	if ((err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid)) < 0)
-		return err;
+		goto __err;
 
 	if (tu->queue) {
 		kfree(tu->queue);
@@ -1477,23 +1479,27 @@
 	}
 	if (tu->tread) {
 		tu->tqueue = (snd_timer_tread_t *)kmalloc(tu->queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL);
-		if (tu->tqueue == NULL) {
-			snd_timer_close(tu->timeri);
-			return -ENOMEM;
-		}
+		if (tu->tqueue == NULL)
+			err = -ENOMEM;
 	} else {
 		tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
-		if (tu->queue == NULL) {
-			snd_timer_close(tu->timeri);
-			return -ENOMEM;
-		}
+		if (tu->queue == NULL)
+			err = -ENOMEM;
 	}
 	
-	tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
-	tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
-	tu->timeri->ccallback = snd_timer_user_ccallback;
-	tu->timeri->callback_data = (void *)tu;
-	return 0;
+      	if (err < 0) {
+		snd_timer_close(tu->timeri);
+      		tu->timeri = NULL;
+      	} else {
+		tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
+		tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
+		tu->timeri->ccallback = snd_timer_user_ccallback;
+		tu->timeri->callback_data = (void *)tu;
+	}
+
+      __err:
+      	up(&tu->tread_sem);
+	return err;
 }
 
 static int snd_timer_user_info(struct file *file, snd_timer_info_t __user *_info)
@@ -1669,6 +1675,23 @@
 	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
 }
 
+static int snd_timer_user_pause(struct file *file)
+{
+	int err;
+	snd_timer_user_t *tu;
+		
+	tu = file->private_data;
+	snd_assert(tu->timeri != NULL, return -ENXIO);
+	return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+}
+
+enum {
+	SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
+	SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
+	SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
+	SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
+};
+
 static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	snd_timer_user_t *tu;
@@ -1685,11 +1708,17 @@
 	{
 		int xarg;
 		
-		if (tu->timeri)		/* too late */
+		down(&tu->tread_sem);
+		if (tu->timeri)	{	/* too late */
+			up(&tu->tread_sem);
 			return -EBUSY;
-		if (get_user(xarg, p))
+		}
+		if (get_user(xarg, p)) {
+			up(&tu->tread_sem);
 			return -EFAULT;
+		}
 		tu->tread = xarg ? 1 : 0;
+		up(&tu->tread_sem);
 		return 0;
 	}
 	case SNDRV_TIMER_IOCTL_GINFO:
@@ -1707,11 +1736,17 @@
 	case SNDRV_TIMER_IOCTL_STATUS:
 		return snd_timer_user_status(file, argp);
 	case SNDRV_TIMER_IOCTL_START:
+	case SNDRV_TIMER_IOCTL_START_OLD:
 		return snd_timer_user_start(file);
 	case SNDRV_TIMER_IOCTL_STOP:
+	case SNDRV_TIMER_IOCTL_STOP_OLD:
 		return snd_timer_user_stop(file);
 	case SNDRV_TIMER_IOCTL_CONTINUE:
+	case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
 		return snd_timer_user_continue(file);
+	case SNDRV_TIMER_IOCTL_PAUSE:
+	case SNDRV_TIMER_IOCTL_PAUSE_OLD:
+		return snd_timer_user_pause(file);
 	}
 	return -ENOTTY;
 }
@@ -1898,4 +1933,3 @@
 EXPORT_SYMBOL(snd_timer_global_register);
 EXPORT_SYMBOL(snd_timer_global_unregister);
 EXPORT_SYMBOL(snd_timer_interrupt);
-EXPORT_SYMBOL(snd_timer_system_resolution);
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index 9fbc395..3de552d 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -106,8 +106,13 @@
 	case SNDRV_TIMER_IOCTL_SELECT:
 	case SNDRV_TIMER_IOCTL_PARAMS:
 	case SNDRV_TIMER_IOCTL_START:
+	case SNDRV_TIMER_IOCTL_START_OLD:
 	case SNDRV_TIMER_IOCTL_STOP:
+	case SNDRV_TIMER_IOCTL_STOP_OLD:
 	case SNDRV_TIMER_IOCTL_CONTINUE:
+	case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
+	case SNDRV_TIMER_IOCTL_PAUSE:
+	case SNDRV_TIMER_IOCTL_PAUSE_OLD:
 	case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
 		return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
 	case SNDRV_TIMER_IOCTL_INFO32:
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 3a3228b..148a856 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -179,6 +179,7 @@
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
 	select SND_GUS_SYNTH
+	select ISAPNP
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards with a TEA6330T bass and treble regulator
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 9fa7a78..563296d 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -83,6 +83,8 @@
 static struct pnp_card_device_id snd_ad1816a_pnpids[] = {
 	/* Analog Devices AD1815 */
 	{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
+	/* Analog Device AD1816? */
+	{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
 	/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
 	{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
 	/* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
index f0570f2..337b0e2 100644
--- a/sound/isa/gus/gus_io.c
+++ b/sound/isa/gus/gus_io.c
@@ -244,6 +244,8 @@
 	return res;
 }
 
+#if 0
+
 void snd_gf1_i_adlib_write(snd_gus_card_t * gus,
 		           unsigned char reg,
 		           unsigned char data)
@@ -265,6 +267,8 @@
 	spin_unlock_irqrestore(&gus->reg_lock, flags);
 }
 
+#endif  /*  0  */
+
 unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus,
 				 unsigned char reg, short w_16bit)
 {
@@ -329,6 +333,8 @@
 	return res;
 }
 
+#if 0
+
 void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data)
 {
 	unsigned long flags;
@@ -405,9 +411,7 @@
 	spin_unlock_irqrestore(&gus->reg_lock, flags);
 }
 
-/*
-
- */
+#endif  /*  0  */
 
 void snd_gf1_select_active_voices(snd_gus_card_t * gus)
 {
@@ -469,6 +473,8 @@
 		printk(" -%i- GF1  pan                    = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
 }
 
+#if 0
+
 void snd_gf1_print_global_registers(snd_gus_card_t * gus)
 {
 	unsigned char global_mode = 0x00;
@@ -528,4 +534,6 @@
 	}
 }
 
+#endif  /*  0  */
+
 #endif
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index 73f81c1..94bbd34 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -459,7 +459,6 @@
 EXPORT_SYMBOL(snd_gf1_look16);
 EXPORT_SYMBOL(snd_gf1_i_write8);
 EXPORT_SYMBOL(snd_gf1_i_look8);
-EXPORT_SYMBOL(snd_gf1_i_write16);
 EXPORT_SYMBOL(snd_gf1_i_look16);
 EXPORT_SYMBOL(snd_gf1_dram_addr);
 EXPORT_SYMBOL(snd_gf1_write_addr);
@@ -470,8 +469,6 @@
 EXPORT_SYMBOL(snd_gf1_free_voice);
 EXPORT_SYMBOL(snd_gf1_ctrl_stop);
 EXPORT_SYMBOL(snd_gf1_stop_voice);
-EXPORT_SYMBOL(snd_gf1_start);
-EXPORT_SYMBOL(snd_gf1_stop);
   /* gus_mixer.c */
 EXPORT_SYMBOL(snd_gf1_new_mixer);
   /* gus_pcm.c */
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
index bfc2b91..609838e 100644
--- a/sound/isa/gus/gus_mem.c
+++ b/sound/isa/gus/gus_mem.c
@@ -39,8 +39,8 @@
 	}
 }
 
-snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc,
-				        snd_gf1_mem_block_t * block)
+static snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc,
+					       snd_gf1_mem_block_t * block)
 {
 	snd_gf1_mem_block_t *pblock, *nblock;
 
@@ -105,8 +105,8 @@
 	return 0;
 }
 
-snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
-				      unsigned int address)
+static snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+					     unsigned int address)
 {
 	snd_gf1_mem_block_t *block;
 
@@ -118,8 +118,8 @@
 	return NULL;
 }
 
-snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
-				       unsigned int *share_id)
+static snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+					      unsigned int *share_id)
 {
 	snd_gf1_mem_block_t *block;
 
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
index b4e66f6..ef687ab 100644
--- a/sound/isa/gus/gus_reset.c
+++ b/sound/isa/gus/gus_reset.c
@@ -161,7 +161,8 @@
 #endif
 }
 
-void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max)
+static void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min,
+				 unsigned short v_max)
 {
 	unsigned long flags;
 	unsigned int daddr;
diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c
index 66552e6..f51c386 100644
--- a/sound/isa/gus/gus_synth.c
+++ b/sound/isa/gus/gus_synth.c
@@ -99,7 +99,8 @@
 	snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
 }
  
-int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
+static int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct,
+				     void *private_data, int atomic, int hop)
 {
 	snd_gus_port_t * p = (snd_gus_port_t *) private_data;
 	
diff --git a/sound/isa/gus/gus_tables.h b/sound/isa/gus/gus_tables.h
index ed8e9d8..4adf098 100644
--- a/sound/isa/gus/gus_tables.h
+++ b/sound/isa/gus/gus_tables.h
@@ -23,6 +23,8 @@
 
 #ifdef __GUS_TABLES_ALLOC__
 
+#if 0
+
 unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] =
 {
       8372,      8870,      9397,      9956,     10548,     11175,
@@ -49,6 +51,8 @@
   12123977,  12844906
 };
 
+#endif  /*  0  */
+
 unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = {
   4095 /* 0   */,1789 /* 1   */,1533 /* 2   */,1383 /* 3   */,1277 /* 4   */,
   1195 /* 5   */,1127 /* 6   */,1070 /* 7   */,1021 /* 8   */,978  /* 9   */,
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
index b72bcfb..3d36f6c 100644
--- a/sound/isa/gus/gus_volume.c
+++ b/sound/isa/gus/gus_volume.c
@@ -55,6 +55,8 @@
 	return (e << 8) | m;
 }
 
+#if 0
+
 unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol)
 {
 	unsigned int rvol;
@@ -108,6 +110,8 @@
 	return (range << 6) | (increment & 0x3f);
 }
 
+#endif  /*  0  */
+
 unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16)
 {
 	freq16 >>= 3;
@@ -120,6 +124,8 @@
 	return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
 }
 
+#if 0
+
 short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
 {
 	static short vibrato_table[] =
@@ -208,3 +214,5 @@
 	}
 	return (unsigned short) fc;
 }
+
+#endif  /*  0  */
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 0b024ec..36a33ae 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -120,6 +120,7 @@
 { 0x414c4770, 0xfffffff0, "ALC203",		NULL,		NULL },
 { 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738,	NULL },
 { 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739,	NULL },
+{ 0x434d4969, 0xffffffff, "CMI9780",		patch_cm9780,	NULL },
 { 0x434d4978, 0xffffffff, "CMI9761",		patch_cm9761,	NULL },
 { 0x434d4982, 0xffffffff, "CMI9761",		patch_cm9761,	NULL },
 { 0x434d4983, 0xffffffff, "CMI9761",		patch_cm9761,	NULL },
@@ -462,12 +463,14 @@
 {
 	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
 	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
-	unsigned short val;
+	unsigned short val, bitmask;
 	
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
 	val = snd_ac97_read_cache(ac97, e->reg);
-	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
+	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
 	if (e->shift_l != e->shift_r)
-		ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1);
+		ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1);
 
 	return 0;
 }
@@ -477,17 +480,19 @@
 	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
 	struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
 	unsigned short val;
-	unsigned short mask;
+	unsigned short mask, bitmask;
 	
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
 	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
 		return -EINVAL;
 	val = ucontrol->value.enumerated.item[0] << e->shift_l;
-	mask = (e->mask - 1) << e->shift_l;
+	mask = (bitmask - 1) << e->shift_l;
 	if (e->shift_l != e->shift_r) {
 		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
 			return -EINVAL;
 		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
-		mask |= (e->mask - 1) << e->shift_r;
+		mask |= (bitmask - 1) << e->shift_r;
 	}
 	return snd_ac97_update_bits(ac97, e->reg, mask, val);
 }
@@ -658,11 +663,6 @@
 AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)
 };
 
-static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = {
-AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
-AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
-};
-
 static const snd_kcontrol_new_t snd_ac97_control_eapd =
 AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);
 
@@ -1072,9 +1072,9 @@
 		unsigned short val;
 		snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
 		val = snd_ac97_read(ac97, reg);
-		if (! *lo_max && (val & cbit[i]))
+		if (! *lo_max && (val & 0x7f) == cbit[i])
 			*lo_max = max[i];
-		if (! *hi_max && (val & (cbit[i] << 8)))
+		if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i])
 			*hi_max = max[i];
 		if (*lo_max && *hi_max)
 			break;
@@ -1872,7 +1872,11 @@
 			goto __access_ok;
 	}
 
-	snd_ac97_write(ac97, AC97_RESET, 0);	/* reset to defaults */
+	/* reset to defaults */
+	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
+		snd_ac97_write(ac97, AC97_RESET, 0);
+	if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
+		snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
 	if (bus->ops->wait)
 		bus->ops->wait(ac97);
 	else {
@@ -1964,21 +1968,21 @@
 		/* note: it's important to set the rate at first */
 		tmp = AC97_MEA_GPIO;
 		if (ac97->ext_mid & AC97_MEI_LINE1) {
-			snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 12000);
+			snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000);
 			tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
 		}
 		if (ac97->ext_mid & AC97_MEI_LINE2) {
-			snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 12000);
+			snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000);
 			tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
 		}
 		if (ac97->ext_mid & AC97_MEI_HANDSET) {
-			snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 12000);
+			snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000);
 			tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;
 		}
-		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));
+		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
 		udelay(100);
 		/* nothing should be in powerdown mode */
-		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));
+		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
 		end_time = jiffies + (HZ / 10);
 		do {
 			if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 13c34a5..b810641 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -64,6 +64,116 @@
 	return ret;
 }
 
+/*
+ * shared line-in/mic controls
+ */
+static int ac97_enum_text_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo,
+			       const char **texts, unsigned int nums)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = nums;
+	if (uinfo->value.enumerated.item > nums - 1)
+		uinfo->value.enumerated.item = nums - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ac97_surround_jack_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static const char *texts[] = { "Shared", "Independent" };
+	return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
+}
+
+static int ac97_surround_jack_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ac97->indep_surround;
+	return 0;
+}
+
+static int ac97_surround_jack_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned char indep = !!ucontrol->value.enumerated.item[0];
+
+	if (indep != ac97->indep_surround) {
+		ac97->indep_surround = indep;
+		if (ac97->build_ops->update_jacks)
+			ac97->build_ops->update_jacks(ac97);
+		return 1;
+	}
+	return 0;
+}
+
+static int ac97_channel_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static const char *texts[] = { "2ch", "4ch", "6ch" };
+	if (kcontrol->private_value)
+		return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */
+	return ac97_enum_text_info(kcontrol, uinfo, texts, 3);
+}
+
+static int ac97_channel_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ac97->channel_mode;
+	return 0;
+}
+
+static int ac97_channel_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+	unsigned char mode = ucontrol->value.enumerated.item[0];
+
+	if (mode != ac97->channel_mode) {
+		ac97->channel_mode = mode;
+		if (ac97->build_ops->update_jacks)
+			ac97->build_ops->update_jacks(ac97);
+		return 1;
+	}
+	return 0;
+}
+
+#define AC97_SURROUND_JACK_MODE_CTL \
+	{ \
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name	= "Surround Jack Mode", \
+		.info = ac97_surround_jack_mode_info, \
+		.get = ac97_surround_jack_mode_get, \
+		.put = ac97_surround_jack_mode_put, \
+	}
+#define AC97_CHANNEL_MODE_CTL \
+	{ \
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name	= "Channel Mode", \
+		.info = ac97_channel_mode_info, \
+		.get = ac97_channel_mode_get, \
+		.put = ac97_channel_mode_put, \
+	}
+#define AC97_CHANNEL_MODE_4CH_CTL \
+	{ \
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name	= "Channel Mode", \
+		.info = ac97_channel_mode_info, \
+		.get = ac97_channel_mode_get, \
+		.put = ac97_channel_mode_put, \
+		.private_value = 1, \
+	}
+
+static inline int is_shared_linein(ac97_t *ac97)
+{
+	return ! ac97->indep_surround && ac97->channel_mode >= 1;
+}
+
+static inline int is_shared_micin(ac97_t *ac97)
+{
+	return ! ac97->indep_surround && ac97->channel_mode >= 2;
+}
+
+
 /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
 
 /* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */
@@ -1390,6 +1500,16 @@
 				    AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
 }
 
+static void ad1888_update_jacks(ac97_t *ac97)
+{
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 12,
+			     is_shared_linein(ac97) ? 0 : 1 << 12);
+	/* shared Mic */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 11,
+			     is_shared_micin(ac97) ? 0 : 1 << 11);
+}
+
 static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1406,8 +1526,8 @@
 		.get = snd_ac97_ad1888_downmix_get,
 		.put = snd_ac97_ad1888_downmix_put
 	},
-	AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0),
-	AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
 static int patch_ad1888_specific(ac97_t *ac97)
@@ -1422,8 +1542,9 @@
 	.build_post_spdif = patch_ad198x_post_spdif,
 	.build_specific = patch_ad1888_specific,
 #ifdef CONFIG_PM
-	.resume = ad18xx_resume
+	.resume = ad18xx_resume,
 #endif
+	.update_jacks = ad1888_update_jacks,
 };
 
 int patch_ad1888(ac97_t * ac97)
@@ -1459,8 +1580,9 @@
 	.build_post_spdif = patch_ad198x_post_spdif,
 	.build_specific = patch_ad1980_specific,
 #ifdef CONFIG_PM
-	.resume = ad18xx_resume
+	.resume = ad18xx_resume,
 #endif
+	.update_jacks = ad1888_update_jacks,
 };
 
 int patch_ad1980(ac97_t * ac97)
@@ -1471,10 +1593,21 @@
 }
 
 static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = {
-	AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0),
 	AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
 };
 
+static void ad1985_update_jacks(ac97_t *ac97)
+{
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 12,
+			     is_shared_linein(ac97) ? 0 : 1 << 12);
+	/* shared Mic */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 11,
+			     is_shared_micin(ac97) ? 0 : 1 << 11);
+	snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 1 << 9,
+			     is_shared_micin(ac97) ? 0 : 1 << 9);
+}
+
 static int patch_ad1985_specific(ac97_t *ac97)
 {
 	int err;
@@ -1488,8 +1621,9 @@
 	.build_post_spdif = patch_ad198x_post_spdif,
 	.build_specific = patch_ad1985_specific,
 #ifdef CONFIG_PM
-	.resume = ad18xx_resume
+	.resume = ad18xx_resume,
 #endif
+	.update_jacks = ad1985_update_jacks,
 };
 
 int patch_ad1985(ac97_t * ac97)
@@ -1521,31 +1655,25 @@
 /*
  * realtek ALC65x/850 codecs
  */
-static int snd_ac97_alc650_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void alc650_update_jacks(ac97_t *ac97)
 {
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
-        return 0;
-}
-
-static int snd_ac97_alc650_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	int change, val;
-	val = !!(snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10));
-	change = (ucontrol->value.integer.value[0] != val);
-	if (change) {
-		/* disable/enable vref */
-		snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
-				     ucontrol->value.integer.value[0] ? (1 << 12) : 0);
-		/* turn on/off center-on-mic */
-		snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
-				     ucontrol->value.integer.value[0] ? (1 << 10) : 0);
-		/* GPIO0 high for mic */
-		snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
-				     ucontrol->value.integer.value[0] ? 0 : 0x100);
-        }
-        return change;
+	int shared;
+	
+	/* shared Line-In */
+	shared = is_shared_linein(ac97);
+	snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9,
+			     shared ? (1 << 9) : 0);
+	/* update shared Mic */
+	shared = is_shared_micin(ac97);
+	/* disable/enable vref */
+	snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
+			     shared ? (1 << 12) : 0);
+	/* turn on/off center-on-mic */
+	snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
+			     shared ? (1 << 10) : 0);
+	/* GPIO0 high for mic */
+	snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
+			     shared ? 0 : 0x100);
 }
 
 static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = {
@@ -1558,8 +1686,8 @@
 	/* 6: Independent Master Volume Right */
 	/* 7: Independent Master Volume Left */
 	/* 8: reserved */
-	AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0),
-	/* 10: mic, see below */
+	/* 9: Line-In/Surround share */
+	/* 10: Mic/CLFE share */
 	/* 11-13: in IEC958 controls */
 	AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
 #if 0 /* always set in patch_alc650 */
@@ -1570,14 +1698,8 @@
 	AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1),
 	AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1),
 #endif
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Mic As Center/LFE",
-		.info = snd_ac97_info_volsw,
-		.get = snd_ac97_alc650_mic_get,
-		.put = snd_ac97_alc650_mic_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
 static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = {
@@ -1601,7 +1723,8 @@
 }
 
 static struct snd_ac97_build_ops patch_alc650_ops = {
-	.build_specific	= patch_alc650_specific
+	.build_specific	= patch_alc650_specific,
+	.update_jacks = alc650_update_jacks
 };
 
 int patch_alc650(ac97_t * ac97)
@@ -1659,37 +1782,27 @@
 	return 0;
 }
 
-static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void alc655_update_jacks(ac97_t *ac97)
 {
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
-        return 0;
-}
-
-static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-
+	int shared;
+	
+	/* shared Line-In */
+	shared = is_shared_linein(ac97);
+	ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9,
+			      shared ? (1 << 9) : 0, 0);
+	/* update shared mic */
+	shared = is_shared_micin(ac97);
 	/* misc control; vrefout disable */
 	snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
-			     ucontrol->value.integer.value[0] ? (1 << 12) : 0);
-	return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
-				     ucontrol->value.integer.value[0] ? (1 << 10) : 0,
-				     0);
+			     shared ? (1 << 12) : 0);
+	ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
+			      shared ? (1 << 10) : 0, 0);
 }
 
-
 static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = {
 	AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
-	AC97_PAGE_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0, 0),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Mic As Center/LFE",
-		.info = snd_ac97_info_volsw,
-		.get = snd_ac97_alc655_mic_get,
-		.put = snd_ac97_alc655_mic_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
 static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
@@ -1759,7 +1872,8 @@
 }
 
 static struct snd_ac97_build_ops patch_alc655_ops = {
-	.build_specific	= patch_alc655_specific
+	.build_specific	= patch_alc655_specific,
+	.update_jacks = alc655_update_jacks
 };
 
 int patch_alc655(ac97_t * ac97)
@@ -1798,63 +1912,33 @@
 #define AC97_ALC850_JACK_SELECT	0x76
 #define AC97_ALC850_MISC1	0x7a
 
-static int ac97_alc850_surround_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void alc850_update_jacks(ac97_t *ac97)
 {
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 12) & 7) == 2;
-        return 0;
-}
-
-static int ac97_alc850_surround_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-
+	int shared;
+	
+	/* shared Line-In */
+	shared = is_shared_linein(ac97);
 	/* SURR 1kOhm (bit4), Amp (bit5) */
 	snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
-			     ucontrol->value.integer.value[0] ? (1<<5) : (1<<4));
+			     shared ? (1<<5) : (1<<4));
 	/* LINE-IN = 0, SURROUND = 2 */
-	return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
-				    ucontrol->value.integer.value[0] ? (2<<12) : (0<<12));
-}
-
-static int ac97_alc850_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 4) & 7) == 2;
-        return 0;
-}
-
-static int ac97_alc850_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-
+	snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
+			     shared ? (2<<12) : (0<<12));
+	/* update shared mic */
+	shared = is_shared_micin(ac97);
 	/* Vref disable (bit12), 1kOhm (bit13) */
 	snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
-			     ucontrol->value.integer.value[0] ? (1<<12) : (1<<13));
+			     shared ? (1<<12) : (1<<13));
 	/* MIC-IN = 1, CENTER-LFE = 2 */
-	return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
-				    ucontrol->value.integer.value[0] ? (2<<4) : (1<<4));
+	snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
+			     shared ? (2<<4) : (1<<4));
 }
 
 static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = {
 	AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Line-In As Surround",
-		.info = snd_ac97_info_volsw,
-		.get = ac97_alc850_surround_get,
-		.put = ac97_alc850_surround_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Mic As Center/LFE",
-		.info = snd_ac97_info_volsw,
-		.get = ac97_alc850_mic_get,
-		.put = ac97_alc850_mic_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
-
+	AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
 static int patch_alc850_specific(ac97_t *ac97)
@@ -1871,7 +1955,8 @@
 }
 
 static struct snd_ac97_build_ops patch_alc850_ops = {
-	.build_specific	= patch_alc850_specific
+	.build_specific	= patch_alc850_specific,
+	.update_jacks = alc850_update_jacks
 };
 
 int patch_alc850(ac97_t *ac97)
@@ -1911,9 +1996,17 @@
 /*
  * C-Media CM97xx codecs
  */
+static void cm9738_update_jacks(ac97_t *ac97)
+{
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10,
+			     is_shared_linein(ac97) ? (1 << 10) : 0);
+}
+
 static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = {
-	AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0),
 	AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_4CH_CTL,
 };
 
 static int patch_cm9738_specific(ac97_t * ac97)
@@ -1922,7 +2015,8 @@
 }
 
 static struct snd_ac97_build_ops patch_cm9738_ops = {
-	.build_specific	= patch_cm9738_specific
+	.build_specific	= patch_cm9738_specific,
+	.update_jacks = cm9738_update_jacks
 };
 
 int patch_cm9738(ac97_t * ac97)
@@ -1986,34 +2080,19 @@
 	/* BIT 8: SPD32 - 32bit SPDIF - not supported yet */
 };
 
-static int snd_ac97_cm9739_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void cm9739_update_jacks(ac97_t *ac97)
 {
-	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000)
-		ucontrol->value.integer.value[0] = 1;
-	else
-		ucontrol->value.integer.value[0] = 0;
-	return 0;
-}
-
-static int snd_ac97_cm9739_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
-				    ucontrol->value.integer.value[0] ? 
-				    0x1000 : 0x2000);
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10,
+			     is_shared_linein(ac97) ? (1 << 10) : 0);
+	/* shared Mic */
+	snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
+			     is_shared_micin(ac97) ? 0x1000 : 0x2000);
 }
 
 static const snd_kcontrol_new_t snd_ac97_cm9739_controls[] = {
-	AC97_SINGLE("Line-In As Surround", AC97_CM9739_MULTI_CHAN, 10, 1, 0),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Mic As Center/LFE",
-		.info = snd_ac97_info_volsw,
-		.get = snd_ac97_cm9739_center_mic_get,
-		.put = snd_ac97_cm9739_center_mic_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
 static int patch_cm9739_specific(ac97_t * ac97)
@@ -2028,7 +2107,8 @@
 
 static struct snd_ac97_build_ops patch_cm9739_ops = {
 	.build_specific	= patch_cm9739_specific,
-	.build_post_spdif = patch_cm9739_post_spdif
+	.build_post_spdif = patch_cm9739_post_spdif,
+	.update_jacks = cm9739_update_jacks
 };
 
 int patch_cm9739(ac97_t * ac97)
@@ -2087,71 +2167,97 @@
 }
 
 #define AC97_CM9761_MULTI_CHAN	0x64
+#define AC97_CM9761_FUNC	0x66
 #define AC97_CM9761_SPDIF_CTRL	0x6c
 
-static int snd_ac97_cm9761_linein_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void cm9761_update_jacks(ac97_t *ac97)
 {
-	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x0400)
-		ucontrol->value.integer.value[0] = 1;
-	else
-		ucontrol->value.integer.value[0] = 0;
-	return 0;
-}
-
-static int snd_ac97_cm9761_linein_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	unsigned short vals[2][2] = {
+	unsigned short surr_vals[2][2] = {
 		{ 0x0008, 0x0400 }, /* off, on */
 		{ 0x0000, 0x0408 }, /* off, on (9761-82 rev.B) */
 	};
-	return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x0408,
-				    vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]);
-}
-
-static int snd_ac97_cm9761_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000)
-		ucontrol->value.integer.value[0] = 1;
-	else
-		ucontrol->value.integer.value[0] = 0;
-	if (ac97->spec.dev_flags) /* 9761-82 rev.B */
-		ucontrol->value.integer.value[0] = !ucontrol->value.integer.value[0];
-	return 0;
-}
-
-static int snd_ac97_cm9761_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-	unsigned short vals[2][2] = {
+	unsigned short clfe_vals[2][2] = {
 		{ 0x2000, 0x1880 }, /* off, on */
 		{ 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */
 	};
-	return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3880,
-				    vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]);
+
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x0408,
+			     surr_vals[ac97->spec.dev_flags][is_shared_linein(ac97)]);
+	/* shared Mic */
+	snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3880,
+			     clfe_vals[ac97->spec.dev_flags][is_shared_micin(ac97)]);
 }
 
 static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Line-In As Surround",
-		.info = snd_ac97_info_volsw,
-		.get = snd_ac97_cm9761_linein_rear_get,
-		.put = snd_ac97_cm9761_linein_rear_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Mic As Center/LFE",
-		.info = snd_ac97_info_volsw,
-		.get = snd_ac97_cm9761_center_mic_get,
-		.put = snd_ac97_cm9761_center_mic_put,
-		.private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-	},
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
+static int cm9761_spdif_out_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int cm9761_spdif_out_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+	if (ac97->regs[AC97_CM9761_FUNC] & 0x1)
+		ucontrol->value.enumerated.item[0] = 2; /* SPDIF-loopback */
+	else if (ac97->regs[AC97_CM9761_SPDIF_CTRL] & 0x2)
+		ucontrol->value.enumerated.item[0] = 1; /* ADC loopback */
+	else
+		ucontrol->value.enumerated.item[0] = 0; /* AC-link */
+	return 0;
+}
+
+static int cm9761_spdif_out_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+	if (ucontrol->value.enumerated.item[0] == 2)
+		return snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0x1);
+	snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0);
+	return snd_ac97_update_bits(ac97, AC97_CM9761_SPDIF_CTRL, 0x2,
+				    ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
+}
+
+static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" };
+static const struct ac97_enum cm9761_dac_clock_enum =
+	AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
+
+static const snd_kcontrol_new_t snd_ac97_cm9761_controls_spdif[] = {
+	{ /* BIT 1: SPDIFS */
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = cm9761_spdif_out_source_info,
+		.get = cm9761_spdif_out_source_get,
+		.put = cm9761_spdif_out_source_put,
+	},
+	/* BIT 2: IG_SPIV */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9761_SPDIF_CTRL, 2, 1, 0),
+	/* BIT 3: SPI2F */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9761_SPDIF_CTRL, 3, 1, 0), 
+	/* BIT 4: SPI2SDI */
+	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9761_SPDIF_CTRL, 4, 1, 0),
+	/* BIT 9-10: DAC_CTL */
+	AC97_ENUM("DAC Clock Source", cm9761_dac_clock_enum),
+};
+
+static int patch_cm9761_post_spdif(ac97_t * ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_cm9761_controls_spdif, ARRAY_SIZE(snd_ac97_cm9761_controls_spdif));
+}
+
 static int patch_cm9761_specific(ac97_t * ac97)
 {
 	return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
@@ -2159,7 +2265,8 @@
 
 static struct snd_ac97_build_ops patch_cm9761_ops = {
 	.build_specific	= patch_cm9761_specific,
-	.build_post_spdif = patch_cm9739_post_spdif /* hope it's identical... */
+	.build_post_spdif = patch_cm9761_post_spdif,
+	.update_jacks = cm9761_update_jacks
 };
 
 int patch_cm9761(ac97_t *ac97)
@@ -2193,24 +2300,25 @@
 	/* to be sure: we overwrite the ext status bits */
 	snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0);
 	/* Don't set 0x0200 here.  This results in the silent analog output */
-	snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0009);
+	snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0001); /* enable spdif-in */
 	ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
 
 	/* set-up multi channel */
 	/* bit 15: pc master beep off
-	 * bit 14: ??
+	 * bit 14: pin47 = EAPD/SPDIF
 	 * bit 13: vref ctl [= cm9739]
-	 * bit 12: center/mic [= cm9739] (reverted on rev B)
-	 * bit 11: ?? (mic/center/lfe) (reverted on rev B)
-	 * bit 10: suddound/line [= cm9739]
-	 * bit  9: mix 2 surround
-	 * bit  8: ?
-	 * bit  7: ?? (mic/center/lfe)
-	 * bit  4: ?? (front)
-	 * bit  3: ?? (line-in/rear share) (revereted with rev B)
-	 * bit  2: ?? (surround)
-	 * bit  1: front mic
-	 * bit  0: mic boost
+	 * bit 12: CLFE control (reverted on rev B)
+	 * bit 11: Mic/center share (reverted on rev B)
+	 * bit 10: suddound/line share
+	 * bit  9: Analog-in mix -> surround
+	 * bit  8: Analog-in mix -> CLFE
+	 * bit  7: Mic/LFE share (mic/center/lfe)
+	 * bit  5: vref select (9761A)
+	 * bit  4: front control
+	 * bit  3: surround control (revereted with rev B)
+	 * bit  2: front mic
+	 * bit  1: stereo mic
+	 * bit  0: mic boost level (0=20dB, 1=30dB)
 	 */
 
 #if 0
@@ -2230,6 +2338,47 @@
 	return 0;
 }
        
+#define AC97_CM9780_SIDE	0x60
+#define AC97_CM9780_JACK	0x62
+#define AC97_CM9780_MIXER	0x64
+#define AC97_CM9780_MULTI_CHAN	0x66
+#define AC97_CM9780_SPDIF	0x6c
+
+static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" };
+static const struct ac97_enum cm9780_ch_select_enum =
+	AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
+static const snd_kcontrol_new_t cm9780_controls[] = {
+	AC97_DOUBLE("Side Playback Switch", AC97_CM9780_SIDE, 15, 7, 1, 1),
+	AC97_DOUBLE("Side Playback Volume", AC97_CM9780_SIDE, 8, 0, 31, 0),
+	AC97_ENUM("Side Playback Route", cm9780_ch_select_enum),
+};
+
+static int patch_cm9780_specific(ac97_t *ac97)
+{
+	return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9780_ops = {
+	.build_specific	= patch_cm9780_specific,
+	.build_post_spdif = patch_cm9761_post_spdif	/* identical with CM9761 */
+};
+
+int patch_cm9780(ac97_t *ac97)
+{
+	unsigned short val;
+
+	ac97->build_ops = &patch_cm9780_ops;
+
+	/* enable spdif */
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+		val = snd_ac97_read(ac97, AC97_CM9780_SPDIF);
+		val |= 0x1; /* SPDI_EN */
+		snd_ac97_write_cache(ac97, AC97_CM9780_SPDIF, val);
+	}
+
+	return 0;
+}
 
 /*
  * VIA VT1616 codec
@@ -2263,9 +2412,21 @@
 	return 0;
 }
 
+/*
+ */
+static void it2646_update_jacks(ac97_t *ac97)
+{
+	/* shared Line-In */
+	snd_ac97_update_bits(ac97, 0x76, 1 << 9,
+			     is_shared_linein(ac97) ? (1<<9) : 0);
+	/* shared Mic */
+	snd_ac97_update_bits(ac97, 0x76, 1 << 10,
+			     is_shared_micin(ac97) ? (1<<10) : 0);
+}
+
 static const snd_kcontrol_new_t snd_ac97_controls_it2646[] = {
-	AC97_SINGLE("Line-In As Surround", 0x76, 9, 1, 0),
-	AC97_SINGLE("Mic As Center/LFE", 0x76, 10, 1, 0),
+	AC97_SURROUND_JACK_MODE_CTL,
+	AC97_CHANNEL_MODE_CTL,
 };
 
 static const snd_kcontrol_new_t snd_ac97_spdif_controls_it2646[] = {
@@ -2285,7 +2446,8 @@
 }
 
 static struct snd_ac97_build_ops patch_it2646_ops = {
-	.build_specific	= patch_it2646_specific
+	.build_specific	= patch_it2646_specific,
+	.update_jacks = it2646_update_jacks
 };
 
 int patch_it2646(ac97_t * ac97)
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 6db51c9..7b7377d 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -54,6 +54,7 @@
 int patch_cm9738(ac97_t * ac97);
 int patch_cm9739(ac97_t * ac97);
 int patch_cm9761(ac97_t * ac97);
+int patch_cm9780(ac97_t * ac97);
 int patch_vt1616(ac97_t * ac97);
 int patch_it2646(ac97_t * ac97);
 int mpatch_si3036(ac97_t * ac97);
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 984d5d4..038f56a 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2270,7 +2270,7 @@
 
 static int __init alsa_card_ali_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ali_exit(void)
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index f1a5f57..ca28b22 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -367,7 +367,7 @@
 	if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
 		snd_pcm_period_elapsed(chip->capture_substream);
 	if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */
-		snd_mpu401_uart_interrupt(irq, chip->rmidi, regs);
+		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
 	/* release the gcr */
 	outb(gcr_status, chip->alt_port + 0xe);
 	
@@ -777,7 +777,7 @@
 
 static int __init alsa_card_als4000_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_als4000_exit(void)
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 6b04c0a..06551a6 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1645,7 +1645,7 @@
 
 static int __init alsa_card_atiixp_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_atiixp_exit(void)
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index fb7cece..9220aae 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1332,7 +1332,7 @@
 
 static int __init alsa_card_atiixp_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_atiixp_exit(void)
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 889b4a1..f6236c6 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -375,7 +375,7 @@
 // initialization of the module
 static int __init alsa_card_vortex_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 // clean up the module
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index b8ae534..72bba7b 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1520,7 +1520,7 @@
 {
 	int err;
 	snd_azf3328_dbgcallenter();
-	err = pci_module_init(&driver);
+	err = pci_register_driver(&driver);
 	snd_azf3328_dbgcallleave();
 	return err;
 }
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 89a7ffe..defdc5a 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -918,7 +918,7 @@
 {
 	if (load_all)
 		driver.id_table = snd_bt87x_default_ids;
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_bt87x_exit(void)
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index deb0288..c623858 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -508,9 +508,17 @@
 	unsigned short running;
 };
 
+typedef struct {
+        u32 serial;
+        char * name;
+        int ac97;
+	int gpio_type;
+} ca0106_details_t;
+
 // definition of the chip-specific record
 struct snd_ca0106 {
 	snd_card_t *card;
+	ca0106_details_t *details;
 	struct pci_dev *pci;
 
 	unsigned long port;
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 82533b4..a56e68e 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -161,18 +161,29 @@
 
 #include "ca0106.h"
 
-typedef struct {
-	u32 serial;
-	char * name;
-} ca0106_names_t;
-
-static ca0106_names_t ca0106_chip_names[] = {
-	 { 0x10021102, "AudigyLS [SB0310]"} , 
-	 { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */
-	 { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
-	 { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
-	 { 0x10091462, "MSI K8N Diamond MB [SB0438]"}, /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
-	 { 0, "AudigyLS [Unknown]" }
+static ca0106_details_t ca0106_chip_details[] = {
+	 /* AudigyLS[SB0310] */
+	 { .serial = 0x10021102,
+	   .name   = "AudigyLS [SB0310]",
+	   .ac97   = 1 } , 
+	 /* Unknown AudigyLS that also says SB0310 on it */
+	 { .serial = 0x10051102,
+	   .name   = "AudigyLS [SB0310b]",
+	   .ac97   = 1 } ,
+	 /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+	 { .serial = 0x10061102,
+	   .name   = "Live! 7.1 24bit [SB0410]",
+	   .gpio_type = 1 } ,
+	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+	 { .serial = 0x10071102,
+	   .name   = "Live! 7.1 24bit [SB0413]",
+	   .gpio_type = 1 } ,
+	 /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+	 { .serial = 0x10091462,
+	   .name   = "MSI K8N Diamond MB [SB0438]",
+	   .gpio_type = 1 } ,
+	 { .serial = 0,
+	   .name   = "AudigyLS [Unknown]" }
 };
 
 /* hardware definition */
@@ -810,6 +821,7 @@
 
 	memset(&ac97, 0, sizeof(ac97));
 	ac97.private_data = chip;
+	ac97.scaps = AC97_SCAP_NO_SPDIF;
 	return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
 }
 
@@ -993,6 +1005,7 @@
 					 ca0106_t **rchip)
 {
 	ca0106_t *chip;
+	ca0106_details_t *c;
 	int err;
 	int ch;
 	static snd_device_ops_t ops = {
@@ -1054,6 +1067,15 @@
 	printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
 	       chip->revision, chip->serial);
 #endif
+	strcpy(card->driver, "CA0106");
+	strcpy(card->shortname, "CA0106");
+
+	for (c=ca0106_chip_details; c->serial; c++) {
+		if (c->serial == chip->serial) break;
+	}
+	chip->details = c;
+	sprintf(card->longname, "%s at 0x%lx irq %i",
+		c->name, chip->port, chip->irq);
 
 	outl(0, chip->port + INTE);
 
@@ -1113,7 +1135,7 @@
 	//snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
 	/* Analog or Digital output */
 	snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
-	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000b0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers */
+	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
 	chip->spdif_enable = 0; /* Set digital SPDIF output off */
 	chip->capture_source = 3; /* Set CAPTURE_SOURCE */
 	//snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
@@ -1138,13 +1160,11 @@
         snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
 	chip->capture_source = 3; /* Set CAPTURE_SOURCE */
 
-        if ((chip->serial == 0x10061102) || 
-	    (chip->serial == 0x10071102) ||
-	    (chip->serial == 0x10091462)) { /* The SB0410 and SB0413 use GPIO differently. */
+        if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
 		/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
 		outl(0x0, chip->port+GPIO);
 		//outl(0x00f0e000, chip->port+GPIO); /* Analog */
-		outl(0x005f4300, chip->port+GPIO); /* Analog */
+		outl(0x005f4301, chip->port+GPIO); /* Analog */
 	} else {
 		outl(0x0, chip->port+GPIO);
 		outl(0x005f03a3, chip->port+GPIO); /* Analog */
@@ -1172,7 +1192,6 @@
 	static int dev;
 	snd_card_t *card;
 	ca0106_t *chip;
-	ca0106_names_t *c;
 	int err;
 
 	if (dev >= SNDRV_CARDS)
@@ -1207,9 +1226,7 @@
 		snd_card_free(card);
 		return err;
 	}
-        if ((chip->serial != 0x10061102) && 
-	    (chip->serial != 0x10071102) && 
-	    (chip->serial != 0x10091462) ) { /* The SB0410 and SB0413 do not have an ac97 chip. */
+        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
 		if ((err = snd_ca0106_ac97(chip)) < 0) {
 			snd_card_free(card);
 			return err;
@@ -1222,15 +1239,6 @@
 
 	snd_ca0106_proc_init(chip);
 
-	strcpy(card->driver, "CA0106");
-	strcpy(card->shortname, "CA0106");
-
-	for (c=ca0106_chip_names; c->serial; c++) {
-		if (c->serial == chip->serial) break;
-	}
-	sprintf(card->longname, "%s at 0x%lx irq %i",
-		c->name, chip->port, chip->irq);
-
 	if ((err = snd_card_register(card)) < 0) {
 		snd_card_free(card);
 		return err;
@@ -1267,7 +1275,7 @@
 {
 	int err;
 
-	if ((err = pci_module_init(&driver)) > 0)
+	if ((err = pci_register_driver(&driver)) > 0)
 		return err;
 
 	return 0;
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 97bed1b..48e2486 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -113,7 +113,7 @@
 		} else {
 			/* Analog */
 			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
-			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000b0000);
+			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
 			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
 				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
 			mask = inl(emu->port + GPIO) | 0x101;
@@ -437,7 +437,7 @@
 static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "Analog Unknown Volume",
+        .name =         "Analog Side Volume",
         .info =         snd_ca0106_volume_info,
         .get =          snd_ca0106_volume_get_analog_unknown,
         .put =          snd_ca0106_volume_put_analog_unknown
@@ -620,11 +620,6 @@
 		return -ENOMEM;
 	if ((err = snd_ctl_add(card, kctl)))
 		return err;
-	if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) {
-		/* already defined by ac97, remove it */
-		/* FIXME: or do we need both controls? */
-		remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT));
-	}
 	if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL)
 		return -ENOMEM;
 	if ((err = snd_ctl_add(card, kctl)))
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index afb7114..3e5161a 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -95,7 +95,7 @@
 };
 
 
-void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value)
+static void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value)
 {
 	int i;
 	u32 status[4];
@@ -418,6 +418,7 @@
 		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_ca0106_proc_reg_write32;
+		entry->mode |= S_IWUSR;
 	}
 	if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
 		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16);
@@ -427,6 +428,7 @@
 		snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_ca0106_proc_reg_write;
+		entry->mode |= S_IWUSR;
 //		entry->private_data = emu;
 	}
 	if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) 
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 113208f..b450338 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -519,40 +519,50 @@
 }
 
 /* bit operations for dword register */
-static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+static int snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
 {
-	unsigned int val;
-	val = inl(cm->iobase + cmd);
+	unsigned int val, oval;
+	val = oval = inl(cm->iobase + cmd);
 	val |= flag;
+	if (val == oval)
+		return 0;
 	outl(val, cm->iobase + cmd);
+	return 1;
 }
 
-static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+static int snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
 {
-	unsigned int val;
-	val = inl(cm->iobase + cmd);
+	unsigned int val, oval;
+	val = oval = inl(cm->iobase + cmd);
 	val &= ~flag;
+	if (val == oval)
+		return 0;
 	outl(val, cm->iobase + cmd);
+	return 1;
 }
 
-#if 0 // not used
 /* bit operations for byte register */
-static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+static int snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
 {
-	unsigned char val;
-	val = inb(cm->iobase + cmd);
+	unsigned char val, oval;
+	val = oval = inb(cm->iobase + cmd);
 	val |= flag;
+	if (val == oval)
+		return 0;
 	outb(val, cm->iobase + cmd);
+	return 1;
 }
 
-static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+static int snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
 {
-	unsigned char val;
-	val = inb(cm->iobase + cmd);
+	unsigned char val, oval;
+	val = oval = inb(cm->iobase + cmd);
 	val &= ~flag;
+	if (val == oval)
+		return 0;
 	outb(val, cm->iobase + cmd);
+	return 1;
 }
-#endif
 
 
 /*
@@ -2250,8 +2260,8 @@
 DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0);
 #endif
 DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0);
-DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
-DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
+// DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
+// DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
 // DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); /* now module option */
 DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
 
@@ -2300,10 +2310,114 @@
 }
 
 
+static int snd_cmipci_line_in_mode_info(snd_kcontrol_t *kcontrol,
+					snd_ctl_elem_info_t *uinfo)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static inline unsigned int get_line_in_mode(cmipci_t *cm)
+{
+	unsigned int val;
+	if (cm->chip_version >= 39) {
+		val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL);
+		if (val & CM_LINE_AS_BASS)
+			return 2;
+	}
+	val = snd_cmipci_read_b(cm, CM_REG_MIXER1);
+	if (val & CM_SPK4)
+		return 1;
+	return 0;
+}
+
+static int snd_cmipci_line_in_mode_get(snd_kcontrol_t *kcontrol,
+				       snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+	spin_lock_irq(&cm->reg_lock);
+	ucontrol->value.enumerated.item[0] = get_line_in_mode(cm);
+	spin_unlock_irq(&cm->reg_lock);
+	return 0;
+}
+
+static int snd_cmipci_line_in_mode_put(snd_kcontrol_t *kcontrol,
+				       snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irq(&cm->reg_lock);
+	if (ucontrol->value.enumerated.item[0] == 2)
+		change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+	else
+		change = snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+	if (ucontrol->value.enumerated.item[0] == 1)
+		change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+	else
+		change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+	spin_unlock_irq(&cm->reg_lock);
+	return change;
+}
+
+static int snd_cmipci_mic_in_mode_info(snd_kcontrol_t *kcontrol,
+				       snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[2] = { "Mic-In", "Center/LFE Output" };
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_cmipci_mic_in_mode_get(snd_kcontrol_t *kcontrol,
+				      snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	/* same bit as spdi_phase */
+	spin_lock_irq(&cm->reg_lock);
+	ucontrol->value.enumerated.item[0] = 
+		(snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0;
+	spin_unlock_irq(&cm->reg_lock);
+	return 0;
+}
+
+static int snd_cmipci_mic_in_mode_put(snd_kcontrol_t *kcontrol,
+				      snd_ctl_elem_value_t *ucontrol)
+{
+	cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irq(&cm->reg_lock);
+	if (ucontrol->value.enumerated.item[0])
+		change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+	else
+		change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+	spin_unlock_irq(&cm->reg_lock);
+	return change;
+}
+
 /* both for CM8338/8738 */
 static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = {
 	DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
-	DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear),
+	{
+		.name = "Line-In Mode",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = snd_cmipci_line_in_mode_info,
+		.get = snd_cmipci_line_in_mode_get,
+		.put = snd_cmipci_line_in_mode_put,
+	},
 };
 
 /* for non-multichannel chips */
@@ -2341,10 +2455,15 @@
 
 /* only for model 039 or later */
 static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
-	DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
 	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
 	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
-	DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */
+	{
+		.name = "Mic-In Mode",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = snd_cmipci_mic_in_mode_info,
+		.get = snd_cmipci_mic_in_mode_get,
+		.put = snd_cmipci_mic_in_mode_put,
+	}
 };
 
 /* card control switches */
@@ -2944,7 +3063,7 @@
 	
 static int __init alsa_card_cmipci_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_cmipci_exit(void)
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index d7e06b3..b6e1854 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -206,7 +206,10 @@
 
 #define BA0_PMCS		0x0344	/* Power Management Control/Status */
 #define BA0_CWPR		0x03e0	/* Configuration Write Protect */
+
 #define BA0_EPPMC		0x03e4	/* Extended PCI Power Management Control */
+#define BA0_EPPMC_FPDN		(1<<14) /* Full Power DowN */
+
 #define BA0_GPIOR		0x03e8	/* GPIO Pin Interface Register */
 
 #define BA0_SPMC		0x03ec	/* Serial Port Power Management Control (& ASDIN2 enable) */
@@ -1461,6 +1464,11 @@
 	int timeout;
 	int retry_count = 2;
 
+	/* Having EPPMC.FPDN=1 prevent proper chip initialisation */
+	tmp = snd_cs4281_peekBA0(chip, BA0_EPPMC);
+	if (tmp & BA0_EPPMC_FPDN)
+		snd_cs4281_pokeBA0(chip, BA0_EPPMC, tmp & ~BA0_EPPMC_FPDN);
+
       __retry:
 	tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
 	if (tmp != BA0_CFLR_DEFAULT) {
@@ -2124,7 +2132,7 @@
 	
 static int __init alsa_card_cs4281_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_cs4281_exit(void)
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 25d6466..db212ec 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -171,7 +171,7 @@
 
 static int __init alsa_card_cs46xx_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_cs46xx_exit(void)
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 6446afe..2085a99 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -228,7 +228,7 @@
 
 static int __init alsa_card_emu10k1_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_emu10k1_exit(void)
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index c3c96f9..77be072 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -170,7 +170,7 @@
 			SPCS_GENERATIONSTATUS | 0x00001200 |
 			0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
 
-	if (emu->audigy && emu->revision == 4) { /* audigy2 */
+	if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
 		/* Hacks for Alice3 to work independent of haP16V driver */
 		u32 tmp;
 
@@ -189,7 +189,7 @@
 		/* Enabled Phased (8-channel) P16V playback */
 		outl(0x0201, emu->port + HCFG2);
 		/* Set playback routing. */
-		snd_emu10k1_ptr_write(emu, CAPTURE_P16V_SOURCE, 0, 78e4);
+		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
 	}
 	if (emu->audigy && (emu->serial == 0x10011102) ) { /* audigy2 Value */
 		/* Hacks for Alice3 to work independent of haP16V driver */
@@ -600,7 +600,7 @@
 	if (emu->port)
 		pci_release_regions(emu->pci);
 	pci_disable_device(emu->pci);
-	if (emu->audigy && emu->revision == 4) /* P16V */	
+	if (emu->card_capabilities->ca0151_chip) /* P16V */	
 		snd_p16v_free(emu);
 	kfree(emu);
 	return 0;
@@ -612,21 +612,24 @@
 	return snd_emu10k1_free(emu);
 }
 
-/* vendor, device, subsystem, emu10k1_chip, emu10k2_chip, ca0102_chip, ca0108_chip, ca0151_chip, spk71, spdif_bug, ac97_chip, ecard, driver, name */
-
 static emu_chip_details_t emu_chip_details[] = {
 	/* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
 	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
 	 .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0108_chip = 1,
-	 .spk71 = 1} ,
+	 .spk71 = 1,
+	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0008, 
 	 .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
-	 .ca0108_chip = 1} ,
+	 .ca0108_chip = 1,
+	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
 	 .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ca0151_chip = 1,
@@ -635,6 +638,7 @@
 	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
 	 .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ca0151_chip = 1,
@@ -643,6 +647,7 @@
 	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
 	 .driver = "Audigy2", .name = "Audigy 2 ZS [2001]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ca0151_chip = 1,
@@ -651,6 +656,7 @@
 	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
 	 .driver = "Audigy2", .name = "Audigy 2 [SB0240]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ca0151_chip = 1,
@@ -659,35 +665,87 @@
 	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
 	 .driver = "Audigy2", .name = "Audigy 2 EX [1005]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ca0151_chip = 1,
 	 .spdif_bug = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
 	 .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]", 
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
 	 .ca0151_chip = 1,
 	 .spk71 = 1,
 	 .spdif_bug = 1,
 	 .ac97_chip = 1} ,
-	{.vendor = 0x1102, .device = 0x0004,
-	 .driver = "Audigy", .name = "Audigy 1 or 2 [Unknown]", 
+	{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
+	 .driver = "Audigy2", .name = "Audigy 2 [Unknown]",
+	 .id = "Audigy2",
 	 .emu10k2_chip = 1,
 	 .ca0102_chip = 1,
-	 .spdif_bug = 1} ,
+	 .ca0151_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10020052,
+	 .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
+	 .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
+	 .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004,
+	 .driver = "Audigy", .name = "Audigy 1 [Unknown]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
 	 .driver = "EMU10K1", .name = "E-mu APS [4001]", 
+	 .id = "APS",
 	 .emu10k1_chip = 1,
 	 .ecard = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+	 .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
 	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
 	 .driver = "EMU10K1", .name = "SB Live 5.1", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
+	 .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", 
+	 .id = "Live",
 	 .emu10k1_chip = 1,
 	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
 	{.vendor = 0x1102, .device = 0x0002,
 	 .driver = "EMU10K1", .name = "SB Live [Unknown]", 
+	 .id = "Live",
 	 .emu10k1_chip = 1,
-	 .ac97_chip = 1} ,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
 	{ } /* terminator */
 };
 
@@ -738,13 +796,15 @@
 	emu->revision = revision;
 	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
 	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
-	emu->card_type = EMU10K1_CARD_CREATIVE;
 	snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
 
 	for (c = emu_chip_details; c->vendor; c++) {
 		if (c->vendor == pci->vendor && c->device == pci->device) {
-			if (c->subsystem == emu->serial) break;
-			if (c->subsystem == 0) break;
+			if (c->subsystem && c->subsystem != emu->serial)
+				continue;
+			if (c->revision && c->revision != emu->revision)
+				continue;
+			break;
 		}
 	}
 	if (c->vendor == 0) {
@@ -759,6 +819,23 @@
 	else
 		snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x\n", c->name, pci->vendor, pci->device, emu->serial);
 	
+	if (!*card->id && c->id) {
+		int i, n = 0;
+		strlcpy(card->id, c->id, sizeof(card->id));
+		for (;;) {
+			for (i = 0; i < snd_ecards_limit; i++) {
+				if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
+					break;
+			}
+			if (i >= snd_ecards_limit)
+				break;
+			n++;
+			if (n >= SNDRV_CARDS)
+				break;
+			snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
+		}
+	}
+
 	is_audigy = emu->audigy = c->emu10k2_chip;
 
 	/* set the DMA transfer mask */
@@ -816,15 +893,6 @@
 
 	pci_set_master(pci);
 
-	if (c->ecard) {
-		emu->card_type = EMU10K1_CARD_EMUAPS;
-		emu->APS = 1;
-	}
-	if (! c->ac97_chip)
-		emu->no_ac97 = 1;
-	
-	emu->spk71 = c->spk71;
-	
 	emu->fx8010.fxbus_mask = 0x303f;
 	if (extin_mask == 0)
 		extin_mask = 0x3fcf;
@@ -833,7 +901,7 @@
 	emu->fx8010.extin_mask = extin_mask;
 	emu->fx8010.extout_mask = extout_mask;
 
-	if (emu->APS) {
+	if (emu->card_capabilities->ecard) {
 		if ((err = snd_emu10k1_ecard_init(emu)) < 0) {
 			snd_emu10k1_free(emu);
 			return err;
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 27dfd8d..f8d9233 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1075,6 +1075,7 @@
 		snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_emu10k1x_proc_reg_write;
+		entry->mode |= S_IWUSR;
 		entry->private_data = emu;
 	}
 	
@@ -1627,7 +1628,7 @@
 {
 	int err;
 
-	if ((err = pci_module_init(&driver)) > 0)
+	if ((err = pci_register_driver(&driver)) > 0)
 		return err;
 
 	return 0;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index b9fa2e8..0529fb2 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1077,7 +1077,7 @@
 	gpr += 2;
 	
 	/* PCM Side Playback (independent from stereo mix) */
-	if (emu->spk71) {
+	if (emu->card_capabilities->spk71) {
 		A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
 		A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
 		snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
@@ -1145,14 +1145,14 @@
 	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
 	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++],
-					emu->no_ac97 ? "CD Playback Volume" : "Audigy CD Playback Volume",
+					emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
 					gpr, 0);
 	gpr += 2;
 	/* Audigy CD Capture Volume */
 	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
 	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++],
-					emu->no_ac97 ? "CD Capture Volume" : "Audigy CD Capture Volume",
+					emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
 					gpr, 0);
 	gpr += 2;
 
@@ -1171,14 +1171,14 @@
 	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
 	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++],
-					emu->no_ac97 ? "Line Playback Volume" : "Line2 Playback Volume",
+					emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
 					gpr, 0);
 	gpr += 2;
 	/* Line2 Capture Volume */
 	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
 	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++],
-					emu->no_ac97 ? "Line Capture Volume" : "Line2 Capture Volume",
+					emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
 					gpr, 0);
 	gpr += 2;
         
@@ -1197,14 +1197,14 @@
 	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
 	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++],
-					emu->no_ac97 ? "Aux Playback Volume" : "Aux2 Playback Volume",
+					emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
 					gpr, 0);
 	gpr += 2;
 	/* Aux2 Capture Volume */
 	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
 	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++],
-					emu->no_ac97 ? "Aux Capture Volume" : "Aux2 Capture Volume",
+					emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
 					gpr, 0);
 	gpr += 2;
 	
@@ -1232,7 +1232,7 @@
 	snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
 	gpr++;
 	
-	if (emu->spk71) {
+	if (emu->card_capabilities->spk71) {
 		/* Stereo Mix Side Playback */
 		A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
 		A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
@@ -1266,7 +1266,7 @@
 	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
 	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
 	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
-	if (emu->spk71) {
+	if (emu->card_capabilities->spk71) {
 		A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
 		A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
 	}
@@ -1359,7 +1359,7 @@
 	A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
 	A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
 	A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
-	if (emu->spk71)
+	if (emu->card_capabilities->spk71)
 		A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
 
 	/* headphone */
@@ -1982,22 +1982,27 @@
 		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000);
 
 	/* EFX capture - capture the 16 EXTINS */
-	OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
-	OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
-	OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
-	OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
-	/* Dont connect anything to FXBUS2 1 and 2.  These are shared with 
-	 * Center/LFE on the SBLive 5.1.  The kX driver only changes the 
-	 * routing when it detects an SBLive 5.1.
-	 *
-	 * Since only 14 of the 16 EXTINs are used, this is not a big problem.  
-	 * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture 
-	 * 0 and 3, then the rest of the EXTINs to the corresponding FX capture 
-	 * channel.
-	 */
-	for (z = 4; z < 14; z++) {
-		OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+	if (emu->card_capabilities->sblive51) {
+		/* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER
+		 * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
+		 *
+		 * Since only 14 of the 16 EXTINs are used, this is not a big problem.  
+		 * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture 
+		 * 0 and 3, then the rest of the EXTINs to the corresponding FX capture 
+		 * channel.  Multitrack recorders will still see the center/lfe output signal 
+		 * on the second and third channels.
+		 */
+		OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
+		OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
+		OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
+		OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
+		for (z = 4; z < 14; z++)
+			OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+	} else {
+		for (z = 0; z < 16; z++)
+			OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
 	}
+	    
 
 	if (gpr > tmp) {
 		snd_BUG();
@@ -2128,7 +2133,6 @@
 	int res;
 
 	memset(info, 0, sizeof(info));
-	info->card = emu->card_type;
 	info->internal_tram_size = emu->fx8010.itram_size;
 	info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
 	fxbus = fxbuses;
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 044663d..6be82c5 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -68,6 +68,7 @@
 	return 0;
 }
 
+#if 0
 static int snd_audigy_spdif_output_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
 	static char *texts[] = {"44100", "48000", "96000"};
@@ -152,6 +153,7 @@
 	.get =          snd_audigy_spdif_output_rate_get,
 	.put =          snd_audigy_spdif_output_rate_put
 };
+#endif
 
 static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol,
                                  snd_ctl_elem_value_t * ucontrol)
@@ -791,7 +793,7 @@
 		NULL
 	};
 
-	if (!emu->no_ac97) {
+	if (emu->card_capabilities->ac97_chip) {
 		ac97_bus_t *pbus;
 		ac97_template_t ac97;
 		static ac97_bus_ops_t ops = {
@@ -833,7 +835,7 @@
 		for (; *c; c++)
 			remove_ctl(card, *c);
 	} else {
-		if (emu->APS)
+		if (emu->card_capabilities->ecard)
 			strcpy(emu->card->mixername, "EMU APS");
 		else if (emu->audigy)
 			strcpy(emu->card->mixername, "SB Audigy");
@@ -918,7 +920,7 @@
 		mix->attn[0] = 0xffff;
 	}
 	
-	if (! emu->APS) { /* FIXME: APS has these controls? */
+	if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
 		/* sb live! and audigy */
 		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
 			return -ENOMEM;
@@ -935,18 +937,20 @@
 			return -ENOMEM;
 		if ((err = snd_ctl_add(card, kctl)))
 			return err;
+#if 0
 		if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
 			return -ENOMEM;
 		if ((err = snd_ctl_add(card, kctl)))
 			return err;
-	} else if (! emu->APS) {
+#endif
+	} else if (! emu->card_capabilities->ecard) {
 		/* sb live! */
 		if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
 			return -ENOMEM;
 		if ((err = snd_ctl_add(card, kctl)))
 			return err;
 	}
-	if (emu->audigy && emu->revision == 4) { /* P16V */
+	if (emu->card_capabilities->ca0151_chip) { /* P16V */
 		if ((err = snd_p16v_mixer(emu)))
 			return err;
 	}
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index d1c2a02..fd7cc38 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -262,7 +262,7 @@
  *
  * returns: cache invalidate size in samples
  */
-static int inline emu10k1_ccis(int stereo, int w_16)
+static inline int emu10k1_ccis(int stereo, int w_16)
 {
 	if (w_16) {
 		return stereo ? 24 : 26;
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index d990d5e..cc22707 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
+#include "p16v.h"
 
 static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu,
 					  snd_info_buffer_t * buffer,
@@ -44,28 +45,34 @@
 	unsigned int status, rate = 0;
 	
 	status = snd_emu10k1_ptr_read(emu, status_reg, 0);
-	if (rate_reg > 0)
-		rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
 
 	snd_iprintf(buffer, "\n%s\n", title);
 
-	snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
-	snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
-	snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
-	snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
-	snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
-	snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
-	snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
-	snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
-	snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
-	snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
-	snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
+	if (status != 0xffffffff) {
+		snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
+		snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
+		snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+		snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
+		snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
+		snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
+		snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
+		snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
+		snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
+		snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
+		snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
 
-	if (rate_reg > 0) {
-		snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
-		snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
-		snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE);
+		if (rate_reg > 0) {
+			rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
+			snd_iprintf(buffer, "S/PDIF Valid          : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
+			snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
+			snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+			/* From ((Rate * 48000 ) / 262144); */
+			snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11); 
+		}
+	} else {
+		snd_iprintf(buffer, "No signal detected.\n");
 	}
+
 }
 
 static void snd_emu10k1_proc_read(snd_info_entry_t *entry, 
@@ -182,7 +189,7 @@
 	
 	snd_iprintf(buffer, "EMU10K1\n\n");
 	snd_iprintf(buffer, "Card                  : %s\n",
-		    emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative"));
+		    emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
 	snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
 	snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
 	snd_iprintf(buffer, "\n");
@@ -223,15 +230,35 @@
 	snd_iprintf(buffer, "\nAll FX Outputs        :\n");
 	for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
 		snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
-	snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1);
-	snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1);
-	snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1);
-	snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS);
-	snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS);
+}
+
+static void snd_emu10k1_proc_spdif_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	emu10k1_t *emu = entry->private_data;
+	snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
+	snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
+#if 0
 	val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
 	snd_iprintf(buffer, "\nZoomed Video\n");
 	snd_iprintf(buffer, "Rate Locked           : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
 	snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+#endif
+}
+
+static void snd_emu10k1_proc_rates_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
+	emu10k1_t *emu = entry->private_data;
+	unsigned int val, tmp, n;
+	val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
+	tmp = (val >> 16) & 0x8;
+	for (n=0;n<4;n++) {
+		tmp = val >> (16 + (n*4));
+		if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
+		else snd_iprintf(buffer, "Channel %d: No input\n", n);
+	}
 }
 
 static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, 
@@ -500,32 +527,46 @@
 		snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_emu_proc_io_reg_write;
+		entry->mode |= S_IWUSR;
 	}
 	if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
 		snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+		entry->mode |= S_IWUSR;
 	}
 	if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
 		snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+		entry->mode |= S_IWUSR;
 	}
 	if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
 		snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+		entry->mode |= S_IWUSR;
 	}
 	if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
 		snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b);
 		entry->c.text.write_size = 64;
 		entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+		entry->mode |= S_IWUSR;
 	}
 #endif
 	
 	if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
 		snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read);
 
+	if (emu->card_capabilities->emu10k2_chip) {
+		if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
+			snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_spdif_read);
+	}
+	if (emu->card_capabilities->ca0151_chip) {
+		if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
+			snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_rates_read);
+	}
+
 	if (! snd_card_proc_new(emu->card, "voices", &entry))
 		snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read);
 
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index b81a7ca..cd8460d 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -37,7 +37,7 @@
 	int handled = 0;
 
 	while ((status = inl(emu->port + IPR)) != 0) {
-		// printk("irq - status = 0x%x\n", status);
+		//printk("emu10k1 irq - status = 0x%x\n", status);
 		orig_status = status;
 		handled = 1;
 		if (status & IPR_PCIERROR) {
@@ -147,9 +147,36 @@
 				snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
 			status &= ~IPR_FXDSP;
 		}
+		if (status & IPR_P16V) {
+			while ((status2 = inl(emu->port + IPR2)) != 0) {
+				u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
+				emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]);
+				emu10k1_voice_t *cvoice = &(emu->p16v_capture_voice);
+
+				//printk(KERN_INFO "status2=0x%x\n", status2);
+				orig_status2 = status2;
+				if(status2 & mask) {
+					if(pvoice->use) {
+						snd_pcm_period_elapsed(pvoice->epcm->substream);
+					} else { 
+						snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
+					}
+				}
+				if(status2 & 0x110000) {
+					//printk(KERN_INFO "capture int found\n");
+					if(cvoice->use) {
+						//printk(KERN_INFO "capture period_elapsed\n");
+						snd_pcm_period_elapsed(cvoice->epcm->substream);
+					}
+				}
+				outl(orig_status2, emu->port + IPR2); /* ack all */
+			}
+			status &= ~IPR_P16V;
+		}
+
 		if (status) {
 			unsigned int bits;
-			//snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
+			snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
 			//make sure any interrupts we don't handle are disabled:
 			bits = INTE_FXDSPENABLE |
 				INTE_PCIERRORENABLE |
@@ -170,20 +197,5 @@
 		}
 		outl(orig_status, emu->port + IPR); /* ack all */
 	}
-	if (emu->audigy && emu->revision == 4) { /* P16V */	
-		while ((status2 = inl(emu->port + IPR2)) != 0) {
-			u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
-			emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]);
-			orig_status2 = status2;
-			if(status2 & mask) {
-				if(pvoice->use) {
-					snd_pcm_period_elapsed(pvoice->epcm->substream);
-				} else { 
-					snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
-				}
-			}
-			outl(orig_status2, emu->port + IPR2); /* ack all */
-		}
-	}
 	return IRQ_RETVAL(handled);
 }
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index d03cb2f..98f9801 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver p16v chips
- *  Version: 0.22
+ *  Version: 0.25
  *
  *  FEATURES currently supported:
  *    Output fixed at S32_LE, 2 channel to hw:0,0
@@ -41,7 +41,15 @@
  *    Integrated with snd-emu10k1 driver.
  *  0.22
  *    Removed #if 0 ... #endif
- *
+ *  0.23
+ *    Implement different capture rates.
+ *  0.24
+ *    Implement different capture source channels.
+ *    e.g. When HD Capture source is set to SPDIF,
+ *    setting HD Capture channel to 0 captures from CDROM digital input.
+ *    setting HD Capture channel to 1 captures from SPDIF in.
+ *  0.25
+ *    Include capture buffer sizes.
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-p16v kernel module.
@@ -119,22 +127,41 @@
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
 	.formats =		SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
-	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 ,
-	.rate_min =		48000,
+	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+	.rate_min =		44100,
 	.rate_max =		192000,
 	.channels_min =		8, 
 	.channels_max =		8,
-	.buffer_bytes_max =	(32*1024),
+	.buffer_bytes_max =	((65536 - 64) * 8),
 	.period_bytes_min =	64,
-	.period_bytes_max =	(16*1024),
+	.period_bytes_max =	(65536 - 64),
 	.periods_min =		2,
 	.periods_max =		8,
 	.fifo_size =		0,
 };
 
+static snd_pcm_hardware_t snd_p16v_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(65536 - 64),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 128) >> 1,  /* size has to be N*64 bytes */
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
 static void snd_p16v_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-	snd_pcm_t *epcm = runtime->private_data;
+	emu10k1_pcm_t *epcm = runtime->private_data;
   
 	if (epcm) {
         	//snd_printk("epcm free: %p\n", epcm);
@@ -178,15 +205,63 @@
 
 	return 0;
 }
+/* open_capture callback */
+static int snd_p16v_pcm_open_capture_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	emu10k1_voice_t *channel = &(emu->p16v_capture_voice);
+	emu10k1_pcm_t *epcm;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+	//snd_printk("epcm kcalloc: %p\n", epcm);
+
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->substream = substream;
+	//snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
+
+	runtime->private_data = epcm;
+	runtime->private_free = snd_p16v_pcm_free_substream;
+  
+	runtime->hw = snd_p16v_capture_hw;
+
+	channel->emu = emu;
+	channel->number = channel_id;
+
+	channel->use=1;
+	//snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
+	//printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+	//channel->interrupt = snd_p16v_pcm_channel_interrupt;
+	channel->epcm=epcm;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+
+	return 0;
+}
+
 
 /* close callback */
 static int snd_p16v_pcm_close_playback(snd_pcm_substream_t *substream)
 {
 	emu10k1_t *emu = snd_pcm_substream_chip(substream);
 	//snd_pcm_runtime_t *runtime = substream->runtime;
-        //emu10k1_pcm_t *epcm = runtime->private_data;
-        emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
-/* FIXME: maybe zero others */
+	//emu10k1_pcm_t *epcm = runtime->private_data;
+	emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+/* close callback */
+static int snd_p16v_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	//snd_pcm_runtime_t *runtime = substream->runtime;
+	//emu10k1_pcm_t *epcm = runtime->private_data;
+	emu->p16v_capture_voice.use=0;
+	/* FIXME: maybe zero others */
 	return 0;
 }
 
@@ -195,36 +270,55 @@
 	return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
 }
 
+static int snd_p16v_pcm_open_capture(snd_pcm_substream_t *substream)
+{
+	// Only using channel 0 for now, but the card has 2 channels.
+	return snd_p16v_pcm_open_capture_channel(substream, 0);
+}
+
 /* hw_params callback */
 static int snd_p16v_pcm_hw_params_playback(snd_pcm_substream_t *substream,
 				      snd_pcm_hw_params_t * hw_params)
 {
 	int result;
-        //snd_printk("hw_params alloc: substream=%p\n", substream);
 	result = snd_pcm_lib_malloc_pages(substream,
 					params_buffer_bytes(hw_params));
-        //snd_printk("hw_params alloc: result=%d\n", result);
-	//dump_stack();
 	return result;
 }
 
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+				      snd_pcm_hw_params_t * hw_params)
+{
+	int result;
+	result = snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+	return result;
+}
+
+
 /* hw_free callback */
 static int snd_p16v_pcm_hw_free_playback(snd_pcm_substream_t *substream)
 {
 	int result;
-        //snd_printk("hw_params free: substream=%p\n", substream);
 	result = snd_pcm_lib_free_pages(substream);
-        //snd_printk("hw_params free: result=%d\n", result);
-	//dump_stack();
 	return result;
 }
 
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+	int result;
+	result = snd_pcm_lib_free_pages(substream);
+	return result;
+}
+
+
 /* prepare playback callback */
 static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
 {
 	emu10k1_t *emu = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	//emu10k1_pcm_t *epcm = runtime->private_data;
 	int channel = substream->pcm->device - emu->p16v_device_offset;
 	u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
 	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
@@ -237,23 +331,21 @@
 	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
         switch (runtime->rate) {
 	case 44100:
-	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x8000); /* FIXME: This will change the capture rate as well! */
-	  break;
-	case 48000:
-	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x0000); /* FIXME: This will change the capture rate as well! */
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
 	  break;
 	case 96000:
-	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x4000); /* FIXME: This will change the capture rate as well! */
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
 	  break;
 	case 192000:
-	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x2000); /* FIXME: This will change the capture rate as well! */
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
 	  break;
+	case 48000:
 	default:
-	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, 0x0000); /* FIXME: This will change the capture rate as well! */
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
 	  break;
 	}
 	/* FIXME: Check emu->buffer.size before actually writing to it. */
-        for(i=0; i < runtime->periods; i++) {
+	for(i=0; i < runtime->periods; i++) {
 		table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
 		table_base[(i*2)+1]=period_size_bytes<<16;
 	}
@@ -262,7 +354,8 @@
 	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
 	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
 	snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
-	snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+	//snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
 	snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
 	snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
 	snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
@@ -270,6 +363,41 @@
 	return 0;
 }
 
+/* prepare capture callback */
+static int snd_p16v_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int channel = substream->pcm->device - emu->p16v_device_offset;
+	u32 tmp;
+	//printk("prepare capture:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
+	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+        switch (runtime->rate) {
+	case 44100:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
+	  break;
+	case 96000:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
+	  break;
+	case 192000:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
+	  break;
+	case 48000:
+	default:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
+	  break;
+	}
+	/* FIXME: Check emu->buffer.size before actually writing to it. */
+	snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
+	snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+	snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+	snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
+	//snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
+	//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+
+	return 0;
+}
+
 static void snd_p16v_intr_enable(emu10k1_t *emu, unsigned int intrenb)
 {
 	unsigned long flags;
@@ -345,6 +473,36 @@
 	return result;
 }
 
+/* trigger_capture callback */
+static int snd_p16v_pcm_trigger_capture(snd_pcm_substream_t *substream,
+                                   int cmd)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = runtime->private_data;
+	int channel = 0;
+	int result = 0;
+	u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_p16v_intr_enable(emu, inte);
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+		snd_p16v_intr_disable(emu, inte);
+		//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+		epcm->running = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
 /* pointer_playback callback */
 static snd_pcm_uframes_t
 snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream)
@@ -370,6 +528,31 @@
 	return ptr;
 }
 
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+	emu10k1_t *emu = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	emu10k1_pcm_t *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+	int channel = 0;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr=ptr2;
+	if (ptr >= runtime->buffer_size) {
+		ptr -= runtime->buffer_size;
+		printk("buffer capture limited!\n");
+	}
+	//printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+	return ptr;
+}
+
 /* operators */
 static snd_pcm_ops_t snd_p16v_playback_front_ops = {
 	.open =        snd_p16v_pcm_open_playback_front,
@@ -382,6 +565,18 @@
 	.pointer =     snd_p16v_pcm_pointer_playback,
 };
 
+static snd_pcm_ops_t snd_p16v_capture_ops = {
+	.open =        snd_p16v_pcm_open_capture,
+	.close =       snd_p16v_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_p16v_pcm_hw_params_capture,
+	.hw_free =     snd_p16v_pcm_hw_free_capture,
+	.prepare =     snd_p16v_pcm_prepare_capture,
+	.trigger =     snd_p16v_pcm_trigger_capture,
+	.pointer =     snd_p16v_pcm_pointer_capture,
+};
+
+
 int snd_p16v_free(emu10k1_t *chip)
 {
 	// release the data
@@ -405,20 +600,22 @@
 	snd_pcm_t *pcm;
 	snd_pcm_substream_t *substream;
 	int err;
-        int capture=0;
+        int capture=1;
   
 	//snd_printk("snd_p16v_pcm called. device=%d\n", device);
 	emu->p16v_device_offset = device;
 	if (rpcm)
 		*rpcm = NULL;
-        //if (device == 0) capture=1; 
+
 	if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
 		return err;
   
 	pcm->private_data = emu;
 	pcm->private_free = snd_p16v_pcm_free;
-
+	// Single playback 8 channel device.
+	// Single capture 2 channel device.
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops);
 
 	pcm->info_flags = 0;
 	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
@@ -431,7 +628,7 @@
 		if ((err = snd_pcm_lib_preallocate_pages(substream, 
 							 SNDRV_DMA_TYPE_DEV, 
 							 snd_dma_pci_data(emu->pci), 
-							 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+							 ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0) 
 			return err;
 		//snd_printk("preallocate playback substream: err=%d\n", err);
 	}
@@ -442,7 +639,7 @@
  		if ((err = snd_pcm_lib_preallocate_pages(substream, 
 	                                           SNDRV_DMA_TYPE_DEV, 
 	                                           snd_dma_pci_data(emu->pci), 
-	                                           64*1024, 64*1024)) < 0)
+	                                           65536 - 64, 65536 - 64)) < 0)
 			return err;
 		//snd_printk("preallocate capture substream: err=%d\n", err);
 	}
@@ -694,6 +891,106 @@
         .put =          snd_p16v_volume_put_spdif_rear
 };
 
+static int snd_p16v_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[8] = { "SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S", "CDIF", "FX", "AC97" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+                uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_p16v_capture_source_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->p16v_capture_source;
+	return 0;
+}
+
+static int snd_p16v_capture_source_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 mask;
+	u32 source;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	change = (emu->p16v_capture_source != val);
+	if (change) {
+		emu->p16v_capture_source = val;
+		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+		mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff;
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask);
+	}
+        return change;
+}
+
+static snd_kcontrol_new_t snd_p16v_capture_source __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"HD Capture source",
+	.info =		snd_p16v_capture_source_info,
+	.get =		snd_p16v_capture_source_get,
+	.put =		snd_p16v_capture_source_put
+};
+
+static int snd_p16v_capture_channel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = { "0", "1", "2", "3",  };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+                uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_p16v_capture_channel_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel;
+	return 0;
+}
+
+static int snd_p16v_capture_channel_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 tmp;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	change = (emu->p16v_capture_channel != val);
+	if (change) {
+		emu->p16v_capture_channel = val;
+		tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc;
+		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val);
+	}
+        return change;
+}
+
+static snd_kcontrol_new_t snd_p16v_capture_channel __devinitdata =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"HD Capture channel",
+	.info =		snd_p16v_capture_channel_info,
+	.get =		snd_p16v_capture_channel_get,
+	.put =		snd_p16v_capture_channel_put
+};
+
 int snd_p16v_mixer(emu10k1_t *emu)
 {
         int err;
@@ -731,6 +1028,14 @@
                 return -ENOMEM;
         if ((err = snd_ctl_add(card, kctl)))
                 return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_capture_source, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_capture_channel, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
         return 0;
 }
 
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index f910399..4e63498 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -2401,7 +2401,7 @@
 	
 static int __init alsa_card_ens137x_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ens137x_exit(void)
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index b4ca8ad..b492777 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1761,7 +1761,7 @@
 
 static int __init alsa_card_es1938_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_es1938_exit(void)
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index faf63ff..ea889b3 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2559,6 +2559,7 @@
 	{ TYPE_MAESTRO2E, 0x103c },
 	{ TYPE_MAESTRO2E, 0x1179 },
 	{ TYPE_MAESTRO2E, 0x14c0 },	/* HP omnibook 4150 */
+	{ TYPE_MAESTRO2E, 0x1558 },
 };
 
 static struct ess_device_list mpu_blacklist[] __devinitdata = {
@@ -2795,7 +2796,7 @@
 
 static int __init alsa_card_es1968_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_es1968_exit(void)
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 08e7c5a..ff10e63 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -195,6 +195,7 @@
 
 static struct pci_device_id snd_fm801_ids[] = {
 	{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
+	{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* Gallant Odyssey Sound 4 */
 	{ 0, }
 };
 
@@ -1468,7 +1469,7 @@
 
 static int __init alsa_card_fm801_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_fm801_exit(void)
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 570a59d..bd8cb33 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,5 +1,5 @@
 snd-hda-intel-objs := hda_intel.o
-snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o
+snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o
 ifdef CONFIG_PROC_FS
 snd-hda-codec-objs += hda_proc.o
 endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 9ed117a..e6efaed 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -49,8 +49,10 @@
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
 	{ 0x10ec, "Realtek" },
+	{ 0x11d4, "Analog Devices" },
 	{ 0x13f6, "C-Media" },
 	{ 0x434d, "C-Media" },
+	{ 0x8384, "SigmaTel" },
 	{} /* terminator */
 };
 
@@ -508,7 +510,7 @@
 	/* FIXME: support for multiple AFGs? */
 	codec->afg = look_for_afg_node(codec);
 	if (! codec->afg) {
-		snd_printk(KERN_ERR "hda_codec: no AFG node found\n");
+		snd_printdd("hda_codec: no AFG node found\n");
 		snd_hda_codec_free(codec);
 		return -ENODEV;
 	}
@@ -548,6 +550,9 @@
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
 				int channel_id, int format)
 {
+	if (! nid)
+		return;
+
 	snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
 		    nid, stream_tag, channel_id, format);
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,
@@ -658,7 +663,7 @@
 /*
  * read/write AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
  */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
+static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
 {
 	struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
 	if (! info)
@@ -667,7 +672,7 @@
 	return info->vol[ch];
 }
 
-int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val)
+static int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val)
 {
 	struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
 	if (! info)
@@ -1448,10 +1453,6 @@
 		snd_assert(info->nid, return -EINVAL);
 		info->ops.prepare = hda_pcm_default_prepare;
 	}
-	if (info->ops.prepare == NULL) {
-		snd_assert(info->nid, return -EINVAL);
-		info->ops.prepare = hda_pcm_default_prepare;
-	}
 	if (info->ops.cleanup == NULL) {
 		snd_assert(info->nid, return -EINVAL);
 		info->ops.cleanup = hda_pcm_default_cleanup;
@@ -1530,7 +1531,7 @@
 	struct hda_board_config *c;
 
 	if (codec->bus->modelname) {
-		for (c = tbl; c->modelname || c->pci_vendor; c++) {
+		for (c = tbl; c->modelname || c->pci_subvendor; c++) {
 			if (c->modelname &&
 			    ! strcmp(codec->bus->modelname, c->modelname)) {
 				snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname);
@@ -1543,9 +1544,9 @@
 		u16 subsystem_vendor, subsystem_device;
 		pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
 		pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device);
-		for (c = tbl; c->modelname || c->pci_vendor; c++) {
-			if (c->pci_vendor == subsystem_vendor &&
-			    c->pci_device == subsystem_device)
+		for (c = tbl; c->modelname || c->pci_subvendor; c++) {
+			if (c->pci_subvendor == subsystem_vendor &&
+			    c->pci_subdevice == subsystem_device)
 				return c->config;
 		}
 	}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index c9e9dc9..1b12035 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -176,16 +176,15 @@
 #define AC_PINCAP_OUT			(1<<4)	/* output capable */
 #define AC_PINCAP_IN			(1<<5)	/* input capable */
 #define AC_PINCAP_BALANCE		(1<<6)	/* balanced I/O capable */
-#define AC_PINCAP_VREF			(7<<8)
+#define AC_PINCAP_VREF			(0x37<<8)
 #define AC_PINCAP_VREF_SHIFT		8
 #define AC_PINCAP_EAPD			(1<<16)	/* EAPD capable */
-/* Vref status (used in pin cap and pin ctl) */
-#define AC_PIN_VREF_HIZ			(1<<0)	/* Hi-Z */
-#define AC_PIN_VREF_50			(1<<1)	/* 50% */
-#define AC_PIN_VREF_GRD			(1<<2)	/* ground */
-#define AC_PIN_VREF_80			(1<<4)	/* 80% */
-#define AC_PIN_VREF_100			(1<<5)	/* 100% */
-
+/* Vref status (used in pin cap) */
+#define AC_PINCAP_VREF_HIZ		(1<<0)	/* Hi-Z */
+#define AC_PINCAP_VREF_50		(1<<1)	/* 50% */
+#define AC_PINCAP_VREF_GRD		(1<<2)	/* ground */
+#define AC_PINCAP_VREF_80		(1<<4)	/* 80% */
+#define AC_PINCAP_VREF_100		(1<<5)	/* 100% */
 
 /* Amplifier capabilities */
 #define AC_AMPCAP_OFFSET		(0x7f<<0)  /* 0dB offset */
@@ -248,6 +247,11 @@
 
 /* Pin widget control - 8bit */
 #define AC_PINCTL_VREFEN		(0x7<<0)
+#define AC_PINCTL_VREF_HIZ		0	/* Hi-Z */
+#define AC_PINCTL_VREF_50		1	/* 50% */
+#define AC_PINCTL_VREF_GRD		2	/* ground */
+#define AC_PINCTL_VREF_80		4	/* 80% */
+#define AC_PINCTL_VREF_100		5	/* 100% */
 #define AC_PINCTL_IN_EN			(1<<5)
 #define AC_PINCTL_OUT_EN		(1<<6)
 #define AC_PINCTL_HP_EN			(1<<7)
@@ -255,7 +259,9 @@
 /* configuration default - 32bit */
 #define AC_DEFCFG_SEQUENCE		(0xf<<0)
 #define AC_DEFCFG_DEF_ASSOC		(0xf<<4)
+#define AC_DEFCFG_ASSOC_SHIFT		4
 #define AC_DEFCFG_MISC			(0xf<<8)
+#define AC_DEFCFG_MISC_SHIFT		8
 #define AC_DEFCFG_COLOR			(0xf<<12)
 #define AC_DEFCFG_COLOR_SHIFT		12
 #define AC_DEFCFG_CONN_TYPE		(0xf<<16)
@@ -413,7 +419,7 @@
 
 	/* codec linked list */
 	struct list_head codec_list;
-	struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS]; /* caddr -> codec */
+	struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; /* caddr -> codec */
 
 	struct semaphore cmd_mutex;
 
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 69f7b6c..bfbeff2 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -44,7 +44,7 @@
 	struct list_head list;
 };
 
-/* pathc-specific record */
+/* patch-specific record */
 struct hda_gspec {
 	struct hda_gnode *dac_node;	/* DAC node */
 	struct hda_gnode *out_pin_node;	/* Output pin (Line-Out) node */
@@ -426,7 +426,7 @@
 		return "Line";
 	case AC_JACK_CD:
 		if (pinctl)
-			*pinctl |= AC_PIN_VREF_GRD;
+			*pinctl |= AC_PINCTL_VREF_GRD;
 		return "CD";
 	case AC_JACK_AUX:
 		if ((location & 0x0f) == AC_JACK_LOC_FRONT)
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 959953c..f05a638 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -51,6 +51,7 @@
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
 static char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS];
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -60,12 +61,17 @@
 MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
 module_param_array(model, charp, NULL, 0444);
 MODULE_PARM_DESC(model, "Use the given board model.");
+module_param_array(position_fix, int, NULL, 0444);
+MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = FIFO size, 1 = none, 2 = POSBUF).");
 
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
 			 "{Intel, ICH6M},"
 			 "{Intel, ICH7},"
-			 "{Intel, ESB2}}");
+			 "{Intel, ESB2},"
+			 "{ATI, SB450},"
+			 "{VIA, VT8251},"
+			 "{VIA, VT8237A}}");
 MODULE_DESCRIPTION("Intel HDA driver");
 
 #define SFX	"hda-intel: "
@@ -150,7 +156,7 @@
 
 /* STATESTS int mask: SD2,SD1,SD0 */
 #define STATESTS_INT_MASK	0x07
-#define AZX_MAX_CODECS		3
+#define AZX_MAX_CODECS		4
 
 /* SD_CTL bits */
 #define SD_CTL_STREAM_RESET	0x01	/* stream reset bit */
@@ -183,6 +189,18 @@
 #define ICH6_MAX_CORB_ENTRIES	256
 #define ICH6_MAX_RIRB_ENTRIES	256
 
+/* position fix mode */
+enum {
+	POS_FIX_FIFO,
+	POS_FIX_NONE,
+	POS_FIX_POSBUF
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_PCI_DEVICE_ID     0x437b
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR   0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP      0x02
+
 
 /*
  * Use CORB/RIRB for communication from/to codecs.
@@ -191,12 +209,6 @@
 #define USE_CORB_RIRB
 
 /*
- * Define this if use the position buffer instead of reading SD_LPIB
- * It's not used as default since SD_LPIB seems to give more accurate position
- */
-/* #define USE_POSBUF */
-
-/*
  */
 
 typedef struct snd_azx azx_t;
@@ -271,6 +283,9 @@
 	struct snd_dma_buffer bdl;
 	struct snd_dma_buffer rb;
 	struct snd_dma_buffer posbuf;
+
+	/* flags */
+	int position_fix;
 };
 
 /*
@@ -638,7 +653,7 @@
  */
 static void azx_init_chip(azx_t *chip)
 {
-	unsigned char tcsel_reg;
+	unsigned char tcsel_reg, ati_misc_cntl2;
 
 	/* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
 	 * TCSEL == Traffic Class Select Register, which sets PCI express QOS
@@ -657,11 +672,20 @@
 	/* initialize the codec command I/O */
 	azx_init_cmd_io(chip);
 
-#ifdef USE_POSBUF
-	/* program the position buffer */
-	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
-	azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
-#endif
+	if (chip->position_fix == POS_FIX_POSBUF) {
+		/* program the position buffer */
+		azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
+		azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+	}
+
+	/* For ATI SB450 azalia HD audio, we need to enable snoop */
+	if (chip->pci->vendor == PCI_VENDOR_ID_ATI && 
+	    chip->pci->device == ATI_SB450_HDAUDIO_PCI_DEVICE_ID) {
+		pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+				     &ati_misc_cntl2);
+		pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+				      (ati_misc_cntl2 & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP);
+	}
 }
 
 
@@ -791,11 +815,12 @@
 	/* upper BDL address */
 	azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
 
-#ifdef USE_POSBUF
-	/* enable the position buffer */
-	if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
-		azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
-#endif
+	if (chip->position_fix == POS_FIX_POSBUF) {
+		/* enable the position buffer */
+		if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+			azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+	}
+
 	/* set the interrupt enable bits in the descriptor control register */
 	azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
 
@@ -1036,16 +1061,20 @@
 
 static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
 {
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	azx_t *chip = apcm->chip;
 	azx_dev_t *azx_dev = get_azx_dev(substream);
 	unsigned int pos;
 
-#ifdef USE_POSBUF
-	/* use the position buffer */
-	pos = *azx_dev->posbuf;
-#else
-	/* read LPIB */
-	pos = azx_sd_readl(azx_dev, SD_LPIB) + azx_dev->fifo_size;
-#endif
+	if (chip->position_fix == POS_FIX_POSBUF) {
+		/* use the position buffer */
+		pos = *azx_dev->posbuf;
+	} else {
+		/* read LPIB */
+		pos = azx_sd_readl(azx_dev, SD_LPIB);
+		if (chip->position_fix == POS_FIX_FIFO)
+			pos += azx_dev->fifo_size;
+	}
 	if (pos >= azx_dev->bufsize)
 		pos = 0;
 	return bytes_to_frames(substream->runtime, pos);
@@ -1155,9 +1184,8 @@
 		azx_dev_t *azx_dev = &chip->azx_dev[i];
 		azx_dev->bdl = (u32 *)(chip->bdl.area + off);
 		azx_dev->bdl_addr = chip->bdl.addr + off;
-#ifdef USE_POSBUF
-		azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
-#endif
+		if (chip->position_fix == POS_FIX_POSBUF)
+			azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
 		/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
 		azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
 		/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
@@ -1237,10 +1265,8 @@
 		snd_dma_free_pages(&chip->bdl);
 	if (chip->rb.area)
 		snd_dma_free_pages(&chip->rb);
-#ifdef USE_POSBUF
 	if (chip->posbuf.area)
 		snd_dma_free_pages(&chip->posbuf);
-#endif
 	pci_release_regions(chip->pci);
 	pci_disable_device(chip->pci);
 	kfree(chip);
@@ -1256,7 +1282,8 @@
 /*
  * constructor
  */
-static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **rchip)
+static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci,
+				int posfix, azx_t **rchip)
 {
 	azx_t *chip;
 	int err = 0;
@@ -1283,6 +1310,8 @@
 	chip->pci = pci;
 	chip->irq = -1;
 
+	chip->position_fix = posfix;
+
 	if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) {
 		kfree(chip);
 		pci_disable_device(pci);
@@ -1314,14 +1343,14 @@
 		snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
 		goto errout;
 	}
-#ifdef USE_POSBUF
-	/* allocate memory for the position buffer */
-	if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
-				       MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) {
-		snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
-		goto errout;
+	if (chip->position_fix == POS_FIX_POSBUF) {
+		/* allocate memory for the position buffer */
+		if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+					       MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) {
+			snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
+			goto errout;
+		}
 	}
-#endif
 	/* allocate CORB/RIRB */
 	if ((err = azx_alloc_cmd_io(chip)) < 0)
 		goto errout;
@@ -1372,7 +1401,7 @@
 		return -ENOMEM;
 	}
 
-	if ((err = azx_create(card, pci, &chip)) < 0) {
+	if ((err = azx_create(card, pci, position_fix[dev], &chip)) < 0) {
 		snd_card_free(card);
 		return err;
 	}
@@ -1424,6 +1453,8 @@
 	{ 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */
 	{ 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */
 	{ 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ESB2 */
+	{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ATI SB450 */
+	{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* VIA VT8251/VT8237A */
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
@@ -1439,7 +1470,7 @@
 
 static int __init alsa_card_azx_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_azx_exit(void)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 7c7b849..b8fbbc4 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -126,8 +126,8 @@
 struct hda_board_config {
 	const char *modelname;
 	int config;
-	unsigned short pci_vendor;
-	unsigned short pci_device;
+	unsigned short pci_subvendor;
+	unsigned short pci_subdevice;
 };
 
 int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl);
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
index cf6abce..a5de684 100644
--- a/sound/pci/hda/hda_patch.h
+++ b/sound/pci/hda/hda_patch.h
@@ -8,10 +8,13 @@
 extern struct hda_codec_preset snd_hda_preset_cmedia[];
 /* Analog Devices codecs */
 extern struct hda_codec_preset snd_hda_preset_analog[];
+/* SigmaTel codecs */
+extern struct hda_codec_preset snd_hda_preset_sigmatel[];
 
 static const struct hda_codec_preset *hda_preset_tables[] = {
 	snd_hda_preset_realtek,
 	snd_hda_preset_cmedia,
 	snd_hda_preset_analog,
+	snd_hda_preset_sigmatel,
 	NULL
 };
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 4d5db7f..15df716 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -157,6 +157,7 @@
 static void print_pin_caps(snd_info_buffer_t *buffer,
 			   struct hda_codec *codec, hda_nid_t nid)
 {
+	static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
 	static char *jack_types[16] = {
 		"Line Out", "Speaker", "HP Out", "CD",
 		"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
@@ -176,7 +177,8 @@
 		snd_iprintf(buffer, " HP");
 	snd_iprintf(buffer, "\n");
 	caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-	snd_iprintf(buffer, "  Pin Default 0x%08x: %s at %s %s\n", caps,
+	snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
+		    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
 		    jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
 		    jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
 		    get_jack_location(caps));
@@ -266,13 +268,19 @@
 
 		if (wid_caps & AC_WCAP_CONN_LIST) {
 			hda_nid_t conn[HDA_MAX_CONNECTIONS];
-			int c, conn_len;
+			int c, conn_len, curr = -1;
 			conn_len = snd_hda_get_connections(codec, nid, conn,
 							   HDA_MAX_CONNECTIONS);
+			if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+				curr = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONNECT_SEL, 0);
 			snd_iprintf(buffer, "  Connection: %d\n", conn_len);
 			snd_iprintf(buffer, "    ");
-			for (c = 0; c < conn_len; c++)
+			for (c = 0; c < conn_len; c++) {
 				snd_iprintf(buffer, " 0x%02x", conn[c]);
+				if (c == curr)
+					snd_iprintf(buffer, "*");
+			}
 			snd_iprintf(buffer, "\n");
 		}
 	}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 75d2384..caa4869 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1,5 +1,5 @@
 /*
- * HD audio interface patch for AD1986A
+ * HD audio interface patch for AD1981HD, AD1983, AD1986A
  *
  * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
  *
@@ -27,13 +27,239 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 
-struct ad1986a_spec {
+struct ad198x_spec {
 	struct semaphore amp_mutex;	/* PCM volume/mute control mutex */
 	struct hda_multi_out multiout;	/* playback */
+	hda_nid_t adc_nid;
+	const struct hda_input_mux *input_mux;
 	unsigned int cur_mux;		/* capture source */
+	unsigned int spdif_route;
+	snd_kcontrol_new_t *mixers;
+	const struct hda_verb *init_verbs;
 	struct hda_pcm pcm_rec[2];	/* PCM information */
 };
 
+/*
+ * input MUX handling (common part)
+ */
+static int ad198x_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int ad198x_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux;
+	return 0;
+}
+
+static int ad198x_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+				     spec->adc_nid, &spec->cur_mux);
+}
+
+/*
+ * initialization (common callbacks)
+ */
+static int ad198x_init(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	snd_hda_sequence_write(codec, spec->init_verbs);
+	return 0;
+}
+
+static int ad198x_build_controls(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_add_new_ctls(codec, spec->mixers);
+	if (err < 0)
+		return err;
+	if (spec->multiout.dig_out_nid)
+		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+						format, substream);
+}
+
+static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	snd_hda_codec_setup_stream(codec, spec->adc_nid, stream_tag, 0, format);
+	return 0;
+}
+
+static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      snd_pcm_substream_t *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	snd_hda_codec_setup_stream(codec, spec->adc_nid, 0, 0, 0);
+	return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream ad198x_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 6,
+	.nid = 0, /* fill later */
+	.ops = {
+		.open = ad198x_playback_pcm_open,
+		.prepare = ad198x_playback_pcm_prepare,
+		.cleanup = ad198x_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream ad198x_pcm_analog_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.prepare = ad198x_capture_pcm_prepare,
+		.cleanup = ad198x_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream ad198x_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.open = ad198x_dig_playback_pcm_open,
+		.close = ad198x_dig_playback_pcm_close
+	},
+};
+
+static int ad198x_build_pcms(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "AD198x Analog";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nid;
+
+	if (spec->multiout.dig_out_nid) {
+		info++;
+		codec->num_pcms++;
+		info->name = "AD198x Digital";
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+	}
+
+	return 0;
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+#ifdef CONFIG_PM
+static int ad198x_resume(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+
+	ad198x_init(codec);
+	snd_hda_resume_ctls(codec, spec->mixers);
+	snd_hda_resume_spdif_out(codec);
+	return 0;
+}
+#endif
+
+static struct hda_codec_ops ad198x_patch_ops = {
+	.build_controls = ad198x_build_controls,
+	.build_pcms = ad198x_build_pcms,
+	.init = ad198x_init,
+	.free = ad198x_free,
+#ifdef CONFIG_PM
+	.resume = ad198x_resume,
+#endif
+};
+
+
+/*
+ * AD1986A specific
+ */
+
 #define AD1986A_SPDIF_OUT	0x02
 #define AD1986A_FRONT_DAC	0x03
 #define AD1986A_SURR_DAC	0x04
@@ -68,7 +294,7 @@
 static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct ad1986a_spec *ad = codec->spec;
+	struct ad198x_spec *ad = codec->spec;
 
 	down(&ad->amp_mutex);
 	snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
@@ -79,7 +305,7 @@
 static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct ad1986a_spec *ad = codec->spec;
+	struct ad198x_spec *ad = codec->spec;
 	int i, change = 0;
 
 	down(&ad->amp_mutex);
@@ -97,7 +323,7 @@
 static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct ad1986a_spec *ad = codec->spec;
+	struct ad198x_spec *ad = codec->spec;
 
 	down(&ad->amp_mutex);
 	snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
@@ -108,7 +334,7 @@
 static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct ad1986a_spec *ad = codec->spec;
+	struct ad198x_spec *ad = codec->spec;
 	int i, change = 0;
 
 	down(&ad->amp_mutex);
@@ -122,32 +348,6 @@
 }
 
 /*
- * input MUX handling
- */
-static int ad1986a_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
-{
-	return snd_hda_input_mux_info(&ad1986a_capture_source, uinfo);
-}
-
-static int ad1986a_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct ad1986a_spec *spec = codec->spec;
-
-	ucontrol->value.enumerated.item[0] = spec->cur_mux;
-	return 0;
-}
-
-static int ad1986a_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct ad1986a_spec *spec = codec->spec;
-
-	return snd_hda_input_mux_put(codec, &ad1986a_capture_source, ucontrol,
-				     AD1986A_ADC, &spec->cur_mux);
-}
-
-/*
  * mixers
  */
 static snd_kcontrol_new_t ad1986a_mixers[] = {
@@ -194,9 +394,9 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Capture Source",
-		.info = ad1986a_mux_enum_info,
-		.get = ad1986a_mux_enum_get,
-		.put = ad1986a_mux_enum_put,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
 	},
 	HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
 	{ } /* end */
@@ -241,183 +441,29 @@
 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
 	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* HP Pin */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* Front, Surround, CLFE Pins */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Mono Pin */
+	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Mic Pin */
+	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* Line, Aux, CD, Beep-In Pin */
+	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
 	{ } /* end */
 };
 
 
-static int ad1986a_init(struct hda_codec *codec)
-{
-	snd_hda_sequence_write(codec, ad1986a_init_verbs);
-	return 0;
-}
-
-static int ad1986a_build_controls(struct hda_codec *codec)
-{
-	int err;
-
-	err = snd_hda_add_new_ctls(codec, ad1986a_mixers);
-	if (err < 0)
-		return err;
-	err = snd_hda_create_spdif_out_ctls(codec, AD1986A_SPDIF_OUT);
-	if (err < 0)
-		return err;
-	return 0;
-}
-
-/*
- * Analog playback callbacks
- */
-static int ad1986a_playback_pcm_open(struct hda_pcm_stream *hinfo,
-				     struct hda_codec *codec,
-				     snd_pcm_substream_t *substream)
-{
-	struct ad1986a_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
-}
-
-static int ad1986a_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					unsigned int stream_tag,
-					unsigned int format,
-					snd_pcm_substream_t *substream)
-{
-	struct ad1986a_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
-						format, substream);
-}
-
-static int ad1986a_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					snd_pcm_substream_t *substream)
-{
-	struct ad1986a_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ad1986a_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-					 struct hda_codec *codec,
-					 snd_pcm_substream_t *substream)
-{
-	struct ad1986a_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ad1986a_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-					  struct hda_codec *codec,
-					  snd_pcm_substream_t *substream)
-{
-	struct ad1986a_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ad1986a_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
-				       struct hda_codec *codec,
-				       unsigned int stream_tag,
-				       unsigned int format,
-				       snd_pcm_substream_t *substream)
-{
-	snd_hda_codec_setup_stream(codec, AD1986A_ADC, stream_tag, 0, format);
-	return 0;
-}
-
-static int ad1986a_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
-				       struct hda_codec *codec,
-				       snd_pcm_substream_t *substream)
-{
-	snd_hda_codec_setup_stream(codec, AD1986A_ADC, 0, 0, 0);
-	return 0;
-}
-
-
-/*
- */
-static struct hda_pcm_stream ad1986a_pcm_analog_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 6,
-	.nid = AD1986A_FRONT_DAC, /* NID to query formats and rates */
-	.ops = {
-		.open = ad1986a_playback_pcm_open,
-		.prepare = ad1986a_playback_pcm_prepare,
-		.cleanup = ad1986a_playback_pcm_cleanup
-	},
-};
-
-static struct hda_pcm_stream ad1986a_pcm_analog_capture = {
-	.substreams = 2,
-	.channels_min = 2,
-	.channels_max = 2,
-	.nid = AD1986A_ADC, /* NID to query formats and rates */
-	.ops = {
-		.prepare = ad1986a_capture_pcm_prepare,
-		.cleanup = ad1986a_capture_pcm_cleanup
-	},
-};
-
-static struct hda_pcm_stream ad1986a_pcm_digital_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	.nid = AD1986A_SPDIF_OUT, 
-	.ops = {
-		.open = ad1986a_dig_playback_pcm_open,
-		.close = ad1986a_dig_playback_pcm_close
-	},
-};
-
-static int ad1986a_build_pcms(struct hda_codec *codec)
-{
-	struct ad1986a_spec *spec = codec->spec;
-	struct hda_pcm *info = spec->pcm_rec;
-
-	codec->num_pcms = 2;
-	codec->pcm_info = info;
-
-	info->name = "AD1986A Analog";
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_analog_playback;
-	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1986a_pcm_analog_capture;
-	info++;
-
-	info->name = "AD1986A Digital";
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_digital_playback;
-
-	return 0;
-}
-
-static void ad1986a_free(struct hda_codec *codec)
-{
-	kfree(codec->spec);
-}
-
-#ifdef CONFIG_PM
-static int ad1986a_resume(struct hda_codec *codec)
-{
-	ad1986a_init(codec);
-	snd_hda_resume_ctls(codec, ad1986a_mixers);
-	snd_hda_resume_spdif_out(codec);
-	return 0;
-}
-#endif
-
-static struct hda_codec_ops ad1986a_patch_ops = {
-	.build_controls = ad1986a_build_controls,
-	.build_pcms = ad1986a_build_pcms,
-	.init = ad1986a_init,
-	.free = ad1986a_free,
-#ifdef CONFIG_PM
-	.resume = ad1986a_resume,
-#endif
-};
-
 static int patch_ad1986a(struct hda_codec *codec)
 {
-	struct ad1986a_spec *spec;
+	struct ad198x_spec *spec;
 
 	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -430,16 +476,323 @@
 	spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
 	spec->multiout.dac_nids = ad1986a_dac_nids;
 	spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
+	spec->adc_nid = AD1986A_ADC;
+	spec->input_mux = &ad1986a_capture_source;
+	spec->mixers = ad1986a_mixers;
+	spec->init_verbs = ad1986a_init_verbs;
 
-	codec->patch_ops = ad1986a_patch_ops;
+	codec->patch_ops = ad198x_patch_ops;
 
 	return 0;
 }
 
 /*
+ * AD1983 specific
+ */
+
+#define AD1983_SPDIF_OUT	0x02
+#define AD1983_DAC		0x03
+#define AD1983_ADC		0x04
+
+static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
+
+static struct hda_input_mux ad1983_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Line", 0x1 },
+		{ "Mix", 0x2 },
+		{ "Mix Mono", 0x3 },
+	},
+};
+
+/*
+ * SPDIF playback route
+ */
+static int ad1983_spdif_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[] = { "PCM", "ADC" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ad1983_spdif_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->spdif_route;
+	return 0;
+}
+
+static int ad1983_spdif_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
+
+	if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
+		spec->spdif_route = ucontrol->value.enumerated.item[0];
+		snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
+				    AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t ad1983_mixers[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 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_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("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "IEC958 Playback Route",
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_verb ad1983_init_verbs[] = {
+	/* Front, HP, Mono; mute as default */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Beep, PCM, Mic, Line-In: mute */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Front, HP selectors; from Mix */
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* Mono selector; from Mix */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic selector; Mic */
+	{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Line-in selector: Line-in */
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Mic boost: 0dB */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* Record selector: mic */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* SPDIF route: PCM */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Front Pin */
+	{0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* HP Pin */
+	{0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* Mono Pin */
+	{0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Mic Pin */
+	{0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* Line Pin */
+	{0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	{ } /* end */
+};
+
+static int patch_ad1983(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+
+	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	init_MUTEX(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
+	spec->multiout.dac_nids = ad1983_dac_nids;
+	spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
+	spec->adc_nid = AD1983_ADC;
+	spec->input_mux = &ad1983_capture_source;
+	spec->mixers = ad1983_mixers;
+	spec->init_verbs = ad1983_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	return 0;
+}
+
+
+/*
+ * AD1981 HD specific
+ */
+
+#define AD1981_SPDIF_OUT	0x02
+#define AD1981_DAC		0x03
+#define AD1981_ADC		0x04
+
+static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
+
+/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
+static struct hda_input_mux ad1981_capture_source = {
+	.num_items = 7,
+	.items = {
+		{ "Front Mic", 0x0 },
+		{ "Line", 0x1 },
+		{ "Mix", 0x2 },
+		{ "Mix Mono", 0x3 },
+		{ "CD", 0x4 },
+		{ "Mic", 0x6 },
+		{ "Aux", 0x7 },
+	},
+};
+
+static snd_kcontrol_new_t ad1981_mixers[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 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_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 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_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("Mic Boost", 0x18, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* identical with AD1983 */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "IEC958 Playback Route",
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_verb ad1981_init_verbs[] = {
+	/* Front, HP, Mono; mute as default */
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Front, HP selectors; from Mix */
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* Mono selector; from Mix */
+	{0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic Mixer; select Front Mic */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Mic boost: 0dB */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* Record selector: Front mic */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* SPDIF route: PCM */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* Front Pin */
+	{0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* HP Pin */
+	{0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	/* Mono Pin */
+	{0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+	/* Front & Rear Mic Pins */
+	{0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+	/* Line Pin */
+	{0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* Digital Beep */
+	{0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Line-Out as Input: disabled */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{ } /* end */
+};
+
+static int patch_ad1981(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+
+	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	init_MUTEX(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
+	spec->multiout.dac_nids = ad1981_dac_nids;
+	spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
+	spec->adc_nid = AD1981_ADC;
+	spec->input_mux = &ad1981_capture_source;
+	spec->mixers = ad1981_mixers;
+	spec->init_verbs = ad1981_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	return 0;
+}
+
+
+/*
  * patch entries
  */
 struct hda_codec_preset snd_hda_preset_analog[] = {
+	{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
+	{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
 	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
 	{} /* terminator */
 };
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index b7cc8e4..087230c 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#define NUM_PINS	11
 
 
 /* board config type */
@@ -38,6 +39,7 @@
 	CMI_FULL,	/* back 6-jack + front-panel 2-jack */
 	CMI_FULL_DIG,	/* back 6-jack + front-panel 2-jack + digital I/O */
 	CMI_ALLOUT,	/* back 5-jack + front-panel 2-jack + digital out */
+	CMI_AUTO,	/* let driver guess it */
 };
 
 struct cmi_spec {
@@ -48,6 +50,7 @@
 
 	/* playback */
 	struct hda_multi_out multiout;
+	hda_nid_t dac_nids[4];		/* NID for each DAC */
 
 	/* capture */
 	hda_nid_t *adc_nids;
@@ -63,6 +66,15 @@
 	const struct cmi_channel_mode *channel_modes;
 
 	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	/* pin deafault configuration */
+	hda_nid_t pin_nid[NUM_PINS];
+	unsigned int def_conf[NUM_PINS];
+	unsigned int pin_def_confs;
+
+	/* multichannel pins */
+	hda_nid_t multich_pin[4];	/* max 8-channel */
+	struct hda_verb multi_init[9];	/* 2 verbs for each pin + terminator */
 };
 
 /*
@@ -278,8 +290,10 @@
 	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
 	/* port-G for CLFE (rear panel) */
 	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
 	/* port-H for side (rear panel) */
 	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
 	/* port-C for line-in (rear panel) */
 	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
 	/* port-B for mic-in (rear panel) with vref */
@@ -305,6 +319,10 @@
 	{ 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
 	/* port-G for CLFE (rear panel) */
 	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
 	/* port-C for surround (rear panel) */
 	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
 	/* port-B for mic-in (rear panel) with vref */
@@ -347,6 +365,174 @@
 	return 0;
 }
 
+#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
+#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
+#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE)
+
+/* get all pin default configuration in def_conf */
+static int cmi9880_get_pin_def_config(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid, nid_start;
+	int i = 0, nodes;
+
+	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+	for (nid = nid_start; nid < nodes + nid_start; nid++) {
+		unsigned int wid_caps = snd_hda_param_read(codec, nid,
+						   AC_PAR_AUDIO_WIDGET_CAP);
+		unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		/* read all default configuration for pin complex */
+		if (wid_type == AC_WID_PIN) {
+			spec->pin_nid[i] = nid;
+			spec->def_conf[i] = 
+				snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONFIG_DEFAULT, 0);
+			i++;
+		}
+	}
+	spec->pin_def_confs = i;
+	return 0;
+}
+
+/* get a pin default configuration of nid in def_conf */
+static unsigned int cmi9880_get_def_config(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct cmi_spec *spec = codec->spec;
+	int i = 0;
+
+	while (spec->pin_nid[i] != nid && i < spec->pin_def_confs)
+		i++;
+	if (i == spec->pin_def_confs)
+		return (unsigned int) -1;
+	else
+		return spec->def_conf[i];
+}
+
+/* decide what pins to use for multichannel playback */
+static int cmi9880_get_multich_pins(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	int i, j, pins, seq[4];
+	int max_channel = 0;
+	unsigned int def_conf, sequence;
+	hda_nid_t nid;
+
+	memset(spec->multich_pin, 0, sizeof(spec->multich_pin));
+	for (pins = 0, i = 0; i < spec->pin_def_confs && pins < 4; i++) {
+		def_conf = spec->def_conf[i];
+		/* skip pin not connected */
+		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+			continue;
+		/* get the sequence if association == 1 */
+		/* the other pins have association = 0, incorrect in spec 1.0 */
+		if (get_defcfg_association(def_conf) == 1) {
+			sequence = get_defcfg_sequence(def_conf);
+			seq[pins] = sequence;
+			spec->multich_pin[pins] = spec->pin_nid[i];
+			pins++;	// ready for next slot
+			max_channel += 2;
+		}
+	}
+	/* sort by sequence, data collected here will be for Windows */ 
+	for (i = 0; i < pins; i++) {
+		for (j = i + 1; j < pins; j++) {
+			if (seq[j] < seq[i]) {
+				sequence = seq[j];
+				nid = spec->multich_pin[j];
+				seq[j] = seq[i];
+				spec->multich_pin[j] = spec->multich_pin[i];
+				seq[i] = sequence;
+				spec->multich_pin[i] = nid;
+			}
+		}
+	}
+	/* the pin assignment is for front, C/LFE, surround and back */
+	if (max_channel >= 6) {
+		hda_nid_t temp;
+		/* exchange pin of C/LFE and surround */
+		temp = spec->multich_pin[1];
+		spec->multich_pin[1] = spec->multich_pin[2];
+		spec->multich_pin[2] = temp;
+	}
+	return max_channel;
+}
+
+/* fill in the multi_dac_nids table, which will decide
+   which audio widget to use for each channel */
+static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int assigned[4];
+	int i, j;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
+	memset(assigned, 0, sizeof(assigned));
+	/* check the pins we found */
+	for (i = 0; i < spec->multiout.max_channels / 2; i++) {
+		nid = spec->multich_pin[i];
+		/* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
+		if (nid <= 0x0e && nid >= 0x0b) {
+			spec->dac_nids[i] = nid - 0x08;
+			assigned[nid - 0x0b] = 1;
+		}
+	}
+	/* left pin can be connect to any audio widget */
+	for (i = 0; i < spec->multiout.max_channels / 2; i++) {
+		if (!assigned[i]) {
+			/* search for an empty channel */
+			/* I should also check the pin type */
+			for (j = 0; j < ARRAY_SIZE(spec->dac_nids); j++)
+				if (! spec->dac_nids[j]) {
+					spec->dac_nids[j] = i + 3;
+					assigned[i] = 1;
+					break;
+				}
+		}
+	}
+	return 0;
+}
+
+/* create multi_init table, which is used for multichannel initialization */
+static int cmi9880_fill_multi_init(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i, j, k, len;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(spec->multi_init, 0, sizeof(spec->multi_init));
+	for (j = 0, i = 0; i < spec->multiout.max_channels / 2; i++) {
+		hda_nid_t conn[4];
+		nid = spec->multich_pin[i];
+		/* set as output */
+		spec->multi_init[j].nid = nid;
+		spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+		spec->multi_init[j].param = 0xc0;
+		j++;
+		/* nid 0x0f,0x10,0x1f,0x20 are needed to set connection */
+		switch (nid) {
+		case 0x0f:
+		case 0x10:
+		case 0x1f:
+		case 0x20:
+			/* set connection */
+			spec->multi_init[j].nid = nid;
+			spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
+			/* find the index in connect list */
+			len = snd_hda_get_connections(codec, nid, conn, 4);
+			for (k = 0; k < len; k++)
+				if (conn[k] == spec->dac_nids[i])
+					break;
+			spec->multi_init[j].param = k < len ? k : 0;
+			j++;
+			break;
+		}
+	}
+	return 0;
+}
+
 static int cmi9880_init(struct hda_codec *codec)
 {
 	struct cmi_spec *spec = codec->spec;
@@ -354,6 +540,8 @@
 		snd_hda_sequence_write(codec, cmi9880_allout_init);
 	else
 		snd_hda_sequence_write(codec, cmi9880_basic_init);
+	if (spec->board_config == CMI_AUTO)
+		snd_hda_sequence_write(codec, spec->multi_init);
 	return 0;
 }
 
@@ -540,6 +728,7 @@
 	{ .modelname = "full", .config = CMI_FULL },
 	{ .modelname = "full_dig", .config = CMI_FULL_DIG },
 	{ .modelname = "allout", .config = CMI_ALLOUT },
+	{ .modelname = "auto", .config = CMI_AUTO },
 	{} /* terminator */
 };
 
@@ -564,10 +753,13 @@
 	codec->spec = spec;
 	spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl);
 	if (spec->board_config < 0) {
-		snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
-		spec->board_config = CMI_FULL_DIG; /* try everything */
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+		spec->board_config = CMI_AUTO; /* try everything */
 	}
 
+	/* copy default DAC NIDs */
+	memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
+
 	switch (spec->board_config) {
 	case CMI_MINIMAL:
 	case CMI_MIN_FP:
@@ -599,10 +791,58 @@
 		spec->input_mux = &cmi9880_no_line_mux;
 		spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
 		break;
+	case CMI_AUTO:
+		{
+		unsigned int port_e, port_f, port_g, port_h;
+		unsigned int port_spdifi, port_spdifo;
+		int max_channels;
+		/* collect pin default configuration */
+		cmi9880_get_pin_def_config(codec);
+		port_e = cmi9880_get_def_config(codec, 0x0f);
+		port_f = cmi9880_get_def_config(codec, 0x10);
+		port_g = cmi9880_get_def_config(codec, 0x1f);
+		port_h = cmi9880_get_def_config(codec, 0x20);
+		port_spdifi = cmi9880_get_def_config(codec, 0x13);
+		port_spdifo = cmi9880_get_def_config(codec, 0x12);
+		spec->front_panel = 1;
+		if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
+		    get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
+			spec->surr_switch = 1;
+			/* no front panel */
+			if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
+			    get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
+				/* no optional rear panel */
+				spec->board_config = CMI_MINIMAL;
+				spec->front_panel = 0;
+				spec->num_ch_modes = 2;
+			} else {
+				spec->board_config = CMI_MIN_FP;
+				spec->num_ch_modes = 3;
+			}
+			spec->channel_modes = cmi9880_channel_modes;
+			spec->input_mux = &cmi9880_basic_mux;
+			spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
+		} else {
+			spec->input_mux = &cmi9880_basic_mux;
+			if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
+				spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+			if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
+				spec->dig_in_nid = CMI_DIG_IN_NID;
+			spec->multiout.max_channels = 8;
+		}
+		max_channels = cmi9880_get_multich_pins(codec);
+		if (max_channels > 0) {
+			spec->multiout.max_channels = max_channels;
+			cmi9880_fill_multi_dac_nids(codec);
+			cmi9880_fill_multi_init(codec);
+		} else
+			snd_printd("patch_cmedia: cannot detect association in defcfg\n");
+		break;
+		}
 	}
 
 	spec->multiout.num_dacs = 4;
-	spec->multiout.dac_nids = cmi9880_dac_nids;
+	spec->multiout.dac_nids = spec->dac_nids;
 
 	spec->adc_nids = cmi9880_adc_nids;
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 17c5062..ee1c4cd 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -39,6 +39,8 @@
 	ALC880_5ST,
 	ALC880_5ST_DIG,
 	ALC880_W810,
+	ALC880_Z71V,
+	ALC880_TEST,
 };
 
 struct alc_spec {
@@ -90,10 +92,25 @@
 	0x02, 0x03, 0x04
 };
 
+static hda_nid_t alc880_z71v_dac_nids[1] = {
+	/* front only? */
+	0x02
+};
+
+#if 0
+/* The datasheet says the node 0x07 is connected from inputs,
+ * but it shows zero connection in the real implementation.
+ */
 static hda_nid_t alc880_adc_nids[3] = {
 	/* ADC0-2 */
 	0x07, 0x08, 0x09,
 };
+#else
+static hda_nid_t alc880_adc_nids[2] = {
+	/* ADC1-2 */
+	0x08, 0x09,
+};
+#endif
 
 #define ALC880_DIGOUT_NID	0x06
 #define ALC880_DIGIN_NID	0x0a
@@ -284,19 +301,24 @@
 	{ 6, NULL }
 };
 
+static struct alc_channel_mode alc880_z71v_modes[1] = {
+	{ 2, NULL }
+};
+
 /*
  */
 static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
+	int items = kcontrol->private_value ? (int)kcontrol->private_value : 2;
 
 	snd_assert(spec->channel_mode, return -ENXIO);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = 2;
-	if (uinfo->value.enumerated.item >= 2)
-		uinfo->value.enumerated.item = 1;
+	uinfo->value.enumerated.items = items;
+	if (uinfo->value.enumerated.item >= items)
+		uinfo->value.enumerated.item = items - 1;
 	sprintf(uinfo->value.enumerated.name, "%dch",
 		spec->channel_mode[uinfo->value.enumerated.item].channels);
 	return 0;
@@ -306,10 +328,16 @@
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
+	int items = kcontrol->private_value ? (int)kcontrol->private_value : 2;
+	int i;
 
 	snd_assert(spec->channel_mode, return -ENXIO);
-	ucontrol->value.enumerated.item[0] =
-		(spec->multiout.max_channels == spec->channel_mode[0].channels) ? 0 : 1;
+	for (i = 0; i < items; i++) {
+		if (spec->multiout.max_channels == spec->channel_mode[i].channels) {
+			ucontrol->value.enumerated.item[0] = i;
+			break;
+		}
+	}
 	return 0;
 }
 
@@ -362,10 +390,11 @@
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	/* We don't use NID 0x07 - see above */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		/* The multiple "Capture Source" controls confuse alsamixer
@@ -416,10 +445,11 @@
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	/* We don't use NID 0x07 - see above */
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		/* The multiple "Capture Source" controls confuse alsamixer
@@ -475,6 +505,37 @@
 	{ } /* end */
 };
 
+static snd_kcontrol_new_t alc880_z71v_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 3,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{ } /* end */
+};
+
 /*
  */
 static int alc_build_controls(struct hda_codec *codec)
@@ -517,8 +578,16 @@
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
 	/* unmute amp left and right */
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-	/* set connection select to line in (default select for this ADC) */
-	{0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* set connection select to mic in */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* unmute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* unmute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* unmute front mixer amp left (volume = 0) */
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
 	/* mute pin widget amp left and right (no gain on this amp) */
@@ -592,8 +661,16 @@
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
 	/* unmute amp left and right */
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-	/* set connection select to line in (default select for this ADC) */
-	{0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* set connection select to mic in */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* unmute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* unmute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* unmute front mixer amp left and right (volume = 0) */
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
 	/* mute pin widget amp left and right (no gain on this amp) */
@@ -719,6 +796,65 @@
 	{ }
 };
 
+static struct hda_verb alc880_z71v_init_verbs[] = {
+	/* front channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* front channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+	/* front channel selector/amp: output 0: unmuted, max volume */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* front out pin: muted, (no volume selection)  */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* front out pin: NOT headphone enable, out enable, vref disabled */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* headphone channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* headphone channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+	/* headphone channel selector/amp: output 0: unmuted, max volume */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* headphone out pin: muted, (no volume selection)  */
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* headpohne out pin: headphone enable, out enable, vref disabled */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+
+	/* Line In pin widget for input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* CD pin widget for input */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* Mic1 (rear panel) pin widget for input and vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	/* Mic2 (front panel) pin widget for input and vref at 80% */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	/* unmute amp left and right */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* unmute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* unmute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	/* set connection select to mic in */
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer
+	 * widget(nid=0x0B) to support the input path of analog loopback
+	 */
+	/* Note: PASD motherboards uses the Line In 2 as the input for front panel mic (mic 2) */
+	/* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03*/
+	/* unmute CD */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	/* unmute Line In */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	/* unmute Mic 1 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	/* unmute Line In 2 (for PASD boards Mic 2) */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+	{ }
+};
+
 static int alc_init(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -842,7 +978,9 @@
 	.substreams = 2,
 	.channels_min = 2,
 	.channels_max = 2,
-	.nid = 0x07, /* NID to query formats and rates */
+	.nid = 0x08, /* NID to query formats and rates
+		      * (0x07 might be broken on some devices)
+		      */
 	.ops = {
 		.prepare = alc880_capture_pcm_prepare,
 		.cleanup = alc880_capture_pcm_cleanup
@@ -921,77 +1059,336 @@
 #endif
 };
 
+
+/*
+ * Test configuration for debugging
+ *
+ * Almost all inputs/outputs are enabled.  I/O pins can be configured via
+ * enum controls.
+ */
+#ifdef CONFIG_SND_DEBUG
+static hda_nid_t alc880_test_dac_nids[4] = {
+	0x02, 0x03, 0x04, 0x05
+};
+
+static struct hda_input_mux alc880_test_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "In-1", 0x0 },
+		{ "In-2", 0x1 },
+		{ "In-3", 0x2 },
+		{ "In-4", 0x3 },
+		{ "CD", 0x4 },
+	},
+};
+
+static struct alc_channel_mode alc880_test_modes[4] = {
+	{ 2, NULL },
+	{ 4, NULL },
+	{ 6, NULL },
+	{ 8, NULL },
+};
+
+static int alc_test_pin_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[] = {
+		"N/A", "Line Out", "HP Out",
+		"In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item >= 8)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int alc_test_pin_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	unsigned int pin_ctl, item = 0;
+
+	pin_ctl = snd_hda_codec_read(codec, nid, 0,
+				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	if (pin_ctl & AC_PINCTL_OUT_EN) {
+		if (pin_ctl & AC_PINCTL_HP_EN)
+			item = 2;
+		else
+			item = 1;
+	} else if (pin_ctl & AC_PINCTL_IN_EN) {
+		switch (pin_ctl & AC_PINCTL_VREFEN) {
+		case AC_PINCTL_VREF_HIZ: item = 3; break;
+		case AC_PINCTL_VREF_50:  item = 4; break;
+		case AC_PINCTL_VREF_GRD: item = 5; break;
+		case AC_PINCTL_VREF_80:  item = 6; break;
+		case AC_PINCTL_VREF_100: item = 7; break;
+		}
+	}
+	ucontrol->value.enumerated.item[0] = item;
+	return 0;
+}
+
+static int alc_test_pin_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	static unsigned int ctls[] = {
+		0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
+		AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
+	};
+	unsigned int old_ctl, new_ctl;
+
+	old_ctl = snd_hda_codec_read(codec, nid, 0,
+				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	new_ctl = ctls[ucontrol->value.enumerated.item[0]];
+	if (old_ctl != new_ctl) {
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000);
+		return 1;
+	}
+	return 0;
+}
+
+static int alc_test_pin_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[] = {
+		"Front", "Surround", "CLFE", "Side"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= 4)
+		uinfo->value.enumerated.item = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int alc_test_pin_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	unsigned int sel;
+
+	sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
+	ucontrol->value.enumerated.item[0] = sel & 3;
+	return 0;
+}
+
+static int alc_test_pin_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+	unsigned int sel;
+
+	sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
+	if (ucontrol->value.enumerated.item[0] != sel) {
+		sel = ucontrol->value.enumerated.item[0] & 3;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel);
+		return 1;
+	}
+	return 0;
+}
+
+#define PIN_CTL_TEST(xname,nid) {			\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+			.name = xname,		       \
+			.info = alc_test_pin_ctl_info, \
+			.get = alc_test_pin_ctl_get,   \
+			.put = alc_test_pin_ctl_put,   \
+			.private_value = nid	       \
+			}
+
+#define PIN_SRC_TEST(xname,nid) {			\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	\
+			.name = xname,		       \
+			.info = alc_test_pin_src_info, \
+			.get = alc_test_pin_src_get,   \
+			.put = alc_test_pin_src_put,   \
+			.private_value = nid	       \
+			}
+
+static snd_kcontrol_new_t alc880_test_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	PIN_CTL_TEST("Front Pin Mode", 0x14),
+	PIN_CTL_TEST("Surround Pin Mode", 0x15),
+	PIN_CTL_TEST("CLFE Pin Mode", 0x16),
+	PIN_CTL_TEST("Side Pin Mode", 0x17),
+	PIN_CTL_TEST("In-1 Pin Mode", 0x18),
+	PIN_CTL_TEST("In-2 Pin Mode", 0x19),
+	PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
+	PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
+	PIN_SRC_TEST("In-1 Pin Source", 0x18),
+	PIN_SRC_TEST("In-2 Pin Source", 0x19),
+	PIN_SRC_TEST("In-3 Pin Source", 0x1a),
+	PIN_SRC_TEST("In-4 Pin Source", 0x1b),
+	HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
+	HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 2,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc880_ch_mode_info,
+		.get = alc880_ch_mode_get,
+		.put = alc880_ch_mode_put,
+		.private_value = ARRAY_SIZE(alc880_test_modes),
+	},
+	{ } /* end */
+};
+
+static struct hda_verb alc880_test_init_verbs[] = {
+	/* Unmute inputs of 0x0c - 0x0f */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7100},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7100},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7100},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0x7100},
+	/* Vol output for 0x0c-0x0f */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+	/* Set output pins 0x14-0x17 */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* Unmute output pins 0x14-0x17 */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* Set input pins 0x18-0x1c */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, /* vref 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	/* Mute input pins 0x18-0x1b */
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+	/* ADC set up */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{ }
+};
+#endif
+
 /*
  */
 
 static struct hda_board_config alc880_cfg_tbl[] = {
 	/* Back 3 jack, front 2 jack */
 	{ .modelname = "3stack", .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe200, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe201, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe202, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe203, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe204, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe205, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe206, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe207, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe208, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe209, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe20a, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe20b, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe20c, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe20d, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe20e, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe20f, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe210, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe211, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe214, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe302, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe303, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe304, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe306, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe307, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe404, .config = ALC880_3ST },
-	{ .pci_vendor = 0x8086, .pci_device = 0xa101, .config = ALC880_3ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x3031, .config = ALC880_3ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x4036, .config = ALC880_3ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x4037, .config = ALC880_3ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x4038, .config = ALC880_3ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x4040, .config = ALC880_3ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x4041, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
 
 	/* Back 3 jack, front 2 jack (Internal add Aux-In) */
-	{ .pci_vendor = 0x1025, .pci_device = 0xe310, .config = ALC880_3ST },
+	{ .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST },
 
 	/* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
 	{ .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe308, .config = ALC880_3ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
 
 	/* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
-	{ .pci_vendor = 0x8086, .pci_device = 0xe305, .config = ALC880_3ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xd402, .config = ALC880_3ST_DIG },
-	{ .pci_vendor = 0x1025, .pci_device = 0xe309, .config = ALC880_3ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG },
+	{ .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG },
 
 	/* Back 5 jack, front 2 jack */
 	{ .modelname = "5stack", .config = ALC880_5ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x3033, .config = ALC880_5ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x4039, .config = ALC880_5ST },
-	{ .pci_vendor = 0x107b, .pci_device = 0x3032, .config = ALC880_5ST },
-	{ .pci_vendor = 0x103c, .pci_device = 0x2a09, .config = ALC880_5ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST },
+	{ .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST },
+	{ .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST },
 
 	/* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
 	{ .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe224, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe400, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe401, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xe402, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xd400, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xd401, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x8086, .pci_device = 0xa100, .config = ALC880_5ST_DIG },
-	{ .pci_vendor = 0x1565, .pci_device = 0x8202, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG },
+	{ .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG },
 
 	{ .modelname = "w810", .config = ALC880_W810 },
-	{ .pci_vendor = 0x161f, .pci_device = 0x203d, .config = ALC880_W810 },
+	{ .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 },
+
+	{ .modelname = "z71v", .config = ALC880_Z71V },
+	{ .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
+
+#ifdef CONFIG_SND_DEBUG
+	{ .modelname = "test", .config = ALC880_TEST },
+#endif
 
 	{}
 };
@@ -1023,6 +1420,16 @@
 		spec->mixers[spec->num_mixers] = alc880_five_stack_mixer;
 		spec->num_mixers++;
 		break;
+	case ALC880_Z71V:
+		spec->mixers[spec->num_mixers] = alc880_z71v_mixer;
+		spec->num_mixers++;
+		break;
+#ifdef CONFIG_SND_DEBUG
+	case ALC880_TEST:
+		spec->mixers[spec->num_mixers] = alc880_test_mixer;
+		spec->num_mixers++;
+		break;
+#endif
 	default:
 		spec->mixers[spec->num_mixers] = alc880_base_mixer;
 		spec->num_mixers++;
@@ -1033,6 +1440,8 @@
 	case ALC880_3ST_DIG:
 	case ALC880_5ST_DIG:
 	case ALC880_W810:
+	case ALC880_Z71V:
+	case ALC880_TEST:
 		spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
 		break;
 	default:
@@ -1063,6 +1472,18 @@
 		spec->channel_mode = alc880_w810_modes;
 		spec->num_channel_mode = ARRAY_SIZE(alc880_w810_modes);
 		break;
+	case ALC880_Z71V:
+		spec->init_verbs = alc880_z71v_init_verbs;
+		spec->channel_mode = alc880_z71v_modes;
+		spec->num_channel_mode = ARRAY_SIZE(alc880_z71v_modes);
+		break;
+#ifdef CONFIG_SND_DEBUG
+	case ALC880_TEST:
+		spec->init_verbs = alc880_test_init_verbs;
+		spec->channel_mode = alc880_test_modes;
+		spec->num_channel_mode = ARRAY_SIZE(alc880_test_modes);
+		break;
+#endif
 	default:
 		spec->init_verbs = alc880_init_verbs_three_stack;
 		spec->channel_mode = alc880_threestack_modes;
@@ -1086,6 +1507,18 @@
 		spec->multiout.dac_nids = alc880_w810_dac_nids;
 		// No dedicated headphone socket - it's shared with built-in speakers.
 		break;
+	case ALC880_Z71V:
+		spec->multiout.num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids);
+		spec->multiout.dac_nids = alc880_z71v_dac_nids;
+		spec->multiout.hp_nid = 0x03;
+		break;
+#ifdef CONFIG_SND_DEBUG
+	case ALC880_TEST:
+		spec->multiout.num_dacs = ARRAY_SIZE(alc880_test_dac_nids);
+		spec->multiout.dac_nids = alc880_test_dac_nids;
+		spec->input_mux = &alc880_test_capture_source;
+		break;
+#endif
 	default:
 		spec->multiout.num_dacs = ARRAY_SIZE(alc880_dac_nids);
 		spec->multiout.dac_nids = alc880_dac_nids;
@@ -1093,7 +1526,8 @@
 		break;
 	}
 
-	spec->input_mux = &alc880_capture_source;
+	if (! spec->input_mux)
+		spec->input_mux = &alc880_capture_source;
 	spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
 	spec->adc_nids = alc880_adc_nids;
 
@@ -1434,11 +1868,13 @@
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
 	/* ADC1: unmute amp left and right */
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* ADC2: unmute amp left and right */
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* ADC3: unmute amp left and right */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* Unmute front loopback */
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
 	/* Unmute rear loopback */
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
new file mode 100644
index 0000000..1534e20
--- /dev/null
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -0,0 +1,560 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for SigmaTel STAC92xx
+ *
+ * Copyright (c) 2005 Embedded Alley Solutions, Inc.
+ * <matt@embeddedalley.com>
+ *
+ * Based on patch_cmedia.c and patch_realtek.c
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#undef STAC_TEST
+
+struct sigmatel_spec {
+	/* playback */
+	struct hda_multi_out multiout;
+	hda_nid_t playback_nid;
+
+	/* capture */
+	hda_nid_t *adc_nids;
+	hda_nid_t *mux_nids;
+	unsigned int num_adcs;
+	hda_nid_t capture_nid;
+
+	/* power management*/
+	hda_nid_t *pstate_nids;
+	unsigned int num_pstates;
+
+	/* pin widgets */
+	hda_nid_t *pin_nids;
+	unsigned int num_pins;
+#ifdef STAC_TEST
+	unsigned int *pin_configs;
+#endif
+
+	/* codec specific stuff */
+	struct hda_verb *init;
+	snd_kcontrol_new_t *mixer;
+
+	/* capture source */
+	const struct hda_input_mux *input_mux;
+	unsigned int cur_mux[2];
+
+	/* channel mode */
+	unsigned int num_ch_modes;
+	unsigned int cur_ch_mode;
+	const struct sigmatel_channel_mode *channel_modes;
+
+	struct hda_pcm pcm_rec[1];	/* PCM information */
+};
+
+static hda_nid_t stac9200_adc_nids[1] = {
+        0x03,
+};
+
+static hda_nid_t stac9200_mux_nids[1] = {
+        0x0c,
+};
+
+static hda_nid_t stac9200_dac_nids[1] = {
+        0x02,
+};
+
+static hda_nid_t stac9200_pstate_nids[3] = {
+	0x01, 0x02, 0x03,
+};
+
+static hda_nid_t stac9200_pin_nids[8] = {
+	0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+};
+
+static hda_nid_t stac922x_adc_nids[2] = {
+        0x06, 0x07,
+};
+
+static hda_nid_t stac922x_mux_nids[2] = {
+        0x12, 0x13,
+};
+
+static hda_nid_t stac922x_dac_nids[4] = {
+        0x02, 0x03, 0x04, 0x05,
+};
+
+static hda_nid_t stac922x_pstate_nids[7] = {
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+};
+
+static hda_nid_t stac922x_pin_nids[10] = {
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+	0x0f, 0x10, 0x11, 0x15, 0x1b,
+};
+
+static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+	return 0;
+}
+
+static int stac92xx_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+				     spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+static struct hda_verb stac9200_ch2_init[] = {
+	/* set dac0mux for dac converter */
+	{ 0x07, 0x701, 0x00},
+	{}
+};
+
+static struct hda_verb stac922x_ch2_init[] = {
+	/* set master volume and direct control */	
+	{ 0x16, 0x70f, 0xff},
+	{}
+};
+
+struct sigmatel_channel_mode {
+	unsigned int channels;
+	const struct hda_verb *sequence;
+};
+
+static snd_kcontrol_new_t stac9200_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Input Mux Volume", 0x0c, 0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+static snd_kcontrol_new_t stac922x_mixer[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x2, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Source",
+		.count = 1,
+		.info = stac92xx_mux_enum_info,
+		.get = stac92xx_mux_enum_get,
+		.put = stac92xx_mux_enum_put,
+	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+static struct hda_input_mux stac9200_input_mux = {
+	.num_items = 5,
+	.items = {
+		{ "Port B", 0x0 },
+		{ "Port C", 0x1 },
+		{ "Port D", 0x2 },
+		{ "Port A", 0x3 },
+		{ "CD", 0x4 },
+	}
+};
+
+static struct hda_input_mux stac922x_input_mux = {
+	.num_items = 7,
+	.items = {
+		{ "Port E", 0x0 },
+		{ "CD", 0x1 },
+		{ "Port F", 0x2 },
+		{ "Port B", 0x3 },
+		{ "Port C", 0x4 },
+		{ "Port D", 0x5 },
+		{ "Port A", 0x6 },
+	}
+};
+
+static int stac92xx_build_controls(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_add_new_ctls(codec, spec->mixer);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+#ifdef STAC_TEST
+static unsigned int stac9200_pin_configs[8] = {
+	0x40000100, 0x40000100, 0x0221401f, 0x01114010,
+	0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
+};
+
+static unsigned int stac922x_pin_configs[14] = {
+	0x40000100, 0x40000100, 0x40000100, 0x01114010,
+	0x01813122, 0x40000100, 0x40000100, 0x40000100,
+	0x40000100, 0x40000100,
+};
+
+static void stac92xx_set_config_regs(struct hda_codec *codec)
+{
+	int i;
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int pin_cfg;
+
+	for (i=0; i < spec->num_pins; i++) {
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
+				    spec->pin_configs[i] & 0x000000ff);
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
+				    (spec->pin_configs[i] & 0x0000ff00) >> 8);
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
+				    (spec->pin_configs[i] & 0x00ff0000) >> 16);
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+				    spec->pin_configs[i] >> 24);
+		pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
+					     AC_VERB_GET_CONFIG_DEFAULT,
+					     0x00);	
+		printk("pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
+	}
+}
+#endif
+
+static int stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int value)
+{
+	unsigned int pin_ctl;
+
+	pin_ctl = snd_hda_codec_read(codec, nid, 0,
+				     AC_VERB_GET_PIN_WIDGET_CONTROL,
+				     0x00);
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    pin_ctl | value);
+
+	return 0;
+}
+
+static int stac92xx_set_vref(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int vref_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) >> AC_PINCAP_VREF_SHIFT;
+	unsigned int vref_ctl = AC_PINCTL_VREF_HIZ;
+
+	if (vref_caps & AC_PINCAP_VREF_100)
+		vref_ctl = AC_PINCTL_VREF_100;
+	else if (vref_caps & AC_PINCAP_VREF_80)
+		vref_ctl = AC_PINCTL_VREF_80;
+	else if (vref_caps & AC_PINCAP_VREF_50)
+		vref_ctl = AC_PINCTL_VREF_50;
+	else if (vref_caps & AC_PINCAP_VREF_GRD)
+		vref_ctl = AC_PINCTL_VREF_GRD;
+
+	stac92xx_set_pinctl(codec, nid, vref_ctl);
+	
+	return 0;
+}
+
+static int stac92xx_config_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_cfg)
+{
+	switch((pin_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) {
+		case AC_JACK_HP_OUT:
+			/* Enable HP amp */
+			stac92xx_set_pinctl(codec, nid, AC_PINCTL_HP_EN);
+			/* Fall through */
+		case AC_JACK_LINE_OUT:
+		case AC_JACK_SPEAKER:
+			/* Enable output */
+			stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+			break;
+		case AC_JACK_MIC_IN:
+			/* Set vref */
+			stac92xx_set_vref(codec, nid);
+		case AC_JACK_CD:
+		case AC_JACK_LINE_IN:
+		case AC_JACK_AUX:
+			/* Enable input */
+			stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+			break;
+	}
+
+	return 0;
+}
+
+static int stac92xx_config_pins(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+	unsigned int pin_cfg;
+
+	for (i=0; i < spec->num_pins; i++) {
+		/* Default to disabled */
+		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    0x00);
+
+		pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
+					     AC_VERB_GET_CONFIG_DEFAULT,
+					     0x00);
+		if (((pin_cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) == AC_JACK_PORT_NONE)
+			continue;	/* Move on */
+
+		stac92xx_config_pin(codec, spec->pin_nids[i], pin_cfg);
+	}
+
+	return 0;
+}
+
+static int stac92xx_init(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	for (i=0; i < spec->num_pstates; i++)
+		snd_hda_codec_write(codec, spec->pstate_nids[i], 0,
+				    AC_VERB_SET_POWER_STATE, 0x00);
+
+	mdelay(100);
+
+	snd_hda_sequence_write(codec, spec->init);
+
+#ifdef STAC_TEST
+	stac92xx_set_config_regs(codec);
+#endif
+
+	stac92xx_config_pins(codec);
+
+	return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      snd_pcm_substream_t *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 unsigned int stream_tag,
+					 unsigned int format,
+					 snd_pcm_substream_t *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+						format, substream);
+}
+
+static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					snd_pcm_substream_t *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture callbacks
+ */
+static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					snd_pcm_substream_t *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+                                   stream_tag, 0, format);
+	return 0;
+}
+
+static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					snd_pcm_substream_t *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+	return 0;
+}
+
+static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x02, /* NID to query formats and rates */
+	.ops = {
+		.open = stac92xx_playback_pcm_open,
+		.prepare = stac92xx_playback_pcm_prepare,
+		.cleanup = stac92xx_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x06, /* NID to query formats and rates */
+	.ops = {
+		.prepare = stac92xx_capture_pcm_prepare,
+		.cleanup = stac92xx_capture_pcm_cleanup
+	},
+};
+
+static int stac92xx_build_pcms(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "STAC92xx";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->playback_nid;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->capture_nid;
+
+	return 0;
+}
+
+static void stac92xx_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+static struct hda_codec_ops stac92xx_patch_ops = {
+	.build_controls = stac92xx_build_controls,
+	.build_pcms = stac92xx_build_pcms,
+	.init = stac92xx_init,
+	.free = stac92xx_free,
+};
+
+static int patch_stac9200(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+
+	spec  = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = 1;
+	spec->multiout.dac_nids = stac9200_dac_nids;
+	spec->adc_nids = stac9200_adc_nids;
+	spec->mux_nids = stac9200_mux_nids;
+	spec->input_mux = &stac9200_input_mux;
+	spec->pstate_nids = stac9200_pstate_nids;
+	spec->num_pstates = 3;
+	spec->pin_nids = stac9200_pin_nids;
+#ifdef STAC_TEST
+	spec->pin_configs = stac9200_pin_configs;
+#endif
+	spec->num_pins = 8;
+	spec->init = stac9200_ch2_init;
+	spec->mixer = stac9200_mixer;
+	spec->playback_nid = 0x02;
+	spec->capture_nid = 0x03;
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+static int patch_stac922x(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+
+	spec  = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = 4;
+	spec->multiout.dac_nids = stac922x_dac_nids;
+	spec->adc_nids = stac922x_adc_nids;
+	spec->mux_nids = stac922x_mux_nids;
+	spec->input_mux = &stac922x_input_mux;
+	spec->pstate_nids = stac922x_pstate_nids;
+	spec->num_pstates = 7;
+	spec->pin_nids = stac922x_pin_nids;
+#ifdef STAC_TEST
+	spec->pin_configs = stac922x_pin_configs;
+#endif
+	spec->num_pins = 10;
+	spec->init = stac922x_ch2_init;
+	spec->mixer = stac922x_mixer;
+	spec->playback_nid = 0x02;
+	spec->capture_nid = 0x06;
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+ 	{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
+ 	{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
+ 	{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
+ 	{ .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
+ 	{ .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
+	{} /* terminator */
+};
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index 7799517..289b0b5 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -30,16 +30,39 @@
 #include <sound/core.h>
 
 #include "ice1712.h"
+#include "envy24ht.h"
 #include "amp.h"
 
+static void wm_put(ice1712_t *ice, int reg, unsigned short val)
+{
+	unsigned short cval;
+	cval = (reg << 9) | val;
+	snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
+}
 
 static int __devinit snd_vt1724_amp_init(ice1712_t *ice)
 {
+	static unsigned short wm_inits[] = {
+		WM_ATTEN_L,	0x0000,	/* 0 db */
+		WM_ATTEN_R,	0x0000,	/* 0 db */
+		WM_DAC_CTRL,	0x0008,	/* 24bit I2S */
+		WM_INT_CTRL,	0x0001, /* 24bit I2S */	
+	};
+
+	unsigned int i;
+
 	/* only use basic functionality for now */
 
 	ice->num_total_dacs = 2;	/* only PSDOUT0 is connected */
 	ice->num_total_adcs = 2;
 
+	/* Chaintech AV-710 has another codecs, which need initialization */
+	/* initialize WM8728 codec */
+	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AV710) {
+		for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
+			wm_put(ice, wm_inits[i], wm_inits[i+1]);
+	}
+
 	return 0;
 }
 
@@ -54,6 +77,13 @@
 /* entry point */
 struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
 	{
+		.subvendor = VT1724_SUBDEVICE_AV710,
+		.name = "Chaintech AV-710",
+		.model = "av710",
+		.chip_init = snd_vt1724_amp_init,
+		.build_controls = snd_vt1724_amp_add_controls,
+	},
+	{
 		.subvendor = VT1724_SUBDEVICE_AUDIO2000,
 		.name = "AMP Ltd AUDIO2000",
 		.model = "amp2000",
diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h
index d58d433..a0fc89b 100644
--- a/sound/pci/ice1712/amp.h
+++ b/sound/pci/ice1712/amp.h
@@ -24,9 +24,23 @@
  *
  */      
 
-#define  AMP_AUDIO2000_DEVICE_DESC 	       "{AMP Ltd,AUDIO2000},"
+#define  AMP_AUDIO2000_DEVICE_DESC 	       "{AMP Ltd,AUDIO2000},"\
+					       "{Chaintech,AV-710},"
 
+#if 0
 #define VT1724_SUBDEVICE_AUDIO2000	0x12142417	/* Advanced Micro Peripherals Ltd AUDIO2000 */
+#else
+#define VT1724_SUBDEVICE_AUDIO2000	0x00030003	/* a dummy ID for AMP Audio2000 */
+#endif
+#define VT1724_SUBDEVICE_AV710		0x12142417	/* AV710 - the same ID with Audio2000! */
+
+/* WM8728 on I2C for AV710 */
+#define WM_DEV		0x36
+
+#define WM_ATTEN_L	0x00
+#define WM_ATTEN_R	0x01
+#define WM_DAC_CTRL	0x02
+#define WM_INT_CTRL	0x03
 
 extern struct snd_ice1712_card_info  snd_vt1724_amp_cards[];
 
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 79fba6b..a2545a5 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2748,7 +2748,7 @@
 
 static int __init alsa_card_ice1712_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ice1712_exit(void)
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 8bb1c58..5ad4728 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -373,6 +373,11 @@
 			unsigned short master[2];
 			unsigned short vol[8];
 		} aureon;
+		/* AC97 register cache for Phase28 */
+		struct phase28_spec {
+			unsigned short master[2];
+			unsigned short vol[8];
+		} phase28;
 		/* Hoontech-specific setting */
 		struct hoontech_spec {
 			unsigned char boxbits[4];
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 95500f0..79b5f12 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2328,7 +2328,7 @@
 
 static int __init alsa_card_ice1724_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ice1724_exit(void)
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index d1f9083..5bf734b 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -45,6 +45,47 @@
 #include "envy24ht.h"
 #include "phase.h"
 
+/* WM8770 registers */
+#define WM_DAC_ATTEN		0x00	/* DAC1-8 analog attenuation */
+#define WM_DAC_MASTER_ATTEN	0x08	/* DAC master analog attenuation */
+#define WM_DAC_DIG_ATTEN	0x09	/* DAC1-8 digital attenuation */
+#define WM_DAC_DIG_MASTER_ATTEN	0x11	/* DAC master digital attenuation */
+#define WM_PHASE_SWAP		0x12	/* DAC phase */
+#define WM_DAC_CTRL1		0x13	/* DAC control bits */
+#define WM_MUTE			0x14	/* mute controls */
+#define WM_DAC_CTRL2		0x15	/* de-emphasis and zefo-flag */
+#define WM_INT_CTRL		0x16	/* interface control */
+#define WM_MASTER		0x17	/* master clock and mode */
+#define WM_POWERDOWN		0x18	/* power-down controls */
+#define WM_ADC_GAIN		0x19	/* ADC gain L(19)/R(1a) */
+#define WM_ADC_MUX		0x1b	/* input MUX */
+#define WM_OUT_MUX1		0x1c	/* output MUX */
+#define WM_OUT_MUX2		0x1e	/* output MUX */
+#define WM_RESET		0x1f	/* software reset */
+
+
+/*
+ * Logarithmic volume values for WM8770
+ * Computed as 20 * Log10(255 / x)
+ */
+static unsigned char wm_vol[256] = {
+	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
+	23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
+	17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
+	13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
+	11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
+	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
+	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0
+};
+
+#define WM_VOL_MAX	(sizeof(wm_vol) - 1)
+#define WM_VOL_MUTE	0x8000
+
 static akm4xxx_t akm_phase22 __devinitdata = {
 	.type = SND_AK4524,
 	.num_dacs = 2,
@@ -124,6 +165,684 @@
 	0x00,	/* GPIO_STATE2 */
 };
 
+static unsigned char phase28_eeprom[] __devinitdata = {
+	0x0b,	/* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+	0x80,	/* ACLINK: I2S */
+	0xfc,	/* I2S: vol, 96k, 24bit, 192k */
+	0xc3,	/* SPDIF: out-en, out-int, spdif-in */
+	0xff,	/* GPIO_DIR */
+	0xff,	/* GPIO_DIR1 */
+	0x5f,	/* GPIO_DIR2 */
+	0x00,	/* GPIO_MASK */
+	0x00,	/* GPIO_MASK1 */
+	0x00,	/* GPIO_MASK2 */
+	0x00,	/* GPIO_STATE */
+	0x00,	/* GPIO_STATE1 */
+	0x00,	/* GPIO_STATE2 */
+};
+
+/*
+ * write data in the SPI mode
+ */
+static void phase28_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits)
+{
+	unsigned int tmp;
+	int i;
+
+	tmp = snd_ice1712_gpio_read(ice);
+
+	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
+					 PHASE28_WM_CS));
+	tmp |= PHASE28_WM_RW;
+	tmp &= ~cs;
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+
+	for (i = bits - 1; i >= 0; i--) {
+		tmp &= ~PHASE28_SPI_CLK;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+		if (data & (1 << i))
+			tmp |= PHASE28_SPI_MOSI;
+		else
+			tmp &= ~PHASE28_SPI_MOSI;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+		tmp |= PHASE28_SPI_CLK;
+		snd_ice1712_gpio_write(ice, tmp);
+		udelay(1);
+	}
+
+	tmp &= ~PHASE28_SPI_CLK;
+	tmp |= cs;
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+	tmp |= PHASE28_SPI_CLK;
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+}
+
+/*
+ * get the current register value of WM codec
+ */
+static unsigned short wm_get(ice1712_t *ice, int reg)
+{
+	reg <<= 1;
+	return ((unsigned short)ice->akm[0].images[reg] << 8) |
+		ice->akm[0].images[reg + 1];
+}
+
+/*
+ * set the register value of WM codec
+ */
+static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val)
+{
+	phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
+}
+
+/*
+ * set the register value of WM codec and remember it
+ */
+static void wm_put(ice1712_t *ice, int reg, unsigned short val)
+{
+	wm_put_nocache(ice, reg, val);
+	reg <<= 1;
+	ice->akm[0].images[reg] = val >> 8;
+	ice->akm[0].images[reg + 1] = val;
+}
+
+static void wm_set_vol(ice1712_t *ice, unsigned int index, unsigned short vol, unsigned short master)
+{
+	unsigned char nvol;
+
+	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+		nvol = 0;
+	else
+		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
+
+	wm_put(ice, index, nvol);
+	wm_put_nocache(ice, index, 0x180 | nvol);
+}
+
+/*
+ * DAC mute control
+ */
+#define wm_pcm_mute_info	phase28_mono_bool_info
+
+static int wm_pcm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	down(&ice->gpio_mutex);
+	ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
+	up(&ice->gpio_mutex);
+	return 0;
+}
+
+static int wm_pcm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned short nval, oval;
+	int change;
+
+	snd_ice1712_save_gpio_status(ice);
+	oval = wm_get(ice, WM_MUTE);
+	nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
+	if ((change = (nval != oval)))
+		wm_put(ice, WM_MUTE, nval);
+	snd_ice1712_restore_gpio_status(ice);
+
+	return change;
+}
+
+/*
+ * Master volume attenuation mixer control
+ */
+static int wm_master_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = WM_VOL_MAX;
+	return 0;
+}
+
+static int wm_master_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int i;
+	for (i=0; i<2; i++)
+		ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
+	return 0;
+}
+
+static int wm_master_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int ch, change = 0;
+
+	snd_ice1712_save_gpio_status(ice);
+	for (ch = 0; ch < 2; ch++) {
+		if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) {
+			int dac;
+			ice->spec.phase28.master[ch] &= WM_VOL_MUTE;
+			ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch];
+			for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+				wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
+					   ice->spec.phase28.vol[dac + ch],
+					   ice->spec.phase28.master[ch]);
+			change = 1;
+		}
+	}
+	snd_ice1712_restore_gpio_status(ice);
+	return change;
+}
+
+static int __devinit phase28_init(ice1712_t *ice)
+{
+	static unsigned short wm_inits_phase28[] = {
+		/* These come first to reduce init pop noise */
+		0x1b, 0x044,		/* ADC Mux (AC'97 source) */
+		0x1c, 0x00B,		/* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
+		0x1d, 0x009,		/* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
+
+		0x18, 0x000,		/* All power-up */
+
+		0x16, 0x122,		/* I2S, normal polarity, 24bit */
+		0x17, 0x022,		/* 256fs, slave mode */
+		0x00, 0,		/* DAC1 analog mute */
+		0x01, 0,		/* DAC2 analog mute */
+		0x02, 0,		/* DAC3 analog mute */
+		0x03, 0,		/* DAC4 analog mute */
+		0x04, 0,		/* DAC5 analog mute */
+		0x05, 0,		/* DAC6 analog mute */
+		0x06, 0,		/* DAC7 analog mute */
+		0x07, 0,		/* DAC8 analog mute */
+		0x08, 0x100,		/* master analog mute */
+		0x09, 0xff,		/* DAC1 digital full */
+		0x0a, 0xff,		/* DAC2 digital full */
+		0x0b, 0xff,		/* DAC3 digital full */
+		0x0c, 0xff,		/* DAC4 digital full */
+		0x0d, 0xff,		/* DAC5 digital full */
+		0x0e, 0xff,		/* DAC6 digital full */
+		0x0f, 0xff,		/* DAC7 digital full */
+		0x10, 0xff,		/* DAC8 digital full */
+		0x11, 0x1ff,		/* master digital full */
+		0x12, 0x000,		/* phase normal */
+		0x13, 0x090,		/* unmute DAC L/R */
+		0x14, 0x000,		/* all unmute */
+		0x15, 0x000,		/* no deemphasis, no ZFLG */
+		0x19, 0x000,		/* -12dB ADC/L */
+		0x1a, 0x000,		/* -12dB ADC/R */
+		(unsigned short)-1
+	};
+
+	unsigned int tmp;
+	akm4xxx_t *ak;
+	unsigned short *p;
+	int i;
+
+	ice->num_total_dacs = 8;
+	ice->num_total_adcs = 2;
+
+	// Initialize analog chips
+	ak = ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+	if (!ak)
+		return -ENOMEM;
+	ice->akm_codecs = 1;
+
+	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
+
+	/* reset the wm codec as the SPI mode */
+	snd_ice1712_save_gpio_status(ice);
+	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
+
+	tmp = snd_ice1712_gpio_read(ice);
+	tmp &= ~PHASE28_WM_RESET;
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+	tmp |= PHASE28_WM_CS;
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+	tmp |= PHASE28_WM_RESET;
+	snd_ice1712_gpio_write(ice, tmp);
+	udelay(1);
+
+	p = wm_inits_phase28;
+	for (; *p != (unsigned short)-1; p += 2)
+		wm_put(ice, p[0], p[1]);
+
+	snd_ice1712_restore_gpio_status(ice);
+
+	ice->spec.phase28.master[0] = WM_VOL_MUTE;
+	ice->spec.phase28.master[1] = WM_VOL_MUTE;
+	for (i = 0; i < ice->num_total_dacs; i++) {
+		ice->spec.phase28.vol[i] = WM_VOL_MUTE;
+		wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
+	}
+
+	return 0;
+}
+
+/*
+ * DAC volume attenuation mixer control
+ */
+static int wm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	int voices = kcontrol->private_value >> 8;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = voices;
+	uinfo->value.integer.min = 0;		/* mute (-101dB) */
+	uinfo->value.integer.max = 0x7F;	/* 0dB */
+	return 0;
+}
+
+static int wm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int i, ofs, voices;
+
+	voices = kcontrol->private_value >> 8;
+	ofs = kcontrol->private_value & 0xff;
+	for (i = 0; i < voices; i++)
+		ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
+	return 0;
+}
+
+static int wm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int i, idx, ofs, voices;
+	int change = 0;
+
+	voices = kcontrol->private_value >> 8;
+	ofs = kcontrol->private_value & 0xff;
+	snd_ice1712_save_gpio_status(ice);
+	for (i = 0; i < voices; i++) {
+		idx  = WM_DAC_ATTEN + ofs + i;
+		if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) {
+			ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE;
+			ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i];
+			wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
+				   ice->spec.phase28.master[i]);
+			change = 1;
+		}
+	}
+	snd_ice1712_restore_gpio_status(ice);
+	return change;
+}
+
+/*
+ * WM8770 mute control
+ */
+static int wm_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = kcontrol->private_value >> 8;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int wm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int voices, ofs, i;
+
+	voices = kcontrol->private_value >> 8;
+	ofs = kcontrol->private_value & 0xFF;
+
+	for (i = 0; i < voices; i++)
+		ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+	return 0;
+}
+
+static int wm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change = 0, voices, ofs, i;
+
+	voices = kcontrol->private_value >> 8;
+	ofs = kcontrol->private_value & 0xFF;
+
+	snd_ice1712_save_gpio_status(ice);
+	for (i = 0; i < voices; i++) {
+		int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+		if (ucontrol->value.integer.value[i] != val) {
+			ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
+			ice->spec.phase28.vol[ofs + i] |=
+				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+			wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
+				   ice->spec.phase28.master[i]);
+			change = 1;
+		}
+	}
+	snd_ice1712_restore_gpio_status(ice);
+
+	return change;
+}
+
+/*
+ * WM8770 master mute control
+ */
+static int wm_master_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int wm_master_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
+	ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
+	return 0;
+}
+
+static int wm_master_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change = 0, i;
+
+	snd_ice1712_save_gpio_status(ice);
+	for (i = 0; i < 2; i++) {
+		int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
+		if (ucontrol->value.integer.value[i] != val) {
+			int dac;
+			ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
+			ice->spec.phase28.master[i] |=
+				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+			for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+				wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
+					   ice->spec.phase28.vol[dac + i],
+					   ice->spec.phase28.master[i]);
+			change = 1;
+		}
+	}
+	snd_ice1712_restore_gpio_status(ice);
+
+	return change;
+}
+
+/* digital master volume */
+#define PCM_0dB 0xff
+#define PCM_RES 128	/* -64dB */
+#define PCM_MIN (PCM_0dB - PCM_RES)
+static int wm_pcm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;		/* mute (-64dB) */
+	uinfo->value.integer.max = PCM_RES;	/* 0dB */
+	return 0;
+}
+
+static int wm_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned short val;
+
+	down(&ice->gpio_mutex);
+	val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+	val = val > PCM_MIN ? (val - PCM_MIN) : 0;
+	ucontrol->value.integer.value[0] = val;
+	up(&ice->gpio_mutex);
+	return 0;
+}
+
+static int wm_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned short ovol, nvol;
+	int change = 0;
+
+	snd_ice1712_save_gpio_status(ice);
+	nvol = ucontrol->value.integer.value[0];
+	nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
+	ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+	if (ovol != nvol) {
+		wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
+		wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
+		change = 1;
+	}
+	snd_ice1712_restore_gpio_status(ice);
+	return change;
+}
+
+/*
+ */
+static int phase28_mono_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+/*
+ * Deemphasis
+ */
+#define phase28_deemp_info	phase28_mono_bool_info
+
+static int phase28_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+	return 0;
+}
+
+static int phase28_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int temp, temp2;
+	temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
+	if (ucontrol->value.integer.value[0])
+		temp |= 0xf;
+	else
+		temp &= ~0xf;
+	if (temp != temp2) {
+		wm_put(ice, WM_DAC_CTRL2, temp);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * ADC Oversampling
+ */
+static int phase28_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[2] = { "128x", "64x"	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+
+        return 0;
+}
+
+static int phase28_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
+	return 0;
+}
+
+static int phase28_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	int temp, temp2;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+	temp2 = temp = wm_get(ice, WM_MASTER);
+
+	if (ucontrol->value.enumerated.item[0])
+		temp |= 0x8;
+	else
+		temp &= ~0x8;
+
+	if (temp != temp2) {
+		wm_put(ice, WM_MASTER, temp);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t phase28_dac_controls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = wm_master_mute_info,
+		.get = wm_master_mute_get,
+		.put = wm_master_mute_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.info = wm_master_vol_info,
+		.get = wm_master_vol_get,
+		.put = wm_master_vol_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Front Playback Switch",
+		.info = wm_mute_info,
+		.get = wm_mute_get,
+		.put = wm_mute_put,
+		.private_value = (2 << 8) | 0
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Front Playback Volume",
+		.info = wm_vol_info,
+		.get = wm_vol_get,
+		.put = wm_vol_put,
+		.private_value = (2 << 8) | 0
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Rear Playback Switch",
+		.info = wm_mute_info,
+		.get = wm_mute_get,
+		.put = wm_mute_put,
+		.private_value = (2 << 8) | 2
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Rear Playback Volume",
+		.info = wm_vol_info,
+		.get = wm_vol_get,
+		.put = wm_vol_put,
+		.private_value = (2 << 8) | 2
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Center Playback Switch",
+		.info = wm_mute_info,
+		.get = wm_mute_get,
+		.put = wm_mute_put,
+		.private_value = (1 << 8) | 4
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Center Playback Volume",
+		.info = wm_vol_info,
+		.get = wm_vol_get,
+		.put = wm_vol_put,
+		.private_value = (1 << 8) | 4
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "LFE Playback Switch",
+		.info = wm_mute_info,
+		.get = wm_mute_get,
+		.put = wm_mute_put,
+		.private_value = (1 << 8) | 5
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "LFE Playback Volume",
+		.info = wm_vol_info,
+		.get = wm_vol_get,
+		.put = wm_vol_put,
+		.private_value = (1 << 8) | 5
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Side Playback Switch",
+		.info = wm_mute_info,
+		.get = wm_mute_get,
+		.put = wm_mute_put,
+		.private_value = (2 << 8) | 6
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Side Playback Volume",
+		.info = wm_vol_info,
+		.get = wm_vol_get,
+		.put = wm_vol_put,
+		.private_value = (2 << 8) | 6
+	}
+};
+
+static snd_kcontrol_new_t wm_controls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Switch",
+		.info = wm_pcm_mute_info,
+		.get = wm_pcm_mute_get,
+		.put = wm_pcm_mute_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Volume",
+		.info = wm_pcm_vol_info,
+		.get = wm_pcm_vol_get,
+		.put = wm_pcm_vol_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "DAC Deemphasis Switch",
+		.info = phase28_deemp_info,
+		.get = phase28_deemp_get,
+		.put = phase28_deemp_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "ADC Oversampling",
+		.info = phase28_oversampling_info,
+		.get = phase28_oversampling_get,
+		.put = phase28_oversampling_put
+	}
+};
+
+static int __devinit phase28_add_controls(ice1712_t *ice)
+{
+	unsigned int i, counts;
+	int err;
+
+	counts = ARRAY_SIZE(phase28_dac_controls);
+	for (i = 0; i < counts; i++) {
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
+		if (err < 0)
+			return err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
 struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
 	{
 		.subvendor = VT1724_SUBDEVICE_PHASE22,
@@ -134,5 +853,14 @@
 		.eeprom_size = sizeof(phase22_eeprom),
 		.eeprom_data = phase22_eeprom,
 	},
+	{
+		.subvendor = VT1724_SUBDEVICE_PHASE28,
+		.name = "Terratec PHASE 28",
+		.model = "phase28",
+		.chip_init = phase28_init,
+		.build_controls = phase28_add_controls,
+		.eeprom_size = sizeof(phase28_eeprom),
+		.eeprom_data = phase28_eeprom,
+	},
 	{ } /* terminator */
 };
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
index 6230cf1..13e841b 100644
--- a/sound/pci/ice1712/phase.h
+++ b/sound/pci/ice1712/phase.h
@@ -24,11 +24,28 @@
  *
  */      
 
-#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"
+#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
+                          "{Terratec,Phase 28},"
 
 #define VT1724_SUBDEVICE_PHASE22	0x3b155011
+#define VT1724_SUBDEVICE_PHASE28	0x3b154911
 
 /* entry point */
 extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
 
+/* PHASE28 GPIO bits */
+#define PHASE28_SPI_MISO	(1 << 21)
+#define PHASE28_WM_RESET	(1 << 20)
+#define PHASE28_SPI_CLK		(1 << 19)
+#define PHASE28_SPI_MOSI	(1 << 18)
+#define PHASE28_WM_RW		(1 << 17)
+#define PHASE28_AC97_RESET	(1 << 16)
+#define PHASE28_DIGITAL_SEL1	(1 << 15)
+#define PHASE28_HP_SEL		(1 << 14)
+#define PHASE28_WM_CS		(1 << 12)
+#define PHASE28_AC97_COMMIT	(1 << 11)
+#define PHASE28_AC97_ADDR	(1 << 10)
+#define PHASE28_AC97_DATA_LOW	(1 << 9)
+#define PHASE28_AC97_DATA_HIGH	(1 << 8)
+#define PHASE28_AC97_DATA_MASK	0xFF
 #endif /* __SOUND_PHASE */
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 3bd9262..ab61e38 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -110,6 +110,15 @@
 		.eeprom_size = sizeof(k8x800_eeprom),
 		.eeprom_data = k8x800_eeprom,
 	},
+	{
+		.subvendor = VT1720_SUBDEVICE_SN25P,
+		.name = "Shuttle SN25P",
+		/* identical with k8x800 */
+		.chip_init = k8x800_init,
+		.build_controls = k8x800_add_controls,
+		.eeprom_size = sizeof(k8x800_eeprom),
+		.eeprom_data = k8x800_eeprom,
+	},
 	{ } /* terminator */
 };
 
diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h
index f949eb8..0b1b0ee 100644
--- a/sound/pci/ice1712/vt1720_mobo.h
+++ b/sound/pci/ice1712/vt1720_mobo.h
@@ -27,12 +27,14 @@
 #define VT1720_MOBO_DEVICE_DESC        "{Albatron,K8X800 Pro II},"\
 				       "{Chaintech,ZNF3-150},"\
 				       "{Chaintech,ZNF3-250},"\
-				       "{Chaintech,9CJS},"
+				       "{Chaintech,9CJS},"\
+				       "{Shuttle,SN25P},"
 
 #define VT1720_SUBDEVICE_K8X800		0xf217052c
 #define VT1720_SUBDEVICE_ZNF3_150	0x0f2741f6
 #define VT1720_SUBDEVICE_ZNF3_250	0x0f2745f6
 #define VT1720_SUBDEVICE_9CJS		0x0f272327
+#define VT1720_SUBDEVICE_SN25P		0x97123650
 
 extern struct snd_ice1712_card_info  snd_vt1720_mobo_cards[];
 
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 8b33b12..9c5710d 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -2849,7 +2849,7 @@
 
 static int __init alsa_card_intel8x0_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_intel8x0_exit(void)
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 67da096..f655cf9 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -500,6 +500,8 @@
 			res = 0xffff;
 		}
 	}
+	if (reg == AC97_GPIO_STATUS)
+		iagetword(chip, 0); /* clear semaphore */
 	return res;
 }
 
@@ -1450,7 +1452,7 @@
 
 static int __init alsa_card_intel8x0m_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_intel8x0m_exit(void)
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index bb1de20..79d8eda 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2541,7 +2541,7 @@
 
 static int __init alsa_card_korg1212_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_korg1212_exit(void)
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 2cf3308..096f151 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -779,6 +779,12 @@
 				   (e.g. for IrDA on Dell Inspirons) */
 };
 
+struct m3_hv_quirk {
+	u16 vendor, device, subsystem_vendor, subsystem_device;
+	u32 config;		/* ALLEGRO_CONFIG hardware volume bits */
+	int is_omnibook;	/* Do HP OmniBook GPIO magic? */
+};
+
 struct m3_list {
 	int curlen;
 	int mem_addr;
@@ -828,6 +834,7 @@
 
 	struct pci_dev *pci;
 	struct m3_quirk *quirk;
+	struct m3_hv_quirk *hv_quirk;
 
 	int dacs_active;
 	int timer_users;
@@ -851,6 +858,11 @@
 	m3_dma_t *substreams;
 
 	spinlock_t reg_lock;
+	spinlock_t ac97_lock;
+
+	snd_kcontrol_t *master_switch;
+	snd_kcontrol_t *master_volume;
+	struct tasklet_struct hwvol_tq;
 
 #ifdef CONFIG_PM
 	u16 *suspend_mem;
@@ -968,6 +980,71 @@
 	{ NULL }
 };
 
+/* These values came from the Windows driver. */
+static struct m3_hv_quirk m3_hv_quirk_list[] = {
+	/* Allegro chips */
+	{ 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x0E11, 0xB112, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x0E11, 0xB114, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x103C, 0x0012, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x103C, 0x0018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x103C, 0x001C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x103C, 0x001D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x103C, 0x001E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x107B, 0x3350, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x10F7, 0x8338, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x10F7, 0x833C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x10F7, 0x833D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x10F7, 0x833E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x10F7, 0x833F, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x13BD, 0x1018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x13BD, 0x1019, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x13BD, 0x101A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x14FF, 0x0F03, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x14FF, 0x0F04, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x14FF, 0x0F05, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x156D, 0xB400, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x156D, 0xB795, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x156D, 0xB797, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x156D, 0xC700, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+	{ 0x125D, 0x1988, 0x1033, 0x80F1, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x103C, 0x001A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, /* HP OmniBook 6100 */
+	{ 0x125D, 0x1988, 0x107B, 0x340A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x107B, 0x3450, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x109F, 0x3134, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x109F, 0x3161, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x144D, 0x3280, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x144D, 0x3281, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x144D, 0xC002, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x144D, 0xC003, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x1509, 0x1740, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x1610, 0x0010, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x1988, 0x1042, 0x1042, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1988, 0x107B, 0x9500, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1988, 0x14FF, 0x0F06, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1988, 0x1558, 0x8586, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1988, 0x161F, 0x2011, HV_CTRL_ENABLE, 0 },
+	/* Maestro3 chips */
+	{ 0x125D, 0x1998, 0x103C, 0x000E, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x103C, 0x0010, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 6000 */
+	{ 0x125D, 0x1998, 0x103C, 0x0011, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 500 */
+	{ 0x125D, 0x1998, 0x103C, 0x001B, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x104D, 0x80A6, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x104D, 0x80AA, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x107B, 0x5300, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x110A, 0x1998, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x13BD, 0x1015, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x13BD, 0x101C, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x13BD, 0x1802, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x1599, 0x0715, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x1998, 0x5643, 0x5643, HV_CTRL_ENABLE, 0 },
+	{ 0x125D, 0x199A, 0x144D, 0x3260, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x199A, 0x144D, 0x3261, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x199A, 0x144D, 0xC000, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+	{ 0x125D, 0x199A, 0x144D, 0xC001, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+	{ 0 }
+};
 
 /*
  * lowlevel functions
@@ -1565,6 +1642,68 @@
 	}
 }
 
+static void snd_m3_update_hw_volume(unsigned long private_data)
+{
+	m3_t *chip = (m3_t *) private_data;
+	int x, val;
+	unsigned long flags;
+
+	/* Figure out which volume control button was pushed,
+	   based on differences from the default register
+	   values. */
+	x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee;
+
+	/* Reset the volume control registers. */
+	outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE);
+	outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE);
+	outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER);
+	outb(0x88, chip->iobase + HW_VOL_COUNTER_MASTER);
+
+	if (!chip->master_switch || !chip->master_volume)
+		return;
+
+	/* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
+	spin_lock_irqsave(&chip->ac97_lock, flags);
+
+	val = chip->ac97->regs[AC97_MASTER_VOL];
+	switch (x) {
+	case 0x88:
+		/* mute */
+		val ^= 0x8000;
+		chip->ac97->regs[AC97_MASTER_VOL] = val;
+		outw(val, chip->iobase + CODEC_DATA);
+		outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_switch->id);
+		break;
+	case 0xaa:
+		/* volume up */
+		if ((val & 0x7f) > 0)
+			val--;
+		if ((val & 0x7f00) > 0)
+			val -= 0x0100;
+		chip->ac97->regs[AC97_MASTER_VOL] = val;
+		outw(val, chip->iobase + CODEC_DATA);
+		outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_volume->id);
+		break;
+	case 0x66:
+		/* volume down */
+		if ((val & 0x7f) < 0x1f)
+			val++;
+		if ((val & 0x7f00) < 0x1f00)
+			val += 0x0100;
+		chip->ac97->regs[AC97_MASTER_VOL] = val;
+		outw(val, chip->iobase + CODEC_DATA);
+		outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &chip->master_volume->id);
+		break;
+	}
+	spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
 static irqreturn_t
 snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -1576,7 +1715,10 @@
 
 	if (status == 0xff)
 		return IRQ_NONE;
-   
+
+	if (status & HV_INT_PENDING)
+		tasklet_hi_schedule(&chip->hwvol_tq);
+
 	/*
 	 * ack an assp int if its running
 	 * and has an int pending
@@ -1605,7 +1747,7 @@
 #endif
 
 	/* ack ints */
-	snd_m3_outw(chip, HOST_INT_STATUS, status);
+	outb(status, chip->iobase + HOST_INT_STATUS);
 
 	return IRQ_HANDLED;
 }
@@ -1842,24 +1984,32 @@
 snd_m3_ac97_read(ac97_t *ac97, unsigned short reg)
 {
 	m3_t *chip = ac97->private_data;
+	unsigned long flags;
+	unsigned short data;
 
 	if (snd_m3_ac97_wait(chip))
 		return 0xffff;
+	spin_lock_irqsave(&chip->ac97_lock, flags);
 	snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
 	if (snd_m3_ac97_wait(chip))
 		return 0xffff;
-	return snd_m3_inw(chip, CODEC_DATA);
+	data = snd_m3_inw(chip, CODEC_DATA);
+	spin_unlock_irqrestore(&chip->ac97_lock, flags);
+	return data;
 }
 
 static void
 snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
 {
 	m3_t *chip = ac97->private_data;
+	unsigned long flags;
 
 	if (snd_m3_ac97_wait(chip))
 		return;
+	spin_lock_irqsave(&chip->ac97_lock, flags);
 	snd_m3_outw(chip, val, CODEC_DATA);
 	snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+	spin_unlock_irqrestore(&chip->ac97_lock, flags);
 }
 
 
@@ -1968,6 +2118,7 @@
 {
 	ac97_bus_t *pbus;
 	ac97_template_t ac97;
+	snd_ctl_elem_id_t id;
 	int err;
 	static ac97_bus_ops_t ops = {
 		.write = snd_m3_ac97_write,
@@ -1988,6 +2139,15 @@
 	schedule_timeout(HZ / 10);
 	snd_ac97_write(chip->ac97, AC97_PCM, 0);
 
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, "Master Playback Switch");
+	chip->master_switch = snd_ctl_find_id(chip->card, &id);
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, "Master Playback Volume");
+	chip->master_volume = snd_ctl_find_id(chip->card, &id);
+
 	return 0;
 }
 
@@ -2293,6 +2453,7 @@
 snd_m3_chip_init(m3_t *chip)
 {
 	struct pci_dev *pcidev = chip->pci;
+	unsigned long io = chip->iobase;
 	u32 n;
 	u16 w;
 	u8 t; /* makes as much sense as 'n', no? */
@@ -2303,8 +2464,27 @@
 	       DISABLE_LEGACY);
 	pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
 
+	if (chip->hv_quirk && chip->hv_quirk->is_omnibook) {
+		/*
+		 * Volume buttons on some HP OmniBook laptops don't work
+		 * correctly. This makes them work for the most part.
+		 *
+		 * Volume up and down buttons on the laptop side work.
+		 * Fn+cursor_up (volme up) works.
+		 * Fn+cursor_down (volume down) doesn't work.
+		 * Fn+F7 (mute) works acts as volume up.
+		 */
+		outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK);
+		outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION);
+		outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA);
+		outw(0xffff, io + GPIO_MASK);
+	}
 	pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
-	n &= REDUCED_DEBOUNCE;
+	n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
+	if (chip->hv_quirk)
+		n |= chip->hv_quirk->config;
+	/* For some reason we must always use reduced debounce. */
+	n |= REDUCED_DEBOUNCE;
 	n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
 	pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
 
@@ -2332,6 +2512,12 @@
 
 	outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); 
 
+	outb(0x00, io + HARDWARE_VOL_CTRL);
+	outb(0x88, io + SHADOW_MIX_REG_VOICE);
+	outb(0x88, io + HW_VOL_COUNTER_VOICE);
+	outb(0x88, io + SHADOW_MIX_REG_MASTER);
+	outb(0x88, io + HW_VOL_COUNTER_MASTER);
+
 	return 0;
 } 
 
@@ -2341,7 +2527,7 @@
 	unsigned long io = chip->iobase;
 
 	/* TODO: MPU401 not supported yet */
-	outw(ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL);
+	outw(ASSP_INT_ENABLE | HV_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL);
 	outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
 	     io + ASSP_CONTROL_C);
 }
@@ -2367,7 +2553,7 @@
 		kfree(chip->substreams);
 	}
 	if (chip->iobase) {
-		snd_m3_outw(chip, HOST_INT_CTRL, 0); /* disable ints */
+		outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */
 	}
 
 #ifdef CONFIG_PM
@@ -2486,7 +2672,7 @@
 	m3_t *chip;
 	int i, err;
 	struct m3_quirk *quirk;
-	u16 subsystem_vendor, subsystem_device;
+	struct m3_hv_quirk *hv_quirk;
 	static snd_device_ops_t ops = {
 		.dev_free =	snd_m3_dev_free,
 	};
@@ -2524,18 +2710,25 @@
 	chip->pci = pci;
 	chip->irq = -1;
 
-	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
-	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
-
 	for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
-		if (subsystem_vendor == quirk->vendor &&
-		    subsystem_device == quirk->device) {
+		if (pci->subsystem_vendor == quirk->vendor &&
+		    pci->subsystem_device == quirk->device) {
 			printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name);
 			chip->quirk = quirk;
 			break;
 		}
 	}
 
+	for (hv_quirk = m3_hv_quirk_list; hv_quirk->vendor; hv_quirk++) {
+		if (pci->vendor == hv_quirk->vendor &&
+		    pci->device == hv_quirk->device &&
+		    pci->subsystem_vendor == hv_quirk->subsystem_vendor &&
+		    pci->subsystem_device == hv_quirk->subsystem_device) {
+			chip->hv_quirk = hv_quirk;
+			break;
+		}
+	}
+
 	chip->external_amp = enable_amp;
 	if (amp_gpio >= 0 && amp_gpio <= 0x0f)
 		chip->amp_gpio = amp_gpio;
@@ -2593,6 +2786,9 @@
 		return err;
 	}
 
+	spin_lock_init(&chip->ac97_lock);
+	tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
+
 	if ((err = snd_m3_mixer(chip)) < 0)
 		return err;
 
@@ -2702,7 +2898,7 @@
 	
 static int __init alsa_card_m3_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_m3_exit(void)
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 65bb0f4..082c0d0 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1431,7 +1431,7 @@
 
 static int __init alsa_card_mixart_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_mixart_exit(void)
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 356fbea..8a52091 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1645,7 +1645,7 @@
 
 static int __init alsa_card_nm256_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_nm256_exit(void)
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index b96acd5..b7b554d 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -2031,7 +2031,7 @@
 
 static int __init alsa_card_rme32_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_rme32_exit(void)
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 8e26668..10c4f45 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -2437,7 +2437,7 @@
 
 static int __init alsa_card_rme96_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_rme96_exit(void)
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 12efbf0..b35ed5f 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -4912,19 +4912,9 @@
 		release_firmware(fw);
 		return -EINVAL;
 	}
-#ifdef SNDRV_BIG_ENDIAN
-	{
-		int i;
-		u32 *src = (u32*)fw->data;
-		for (i = 0; i < ARRAY_SIZE(hdsp->firmware_cache); i++, src++)
-			hdsp->firmware_cache[i] = ((*src & 0x000000ff) << 16) |
-				((*src & 0x0000ff00) << 8)  |
-				((*src & 0x00ff0000) >> 8)  |
-				((*src & 0xff000000) >> 16);
-	}
-#else
+
 	memcpy(hdsp->firmware_cache, fw->data, sizeof(hdsp->firmware_cache));
-#endif
+
 	release_firmware(fw);
 		
 	hdsp->state |= HDSP_FirmwareCached;
@@ -5194,7 +5184,7 @@
 
 static int __init alsa_card_hdsp_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_hdsp_exit(void)
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 69cd81e..5861f23 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2664,7 +2664,7 @@
 
 static int __init alsa_card_hammerfall_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_hammerfall_exit(void)
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index cfd2c5f..60ecb2b 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1522,7 +1522,7 @@
 
 static int __init alsa_card_sonicvibes_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_sonicvibes_exit(void)
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index ad58e08..5d21cb8 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -184,7 +184,7 @@
 
 static int __init alsa_card_trident_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_trident_exit(void)
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 9b4d74d..bb322de 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -101,7 +101,7 @@
 module_param_array(ac97_quirk, charp, NULL, 0444);
 MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
 module_param_array(dxs_support, int, NULL, 0444);
-MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA)");
+MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)");
 
 
 /* pci ids */
@@ -302,6 +302,7 @@
 #define VIA_DXS_DISABLE	2
 #define VIA_DXS_48K	3
 #define VIA_DXS_NO_VRA	4
+#define VIA_DXS_SRC	5
 
 
 /*
@@ -380,6 +381,7 @@
 	struct via_rate_lock rates[2]; /* playback and capture */
 	unsigned int dxs_fixed: 1;	/* DXS channel accepts only 48kHz */
 	unsigned int no_vra: 1;		/* no need to set VRA on DXS channels */
+	unsigned int dxs_src: 1;	/* use full SRC capabilities of DXS */
 	unsigned int spdif_on: 1;	/* only spdif rates work to external DACs */
 
 	snd_pcm_t *pcms[2];
@@ -924,15 +926,17 @@
 	via82xx_t *chip = snd_pcm_substream_chip(substream);
 	viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
 	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ac97_rate = chip->dxs_src ? 48000 : runtime->rate;
 	int rate_changed;
 	u32 rbits;
 
-	if ((rate_changed = via_lock_rate(&chip->rates[0], runtime->rate)) < 0)
+	if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0)
 		return rate_changed;
 	if (rate_changed) {
 		snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
 				  chip->no_vra ? 48000 : runtime->rate);
-		snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+		snd_ac97_set_rate(chip->ac97, AC97_SPDIF,
+				  chip->no_vra ? 48000 : runtime->rate);
 	}
 	if (runtime->rate == 48000)
 		rbits = 0xfffff;
@@ -1074,6 +1078,12 @@
 		/* fixed DXS playback rate */
 		runtime->hw.rates = SNDRV_PCM_RATE_48000;
 		runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+	} else if (chip->dxs_src && viadev->reg_offset < 0x40) {
+		/* use full SRC capabilities of DXS */
+		runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS |
+				     SNDRV_PCM_RATE_8000_48000);
+		runtime->hw.rate_min = 8000;
+		runtime->hw.rate_max = 48000;
 	} else if (! ratep->rate) {
 		int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
 		runtime->hw.rates = chip->ac97->rates[idx];
@@ -2149,14 +2159,17 @@
 		{ .vendor = 0x1043, .device = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/
 		{ .vendor = 0x1043, .device = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */
 		{ .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ 
+		{ .vendor = 0x1043, .device = 0x812a, .action = VIA_DXS_SRC    }, /* ASUS A8V Deluxe */ 
 		{ .vendor = 0x1071, .device = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */
 		{ .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
 		{ .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
 		{ .vendor = 0x1106, .device = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */
 		{ .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
+		{ .vendor = 0x1106, .device = 0xc001, .action = VIA_DXS_SRC }, /* Insight P4-ITX */
 		{ .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
 		{ .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
 		{ .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
+		{ .vendor = 0x1462, .device = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */
 		{ .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
 		{ .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
 		{ .vendor = 0x1462, .device = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */
@@ -2166,6 +2179,7 @@
 		{ .vendor = 0x147b, .device = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
 		{ .vendor = 0x147b, .device = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
 		{ .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
+		{ .vendor = 0x14ff, .device = 0x0408, .action = VIA_DXS_NO_VRA }, /* Twinhead mobo */
 		{ .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
 		{ .vendor = 0x1584, .device = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
 		{ .vendor = 0x161f, .device = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
@@ -2288,6 +2302,10 @@
 				chip->dxs_fixed = 1;
 			else if (dxs_support[dev] == VIA_DXS_NO_VRA)
 				chip->no_vra = 1;
+			else if (dxs_support[dev] == VIA_DXS_SRC) {
+				chip->no_vra = 1;
+				chip->dxs_src = 1;
+			}
 		}
 		if ((err = snd_via8233_init_misc(chip, dev)) < 0)
 			goto __error;
@@ -2334,7 +2352,7 @@
 
 static int __init alsa_card_via82xx_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_via82xx_exit(void)
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index ea5c6f6..276ce52 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -938,7 +938,7 @@
  *
  */
 
-static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+static int snd_via82xx_chip_init(via82xx_t *chip)
 {
 	unsigned int val;
 	int max_count;
@@ -1233,7 +1233,7 @@
 
 static int __init alsa_card_via82xx_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_via82xx_exit(void)
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 4ffbb25..dca6bd2 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -260,7 +260,7 @@
 
 static int __init alsa_card_vx222_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_vx222_exit(void)
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 9f3ef22..5b5b624 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -360,7 +360,7 @@
 
 static int __init alsa_card_ymfpci_init(void)
 {
-	return pci_module_init(&driver);
+	return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ymfpci_exit(void)
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 05f1629..997cf37 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -1421,17 +1421,15 @@
 
 static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
-	unsigned int mask = 1;
-
 	switch (kcontrol->private_value) {
 	case YDSXGR_SPDIFOUTCTRL: break;
 	case YDSXGR_SPDIFINCTRL: break;
 	default: return -EINVAL;
 	}
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
+	uinfo->value.integer.max = 1;
 	return 0;
 }
 
@@ -1439,7 +1437,7 @@
 {
 	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
 	int reg = kcontrol->private_value;
-	unsigned int shift = 0, mask = 1, invert = 0;
+	unsigned int shift = 0, mask = 1;
 	
 	switch (kcontrol->private_value) {
 	case YDSXGR_SPDIFOUTCTRL: break;
@@ -1447,8 +1445,6 @@
 	default: return -EINVAL;
 	}
 	ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask;
-	if (invert)
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
 	return 0;
 }
 
@@ -1456,7 +1452,7 @@
 {
 	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
 	int reg = kcontrol->private_value;
-	unsigned int shift = 0, mask = 1, invert = 0;
+	unsigned int shift = 0, mask = 1;
 	int change;
 	unsigned int val, oval;
 	
@@ -1466,8 +1462,6 @@
 	default: return -EINVAL;
 	}
 	val = (ucontrol->value.integer.value[0] & mask);
-	if (invert)
-		val = mask - val;
 	val <<= shift;
 	spin_lock_irq(&chip->reg_lock);
 	oval = snd_ymfpci_readl(chip, reg);
@@ -1487,14 +1481,13 @@
 static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
 	unsigned int reg = kcontrol->private_value;
-	unsigned int mask = 16383;
 
 	if (reg < 0x80 || reg >= 0xc0)
 		return -EINVAL;
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 2;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
+	uinfo->value.integer.max = 16383;
 	return 0;
 }
 
@@ -1502,7 +1495,7 @@
 {
 	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
 	unsigned int reg = kcontrol->private_value;
-	unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+	unsigned int shift_left = 0, shift_right = 16, mask = 16383;
 	unsigned int val;
 	
 	if (reg < 0x80 || reg >= 0xc0)
@@ -1512,10 +1505,6 @@
 	spin_unlock_irq(&chip->reg_lock);
 	ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
 	ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
-	if (invert) {
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
-	}
 	return 0;
 }
 
@@ -1523,7 +1512,7 @@
 {
 	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
 	unsigned int reg = kcontrol->private_value;
-	unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+	unsigned int shift_left = 0, shift_right = 16, mask = 16383;
 	int change;
 	unsigned int val1, val2, oval;
 	
@@ -1531,10 +1520,6 @@
 		return -EINVAL;
 	val1 = ucontrol->value.integer.value[0] & mask;
 	val2 = ucontrol->value.integer.value[1] & mask;
-	if (invert) {
-		val1 = mask - val1;
-		val2 = mask - val2;
-	}
 	val1 <<= shift_left;
 	val2 <<= shift_right;
 	spin_lock_irq(&chip->reg_lock);
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 9329e99..f05d02f 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -6,6 +6,7 @@
 config SND_USB_AUDIO
 	tristate "USB Audio/MIDI driver"
 	depends on SND && USB
+	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
 	help
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index aae6614..a82412b 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -98,7 +98,7 @@
 #define MAX_PACKS	10
 #define MAX_PACKS_HS	(MAX_PACKS * 8)	/* in high speed mode */
 #define MAX_URBS	5	/* max. 20ms long packets */
-#define SYNC_URBS	2	/* always two urbs for sync */
+#define SYNC_URBS	4	/* always four urbs for sync */
 #define MIN_PACKS_URB	1	/* minimum 1 packet per urb */
 
 typedef struct snd_usb_substream snd_usb_substream_t;
@@ -177,7 +177,7 @@
 	unsigned int nurbs;			/* # urbs */
 	snd_urb_ctx_t dataurb[MAX_URBS];	/* data urb table */
 	snd_urb_ctx_t syncurb[SYNC_URBS];	/* sync urb table */
-	char syncbuf[SYNC_URBS * MAX_PACKS * 4]; /* sync buffer; it's so small - let's get static */
+	char syncbuf[SYNC_URBS * 4];	/* sync buffer; it's so small - let's get static */
 	char *tmpbuf;			/* temporary buffer for playback */
 
 	u64 formats;			/* format bitmasks (all or'ed) */
@@ -251,17 +251,13 @@
 {
 	unsigned char *cp = urb->transfer_buffer;
 	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
-	int i, offs;
 
-	urb->number_of_packets = ctx->packets;
 	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) {
-		urb->iso_frame_desc[i].length = 3;
-		urb->iso_frame_desc[i].offset = offs;
-		cp[0] = subs->freqn >> 2;
-		cp[1] = subs->freqn >> 10;
-		cp[2] = subs->freqn >> 18;
-	}
+	urb->iso_frame_desc[0].length = 3;
+	urb->iso_frame_desc[0].offset = 0;
+	cp[0] = subs->freqn >> 2;
+	cp[1] = subs->freqn >> 10;
+	cp[2] = subs->freqn >> 18;
 	return 0;
 }
 
@@ -277,18 +273,14 @@
 {
 	unsigned char *cp = urb->transfer_buffer;
 	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
-	int i, offs;
 
-	urb->number_of_packets = ctx->packets;
 	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) {
-		urb->iso_frame_desc[i].length = 4;
-		urb->iso_frame_desc[i].offset = offs;
-		cp[0] = subs->freqn;
-		cp[1] = subs->freqn >> 8;
-		cp[2] = subs->freqn >> 16;
-		cp[3] = subs->freqn >> 24;
-	}
+	urb->iso_frame_desc[0].length = 4;
+	urb->iso_frame_desc[0].offset = 0;
+	cp[0] = subs->freqn;
+	cp[1] = subs->freqn >> 8;
+	cp[2] = subs->freqn >> 16;
+	cp[3] = subs->freqn >> 24;
 	return 0;
 }
 
@@ -418,15 +410,11 @@
 				     snd_pcm_runtime_t *runtime,
 				     struct urb *urb)
 {
-	int i, offs;
 	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
 
-	urb->number_of_packets = ctx->packets;
 	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) {
-		urb->iso_frame_desc[i].length = 3;
-		urb->iso_frame_desc[i].offset = offs;
-	}
+	urb->iso_frame_desc[0].length = 3;
+	urb->iso_frame_desc[0].offset = 0;
 	return 0;
 }
 
@@ -440,15 +428,11 @@
 					snd_pcm_runtime_t *runtime,
 					struct urb *urb)
 {
-	int i, offs;
 	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
 
-	urb->number_of_packets = ctx->packets;
 	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) {
-		urb->iso_frame_desc[i].length = 4;
-		urb->iso_frame_desc[i].offset = offs;
-	}
+	urb->iso_frame_desc[0].length = 4;
+	urb->iso_frame_desc[0].offset = 0;
 	return 0;
 }
 
@@ -462,31 +446,17 @@
 				    snd_pcm_runtime_t *runtime,
 				    struct urb *urb)
 {
-	int i;
-	unsigned int f, found;
-	unsigned char *cp = urb->transfer_buffer;
+	unsigned int f;
 	unsigned long flags;
 
-	found = 0;
-	for (i = 0; i < urb->number_of_packets; i++, cp += 4) {
-		if (urb->iso_frame_desc[i].status ||
-		    urb->iso_frame_desc[i].actual_length < 3)
-			continue;
-		f = combine_triple(cp) << 2;
-#if 0
-		if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) {
-			snd_printd(KERN_WARNING "requested frequency %d (%u,%03uHz) out of range (current nominal %d (%u,%03uHz))\n",
-				   f, f >> 14, (f & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1),
-				   subs->freqn, subs->freqn >> 14, (subs->freqn & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1));
-			continue;
+	if (urb->iso_frame_desc[0].status == 0 &&
+	    urb->iso_frame_desc[0].actual_length == 3) {
+		f = combine_triple((u8*)urb->transfer_buffer) << 2;
+		if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+			spin_lock_irqsave(&subs->lock, flags);
+			subs->freqm = f;
+			spin_unlock_irqrestore(&subs->lock, flags);
 		}
-#endif
-		found = f;
-	}
-	if (found) {
-		spin_lock_irqsave(&subs->lock, flags);
-		subs->freqm = found;
-		spin_unlock_irqrestore(&subs->lock, flags);
 	}
 
 	return 0;
@@ -502,22 +472,17 @@
 				       snd_pcm_runtime_t *runtime,
 				       struct urb *urb)
 {
-	int i;
-	unsigned int found;
-	unsigned char *cp = urb->transfer_buffer;
+	unsigned int f;
 	unsigned long flags;
 
-	found = 0;
-	for (i = 0; i < urb->number_of_packets; i++, cp += 4) {
-		if (urb->iso_frame_desc[i].status ||
-		    urb->iso_frame_desc[i].actual_length < 4)
-			continue;
-		found = combine_quad(cp) & 0x0fffffff;
-	}
-	if (found) {
-		spin_lock_irqsave(&subs->lock, flags);
-		subs->freqm = found;
-		spin_unlock_irqrestore(&subs->lock, flags);
+	if (urb->iso_frame_desc[0].status == 0 &&
+	    urb->iso_frame_desc[0].actual_length == 4) {
+		f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+		if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+			spin_lock_irqsave(&subs->lock, flags);
+			subs->freqm = f;
+			spin_unlock_irqrestore(&subs->lock, flags);
+		}
 	}
 
 	return 0;
@@ -600,6 +565,8 @@
 		/* set the buffer pointer */
 		urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;
 		subs->hwptr += offs;
+		if (subs->hwptr == runtime->buffer_size)
+			subs->hwptr = 0;
 	}
 	spin_unlock_irqrestore(&subs->lock, flags);
 	urb->transfer_buffer_length = offs * stride;
@@ -1039,22 +1006,19 @@
 			snd_urb_ctx_t *u = &subs->syncurb[i];
 			u->index = i;
 			u->subs = subs;
-			u->packets = nrpacks;
-			u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+			u->packets = 1;
+			u->urb = usb_alloc_urb(1, GFP_KERNEL);
 			if (! u->urb) {
 				release_substream_urbs(subs, 0);
 				return -ENOMEM;
 			}
-			u->urb->transfer_buffer = subs->syncbuf + i * nrpacks * 4;
-			u->urb->transfer_buffer_length = nrpacks * 4;
+			u->urb->transfer_buffer = subs->syncbuf + i * 4;
+			u->urb->transfer_buffer_length = 4;
 			u->urb->dev = subs->dev;
 			u->urb->pipe = subs->syncpipe;
 			u->urb->transfer_flags = URB_ISO_ASAP;
-			u->urb->number_of_packets = u->packets;
-			if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
-				u->urb->interval = 8;
-			else
-				u->urb->interval = 1;
+			u->urb->number_of_packets = 1;
+			u->urb->interval = 1 << subs->syncinterval;
 			u->urb->context = u;
 			u->urb->complete = snd_usb_complete_callback(snd_complete_sync_urb);
 		}
@@ -1272,7 +1236,17 @@
 			subs->syncpipe = usb_rcvisocpipe(dev, ep);
 		else
 			subs->syncpipe = usb_sndisocpipe(dev, ep);
-		subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+		    get_endpoint(alts, 1)->bRefresh >= 1 &&
+		    get_endpoint(alts, 1)->bRefresh <= 9)
+			subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+			subs->syncinterval = 1;
+		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+			 get_endpoint(alts, 1)->bInterval <= 16)
+			subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+		else
+			subs->syncinterval = 3;
 	}
 
 	/* always fill max packet size */
@@ -1990,10 +1964,11 @@
 			snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
 		snd_iprintf(buffer, "]\n");
 		snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
-		snd_iprintf(buffer, "    Momentary freq = %u Hz\n",
+		snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
 			    snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
 			    ? get_full_speed_hz(subs->freqm)
-			    : get_high_speed_hz(subs->freqm));
+			    : get_high_speed_hz(subs->freqm),
+			    subs->freqm >> 16, subs->freqm & 0xffff);
 	} else {
 		snd_iprintf(buffer, "  Status: Stop\n");
 	}
@@ -2183,17 +2158,15 @@
 /*
  * check if the device uses big-endian samples
  */
-static int is_big_endian_format(struct usb_device *dev, struct audioformat *fp)
+static int is_big_endian_format(snd_usb_audio_t *chip, struct audioformat *fp)
 {
-	/* M-Audio */
-	if (le16_to_cpu(dev->descriptor.idVendor) == 0x0763) {
-		/* Quattro: captured data only */
-		if (le16_to_cpu(dev->descriptor.idProduct) == 0x2001 &&
-		    fp->endpoint & USB_DIR_IN)
+	switch (chip->usb_id) {
+	case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
+		if (fp->endpoint & USB_DIR_IN)
 			return 1;
-		/* Audiophile USB */
-		if (le16_to_cpu(dev->descriptor.idProduct) == 0x2003)
-			return 1;
+		break;
+	case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+		return 1;
 	}
 	return 0;
 }
@@ -2207,7 +2180,7 @@
  * @format: the format tag (wFormatTag)
  * @fmt: the format type descriptor
  */
-static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_i_type(snd_usb_audio_t *chip, struct audioformat *fp,
 				     int format, unsigned char *fmt)
 {
 	int pcm_format;
@@ -2220,12 +2193,12 @@
 	switch (format) {
 	case 0: /* some devices don't define this correctly... */
 		snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
-			    dev->devnum, fp->iface, fp->altsetting);
+			    chip->dev->devnum, fp->iface, fp->altsetting);
 		/* fall-through */
 	case USB_AUDIO_FORMAT_PCM:
 		if (sample_width > sample_bytes * 8) {
 			snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
-				   dev->devnum, fp->iface, fp->altsetting,
+				   chip->dev->devnum, fp->iface, fp->altsetting,
 				   sample_width, sample_bytes);
 		}
 		/* check the format byte size */
@@ -2234,13 +2207,13 @@
 			pcm_format = SNDRV_PCM_FORMAT_S8;
 			break;
 		case 2:
-			if (is_big_endian_format(dev, fp))
+			if (is_big_endian_format(chip, fp))
 				pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */
 			else
 				pcm_format = SNDRV_PCM_FORMAT_S16_LE;
 			break;
 		case 3:
-			if (is_big_endian_format(dev, fp))
+			if (is_big_endian_format(chip, fp))
 				pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */
 			else
 				pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
@@ -2250,14 +2223,14 @@
 			break;
 		default:
 			snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
-				   dev->devnum, fp->iface, fp->altsetting, sample_width, sample_bytes);
+				   chip->dev->devnum, fp->iface,
+				   fp->altsetting, sample_width, sample_bytes);
 			break;
 		}
 		break;
 	case USB_AUDIO_FORMAT_PCM8:
 		/* Dallas DS4201 workaround */
-		if (le16_to_cpu(dev->descriptor.idVendor) == 0x04fa &&
-		    le16_to_cpu(dev->descriptor.idProduct) == 0x4201)
+		if (chip->usb_id == USB_ID(0x04fa, 0x4201))
 			pcm_format = SNDRV_PCM_FORMAT_S8;
 		else
 			pcm_format = SNDRV_PCM_FORMAT_U8;
@@ -2273,7 +2246,7 @@
 		break;
 	default:
 		snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
-			   dev->devnum, fp->iface, fp->altsetting, format);
+			   chip->dev->devnum, fp->iface, fp->altsetting, format);
 		break;
 	}
 	return pcm_format;
@@ -2290,13 +2263,13 @@
  * @offset: the start offset of descriptor pointing the rate type
  *          (7 for type I and II, 8 for type II)
  */
-static int parse_audio_format_rates(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_rates(snd_usb_audio_t *chip, struct audioformat *fp,
 				    unsigned char *fmt, int offset)
 {
 	int nr_rates = fmt[offset];
 	if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
 		snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
-				   dev->devnum, fp->iface, fp->altsetting);
+				   chip->dev->devnum, fp->iface, fp->altsetting);
 		return -1;
 	}
 
@@ -2343,7 +2316,7 @@
 /*
  * parse the format type I and III descriptors
  */
-static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_i(snd_usb_audio_t *chip, struct audioformat *fp,
 				int format, unsigned char *fmt)
 {
 	int pcm_format;
@@ -2355,7 +2328,7 @@
 		 */
 		pcm_format = SNDRV_PCM_FORMAT_S16_LE;
 	} else {
-		pcm_format = parse_audio_format_i_type(dev, fp, format, fmt);
+		pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
 		if (pcm_format < 0)
 			return -1;
 	}
@@ -2363,16 +2336,16 @@
 	fp->channels = fmt[4];
 	if (fp->channels < 1) {
 		snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
-			   dev->devnum, fp->iface, fp->altsetting, fp->channels);
+			   chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
 		return -1;
 	}
-	return parse_audio_format_rates(dev, fp, fmt, 7);
+	return parse_audio_format_rates(chip, fp, fmt, 7);
 }
 
 /*
  * prase the format type II descriptor
  */
-static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_ii(snd_usb_audio_t *chip, struct audioformat *fp,
 				 int format, unsigned char *fmt)
 {
 	int brate, framesize;
@@ -2387,7 +2360,7 @@
 		break;
 	default:
 		snd_printd(KERN_INFO "%d:%u:%d : unknown format tag 0x%x is detected.  processed as MPEG.\n",
-			   dev->devnum, fp->iface, fp->altsetting, format);
+			   chip->dev->devnum, fp->iface, fp->altsetting, format);
 		fp->format = SNDRV_PCM_FORMAT_MPEG;
 		break;
 	}
@@ -2396,10 +2369,10 @@
 	framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */
 	snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
 	fp->frame_size = framesize;
-	return parse_audio_format_rates(dev, fp, fmt, 8); /* fmt[8..] sample rates */
+	return parse_audio_format_rates(chip, fp, fmt, 8); /* fmt[8..] sample rates */
 }
 
-static int parse_audio_format(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format(snd_usb_audio_t *chip, struct audioformat *fp,
 			      int format, unsigned char *fmt, int stream)
 {
 	int err;
@@ -2407,29 +2380,30 @@
 	switch (fmt[3]) {
 	case USB_FORMAT_TYPE_I:
 	case USB_FORMAT_TYPE_III:
-		err = parse_audio_format_i(dev, fp, format, fmt);
+		err = parse_audio_format_i(chip, fp, format, fmt);
 		break;
 	case USB_FORMAT_TYPE_II:
-		err = parse_audio_format_ii(dev, fp, format, fmt);
+		err = parse_audio_format_ii(chip, fp, format, fmt);
 		break;
 	default:
 		snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
-			   dev->devnum, fp->iface, fp->altsetting, fmt[3]);
+			   chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
 		return -1;
 	}
 	fp->fmt_type = fmt[3];
 	if (err < 0)
 		return err;
 #if 1
-	/* FIXME: temporary hack for extigy */
+	/* FIXME: temporary hack for extigy/audigy 2 nx */
 	/* extigy apparently supports sample rates other than 48k
 	 * but not in ordinary way.  so we enable only 48k atm.
 	 */
-	if (le16_to_cpu(dev->descriptor.idVendor) == 0x041e && 
-	    le16_to_cpu(dev->descriptor.idProduct) == 0x3000) {
+	if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
+	    chip->usb_id == USB_ID(0x041e, 0x3020)) {
 		if (fmt[3] == USB_FORMAT_TYPE_I &&
 		    stream == SNDRV_PCM_STREAM_PLAYBACK &&
-		    fp->rates != SNDRV_PCM_RATE_48000)
+		    fp->rates != SNDRV_PCM_RATE_48000 &&
+		    fp->rates != SNDRV_PCM_RATE_96000)
 			return -1; /* use 48k only */
 	}
 #endif
@@ -2528,40 +2502,35 @@
 
 		/* some quirks for attributes here */
 
-		/* workaround for AudioTrak Optoplay */
-		if (le16_to_cpu(dev->descriptor.idVendor) == 0x0a92 &&
-		    le16_to_cpu(dev->descriptor.idProduct) == 0x0053) {
+		switch (chip->usb_id) {
+		case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
 			/* Optoplay sets the sample rate attribute although
 			 * it seems not supporting it in fact.
 			 */
 			fp->attributes &= ~EP_CS_ATTR_SAMPLE_RATE;
-		}
-
-		/* workaround for M-Audio Audiophile USB */
-		if (le16_to_cpu(dev->descriptor.idVendor) == 0x0763 &&
-		    le16_to_cpu(dev->descriptor.idProduct) == 0x2003) {
+			break;
+		case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
+		case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
 			/* doesn't set the sample rate attribute, but supports it */
 			fp->attributes |= EP_CS_ATTR_SAMPLE_RATE;
-		}
-
+			break;
+		case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
+		case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
+						an older model 77d:223) */
 		/*
 		 * plantronics headset and Griffin iMic have set adaptive-in
 		 * although it's really not...
 		 */
-		if ((le16_to_cpu(dev->descriptor.idVendor) == 0x047f &&
-		     le16_to_cpu(dev->descriptor.idProduct) == 0x0ca1) ||
-		    /* Griffin iMic (note that there is an older model 77d:223) */
-		    (le16_to_cpu(dev->descriptor.idVendor) == 0x077d &&
-		     le16_to_cpu(dev->descriptor.idProduct) == 0x07af)) {
 			fp->ep_attr &= ~EP_ATTR_MASK;
 			if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 				fp->ep_attr |= EP_ATTR_ADAPTIVE;
 			else
 				fp->ep_attr |= EP_ATTR_SYNC;
+			break;
 		}
 
 		/* ok, let's parse further... */
-		if (parse_audio_format(dev, fp, format, fmt, stream) < 0) {
+		if (parse_audio_format(chip, fp, format, fmt, stream) < 0) {
 			kfree(fp->rate_table);
 			kfree(fp);
 			continue;
@@ -2587,7 +2556,7 @@
  * disconnect streams
  * called from snd_usb_audio_disconnect()
  */
-static void snd_usb_stream_disconnect(struct list_head *head, struct usb_driver *driver)
+static void snd_usb_stream_disconnect(struct list_head *head)
 {
 	int idx;
 	snd_usb_stream_t *as;
@@ -2796,7 +2765,7 @@
 			.type = QUIRK_MIDI_FIXED_ENDPOINT,
 			.data = &ua25_ep
 		};
-		if (le16_to_cpu(chip->dev->descriptor.idProduct) == 0x002b)
+		if (chip->usb_id == USB_ID(0x0582, 0x002b))
 			return snd_usb_create_midi_interface(chip, iface,
 							     &ua700_quirk);
 		else
@@ -2959,6 +2928,25 @@
 	return 0;
 }
 
+static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
+{
+#if 0
+	/* TODO: enable this when high speed synchronization actually works */
+	u8 buf = 1;
+
+	snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
+			USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+			0, 0, &buf, 1, 1000);
+	if (buf == 0) {
+		snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
+				USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+				1, 2000, NULL, 0, 1000);
+		return -ENODEV;
+	}
+#endif
+	return 0;
+}
+
 
 /*
  * audio-interface quirks
@@ -3015,8 +3003,8 @@
 	snd_usb_audio_t *chip = entry->private_data;
 	if (! chip->shutdown)
 		snd_iprintf(buffer, "%04x:%04x\n", 
-			    le16_to_cpu(chip->dev->descriptor.idVendor),
-			    le16_to_cpu(chip->dev->descriptor.idProduct));
+			    USB_ID_VENDOR(chip->usb_id),
+			    USB_ID_PRODUCT(chip->usb_id));
 }
 
 static void snd_usb_audio_create_proc(snd_usb_audio_t *chip)
@@ -3086,8 +3074,11 @@
 	chip->index = idx;
 	chip->dev = dev;
 	chip->card = card;
+	chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+			      le16_to_cpu(dev->descriptor.idProduct));
 	INIT_LIST_HEAD(&chip->pcm_list);
 	INIT_LIST_HEAD(&chip->midi_list);
+	INIT_LIST_HEAD(&chip->mixer_list);
 
 	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
 		snd_usb_audio_free(chip);
@@ -3097,8 +3088,7 @@
 
 	strcpy(card->driver, "USB-Audio");
 	sprintf(component, "USB%04x:%04x",
-		le16_to_cpu(dev->descriptor.idVendor),
-		le16_to_cpu(dev->descriptor.idProduct));
+		USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
 	snd_component_add(card, component);
 
 	/* retrieve the device string as shortname */
@@ -3110,8 +3100,8 @@
       			       card->shortname, sizeof(card->shortname)) <= 0) {
 			/* no name available from anywhere, so use ID */
 			sprintf(card->shortname, "USB Device %#04x:%#04x",
-				le16_to_cpu(dev->descriptor.idVendor),
-				le16_to_cpu(dev->descriptor.idProduct));
+				USB_ID_VENDOR(chip->usb_id),
+				USB_ID_PRODUCT(chip->usb_id));
 		}
 	}
 
@@ -3142,8 +3132,6 @@
 
 	snd_usb_audio_create_proc(chip);
 
-	snd_card_set_dev(card, &dev->dev);
-
 	*rchip = chip;
 	return 0;
 }
@@ -3169,21 +3157,28 @@
 	snd_usb_audio_t *chip;
 	struct usb_host_interface *alts;
 	int ifnum;
+	u32 id;
 
 	alts = &intf->altsetting[0];
 	ifnum = get_iface_desc(alts)->bInterfaceNumber;
+	id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+		    le16_to_cpu(dev->descriptor.idProduct));
 
 	if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
 		goto __err_val;
 
 	/* SB Extigy needs special boot-up sequence */
 	/* if more models come, this will go to the quirk list. */
-	if (le16_to_cpu(dev->descriptor.idVendor) == 0x041e && 
-	    le16_to_cpu(dev->descriptor.idProduct) == 0x3000) {
+	if (id == USB_ID(0x041e, 0x3000)) {
 		if (snd_usb_extigy_boot_quirk(dev, intf) < 0)
 			goto __err_val;
 		config = dev->actconfig;
 	}
+	/* SB Audigy 2 NX needs its own boot-up magic, too */
+	if (id == USB_ID(0x041e, 0x3020)) {
+		if (snd_usb_audigy2nx_boot_quirk(dev) < 0)
+			goto __err_val;
+	}
 
 	/*
 	 * found a config.  now register to ALSA
@@ -3213,11 +3208,12 @@
 		}
 		for (i = 0; i < SNDRV_CARDS; i++)
 			if (enable[i] && ! usb_chip[i] &&
-			    (vid[i] == -1 || vid[i] == le16_to_cpu(dev->descriptor.idVendor)) &&
-			    (pid[i] == -1 || pid[i] == le16_to_cpu(dev->descriptor.idProduct))) {
+			    (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
+			    (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
 				if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
 					goto __error;
 				}
+				snd_card_set_dev(chip->card, &intf->dev);
 				break;
 			}
 		if (! chip) {
@@ -3281,11 +3277,15 @@
 		snd_card_disconnect(card);
 		/* release the pcm resources */
 		list_for_each(p, &chip->pcm_list) {
-			snd_usb_stream_disconnect(p, &usb_audio_driver);
+			snd_usb_stream_disconnect(p);
 		}
 		/* release the midi resources */
 		list_for_each(p, &chip->midi_list) {
-			snd_usbmidi_disconnect(p, &usb_audio_driver);
+			snd_usbmidi_disconnect(p);
+		}
+		/* release mixer resources */
+		list_for_each(p, &chip->mixer_list) {
+			snd_usb_mixer_disconnect(p);
 		}
 		usb_chip[chip->index] = NULL;
 		up(&register_mutex);
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index eecbf19..aedb42a 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -118,6 +118,11 @@
 /* maximum number of endpoints per interface */
 #define MIDI_MAX_ENDPOINTS 2
 
+/* handling of USB vendor/product ID pairs as 32-bit numbers */
+#define USB_ID(vendor, product) (((vendor) << 16) | (product))
+#define USB_ID_VENDOR(id) ((id) >> 16)
+#define USB_ID_PRODUCT(id) ((u16)(id))
+
 /*
  */
 
@@ -127,6 +132,7 @@
 	int index;
 	struct usb_device *dev;
 	snd_card_t *card;
+	u32 usb_id;
 	int shutdown;
 	int num_interfaces;
 
@@ -136,7 +142,7 @@
 	struct list_head midi_list;	/* list of midi interfaces */
 	int next_midi_device;
 
-	unsigned int ignore_ctl_error;	/* for mixer */
+	struct list_head mixer_list;	/* list of mixer interfaces */
 };
 
 /*
@@ -219,11 +225,12 @@
 int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
 
 int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif);
+void snd_usb_mixer_disconnect(struct list_head *p);
 
 int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk);
 void snd_usbmidi_input_stop(struct list_head* p);
 void snd_usbmidi_input_start(struct list_head* p);
-void snd_usbmidi_disconnect(struct list_head *p, struct usb_driver *driver);
+void snd_usbmidi_disconnect(struct list_head *p);
 
 /*
  * retrieve usb_interface descriptor from the host interface
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 5d32857..bee7006 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -912,7 +912,7 @@
 /*
  * Unlinks all URBs (must be done before the usb_device is deleted).
  */
-void snd_usbmidi_disconnect(struct list_head* p, struct usb_driver *driver)
+void snd_usbmidi_disconnect(struct list_head* p)
 {
 	snd_usb_midi_t* umidi;
 	int i;
@@ -955,88 +955,87 @@
  * such as internal control or synthesizer ports.
  */
 static struct {
-	__u16 vendor;
-	__u16 product;
+	u32 id;
 	int port;
 	const char *name_format;
 } snd_usbmidi_port_names[] = {
 	/* Roland UA-100 */
-	{0x0582, 0x0000, 2, "%s Control"},
+	{ USB_ID(0x0582, 0x0000), 2, "%s Control" },
 	/* Roland SC-8850 */
-	{0x0582, 0x0003, 0, "%s Part A"},
-	{0x0582, 0x0003, 1, "%s Part B"},
-	{0x0582, 0x0003, 2, "%s Part C"},
-	{0x0582, 0x0003, 3, "%s Part D"},
-	{0x0582, 0x0003, 4, "%s MIDI 1"},
-	{0x0582, 0x0003, 5, "%s MIDI 2"},
+	{ USB_ID(0x0582, 0x0003), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x0003), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x0003), 2, "%s Part C" },
+	{ USB_ID(0x0582, 0x0003), 3, "%s Part D" },
+	{ USB_ID(0x0582, 0x0003), 4, "%s MIDI 1" },
+	{ USB_ID(0x0582, 0x0003), 5, "%s MIDI 2" },
 	/* Roland U-8 */
-	{0x0582, 0x0004, 0, "%s MIDI"},
-	{0x0582, 0x0004, 1, "%s Control"},
+	{ USB_ID(0x0582, 0x0004), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x0004), 1, "%s Control" },
 	/* Roland SC-8820 */
-	{0x0582, 0x0007, 0, "%s Part A"},
-	{0x0582, 0x0007, 1, "%s Part B"},
-	{0x0582, 0x0007, 2, "%s MIDI"},
+	{ USB_ID(0x0582, 0x0007), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x0007), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x0007), 2, "%s MIDI" },
 	/* Roland SK-500 */
-	{0x0582, 0x000b, 0, "%s Part A"},
-	{0x0582, 0x000b, 1, "%s Part B"},
-	{0x0582, 0x000b, 2, "%s MIDI"},
+	{ USB_ID(0x0582, 0x000b), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x000b), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x000b), 2, "%s MIDI" },
 	/* Roland SC-D70 */
-	{0x0582, 0x000c, 0, "%s Part A"},
-	{0x0582, 0x000c, 1, "%s Part B"},
-	{0x0582, 0x000c, 2, "%s MIDI"},
+	{ USB_ID(0x0582, 0x000c), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x000c), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x000c), 2, "%s MIDI" },
 	/* Edirol UM-880 */
-	{0x0582, 0x0014, 8, "%s Control"},
+	{ USB_ID(0x0582, 0x0014), 8, "%s Control" },
 	/* Edirol SD-90 */
-	{0x0582, 0x0016, 0, "%s Part A"},
-	{0x0582, 0x0016, 1, "%s Part B"},
-	{0x0582, 0x0016, 2, "%s MIDI 1"},
-	{0x0582, 0x0016, 3, "%s MIDI 2"},
+	{ USB_ID(0x0582, 0x0016), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x0016), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x0016), 2, "%s MIDI 1" },
+	{ USB_ID(0x0582, 0x0016), 3, "%s MIDI 2" },
 	/* Edirol UM-550 */
-	{0x0582, 0x0023, 5, "%s Control"},
+	{ USB_ID(0x0582, 0x0023), 5, "%s Control" },
 	/* Edirol SD-20 */
-	{0x0582, 0x0027, 0, "%s Part A"},
-	{0x0582, 0x0027, 1, "%s Part B"},
-	{0x0582, 0x0027, 2, "%s MIDI"},
+	{ USB_ID(0x0582, 0x0027), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x0027), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x0027), 2, "%s MIDI" },
 	/* Edirol SD-80 */
-	{0x0582, 0x0029, 0, "%s Part A"},
-	{0x0582, 0x0029, 1, "%s Part B"},
-	{0x0582, 0x0029, 2, "%s MIDI 1"},
-	{0x0582, 0x0029, 3, "%s MIDI 2"},
+	{ USB_ID(0x0582, 0x0029), 0, "%s Part A" },
+	{ USB_ID(0x0582, 0x0029), 1, "%s Part B" },
+	{ USB_ID(0x0582, 0x0029), 2, "%s MIDI 1" },
+	{ USB_ID(0x0582, 0x0029), 3, "%s MIDI 2" },
 	/* Edirol UA-700 */
-	{0x0582, 0x002b, 0, "%s MIDI"},
-	{0x0582, 0x002b, 1, "%s Control"},
+	{ USB_ID(0x0582, 0x002b), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x002b), 1, "%s Control" },
 	/* Roland VariOS */
-	{0x0582, 0x002f, 0, "%s MIDI"},
-	{0x0582, 0x002f, 1, "%s External MIDI"},
-	{0x0582, 0x002f, 2, "%s Sync"},
+	{ USB_ID(0x0582, 0x002f), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x002f), 1, "%s External MIDI" },
+	{ USB_ID(0x0582, 0x002f), 2, "%s Sync" },
 	/* Edirol PCR */
-	{0x0582, 0x0033, 0, "%s MIDI"},
-	{0x0582, 0x0033, 1, "%s 1"},
-	{0x0582, 0x0033, 2, "%s 2"},
+	{ USB_ID(0x0582, 0x0033), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x0033), 1, "%s 1" },
+	{ USB_ID(0x0582, 0x0033), 2, "%s 2" },
 	/* BOSS GS-10 */
-	{0x0582, 0x003b, 0, "%s MIDI"},
-	{0x0582, 0x003b, 1, "%s Control"},
+	{ USB_ID(0x0582, 0x003b), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x003b), 1, "%s Control" },
 	/* Edirol UA-1000 */
-	{0x0582, 0x0044, 0, "%s MIDI"},
-	{0x0582, 0x0044, 1, "%s Control"},
+	{ USB_ID(0x0582, 0x0044), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x0044), 1, "%s Control" },
 	/* Edirol UR-80 */
-	{0x0582, 0x0048, 0, "%s MIDI"},
-	{0x0582, 0x0048, 1, "%s 1"},
-	{0x0582, 0x0048, 2, "%s 2"},
+	{ USB_ID(0x0582, 0x0048), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x0048), 1, "%s 1" },
+	{ USB_ID(0x0582, 0x0048), 2, "%s 2" },
 	/* Edirol PCR-A */
-	{0x0582, 0x004d, 0, "%s MIDI"},
-	{0x0582, 0x004d, 1, "%s 1"},
-	{0x0582, 0x004d, 2, "%s 2"},
+	{ USB_ID(0x0582, 0x004d), 0, "%s MIDI" },
+	{ USB_ID(0x0582, 0x004d), 1, "%s 1" },
+	{ USB_ID(0x0582, 0x004d), 2, "%s 2" },
 	/* M-Audio MidiSport 8x8 */
-	{0x0763, 0x1031, 8, "%s Control"},
-	{0x0763, 0x1033, 8, "%s Control"},
+	{ USB_ID(0x0763, 0x1031), 8, "%s Control" },
+	{ USB_ID(0x0763, 0x1033), 8, "%s Control" },
 	/* MOTU Fastlane */
-	{0x07fd, 0x0001, 0, "%s MIDI A"},
-	{0x07fd, 0x0001, 1, "%s MIDI B"},
+	{ USB_ID(0x07fd, 0x0001), 0, "%s MIDI A" },
+	{ USB_ID(0x07fd, 0x0001), 1, "%s MIDI B" },
 	/* Emagic Unitor8/AMT8/MT4 */
-	{0x086a, 0x0001, 8, "%s Broadcast"},
-	{0x086a, 0x0002, 8, "%s Broadcast"},
-	{0x086a, 0x0003, 4, "%s Broadcast"},
+	{ USB_ID(0x086a, 0x0001), 8, "%s Broadcast" },
+	{ USB_ID(0x086a, 0x0002), 8, "%s Broadcast" },
+	{ USB_ID(0x086a, 0x0003), 4, "%s Broadcast" },
 };
 
 static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi,
@@ -1044,7 +1043,6 @@
 				       snd_rawmidi_substream_t** rsubstream)
 {
 	int i;
-	__u16 vendor, product;
 	const char *name_format;
 
 	snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number);
@@ -1055,11 +1053,8 @@
 
 	/* TODO: read port name from jack descriptor */
 	name_format = "%s MIDI %d";
-	vendor = le16_to_cpu(umidi->chip->dev->descriptor.idVendor);
-	product = le16_to_cpu(umidi->chip->dev->descriptor.idProduct);
 	for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) {
-		if (snd_usbmidi_port_names[i].vendor == vendor &&
-		    snd_usbmidi_port_names[i].product == product &&
+		if (snd_usbmidi_port_names[i].id == umidi->chip->usb_id &&
 		    snd_usbmidi_port_names[i].port == number) {
 			name_format = snd_usbmidi_port_names[i].name_format;
 			break;
@@ -1226,9 +1221,12 @@
 	struct usb_endpoint_descriptor* epd;
 	int i, out_eps = 0, in_eps = 0;
 
-	if (le16_to_cpu(umidi->chip->dev->descriptor.idVendor) == 0x0582)
+	if (USB_ID_VENDOR(umidi->chip->usb_id) == 0x0582)
 		snd_usbmidi_switch_roland_altsetting(umidi);
 
+	if (endpoint[0].out_ep || endpoint[0].in_ep)
+		return 0;	
+
 	intf = umidi->iface;
 	if (!intf || intf->num_altsetting < 1)
 		return -ENOENT;
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 5f19069..83ba665 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -35,10 +35,11 @@
 #include <linux/usb.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
 
 #include "usbaudio.h"
 
-
 /*
  */
 
@@ -50,6 +51,31 @@
 typedef struct usb_mixer_elem_info usb_mixer_elem_info_t;
 
 
+struct usb_mixer_interface {
+	snd_usb_audio_t *chip;
+	unsigned int ctrlif;
+	struct list_head list;
+	unsigned int ignore_ctl_error;
+	struct urb *urb;
+	usb_mixer_elem_info_t **id_elems; /* array[256], indexed by unit id */
+
+	/* Sound Blaster remote control stuff */
+	enum {
+		RC_NONE,
+		RC_EXTIGY,
+		RC_AUDIGY2NX,
+	} rc_type;
+	unsigned long rc_hwdep_open;
+	u32 rc_code;
+	wait_queue_head_t rc_waitq;
+	struct urb *rc_urb;
+	struct usb_ctrlrequest *rc_setup_packet;
+	u8 rc_buffer[6];
+
+	u8 audigy2nx_leds[3];
+};
+
+
 struct usb_audio_term {
 	int id;
 	int type;
@@ -62,26 +88,26 @@
 
 struct usb_mixer_build {
 	snd_usb_audio_t *chip;
+	struct usb_mixer_interface *mixer;
 	unsigned char *buffer;
 	unsigned int buflen;
-	unsigned int ctrlif;
-	unsigned short vendor;
-	unsigned short product;
-	DECLARE_BITMAP(unitbitmap, 32*32);
+	DECLARE_BITMAP(unitbitmap, 256);
 	usb_audio_term_t oterm;
 	const struct usbmix_name_map *map;
+	const struct usbmix_selector_map *selector_map;
 };
 
 struct usb_mixer_elem_info {
-	snd_usb_audio_t *chip;
-	unsigned int ctrlif;
+	struct usb_mixer_interface *mixer;
+	usb_mixer_elem_info_t *next_id_elem; /* list of controls with same id */
+	snd_ctl_elem_id_t *elem_id;
 	unsigned int id;
 	unsigned int control;	/* CS or ICN (high byte) */
 	unsigned int cmask; /* channel mask bitmap: 0 = master */
 	int channels;
 	int val_type;
 	int min, max, res;
-	unsigned int initialized: 1;
+	u8 initialized;
 };
 
 
@@ -187,6 +213,21 @@
 	return 0;
 }
 
+/* get the mapped selector source name */
+static int check_mapped_selector_name(mixer_build_t *state, int unitid,
+				      int index, char *buf, int buflen)
+{
+	const struct usbmix_selector_map *p;
+
+	if (! state->selector_map)
+		return 0;
+	for (p = state->selector_map; p->id; p++) {
+		if (p->id == unitid && index < p->count)
+			return strlcpy(buf, p->names[index], buflen);
+	}
+	return 0;
+}
+
 /*
  * find an audio control unit with the given unit id
  */
@@ -301,16 +342,18 @@
 	int timeout = 10;
 
 	while (timeout-- > 0) {
-		if (snd_usb_ctl_msg(cval->chip->dev, usb_rcvctrlpipe(cval->chip->dev, 0),
+		if (snd_usb_ctl_msg(cval->mixer->chip->dev,
+				    usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
 				    request,
 				    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
-				    validx, cval->ctrlif | (cval->id << 8),
+				    validx, cval->mixer->ctrlif | (cval->id << 8),
 				    buf, val_len, 100) >= 0) {
 			*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
 			return 0;
 		}
 	}
-	snd_printdd(KERN_ERR "cannot get ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type);
+	snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+		    request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
 	return -EINVAL;
 }
 
@@ -339,13 +382,15 @@
 	buf[0] = value_set & 0xff;
 	buf[1] = (value_set >> 8) & 0xff;
 	while (timeout -- > 0)
-		if (snd_usb_ctl_msg(cval->chip->dev, usb_sndctrlpipe(cval->chip->dev, 0),
+		if (snd_usb_ctl_msg(cval->mixer->chip->dev,
+				    usb_sndctrlpipe(cval->mixer->chip->dev, 0),
 				    request,
 				    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
-				    validx, cval->ctrlif | (cval->id << 8),
+				    validx, cval->mixer->ctrlif | (cval->id << 8),
 				    buf, val_len, 100) >= 0)
 			return 0;
-	snd_printdd(KERN_ERR "cannot set ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d, data = 0x%x/0x%x\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
+	snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
+		    request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
 	return -EINVAL;
 }
 
@@ -385,16 +430,22 @@
  * if failed, give up and free the control instance.
  */
 
-static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl)
+static int add_control_to_empty(mixer_build_t *state, snd_kcontrol_t *kctl)
 {
+	usb_mixer_elem_info_t *cval = kctl->private_data;
 	int err;
-	while (snd_ctl_find_id(card, &kctl->id))
+
+	while (snd_ctl_find_id(state->chip->card, &kctl->id))
 		kctl->id.index++;
-	if ((err = snd_ctl_add(card, kctl)) < 0) {
+	if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) {
 		snd_printd(KERN_ERR "cannot add control (err = %d)\n", err);
 		snd_ctl_free_one(kctl);
+		return err;
 	}
-	return err;
+	cval->elem_id = &kctl->id;
+	cval->next_id_elem = state->mixer->id_elems[cval->id];
+	state->mixer->id_elems[cval->id] = cval;
+	return 0;
 }
 
 
@@ -608,7 +659,8 @@
 		}
 		if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
 		    get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
-			snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id);
+			snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n",
+				   cval->id, cval->mixer->ctrlif, cval->control, cval->id);
 			return -EINVAL;
 		}
 		if (get_ctl_value(cval, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
@@ -668,7 +720,7 @@
 			if (cval->cmask & (1 << c)) {
 				err = get_cur_mix_value(cval, c + 1, &val);
 				if (err < 0) {
-					if (cval->chip->ignore_ctl_error) {
+					if (cval->mixer->ignore_ctl_error) {
 						ucontrol->value.integer.value[0] = cval->min;
 						return 0;
 					}
@@ -684,7 +736,7 @@
 		/* master channel */
 		err = get_cur_mix_value(cval, 0, &val);
 		if (err < 0) {
-			if (cval->chip->ignore_ctl_error) {
+			if (cval->mixer->ignore_ctl_error) {
 				ucontrol->value.integer.value[0] = cval->min;
 				return 0;
 			}
@@ -710,7 +762,7 @@
 			if (cval->cmask & (1 << c)) {
 				err = get_cur_mix_value(cval, c + 1, &oval);
 				if (err < 0) {
-					if (cval->chip->ignore_ctl_error)
+					if (cval->mixer->ignore_ctl_error)
 						return 0;
 					return err;
 				}
@@ -727,7 +779,7 @@
 	} else {
 		/* master channel */
 		err = get_cur_mix_value(cval, 0, &oval);
-		if (err < 0 && cval->chip->ignore_ctl_error)
+		if (err < 0 && cval->mixer->ignore_ctl_error)
 			return 0;
 		if (err < 0)
 			return err;
@@ -779,8 +831,7 @@
 		snd_printk(KERN_ERR "cannot malloc kcontrol\n");
 		return;
 	}
-	cval->chip = state->chip;
-	cval->ctrlif = state->ctrlif;
+	cval->mixer = state->mixer;
 	cval->id = unitid;
 	cval->control = control;
 	cval->cmask = ctl_mask;
@@ -855,16 +906,21 @@
 	/* note that detection between firmware 2.1.1.7 (N101) and later 2.1.1.21 */
 	/* is not very clear from datasheets */
 	/* I hope that the min value is -15360 for newer firmware --jk */
-	if (((state->vendor == 0x471 && (state->product == 0x104 || state->product == 0x105 || state->product == 0x101)) ||
-	     (state->vendor == 0x672 && state->product == 0x1041)) && !strcmp(kctl->id.name, "PCM Playback Volume") &&
-	     cval->min == -15616) {
-		snd_printk("USB Audio: using volume control quirk for the UDA1321/N101 chip\n");
-		cval->max = -256;
+	switch (state->chip->usb_id) {
+	case USB_ID(0x0471, 0x0101):
+	case USB_ID(0x0471, 0x0104):
+	case USB_ID(0x0471, 0x0105):
+	case USB_ID(0x0672, 0x1041):
+		if (!strcmp(kctl->id.name, "PCM Playback Volume") &&
+		    cval->min == -15616) {
+			snd_printk("using volume control quirk for the UDA1321/N101 chip\n");
+			cval->max = -256;
+		}
 	}
 
 	snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
 		    cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res);
-	add_control_to_empty(state->chip->card, kctl);
+	add_control_to_empty(state, kctl);
 }
 
 
@@ -947,8 +1003,7 @@
 	if (! cval)
 		return;
 
-	cval->chip = state->chip;
-	cval->ctrlif = state->ctrlif;
+	cval->mixer = state->mixer;
 	cval->id = unitid;
 	cval->control = in_ch + 1; /* based on 1 */
 	cval->val_type = USB_MIXER_S16;
@@ -979,7 +1034,7 @@
 
 	snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n",
 		    cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
-	add_control_to_empty(state->chip->card, kctl);
+	add_control_to_empty(state, kctl);
 }
 
 
@@ -1042,7 +1097,7 @@
 	int err, val;
 
 	err = get_cur_ctl_value(cval, cval->control << 8, &val);
-	if (err < 0 && cval->chip->ignore_ctl_error) {
+	if (err < 0 && cval->mixer->ignore_ctl_error) {
 		ucontrol->value.integer.value[0] = cval->min;
 		return 0;
 	}
@@ -1061,7 +1116,7 @@
 
 	err = get_cur_ctl_value(cval, cval->control << 8, &oval);
 	if (err < 0) {
-		if (cval->chip->ignore_ctl_error)
+		if (cval->mixer->ignore_ctl_error)
 			return 0;
 		return err;
 	}
@@ -1179,9 +1234,6 @@
 	}
 
 	type = combine_word(&dsc[4]);
-	if (! type)
-		return 0; /* undefined? */
-
 	for (info = list; info && info->type; info++)
 		if (info->type == type)
 			break;
@@ -1199,8 +1251,7 @@
 			snd_printk(KERN_ERR "cannot malloc kcontrol\n");
 			return -ENOMEM;
 		}
-		cval->chip = state->chip;
-		cval->ctrlif = state->ctrlif;
+		cval->mixer = state->mixer;
 		cval->id = unitid;
 		cval->control = valinfo->control;
 		cval->val_type = valinfo->val_type;
@@ -1241,7 +1292,7 @@
 
 		snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
 			    cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
-		if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
+		if ((err = add_control_to_empty(state, kctl)) < 0)
 			return err;
 	}
 	return 0;
@@ -1289,7 +1340,7 @@
 
 	err = get_cur_ctl_value(cval, 0, &val);
 	if (err < 0) {
-		if (cval->chip->ignore_ctl_error) {
+		if (cval->mixer->ignore_ctl_error) {
 			ucontrol->value.enumerated.item[0] = 0;
 			return 0;
 		}
@@ -1308,7 +1359,7 @@
 
 	err = get_cur_ctl_value(cval, 0, &oval);
 	if (err < 0) {
-		if (cval->chip->ignore_ctl_error)
+		if (cval->mixer->ignore_ctl_error)
 			return 0;
 		return err;
 	}
@@ -1386,8 +1437,7 @@
 		snd_printk(KERN_ERR "cannot malloc kcontrol\n");
 		return -ENOMEM;
 	}
-	cval->chip = state->chip;
-	cval->ctrlif = state->ctrlif;
+	cval->mixer = state->mixer;
 	cval->id = unitid;
 	cval->val_type = USB_MIXER_U8;
 	cval->channels = 1;
@@ -1415,7 +1465,9 @@
 			kfree(cval);
 			return -ENOMEM;
 		}
-		if (check_input_term(state, desc[5 + i], &iterm) >= 0)
+		len = check_mapped_selector_name(state, unitid, i, namelist[i],
+						 MAX_ITEM_NAME_LEN);
+		if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0)
 			len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
 		if (! len)
 			sprintf(namelist[i], "Input %d", i);
@@ -1450,7 +1502,7 @@
 
 	snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
 		    cval->id, kctl->id.name, num_ins);
-	if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
+	if ((err = add_control_to_empty(state, kctl)) < 0)
 		return err;
 
 	return 0;
@@ -1493,41 +1545,55 @@
 	}
 }
 
+static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
+{
+	kfree(mixer->id_elems);
+	if (mixer->urb) {
+		kfree(mixer->urb->transfer_buffer);
+		usb_free_urb(mixer->urb);
+	}
+	if (mixer->rc_urb)
+		usb_free_urb(mixer->rc_urb);
+	kfree(mixer->rc_setup_packet);
+	kfree(mixer);
+}
+
+static int snd_usb_mixer_dev_free(snd_device_t *device)
+{
+	struct usb_mixer_interface *mixer = device->device_data;
+	snd_usb_mixer_free(mixer);
+	return 0;
+}
+
 /*
  * create mixer controls
  *
  * walk through all OUTPUT_TERMINAL descriptors to search for mixers
  */
-int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
+static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 {
 	unsigned char *desc;
 	mixer_build_t state;
 	int err;
 	const struct usbmix_ctl_map *map;
-	struct usb_device_descriptor *dev = &chip->dev->descriptor;
-	struct usb_host_interface *hostif = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
+	struct usb_host_interface *hostif;
 
-	strcpy(chip->card->mixername, "USB Mixer");
-
+	hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
 	memset(&state, 0, sizeof(state));
-	state.chip = chip;
+	state.chip = mixer->chip;
+	state.mixer = mixer;
 	state.buffer = hostif->extra;
 	state.buflen = hostif->extralen;
-	state.ctrlif = ctrlif;
-	state.vendor = le16_to_cpu(dev->idVendor);
-	state.product = le16_to_cpu(dev->idProduct);
 
 	/* check the mapping table */
-	for (map = usbmix_ctl_maps; map->vendor; map++) {
-		if (map->vendor == state.vendor && map->product == state.product) {
+	for (map = usbmix_ctl_maps; map->id; map++) {
+		if (map->id == state.chip->usb_id) {
 			state.map = map->map;
-			chip->ignore_ctl_error = map->ignore_ctl_error;
+			state.selector_map = map->selector_map;
+			mixer->ignore_ctl_error = map->ignore_ctl_error;
 			break;
 		}
 	}
-#ifdef IGNORE_CTL_ERROR
-	chip->ignore_ctl_error = 1;
-#endif
 
 	desc = NULL;
 	while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) {
@@ -1543,3 +1609,393 @@
 	}
 	return 0;
 }
+
+static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
+				    int unitid)
+{
+	usb_mixer_elem_info_t *info;
+
+	for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem)
+		snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       info->elem_id);
+}
+
+static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
+					int unitid)
+{
+	if (mixer->rc_type == RC_NONE)
+		return;
+	/* unit ids specific to Extigy/Audigy 2 NX: */
+	switch (unitid) {
+	case 0: /* remote control */
+		mixer->rc_urb->dev = mixer->chip->dev;
+		usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
+		break;
+	case 4: /* digital in jack */
+	case 7: /* line in jacks */
+	case 19: /* speaker out jacks */
+	case 20: /* headphones out jack */
+		break;
+	default:
+		snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+		break;
+	}
+}
+
+static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_mixer_interface *mixer = urb->context;
+
+	if (urb->status == 0) {
+		u8 *buf = urb->transfer_buffer;
+		int i;
+
+		for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) {
+			snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n",
+				   buf[0], buf[1]);
+			/* ignore any notifications not from the control interface */
+			if ((buf[0] & 0x0f) != 0)
+				continue;
+			if (!(buf[0] & 0x40))
+				snd_usb_mixer_notify_id(mixer, buf[1]);
+			else
+				snd_usb_mixer_memory_change(mixer, buf[1]);
+		}
+	}
+	if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
+		urb->dev = mixer->chip->dev;
+		usb_submit_urb(urb, GFP_ATOMIC);
+	}
+}
+
+/* create the handler for the optional status interrupt endpoint */
+static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
+{
+	struct usb_host_interface *hostif;
+	struct usb_endpoint_descriptor *ep;
+	void *transfer_buffer;
+	int buffer_length;
+	unsigned int epnum;
+
+	hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
+	/* we need one interrupt input endpoint */
+	if (get_iface_desc(hostif)->bNumEndpoints < 1)
+		return 0;
+	ep = get_endpoint(hostif, 0);
+	if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN ||
+	    (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+		return 0;
+
+	epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	buffer_length = le16_to_cpu(ep->wMaxPacketSize);
+	transfer_buffer = kmalloc(buffer_length, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+	mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!mixer->urb) {
+		kfree(transfer_buffer);
+		return -ENOMEM;
+	}
+	usb_fill_int_urb(mixer->urb, mixer->chip->dev,
+			 usb_rcvintpipe(mixer->chip->dev, epnum),
+			 transfer_buffer, buffer_length,
+			 snd_usb_mixer_status_complete, mixer, ep->bInterval);
+	usb_submit_urb(mixer->urb, GFP_KERNEL);
+	return 0;
+}
+
+static void snd_usb_soundblaster_remote_complete(struct urb *urb,
+						 struct pt_regs *regs)
+{
+	struct usb_mixer_interface *mixer = urb->context;
+	/*
+	 * format of remote control data:
+	 * Extigy:	xx 00
+	 * Audigy 2 NX:	06 80 xx 00 00 00
+	 */
+	int offset = mixer->rc_type == RC_EXTIGY ? 0 : 2;
+	u32 code;
+
+	if (urb->status < 0 || urb->actual_length <= offset)
+		return;
+	code = mixer->rc_buffer[offset];
+	/* the Mute button actually changes the mixer control */
+	if (code == 13)
+		snd_usb_mixer_notify_id(mixer, 18);
+	mixer->rc_code = code;
+	wmb();
+	wake_up(&mixer->rc_waitq);
+}
+
+static int snd_usb_sbrc_hwdep_open(snd_hwdep_t *hw, struct file *file)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+
+	if (test_and_set_bit(0, &mixer->rc_hwdep_open))
+		return -EBUSY;
+	return 0;
+}
+
+static int snd_usb_sbrc_hwdep_release(snd_hwdep_t *hw, struct file *file)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+
+	clear_bit(0, &mixer->rc_hwdep_open);
+	smp_mb__after_clear_bit();
+	return 0;
+}
+
+static long snd_usb_sbrc_hwdep_read(snd_hwdep_t *hw, char __user *buf,
+				     long count, loff_t *offset)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+	int err;
+	u32 rc_code;
+
+	if (count != 1 && count != 4)
+		return -EINVAL;
+	err = wait_event_interruptible(mixer->rc_waitq,
+				       (rc_code = xchg(&mixer->rc_code, 0)) != 0);
+	if (err == 0) {
+		if (count == 1)
+			err = put_user(rc_code, buf);
+		else
+			err = put_user(rc_code, (u32 __user *)buf);
+	}
+	return err < 0 ? err : count;
+}
+
+static unsigned int snd_usb_sbrc_hwdep_poll(snd_hwdep_t *hw, struct file *file,
+					    poll_table *wait)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+
+	poll_wait(file, &mixer->rc_waitq, wait);
+	return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
+}
+
+static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
+{
+	snd_hwdep_t *hwdep;
+	int err, len;
+
+	switch (mixer->chip->usb_id) {
+	case USB_ID(0x041e, 0x3000):
+		mixer->rc_type = RC_EXTIGY;
+		len = 2;
+		break;
+	case USB_ID(0x041e, 0x3020):
+		mixer->rc_type = RC_AUDIGY2NX;
+		len = 6;
+		break;
+	default:
+		return 0;
+	}
+
+	init_waitqueue_head(&mixer->rc_waitq);
+	err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
+	if (err < 0)
+		return err;
+	snprintf(hwdep->name, sizeof(hwdep->name),
+		 "%s remote control", mixer->chip->card->shortname);
+	hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
+	hwdep->private_data = mixer;
+	hwdep->ops.read = snd_usb_sbrc_hwdep_read;
+	hwdep->ops.open = snd_usb_sbrc_hwdep_open;
+	hwdep->ops.release = snd_usb_sbrc_hwdep_release;
+	hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+
+	mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!mixer->rc_urb)
+		return -ENOMEM;
+	mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
+	if (!mixer->rc_setup_packet) {
+		usb_free_urb(mixer->rc_urb);
+		mixer->rc_urb = NULL;
+		return -ENOMEM;
+	}
+	mixer->rc_setup_packet->bRequestType =
+		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+	mixer->rc_setup_packet->bRequest = GET_MEM;
+	mixer->rc_setup_packet->wValue = cpu_to_le16(0);
+	mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
+	mixer->rc_setup_packet->wLength = cpu_to_le16(len);
+	usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
+			     usb_rcvctrlpipe(mixer->chip->dev, 0),
+			     (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
+			     snd_usb_soundblaster_remote_complete, mixer);
+	return 0;
+}
+
+static int snd_audigy2nx_led_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_audigy2nx_led_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
+	return 0;
+}
+
+static int snd_audigy2nx_led_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	int value = ucontrol->value.integer.value[0];
+	int err, changed;
+
+	if (value > 1)
+		return -EINVAL;
+	changed = value != mixer->audigy2nx_leds[index];
+	err = snd_usb_ctl_msg(mixer->chip->dev,
+			      usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+			      value, index + 2, NULL, 0, 100);
+	if (err < 0)
+		return err;
+	mixer->audigy2nx_leds[index] = value;
+	return changed;
+}
+
+static snd_kcontrol_new_t snd_audigy2nx_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "CMSS LED Switch",
+		.info = snd_audigy2nx_led_info,
+		.get = snd_audigy2nx_led_get,
+		.put = snd_audigy2nx_led_put,
+		.private_value = 0,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Power LED Switch",
+		.info = snd_audigy2nx_led_info,
+		.get = snd_audigy2nx_led_get,
+		.put = snd_audigy2nx_led_put,
+		.private_value = 1,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Dolby Digital LED Switch",
+		.info = snd_audigy2nx_led_info,
+		.get = snd_audigy2nx_led_get,
+		.put = snd_audigy2nx_led_put,
+		.private_value = 2,
+	},
+};
+
+static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+{
+	int i, err;
+
+	for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+		err = snd_ctl_add(mixer->chip->card,
+				  snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+		if (err < 0)
+			return err;
+	}
+	mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
+	return 0;
+}
+
+static void snd_audigy2nx_proc_read(snd_info_entry_t *entry,
+				    snd_info_buffer_t *buffer)
+{
+	static const struct {
+		int unitid;
+		const char *name;
+	} jacks[] = {
+		{4,  "dig in "},
+		{7,  "line in"},
+		{19, "spk out"},
+		{20, "hph out"},
+	};
+	struct usb_mixer_interface *mixer = entry->private_data;
+	int i, err;
+	u8 buf[3];
+
+	snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
+	for (i = 0; i < ARRAY_SIZE(jacks); ++i) {
+		snd_iprintf(buffer, "%s: ", jacks[i].name);
+		err = snd_usb_ctl_msg(mixer->chip->dev,
+				      usb_rcvctrlpipe(mixer->chip->dev, 0),
+				      GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
+				      USB_RECIP_INTERFACE, 0,
+				      jacks[i].unitid << 8, buf, 3, 100);
+		if (err == 3 && buf[0] == 3)
+			snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
+		else
+			snd_iprintf(buffer, "?\n");
+	}
+}
+
+int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
+{
+	static snd_device_ops_t dev_ops = {
+		.dev_free = snd_usb_mixer_dev_free
+	};
+	struct usb_mixer_interface *mixer;
+	int err;
+
+	strcpy(chip->card->mixername, "USB Mixer");
+
+	mixer = kcalloc(1, sizeof(*mixer), GFP_KERNEL);
+	if (!mixer)
+		return -ENOMEM;
+	mixer->chip = chip;
+	mixer->ctrlif = ctrlif;
+#ifdef IGNORE_CTL_ERROR
+	mixer->ignore_ctl_error = 1;
+#endif
+	mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL);
+	if (!mixer->id_elems) {
+		kfree(mixer);
+		return -ENOMEM;
+	}
+
+	if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
+	    (err = snd_usb_mixer_status_create(mixer)) < 0)
+		goto _error;
+
+	if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
+		goto _error;
+
+	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) {
+		snd_info_entry_t *entry;
+
+		if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
+			goto _error;
+		if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
+			snd_info_set_text_ops(entry, mixer, 1024,
+					      snd_audigy2nx_proc_read);
+	}
+
+	err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
+	if (err < 0)
+		goto _error;
+	list_add(&mixer->list, &chip->mixer_list);
+	return 0;
+
+_error:
+	snd_usb_mixer_free(mixer);
+	return err;
+}
+
+void snd_usb_mixer_disconnect(struct list_head *p)
+{
+	struct usb_mixer_interface *mixer;
+	
+	mixer = list_entry(p, struct usb_mixer_interface, list);
+	if (mixer->urb)
+		usb_kill_urb(mixer->urb);
+	if (mixer->rc_urb)
+		usb_kill_urb(mixer->rc_urb);
+}
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index c69b4b0..f05500b 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -26,10 +26,16 @@
 	int control;
 };
 
+struct usbmix_selector_map {
+	int id;
+	int count;
+	const char **names;
+};
+
 struct usbmix_ctl_map {
-	int vendor;
-	int product;
+	u32 id;
 	const struct usbmix_name_map *map;
+	const struct usbmix_selector_map *selector_map;
 	int ignore_ctl_error;
 };
 
@@ -91,6 +97,96 @@
 	{ 0 } /* terminator */
 };
 
+/* Sound Blaster MP3+ controls mapping
+ * The default mixer channels have totally misleading names,
+ * e.g. no Master and fake PCM volume
+ *			Pavel Mihaylov <bin@bash.info>
+ */
+static struct usbmix_name_map mp3plus_map[] = {
+	/* 1: IT pcm */
+	/* 2: IT mic */
+	/* 3: IT line */
+	/* 4: IT digital in */
+	/* 5: OT digital out */
+	/* 6: OT speaker */
+	/* 7: OT pcm capture */
+	{ 8, "Capture Input Source" }, /* FU, default PCM Capture Source */
+		/* (Mic, Input 1 = Line input, Input 2 = Optical input) */
+	{ 9, "Master Playback" }, /* FU, default Speaker 1 */
+	/* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */
+	/* { 10, "Mic Capture", 2 }, */ /* FU, Mic Capture */
+	{ 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */
+	{ 11, "Line Capture" }, /* FU, default PCM Capture */
+	{ 12, "Digital In Playback" }, /* FU, default PCM 1 */
+	/* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */
+	{ 14, "Line Playback" }, /* FU, default Speaker */
+	/* 15: MU */
+	{ 0 } /* terminator */
+};
+
+/* Topology of SB Audigy 2 NX
+
+          +----------------------------->EU[27]--+
+          |                                      v
+          | +----------------------------------->SU[29]---->FU[22]-->Dig_OUT[24]
+          | |                                    ^
+USB_IN[1]-+------------+              +->EU[17]->+->FU[11]-+
+            |          v              |          v         |
+Dig_IN[4]---+->FU[6]-->MU[16]->FU[18]-+->EU[21]->SU[31]----->FU[30]->Hph_OUT[20]
+            |          ^              |                    |
+Lin_IN[7]-+--->FU[8]---+              +->EU[23]->FU[28]------------->Spk_OUT[19]
+          | |                                              v
+          +--->FU[12]------------------------------------->SU[14]--->USB_OUT[15]
+            |                                              ^
+            +->FU[13]--------------------------------------+
+*/
+static struct usbmix_name_map audigy2nx_map[] = {
+	/* 1: IT pcm playback */
+	/* 4: IT digital in */
+	{ 6, "Digital In Playback" }, /* FU */
+	/* 7: IT line in */
+	{ 8, "Line Playback" }, /* FU */
+	{ 11, "What-U-Hear Capture" }, /* FU */
+	{ 12, "Line Capture" }, /* FU */
+	{ 13, "Digital In Capture" }, /* FU */
+	{ 14, "Capture Source" }, /* SU */
+	/* 15: OT pcm capture */
+	/* 16: MU w/o controls */
+	{ 17, NULL }, /* DISABLED: EU (for what?) */
+	{ 18, "Master Playback" }, /* FU */
+	/* 19: OT speaker */
+	/* 20: OT headphone */
+	{ 21, NULL }, /* DISABLED: EU (for what?) */
+	{ 22, "Digital Out Playback" }, /* FU */
+	{ 23, NULL }, /* DISABLED: EU (for what?) */
+	/* 24: OT digital out */
+	{ 27, NULL }, /* DISABLED: EU (for what?) */
+	{ 28, "Speaker Playback" }, /* FU */
+	{ 29, "Digital Out Source" }, /* SU */
+	{ 30, "Headphone Playback" }, /* FU */
+	{ 31, "Headphone Source" }, /* SU */
+	{ 0 } /* terminator */
+};
+
+static struct usbmix_selector_map audigy2nx_selectors[] = {
+	{
+		.id = 14, /* Capture Source */
+		.count = 3,
+		.names = (const char*[]) {"Line", "Digital In", "What-U-Hear"}
+	},
+	{
+		.id = 29, /* Digital Out Source */
+		.count = 3,
+		.names = (const char*[]) {"Front", "PCM", "Digital In"}
+	},
+	{
+		.id = 31, /* Headphone Source */
+		.count = 2,
+		.names = (const char*[]) {"Front", "Side"}
+	},
+	{ 0 } /* terminator */
+};
+
 /* LineX FM Transmitter entry - needed to bypass controls bug */
 static struct usbmix_name_map linex_map[] = {
 	/* 1: IT pcm */
@@ -127,9 +223,29 @@
  */
 
 static struct usbmix_ctl_map usbmix_ctl_maps[] = {
-	{ 0x41e, 0x3000, extigy_map, 1 },
-	{ 0x8bb, 0x2702, linex_map, 1 },
-	{ 0xc45, 0x1158, justlink_map, 0 },
+	{
+		.id = USB_ID(0x041e, 0x3000),
+		.map = extigy_map,
+		.ignore_ctl_error = 1,
+	},
+	{
+		.id = USB_ID(0x041e, 0x3010),
+		.map = mp3plus_map,
+	},
+	{
+		.id = USB_ID(0x041e, 0x3020),
+		.map = audigy2nx_map,
+		.selector_map = audigy2nx_selectors,
+	},
+	{
+		.id = USB_ID(0x08bb, 0x2702),
+		.map = linex_map,
+		.ignore_ctl_error = 1,
+	},
+	{
+		.id = USB_ID(0x0c45, 0x1158),
+		.map = justlink_map,
+	},
 	{ 0 } /* terminator */
 };
 
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 88bbd94..f513564 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -203,11 +203,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "EDIROL",
 		.product_name = "UM-4",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x000f,
-			.in_cables  = 0x000f
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x000f,
+					.in_cables  = 0x000f
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -216,11 +233,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "Roland",
 		.product_name = "SC-8850",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x003f,
-			.in_cables  = 0x003f
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x003f,
+					.in_cables  = 0x003f
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -229,11 +263,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "Roland",
 		.product_name = "U-8",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0005,
-			.in_cables  = 0x0005
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0005,
+					.in_cables  = 0x0005
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -242,11 +293,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "EDIROL",
 		.product_name = "UM-2",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0003,
-			.in_cables  = 0x0003
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0003,
+					.in_cables  = 0x0003
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -255,11 +323,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "Roland",
 		.product_name = "SC-8820",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0013,
-			.in_cables  = 0x0013
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0013,
+					.in_cables  = 0x0013
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -268,11 +353,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "Roland",
 		.product_name = "PC-300",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0001,
-			.in_cables  = 0x0001
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -281,11 +383,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "EDIROL",
 		.product_name = "UM-1",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0001,
-			.in_cables  = 0x0001
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -294,11 +413,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "Roland",
 		.product_name = "SK-500",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0013,
-			.in_cables  = 0x0013
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0013,
+					.in_cables  = 0x0013
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -421,11 +557,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "EDIROL",
 		.product_name = "SD-90",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x000f,
-			.in_cables  = 0x000f
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x000f,
+					.in_cables  = 0x000f
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -434,11 +587,28 @@
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "Roland",
 		.product_name = "MMP-2",
-		.ifnum = 2,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0001,
-			.in_cables  = 0x0001
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_IGNORE_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const snd_usb_midi_endpoint_info_t) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
@@ -609,15 +779,33 @@
 	}
 },
 {
+	/*
+	 * This quirk is for the "Advanced Driver" mode.  If off, the GS-10
+	 * has ID 0x003c and is standard compliant, but has only 16-bit PCM
+	 * and no MIDI.
+	 */
 	USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b),
 	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
 		.vendor_name = "BOSS",
 		.product_name = "GS-10",
-		.ifnum = 3,
-		.type = QUIRK_MIDI_FIXED_ENDPOINT,
-		.data = & (const snd_usb_midi_endpoint_info_t) {
-			.out_cables = 0x0003,
-			.in_cables  = 0x0003
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = & (const snd_usb_audio_quirk_t[]) {
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 3,
+				.type = QUIRK_MIDI_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = -1
+			}
 		}
 	}
 },
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 89ee8b7..e6e6da1 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -442,7 +442,7 @@
 		snd_card_disconnect((snd_card_t*)ptr);
 		/* release the midi resources */
 		list_for_each(p, &usX2Y->chip.midi_list) {
-			snd_usbmidi_disconnect(p, &snd_usX2Y_usb_driver);
+			snd_usbmidi_disconnect(p);
 		}
 		if (usX2Y->us428ctls_sharedmem) 
 			wake_up(&usX2Y->us428ctls_wait_queue_head);