[kernel] r10357 - in dists/trunk/linux-2.6/debian/patches: bugfix/all series

Maximilian Attems maks at alioth.debian.org
Fri Feb 1 14:00:13 UTC 2008


Author: maks
Date: Fri Feb  1 14:00:08 2008
New Revision: 10357

Log:
update to patch-2.6.24-git10

block, net, ide fixes
alsa merge


Added:
   dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git10
      - copied, changed from r10356, /dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git9
Removed:
   dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git9
Modified:
   dists/trunk/linux-2.6/debian/patches/series/1~experimental.1

Copied: dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git10 (from r10356, /dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git9)
==============================================================================
--- /dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git9	(original)
+++ dists/trunk/linux-2.6/debian/patches/bugfix/all/patch-2.6.24-git10	Fri Feb  1 14:00:08 2008
@@ -5425,6 +5425,3692 @@
 -Please try these options and post any problems/successes to me.
 -
 -Alan Hourihane <alanh at fairlite.demon.co.uk>
+diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
+index 4b48c2e..e985cf5 100644
+--- a/Documentation/sound/alsa/ALSA-Configuration.txt
++++ b/Documentation/sound/alsa/ALSA-Configuration.txt
+@@ -57,7 +57,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 		- Default: 1
+ 		- For auto-loading more than one card, specify this
+ 		  option together with snd-card-X aliases.
+-
++    slots	- Reserve the slot index for the given driver.
++		  This option takes multiple strings.		
++		  See "Module Autoloading Support" section for details.
+   
+   Module snd-pcm-oss
+   ------------------
+@@ -148,13 +150,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on Analog Devices AD1816A/AD1815 ISA chips.
+ 
+-    port	- port # for AD1816A chip (PnP setup)
+-    mpu_port	- port # for MPU-401 UART (PnP setup)
+-    fm_port	- port # for OPL3 (PnP setup)
+-    irq		- IRQ # for AD1816A chip (PnP setup)
+-    mpu_irq	- IRQ # for MPU-401 UART (PnP setup)
+-    dma1	- first DMA # for AD1816A chip (PnP setup)
+-    dma2	- second DMA # for AD1816A chip (PnP setup)
+     clockfreq   - Clock frequency for AD1816A chip (default = 0, 33000Hz)
+     
+     This module supports multiple cards, autoprobe and PnP.
+@@ -201,14 +196,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on Avance Logic ALS100/ALS120 ISA chips.
+ 
+-    port	- port # for ALS100 (SB16) chip (PnP setup)
+-    irq		- IRQ # for ALS100 (SB16) chip (PnP setup)
+-    dma8	- 8-bit DMA # for ALS100 (SB16) chip (PnP setup)
+-    dma16	- 16-bit DMA # for ALS100 (SB16) chip (PnP setup)
+-    mpu_port	- port # for MPU-401 UART (PnP setup)
+-    mpu_irq	- IRQ # for MPU-401 (PnP setup)
+-    fm_port	- port # for OPL3 FM (PnP setup)
+-    
+     This module supports multiple cards, autoprobe and PnP.
+ 
+     The power-management is supported.
+@@ -302,15 +289,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on Aztech System AZT2320 ISA chip (PnP only).
+ 
+-    port	- port # for AZT2320 chip (PnP setup)
+-    wss_port	- port # for WSS (PnP setup)
+-    mpu_port	- port # for MPU-401 UART (PnP setup)
+-    fm_port	- FM port # for AZT2320 chip (PnP setup)
+-    irq		- IRQ # for AZT2320 (WSS) chip (PnP setup)
+-    mpu_irq	- IRQ # for MPU-401 UART (PnP setup)
+-    dma1	- 1st DMA # for AZT2320 (WSS) chip (PnP setup)
+-    dma2	- 2nd DMA # for AZT2320 (WSS) chip (PnP setup)
+-    
+     This module supports multiple cards, PnP and autoprobe.
+     
+     The power-management is supported.
+@@ -350,6 +328,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on C-Media CMI8330 ISA chips.
+ 
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     wssport	- port # for CMI8330 chip (WSS)
+     wssirq	- IRQ # for CMI8330 chip (WSS)
+     wssdma	- first DMA # for CMI8330 chip (WSS)
+@@ -404,6 +386,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on CS4232/CS4232A ISA chips.
+ 
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port	- port # for CS4232 chip (PnP setup - 0x534)
+     cport	- control port # for CS4232 chip (PnP setup - 0x120,0x210,0xf00)
+     mpu_port	- port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
+@@ -412,10 +398,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     mpu_irq	- IRQ # for MPU-401 UART (9,11,12,15)
+     dma1	- first DMA # for CS4232 chip (0,1,3)
+     dma2	- second DMA # for Yamaha CS4232 chip (0,1,3), -1 = disable
+-    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
+     
+     This module supports multiple cards. This module does not support autoprobe
+-    thus main port must be specified!!! Other ports are optional.
++    (if ISA PnP is not used) thus main port must be specified!!! Other ports are
++    optional.
+ 
+     The power-management is supported.
+     
+@@ -425,6 +411,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     Module for sound cards based on CS4235/CS4236/CS4236B/CS4237B/
+                                    CS4238B/CS4239 ISA chips.
+ 
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port	- port # for CS4236 chip (PnP setup - 0x534)
+     cport	- control port # for CS4236 chip (PnP setup - 0x120,0x210,0xf00)
+     mpu_port	- port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
+@@ -433,7 +423,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     mpu_irq	- IRQ # for MPU-401 UART (9,11,12,15)
+     dma1	- first DMA # for CS4236 chip (0,1,3)
+     dma2	- second DMA # for CS4236 chip (0,1,3), -1 = disable
+-    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
+     
+     This module supports multiple cards. This module does not support autoprobe
+     (if ISA PnP is not used) thus main port and control port must be
+@@ -503,13 +492,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     Module for Diamond Technologies DT-019X / Avance Logic ALS-007 (PnP
+     only)
+ 
+-    port	- Port # (PnP setup)
+-    mpu_port	- Port # for MPU-401 (PnP setup)
+-    fm_port	- Port # for FM OPL-3 (PnP setup)
+-    irq		- IRQ # (PnP setup)
+-    mpu_irq	- IRQ # for MPU-401 (PnP setup)
+-    dma8	- DMA # (PnP setup)
+-
+     This module supports multiple cards.  This module is enabled only with
+     ISA PnP support.
+ 
+@@ -607,10 +589,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on ESS ES968 chip (PnP only).
+ 
+-    port	- port # for ES968 (SB8) chip (PnP setup)
+-    irq		- IRQ # for ES968 (SB8) chip (PnP setup)
+-    dma1	- DMA # for ES968 (SB8) chip (PnP setup)
+-    
+     This module supports multiple cards, PnP and autoprobe.
+     
+     The power-management is supported.
+@@ -633,13 +611,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for ESS AudioDrive ES-18xx sound cards.
+ 
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port	- port # for ES-18xx chip (0x220,0x240,0x260)
+     mpu_port	- port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
+     fm_port	- port # for FM (optional, not used)
+     irq		- IRQ # for ES-18xx chip (5,7,9,10)
+     dma1	- first DMA # for ES-18xx chip (0,1,3)
+     dma2	- first DMA # for ES-18xx chip (0,1,3)
+-    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
+ 
+     This module supports multiple cards, ISA PnP and autoprobe (without MPU-401
+     port if native ISA PnP routines are not used).
+@@ -763,9 +744,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 		VIA VT8251/VT8237A,
+ 		SIS966, ULI M5461
+ 
++    [Multiple options for each card instance]
+     model	- force the model name
+     position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
+     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
++    
++    [Single (global) options]
+     single_cmd  - Use single immediate commands to communicate with
+ 		codecs (for debugging only)
+     enable_msi	- Enable Message Signaled Interrupt (MSI) (default = off)
+@@ -774,8 +758,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     power_save_controller - Reset HD-audio controller in power-saving mode
+ 		(default = on)
+ 
+-    This module supports one card and autoprobe.
+-
++    This module supports multiple cards and autoprobe.
++    
+     Each codec may have a model table for different configurations.
+     If your machine isn't listed there, the default (usually minimal)
+     configuration is set up.  You can pass "model=<name>" option to
+@@ -817,17 +801,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 	  will		Will laptops (PB V7900)
+ 	  replacer	Replacer 672V
+ 	  basic		fixed pin assignment (old default model)
++	  test		for testing/debugging purpose, almost all controls can
++			adjusted.  Appearing only when compiled with
++			$CONFIG_SND_DEBUG=y
+ 	  auto		auto-config reading BIOS (default)
+ 
+ 	ALC262
+ 	  fujitsu	Fujitsu Laptop
+ 	  hp-bpc	HP xw4400/6400/8400/9400 laptops
+ 	  hp-bpc-d7000	HP BPC D7000
++	  hp-tc-t5735	HP Thin Client T5735
++	  hp-rp5700	HP RP5700
+ 	  benq		Benq ED8
+ 	  benq-t31	Benq T31
+ 	  hippo		Hippo (ATI) with jack detection, Sony UX-90s
+ 	  hippo_1	Hippo (Benq) with jack detection
+ 	  sony-assamd	Sony ASSAMD
++	  ultra		Samsung Q1 Ultra Vista model
+ 	  basic		fixed pin assignment w/o SPDIF
+ 	  auto		auto-config reading BIOS (default)
+ 
+@@ -835,6 +825,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 	  3stack	3-stack model
+ 	  toshiba	Toshiba A205
+ 	  acer		Acer laptops
++	  dell		Dell OEM laptops (Vostro 1200)
++	  test		for testing/debugging purpose, almost all controls can
++			adjusted.  Appearing only when compiled with
++			$CONFIG_SND_DEBUG=y
+ 	  auto		auto-config reading BIOS (default)
+ 
+ 	ALC662
+@@ -843,6 +837,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 	  3stack-6ch-dig 3-stack (6-channel) with SPDIF
+ 	  6stack-dig	 6-stack with SPDIF
+ 	  lenovo-101e	 Lenovo laptop
++	  eeepc-p701	ASUS Eeepc P701
++	  eeepc-ep20	ASUS Eeepc EP20
+ 	  auto		auto-config reading BIOS (default)
+ 
+ 	ALC882/885
+@@ -877,6 +873,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 	  haier-w66	Haier W66
+ 	  6stack-hp	HP machines with 6stack (Nettle boards)
+ 	  3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
++	  6stack-dell	Dell machines with 6stack (Inspiron 530)
++	  mitac		Mitac 8252D
+ 	  auto		auto-config reading BIOS (default)
+ 
+ 	ALC861/660
+@@ -928,6 +926,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 	AD1984
+ 	  basic		default configuration
+ 	  thinkpad	Lenovo Thinkpad T61/X61
++	  dell		Dell T3400
+ 
+ 	AD1986A
+ 	  6stack	6-jack, separate surrounds (default)
+@@ -947,7 +946,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 	  auto		auto-config reading BIOS (default)
+ 	
+ 	Conexant 5045
+-	  laptop	Laptop config 
++	  laptop-hpsense    Laptop with HP sense (old model laptop)
++	  laptop-micsense   Laptop with Mic sense (old model fujitsu)
++	  laptop-hpmicsense Laptop with HP and Mic senses
++	  benq		Benq R55E
+ 	  test		for testing/debugging purpose, almost all controls
+ 			can be adjusted.  Appearing only when compiled with
+ 			$CONFIG_SND_DEBUG=y
+@@ -960,6 +962,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 			can be adjusted.  Appearing only when compiled with
+ 			$CONFIG_SND_DEBUG=y
+ 
++	Conexant 5051
++	  laptop	Basic Laptop config (default)
++	  hp		HP Spartan laptop
++
+ 	STAC9200
+ 	  ref		Reference board
+ 	  dell-d21	Dell (unknown)
+@@ -1091,6 +1097,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     See hdspm.txt for details.
+ 
++  Module snd-hifier
++  -----------------
++
++    Module for the MediaTek/TempoTec HiFier Fantasia sound card.
++
++    This module supports autoprobe and multiple cards.
++
++    Power management is _not_ supported.
++
+   Module snd-ice1712
+   ------------------
+ 
+@@ -1156,11 +1171,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 			* Chaintech 9CJS
+ 			* Chaintech AV-710
+ 			* Shuttle SN25P
++			* Onkyo SE-90PCI
++			* Onkyo SE-200PCI
+ 
+     model       - Use the given board model, one of the following:
+ 		  revo51, revo71, amp2000, prodigy71, prodigy71lt,
+ 		  prodigy192, aureon51, aureon71, universe, ap192,
+-		  k8x800, phase22, phase28, ms300, av710
++		  k8x800, phase22, phase28, ms300, av710, se200pci,
++		  se90pci
+ 
+     This module supports multiple cards and autoprobe.
+ 
+@@ -1257,15 +1275,19 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     Module for Gravis UltraSound PnP, Dynasonic 3-D/Pro, STB Sound Rage 32
+     and other sound cards based on AMD InterWave (tm) chip.
+   
+-    port	- port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
+-    irq		- IRQ # for InterWave chip (3,5,9,11,12,15)
+-    dma1	- DMA # for InterWave chip (0,1,3,5,6,7)
+-    dma2	- DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
+     joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+     midi	- 1 = MIDI UART enable, 0 = MIDI UART disable (default)
+     pcm_voices	- reserved PCM voices for the synthesizer (default 2)
+     effect	- 1 = InterWave effects enable (default 0);
+                   requires 8 voices
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
++    port	- port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
++    irq		- IRQ # for InterWave chip (3,5,9,11,12,15)
++    dma1	- DMA # for InterWave chip (0,1,3,5,6,7)
++    dma2	- DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
+ 
+     This module supports multiple cards, autoprobe and ISA PnP.
+ 
+@@ -1276,16 +1298,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     and other sound cards based on AMD InterWave (tm) chip with TEA6330T
+     circuit for extended control of bass, treble and master volume.
+   
+-    port	- port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
+-    port_tc	- tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380)
+-    irq		- IRQ # for InterWave chip (3,5,9,11,12,15)
+-    dma1	- DMA # for InterWave chip (0,1,3,5,6,7)
+-    dma2	- DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
+     joystick_dac - 0 to 31, (0.59V-4.52V or 0.389V-2.98V)
+     midi	- 1 = MIDI UART enable, 0 = MIDI UART disable (default)
+     pcm_voices	- reserved PCM voices for the synthesizer (default 2)
+     effect	- 1 = InterWave effects enable (default 0);
+                   requires 8 voices
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
++    port	- port # for InterWave chip (0x210,0x220,0x230,0x240,0x250,0x260)
++    port_tc	- tone control (i2c bus) port # for TEA6330T chip (0x350,0x360,0x370,0x380)
++    irq		- IRQ # for InterWave chip (3,5,9,11,12,15)
++    dma1	- DMA # for InterWave chip (0,1,3,5,6,7)
++    dma2	- DMA # for InterWave chip (0,1,3,5,6,7,-1=disable)
+ 
+     This module supports multiple cards, autoprobe and ISA PnP.
+ 
+@@ -1473,6 +1499,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for Yamaha OPL3-SA2/SA3 sound cards.
+ 
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port	- control port # for OPL3-SA chip (0x370)
+     sb_port	- SB port # for OPL3-SA chip (0x220,0x240)
+     wss_port	- WSS port # for OPL3-SA chip (0x530,0xe80,0xf40,0x604)
+@@ -1481,7 +1511,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     irq		- IRQ # for OPL3-SA chip (5,7,9,10)
+     dma1	- first DMA # for Yamaha OPL3-SA chip (0,1,3)
+     dma2	- second DMA # for Yamaha OPL3-SA chip (0,1,3), -1 = disable
+-    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
+     
+     This module supports multiple cards and ISA PnP.  It does not support
+     autoprobe (if ISA PnP is not used) thus all ports must be specified!!!
+@@ -1494,6 +1523,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     Module for sound cards based on OPTi 82c92x and Analog Devices AD1848 chips.
+     Module works with OAK Mozart cards as well.
+     
++    isapnp    - ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port      - port # for WSS chip (0x530,0xe80,0xf40,0x604)
+     mpu_port  - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
+     fm_port   - port # for OPL3 device (0x388)
+@@ -1508,6 +1541,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on OPTi 82c92x and Crystal CS4231 chips.
+     
++    isapnp    - ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port      - port # for WSS chip (0x530,0xe80,0xf40,0x604)
+     mpu_port  - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
+     fm_port   - port # for OPL3 device (0x388)
+@@ -1523,6 +1560,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for sound cards based on OPTi 82c93x chips.
+     
++    isapnp    - ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port      - port # for WSS chip (0x530,0xe80,0xf40,0x604)
+     mpu_port  - port # for MPU-401 UART (0x300,0x310,0x320,0x330)
+     fm_port   - port # for OPL3 device (0x388)
+@@ -1533,6 +1574,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     This module supports only one card, autoprobe and PnP.
+ 
++  Module snd-oxygen
++  -----------------
++
++    Module for sound cards based on the C-Media CMI8788 chip:
++    * Asound A-8788
++    * AuzenTech X-Meridian
++    * Bgears b-Enspirer
++    * Club3D Theatron DTS
++    * HT-Omega Claro
++    * Razer Barracuda AC-1
++    * Sondigo Inferno
++
++    This module supports autoprobe and multiple cards.
++
++    Power management is _not_ supported.
++
+   Module snd-pcxhr
+   ----------------
+ 
+@@ -1647,6 +1704,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 					  SoundBlaster AWE 32 (PnP),
+ 					  SoundBlaster AWE 64 PnP
+ 
++    mic_agc	- Mic Auto-Gain-Control - 0 = disable, 1 = enable (default)
++    csp		- ASP/CSP chip support - 0 = disable (default), 1 = enable
++    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     port	- port # for SB DSP 4.x chip (0x220,0x240,0x260)
+     mpu_port	- port # for MPU-401 UART (0x300,0x330), -1 = disable
+     awe_port	- base port # for EMU8000 synthesizer (0x620,0x640,0x660)
+@@ -1654,9 +1717,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     irq		- IRQ # for SB DSP 4.x chip (5,7,9,10)
+     dma8	- 8-bit DMA # for SB DSP 4.x chip (0,1,3)
+     dma16	- 16-bit DMA # for SB DSP 4.x chip (5,6,7)
+-    mic_agc	- Mic Auto-Gain-Control - 0 = disable, 1 = enable (default)
+-    csp		- ASP/CSP chip support - 0 = disable (default), 1 = enable
+-    isapnp	- ISA PnP detection - 0 = disable, 1 = enable (default)
+     
+     This module supports multiple cards, autoprobe and ISA PnP.
+ 
+@@ -1739,18 +1799,21 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+ 
+     Module for Turtle Beach Maui, Tropez and Tropez+ sound cards.
+ 
++    use_cs4232_midi - Use CS4232 MPU-401 interface
++                      (inaccessibly located inside your computer)
++    isapnp          - ISA PnP detection - 0 = disable, 1 = enable (default)
++
++    with isapnp=0, the following options are available:
++
+     cs4232_pcm_port - Port # for CS4232 PCM interface.
+     cs4232_pcm_irq  - IRQ # for CS4232 PCM interface (5,7,9,11,12,15).
+     cs4232_mpu_port - Port # for CS4232 MPU-401 interface.
+     cs4232_mpu_irq  - IRQ # for CS4232 MPU-401 interface (9,11,12,15).
+-    use_cs4232_midi - Use CS4232 MPU-401 interface
+-                      (inaccessibly located inside your computer)
+     ics2115_port    - Port # for ICS2115
+     ics2115_irq     - IRQ # for ICS2115
+     fm_port         - FM OPL-3 Port #
+     dma1            - DMA1 # for CS4232 PCM interface.
+     dma2            - DMA2 # for CS4232 PCM interface.
+-    isapnp          - ISA PnP detection - 0 = disable, 1 = enable (default)
+ 
+     The below are options for wavefront_synth features:
+     wf_raw	    - Assume that we need to boot the OS (default:no)
+@@ -1965,6 +2028,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
+     
+     This module supports multiple cards.
+ 
++  Module snd-virtuoso
++  -------------------
++
++    Module for sound cards based on the Asus AV200 chip, i.e.,
++    Xonar D2 and Xonar D2X.
++
++    This module supports autoprobe and multiple cards.
++
++    Power management is _not_ supported.
++
+   Module snd-vx222
+   ----------------
+ 
+@@ -2135,6 +2208,23 @@ alias sound-slot-1 snd-ens1371
+ In this example, the interwave card is always loaded as the first card
+ (index 0) and ens1371 as the second (index 1).
+ 
++Alternative (and new) way to fixate the slot assignment is to use
++"slots" option of snd module.  In the case above, specify like the
++following: 
++
++options snd slots=snd-interwave,snd-ens1371
++
++Then, the first slot (#0) is reserved for snd-interwave driver, and
++the second (#1) for snd-ens1371.  You can omit index option in each
++driver if slots option is used (although you can still have them at
++the same time as long as they don't conflict).
++
++The slots option is especially useful for avoiding the possible
++hot-plugging and the resultant slot conflict.  For example, in the
++case above again, the first two slots are already reserved.  If any
++other driver (e.g. snd-usb-audio) is loaded before snd-interwave or
++snd-ens1371, it will be assigned to the third or later slot.
++
+ 
+ ALSA PCM devices to OSS devices mapping
+ =======================================
+diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+index 2c3fc3c..b03df4d 100644
+--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
++++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+@@ -18,7 +18,7 @@
+       </affiliation>
+      </author>
+ 
+-     <date>September 10, 2007</date>
++     <date>Oct 15, 2007</date>
+      <edition>0.3.7</edition>
+ 
+     <abstract>
+@@ -67,7 +67,7 @@
+       This document describes how to write an
+       <ulink url="http://www.alsa-project.org/"><citetitle>
+       ALSA (Advanced Linux Sound Architecture)</citetitle></ulink>
+-      driver. The document focuses mainly on the PCI soundcard.
++      driver. The document focuses mainly on PCI soundcards.
+       In the case of other device types, the API might
+       be different, too. However, at least the ALSA kernel API is
+       consistent, and therefore it would be still a bit help for
+@@ -75,23 +75,23 @@
+     </para>
+ 
+     <para>
+-    The target of this document is ones who already have enough
+-    skill of C language and have the basic knowledge of linux
+-    kernel programming.  This document doesn't explain the general
+-    topics of linux kernel codes and doesn't cover the detail of
+-    implementation of each low-level driver.  It describes only how is
++    This document targets people who already have enough
++    C language skills and have basic linux kernel programming
++    knowledge.  This document doesn't explain the general
++    topic of linux kernel coding and doesn't cover low-level
++    driver implementation details. It only describes
+     the standard way to write a PCI sound driver on ALSA.
+     </para>
+ 
+     <para>
+-      If you are already familiar with the older ALSA ver.0.5.x, you
+-    can check the drivers such as <filename>es1938.c</filename> or
+-    <filename>maestro3.c</filename> which have also almost the same
++      If you are already familiar with the older ALSA ver.0.5.x API, you
++    can check the drivers such as <filename>sound/pci/es1938.c</filename> or
++    <filename>sound/pci/maestro3.c</filename> which have also almost the same
+     code-base in the ALSA 0.5.x tree, so you can compare the differences.
+     </para>
+ 
+     <para>
+-      This document is still a draft version. Any feedbacks and
++      This document is still a draft version. Any feedback and
+     corrections, please!!
+     </para>
+   </preface>
+@@ -106,7 +106,7 @@
+     <section id="file-tree-general">
+       <title>General</title>
+       <para>
+-        The ALSA drivers are provided in the two ways.
++        The ALSA drivers are provided in two ways.
+       </para>
+ 
+       <para>
+@@ -114,15 +114,14 @@
+       ALSA's ftp site, and another is the 2.6 (or later) Linux kernel
+       tree. To synchronize both, the ALSA driver tree is split into
+       two different trees: alsa-kernel and alsa-driver. The former
+-      contains purely the source codes for the Linux 2.6 (or later)
++      contains purely the source code for the Linux 2.6 (or later)
+       tree. This tree is designed only for compilation on 2.6 or
+       later environment. The latter, alsa-driver, contains many subtle
+-      files for compiling the ALSA driver on the outside of Linux
+-      kernel like configure script, the wrapper functions for older,
+-      2.2 and 2.4 kernels, to adapt the latest kernel API,
++      files for compiling ALSA drivers outside of the Linux kernel tree,
++      wrapper functions for older 2.2 and 2.4 kernels, to adapt the latest kernel API,
+       and additional drivers which are still in development or in
+       tests.  The drivers in alsa-driver tree will be moved to
+-      alsa-kernel (eventually 2.6 kernel tree) once when they are
++      alsa-kernel (and eventually to the 2.6 kernel tree) when they are
+       finished and confirmed to work fine.
+       </para>
+ 
+@@ -168,7 +167,7 @@
+     <section id="file-tree-core-directory">
+       <title>core directory</title>
+       <para>
+-        This directory contains the middle layer, that is, the heart
++        This directory contains the middle layer which is the heart
+       of ALSA drivers. In this directory, the native ALSA modules are
+       stored. The sub-directories contain different modules and are
+       dependent upon the kernel config. 
+@@ -181,7 +180,7 @@
+           The codes for PCM and mixer OSS emulation modules are stored
+         in this directory. The rawmidi OSS emulation is included in
+         the ALSA rawmidi code since it's quite small. The sequencer
+-        code is stored in core/seq/oss directory (see
++        code is stored in <filename>core/seq/oss</filename> directory (see
+         <link linkend="file-tree-core-directory-seq-oss"><citetitle>
+         below</citetitle></link>).
+         </para>
+@@ -200,7 +199,7 @@
+       <section id="file-tree-core-directory-seq">
+         <title>core/seq</title>
+         <para>
+-          This and its sub-directories are for the ALSA
++          This directory and its sub-directories are for the ALSA
+         sequencer. This directory contains the sequencer core and
+         primary sequencer modules such like snd-seq-midi,
+         snd-seq-virmidi, etc. They are compiled only when
+@@ -229,22 +228,22 @@
+       <title>include directory</title>
+       <para>
+         This is the place for the public header files of ALSA drivers,
+-      which are to be exported to the user-space, or included by
++      which are to be exported to user-space, or included by
+       several files at different directories. Basically, the private
+       header files should not be placed in this directory, but you may
+-      still find files there, due to historical reason :) 
++      still find files there, due to historical reasons :) 
+       </para>
+     </section>
+ 
+     <section id="file-tree-drivers-directory">
+       <title>drivers directory</title>
+       <para>
+-        This directory contains the codes shared among different drivers
+-      on the different architectures.  They are hence supposed not to be
++        This directory contains code shared among different drivers
++      on different architectures.  They are hence supposed not to be
+       architecture-specific.
+       For example, the dummy pcm driver and the serial MIDI
+       driver are found in this directory. In the sub-directories,
+-      there are the codes for components which are independent from
++      there is code for components which are independent from
+       bus and cpu architectures. 
+       </para>
+ 
+@@ -271,7 +270,7 @@
+ 
+       <para>
+         Although there is a standard i2c layer on Linux, ALSA has its
+-      own i2c codes for some cards, because the soundcard needs only a
++      own i2c code for some cards, because the soundcard needs only a
+       simple operation and the standard i2c API is too complicated for
+       such a purpose. 
+       </para>
+@@ -292,28 +291,28 @@
+ 
+         <para>
+           So far, there is only Emu8000/Emu10k1 synth driver under
+-        synth/emux sub-directory. 
++        the <filename>synth/emux</filename> sub-directory. 
+         </para>
+     </section>
+ 
+     <section id="file-tree-pci-directory">
+       <title>pci directory</title>
+       <para>
+-        This and its sub-directories hold the top-level card modules
+-      for PCI soundcards and the codes specific to the PCI BUS.
++        This directory and its sub-directories hold the top-level card modules
++      for PCI soundcards and the code specific to the PCI BUS.
+       </para>
+ 
+       <para>
+-        The drivers compiled from a single file is stored directly on
+-      pci directory, while the drivers with several source files are
+-      stored on its own sub-directory (e.g. emu10k1, ice1712). 
++        The drivers compiled from a single file are stored directly
++      in the pci directory, while the drivers with several source files are
++      stored on their own sub-directory (e.g. emu10k1, ice1712). 
+       </para>
+     </section>
+ 
+     <section id="file-tree-isa-directory">
+       <title>isa directory</title>
+       <para>
+-        This and its sub-directories hold the top-level card modules
++        This directory and its sub-directories hold the top-level card modules
+       for ISA soundcards. 
+       </para>
+     </section>
+@@ -321,16 +320,16 @@
+     <section id="file-tree-arm-ppc-sparc-directories">
+       <title>arm, ppc, and sparc directories</title>
+       <para>
+-        These are for the top-level card modules which are
+-      specific to each given architecture. 
++        They are used for top-level card modules which are
++      specific to one of these architectures. 
+       </para>
+     </section>
+ 
+     <section id="file-tree-usb-directory">
+       <title>usb directory</title>
+       <para>
+-        This contains the USB-audio driver. On the latest version, the
+-      USB MIDI driver is integrated together with usb-audio driver. 
++        This directory contains the USB-audio driver. In the latest version, the
++      USB MIDI driver is integrated in the usb-audio driver. 
+       </para>
+     </section>
+ 
+@@ -338,16 +337,17 @@
+       <title>pcmcia directory</title>
+       <para>
+         The PCMCIA, especially PCCard drivers will go here. CardBus
+-      drivers will be on pci directory, because its API is identical
+-      with the standard PCI cards. 
++      drivers will be in the pci directory, because their API is identical
++      to that of standard PCI cards. 
+       </para>
+     </section>
+ 
+     <section id="file-tree-oss-directory">
+       <title>oss directory</title>
+       <para>
+-        The OSS/Lite source files are stored here on Linux 2.6 (or
+-      later) tree. (In the ALSA driver tarball, it's empty, of course :) 
++        The OSS/Lite source files are stored here in Linux 2.6 (or
++      later) tree. In the ALSA driver tarball, this directory is empty,
++      of course :) 
+       </para>
+     </section>
+   </chapter>
+@@ -362,7 +362,7 @@
+     <section id="basic-flow-outline">
+       <title>Outline</title>
+       <para>
+-        The minimum flow of PCI soundcard is like the following:
++        The minimum flow for PCI soundcards is as follows:
+ 
+         <itemizedlist>
+           <listitem><para>define the PCI ID table (see the section
+@@ -370,9 +370,13 @@
+           </citetitle></link>).</para></listitem> 
+           <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_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>
++          <listitem><para>create a <structname>pci_driver</structname> structure
++	  containing the three pointers above.</para></listitem>
++          <listitem><para>create an <function>init()</function> function just calling
++	  the <function>pci_register_driver()</function> to register the pci_driver table
++	  defined above.</para></listitem>
++          <listitem><para>create an <function>exit()</function> function to call
++	  the <function>pci_unregister_driver()</function> function.</para></listitem>
+         </itemizedlist>
+       </para>
+     </section>
+@@ -382,15 +386,14 @@
+       <para>
+         The code example is shown below. Some parts are kept
+       unimplemented at this moment but will be filled in the
+-      succeeding sections. The numbers in comment lines of
+-      <function>snd_mychip_probe()</function> function are the
+-      markers. 
++      next sections. The numbers in the comment lines of the
++      <function>snd_mychip_probe()</function> function
++      refer to details explained in the following section. 
+ 
+         <example>
+-          <title>Basic Flow for PCI Drivers Example</title>
++          <title>Basic Flow for PCI Drivers - Example</title>
+           <programlisting>
+ <![CDATA[
+-  #include <sound/driver.h>
+   #include <linux/init.h>
+   #include <linux/pci.h>
+   #include <linux/slab.h>
+@@ -398,6 +401,7 @@
+   #include <sound/initval.h>
+ 
+   /* module parameters (see "Module Parameters") */
++  /* SNDRV_CARDS: maximum number of cards supported by this module */
+   static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+   static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+   static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+@@ -405,13 +409,13 @@
+   /* definition of the chip-specific record */
+   struct mychip {
+           struct snd_card *card;
+-          /* rest of implementation will be in the section
+-           * "PCI Resource Managements"
++          /* the rest of the implementation will be in section
++           * "PCI Resource Management"
+            */
+   };
+ 
+   /* chip-specific destructor
+-   * (see "PCI Resource Managements")
++   * (see "PCI Resource Management")
+    */
+   static int snd_mychip_free(struct mychip *chip)
+   {
+@@ -442,7 +446,7 @@
+           *rchip = NULL;
+ 
+           /* check PCI availability here
+-           * (see "PCI Resource Managements")
++           * (see "PCI Resource Management")
+            */
+           ....
+ 
+@@ -454,7 +458,7 @@
+           chip->card = card;
+ 
+           /* rest of initialization here; will be implemented
+-           * later, see "PCI Resource Managements"
++           * later, see "PCI Resource Management"
+            */
+           ....
+ 
+@@ -521,7 +525,7 @@
+           return 0;
+   }
+ 
+-  /* destructor -- see "Destructor" sub-section */
++  /* destructor -- see the "Destructor" sub-section */
+   static void __devexit snd_mychip_remove(struct pci_dev *pci)
+   {
+           snd_card_free(pci_get_drvdata(pci));
+@@ -536,16 +540,16 @@
+     <section id="basic-flow-constructor">
+       <title>Constructor</title>
+       <para>
+-        The real constructor of PCI drivers is probe callback. The
+-      probe callback and other component-constructors which are called
+-      from probe callback should be defined with
+-      <parameter>__devinit</parameter> prefix. You 
+-      cannot use <parameter>__init</parameter> prefix for them,
++        The real constructor of PCI drivers is the <function>probe</function> callback.
++      The <function>probe</function> callback and other component-constructors which are called
++      from the <function>probe</function> callback should be defined with
++      the <parameter>__devinit</parameter> prefix. You 
++      cannot use the <parameter>__init</parameter> prefix for them,
+       because any PCI device could be a hotplug device. 
+       </para>
+ 
+       <para>
+-        In the probe callback, the following scheme is often used.
++        In the <function>probe</function> callback, the following scheme is often used.
+       </para>
+ 
+       <section id="basic-flow-constructor-device-index">
+@@ -570,7 +574,7 @@
+         </para>
+ 
+         <para>
+-          At each time probe callback is called, check the
++          Each time the <function>probe</function> callback is called, check the
+         availability of the device. If not available, simply increment
+         the device index and returns. dev will be incremented also
+         later (<link
+@@ -594,7 +598,7 @@
+         </para>
+ 
+         <para>
+-          The detail will be explained in the section
++          The details will be explained in the section
+           <link linkend="card-management-card-instance"><citetitle>
+           Management of Cards and Components</citetitle></link>.
+         </para>
+@@ -619,9 +623,9 @@
+             </programlisting>
+           </informalexample>
+ 
+-          The detail will be explained in the section <link
++          The details will be explained in the section <link
+         linkend="pci-resource"><citetitle>PCI Resource
+-        Managements</citetitle></link>.
++        Management</citetitle></link>.
+         </para>
+       </section>
+ 
+@@ -640,7 +644,7 @@
+           </informalexample>
+ 
+           The driver field holds the minimal ID string of the
+-        chip. This is referred by alsa-lib's configurator, so keep it
++        chip. This is used by alsa-lib's configurator, so keep it
+         simple but unique. 
+           Even the same driver can have different driver IDs to
+         distinguish the functionality of each chip type. 
+@@ -648,7 +652,7 @@
+ 
+         <para>
+           The shortname field is a string shown as more verbose
+-        name. The longname field contains the information which is
++        name. The longname field contains the information
+         shown in <filename>/proc/asound/cards</filename>. 
+         </para>
+       </section>
+@@ -703,7 +707,7 @@
+           </informalexample>
+ 
+           In the above, the card record is stored. This pointer is
+-        referred in the remove callback and power-management
++        used in the remove callback and power-management
+         callbacks, too. 
+         </para>
+       </section>
+@@ -746,7 +750,6 @@
+         <informalexample>
+           <programlisting>
+ <![CDATA[
+-  #include <sound/driver.h>
+   #include <linux/init.h>
+   #include <linux/pci.h>
+   #include <linux/slab.h>
+@@ -757,22 +760,22 @@
+         </informalexample>
+ 
+ 	where the last one is necessary only when module options are
+-      defined in the source file.  If the codes are split to several
+-      files, the file without module options don't need them.
++      defined in the source file.  If the code is split into several
++      files, the files without module options don't need them.
+       </para>
+ 
+       <para>
+-        In addition to them, you'll need
+-      <filename>&lt;linux/interrupt.h&gt;</filename> for the interrupt
+-      handling, and <filename>&lt;asm/io.h&gt;</filename> for the i/o
+-      access. If you use <function>mdelay()</function> or
++        In addition to these headers, you'll need
++      <filename>&lt;linux/interrupt.h&gt;</filename> for interrupt
++      handling, and <filename>&lt;asm/io.h&gt;</filename> for I/O
++      access. If you use the <function>mdelay()</function> or
+       <function>udelay()</function> functions, you'll need to include
+-      <filename>&lt;linux/delay.h&gt;</filename>, too. 
++      <filename>&lt;linux/delay.h&gt;</filename> too. 
+       </para>
+ 
+       <para>
+-      The ALSA interfaces like PCM or control API are defined in other
+-      header files as <filename>&lt;sound/xxx.h&gt;</filename>.
++      The ALSA interfaces like the PCM and control APIs are defined in other
++      <filename>&lt;sound/xxx.h&gt;</filename> header files.
+       They have to be included after
+       <filename>&lt;sound/core.h&gt;</filename>.
+       </para>
+@@ -795,12 +798,12 @@
+ 
+       <para>
+       A card record is the headquarters of the soundcard.  It manages
+-      the list of whole devices (components) on the soundcard, such as
++      the whole list of devices (components) on the soundcard, such as
+       PCM, mixers, MIDI, synthesizer, and so on.  Also, the card
+       record holds the ID and the name strings of the card, manages
+       the root of proc files, and controls the power-management states
+       and hotplug disconnections.  The component list on the card
+-      record is used to manage the proper releases of resources at
++      record is used to manage the correct release of resources at
+       destruction. 
+       </para>
+ 
+@@ -824,9 +827,8 @@
+         <constant>THIS_MODULE</constant>),
+         and the size of extra-data space.  The last argument is used to
+         allocate card-&gt;private_data for the
+-        chip-specific data.  Note that this data
+-        <emphasis>is</emphasis> allocated by
+-        <function>snd_card_new()</function>.
++        chip-specific data.  Note that these data
++        are allocated by <function>snd_card_new()</function>.
+       </para>
+     </section>
+ 
+@@ -834,10 +836,10 @@
+       <title>Components</title>
+       <para>
+         After the card is created, you can attach the components
+-      (devices) to the card instance. On ALSA driver, a component is
++      (devices) to the card instance. In an ALSA driver, a component is
+       represented as a struct <structname>snd_device</structname> object.
+       A component can be a PCM instance, a control interface, a raw
+-      MIDI interface, etc.  Each of such instances has one component
++      MIDI interface, etc.  Each such instance has one component
+       entry.
+       </para>
+ 
+@@ -859,7 +861,7 @@
+       (<constant>SNDRV_DEV_XXX</constant>), the data pointer, and the
+       callback pointers (<parameter>&amp;ops</parameter>). The
+       device-level defines the type of components and the order of
+-      registration and de-registration.  For most of components, the
++      registration and de-registration.  For most components, the
+       device-level is already defined.  For a user-defined component,
+       you can use <constant>SNDRV_DEV_LOWLEVEL</constant>.
+       </para>
+@@ -867,13 +869,13 @@
+       <para>
+       This function itself doesn't allocate the data space. The data
+       must be allocated manually beforehand, and its pointer is passed
+-      as the argument. This pointer is used as the identifier
+-      (<parameter>chip</parameter> in the above example) for the
+-      instance. 
++      as the argument. This pointer is used as the
++      (<parameter>chip</parameter> identifier in the above example)
++      for the instance. 
+       </para>
+ 
+       <para>
+-        Each ALSA pre-defined component such as ac97 or pcm calls
++        Each pre-defined ALSA component such as ac97 and pcm calls
+       <function>snd_device_new()</function> inside its
+       constructor. The destructor for each component is defined in the
+       callback pointers.  Hence, you don't need to take care of
+@@ -881,19 +883,19 @@
+       </para>
+ 
+       <para>
+-        If you would like to create your own component, you need to
+-      set the destructor function to dev_free callback in
+-      <parameter>ops</parameter>, so that it can be released
+-      automatically via <function>snd_card_free()</function>. The
+-      example will be shown later as an implementation of a
+-      chip-specific data. 
++        If you wish to create your own component, you need to
++      set the destructor function to the dev_free callback in
++      the <parameter>ops</parameter>, so that it can be released
++      automatically via <function>snd_card_free()</function>.
++      The next example will show an implementation of chip-specific
++      data.
+       </para>
+     </section>
+ 
+     <section id="card-management-chip-specific">
+       <title>Chip-Specific Data</title>
+       <para>
+-      The chip-specific information, e.g. the i/o port address, its
++      Chip-specific information, e.g. the I/O port address, its
+       resource pointer, or the irq number, is stored in the
+       chip-specific record.
+ 
+@@ -909,13 +911,14 @@
+       </para>
+ 
+       <para>
+-        In general, there are two ways to allocate the chip record.
++        In general, there are two ways of allocating the chip record.
+       </para>
+ 
+       <section id="card-management-chip-specific-snd-card-new">
+         <title>1. Allocating via <function>snd_card_new()</function>.</title>
+         <para>
+-          As mentioned above, you can pass the extra-data-length to the 4th argument of <function>snd_card_new()</function>, i.e.
++          As mentioned above, you can pass the extra-data-length
++	  to the 4th argument of <function>snd_card_new()</function>, i.e.
+ 
+           <informalexample>
+             <programlisting>
+@@ -925,7 +928,7 @@
+             </programlisting>
+           </informalexample>
+ 
+-          whether struct <structname>mychip</structname> is the type of the chip record.
++          struct <structname>mychip</structname> is the type of the chip record.
+         </para>
+ 
+         <para>
+@@ -1037,8 +1040,8 @@
+       <title>Registration and Release</title>
+       <para>
+         After all components are assigned, register the card instance
+-      by calling <function>snd_card_register()</function>. The access
+-      to the device files are enabled at this point. That is, before
++      by calling <function>snd_card_register()</function>. Access
++      to the device files is enabled at this point. That is, before
+       <function>snd_card_register()</function> is called, the
+       components are safely inaccessible from external side. If this
+       call fails, exit the probe function after releasing the card via
+@@ -1047,7 +1050,7 @@
+ 
+       <para>
+         For releasing the card instance, you can call simply
+-      <function>snd_card_free()</function>. As already mentioned, all
++      <function>snd_card_free()</function>. As mentioned earlier, all
+       components are released automatically by this call. 
+       </para>
+ 
+@@ -1055,7 +1058,7 @@
+         As further notes, the destructors (both
+       <function>snd_mychip_dev_free</function> and
+       <function>snd_mychip_free</function>) cannot be defined with
+-      <parameter>__devexit</parameter> prefix, because they may be
++      the <parameter>__devexit</parameter> prefix, because they may be
+       called from the constructor, too, at the false path. 
+       </para>
+ 
+@@ -1071,20 +1074,20 @@
+ 
+ 
+ <!-- ****************************************************** -->
+-<!-- PCI Resource Managements  -->
++<!-- PCI Resource Management  -->
+ <!-- ****************************************************** -->
+   <chapter id="pci-resource">
+-    <title>PCI Resource Managements</title>
++    <title>PCI Resource Management</title>
+ 
+     <section id="pci-resource-example">
+       <title>Full Code Example</title>
+       <para>
+-        In this section, we'll finish the chip-specific constructor,
+-      destructor and PCI entries. The example code is shown first,
++        In this section, we'll complete the chip-specific constructor,
++      destructor and PCI entries. Example code is shown first,
+       below. 
+ 
+         <example>
+-          <title>PCI Resource Managements Example</title>
++          <title>PCI Resource Management Example</title>
+           <programlisting>
+ <![CDATA[
+   struct mychip {
+@@ -1103,7 +1106,7 @@
+           /* release the irq */
+           if (chip->irq >= 0)
+                   free_irq(chip->irq, chip);
+-          /* release the i/o ports & memory */
++          /* release the I/O ports & memory */
+           pci_release_regions(chip->pci);
+           /* disable the PCI entry */
+           pci_disable_device(chip->pci);
+@@ -1196,13 +1199,13 @@
+           .remove = __devexit_p(snd_mychip_remove),
+   };
+ 
+-  /* initialization of the module */
++  /* module initialization */
+   static int __init alsa_card_mychip_init(void)
+   {
+           return pci_register_driver(&driver);
+   }
+ 
+-  /* clean up the module */
++  /* module clean up */
+   static void __exit alsa_card_mychip_exit(void)
+   {
+           pci_unregister_driver(&driver);
+@@ -1228,10 +1231,10 @@
+       </para>
+ 
+       <para>
+-        In the case of PCI devices, you have to call at first
+-      <function>pci_enable_device()</function> function before
++        In the case of PCI devices, you first have to call
++      the <function>pci_enable_device()</function> function before
+       allocating resources. Also, you need to set the proper PCI DMA
+-      mask to limit the accessed i/o range. In some cases, you might
++      mask to limit the accessed I/O range. In some cases, you might
+       need to call <function>pci_set_master()</function> function,
+       too.
+       </para>
+@@ -1261,15 +1264,15 @@
+     <section id="pci-resource-resource-allocation">
+       <title>Resource Allocation</title>
+       <para>
+-        The allocation of I/O ports and irqs are done via standard kernel
++        The allocation of I/O ports and irqs is done via standard kernel
+       functions. Unlike ALSA ver.0.5.x., there are no helpers for
+       that. And these resources must be released in the destructor
+       function (see below). Also, on ALSA 0.9.x, you don't need to
+-      allocate (pseudo-)DMA for PCI like ALSA 0.5.x.
++      allocate (pseudo-)DMA for PCI like in ALSA 0.5.x.
+       </para>
+ 
+       <para>
+-        Now assume that this PCI device has an I/O port with 8 bytes
++        Now assume that the PCI device has an I/O port with 8 bytes
+         and an interrupt. Then struct <structname>mychip</structname> will have the
+         following fields:
+ 
+@@ -1288,7 +1291,7 @@
+       </para>
+ 
+       <para>
+-        For an i/o port (and also a memory region), you need to have
++        For an I/O port (and also a memory region), you need to have
+       the resource pointer for the standard resource management. For
+       an irq, you have to keep only the irq number (integer). But you
+       need to initialize this number as -1 before actual allocation,
+@@ -1299,7 +1302,7 @@
+       </para>
+ 
+       <para>
+-        The allocation of an i/o port is done like this:
++        The allocation of an I/O port is done like this:
+ 
+         <informalexample>
+           <programlisting>
+@@ -1318,12 +1321,12 @@
+ 
+       <para>
+         <!-- obsolete -->
+-        It will reserve the i/o port region of 8 bytes of the given
++        It will reserve the I/O port region of 8 bytes of the given
+       PCI device. The returned value, chip-&gt;res_port, is allocated
+       via <function>kmalloc()</function> by
+       <function>request_region()</function>. The pointer must be
+-      released via <function>kfree()</function>, but there is some
+-      problem regarding this. This issue will be explained more below.
++      released via <function>kfree()</function>, but there is a
++      problem with this. This issue will be explained later.
+       </para>
+ 
+       <para>
+@@ -1351,8 +1354,8 @@
+       </para>
+ 
+       <para>
+-      On the PCI bus, the interrupts can be shared. Thus,
+-      <constant>IRQF_SHARED</constant> is given as the interrupt flag of
++      On the PCI bus, interrupts can be shared. Thus,
++      <constant>IRQF_SHARED</constant> is used as the interrupt flag of
+       <function>request_irq()</function>. 
+       </para>
+ 
+@@ -1364,7 +1367,7 @@
+       </para>
+ 
+       <para>
+-        I won't define the detail of the interrupt handler at this
++        I won't give details about the interrupt handler at this
+         point, but at least its appearance can be explained now. The
+         interrupt handler looks usually like the following: 
+ 
+@@ -1386,11 +1389,11 @@
+         Now let's write the corresponding destructor for the resources
+       above. The role of destructor is simple: disable the hardware
+       (if already activated) and release the resources. So far, we
+-      have no hardware part, so the disabling is not written here. 
++      have no hardware part, so the disabling code is not written here. 
+       </para>
+ 
+       <para>
+-        For releasing the resources, <quote>check-and-release</quote>
++        To release the resources, the <quote>check-and-release</quote>
+         method is a safer way. For the interrupt, do like this: 
+ 
+         <informalexample>
+@@ -1410,7 +1413,7 @@
+       <para>
+         When you requested I/O ports or memory regions via
+ 	<function>pci_request_region()</function> or
+-	<function>pci_request_regions()</function> like this example,
++	<function>pci_request_regions()</function> like in this example,
+ 	release the resource(s) using the corresponding function,
+ 	<function>pci_release_region()</function> or
+ 	<function>pci_release_regions()</function>.
+@@ -1429,7 +1432,7 @@
+ 	or <function>request_mem_region</function>, you can release it via
+ 	<function>release_resource()</function>.  Suppose that you keep
+ 	the resource pointer returned from <function>request_region()</function>
+-	in chip-&gt;res_port, the release procedure looks like below:
++	in chip-&gt;res_port, the release procedure looks like:
+ 
+         <informalexample>
+           <programlisting>
+@@ -1442,7 +1445,7 @@
+ 
+       <para>
+       Don't forget to call <function>pci_disable_device()</function>
+-      before all finished.
++      before the end.
+       </para>
+ 
+       <para>
+@@ -1459,14 +1462,14 @@
+ 
+       <para>
+       Again, remember that you cannot
+-      set <parameter>__devexit</parameter> prefix for this destructor. 
++      use the <parameter>__devexit</parameter> prefix for this destructor. 
+       </para>
+ 
+       <para>
+-      We didn't implement the hardware-disabling part in the above.
++      We didn't implement the hardware disabling part in the above.
+       If you need to do this, please note that the destructor may be
+       called even before the initialization of the chip is completed.
+-      It would be better to have a flag to skip the hardware-disabling
++      It would be better to have a flag to skip hardware disabling
+       if the hardware was not initialized yet.
+       </para>
+ 
+@@ -1475,14 +1478,14 @@
+       <function>snd_device_new()</function> with
+       <constant>SNDRV_DEV_LOWLELVEL</constant> , its destructor is 
+       called at the last.  That is, it is assured that all other
+-      components like PCMs and controls have been already released.
+-      You don't have to call stopping PCMs, etc. explicitly, but just
+-      stop the hardware in the low-level.
++      components like PCMs and controls have already been released.
++      You don't have to stop PCMs, etc. explicitly, but just
++      call low-level hardware stopping.
+       </para>
+ 
+       <para>
+         The management of a memory-mapped region is almost as same as
+-        the management of an i/o port. You'll need three fields like
++        the management of an I/O port. You'll need three fields like
+         the following: 
+ 
+         <informalexample>
+@@ -1561,8 +1564,8 @@
+     <section id="pci-resource-entries">
+       <title>PCI Entries</title>
+       <para>
+-        So far, so good. Let's finish the rest of missing PCI
+-      stuffs. At first, we need a
++        So far, so good. Let's finish the missing PCI
++      stuff. At first, we need a
+       <structname>pci_device_id</structname> table for this
+       chipset. It's a table of PCI vendor/device ID number, and some
+       masks. 
+@@ -1588,13 +1591,13 @@
+ 
+       <para>
+         The first and second fields of
+-      <structname>pci_device_id</structname> struct are the vendor and
+-      device IDs. If you have nothing special to filter the matching
+-      devices, you can use the rest of fields like above. The last
+-      field of <structname>pci_device_id</structname> struct is a
++      the <structname>pci_device_id</structname> structure are the vendor and
++      device IDs. If you have no reason to filter the matching
++      devices, you can leave the remaining fields as above. The last
++      field of the <structname>pci_device_id</structname> struct contains
+       private data for this entry. You can specify any value here, for
+-      example, to tell the type of different operations per each
+-      device IDs. Such an example is found in intel8x0 driver. 
++      example, to define specific operations for supported device IDs.
++      Such an example is found in the intel8x0 driver. 
+       </para>
+ 
+       <para>
+@@ -1621,10 +1624,10 @@
+ 
+       <para>
+         The <structfield>probe</structfield> and
+-      <structfield>remove</structfield> functions are what we already
+-      defined in 
+-      the previous sections. The <structfield>remove</structfield> should
+-      be defined with 
++      <structfield>remove</structfield> functions have already
++      been defined in the previous sections.
++      The <structfield>remove</structfield> function should
++      be defined with the 
+       <function>__devexit_p()</function> macro, so that it's not
+       defined for built-in (and non-hot-pluggable) case. The
+       <structfield>name</structfield> 
+@@ -1665,8 +1668,7 @@
+ 
+       <para>
+         Oh, one thing was forgotten. If you have no exported symbols,
+-        you need to declare it on 2.2 or 2.4 kernels (on 2.6 kernels
+-        it's not necessary, though).
++        you need to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels).
+ 
+         <informalexample>
+           <programlisting>
+@@ -1698,7 +1700,7 @@
+ 
+       <para>
+         For accessing to the PCM layer, you need to include
+-      <filename>&lt;sound/pcm.h&gt;</filename> above all. In addition,
++      <filename>&lt;sound/pcm.h&gt;</filename> first. In addition,
+       <filename>&lt;sound/pcm_params.h&gt;</filename> might be needed
+       if you access to some functions related with hw_param. 
+       </para>
+@@ -1707,21 +1709,21 @@
+         Each card device can have up to four pcm instances. A pcm
+       instance corresponds to a pcm device file. The limitation of
+       number of instances comes only from the available bit size of
+-      the linux's device number. Once when 64bit device number is
+-      used, we'll have more available pcm instances. 
++      the Linux's device numbers. Once when 64bit device number is
++      used, we'll have more pcm instances available. 
+       </para>
+ 
+       <para>
+         A pcm instance consists of pcm playback and capture streams,
+       and each pcm stream consists of one or more pcm substreams. Some
+-      soundcard supports the multiple-playback function. For example,
++      soundcards support multiple playback functions. For example,
+       emu10k1 has a PCM playback of 32 stereo substreams. In this case, at
+       each open, a free substream is (usually) automatically chosen
+       and opened. Meanwhile, when only one substream exists and it was
+-      already opened, the succeeding open will result in the blocking
+-      or the error with <constant>EAGAIN</constant> according to the
+-      file open mode. But you don't have to know the detail in your
+-      driver. The PCM middle layer will take all such jobs. 
++      already opened, the successful open will either block
++      or error with <constant>EAGAIN</constant> according to the
++      file open mode. But you don't have to care about such details in your
++      driver. The PCM middle layer will take care of such work.
+       </para>
+     </section>
+ 
+@@ -1944,7 +1946,7 @@
+     <section id="pcm-interface-constructor">
+       <title>Constructor</title>
+       <para>
+-        A pcm instance is allocated by <function>snd_pcm_new()</function>
++        A pcm instance is allocated by the <function>snd_pcm_new()</function>
+       function. It would be better to create a constructor for pcm,
+       namely, 
+ 
+@@ -1971,23 +1973,23 @@
+       </para>
+ 
+       <para>
+-        The <function>snd_pcm_new()</function> function takes the four
++        The <function>snd_pcm_new()</function> function takes four
+       arguments. The first argument is the card pointer to which this
+       pcm is assigned, and the second is the ID string. 
+       </para>
+ 
+       <para>
+         The third argument (<parameter>index</parameter>, 0 in the
+-      above) is the index of this new pcm. It begins from zero. When
+-      you will create more than one pcm instances, specify the
++      above) is the index of this new pcm. It begins from zero. If
++      you create more than one pcm instances, specify the
+       different numbers in this argument. For example,
+       <parameter>index</parameter> = 1 for the second PCM device.  
+       </para>
+ 
+       <para>
+         The fourth and fifth arguments are the number of substreams
+-      for playback and capture, respectively. Here both 1 are given in
+-      the above example.  When no playback or no capture is available,
++      for playback and capture, respectively. Here 1 is used for
++      both arguments. When no playback or capture substreams are available,
+       pass 0 to the corresponding argument.
+       </para>
+ 
+@@ -2045,13 +2047,13 @@
+           </programlisting>
+         </informalexample>
+ 
+-        Each of callbacks is explained in the subsection 
++        All the callbacks are described in the
+         <link linkend="pcm-interface-operators"><citetitle>
+-        Operators</citetitle></link>.
++        Operators</citetitle></link> subsection.
+       </para>
+ 
+       <para>
+-        After setting the operators, most likely you'd like to
++        After setting the operators, you probably will want to
+         pre-allocate the buffer. For the pre-allocation, simply call
+         the following: 
+ 
+@@ -2065,8 +2067,8 @@
+           </programlisting>
+         </informalexample>
+ 
+-        It will allocate up to 64kB buffer as default. The details of
+-      buffer management will be described in the later section <link
++        It will allocate a buffer up to 64kB as default.
++      Buffer management details will be described in the later section <link
+       linkend="buffer-and-memory"><citetitle>Buffer and Memory
+       Management</citetitle></link>. 
+       </para>
+@@ -2095,13 +2097,13 @@
+       <para>
+         The destructor for a pcm instance is not always
+       necessary. Since the pcm device will be released by the middle
+-      layer code automatically, you don't have to call destructor
++      layer code automatically, you don't have to call the destructor
+       explicitly.
+       </para>
+ 
+       <para>
+-        The destructor would be necessary when you created some
+-        special records internally and need to release them. In such a
++        The destructor would be necessary if you created
++        special records internally and needed to release them. In such a
+         case, set the destructor function to
+         pcm-&gt;private_free: 
+ 
+@@ -2141,16 +2143,15 @@
+ 	  When the PCM substream is opened, a PCM runtime instance is
+ 	allocated and assigned to the substream. This pointer is
+ 	accessible via <constant>substream-&gt;runtime</constant>.
+-	This runtime pointer holds the various information; it holds
+-	the copy of hw_params and sw_params configurations, the buffer
+-	pointers, mmap records, spinlocks, etc.  Almost everything you
+-	need for controlling the PCM can be found there.
++	This runtime pointer holds most information you need
++	to control the PCM: the copy of hw_params and sw_params configurations, the buffer
++	pointers, mmap records, spinlocks, etc.
+ 	</para>
+ 
+ 	<para>
+ 	The definition of runtime instance is found in
+-	<filename>&lt;sound/pcm.h&gt;</filename>.  Here is the
+-	copy from the file.
++	<filename>&lt;sound/pcm.h&gt;</filename>.  Here are
++       the contents of this file:
+           <informalexample>
+             <programlisting>
+ <![CDATA[
+@@ -2185,7 +2186,6 @@ struct _snd_pcm_runtime {
+ 	struct timespec tstamp_mode;	/* mmap timestamp is updated */
+   	unsigned int period_step;
+ 	unsigned int sleep_min;		/* min ticks to sleep */
+-	snd_pcm_uframes_t xfer_align;	/* xfer size need to be a multiple */
+ 	snd_pcm_uframes_t start_threshold;
+ 	snd_pcm_uframes_t stop_threshold;
+ 	snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
+@@ -2244,7 +2244,7 @@ struct _snd_pcm_runtime {
+ 	<para>
+ 	  For the operators (callbacks) of each sound driver, most of
+ 	these records are supposed to be read-only.  Only the PCM
+-	middle-layer changes / updates these info.  The exceptions are
++	middle-layer changes / updates them.  The exceptions are
+ 	the hardware description (hw), interrupt callbacks
+ 	(transfer_ack_xxx), DMA buffer information, and the private
+ 	data.  Besides, if you use the standard buffer allocation
+@@ -2285,7 +2285,7 @@ struct _snd_pcm_runtime {
+ 	</para>
+ 
+ 	<para>
+-	  Typically, you'll have a hardware descriptor like below:
++	  Typically, you'll have a hardware descriptor as below:
+           <informalexample>
+             <programlisting>
+ <![CDATA[
+@@ -2320,10 +2320,10 @@ struct _snd_pcm_runtime {
+         <constant>SNDRV_PCM_INFO_XXX</constant>. Here, at least, you
+         have to specify whether the mmap is supported and which
+         interleaved format is supported.
+-        When the mmap is supported, add
++        When the is supported, add the
+         <constant>SNDRV_PCM_INFO_MMAP</constant> flag here. When the
+         hardware supports the interleaved or the non-interleaved
+-        format, <constant>SNDRV_PCM_INFO_INTERLEAVED</constant> or
++        formats, <constant>SNDRV_PCM_INFO_INTERLEAVED</constant> or
+         <constant>SNDRV_PCM_INFO_NONINTERLEAVED</constant> flag must
+         be set, respectively. If both are supported, you can set both,
+         too. 
+@@ -2331,7 +2331,7 @@ struct _snd_pcm_runtime {
+ 
+         <para>
+           In the above example, <constant>MMAP_VALID</constant> and
+-        <constant>BLOCK_TRANSFER</constant> are specified for OSS mmap
++        <constant>BLOCK_TRANSFER</constant> are specified for the OSS mmap
+         mode. Usually both are set. Of course,
+         <constant>MMAP_VALID</constant> is set only if the mmap is
+         really supported. 
+@@ -2345,11 +2345,11 @@ struct _snd_pcm_runtime {
+         <quote>pause</quote> operation, while the
+         <constant>RESUME</constant> bit means that the pcm supports
+         the full <quote>suspend/resume</quote> operation.
+-	If <constant>PAUSE</constant> flag is set,
++	If the <constant>PAUSE</constant> flag is set,
+ 	the <structfield>trigger</structfield> callback below
+         must handle the corresponding (pause push/release) commands.
+ 	The suspend/resume trigger commands can be defined even without
+-	<constant>RESUME</constant> flag.  See <link
++	the <constant>RESUME</constant> flag.  See <link
+ 	linkend="power-management"><citetitle>
+ 	Power Management</citetitle></link> section for details.
+         </para>
+@@ -2382,7 +2382,7 @@ struct _snd_pcm_runtime {
+         <constant>CONTINUOUS</constant> bit additionally.
+         The pre-defined rate bits are provided only for typical
+ 	rates. If your chip supports unconventional rates, you need to add
+-        <constant>KNOT</constant> bit and set up the hardware
++        the <constant>KNOT</constant> bit and set up the hardware
+         constraint manually (explained later).
+         </para>
+ 	</listitem>
+@@ -2390,8 +2390,8 @@ struct _snd_pcm_runtime {
+ 	<listitem>
+ 	<para>
+ 	<structfield>rate_min</structfield> and
+-	<structfield>rate_max</structfield> define the minimal and
+-	maximal sample rate.  This should correspond somehow to
++	<structfield>rate_max</structfield> define the minimum and
++	maximum sample rate.  This should correspond somehow to
+ 	<structfield>rates</structfield> bits.
+ 	</para>
+ 	</listitem>
+@@ -2400,7 +2400,7 @@ struct _snd_pcm_runtime {
+ 	<para>
+ 	<structfield>channel_min</structfield> and
+ 	<structfield>channel_max</structfield> 
+-	define, as you might already expected, the minimal and maximal
++	define, as you might already expected, the minimum and maximum
+ 	number of channels.
+ 	</para>
+ 	</listitem>
+@@ -2408,21 +2408,21 @@ struct _snd_pcm_runtime {
+ 	<listitem>
+ 	<para>
+ 	<structfield>buffer_bytes_max</structfield> defines the
+-	maximal buffer size in bytes.  There is no
++	maximum buffer size in bytes.  There is no
+ 	<structfield>buffer_bytes_min</structfield> field, since
+-	it can be calculated from the minimal period size and the
+-	minimal number of periods.
++	it can be calculated from the minimum period size and the
++	minimum number of periods.
+ 	Meanwhile, <structfield>period_bytes_min</structfield> and
+-	define the minimal and maximal size of the period in bytes.
++	define the minimum and maximum size of the period in bytes.
+ 	<structfield>periods_max</structfield> and
+-	<structfield>periods_min</structfield> define the maximal and
+-	minimal number of periods in the buffer.
++	<structfield>periods_min</structfield> define the maximum and
++	minimum number of periods in the buffer.
+         </para>
+ 
+ 	<para>
+-	The <quote>period</quote> is a term, that corresponds to
+-	fragment in the OSS world.  The period defines the size at
+-	which the PCM interrupt is generated. This size strongly
++	The <quote>period</quote> is a term that corresponds to
++	a fragment in the OSS world. The period defines the size at
++	which a PCM interrupt is generated. This size strongly
+ 	depends on the hardware. 
+ 	Generally, the smaller period size will give you more
+ 	interrupts, that is, more controls. 
+@@ -2435,8 +2435,8 @@ struct _snd_pcm_runtime {
+ 	<listitem>
+ 	<para>
+ 	There is also a field <structfield>fifo_size</structfield>.
+-	This specifies the size of the hardware FIFO, but it's not
+-	used currently in the driver nor in the alsa-lib.  So, you
++	This specifies the size of the hardware FIFO, but currently it
++	is neither used in the driver nor in the alsa-lib.  So, you
+ 	can ignore this field.
+ 	</para>
+ 	</listitem>
+@@ -2450,7 +2450,7 @@ struct _snd_pcm_runtime {
+ 	Ok, let's go back again to the PCM runtime records.
+ 	The most frequently referred records in the runtime instance are
+ 	the PCM configurations.
+-	The PCM configurations are stored on runtime instance
++	The PCM configurations are stored in the runtime instance
+ 	after the application sends <type>hw_params</type> data via
+ 	alsa-lib.  There are many fields copied from hw_params and
+ 	sw_params structs.  For example,
+@@ -2461,11 +2461,11 @@ struct _snd_pcm_runtime {
+ 
+ 	<para>
+ 	One thing to be noted is that the configured buffer and period
+-	sizes are stored in <quote>frames</quote> in the runtime
++	sizes are stored in <quote>frames</quote> in the runtime.
+         In the ALSA world, 1 frame = channels * samples-size.
+ 	For conversion between frames and bytes, you can use the
+-	helper functions, <function>frames_to_bytes()</function> and
+-          <function>bytes_to_frames()</function>. 
++	<function>frames_to_bytes()</function> and
++          <function>bytes_to_frames()</function> helper functions. 
+           <informalexample>
+             <programlisting>
+ <![CDATA[
+@@ -2515,7 +2515,7 @@ struct _snd_pcm_runtime {
+ 	<structfield>dma_area</structfield> is necessary when the
+ 	buffer is mmapped.  If your driver doesn't support mmap, this
+ 	field is not necessary.  <structfield>dma_addr</structfield>
+-	is also not mandatory.  You can use
++	is also optional.  You can use
+ 	<structfield>dma_private</structfield> as you like, too.
+ 	</para>
+ 	</section>
+@@ -2524,14 +2524,14 @@ struct _snd_pcm_runtime {
+ 	<title>Running Status</title>
+ 	<para>
+ 	The running status can be referred via <constant>runtime-&gt;status</constant>.
+-	This is the pointer to struct <structname>snd_pcm_mmap_status</structname>
++	This is the pointer to the struct <structname>snd_pcm_mmap_status</structname>
+ 	record.  For example, you can get the current DMA hardware
+ 	pointer via <constant>runtime-&gt;status-&gt;hw_ptr</constant>.
+ 	</para>
+ 
+ 	<para>
+ 	The DMA application pointer can be referred via
+-	<constant>runtime-&gt;control</constant>, which points
++	<constant>runtime-&gt;control</constant>, which points to the
+ 	struct <structname>snd_pcm_mmap_control</structname> record.
+ 	However, accessing directly to this value is not recommended.
+ 	</para>
+@@ -2542,14 +2542,14 @@ struct _snd_pcm_runtime {
+ 	<para>
+ 	You can allocate a record for the substream and store it in
+ 	<constant>runtime-&gt;private_data</constant>.  Usually, this
+-	done in
++	is done in
+ 	<link linkend="pcm-interface-operators-open-callback"><citetitle>
+ 	the open callback</citetitle></link>.
+ 	Don't mix this with <constant>pcm-&gt;private_data</constant>.
+-	The <constant>pcm-&gt;private_data</constant> usually points the
++	The <constant>pcm-&gt;private_data</constant> usually points to the
+ 	chip instance assigned statically at the creation of PCM, while the 
+-	<constant>runtime-&gt;private_data</constant> points a dynamic
+-	data created at the PCM open callback.
++	<constant>runtime-&gt;private_data</constant> points to a dynamic
++	data structure created at the PCM open callback.
+ 
+           <informalexample>
+             <programlisting>
+@@ -2579,7 +2579,7 @@ struct _snd_pcm_runtime {
+ 	<para>
+ 	The field <structfield>transfer_ack_begin</structfield> and
+ 	<structfield>transfer_ack_end</structfield> are called at
+-	the beginning and the end of
++	the beginning and at the end of
+ 	<function>snd_pcm_period_elapsed()</function>, respectively. 
+ 	</para>
+ 	</section>
+@@ -2589,17 +2589,18 @@ struct _snd_pcm_runtime {
+     <section id="pcm-interface-operators">
+       <title>Operators</title>
+       <para>
+-        OK, now let me explain the detail of each pcm callback
++        OK, now let me give details about each pcm callback
+       (<parameter>ops</parameter>). In general, every callback must
+-      return 0 if successful, or a negative number with the error
+-      number such as <constant>-EINVAL</constant> at any
+-      error. 
++      return 0 if successful, or a negative error number
++      such as <constant>-EINVAL</constant>. To choose an appropriate
++      error number, it is advised to check what value other parts of
++      the kernel return when the same kind of request fails.
+       </para>
+ 
+       <para>
+         The callback function takes at least the argument with
+-        <structname>snd_pcm_substream</structname> pointer. For retrieving the
+-        chip record from the given substream instance, you can use the
++        <structname>snd_pcm_substream</structname> pointer. To retrieve
++        the chip record from the given substream instance, you can use the
+         following macro. 
+ 
+         <informalexample>
+@@ -2616,7 +2617,7 @@ struct _snd_pcm_runtime {
+ 	The macro reads <constant>substream-&gt;private_data</constant>,
+ 	which is a copy of <constant>pcm-&gt;private_data</constant>.
+ 	You can override the former if you need to assign different data
+-	records per PCM substream.  For example, cmi8330 driver assigns
++	records per PCM substream.  For example, the cmi8330 driver assigns
+ 	different private_data for playback and capture directions,
+ 	because it uses two different codecs (SB- and AD-compatible) for
+ 	different directions.
+@@ -2709,7 +2710,7 @@ struct _snd_pcm_runtime {
+       <section id="pcm-interface-operators-ioctl-callback">
+         <title>ioctl callback</title>
+         <para>
+-          This is used for any special action to pcm ioctls. But
++          This is used for any special call to pcm ioctls. But
+         usually you can pass a generic ioctl callback, 
+         <function>snd_pcm_lib_ioctl</function>.
+         </para>
+@@ -2726,9 +2727,6 @@ struct _snd_pcm_runtime {
+ ]]>
+             </programlisting>
+           </informalexample>
+-
+-          This and <structfield>hw_free</structfield> callbacks exist
+-        only on ALSA 0.9.x. 
+         </para>
+ 
+         <para>
+@@ -2740,13 +2738,13 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-          Many hardware set-up should be done in this callback,
++          Many hardware setups should be done in this callback,
+         including the allocation of buffers. 
+         </para>
+ 
+         <para>
+           Parameters to be initialized are retrieved by
+-          <function>params_xxx()</function> macros. For allocating a
++          <function>params_xxx()</function> macros. To allocate
+           buffer, you can call a helper function, 
+ 
+           <informalexample>
+@@ -2772,8 +2770,8 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-          Thus, you need to take care not to allocate the same buffers
+-        many times, which will lead to memory leak!  Calling the
++          Thus, you need to be careful not to allocate the same buffers
++        many times, which will lead to memory leaks!  Calling the
+         helper function above many times is OK. It will release the
+         previous buffer automatically when it was already allocated. 
+         </para>
+@@ -2782,7 +2780,7 @@ struct _snd_pcm_runtime {
+           Another note is that this callback is non-atomic
+         (schedulable). This is important, because the
+         <structfield>trigger</structfield> callback 
+-        is atomic (non-schedulable). That is, mutex or any
++        is atomic (non-schedulable). That is, mutexes or any
+         schedule-related functions are not available in
+         <structfield>trigger</structfield> callback.
+ 	Please see the subsection
+@@ -2843,15 +2841,15 @@ struct _snd_pcm_runtime {
+         <quote>prepared</quote>. You can set the format type, sample
+         rate, etc. here. The difference from
+         <structfield>hw_params</structfield> is that the 
+-        <structfield>prepare</structfield> callback will be called at each
++        <structfield>prepare</structfield> callback will be called each
+         time 
+         <function>snd_pcm_prepare()</function> is called, i.e. when
+-        recovered after underruns, etc. 
++        recovering after underruns, etc. 
+         </para>
+ 
+         <para>
+-	Note that this callback became non-atomic since the recent version.
+-	You can use schedule-related functions safely in this callback now.
++	Note that this callback is now non-atomic.
++	You can use schedule-related functions safely in this callback.
+         </para>
+ 
+         <para>
+@@ -2871,7 +2869,7 @@ struct _snd_pcm_runtime {
+ 
+         <para>
+           Be careful that this callback will be called many times at
+-        each set up, too. 
++        each setup, too. 
+         </para>
+       </section>
+ 
+@@ -2893,7 +2891,7 @@ struct _snd_pcm_runtime {
+           Which action is specified in the second argument,
+           <constant>SNDRV_PCM_TRIGGER_XXX</constant> in
+           <filename>&lt;sound/pcm.h&gt;</filename>. At least,
+-          <constant>START</constant> and <constant>STOP</constant>
++          the <constant>START</constant> and <constant>STOP</constant>
+           commands must be defined in this callback. 
+ 
+           <informalexample>
+@@ -2915,8 +2913,8 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-          When the pcm supports the pause operation (given in info
+-        field of the hardware table), <constant>PAUSE_PUSE</constant>
++          When the pcm supports the pause operation (given in the info
++        field of the hardware table), the <constant>PAUSE_PUSE</constant>
+         and <constant>PAUSE_RELEASE</constant> commands must be
+         handled here, too. The former is the command to pause the pcm,
+         and the latter to restart the pcm again. 
+@@ -2925,21 +2923,21 @@ struct _snd_pcm_runtime {
+         <para>
+           When the pcm supports the suspend/resume operation,
+ 	regardless of full or partial suspend/resume support,
+-        <constant>SUSPEND</constant> and <constant>RESUME</constant>
++        the <constant>SUSPEND</constant> and <constant>RESUME</constant>
+         commands must be handled, too.
+         These commands are issued when the power-management status is
+         changed.  Obviously, the <constant>SUSPEND</constant> and
+-        <constant>RESUME</constant>
+-        do suspend and resume of the pcm substream, and usually, they
+-        are identical with <constant>STOP</constant> and
++        <constant>RESUME</constant> commands
++        suspend and resume the pcm substream, and usually, they
++        are identical to the <constant>STOP</constant> and
+         <constant>START</constant> commands, respectively.
+-	  See <link linkend="power-management"><citetitle>
++	  See the <link linkend="power-management"><citetitle>
+ 	Power Management</citetitle></link> section for details.
+         </para>
+ 
+         <para>
+           As mentioned, this callback is atomic.  You cannot call
+-	  the function going to sleep.
++	  functions which may sleep.
+ 	  The trigger callback should be as minimal as possible,
+ 	  just really triggering the DMA.  The other stuff should be
+ 	  initialized hw_params and prepare callbacks properly
+@@ -2960,8 +2958,8 @@ struct _snd_pcm_runtime {
+ 
+           This callback is called when the PCM middle layer inquires
+         the current hardware position on the buffer. The position must
+-        be returned in frames (which was in bytes on ALSA 0.5.x),
+-        ranged from 0 to buffer_size - 1.
++        be returned in frames,
++        ranging from 0 to buffer_size - 1.
+         </para>
+ 
+         <para>
+@@ -2983,7 +2981,7 @@ struct _snd_pcm_runtime {
+         <para>
+           These callbacks are not mandatory, and can be omitted in
+         most cases. These callbacks are used when the hardware buffer
+-        cannot be on the normal memory space. Some chips have their
++        cannot be in the normal memory space. Some chips have their
+         own buffer on the hardware which is not mappable. In such a
+         case, you have to transfer the data manually from the memory
+         buffer to the hardware buffer. Or, if the buffer is
+@@ -3018,8 +3016,8 @@ struct _snd_pcm_runtime {
+         <title>page callback</title>
+ 
+         <para>
+-          This callback is also not mandatory. This callback is used
+-        mainly for the non-contiguous buffer. The mmap calls this
++          This callback is optional too. This callback is used
++        mainly for non-contiguous buffers. The mmap calls this
+         callback to get the page address. Some examples will be
+         explained in the later section <link
+         linkend="buffer-and-memory"><citetitle>Buffer and Memory
+@@ -3035,7 +3033,7 @@ struct _snd_pcm_runtime {
+       role of PCM interrupt handler in the sound driver is to update
+       the buffer position and to tell the PCM middle layer when the
+       buffer position goes across the prescribed period size. To
+-      inform this, call <function>snd_pcm_period_elapsed()</function>
++      inform this, call the <function>snd_pcm_period_elapsed()</function>
+       function. 
+       </para>
+ 
+@@ -3072,7 +3070,7 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-          A typical coding would be like:
++          Typical code would be like:
+ 
+           <example>
+ 	    <title>Interrupt Handler Case #1</title>
+@@ -3101,21 +3099,21 @@ struct _snd_pcm_runtime {
+       </section>
+ 
+       <section id="pcm-interface-interrupt-handler-timer">
+-        <title>High-frequent timer interrupts</title>
++        <title>High frequency timer interrupts</title>
+         <para>
+-	This is the case when the hardware doesn't generate interrupts
+-        at the period boundary but do timer-interrupts at the fixed
++	This happense when the hardware doesn't generate interrupts
++        at the period boundary but issues timer interrupts at a fixed
+         timer rate (e.g. es1968 or ymfpci drivers). 
+         In this case, you need to check the current hardware
+-        position and accumulates the processed sample length at each
+-        interrupt.  When the accumulated size overcomes the period
++        position and accumulate the processed sample length at each
++        interrupt.  When the accumulated size exceeds the period
+         size, call 
+         <function>snd_pcm_period_elapsed()</function> and reset the
+         accumulator. 
+         </para>
+ 
+         <para>
+-          A typical coding would be like the following.
++          Typical code would be like the following.
+ 
+           <example>
+ 	    <title>Interrupt Handler Case #2</title>
+@@ -3178,32 +3176,33 @@ struct _snd_pcm_runtime {
+     <section id="pcm-interface-atomicity">
+       <title>Atomicity</title>
+       <para>
+-      One of the most important (and thus difficult to debug) problem
+-      on the kernel programming is the race condition.
+-      On linux kernel, usually it's solved via spin-locks or
+-      semaphores.  In general, if the race condition may
+-      happen in the interrupt handler, it's handled as atomic, and you
+-      have to use spinlock for protecting the critical session.  If it
+-      never happens in the interrupt and it may take relatively long
+-      time, you should use semaphore.
++      One of the most important (and thus difficult to debug) problems
++      in kernel programming are race conditions.
++      In the Linux kernel, they are usually avoided via spin-locks, mutexes
++      or semaphores.  In general, if a race condition can happen
++      in an interrupt handler, it has to be managed atomically, and you
++      have to use a spinlock to protect the critical session. If the
++      critical section is not in interrupt handler code and
++      if taking a relatively long time to execute is acceptable, you
++      should use mutexes or semaphores instead.
+       </para>
+ 
+       <para>
+       As already seen, some pcm callbacks are atomic and some are
+-      not.  For example, <parameter>hw_params</parameter> callback is
++      not.  For example, the <parameter>hw_params</parameter> callback is
+       non-atomic, while <parameter>trigger</parameter> callback is
+       atomic.  This means, the latter is called already in a spinlock
+       held by the PCM middle layer. Please take this atomicity into
+-      account when you use a spinlock or a semaphore in the callbacks.
++      account when you choose a locking scheme in the callbacks.
+       </para>
+ 
+       <para>
+       In the atomic callbacks, you cannot use functions which may call
+       <function>schedule</function> or go to
+-      <function>sleep</function>.  The semaphore and mutex do sleep,
++      <function>sleep</function>.  Semaphores and mutexes can sleep,
+       and hence they cannot be used inside the atomic callbacks
+       (e.g. <parameter>trigger</parameter> callback).
+-      For taking a certain delay in such a callback, please use
++      To implement some delay in such a callback, please use
+       <function>udelay()</function> or <function>mdelay()</function>.
+       </para>
+ 
+@@ -3257,7 +3256,7 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         There are many different constraints.
+-        Look in <filename>sound/pcm.h</filename> for a complete list.
++        Look at <filename>sound/pcm.h</filename> for a complete list.
+         You can even define your own constraint rules.
+         For example, let's suppose my_chip can manage a substream of 1 channel
+         if and only if the format is S16_LE, otherwise it supports any format
+@@ -3346,7 +3345,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        I won't explain more details here, rather I
++        I won't give more details here, rather I
+         would like to say, <quote>Luke, use the source.</quote>
+       </para>
+     </section>
+@@ -3364,10 +3363,9 @@ struct _snd_pcm_runtime {
+       <title>General</title>
+       <para>
+         The control interface is used widely for many switches,
+-      sliders, etc. which are accessed from the user-space. Its most
+-      important use is the mixer interface. In other words, on ALSA
+-      0.9.x, all the mixer stuff is implemented on the control kernel
+-      API (while there was an independent mixer kernel API on 0.5.x). 
++      sliders, etc. which are accessed from user-space. Its most
++      important use is the mixer interface. In other words, since ALSA
++      0.9.x, all the mixer stuff is implemented on the control kernel API.
+       </para>
+ 
+       <para>
+@@ -3379,14 +3377,15 @@ struct _snd_pcm_runtime {
+       <para>
+         The control API is defined in
+       <filename>&lt;sound/control.h&gt;</filename>.
+-      Include this file if you add your own controls.
++      Include this file if you want to add your own controls.
+       </para>
+     </section>
+ 
+     <section id="control-interface-definition">
+       <title>Definition of Controls</title>
+       <para>
+-        For creating a new control, you need to define the three
++        To create a new control, you need to define the
++	following three
+       callbacks: <structfield>info</structfield>,
+       <structfield>get</structfield> and
+       <structfield>put</structfield>. Then, define a
+@@ -3414,13 +3413,13 @@ struct _snd_pcm_runtime {
+       <para>
+         Most likely the control is created via
+       <function>snd_ctl_new1()</function>, and in such a case, you can
+-      add <parameter>__devinitdata</parameter> prefix to the
+-      definition like above. 
++      add the <parameter>__devinitdata</parameter> prefix to the
++      definition as above. 
+       </para>
+ 
+       <para>
+-        The <structfield>iface</structfield> field specifies the type of
+-      the control, <constant>SNDRV_CTL_ELEM_IFACE_XXX</constant>, which
++        The <structfield>iface</structfield> field specifies the control
++      type, <constant>SNDRV_CTL_ELEM_IFACE_XXX</constant>, which
+       is usually <constant>MIXER</constant>.
+       Use <constant>CARD</constant> for global controls that are not
+       logically part of the mixer.
+@@ -3435,12 +3434,11 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         The <structfield>name</structfield> is the name identifier
+-      string. On ALSA 0.9.x, the control name is very important,
++      string. Since ALSA 0.9.x, the control name is very important,
+       because its role is classified from its name. There are
+       pre-defined standard control names. The details are described in
+-      the subsection
+-      <link linkend="control-interface-control-names"><citetitle>
+-      Control Names</citetitle></link>.
++      the <link linkend="control-interface-control-names"><citetitle>
++      Control Names</citetitle></link> subsection.
+       </para>
+ 
+       <para>
+@@ -3456,15 +3454,15 @@ struct _snd_pcm_runtime {
+         The <structfield>access</structfield> field contains the access
+       type of this control. Give the combination of bit masks,
+       <constant>SNDRV_CTL_ELEM_ACCESS_XXX</constant>, there.
+-      The detailed will be explained in the subsection
+-      <link linkend="control-interface-access-flags"><citetitle>
+-      Access Flags</citetitle></link>.
++      The details will be explained in
++      the <link linkend="control-interface-access-flags"><citetitle>
++      Access Flags</citetitle></link> subsection.
+       </para>
+ 
+       <para>
+         The <structfield>private_value</structfield> field contains
+       an arbitrary long integer value for this record. When using
+-      generic <structfield>info</structfield>,
++      the generic <structfield>info</structfield>,
+       <structfield>get</structfield> and
+       <structfield>put</structfield> callbacks, you can pass a value 
+       through this field. If several small numbers are necessary, you can
+@@ -3489,7 +3487,7 @@ struct _snd_pcm_runtime {
+     <section id="control-interface-control-names">
+       <title>Control Names</title>
+       <para>
+-        There are some standards for defining the control names. A
++        There are some standards to define the control names. A
+       control is usually defined from the three parts as
+       <quote>SOURCE DIRECTION FUNCTION</quote>. 
+       </para>
+@@ -3497,7 +3495,7 @@ struct _snd_pcm_runtime {
+       <para>
+         The first, <constant>SOURCE</constant>, specifies the source
+       of the control, and is a string such as <quote>Master</quote>,
+-      <quote>PCM</quote>, <quote>CD</quote> or
++      <quote>PCM</quote>, <quote>CD</quote> and
+       <quote>Line</quote>. There are many pre-defined sources. 
+       </para>
+ 
+@@ -3575,22 +3573,22 @@ struct _snd_pcm_runtime {
+       <title>Access Flags</title>
+ 
+       <para>
+-      The access flag is the bit-flags which specifies the access type
++      The access flag is the bitmask which specifies the access type
+       of the given control.  The default access type is
+       <constant>SNDRV_CTL_ELEM_ACCESS_READWRITE</constant>, 
+       which means both read and write are allowed to this control.
+       When the access flag is omitted (i.e. = 0), it is
+-      regarded as <constant>READWRITE</constant> access as default. 
++      considered as <constant>READWRITE</constant> access as default. 
+       </para>
+ 
+       <para>
+       When the control is read-only, pass
+       <constant>SNDRV_CTL_ELEM_ACCESS_READ</constant> instead.
+       In this case, you don't have to define
+-      <structfield>put</structfield> callback.
++      the <structfield>put</structfield> callback.
+       Similarly, when the control is write-only (although it's a rare
+-      case), you can use <constant>WRITE</constant> flag instead, and
+-      you don't need <structfield>get</structfield> callback.
++      case), you can use the <constant>WRITE</constant> flag instead, and
++      you don't need the <structfield>get</structfield> callback.
+       </para>
+ 
+       <para>
+@@ -3598,15 +3596,15 @@ struct _snd_pcm_runtime {
+       <constant>VOLATILE</constant> flag should be given.  This means
+       that the control may be changed without
+       <link linkend="control-interface-change-notification"><citetitle>
+-      notification</citetitle></link>.  Applications should poll such
++      notification</citetitle></link>. Applications should poll such
+       a control constantly.
+       </para>
+ 
+       <para>
+       When the control is inactive, set
+-      <constant>INACTIVE</constant> flag, too.
++      the <constant>INACTIVE</constant> flag, too.
+       There are <constant>LOCK</constant> and
+-      <constant>OWNER</constant> flags for changing the write
++      <constant>OWNER</constant> flags to change the write
+       permissions.
+       </para>
+ 
+@@ -3619,10 +3617,10 @@ struct _snd_pcm_runtime {
+         <title>info callback</title>
+         <para>
+           The <structfield>info</structfield> callback is used to get
+-        the detailed information of this control. This must store the
++        detailed information on this control. This must store the
+         values of the given struct <structname>snd_ctl_elem_info</structname>
+         object. For example, for a boolean control with a single
+-        element will be: 
++        element: 
+ 
+           <example>
+ 	    <title>Example of info callback</title>
+@@ -3653,7 +3651,7 @@ struct _snd_pcm_runtime {
+         volume would have count = 2. The
+         <structfield>value</structfield> field is a union, and 
+         the values stored are depending on the type. The boolean and
+-        integer are identical. 
++        integer types are identical. 
+         </para>
+ 
+         <para>
+@@ -3684,7 +3682,7 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-	  Some common info callbacks are prepared for easy use:
++	  Some common info callbacks are available for your convenience:
+ 	<function>snd_ctl_boolean_mono_info()</function> and
+ 	<function>snd_ctl_boolean_stereo_info()</function>.
+ 	Obviously, the former is an info callback for a mono channel
+@@ -3699,7 +3697,7 @@ struct _snd_pcm_runtime {
+ 
+         <para>
+           This callback is used to read the current value of the
+-        control and to return to the user-space. 
++        control and to return to user-space. 
+         </para>
+ 
+         <para>
+@@ -3722,11 +3720,11 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-	The <structfield>value</structfield> field is depending on
+-        the type of control as well as on info callback.  For example,
++	The <structfield>value</structfield> field depends on 
++        the type of control as well as on the info callback.  For example,
+ 	the sb driver uses this field to store the register offset,
+         the bit-shift and the bit-mask.  The
+-        <structfield>private_value</structfield> is set like
++        <structfield>private_value</structfield> field is set as follows:
+           <informalexample>
+             <programlisting>
+ <![CDATA[
+@@ -3752,7 +3750,8 @@ struct _snd_pcm_runtime {
+ 	</para>
+ 
+ 	<para>
+-	In <structfield>get</structfield> callback, you have to fill all the elements if the
++	In the <structfield>get</structfield> callback,
++	you have to fill all the elements if the
+         control has more than one elements,
+         i.e. <structfield>count</structfield> &gt; 1.
+ 	In the example above, we filled only one element
+@@ -3765,7 +3764,7 @@ struct _snd_pcm_runtime {
+         <title>put callback</title>
+ 
+         <para>
+-          This callback is used to write a value from the user-space.
++          This callback is used to write a value from user-space.
+         </para>
+ 
+         <para>
+@@ -3799,7 +3798,7 @@ struct _snd_pcm_runtime {
+         </para>
+ 
+         <para>
+-	Like <structfield>get</structfield> callback,
++	As in the <structfield>get</structfield> callback,
+ 	when the control has more than one elements,
+ 	all elements must be evaluated in this callback, too.
+         </para>
+@@ -3817,7 +3816,7 @@ struct _snd_pcm_runtime {
+       <title>Constructor</title>
+       <para>
+         When everything is ready, finally we can create a new
+-      control. For creating a control, there are two functions to be
++      control. To create a control, there are two functions to be
+       called, <function>snd_ctl_new1()</function> and
+       <function>snd_ctl_add()</function>. 
+       </para>
+@@ -3839,14 +3838,14 @@ struct _snd_pcm_runtime {
+       struct <structname>snd_kcontrol_new</structname> object defined above, and chip
+       is the object pointer to be passed to
+       kcontrol-&gt;private_data 
+-      which can be referred in callbacks. 
++      which can be referred to in callbacks. 
+       </para>
+ 
+       <para>
+         <function>snd_ctl_new1()</function> allocates a new
+       <structname>snd_kcontrol</structname> instance (that's why the definition
+       of <parameter>my_control</parameter> can be with
+-      <parameter>__devinitdata</parameter> 
++      the <parameter>__devinitdata</parameter> 
+       prefix), and <function>snd_ctl_add</function> assigns the given
+       control component to the card. 
+       </para>
+@@ -3941,7 +3940,7 @@ struct _snd_pcm_runtime {
+       <title>General</title>
+       <para>
+         The ALSA AC97 codec layer is a well-defined one, and you don't
+-      have to write many codes to control it. Only low-level control
++      have to write much code to control it. Only low-level control
+       routines are necessary. The AC97 codec API is defined in
+       <filename>&lt;sound/ac97_codec.h&gt;</filename>. 
+       </para>
+@@ -4004,7 +4003,7 @@ struct _snd_pcm_runtime {
+     <section id="api-ac97-constructor">
+       <title>Constructor</title>
+       <para>
+-        For creating an ac97 instance, first call <function>snd_ac97_bus</function>
++        To create an ac97 instance, first call <function>snd_ac97_bus</function>
+       with an <type>ac97_bus_ops_t</type> record with callback functions.
+ 
+         <informalexample>
+@@ -4042,12 +4041,12 @@ struct _snd_pcm_runtime {
+           </programlisting>
+         </informalexample>
+ 
+-        where chip-&gt;ac97 is the pointer of a newly created
++        where chip-&gt;ac97 is a pointer to a newly created
+         <type>ac97_t</type> instance.
+         In this case, the chip pointer is set as the private data, so that
+         the read/write callback functions can refer to this chip instance.
+         This instance is not necessarily stored in the chip
+-	record.  When you need to change the register values from the
++	record.  If you need to change the register values from the
+         driver, or need the suspend/resume of ac97 codecs, keep this
+         pointer to pass to the corresponding functions.
+       </para>
+@@ -4098,7 +4097,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-      These callbacks are non-atomic like the callbacks of control API.
++      These callbacks are non-atomic like the control API callbacks.
+       </para>
+ 
+       <para>
+@@ -4110,14 +4109,14 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         The <structfield>reset</structfield> callback is used to reset
+-      the codec. If the chip requires a special way of reset, you can
++      the codec. If the chip requires a special kind of reset, you can
+       define this callback. 
+       </para>
+ 
+       <para>
+-        The <structfield>wait</structfield> callback is used for a
+-      certain wait at the standard initialization of the codec. If the
+-      chip requires the extra wait-time, define this callback. 
++        The <structfield>wait</structfield> callback is used to
++      add some waiting time in the standard initialization of the codec. If the
++      chip requires the extra waiting time, define this callback. 
+       </para>
+ 
+       <para>
+@@ -4172,7 +4171,7 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         <function>snd_ac97_update_bits()</function> is used to update
+-        some bits of the given register.  
++        some bits in the given register.  
+ 
+         <informalexample>
+           <programlisting>
+@@ -4185,7 +4184,7 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         Also, there is a function to change the sample rate (of a
+-        certain register such as
++        given register such as
+         <constant>AC97_PCM_FRONT_DAC_RATE</constant>) when VRA or
+         DRA is supported by the codec:
+         <function>snd_ac97_set_rate()</function>. 
+@@ -4200,11 +4199,11 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        The following registers are available for setting the rate:
++        The following registers are available to set the rate:
+       <constant>AC97_PCM_MIC_ADC_RATE</constant>,
+       <constant>AC97_PCM_FRONT_DAC_RATE</constant>,
+       <constant>AC97_PCM_LR_ADC_RATE</constant>,
+-      <constant>AC97_SPDIF</constant>. When the
++      <constant>AC97_SPDIF</constant>. When
+       <constant>AC97_SPDIF</constant> is specified, the register is
+       not really changed but the corresponding IEC958 status bits will
+       be updated. 
+@@ -4214,12 +4213,11 @@ struct _snd_pcm_runtime {
+     <section id="api-ac97-clock-adjustment">
+       <title>Clock Adjustment</title>
+       <para>
+-        On some chip, the clock of the codec isn't 48000 but using a
++        In some chips, the clock of the codec isn't 48000 but using a
+       PCI clock (to save a quartz!). In this case, change the field
+       bus-&gt;clock to the corresponding
+       value. For example, intel8x0 
+-      and es1968 drivers have the auto-measurement function of the
+-      clock. 
++      and es1968 drivers have their own function to read from the clock.
+       </para>
+     </section>
+ 
+@@ -4239,15 +4237,13 @@ struct _snd_pcm_runtime {
+         When there are several codecs on the same card, you need to
+       call <function>snd_ac97_mixer()</function> multiple times with
+       ac97.num=1 or greater. The <structfield>num</structfield> field
+-      specifies the codec 
+-      number. 
++      specifies the codec number. 
+       </para>
+ 
+       <para>
+-        If you have set up multiple codecs, you need to either write
++        If you set up multiple codecs, you either need to write
+       different callbacks for each codec or check
+-      ac97-&gt;num in the 
+-      callback routines. 
++      ac97-&gt;num in the callback routines. 
+       </para>
+     </section>
+ 
+@@ -4271,7 +4267,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        Some soundchips have similar but a little bit different
++        Some soundchips have a similar but slightly different
+       implementation of mpu401 stuff. For example, emu10k1 has its own
+       mpu401 routines. 
+       </para>
+@@ -4280,7 +4276,7 @@ struct _snd_pcm_runtime {
+     <section id="midi-interface-constructor">
+       <title>Constructor</title>
+       <para>
+-        For creating a rawmidi object, call
++        To create a rawmidi object, call
+       <function>snd_mpu401_uart_new()</function>. 
+ 
+         <informalexample>
+@@ -4307,25 +4303,24 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        The 4th argument is the i/o port address. Many
+-      backward-compatible MPU401 has an i/o port such as 0x330. Or, it
+-      might be a part of its own PCI i/o region. It depends on the
++        The 4th argument is the I/O port address. Many
++      backward-compatible MPU401 have an I/O port such as 0x330. Or, it
++      might be a part of its own PCI I/O region. It depends on the
+       chip design. 
+       </para>
+ 
+       <para>
+-	The 5th argument is bitflags for additional information.
+-        When the i/o port address above is a part of the PCI i/o
+-      region, the MPU401 i/o port might have been already allocated
++	The 5th argument is a bitflag for additional information.
++        When the I/O port address above is part of the PCI I/O
++      region, the MPU401 I/O port might have been already allocated
+       (reserved) by the driver itself. In such a case, pass a bit flag
+       <constant>MPU401_INFO_INTEGRATED</constant>,
+-      and 
+-      the mpu401-uart layer will allocate the i/o ports by itself. 
++      and the mpu401-uart layer will allocate the I/O ports by itself. 
+       </para>
+ 
+ 	<para>
+ 	When the controller supports only the input or output MIDI stream,
+-	pass <constant>MPU401_INFO_INPUT</constant> or
++	pass the <constant>MPU401_INFO_INPUT</constant> or
+ 	<constant>MPU401_INFO_OUTPUT</constant> bitflag, respectively.
+ 	Then the rawmidi instance is created as a single stream.
+ 	</para>
+@@ -4333,7 +4328,7 @@ struct _snd_pcm_runtime {
+ 	<para>
+ 	<constant>MPU401_INFO_MMIO</constant> bitflag is used to change
+ 	the access method to MMIO (via readb and writeb) instead of
+-	iob and outb.  In this case, you have to pass the iomapped address
++	iob and outb. In this case, you have to pass the iomapped address
+ 	to <function>snd_mpu401_uart_new()</function>.
+ 	</para>
+ 
+@@ -4341,7 +4336,7 @@ struct _snd_pcm_runtime {
+ 	When <constant>MPU401_INFO_TX_IRQ</constant> is set, the output
+ 	stream isn't checked in the default interrupt handler.  The driver
+ 	needs to call <function>snd_mpu401_uart_interrupt_tx()</function>
+-	by itself to start processing the output stream in irq handler.
++	by itself to start processing the output stream in the irq handler.
+ 	</para>
+ 
+       <para>
+@@ -4381,7 +4376,7 @@ struct _snd_pcm_runtime {
+       (<parameter>irq_flags</parameter>). Otherwise, pass the flags
+       for irq allocation 
+       (<constant>SA_XXX</constant> bits) to it, and the irq will be
+-      reserved by the mpu401-uart layer. If the card doesn't generates
++      reserved by the mpu401-uart layer. If the card doesn't generate
+       UART interrupts, pass -1 as the irq number. Then a timer
+       interrupt will be invoked for polling. 
+       </para>
+@@ -4392,8 +4387,8 @@ struct _snd_pcm_runtime {
+       <para>
+         When the interrupt is allocated in
+       <function>snd_mpu401_uart_new()</function>, the private
+-      interrupt handler is used, hence you don't have to do nothing
+-      else than creating the mpu401 stuff. Otherwise, you have to call
++      interrupt handler is used, hence you don't have anything else to do
++      than creating the mpu401 stuff. Otherwise, you have to call
+       <function>snd_mpu401_uart_interrupt()</function> explicitly when
+       a UART interrupt is invoked and checked in your own interrupt
+       handler.  
+@@ -4480,8 +4475,8 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+       The fourth and fifth arguments are the number of output and
+-      input substreams, respectively, of this device.  (A substream is
+-      the equivalent of a MIDI port.)
++      input substreams, respectively, of this device (a substream is
++      the equivalent of a MIDI port).
+       </para>
+ 
+       <para>
+@@ -4498,7 +4493,7 @@ struct _snd_pcm_runtime {
+       <para>
+       After the rawmidi device is created, you need to set the
+       operators (callbacks) for each substream.  There are helper
+-      functions to set the operators for all substream of a device:
++      functions to set the operators for all the substreams of a device:
+         <informalexample>
+           <programlisting>
+ <![CDATA[
+@@ -4528,8 +4523,8 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-      If there is more than one substream, you should give each one a
+-      unique name:
++      If there are more than one substream, you should give a
++      unique name to each of them:
+         <informalexample>
+           <programlisting>
+ <![CDATA[
+@@ -4550,7 +4545,7 @@ struct _snd_pcm_runtime {
+       <title>Callbacks</title>
+ 
+       <para>
+-      In all callbacks, the private data that you've set for the
++      In all the callbacks, the private data that you've set for the
+       rawmidi device can be accessed as
+       substream-&gt;rmidi-&gt;private_data.
+       <!-- <code> isn't available before DocBook 4.3 -->
+@@ -4583,8 +4578,8 @@ struct _snd_pcm_runtime {
+ 
+         <para>
+         This is called when a substream is opened.
+-        You can initialize the hardware here, but you should not yet
+-        start transmitting/receiving data.
++        You can initialize the hardware here, but you shouldn't
++        start transmitting/receiving data yet.
+         </para>
+       </section>
+ 
+@@ -4632,9 +4627,9 @@ struct _snd_pcm_runtime {
+         To read data from the buffer, call
+         <function>snd_rawmidi_transmit_peek</function>.  It will
+         return the number of bytes that have been read; this will be
+-        less than the number of bytes requested when there is no more
++        less than the number of bytes requested when there are no more
+         data in the buffer.
+-        After the data has been transmitted successfully, call
++        After the data have been transmitted successfully, call
+         <function>snd_rawmidi_transmit_ack</function> to remove the
+         data from the substream buffer:
+           <informalexample>
+@@ -4655,7 +4650,7 @@ struct _snd_pcm_runtime {
+         <para>
+         If you know beforehand that the hardware will accept data, you
+         can use the <function>snd_rawmidi_transmit</function> function
+-        which reads some data and removes it from the buffer at once:
++        which reads some data and removes them from the buffer at once:
+           <informalexample>
+             <programlisting>
+ <![CDATA[
+@@ -4749,13 +4744,13 @@ struct _snd_pcm_runtime {
+ 
+         <para>
+         This is only used with output substreams.  This function should wait
+-        until all data read from the substream buffer has been transmitted.
++        until all data read from the substream buffer have been transmitted.
+         This ensures that the device can be closed and the driver unloaded
+         without losing data.
+         </para>
+ 
+         <para>
+-        This callback is optional.  If you do not set
++        This callback is optional. If you do not set
+         <structfield>drain</structfield> in the struct snd_rawmidi_ops
+         structure, ALSA will simply wait for 50&nbsp;milliseconds
+         instead.
+@@ -4775,24 +4770,24 @@ struct _snd_pcm_runtime {
+     <section id="misc-devices-opl3">
+       <title>FM OPL3</title>
+       <para>
+-        The FM OPL3 is still used on many chips (mainly for backward
++        The FM OPL3 is still used in many chips (mainly for backward
+       compatibility). ALSA has a nice OPL3 FM control layer, too. The
+       OPL3 API is defined in
+       <filename>&lt;sound/opl3.h&gt;</filename>. 
+       </para>
+ 
+       <para>
+-        FM registers can be directly accessed through direct-FM API,
++        FM registers can be directly accessed through the direct-FM API,
+       defined in <filename>&lt;sound/asound_fm.h&gt;</filename>. In
+       ALSA native mode, FM registers are accessed through
+-      Hardware-Dependant Device direct-FM extension API, whereas in
+-      OSS compatible mode, FM registers can be accessed with OSS
+-      direct-FM compatible API on <filename>/dev/dmfmX</filename> device. 
++      the Hardware-Dependant Device direct-FM extension API, whereas in
++      OSS compatible mode, FM registers can be accessed with the OSS
++      direct-FM compatible API in <filename>/dev/dmfmX</filename> device. 
+       </para>
+ 
+       <para>
+-        For creating the OPL3 component, you have two functions to
+-        call. The first one is a constructor for <type>opl3_t</type>
++        To create the OPL3 component, you have two functions to
++        call. The first one is a constructor for the <type>opl3_t</type>
+         instance. 
+ 
+         <informalexample>
+@@ -4819,12 +4814,12 @@ struct _snd_pcm_runtime {
+       <para>
+         When the left and right ports have been already allocated by
+       the card driver, pass non-zero to the fifth argument
+-      (<parameter>integrated</parameter>). Otherwise, opl3 module will
++      (<parameter>integrated</parameter>). Otherwise, the opl3 module will
+       allocate the specified ports by itself. 
+       </para>
+ 
+       <para>
+-        When the accessing to the hardware requires special method
++        When the accessing the hardware requires special method
+         instead of the standard I/O access, you can create opl3 instance
+         separately with <function>snd_opl3_new()</function>.
+ 
+@@ -4845,13 +4840,13 @@ struct _snd_pcm_runtime {
+ 	access function, the private data and the destructor.
+ 	The l_port and r_port are not necessarily set.  Only the
+ 	command must be set properly.  You can retrieve the data
+-	from opl3-&gt;private_data field.
++	from the opl3-&gt;private_data field.
+       </para>
+ 
+       <para>
+ 	After creating the opl3 instance via <function>snd_opl3_new()</function>,
+ 	call <function>snd_opl3_init()</function> to initialize the chip to the
+-	proper state.  Note that <function>snd_opl3_create()</function> always
++	proper state. Note that <function>snd_opl3_create()</function> always
+ 	calls it internally.
+       </para>
+ 
+@@ -4884,7 +4879,7 @@ struct _snd_pcm_runtime {
+     <section id="misc-devices-hardware-dependent">
+       <title>Hardware-Dependent Devices</title>
+       <para>
+-        Some chips need the access from the user-space for special
++        Some chips need user-space access for special
+       controls or for loading the micro code. In such a case, you can
+       create a hwdep (hardware-dependent) device. The hwdep API is
+       defined in <filename>&lt;sound/hwdep.h&gt;</filename>. You can
+@@ -4893,7 +4888,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        Creation of the <type>hwdep</type> instance is done via
++        The creation of the <type>hwdep</type> instance is done via
+         <function>snd_hwdep_new()</function>. 
+ 
+         <informalexample>
+@@ -4912,8 +4907,8 @@ struct _snd_pcm_runtime {
+         You can then pass any pointer value to the
+         <parameter>private_data</parameter>.
+         If you assign a private data, you should define the
+-        destructor, too. The destructor function is set to
+-        <structfield>private_free</structfield> field.  
++        destructor, too. The destructor function is set in
++        the <structfield>private_free</structfield> field.  
+ 
+         <informalexample>
+           <programlisting>
+@@ -4925,7 +4920,7 @@ struct _snd_pcm_runtime {
+           </programlisting>
+         </informalexample>
+ 
+-        and the implementation of destructor would be:
++        and the implementation of the destructor would be:
+ 
+         <informalexample>
+           <programlisting>
+@@ -4943,7 +4938,7 @@ struct _snd_pcm_runtime {
+       <para>
+         The arbitrary file operations can be defined for this
+         instance. The file operators are defined in
+-        <parameter>ops</parameter> table. For example, assume that
++        the <parameter>ops</parameter> table. For example, assume that
+         this chip needs an ioctl. 
+ 
+         <informalexample>
+@@ -4964,7 +4959,7 @@ struct _snd_pcm_runtime {
+       <title>IEC958 (S/PDIF)</title>
+       <para>
+         Usually the controls for IEC958 devices are implemented via
+-      control interface. There is a macro to compose a name string for
++      the control interface. There is a macro to compose a name string for
+       IEC958 controls, <function>SNDRV_CTL_NAME_IEC958()</function>
+       defined in <filename>&lt;include/asound.h&gt;</filename>.  
+       </para>
+@@ -4973,7 +4968,7 @@ struct _snd_pcm_runtime {
+         There are some standard controls for IEC958 status bits. These
+       controls use the type <type>SNDRV_CTL_ELEM_TYPE_IEC958</type>,
+       and the size of element is fixed as 4 bytes array
+-      (value.iec958.status[x]). For <structfield>info</structfield>
++      (value.iec958.status[x]). For the <structfield>info</structfield>
+       callback, you don't specify 
+       the value field for this type (the count field must be set,
+       though). 
+@@ -5001,7 +4996,7 @@ struct _snd_pcm_runtime {
+       enable/disable or to set the raw bit mode. The implementation
+       will depend on the chip, but the control should be named as
+       <quote>IEC958 xxx</quote>, preferably using
+-      <function>SNDRV_CTL_NAME_IEC958()</function> macro. 
++      the <function>SNDRV_CTL_NAME_IEC958()</function> macro. 
+       </para>
+ 
+       <para>
+@@ -5036,12 +5031,12 @@ struct _snd_pcm_runtime {
+         The allocation of pages with fallback is
+       <function>snd_malloc_xxx_pages_fallback()</function>. This
+       function tries to allocate the specified pages but if the pages
+-      are not available, it tries to reduce the page sizes until the
++      are not available, it tries to reduce the page sizes until
+       enough space is found.
+       </para>
+ 
+       <para>
+-      For releasing the space, call
++      The release the pages, call
+       <function>snd_free_xxx_pages()</function> function. 
+       </para>
+ 
+@@ -5050,8 +5045,8 @@ struct _snd_pcm_runtime {
+        a large contiguous physical space
+        at the time the module is loaded for the later use.
+        This is called <quote>pre-allocation</quote>.
+-       As already written, you can call the following function at the
+-       construction of pcm instance (in the case of PCI bus). 
++       As already written, you can call the following function at 
++       pcm instance construction time (in the case of PCI bus). 
+ 
+         <informalexample>
+           <programlisting>
+@@ -5063,34 +5058,34 @@ struct _snd_pcm_runtime {
+         </informalexample>
+ 
+         where <parameter>size</parameter> is the byte size to be
+-      pre-allocated and the <parameter>max</parameter> is the maximal
+-      size to be changed via <filename>prealloc</filename> proc file.
+-      The allocator will try to get as large area as possible
++      pre-allocated and the <parameter>max</parameter> is the maximum
++      size to be changed via the <filename>prealloc</filename> proc file.
++      The allocator will try to get an area as large as possible
+       within the given size. 
+       </para>
+ 
+       <para>
+       The second argument (type) and the third argument (device pointer)
+       are dependent on the bus.
+-      In the case of ISA bus, pass <function>snd_dma_isa_data()</function>
++      In the case of the ISA bus, pass <function>snd_dma_isa_data()</function>
+       as the third argument with <constant>SNDRV_DMA_TYPE_DEV</constant> type.
+       For the continuous buffer unrelated to the bus can be pre-allocated
+       with <constant>SNDRV_DMA_TYPE_CONTINUOUS</constant> type and the
+       <function>snd_dma_continuous_data(GFP_KERNEL)</function> device pointer,
+-      whereh <constant>GFP_KERNEL</constant> is the kernel allocation flag to
++      where <constant>GFP_KERNEL</constant> is the kernel allocation flag to
+       use.  For the SBUS, <constant>SNDRV_DMA_TYPE_SBUS</constant> and
+       <function>snd_dma_sbus_data(sbus_dev)</function> are used instead.
+       For the PCI scatter-gather buffers, use
+       <constant>SNDRV_DMA_TYPE_DEV_SG</constant> with
+       <function>snd_dma_pci_data(pci)</function>
+-      (see the section
++      (see the 
+           <link linkend="buffer-and-memory-non-contiguous"><citetitle>Non-Contiguous Buffers
+-          </citetitle></link>).
++          </citetitle></link> section).
+       </para>
+ 
+       <para>
+-        Once when the buffer is pre-allocated, you can use the
+-        allocator in the <structfield>hw_params</structfield> callback 
++        Once the buffer is pre-allocated, you can use the
++        allocator in the <structfield>hw_params</structfield> callback: 
+ 
+         <informalexample>
+           <programlisting>
+@@ -5116,8 +5111,8 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        The first case works fine if the external hardware buffer is enough
+-      large.  This method doesn't need any extra buffers and thus is
++        The first case works fine if the external hardware buffer is large
++      enough.  This method doesn't need any extra buffers and thus is
+       more effective. You need to define the
+       <structfield>copy</structfield> and
+       <structfield>silence</structfield> callbacks for 
+@@ -5127,25 +5122,25 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        The second case allows the mmap of the buffer, although you have
+-      to handle an interrupt or a tasklet for transferring the data
++        The second case allows for mmap on the buffer, although you have
++      to handle an interrupt or a tasklet to transfer the data
+       from the intermediate buffer to the hardware buffer. You can find an
+-      example in vxpocket driver. 
++      example in the vxpocket driver. 
+       </para>
+ 
+       <para>
+-        Another case is that the chip uses a PCI memory-map
++        Another case is when the chip uses a PCI memory-map
+       region for the buffer instead of the host memory. In this case,
+-      mmap is available only on certain architectures like intel. In
+-      non-mmap mode, the data cannot be transferred as the normal
+-      way. Thus you need to define <structfield>copy</structfield> and
+-      <structfield>silence</structfield> callbacks as well 
++      mmap is available only on certain architectures like the Intel one.
++      In non-mmap mode, the data cannot be transferred as in the normal
++      way. Thus you need to define the <structfield>copy</structfield> and
++      <structfield>silence</structfield> callbacks as well, 
+       as in the cases above. The examples are found in
+       <filename>rme32.c</filename> and <filename>rme96.c</filename>. 
+       </para>
+ 
+       <para>
+-        The implementation of <structfield>copy</structfield> and
++        The implementation of the <structfield>copy</structfield> and
+         <structfield>silence</structfield> callbacks depends upon 
+         whether the hardware supports interleaved or non-interleaved
+         samples. The <structfield>copy</structfield> callback is
+@@ -5184,8 +5179,8 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         What you have to do in this callback is again different
+-        between playback and capture directions. In the case of
+-        playback, you do: copy the given amount of data
++        between playback and capture directions. In the
++        playback case, you copy the given amount of data
+         (<parameter>count</parameter>) at the specified pointer
+         (<parameter>src</parameter>) to the specified offset
+         (<parameter>pos</parameter>) on the hardware buffer. When
+@@ -5202,7 +5197,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        For the capture direction, you do: copy the given amount of
++        For the capture direction, you copy the given amount of
+         data (<parameter>count</parameter>) at the specified offset
+         (<parameter>pos</parameter>) on the hardware buffer to the
+         specified pointer (<parameter>dst</parameter>). 
+@@ -5216,7 +5211,7 @@ struct _snd_pcm_runtime {
+           </programlisting>
+         </informalexample>
+ 
+-        Note that both of the position and the data amount are given
++        Note that both the position and the amount of data are given
+       in frames. 
+       </para>
+ 
+@@ -5247,7 +5242,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        The meanings of arguments are identical with the
++        The meanings of arguments are the same as in the
+       <structfield>copy</structfield> 
+       callback, although there is no <parameter>src/dst</parameter>
+       argument. In the case of interleaved samples, the channel
+@@ -5284,8 +5279,8 @@ struct _snd_pcm_runtime {
+     <section id="buffer-and-memory-non-contiguous">
+       <title>Non-Contiguous Buffers</title>
+       <para>
+-        If your hardware supports the page table like emu10k1 or the
+-      buffer descriptors like via82xx, you can use the scatter-gather
++        If your hardware supports the page table as in emu10k1 or the
++      buffer descriptors as in via82xx, you can use the scatter-gather
+       (SG) DMA. ALSA provides an interface for handling SG-buffers.
+       The API is provided in <filename>&lt;sound/pcm.h&gt;</filename>. 
+       </para>
+@@ -5296,7 +5291,7 @@ struct _snd_pcm_runtime {
+         <function>snd_pcm_lib_preallocate_pages_for_all()</function>
+         with <constant>SNDRV_DMA_TYPE_DEV_SG</constant>
+ 	in the PCM constructor like other PCI pre-allocator.
+-        You need to pass the <function>snd_dma_pci_data(pci)</function>,
++        You need to pass <function>snd_dma_pci_data(pci)</function>,
+         where pci is the struct <structname>pci_dev</structname> pointer
+         of the chip as well.
+         The <type>struct snd_sg_buf</type> instance is created as
+@@ -5314,7 +5309,7 @@ struct _snd_pcm_runtime {
+ 
+       <para>
+         Then call <function>snd_pcm_lib_malloc_pages()</function>
+-      in <structfield>hw_params</structfield> callback
++      in the <structfield>hw_params</structfield> callback
+       as well as in the case of normal PCI buffer.
+       The SG-buffer handler will allocate the non-contiguous kernel
+       pages of the given size and map them onto the virtually contiguous
+@@ -5335,7 +5330,7 @@ struct _snd_pcm_runtime {
+       </para>
+ 
+       <para>
+-        For releasing the data, call
++        To release the data, call
+       <function>snd_pcm_lib_free_pages()</function> in the
+       <structfield>hw_free</structfield> callback as usual.
+       </para>
+@@ -5390,7 +5385,7 @@ struct _snd_pcm_runtime {
+     </para>
+ 
+     <para>
+-      For creating a proc file, call
++      To create a proc file, call
+       <function>snd_card_proc_new()</function>. 
+ 
+       <informalexample>
+@@ -5402,7 +5397,7 @@ struct _snd_pcm_runtime {
+         </programlisting>
+       </informalexample>
+ 
+-      where the second argument specifies the proc-file name to be
++      where the second argument specifies the name of the proc file to be
+     created. The above example will create a file
+     <filename>my-file</filename> under the card directory,
+     e.g. <filename>/proc/asound/card0/my-file</filename>. 
+@@ -5417,8 +5412,8 @@ struct _snd_pcm_runtime {
+ 
+     <para>
+       When the creation is successful, the function stores a new
+-    instance at the pointer given in the third argument.
+-    It is initialized as a text proc file for read only.  For using
++    instance in the pointer given in the third argument.
++    It is initialized as a text proc file for read only.  To use
+     this proc file as a read-only text file as it is, set the read
+     callback with a private data via 
+      <function>snd_info_set_text_ops()</function>.
+@@ -5470,9 +5465,9 @@ struct _snd_pcm_runtime {
+     </para>
+ 
+     <para>
+-    The file permission can be changed afterwards.  As default, it's
+-    set as read only for all users.  If you want to add the write
+-    permission to the user (root as default), set like below:
++    The file permissions can be changed afterwards.  As default, it's
++    set as read only for all users.  If you want to add write
++    permission for the user (root as default), do as follows:
+ 
+       <informalexample>
+         <programlisting>
+@@ -5503,7 +5498,7 @@ struct _snd_pcm_runtime {
+     </para>
+ 
+     <para>
+-      For a raw-data proc-file, set the attributes like the following:
++      For a raw-data proc-file, set the attributes as follows:
+ 
+       <informalexample>
+         <programlisting>
+@@ -5524,7 +5519,7 @@ struct _snd_pcm_runtime {
+ 
+     <para>
+       The callback is much more complicated than the text-file
+-      version. You need to use a low-level i/o functions such as
++      version. You need to use a low-level I/O functions such as
+       <function>copy_from/to_user()</function> to transfer the
+       data.
+ 
+@@ -5560,28 +5555,28 @@ struct _snd_pcm_runtime {
+     <title>Power Management</title>
+     <para>
+       If the chip is supposed to work with suspend/resume
+-      functions, you need to add the power-management codes to the
+-      driver. The additional codes for the power-management should be
++      functions, you need to add power-management code to the
++      driver. The additional code for power-management should be
+       <function>ifdef</function>'ed with
+       <constant>CONFIG_PM</constant>. 
+     </para>
+ 
+ 	<para>
+-	If the driver supports the suspend/resume
+-	<emphasis>fully</emphasis>, that is, the device can be
+-	properly resumed to the status at the suspend is called,
+-	you can set <constant>SNDRV_PCM_INFO_RESUME</constant> flag
+-	to pcm info field.  Usually, this is possible when the
+-	registers of ths chip can be safely saved and restored to the
+-	RAM.  If this is set, the trigger callback is called with
+-	<constant>SNDRV_PCM_TRIGGER_RESUME</constant> after resume
+-	callback is finished. 
++	If the driver <emphasis>fully</emphasis> supports suspend/resume
++	that is, the device can be
++	properly resumed to its state when suspend was called,
++	you can set the <constant>SNDRV_PCM_INFO_RESUME</constant> flag
++	in the pcm info field.  Usually, this is possible when the
++	registers of the chip can be safely saved and restored to
++	RAM. If this is set, the trigger callback is called with
++	<constant>SNDRV_PCM_TRIGGER_RESUME</constant> after the resume
++	callback completes. 
+ 	</para>
+ 
+ 	<para>
+-	Even if the driver doesn't support PM fully but only the
+-	partial suspend/resume is possible, it's still worthy to
+-	implement suspend/resume callbacks.  In such a case, applications
++	Even if the driver doesn't support PM fully but 
++	partial suspend/resume is still possible, it's still worthy to
++	implement suspend/resume callbacks. In such a case, applications
+ 	would reset the status by calling
+ 	<function>snd_pcm_prepare()</function> and restart the stream
+ 	appropriately.  Hence, you can define suspend/resume callbacks
+@@ -5590,22 +5585,22 @@ struct _snd_pcm_runtime {
+ 	</para>
+ 	
+ 	<para>
+-	Note that the trigger with SUSPEND can be always called when
++	Note that the trigger with SUSPEND can always be called when
+ 	<function>snd_pcm_suspend_all</function> is called,
+-	regardless of <constant>SNDRV_PCM_INFO_RESUME</constant> flag.
++	regardless of the <constant>SNDRV_PCM_INFO_RESUME</constant> flag.
+ 	The <constant>RESUME</constant> flag affects only the behavior
+ 	of <function>snd_pcm_resume()</function>.
+ 	(Thus, in theory,
+ 	<constant>SNDRV_PCM_TRIGGER_RESUME</constant> isn't needed
+ 	to be handled in the trigger callback when no
+ 	<constant>SNDRV_PCM_INFO_RESUME</constant> flag is set.  But,
+-	it's better to keep it for compatibility reason.)
++	it's better to keep it for compatibility reasons.)
+ 	</para>
+     <para>
+       In the earlier version of ALSA drivers, a common
+       power-management layer was provided, but it has been removed.
+       The driver needs to define the suspend/resume hooks according to
+-      the bus the device is assigned.  In the case of PCI driver, the
++      the bus the device is connected to.  In the case of PCI drivers, the
+       callbacks look like below:
+ 
+       <informalexample>
+@@ -5629,7 +5624,7 @@ struct _snd_pcm_runtime {
+     </para>
+ 
+     <para>
+-      The scheme of the real suspend job is as following.
++      The scheme of the real suspend job is as follows.
+ 
+       <orderedlist>
+         <listitem><para>Retrieve the card and the chip data.</para></listitem>
+@@ -5679,11 +5674,11 @@ struct _snd_pcm_runtime {
+     </para>
+ 
+     <para>
+-    The scheme of the real resume job is as following.
++    The scheme of the real resume job is as follows.
+ 
+     <orderedlist>
+     <listitem><para>Retrieve the card and the chip data.</para></listitem>
+-    <listitem><para>Set up PCI.  First, call <function>pci_restore_state()</function>.
++    <listitem><para>Set up PCI. First, call <function>pci_restore_state()</function>.
+     	Then enable the pci device again by calling <function>pci_enable_device()</function>.
+ 	Call <function>pci_set_master()</function> if necessary, too.</para></listitem>
+     <listitem><para>Re-initialize the chip.</para></listitem>
+@@ -5734,7 +5729,7 @@ struct _snd_pcm_runtime {
+ 	<function>snd_pcm_suspend_all()</function> or
+ 	<function>snd_pcm_suspend()</function>.  It means that the PCM
+ 	streams are already stoppped when the register snapshot is
+-	taken.  But, remind that you don't have to restart the PCM
++	taken.  But, remember that you don't have to restart the PCM
+ 	stream in the resume callback. It'll be restarted via 
+ 	trigger call with <constant>SNDRV_PCM_TRIGGER_RESUME</constant>
+ 	when necessary.
+@@ -5795,7 +5790,7 @@ struct _snd_pcm_runtime {
+     </para>
+ 
+     <para>
+-      If you need a space for saving the registers, allocate the
++      If you need a space to save the registers, allocate the
+ 	buffer for it here, too, since it would be fatal
+     if you cannot allocate a memory in the suspend phase.
+     The allocated buffer should be released in the corresponding
+@@ -5833,7 +5828,7 @@ struct _snd_pcm_runtime {
+     <title>Module Parameters</title>
+     <para>
+       There are standard module options for ALSA. At least, each
+-      module should have <parameter>index</parameter>,
++      module should have the <parameter>index</parameter>,
+       <parameter>id</parameter> and <parameter>enable</parameter>
+       options. 
+     </para>
+@@ -5841,8 +5836,8 @@ struct _snd_pcm_runtime {
+     <para>
+       If the module supports multiple cards (usually up to
+       8 = <constant>SNDRV_CARDS</constant> cards), they should be
+-      arrays.  The default initial values are defined already as
+-      constants for ease of programming:
++      arrays. The default initial values are defined already as
++      constants for easier programming:
+ 
+       <informalexample>
+         <programlisting>
+@@ -5858,7 +5853,7 @@ struct _snd_pcm_runtime {
+     <para>
+       If the module supports only a single card, they could be single
+     variables, instead.  <parameter>enable</parameter> option is not
+-    always necessary in this case, but it wouldn't be so bad to have a
++    always necessary in this case, but it would be better to have a
+     dummy option for compatibility.
+     </para>
+ 
+@@ -5923,22 +5918,22 @@ struct _snd_pcm_runtime {
+ 	</para>
+ 
+ 	<para>
+-	Suppose that you'll create a new PCI driver for the card
++	Suppose that you create a new PCI driver for the card
+ 	<quote>xyz</quote>.  The card module name would be
+-	snd-xyz.  The new driver is usually put into alsa-driver
++	snd-xyz.  The new driver is usually put into the alsa-driver
+ 	tree, <filename>alsa-driver/pci</filename> directory in
+ 	the case of PCI cards.
+ 	Then the driver is evaluated, audited and tested
+ 	by developers and users.  After a certain time, the driver
+-	will go to alsa-kernel tree (to the corresponding directory,
++	will go to the alsa-kernel tree (to the corresponding directory,
+ 	such as <filename>alsa-kernel/pci</filename>) and eventually
+-	integrated into Linux 2.6 tree (the directory would be
++ 	will be integrated into the Linux 2.6 tree (the directory would be
+ 	<filename>linux/sound/pci</filename>).
+ 	</para>
+ 
+ 	<para>
+ 	In the following sections, the driver code is supposed
+-	to be put into alsa-driver tree.  The two cases are assumed:
++	to be put into alsa-driver tree. The two cases are covered:
+ 	a driver consisting of a single source file and one consisting
+ 	of several source files.
+ 	</para>
+@@ -6033,7 +6028,7 @@ struct _snd_pcm_runtime {
+ 	<listitem>
+ 	<para>
+ 	Add a new directory (<filename>xyz</filename>) in
+-	<filename>alsa-driver/pci/Makefile</filename> like below
++	<filename>alsa-driver/pci/Makefile</filename> as below
+ 
+       <informalexample>
+         <programlisting>
+@@ -6102,7 +6097,7 @@ struct _snd_pcm_runtime {
+     <section id="useful-functions-snd-printk">
+       <title><function>snd_printk()</function> and friends</title>
+       <para>
+-        ALSA provides a verbose version of
++        ALSA provides a verbose version of the
+       <function>printk()</function> function. If a kernel config
+       <constant>CONFIG_SND_VERBOSE_PRINTK</constant> is set, this
+       function prints the given message together with the file name
+@@ -6170,7 +6165,7 @@ struct _snd_pcm_runtime {
+     <section id="useful-functions-snd-bug">
+       <title><function>snd_BUG()</function></title>
+       <para>
+-        It shows <computeroutput>BUG?</computeroutput> message and
++        It shows the <computeroutput>BUG?</computeroutput> message and
+       stack trace as well as <function>snd_assert</function> at the point.
+       It's useful to show that a fatal error happens there. 
+       </para>
+@@ -6199,6 +6194,4 @@ struct _snd_pcm_runtime {
+     in the hardware constraints section.
+     </para>
+   </chapter>
+-
+-
+ </book>
+diff --git a/Documentation/sound/alsa/soc/DAI.txt b/Documentation/sound/alsa/soc/DAI.txt
+index 3feeb9e..0ebd7ea 100644
+--- a/Documentation/sound/alsa/soc/DAI.txt
++++ b/Documentation/sound/alsa/soc/DAI.txt
+@@ -1,5 +1,5 @@
+ ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
+-SoC controllers and portable audio CODECS today, namely AC97, I2S and PCM.
++SoC controllers and portable audio CODECs today, namely AC97, I2S and PCM.
+ 
+ 
+ AC97
+@@ -25,7 +25,7 @@ left/right clock (LRC) synchronise the link. I2S is flexible in that either the
+ controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
+ usually varies depending on the sample rate and the master system clock
+ (SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
+-ADC and DAC LRCLK's, this allows for simultaneous capture and playback at
++ADC and DAC LRCLKs, this allows for simultaneous capture and playback at
+ different sample rates.
+ 
+ I2S has several different operating modes:-
+@@ -35,7 +35,7 @@ I2S has several different operating modes:-
+ 
+  o Left Justified - MSB is transmitted on transition of LRC.
+ 
+- o Right Justified - MSB is transmitted sample size BCLK's before LRC
++ o Right Justified - MSB is transmitted sample size BCLKs before LRC
+                      transition.
+ 
+ PCM
+diff --git a/Documentation/sound/alsa/soc/clocking.txt b/Documentation/sound/alsa/soc/clocking.txt
+index 1493088..b130016 100644
+--- a/Documentation/sound/alsa/soc/clocking.txt
++++ b/Documentation/sound/alsa/soc/clocking.txt
+@@ -13,7 +13,7 @@ or SYSCLK). This audio master clock can be derived from a number of sources
+ (e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
+ audio playback and capture sample rates.
+ 
+-Some master clocks (e.g. PLL's and CPU based clocks) are configurable in that
++Some master clocks (e.g. PLLs and CPU based clocks) are configurable in that
+ their speed can be altered by software (depending on the system use and to save
+ power). Other master clocks are fixed at a set frequency (i.e. crystals).
+ 
+@@ -41,11 +41,11 @@ BCLK = LRC * x
+ BCLK = LRC * Channels * Word Size
+ 
+ This relationship depends on the codec or SoC CPU in particular. In general
+-it's best to configure BCLK to the lowest possible speed (depending on your
+-rate, number of channels and wordsize) to save on power.
++it is best to configure BCLK to the lowest possible speed (depending on your
++rate, number of channels and word size) to save on power.
+ 
+-It's also desirable to use the codec (if possible) to drive (or master) the
+-audio clocks as it's usually gives more accurate sample rates than the CPU.
++It is also desirable to use the codec (if possible) to drive (or master) the
++audio clocks as it usually gives more accurate sample rates than the CPU.
+ 
+ 
+ 
+diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt
+index 1e766ad..1e95342 100644
+--- a/Documentation/sound/alsa/soc/codec.txt
++++ b/Documentation/sound/alsa/soc/codec.txt
+@@ -9,7 +9,7 @@ code should be added to the platform and machine drivers respectively.
+ Each codec driver *must* provide the following features:-
+ 
+  1) Codec DAI and PCM configuration
+- 2) Codec control IO - using I2C, 3 Wire(SPI) or both API's
++ 2) Codec control IO - using I2C, 3 Wire(SPI) or both APIs
+  3) Mixers and audio controls
+  4) Codec audio operations
+ 
+@@ -19,7 +19,7 @@ Optionally, codec drivers can also provide:-
+  6) DAPM event handler.
+  7) DAC Digital mute control.
+ 
+-It's probably best to use this guide in conjunction with the existing codec
++Its probably best to use this guide in conjunction with the existing codec
+ driver code in sound/soc/codecs/
+ 
+ ASoC Codec driver breakdown
+@@ -27,8 +27,8 @@ ASoC Codec driver breakdown
+ 
+ 1 - Codec DAI and PCM configuration
+ -----------------------------------
+-Each codec driver must have a struct snd_soc_codec_dai to define it's DAI and
+-PCM's capabilities and operations. This struct is exported so that it can be
++Each codec driver must have a struct snd_soc_codec_dai to define its DAI and
++PCM capabilities and operations. This struct is exported so that it can be
+ registered with the core by your machine driver.
+ 
+ e.g.
+@@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(wm8731_dai);
+ 
+ 2 - Codec control IO
+ --------------------
+-The codec can usually be controlled via an I2C or SPI style interface (AC97
+-combines control with data in the DAI). The codec drivers will have to provide
+-functions to read and write the codec registers along with supplying a register
+-cache:-
++The codec can usually be controlled via an I2C or SPI style interface
++(AC97 combines control with data in the DAI). The codec drivers provide
++functions to read and write the codec registers along with supplying a
++register cache:-
+ 
+ 	/* IO control data and register cache */
+-    void *control_data; /* codec control (i2c/3wire) data */
+-    void *reg_cache;
++	void *control_data; /* codec control (i2c/3wire) data */
++	void *reg_cache;
+ 
+-Codec read/write should do any data formatting and call the hardware read write
+-below to perform the IO. These functions are called by the core and alsa when
+-performing DAPM or changing the mixer:-
++Codec read/write should do any data formatting and call the hardware
++read write below to perform the IO. These functions are called by the
++core and ALSA when performing DAPM or changing the mixer:-
+ 
+     unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+     int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+@@ -131,7 +131,7 @@ Defines a stereo enumerated control
+ 
+ 4 - Codec Audio Operations
+ --------------------------
+-The codec driver also supports the following alsa operations:-
++The codec driver also supports the following ALSA operations:-
+ 
+ /* SoC audio ops */
+ struct snd_soc_ops {
+@@ -142,15 +142,15 @@ struct snd_soc_ops {
+ 	int (*prepare)(struct snd_pcm_substream *);
+ };
+ 
+-Please refer to the alsa driver PCM documentation for details.
++Please refer to the ALSA driver PCM documentation for details.
+ http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
+ 
+ 
+ 5 - DAPM description.
+ ---------------------
+-The Dynamic Audio Power Management description describes the codec's power
+-components, their relationships and registers to the ASoC core. Please read
+-dapm.txt for details of building the description.
++The Dynamic Audio Power Management description describes the codec power
++components and their relationships and registers to the ASoC core.
++Please read dapm.txt for details of building the description.
+ 
+ Please also see the examples in other codec drivers.
+ 
+@@ -158,8 +158,8 @@ Please also see the examples in other codec drivers.
+ 6 - DAPM event handler
+ ----------------------
+ This function is a callback that handles codec domain PM calls and system
+-domain PM calls (e.g. suspend and resume). It's used to put the codec to sleep
+-when not in use.
++domain PM calls (e.g. suspend and resume). It is used to put the codec
++to sleep when not in use.
+ 
+ Power states:-
+ 
+@@ -175,13 +175,14 @@ Power states:-
+ 	SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
+ 
+ 
+-7 - Codec DAC digital mute control.
+-------------------------------------
+-Most codecs have a digital mute before the DAC's that can be used to minimise
+-any system noise.  The mute stops any digital data from entering the DAC.
++7 - Codec DAC digital mute control
++----------------------------------
++Most codecs have a digital mute before the DACs that can be used to
++minimise any system noise.  The mute stops any digital data from
++entering the DAC.
+ 
+-A callback can be created that is called by the core for each codec DAI when the
+-mute is applied or freed.
++A callback can be created that is called by the core for each codec DAI
++when the mute is applied or freed.
+ 
+ i.e.
+ 
+diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt
+index ab0766f..c784a18 100644
+--- a/Documentation/sound/alsa/soc/dapm.txt
++++ b/Documentation/sound/alsa/soc/dapm.txt
+@@ -4,20 +4,20 @@ Dynamic Audio Power Management for Portable Devices
+ 1. Description
+ ==============
+ 
+-Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices
+-to use the minimum amount of power within the audio subsystem at all times. It
+-is independent of other kernel PM and as such, can easily co-exist with the
+-other PM systems.
++Dynamic Audio Power Management (DAPM) is designed to allow portable
++Linux devices to use the minimum amount of power within the audio
++subsystem at all times. It is independent of other kernel PM and as
++such, can easily co-exist with the other PM systems.
+ 
+-DAPM is also completely transparent to all user space applications as all power
+-switching is done within the ASoC core. No code changes or recompiling are
+-required for user space applications. DAPM makes power switching decisions based
+-upon any audio stream (capture/playback) activity and audio mixer settings
+-within the device.
++DAPM is also completely transparent to all user space applications as
++all power switching is done within the ASoC core. No code changes or
++recompiling are required for user space applications. DAPM makes power
++switching decisions based upon any audio stream (capture/playback)
++activity and audio mixer settings within the device.
+ 
+-DAPM spans the whole machine. It covers power control within the entire audio
+-subsystem, this includes internal codec power blocks and machine level power
+-systems.
++DAPM spans the whole machine. It covers power control within the entire
++audio subsystem, this includes internal codec power blocks and machine
++level power systems.
+ 
+ There are 4 power domains within DAPM
+ 
+@@ -34,7 +34,7 @@ There are 4 power domains within DAPM
+       Automatically set when mixer and mux settings are changed by the user.
+       e.g. alsamixer, amixer.
+ 
+-   4. Stream domain - DAC's and ADC's.
++   4. Stream domain - DACs and ADCs.
+       Enabled and disabled when stream playback/capture is started and
+       stopped respectively. e.g. aplay, arecord.
+ 
+@@ -51,7 +51,7 @@ widgets hereafter.
+ Audio DAPM widgets fall into a number of types:-
+ 
+  o Mixer      - Mixes several analog signals into a single analog signal.
+- o Mux        - An analog switch that outputs only 1 of it's inputs.
++ o Mux        - An analog switch that outputs only one of many inputs.
+  o PGA        - A programmable gain amplifier or attenuation widget.
+  o ADC        - Analog to Digital Converter
+  o DAC        - Digital to Analog Converter
+@@ -78,14 +78,14 @@ parameters for stream name and kcontrols.
+ 2.1 Stream Domain Widgets
+ -------------------------
+ 
+-Stream Widgets relate to the stream power domain and only consist of ADC's
+-(analog to digital converters) and DAC's (digital to analog converters).
++Stream Widgets relate to the stream power domain and only consist of ADCs
++(analog to digital converters) and DACs (digital to analog converters).
+ 
+ Stream widgets have the following format:-
+ 
+ SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
+ 
+-NOTE: the stream name must match the corresponding stream name in your codecs
++NOTE: the stream name must match the corresponding stream name in your codec
+ snd_soc_codec_dai.
+ 
+ e.g. stream widgets for HiFi playback and capture
+@@ -97,7 +97,7 @@ SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
+ 2.2 Path Domain Widgets
+ -----------------------
+ 
+-Path domain widgets have a ability to control or effect the audio signal or
++Path domain widgets have a ability to control or affect the audio signal or
+ audio paths within the audio subsystem. They have the following form:-
+ 
+ SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
+@@ -149,7 +149,7 @@ SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
+ 2.4 Codec Domain
+ ----------------
+ 
+-The Codec power domain has no widgets and is handled by the codecs DAPM event
++The codec power domain has no widgets and is handled by the codecs DAPM event
+ handler. This handler is called when the codec powerstate is changed wrt to any
+ stream event or by kernel PM events.
+ 
+@@ -158,8 +158,8 @@ stream event or by kernel PM events.
+ -------------------
+ 
+ Sometimes widgets exist in the codec or machine audio map that don't have any
+-corresponding register bit for power control. In this case it's necessary to
+-create a virtual widget - a widget with no control bits e.g.
++corresponding soft power control. In this case it is necessary to create
++a virtual widget - a widget with no control bits e.g.
+ 
+ SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
+ 
+@@ -172,13 +172,14 @@ subsystem individually with a call to snd_soc_dapm_new_control().
+ 3. Codec Widget Interconnections
+ ================================
+ 
+-Widgets are connected to each other within the codec and machine by audio
+-paths (called interconnections). Each interconnection must be defined in order
+-to create a map of all audio paths between widgets.
++Widgets are connected to each other within the codec and machine by audio paths
++(called interconnections). Each interconnection must be defined in order to
++create a map of all audio paths between widgets.
++
+ This is easiest with a diagram of the codec (and schematic of the machine audio
+ system), as it requires joining widgets together via their audio signal paths.
+ 
+-i.e. from the WM8731 codec's output mixer (wm8731.c)
++e.g., from the WM8731 output mixer (wm8731.c)
+ 
+ The WM8731 output mixer has 3 inputs (sources)
+ 
+diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt
+index 72bd222..f370e7d 100644
+--- a/Documentation/sound/alsa/soc/machine.txt
++++ b/Documentation/sound/alsa/soc/machine.txt
+@@ -16,7 +16,7 @@ struct snd_soc_machine {
+ 	int (*remove)(struct platform_device *pdev);
+ 
+ 	/* the pre and post PM functions are used to do any PM work before and
+-	 * after the codec and DAI's do any PM work. */
++	 * after the codec and DAIs do any PM work. */
+ 	int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
+ 	int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
+ 	int (*resume_pre)(struct platform_device *pdev);
+@@ -38,7 +38,7 @@ probe/remove are optional. Do any machine specific probe here.
+ suspend()/resume()
+ ------------------
+ The machine driver has pre and post versions of suspend and resume to take care
+-of any machine audio tasks that have to be done before or after the codec, DAI's
++of any machine audio tasks that have to be done before or after the codec, DAIs
+ and DMA is suspended and resumed. Optional.
+ 
+ 
+@@ -49,10 +49,10 @@ The machine specific audio operations can be set here. Again this is optional.
+ 
+ Machine DAI Configuration
+ -------------------------
+-The machine DAI configuration glues all the codec and CPU DAI's together. It can
++The machine DAI configuration glues all the codec and CPU DAIs together. It can
+ also be used to set up the DAI system clock and for any machine related DAI
+ initialisation e.g. the machine audio map can be connected to the codec audio
+-map, unconnnected codec pins can be set as such. Please see corgi.c, spitz.c
++map, unconnected codec pins can be set as such. Please see corgi.c, spitz.c
+ for examples.
+ 
+ struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
+@@ -67,7 +67,7 @@ static struct snd_soc_dai_link corgi_dai = {
+ 	.ops = &corgi_ops,
+ };
+ 
+-struct snd_soc_machine then sets up the machine with it's DAI's. e.g.
++struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
+ 
+ /* corgi audio machine driver */
+ static struct snd_soc_machine snd_soc_machine_corgi = {
+@@ -110,4 +110,4 @@ details.
+ Machine Controls
+ ----------------
+ 
+-Machine specific audio mixer controls can be added in the dai init function.
+\ No newline at end of file
++Machine specific audio mixer controls can be added in the DAI init function.
+diff --git a/Documentation/sound/alsa/soc/overview.txt b/Documentation/sound/alsa/soc/overview.txt
+index c47ce95..1e4c6d3 100644
+--- a/Documentation/sound/alsa/soc/overview.txt
++++ b/Documentation/sound/alsa/soc/overview.txt
+@@ -1,25 +1,26 @@
+ ALSA SoC Layer
+ ==============
+ 
+-The overall project goal of the ALSA System on Chip (ASoC) layer is to provide
+-better ALSA support for embedded system-on-chip processors (e.g. pxa2xx, au1x00,
+-iMX, etc) and portable audio codecs. Currently there is some support in the
+-kernel for SoC audio, however it has some limitations:-
++The overall project goal of the ALSA System on Chip (ASoC) layer is to
++provide better ALSA support for embedded system-on-chip processors (e.g.
++pxa2xx, au1x00, iMX, etc) and portable audio codecs.  Prior to the ASoC
++subsystem there was some support in the kernel for SoC audio, however it
++had some limitations:-
+ 
+-  * Currently, codec drivers are often tightly coupled to the underlying SoC
+-    CPU. This is not ideal and leads to code duplication i.e. Linux now has 4
+-    different wm8731 drivers for 4 different SoC platforms.
++  * Codec drivers were often tightly coupled to the underlying SoC
++    CPU. This is not ideal and leads to code duplication - for example,
++    Linux had different wm8731 drivers for 4 different SoC platforms.
+ 
+-  * There is no standard method to signal user initiated audio events (e.g.
++  * There was no standard method to signal user initiated audio events (e.g.
+     Headphone/Mic insertion, Headphone/Mic detection after an insertion
+     event). These are quite common events on portable devices and often require
+     machine specific code to re-route audio, enable amps, etc., after such an
+     event.
+ 
+-  * Current drivers tend to power up the entire codec when playing
+-    (or recording) audio. This is fine for a PC, but tends to waste a lot of
+-    power on portable devices. There is also no support for saving power via
+-    changing codec oversampling rates, bias currents, etc.
++  * Drivers tended to power up the entire codec when playing (or
++    recording) audio. This is fine for a PC, but tends to waste a lot of
++    power on portable devices. There was also no support for saving
++    power via changing codec oversampling rates, bias currents, etc.
+ 
+ 
+ ASoC Design
+@@ -31,12 +32,13 @@ features :-
+   * Codec independence. Allows reuse of codec drivers on other platforms
+     and machines.
+ 
+-  * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC interface
+-    and codec registers it's audio interface capabilities with the core and are
+-    subsequently matched and configured when the application hw params are known.
++  * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC
++    interface and codec registers it's audio interface capabilities with the
++    core and are subsequently matched and configured when the application
++    hardware parameters are known.
+ 
+   * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
+-    it's minimum power state at all times. This includes powering up/down
++    its minimum power state at all times. This includes powering up/down
+     internal power blocks depending on the internal codec audio routing and any
+     active streams.
+ 
+@@ -45,16 +47,16 @@ features :-
+     signals the codec when to change power states.
+ 
+   * Machine specific controls: Allow machines to add controls to the sound card
+-    (e.g. volume control for speaker amp).
++    (e.g. volume control for speaker amplifier).
+ 
+ To achieve all this, ASoC basically splits an embedded audio system into 3
+ components :-
+ 
+   * Codec driver: The codec driver is platform independent and contains audio
+-    controls, audio interface capabilities, codec dapm definition and codec IO
++    controls, audio interface capabilities, codec DAPM definition and codec IO
+     functions.
+ 
+-  * Platform driver: The platform driver contains the audio dma engine and audio
++  * Platform driver: The platform driver contains the audio DMA engine and audio
+     interface drivers (e.g. I2S, AC97, PCM) for that platform.
+ 
+   * Machine driver: The machine driver handles any machine specific controls and
+@@ -81,4 +83,4 @@ machine.txt: Machine driver internals.
+ 
+ pop_clicks.txt: How to minimise audio artifacts.
+ 
+-clocking.txt: ASoC clocking for best power performance.
+\ No newline at end of file
++clocking.txt: ASoC clocking for best power performance.
+diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/alsa/soc/platform.txt
+index d4678b4..b681d17 100644
+--- a/Documentation/sound/alsa/soc/platform.txt
++++ b/Documentation/sound/alsa/soc/platform.txt
+@@ -8,7 +8,7 @@ specific code.
+ Audio DMA
+ =========
+ 
+-The platform DMA driver optionally supports the following alsa operations:-
++The platform DMA driver optionally supports the following ALSA operations:-
+ 
+ /* SoC audio ops */
+ struct snd_soc_ops {
+@@ -38,7 +38,7 @@ struct snd_soc_platform {
+ 	struct snd_pcm_ops *pcm_ops;
+ };
+ 
+-Please refer to the alsa driver documentation for details of audio DMA.
++Please refer to the ALSA driver documentation for details of audio DMA.
+ http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
+ 
+ An example DMA driver is soc/pxa/pxa2xx-pcm.c
+@@ -52,7 +52,7 @@ Each SoC DAI driver must provide the following features:-
+  1) Digital audio interface (DAI) description
+  2) Digital audio interface configuration
+  3) PCM's description
+- 4) Sysclk configuration
++ 4) SYSCLK configuration
+  5) Suspend and resume (optional)
+ 
+ Please see codec.txt for a description of items 1 - 4.
+diff --git a/Documentation/sound/alsa/soc/pops_clicks.txt b/Documentation/sound/alsa/soc/pops_clicks.txt
+index 3371bd9..e1e74da 100644
+--- a/Documentation/sound/alsa/soc/pops_clicks.txt
++++ b/Documentation/sound/alsa/soc/pops_clicks.txt
+@@ -15,11 +15,11 @@ click every time a component power state is changed.
+ Minimising Playback Pops and Clicks
+ ===================================
+ 
+-Playback pops in portable audio subsystems cannot be completely eliminated atm,
+-however future audio codec hardware will have better pop and click suppression.
+-Pops can be reduced within playback by powering the audio components in a
+-specific order. This order is different for startup and shutdown and follows
+-some basic rules:-
++Playback pops in portable audio subsystems cannot be completely eliminated
++currently, however future audio codec hardware will have better pop and click
++suppression.  Pops can be reduced within playback by powering the audio
++components in a specific order. This order is different for startup and
++shutdown and follows some basic rules:-
+ 
+  Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
+ 
 diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
 index 00cb646..0924e6e 100644
 --- a/Documentation/video4linux/CARDLIST.cx23885
@@ -8434,7 +12120,7 @@
 +参考Satyam Sharma,Johannes Stezenbach,Jesper Juhl,Heikki Orsila,
 +H. Peter Anvin,Philipp Hahn和Stefan Richter的意见改善了本档。
 diff --git a/MAINTAINERS b/MAINTAINERS
-index 2340cfb..2d5ff3e 100644
+index 2340cfb..093cf04 100644
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
 @@ -646,6 +646,17 @@ M:	ecashin at coraid.com
@@ -8619,6 +12305,16 @@
  M:	linux390 at de.ibm.com
  L:	linux-s390 at vger.kernel.org
  W:	http://www.ibm.com/developerworks/linux/linux390/
+@@ -3520,6 +3571,9 @@ S:	Maintained
+ SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
+ P:	Liam Girdwood
+ M:	liam.girdwood at wolfsonmicro.com
++P:	Mark Brown
++M:	broonie at opensource.wolfsonmicro.com
++T:	git opensource.wolfsonmicro.com/linux-2.6-asoc
+ L:	alsa-devel at alsa-project.org (subscribers-only)
+ S:	Supported
+ 
 diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S
 index 55c05b5..f13249b 100644
 --- a/arch/alpha/kernel/vmlinux.lds.S
@@ -60581,6 +64277,28 @@
 -	}
 -}
 -#endif /* CONFIG_SMP */
+diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
+index 73e7c2e..5ae177f 100644
+--- a/arch/ia64/kernel/perfmon.c
++++ b/arch/ia64/kernel/perfmon.c
+@@ -2631,7 +2631,7 @@ pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task)
+ 	 */
+ 	if (task == current) return 0;
+ 
+-	if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) {
++	if (!task_is_stopped_or_traced(task)) {
+ 		DPRINT(("cannot attach to non-stopped task [%d] state=%ld\n", task_pid_nr(task), task->state));
+ 		return -EBUSY;
+ 	}
+@@ -4792,7 +4792,7 @@ recheck:
+ 	 * the task must be stopped.
+ 	 */
+ 	if (PFM_CMD_STOPPED(cmd)) {
+-		if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) {
++		if (!task_is_stopped_or_traced(task)) {
+ 			DPRINT(("[%d] task not in stopped state\n", task_pid_nr(task)));
+ 			return -EBUSY;
+ 		}
 diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
 index 4ac2b1f..86028c6 100644
 --- a/arch/ia64/kernel/setup.c
@@ -146749,6 +150467,22 @@
  
  static int __init idle_param(char *p)
  {
+diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c
+index 9916a0f..c5cfd4b 100644
+--- a/arch/powerpc/platforms/pasemi/iommu.c
++++ b/arch/powerpc/platforms/pasemi/iommu.c
+@@ -182,8 +182,10 @@ static void pci_dma_dev_setup_pasemi(struct pci_dev *dev)
+ 	 * CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE at build time.
+ 	 */
+ 	if (dev->vendor == 0x1959 && dev->device == 0xa007 &&
+-	    !firmware_has_feature(FW_FEATURE_LPAR))
++	    !firmware_has_feature(FW_FEATURE_LPAR)) {
+ 		dev->dev.archdata.dma_ops = &dma_direct_ops;
++		dev->dev.archdata.dma_data = 0;
++	}
+ #endif
+ 
+ 	dev->dev.archdata.dma_data = &iommu_table_iobmap;
 diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h
 index 516acab..b1e524f 100644
 --- a/arch/powerpc/platforms/pasemi/pasemi.h
@@ -253784,7 +257518,7 @@
  #endif
  
 diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
-index e2fcf20..db28aa9 100644
+index e2fcf20..d608c9e 100644
 --- a/arch/x86/kernel/cpu/common.c
 +++ b/arch/x86/kernel/cpu/common.c
 @@ -22,43 +22,48 @@
@@ -253882,8 +257616,15 @@
  	return 1;
  }
  __setup("nosep", x86_sep_setup);
-@@ -281,6 +278,33 @@ void __init cpu_detect(struct cpuinfo_x86 *c)
+@@ -277,10 +274,39 @@ void __init cpu_detect(struct cpuinfo_x86 *c)
+ 		if (c->x86 >= 0x6)
+ 			c->x86_model += ((tfms >> 16) & 0xF) << 4;
+ 		c->x86_mask = tfms & 15;
+-		if (cap0 & (1<<19))
++		if (cap0 & (1<<19)) {
  			c->x86_cache_alignment = ((misc >> 8) & 0xff) * 8;
++			c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
++		}
  	}
  }
 +static void __cpuinit early_get_cap(struct cpuinfo_x86 *c)
@@ -253916,7 +257657,15 @@
  
  /* Do minimum CPU detection early.
     Fields really needed: vendor, cpuid_level, family, model, mask, cache alignment.
-@@ -300,6 +324,17 @@ static void __init early_cpu_detect(void)
+@@ -293,6 +319,7 @@ static void __init early_cpu_detect(void)
+ 	struct cpuinfo_x86 *c = &boot_cpu_data;
+ 
+ 	c->x86_cache_alignment = 32;
++	c->x86_clflush_size = 32;
+ 
+ 	if (!have_cpuid_p())
+ 		return;
+@@ -300,6 +327,17 @@ static void __init early_cpu_detect(void)
  	cpu_detect(c);
  
  	get_cpu_vendor(c, 1);
@@ -253934,7 +257683,7 @@
  }
  
  static void __cpuinit generic_identify(struct cpuinfo_x86 * c)
-@@ -357,8 +392,6 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 * c)
+@@ -357,8 +395,6 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 * c)
  		init_scattered_cpuid_features(c);
  	}
  
@@ -253943,7 +257692,7 @@
  #ifdef CONFIG_X86_HT
  	c->phys_proc_id = (cpuid_ebx(1) >> 24) & 0xff;
  #endif
-@@ -392,7 +425,7 @@ __setup("serialnumber", x86_serial_nr_setup);
+@@ -392,7 +428,7 @@ __setup("serialnumber", x86_serial_nr_setup);
  /*
   * This does the hard work of actually picking apart the CPU stuff...
   */
@@ -253952,7 +257701,7 @@
  {
  	int i;
  
-@@ -418,20 +451,9 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
+@@ -418,20 +454,9 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
  
  	generic_identify(c);
  
@@ -253974,7 +257723,7 @@
  	/*
  	 * Vendor-specific initialization.  In this section we
  	 * canonicalize the feature flags, meaning if there are
-@@ -453,23 +475,6 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
+@@ -453,23 +478,6 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
  	 * we do "generic changes."
  	 */
  
@@ -253998,7 +257747,7 @@
  	/* If the model name is still unset, do table lookup. */
  	if ( !c->x86_model_id[0] ) {
  		char *p;
-@@ -482,13 +487,6 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
+@@ -482,13 +490,6 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
  				c->x86, c->x86_model);
  	}
  
@@ -254012,7 +257761,7 @@
  	/*
  	 * On SMP, boot_cpu_data holds the common feature set between
  	 * all CPUs; so make sure that we indicate which features are
-@@ -501,8 +499,14 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
+@@ -501,8 +502,14 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
  			boot_cpu_data.x86_capability[i] &= c->x86_capability[i];
  	}
  
@@ -254027,7 +257776,7 @@
  }
  
  void __init identify_boot_cpu(void)
-@@ -510,7 +514,6 @@ void __init identify_boot_cpu(void)
+@@ -510,7 +517,6 @@ void __init identify_boot_cpu(void)
  	identify_cpu(&boot_cpu_data);
  	sysenter_setup();
  	enable_sep_cpu();
@@ -254035,7 +257784,7 @@
  }
  
  void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c)
-@@ -567,6 +570,13 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c)
+@@ -567,6 +573,13 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c)
  }
  #endif
  
@@ -254049,7 +257798,7 @@
  void __cpuinit print_cpu_info(struct cpuinfo_x86 *c)
  {
  	char *vendor = NULL;
-@@ -590,6 +600,17 @@ void __cpuinit print_cpu_info(struct cpuinfo_x86 *c)
+@@ -590,6 +603,17 @@ void __cpuinit print_cpu_info(struct cpuinfo_x86 *c)
  		printk("\n");
  }
  
@@ -254067,7 +257816,7 @@
  cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE;
  
  /* This is hacky. :)
-@@ -620,21 +641,13 @@ void __init early_cpu_init(void)
+@@ -620,21 +644,13 @@ void __init early_cpu_init(void)
  	nexgen_init_cpu();
  	umc_init_cpu();
  	early_cpu_detect();
@@ -254090,7 +257839,7 @@
  	return regs;
  }
  
-@@ -642,7 +655,7 @@ struct pt_regs * __devinit idle_regs(struct pt_regs *regs)
+@@ -642,7 +658,7 @@ struct pt_regs * __devinit idle_regs(struct pt_regs *regs)
   * it's on the real one. */
  void switch_to_new_gdt(void)
  {
@@ -254099,7 +257848,7 @@
  
  	gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
  	gdt_descr.size = GDT_SIZE - 1;
-@@ -672,12 +685,6 @@ void __cpuinit cpu_init(void)
+@@ -672,12 +688,6 @@ void __cpuinit cpu_init(void)
  
  	if (cpu_has_vme || cpu_has_tsc || cpu_has_de)
  		clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
@@ -254112,7 +257861,7 @@
  
  	load_idt(&idt_descr);
  	switch_to_new_gdt();
-@@ -691,7 +698,7 @@ void __cpuinit cpu_init(void)
+@@ -691,7 +701,7 @@ void __cpuinit cpu_init(void)
  		BUG();
  	enter_lazy_tlb(&init_mm, curr);
  
@@ -254443,9 +258192,29 @@
  // arch_initcall(intel_cpu_init);
  
 diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
-index 9f530ff..8b4507b 100644
+index 9f530ff..1b88986 100644
 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c
 +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
+@@ -352,8 +352,8 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
+ 	 */
+ 	if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
+ 		/* supports eax=2  call */
+-		int i, j, n;
+-		int regs[4];
++		int j, n;
++		unsigned int regs[4];
+ 		unsigned char *dp = (unsigned char *)regs;
+ 		int only_trace = 0;
+ 
+@@ -368,7 +368,7 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
+ 
+ 			/* If bit 31 is set, this is an unknown format */
+ 			for ( j = 0 ; j < 3 ; j++ ) {
+-				if ( regs[j] < 0 ) regs[j] = 0;
++				if (regs[j] & (1 << 31)) regs[j] = 0;
+ 			}
+ 
+ 			/* Byte 0 is level count, not a descriptor */
 @@ -733,10 +733,8 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev)
  	if (unlikely(retval < 0))
  		return retval;
@@ -255748,7 +259517,7 @@
  		.ss		= __KERNEL_DS,
 diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c
 new file mode 100644
-index 0000000..1c5ca4d
+index 0000000..dcd918c
 --- /dev/null
 +++ b/arch/x86/kernel/ds.c
 @@ -0,0 +1,464 @@
@@ -255977,7 +259746,7 @@
 +	if (*dsp)
 +		kfree((void *)get_bts_buffer_base(*dsp));
 +	kfree(*dsp);
-+	*dsp = 0;
++	*dsp = NULL;
 +
 +	return 0;
 +}
@@ -274310,6 +278079,19 @@
 +}
 +EXPORT_SYMBOL(native_read_tsc);
 +
+diff --git a/arch/x86/kernel/scx200_32.c b/arch/x86/kernel/scx200_32.c
+index 87bc159..7e004ac 100644
+--- a/arch/x86/kernel/scx200_32.c
++++ b/arch/x86/kernel/scx200_32.c
+@@ -65,7 +65,7 @@ static int __devinit scx200_probe(struct pci_dev *pdev, const struct pci_device_
+ 		base = pci_resource_start(pdev, 0);
+ 		printk(KERN_INFO NAME ": GPIO base 0x%x\n", base);
+ 
+-		if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) {
++		if (!request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO")) {
+ 			printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n");
+ 			return -EBUSY;
+ 		}
 diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c
 index 3558ac7..309366f 100644
 --- a/arch/x86/kernel/setup64.c
@@ -320932,7 +324714,7 @@
  #endif
 diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
 new file mode 100644
-index 0000000..ed79572
+index 0000000..a177d76
 --- /dev/null
 +++ b/arch/x86/mm/ioremap.c
 @@ -0,0 +1,501 @@
@@ -321278,7 +325060,7 @@
 +	for (idx = FIX_BTMAP_BEGIN; idx >= FIX_BTMAP_END; idx--) {
 +		addr = fix_to_virt(idx);
 +		pte = early_ioremap_pte(addr);
-+		if (!*pte & _PAGE_PRESENT) {
++		if (*pte & _PAGE_PRESENT) {
 +			phys = *pte & PAGE_MASK;
 +			set_fixmap(idx, phys);
 +		}
@@ -323238,10 +327020,10 @@
 +module_init(exercise_pageattr);
 diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
 new file mode 100644
-index 0000000..1cc6607
+index 0000000..e297bd6
 --- /dev/null
 +++ b/arch/x86/mm/pageattr.c
-@@ -0,0 +1,564 @@
+@@ -0,0 +1,563 @@
 +/*
 + * Copyright 2002 Andi Kleen, SuSE Labs.
 + * Thanks to Ben LaHaise for precious feedback.
@@ -323643,8 +327425,7 @@
 +static inline int change_page_attr_clear(unsigned long addr, int numpages,
 +					 pgprot_t mask)
 +{
-+	return __change_page_attr_set_clr(addr, numpages, __pgprot(0), mask);
-+
++	return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask);
 +}
 +
 +int set_memory_uc(unsigned long addr, int numpages)
@@ -324352,7 +328133,7 @@
 -EXPORT_SYMBOL(change_page_attr);
 -EXPORT_SYMBOL(global_flush_tlb);
 diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c
-index be61a1d..2ae5999 100644
+index be61a1d..cb3aa47 100644
 --- a/arch/x86/mm/pgtable_32.c
 +++ b/arch/x86/mm/pgtable_32.c
 @@ -195,11 +195,6 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
@@ -324427,15 +328208,10 @@
  {
 -	pmd_t *pmd;
 +	int i;
- 
--	if (idx >= USER_PTRS_PER_PGD) {
--		pmd = (pmd_t *)__get_free_page(GFP_KERNEL);
++
 +	for(i = 0; i < UNSHARED_PTRS_PER_PGD; i++) {
 +		pgd_t pgd = pgdp[i];
- 
--		if (pmd)
--			memcpy(pmd,
--			       (void *)pgd_page_vaddr(swapper_pg_dir[idx]),
++
 +		if (pgd_val(pgd) != 0) {
 +			pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd);
 +
@@ -324463,12 +328239,17 @@
 +	pud_t *pud;
 +	unsigned long addr;
 +	int i;
-+
+ 
+-	if (idx >= USER_PTRS_PER_PGD) {
+-		pmd = (pmd_t *)__get_free_page(GFP_KERNEL);
 +	pud = pud_offset(pgd, 0);
 + 	for (addr = i = 0; i < UNSHARED_PTRS_PER_PGD;
 +	     i++, pud++, addr += PUD_SIZE) {
 +		pmd_t *pmd = pmd_alloc_one(mm, addr);
-+
+ 
+-		if (pmd)
+-			memcpy(pmd,
+-			       (void *)pgd_page_vaddr(swapper_pg_dir[idx]),
 +		if (!pmd) {
 +			pgd_mop_up_pmds(pgd);
 +			return 0;
@@ -324510,14 +328291,14 @@
  
 -	if (PTRS_PER_PMD == 1 || !pgd)
 -		return pgd;
-+	mm->pgd = pgd;		/* so that alloc_pd can use it */
- 
+-
 - 	for (i = 0; i < UNSHARED_PTRS_PER_PGD; ++i) {
 -		pmd_t *pmd = pmd_cache_alloc(i);
 -
 -		if (!pmd)
 -			goto out_oom;
--
++	mm->pgd = pgd;		/* so that alloc_pd can use it */
+ 
 -		paravirt_alloc_pd(__pa(pmd) >> PAGE_SHIFT);
 -		set_pgd(&pgd[i], __pgd(1 + __pa(pmd)));
 +	if (pgd && !pgd_prepopulate_pmd(mm, pgd)) {
@@ -324555,11 +328336,32 @@
  	quicklist_free(0, pgd_dtor, pgd);
  }
  
-@@ -372,4 +376,3 @@ void check_pgt_cache(void)
- {
+@@ -373,3 +377,25 @@ void check_pgt_cache(void)
  	quicklist_trim(0, pgd_dtor, 25, 16);
  }
--
+ 
++void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte)
++{
++	paravirt_release_pt(page_to_pfn(pte));
++	tlb_remove_page(tlb, pte);
++}
++
++#ifdef CONFIG_X86_PAE
++
++void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
++{
++	/* This is called just after the pmd has been detached from
++	   the pgd, which requires a full tlb flush to be recognized
++	   by the CPU.  Rather than incurring multiple tlb flushes
++	   while the address space is being pulled down, make the tlb
++	   gathering machinery do a full flush when we're done. */
++	tlb->fullmm = 1;
++
++	paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT);
++	tlb_remove_page(tlb, virt_to_page(pmd));
++}
++
++#endif
 diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c
 index ea85172..65416f8 100644
 --- a/arch/x86/mm/srat_64.c
@@ -400952,6 +404754,18 @@
  				CCNT(timer1);
  				/* Wait after HSync */
  				CCNT(timer2);
+diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
+index 7549939..986a836 100644
+--- a/drivers/input/touchscreen/ucb1400_ts.c
++++ b/drivers/input/touchscreen/ucb1400_ts.c
+@@ -27,7 +27,6 @@
+ #include <linux/kthread.h>
+ #include <linux/freezer.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/ac97_codec.h>
+ 
 diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
 index f449dae..23ae66c 100644
 --- a/drivers/isdn/capi/capi.c
@@ -418565,7 +422379,7 @@
  /*:*/
  
 diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
-index 482aec2..61f2f8e 100644
+index 482aec2..6351878 100644
 --- a/drivers/lguest/x86/core.c
 +++ b/drivers/lguest/x86/core.c
 @@ -60,7 +60,7 @@ static struct lguest_pages *lguest_pages(unsigned int cpu)
@@ -418610,7 +422424,7 @@
  	 * level 1). */
 -	pages->state.guest_tss.esp1 = lg->esp1;
 -	pages->state.guest_tss.ss1 = lg->ss1;
-+	pages->state.guest_tss.esp1 = cpu->esp1;
++	pages->state.guest_tss.sp1 = cpu->esp1;
 +	pages->state.guest_tss.ss1 = cpu->ss1;
  
  	/* Copy direct-to-Guest trap entries. */
@@ -441876,10 +445690,18 @@
  	  This is a video4linux driver for Conexant 2388x based
  	  TV cards.
 diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
-index 40ffd7a..8735227 100644
+index 40ffd7a..316b106 100644
 --- a/drivers/media/video/cx88/cx88-alsa.c
 +++ b/drivers/media/video/cx88/cx88-alsa.c
-@@ -417,7 +417,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
+@@ -33,7 +33,6 @@
+ #include <linux/pci.h>
+ 
+ #include <asm/delay.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -417,7 +416,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
  	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
  	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
  
@@ -457079,10 +460901,18 @@
  obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
  
 diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
-index 4878f30..ba25310 100644
+index 4878f30..047add8 100644
 --- a/drivers/media/video/saa7134/saa7134-alsa.c
 +++ b/drivers/media/video/saa7134/saa7134-alsa.c
-@@ -1077,24 +1077,14 @@ static int saa7134_alsa_init(void)
+@@ -21,7 +21,6 @@
+ #include <linux/time.h>
+ #include <linux/wait.h>
+ #include <linux/module.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/pcm.h>
+@@ -1077,24 +1076,14 @@ static int saa7134_alsa_init(void)
  	struct saa7134_dev *dev = NULL;
  	struct list_head *list;
  
@@ -461593,10 +465423,18 @@
  	saa7134_buffer_next(dev,&dev->video_q);
  
 diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
-index 66a390c..ce45030 100644
+index 66a390c..b88ca99 100644
 --- a/drivers/media/video/saa7134/saa7134.h
 +++ b/drivers/media/video/saa7134/saa7134.h
-@@ -240,6 +240,19 @@ struct saa7134_format {
+@@ -38,7 +38,6 @@
+ #include <media/ir-common.h>
+ #include <media/ir-kbd-i2c.h>
+ #include <media/videobuf-dma-sg.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+@@ -240,6 +239,19 @@ struct saa7134_format {
  #define SAA7134_BOARD_SABRENT_TV_PCB05     115
  #define SAA7134_BOARD_10MOONSTVMASTER3     116
  #define SAA7134_BOARD_AVERMEDIA_SUPER_007  117
@@ -461616,7 +465454,7 @@
  
  #define SAA7134_MAXBOARDS 8
  #define SAA7134_INPUT_MAX 8
-@@ -481,7 +494,7 @@ struct saa7134_dev {
+@@ -481,7 +493,7 @@ struct saa7134_dev {
  	/* i2c i/o */
  	struct i2c_adapter         i2c_adap;
  	struct i2c_client          i2c_client;
@@ -461625,7 +465463,7 @@
  
  	/* video overlay */
  	struct v4l2_framebuffer    ovbuf;
-@@ -566,6 +579,12 @@ struct saa7134_dev {
+@@ -566,6 +578,12 @@ struct saa7134_dev {
  
  #define saa_wait(us) { udelay(us); }
  
@@ -461638,7 +465476,7 @@
  /* ----------------------------------------------------------- */
  /* saa7134-core.c                                              */
  
-@@ -596,9 +615,6 @@ void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
+@@ -596,9 +614,6 @@ void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
  void saa7134_buffer_timeout(unsigned long data);
  void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
  
@@ -461648,7 +465486,7 @@
  int saa7134_set_dmabits(struct saa7134_dev *dev);
  
  extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
-@@ -628,16 +644,17 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev,
+@@ -628,16 +643,17 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev,
  /* ----------------------------------------------------------- */
  /* saa7134-video.c                                             */
  
@@ -461670,7 +465508,7 @@
  int saa7134_video_init1(struct saa7134_dev *dev);
  int saa7134_video_init2(struct saa7134_dev *dev);
  void saa7134_irq_video_signalchange(struct saa7134_dev *dev);
-@@ -682,6 +699,7 @@ void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+@@ -682,6 +698,7 @@ void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
  void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level);
  int saa7134_tvaudio_getstereo(struct saa7134_dev *dev);
  
@@ -705181,6 +709019,18 @@
  	select USB_GADGET_SELECTED
  	help
  	   Many Atmel AT91 processors (such as the AT91RM2000) have a
+diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
+index 0689189..7da7fcb 100644
+--- a/drivers/usb/gadget/gmidi.c
++++ b/drivers/usb/gadget/gmidi.c
+@@ -24,7 +24,6 @@
+ #include <linux/utsname.h>
+ #include <linux/device.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/initval.h>
+ #include <sound/rawmidi.h>
 diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
 index ecfe800..ddd4ee1 100644
 --- a/drivers/usb/host/ohci-hcd.c
@@ -712165,6 +716015,49 @@
 +	netlink_kernel_release(ecryptfs_nl_sock);
  	ecryptfs_nl_sock = NULL;
  }
+diff --git a/fs/eventpoll.c b/fs/eventpoll.c
+index 34f68f3..81c04ab 100644
+--- a/fs/eventpoll.c
++++ b/fs/eventpoll.c
+@@ -656,8 +656,7 @@ is_linked:
+ 	 * wait list.
+ 	 */
+ 	if (waitqueue_active(&ep->wq))
+-		__wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE |
+-				 TASK_INTERRUPTIBLE);
++		wake_up_locked(&ep->wq);
+ 	if (waitqueue_active(&ep->poll_wait))
+ 		pwake++;
+ 
+@@ -780,7 +779,7 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
+ 
+ 		/* Notify waiting tasks that events are available */
+ 		if (waitqueue_active(&ep->wq))
+-			__wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE);
++			wake_up_locked(&ep->wq);
+ 		if (waitqueue_active(&ep->poll_wait))
+ 			pwake++;
+ 	}
+@@ -854,8 +853,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi, struct epoll_even
+ 
+ 			/* Notify waiting tasks that events are available */
+ 			if (waitqueue_active(&ep->wq))
+-				__wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE |
+-						 TASK_INTERRUPTIBLE);
++				wake_up_locked(&ep->wq);
+ 			if (waitqueue_active(&ep->poll_wait))
+ 				pwake++;
+ 		}
+@@ -978,8 +976,7 @@ errxit:
+ 		 * wait list (delayed after we release the lock).
+ 		 */
+ 		if (waitqueue_active(&ep->wq))
+-			__wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE |
+-					 TASK_INTERRUPTIBLE);
++			wake_up_locked(&ep->wq);
+ 		if (waitqueue_active(&ep->poll_wait))
+ 			pwake++;
+ 	}
 diff --git a/fs/ext2/super.c b/fs/ext2/super.c
 index 154e25f..6abaf75 100644
 --- a/fs/ext2/super.c
@@ -727822,7 +731715,7 @@
  	if (unlikely(status != 0))
  		goto out;
 diff --git a/fs/nfs/client.c b/fs/nfs/client.c
-index a6f6254..685c43f 100644
+index a6f6254..c5c0175 100644
 --- a/fs/nfs/client.c
 +++ b/fs/nfs/client.c
 @@ -34,6 +34,8 @@
@@ -728089,6 +731982,15 @@
  	} while (new);
  
  	return ERR_PTR(-ENOMEM);
+@@ -302,7 +386,7 @@ found_client:
+ 	if (new)
+ 		nfs_free_client(new);
+ 
+-	error = wait_event_interruptible(nfs_client_active_wq,
++	error = wait_event_killable(nfs_client_active_wq,
+ 				clp->cl_cons_state != NFS_CS_INITING);
+ 	if (error < 0) {
+ 		nfs_put_client(clp);
 @@ -344,12 +428,16 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
  	switch (proto) {
  	case XPRT_TRANSPORT_TCP:
@@ -728228,7 +732130,18 @@
  	if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
  		struct rpc_auth *auth;
  
-@@ -502,6 +600,7 @@ static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t
+@@ -491,10 +589,6 @@ static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t
+ 	if (server->flags & NFS_MOUNT_SOFT)
+ 		server->client->cl_softrtry = 1;
+ 
+-	server->client->cl_intr = 0;
+-	if (server->flags & NFS4_MOUNT_INTR)
+-		server->client->cl_intr = 1;
+-
+ 	return 0;
+ }
+ 
+@@ -502,6 +596,7 @@ static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t
   * Initialise an NFS2 or NFS3 client
   */
  static int nfs_init_client(struct nfs_client *clp,
@@ -728236,7 +732149,7 @@
  			   const struct nfs_parsed_mount_data *data)
  {
  	int error;
-@@ -512,18 +611,11 @@ static int nfs_init_client(struct nfs_client *clp,
+@@ -512,18 +607,11 @@ static int nfs_init_client(struct nfs_client *clp,
  		return 0;
  	}
  
@@ -728256,7 +732169,7 @@
  	if (error < 0)
  		goto error;
  	nfs_mark_client_ready(clp, NFS_CS_READY);
-@@ -541,25 +633,34 @@ error:
+@@ -541,25 +629,34 @@ error:
  static int nfs_init_server(struct nfs_server *server,
  			   const struct nfs_parsed_mount_data *data)
  {
@@ -728296,7 +732209,7 @@
  	if (error < 0)
  		goto error;
  
-@@ -583,7 +684,7 @@ static int nfs_init_server(struct nfs_server *server,
+@@ -583,7 +680,7 @@ static int nfs_init_server(struct nfs_server *server,
  	if (error < 0)
  		goto error;
  
@@ -728305,7 +732218,7 @@
  	if (error < 0)
  		goto error;
  
-@@ -729,6 +830,9 @@ static struct nfs_server *nfs_alloc_server(void)
+@@ -729,6 +826,9 @@ static struct nfs_server *nfs_alloc_server(void)
  	INIT_LIST_HEAD(&server->client_link);
  	INIT_LIST_HEAD(&server->master_link);
  
@@ -728315,7 +732228,7 @@
  	server->io_stats = nfs_alloc_iostats();
  	if (!server->io_stats) {
  		kfree(server);
-@@ -840,7 +944,7 @@ error:
+@@ -840,7 +940,7 @@ error:
   * Initialise an NFS4 client record
   */
  static int nfs4_init_client(struct nfs_client *clp,
@@ -728324,7 +732237,7 @@
  		const char *ip_addr,
  		rpc_authflavor_t authflavour)
  {
-@@ -855,7 +959,7 @@ static int nfs4_init_client(struct nfs_client *clp,
+@@ -855,7 +955,7 @@ static int nfs4_init_client(struct nfs_client *clp,
  	/* Check NFS protocol revision and initialize RPC op vector */
  	clp->rpc_ops = &nfs_v4_clientops;
  
@@ -728333,7 +732246,7 @@
  					RPC_CLNT_CREATE_DISCRTRY);
  	if (error < 0)
  		goto error;
-@@ -882,23 +986,32 @@ error:
+@@ -882,23 +982,32 @@ error:
   * Set up an NFS4 client
   */
  static int nfs4_set_client(struct nfs_server *server,
@@ -728370,7 +732283,7 @@
  	if (error < 0)
  		goto error_put;
  
-@@ -919,10 +1032,26 @@ error:
+@@ -919,10 +1028,26 @@ error:
  static int nfs4_init_server(struct nfs_server *server,
  		const struct nfs_parsed_mount_data *data)
  {
@@ -728397,7 +732310,7 @@
  	/* Initialise the client representation from the mount data */
  	server->flags = data->flags & NFS_MOUNT_FLAGMASK;
  	server->caps |= NFS_CAP_ATOMIC_OPEN;
-@@ -937,8 +1066,9 @@ static int nfs4_init_server(struct nfs_server *server,
+@@ -937,8 +1062,9 @@ static int nfs4_init_server(struct nfs_server *server,
  	server->acdirmin = data->acdirmin * HZ;
  	server->acdirmax = data->acdirmax * HZ;
  
@@ -728408,7 +732321,7 @@
  	/* Done */
  	dprintk("<-- nfs4_init_server() = %d\n", error);
  	return error;
-@@ -961,17 +1091,6 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
+@@ -961,17 +1087,6 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
  	if (!server)
  		return ERR_PTR(-ENOMEM);
  
@@ -728426,7 +732339,7 @@
  	/* set up the general RPC client */
  	error = nfs4_init_server(server, data);
  	if (error < 0)
-@@ -1039,12 +1158,13 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+@@ -1039,12 +1154,13 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
  
  	/* Get a client representation.
  	 * Note: NFSv4 always uses TCP, */
@@ -728446,7 +732359,7 @@
  	if (error < 0)
  		goto error;
  
-@@ -1052,7 +1172,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+@@ -1052,7 +1168,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
  	nfs_server_copy_userdata(server, parent_server);
  	server->caps |= NFS_CAP_ATOMIC_OPEN;
  
@@ -728455,7 +732368,7 @@
  	if (error < 0)
  		goto error;
  
-@@ -1121,7 +1241,9 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
+@@ -1121,7 +1237,9 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
  
  	server->fsid = fattr->fsid;
  
@@ -728466,7 +732379,7 @@
  	if (error < 0)
  		goto out_free_server;
  	if (!IS_ERR(source->client_acl))
-@@ -1263,10 +1385,10 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
+@@ -1263,10 +1381,10 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
  	/* display one transport per line on subsequent lines */
  	clp = list_entry(v, struct nfs_client, cl_share_link);
  
@@ -728481,7 +732394,7 @@
  		   atomic_read(&clp->cl_count),
  		   clp->cl_hostname);
  
-@@ -1342,10 +1464,10 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
+@@ -1342,10 +1460,10 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
  		 (unsigned long long) server->fsid.major,
  		 (unsigned long long) server->fsid.minor);
  
@@ -728885,28 +732798,19 @@
  		list_add_tail(&NFS_I(inode)->access_cache_inode_lru, &nfs_access_lru_list);
  		spin_unlock(&nfs_access_lru_lock);
 diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
-index 3c9d16b..f8e165c 100644
+index 3c9d16b..16844f9 100644
 --- a/fs/nfs/direct.c
 +++ b/fs/nfs/direct.c
-@@ -188,12 +188,17 @@ static void nfs_direct_req_release(struct nfs_direct_req *dreq)
- static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
- {
- 	ssize_t result = -EIOCBQUEUED;
-+	struct rpc_clnt *clnt;
-+	sigset_t oldset;
- 
- 	/* Async requests don't wait here */
+@@ -193,7 +193,7 @@ static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
  	if (dreq->iocb)
  		goto out;
  
-+	clnt = NFS_CLIENT(dreq->inode);
-+	rpc_clnt_sigmask(clnt, &oldset);
- 	result = wait_for_completion_interruptible(&dreq->completion);
-+	rpc_clnt_sigunmask(clnt, &oldset);
+-	result = wait_for_completion_interruptible(&dreq->completion);
++	result = wait_for_completion_killable(&dreq->completion);
  
  	if (!result)
  		result = dreq->error;
-@@ -272,6 +277,16 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
+@@ -272,6 +272,16 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
  	unsigned long user_addr = (unsigned long)iov->iov_base;
  	size_t count = iov->iov_len;
  	size_t rsize = NFS_SERVER(inode)->rsize;
@@ -728923,7 +732827,7 @@
  	unsigned int pgbase;
  	int result;
  	ssize_t started = 0;
-@@ -311,7 +326,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
+@@ -311,7 +321,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
  
  		data->req = (struct nfs_page *) dreq;
  		data->inode = inode;
@@ -728932,7 +732836,7 @@
  		data->args.fh = NFS_FH(inode);
  		data->args.context = ctx;
  		data->args.offset = pos;
-@@ -321,14 +336,16 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
+@@ -321,14 +331,16 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
  		data->res.fattr = &data->fattr;
  		data->res.eof = 0;
  		data->res.count = bytes;
@@ -728942,12 +732846,12 @@
 -		rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC,
 -				&nfs_read_direct_ops, data);
 -		NFS_PROTO(inode)->read_setup(data);
--
--		data->task.tk_cookie = (unsigned long) inode;
 +		task_setup_data.task = &data->task;
 +		task_setup_data.callback_data = data;
 +		NFS_PROTO(inode)->read_setup(data, &msg);
  
+-		data->task.tk_cookie = (unsigned long) inode;
+-
 -		rpc_execute(&data->task);
 +		task = rpc_run_task(&task_setup_data);
 +		if (!IS_ERR(task))
@@ -728955,7 +732859,7 @@
  
  		dprintk("NFS: %5u initiated direct read call "
  			"(req %s/%Ld, %zu bytes @ offset %Lu)\n",
-@@ -391,9 +408,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
+@@ -391,9 +403,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
  			       unsigned long nr_segs, loff_t pos)
  {
  	ssize_t result = 0;
@@ -728965,7 +732869,7 @@
  	struct nfs_direct_req *dreq;
  
  	dreq = nfs_direct_req_alloc();
-@@ -405,11 +420,9 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
+@@ -405,11 +415,9 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
  	if (!is_sync_kiocb(iocb))
  		dreq->iocb = iocb;
  
@@ -728977,7 +732881,7 @@
  	nfs_direct_req_release(dreq);
  
  	return result;
-@@ -431,6 +444,15 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
+@@ -431,6 +439,15 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
  	struct inode *inode = dreq->inode;
  	struct list_head *p;
  	struct nfs_write_data *data;
@@ -728993,7 +732897,7 @@
  
  	dreq->count = 0;
  	get_dreq(dreq);
-@@ -440,6 +462,9 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
+@@ -440,6 +457,9 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
  
  		get_dreq(dreq);
  
@@ -729003,7 +732907,7 @@
  		/*
  		 * Reset data->res.
  		 */
-@@ -451,17 +476,18 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
+@@ -451,17 +471,18 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
  		 * Reuse data->task; data->args should not have changed
  		 * since the original request was sent.
  		 */
@@ -729029,7 +732933,7 @@
  
  		dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n",
  				data->task.tk_pid,
-@@ -504,9 +530,23 @@ static const struct rpc_call_ops nfs_commit_direct_ops = {
+@@ -504,9 +525,23 @@ static const struct rpc_call_ops nfs_commit_direct_ops = {
  static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
  {
  	struct nfs_write_data *data = dreq->commit_data;
@@ -729054,7 +732958,7 @@
  
  	data->args.fh = NFS_FH(data->inode);
  	data->args.offset = 0;
-@@ -515,18 +555,16 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
+@@ -515,18 +550,16 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
  	data->res.fattr = &data->fattr;
  	data->res.verf = &data->verf;
  
@@ -729077,7 +732981,7 @@
  }
  
  static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
-@@ -641,6 +679,16 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
+@@ -641,6 +674,16 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
  	struct inode *inode = ctx->path.dentry->d_inode;
  	unsigned long user_addr = (unsigned long)iov->iov_base;
  	size_t count = iov->iov_len;
@@ -729094,7 +732998,7 @@
  	size_t wsize = NFS_SERVER(inode)->wsize;
  	unsigned int pgbase;
  	int result;
-@@ -683,25 +731,27 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
+@@ -683,25 +726,27 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
  
  		data->req = (struct nfs_page *) dreq;
  		data->inode = inode;
@@ -729130,7 +733034,7 @@
  
  		dprintk("NFS: %5u initiated direct write call "
  			"(req %s/%Ld, %zu bytes @ offset %Lu)\n",
-@@ -767,12 +817,10 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
+@@ -767,12 +812,10 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
  				size_t count)
  {
  	ssize_t result = 0;
@@ -729144,7 +733048,7 @@
  
  	dreq = nfs_direct_req_alloc();
  	if (!dreq)
-@@ -780,18 +828,16 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
+@@ -780,18 +823,16 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
  	nfs_alloc_commit_data(dreq);
  
  	if (dreq->commit_data == NULL || count < wsize)
@@ -729410,7 +733314,7 @@
  
  int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid)
 diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
-index db5d96d..3f332e5 100644
+index db5d96d..966a885 100644
 --- a/fs/nfs/inode.c
 +++ b/fs/nfs/inode.c
 @@ -192,7 +192,7 @@ void nfs_invalidate_atime(struct inode *inode)
@@ -729440,7 +733344,24 @@
  			/* Deal with crossing mountpoints */
  			if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
  				if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
-@@ -461,9 +461,18 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+@@ -433,15 +433,11 @@ static int nfs_wait_schedule(void *word)
+  */
+ static int nfs_wait_on_inode(struct inode *inode)
+ {
+-	struct rpc_clnt	*clnt = NFS_CLIENT(inode);
+ 	struct nfs_inode *nfsi = NFS_I(inode);
+-	sigset_t oldmask;
+ 	int error;
+ 
+-	rpc_clnt_sigmask(clnt, &oldmask);
+ 	error = wait_on_bit_lock(&nfsi->flags, NFS_INO_REVALIDATING,
+-					nfs_wait_schedule, TASK_INTERRUPTIBLE);
+-	rpc_clnt_sigunmask(clnt, &oldmask);
++					nfs_wait_schedule, TASK_KILLABLE);
+ 
+ 	return error;
+ }
+@@ -461,9 +457,18 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
  	int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
  	int err;
  
@@ -729461,7 +733382,7 @@
  
  	/*
  	 * We may force a getattr if the user cares about atime.
-@@ -659,7 +668,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+@@ -659,7 +664,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
  		if (status == -ESTALE) {
  			nfs_zap_caches(inode);
  			if (!S_ISDIR(inode->i_mode))
@@ -729470,7 +733391,7 @@
  		}
  		goto out;
  	}
-@@ -814,8 +823,9 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+@@ -814,8 +819,9 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  			if (S_ISDIR(inode->i_mode))
  				nfsi->cache_validity |= NFS_INO_INVALID_DATA;
  		}
@@ -729482,7 +733403,7 @@
  	}
  }
  
-@@ -1019,7 +1029,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+@@ -1019,7 +1025,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  			dprintk("NFS: mtime change on server for file %s/%ld\n",
  					inode->i_sb->s_id, inode->i_ino);
  			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
@@ -729492,7 +733413,7 @@
  		}
  		/* If ctime has changed we should definitely clear access+acl caches */
  		if (!timespec_equal(&inode->i_ctime, &fattr->ctime))
-@@ -1028,7 +1039,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+@@ -1028,7 +1035,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  		dprintk("NFS: change_attr change on server for file %s/%ld\n",
  				inode->i_sb->s_id, inode->i_ino);
  		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
@@ -729502,7 +733423,7 @@
  	}
  
  	/* Check if our cached file size is stale */
-@@ -1133,7 +1145,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+@@ -1133,7 +1141,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  void nfs4_clear_inode(struct inode *inode)
  {
  	/* If we are holding a delegation, return it! */
@@ -729568,6 +733489,19 @@
  
  /* namespace.c */
  extern char *nfs_path(const char *base,
+diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
+index 8afd9f7..49c7cd0 100644
+--- a/fs/nfs/mount_clnt.c
++++ b/fs/nfs/mount_clnt.c
+@@ -56,7 +56,7 @@ int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path,
+ 		.program	= &mnt_program,
+ 		.version	= version,
+ 		.authflavor	= RPC_AUTH_UNIX,
+-		.flags		= RPC_CLNT_CREATE_INTR,
++		.flags		= 0,
+ 	};
+ 	struct rpc_clnt		*mnt_clnt;
+ 	int			status;
 diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
 index acfc56f..be4ce1c 100644
 --- a/fs/nfs/namespace.c
@@ -729669,10 +733603,30 @@
  	} else if (iov->iov_len != hdrlen) {
  		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
 diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
-index 4cdc236..b353c1a 100644
+index 4cdc236..549dbce 100644
 --- a/fs/nfs/nfs3proc.c
 +++ b/fs/nfs/nfs3proc.c
-@@ -732,16 +732,9 @@ static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data)
+@@ -27,17 +27,14 @@
+ static int
+ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
+ {
+-	sigset_t oldset;
+ 	int res;
+-	rpc_clnt_sigmask(clnt, &oldset);
+ 	do {
+ 		res = rpc_call_sync(clnt, msg, flags);
+ 		if (res != -EJUKEBOX)
+ 			break;
+-		schedule_timeout_interruptible(NFS_JUKEBOX_RETRY_TIME);
++		schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
+ 		res = -ERESTARTSYS;
+-	} while (!signalled());
+-	rpc_clnt_sigunmask(clnt, &oldset);
++	} while (!fatal_signal_pending(current));
+ 	return res;
+ }
+ 
+@@ -732,16 +729,9 @@ static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data)
  	return 0;
  }
  
@@ -729691,7 +733645,7 @@
  }
  
  static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data)
-@@ -753,24 +746,9 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data)
+@@ -753,24 +743,9 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data)
  	return 0;
  }
  
@@ -729718,7 +733672,7 @@
  }
  
  static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data)
-@@ -781,22 +759,17 @@ static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+@@ -781,22 +756,17 @@ static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data)
  	return 0;
  }
  
@@ -729908,7 +733862,7 @@
  			snprintf(page, PAGE_SIZE, "%s:%s",
  					mountdata.hostname,
 diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
-index 9e2e1c7..5c189bd 100644
+index 9e2e1c7..027e109 100644
 --- a/fs/nfs/nfs4proc.c
 +++ b/fs/nfs/nfs4proc.c
 @@ -210,7 +210,7 @@ static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
@@ -729920,7 +733874,20 @@
  	nfsi->change_attr = cinfo->after;
  	spin_unlock(&dir->i_lock);
  }
-@@ -718,19 +718,6 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
+@@ -316,12 +316,9 @@ static void nfs4_opendata_put(struct nfs4_opendata *p)
+ 
+ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
+ {
+-	sigset_t oldset;
+ 	int ret;
+ 
+-	rpc_clnt_sigmask(task->tk_client, &oldset);
+ 	ret = rpc_wait_for_completion_task(task);
+-	rpc_clnt_sigunmask(task->tk_client, &oldset);
+ 	return ret;
+ }
+ 
+@@ -718,19 +715,6 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
  	return err;
  }
  
@@ -729940,7 +733907,7 @@
  static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
  {
  	struct nfs4_opendata *data = calldata;
-@@ -767,7 +754,6 @@ out_free:
+@@ -767,7 +751,6 @@ out_free:
  }
  
  static const struct rpc_call_ops nfs4_open_confirm_ops = {
@@ -729948,7 +733915,7 @@
  	.rpc_call_done = nfs4_open_confirm_done,
  	.rpc_release = nfs4_open_confirm_release,
  };
-@@ -779,12 +765,26 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
+@@ -779,12 +762,26 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
  {
  	struct nfs_server *server = NFS_SERVER(data->dir->d_inode);
  	struct rpc_task *task;
@@ -729976,7 +733943,7 @@
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	status = nfs4_wait_for_completion_rpc_task(task);
-@@ -801,13 +801,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
+@@ -801,13 +798,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
  {
  	struct nfs4_opendata *data = calldata;
  	struct nfs4_state_owner *sp = data->owner;
@@ -729991,7 +733958,7 @@
  	if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
  		return;
  	/*
-@@ -832,11 +826,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
+@@ -832,11 +823,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
  	data->o_arg.id = sp->so_owner_id.id;
  	data->o_arg.clientid = sp->so_client->cl_clientid;
  	if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) {
@@ -730005,7 +733972,7 @@
  	return;
  out_no_action:
  	task->tk_action = NULL;
-@@ -908,13 +902,26 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
+@@ -908,13 +899,26 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
  	struct nfs_openargs *o_arg = &data->o_arg;
  	struct nfs_openres *o_res = &data->o_res;
  	struct rpc_task *task;
@@ -730033,7 +734000,7 @@
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	status = nfs4_wait_for_completion_rpc_task(task);
-@@ -1244,12 +1251,6 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
+@@ -1244,12 +1248,6 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
  {
  	struct nfs4_closedata *calldata = data;
  	struct nfs4_state *state = calldata->state;
@@ -730046,7 +734013,7 @@
  	int clear_rd, clear_wr, clear_rdwr;
  
  	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
-@@ -1276,14 +1277,14 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
+@@ -1276,14 +1274,14 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
  	}
  	nfs_fattr_init(calldata->res.fattr);
  	if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) {
@@ -730064,7 +734031,7 @@
  }
  
  static const struct rpc_call_ops nfs4_close_ops = {
-@@ -1309,6 +1310,16 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
+@@ -1309,6 +1307,16 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
  	struct nfs4_closedata *calldata;
  	struct nfs4_state_owner *sp = state->owner;
  	struct rpc_task *task;
@@ -730081,7 +734048,7 @@
  	int status = -ENOMEM;
  
  	calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
-@@ -1328,7 +1339,10 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
+@@ -1328,7 +1336,10 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
  	calldata->path.mnt = mntget(path->mnt);
  	calldata->path.dentry = dget(path->dentry);
  
@@ -730093,7 +734060,7 @@
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	status = 0;
-@@ -2414,18 +2428,10 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
+@@ -2414,18 +2425,10 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
  	return 0;
  }
  
@@ -730114,7 +734081,7 @@
  }
  
  static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
-@@ -2443,33 +2449,15 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
+@@ -2443,33 +2446,15 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
  	return 0;
  }
  
@@ -730152,7 +734119,7 @@
  }
  
  static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
-@@ -2484,20 +2472,13 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+@@ -2484,20 +2469,13 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
  	return 0;
  }
  
@@ -730175,7 +734142,65 @@
  }
  
  /*
-@@ -2910,14 +2891,20 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po
+@@ -2804,9 +2782,9 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
+ 	return 0;
+ }
+ 
+-static int nfs4_wait_bit_interruptible(void *word)
++static int nfs4_wait_bit_killable(void *word)
+ {
+-	if (signal_pending(current))
++	if (fatal_signal_pending(current))
+ 		return -ERESTARTSYS;
+ 	schedule();
+ 	return 0;
+@@ -2814,18 +2792,14 @@ static int nfs4_wait_bit_interruptible(void *word)
+ 
+ static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
+ {
+-	sigset_t oldset;
+ 	int res;
+ 
+ 	might_sleep();
+ 
+ 	rwsem_acquire(&clp->cl_sem.dep_map, 0, 0, _RET_IP_);
+ 
+-	rpc_clnt_sigmask(clnt, &oldset);
+ 	res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
+-			nfs4_wait_bit_interruptible,
+-			TASK_INTERRUPTIBLE);
+-	rpc_clnt_sigunmask(clnt, &oldset);
++			nfs4_wait_bit_killable, TASK_KILLABLE);
+ 
+ 	rwsem_release(&clp->cl_sem.dep_map, 1, _RET_IP_);
+ 	return res;
+@@ -2833,7 +2807,6 @@ static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
+ 
+ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
+ {
+-	sigset_t oldset;
+ 	int res = 0;
+ 
+ 	might_sleep();
+@@ -2842,14 +2815,9 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
+ 		*timeout = NFS4_POLL_RETRY_MIN;
+ 	if (*timeout > NFS4_POLL_RETRY_MAX)
+ 		*timeout = NFS4_POLL_RETRY_MAX;
+-	rpc_clnt_sigmask(clnt, &oldset);
+-	if (clnt->cl_intr) {
+-		schedule_timeout_interruptible(*timeout);
+-		if (signalled())
+-			res = -ERESTARTSYS;
+-	} else
+-		schedule_timeout_uninterruptible(*timeout);
+-	rpc_clnt_sigunmask(clnt, &oldset);
++	schedule_timeout_killable(*timeout);
++	if (fatal_signal_pending(current))
++		res = -ERESTARTSYS;
+ 	*timeout <<= 1;
+ 	return res;
+ }
+@@ -2910,14 +2878,20 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po
  
  	for(;;) {
  		setclientid.sc_name_len = scnprintf(setclientid.sc_name,
@@ -730200,7 +734225,7 @@
  				clp->cl_ipaddr, port >> 8, port & 255);
  
  		status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
-@@ -2981,25 +2968,11 @@ struct nfs4_delegreturndata {
+@@ -2981,25 +2955,11 @@ struct nfs4_delegreturndata {
  	struct nfs4_delegreturnres res;
  	struct nfs_fh fh;
  	nfs4_stateid stateid;
@@ -730226,7 +734251,7 @@
  static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
  {
  	struct nfs4_delegreturndata *data = calldata;
-@@ -3010,24 +2983,30 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
+@@ -3010,24 +2970,30 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
  
  static void nfs4_delegreturn_release(void *calldata)
  {
@@ -730263,7 +734288,7 @@
  
  	data = kmalloc(sizeof(*data), GFP_KERNEL);
  	if (data == NULL)
-@@ -3039,30 +3018,37 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
+@@ -3039,30 +3005,37 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
  	memcpy(&data->stateid, stateid, sizeof(data->stateid));
  	data->res.fattr = &data->fattr;
  	data->res.server = server;
@@ -730310,7 +734335,16 @@
  		switch (err) {
  			case -NFS4ERR_STALE_STATEID:
  			case -NFS4ERR_EXPIRED:
-@@ -3230,12 +3216,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
+@@ -3083,7 +3056,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
+ static unsigned long
+ nfs4_set_lock_task_retry(unsigned long timeout)
+ {
+-	schedule_timeout_interruptible(timeout);
++	schedule_timeout_killable(timeout);
+ 	timeout <<= 1;
+ 	if (timeout > NFS4_LOCK_MAXTIMEOUT)
+ 		return NFS4_LOCK_MAXTIMEOUT;
+@@ -3230,12 +3203,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
  static void nfs4_locku_prepare(struct rpc_task *task, void *data)
  {
  	struct nfs4_unlockdata *calldata = data;
@@ -730323,7 +734357,7 @@
  
  	if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
  		return;
-@@ -3245,7 +3225,7 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
+@@ -3245,7 +3212,7 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
  		return;
  	}
  	calldata->timestamp = jiffies;
@@ -730332,7 +734366,7 @@
  }
  
  static const struct rpc_call_ops nfs4_locku_ops = {
-@@ -3260,6 +3240,16 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
+@@ -3260,6 +3227,16 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
  		struct nfs_seqid *seqid)
  {
  	struct nfs4_unlockdata *data;
@@ -730349,7 +734383,7 @@
  
  	/* Ensure this is an unlock - when canceling a lock, the
  	 * canceled lock is passed in, and it won't be an unlock.
-@@ -3272,7 +3262,10 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
+@@ -3272,7 +3249,10 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
  		return ERR_PTR(-ENOMEM);
  	}
  
@@ -730361,7 +734395,7 @@
  }
  
  static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
-@@ -3331,15 +3324,12 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
+@@ -3331,15 +3311,12 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
  
  	p->arg.fh = NFS_FH(inode);
  	p->arg.fl = &p->fl;
@@ -730381,7 +734415,7 @@
  	p->arg.lock_stateid = &lsp->ls_stateid;
  	p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
  	p->arg.lock_owner.id = lsp->ls_id.id;
-@@ -3348,9 +3338,9 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
+@@ -3348,9 +3325,9 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
  	p->ctx = get_nfs_open_context(ctx);
  	memcpy(&p->fl, fl, sizeof(p->fl));
  	return p;
@@ -730393,7 +734427,7 @@
  	kfree(p);
  	return NULL;
  }
-@@ -3359,31 +3349,20 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
+@@ -3359,31 +3336,20 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
  {
  	struct nfs4_lockdata *data = calldata;
  	struct nfs4_state *state = data->lsp->ls_state;
@@ -730430,7 +734464,7 @@
  	dprintk("%s: done!, ret = %d\n", __FUNCTION__, data->rpc_status);
  }
  
-@@ -3419,6 +3398,7 @@ static void nfs4_lock_release(void *calldata)
+@@ -3419,6 +3385,7 @@ static void nfs4_lock_release(void *calldata)
  	struct nfs4_lockdata *data = calldata;
  
  	dprintk("%s: begin!\n", __FUNCTION__);
@@ -730438,7 +734472,7 @@
  	if (data->cancelled != 0) {
  		struct rpc_task *task;
  		task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
-@@ -3428,8 +3408,6 @@ static void nfs4_lock_release(void *calldata)
+@@ -3428,8 +3395,6 @@ static void nfs4_lock_release(void *calldata)
  		dprintk("%s: cancelling lock!\n", __FUNCTION__);
  	} else
  		nfs_free_seqid(data->arg.lock_seqid);
@@ -730447,7 +734481,7 @@
  	nfs4_put_lock_state(data->lsp);
  	put_nfs_open_context(data->ctx);
  	kfree(data);
-@@ -3446,6 +3424,16 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
+@@ -3446,6 +3411,16 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
  {
  	struct nfs4_lockdata *data;
  	struct rpc_task *task;
@@ -730464,7 +734498,7 @@
  	int ret;
  
  	dprintk("%s: begin!\n", __FUNCTION__);
-@@ -3457,8 +3445,10 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
+@@ -3457,8 +3432,10 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
  		data->arg.block = 1;
  	if (reclaim != 0)
  		data->arg.reclaim = 1;
@@ -730477,7 +734511,7 @@
  	if (IS_ERR(task))
  		return PTR_ERR(task);
  	ret = nfs4_wait_for_completion_rpc_task(task);
-@@ -3631,10 +3621,6 @@ int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf,
+@@ -3631,10 +3608,6 @@ int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf,
  	if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
  		return -EOPNOTSUPP;
  
@@ -730692,11 +734726,43 @@
  
  		/* We ignore &savep and don't do consistency checks on
  		 * the attr length.  Let userspace figure it out.... */
+diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
+index 4b03345..531379d 100644
+--- a/fs/nfs/nfsroot.c
++++ b/fs/nfs/nfsroot.c
+@@ -228,10 +228,7 @@ static int __init root_nfs_parse(char *name, char *buf)
+ 				nfs_data.flags &= ~NFS_MOUNT_SOFT;
+ 				break;
+ 			case Opt_intr:
+-				nfs_data.flags |= NFS_MOUNT_INTR;
+-				break;
+ 			case Opt_nointr:
+-				nfs_data.flags &= ~NFS_MOUNT_INTR;
+ 				break;
+ 			case Opt_posix:
+ 				nfs_data.flags |= NFS_MOUNT_POSIX;
 diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
-index 345bb9b..3b3dbb9 100644
+index 345bb9b..7f07920 100644
 --- a/fs/nfs/pagelist.c
 +++ b/fs/nfs/pagelist.c
-@@ -111,13 +111,14 @@ void nfs_unlock_request(struct nfs_page *req)
+@@ -58,7 +58,6 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
+ 		   struct page *page,
+ 		   unsigned int offset, unsigned int count)
+ {
+-	struct nfs_server *server = NFS_SERVER(inode);
+ 	struct nfs_page		*req;
+ 
+ 	for (;;) {
+@@ -67,7 +66,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
+ 		if (req != NULL)
+ 			break;
+ 
+-		if (signalled() && (server->flags & NFS_MOUNT_INTR))
++		if (fatal_signal_pending(current))
+ 			return ERR_PTR(-ERESTARTSYS);
+ 		yield();
+ 	}
+@@ -111,13 +110,14 @@ void nfs_unlock_request(struct nfs_page *req)
   * nfs_set_page_tag_locked - Tag a request as locked
   * @req:
   */
@@ -730714,7 +734780,7 @@
  	return 1;
  }
  
-@@ -132,9 +133,10 @@ void nfs_clear_page_tag_locked(struct nfs_page *req)
+@@ -132,9 +132,10 @@ void nfs_clear_page_tag_locked(struct nfs_page *req)
  	if (req->wb_page != NULL) {
  		spin_lock(&inode->i_lock);
  		radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
@@ -730727,7 +734793,50 @@
  }
  
  /**
-@@ -421,6 +423,7 @@ int nfs_scan_list(struct nfs_inode *nfsi,
+@@ -175,11 +176,11 @@ void nfs_release_request(struct nfs_page *req)
+ 	kref_put(&req->wb_kref, nfs_free_request);
+ }
+ 
+-static int nfs_wait_bit_interruptible(void *word)
++static int nfs_wait_bit_killable(void *word)
+ {
+ 	int ret = 0;
+ 
+-	if (signal_pending(current))
++	if (fatal_signal_pending(current))
+ 		ret = -ERESTARTSYS;
+ 	else
+ 		schedule();
+@@ -190,26 +191,18 @@ static int nfs_wait_bit_interruptible(void *word)
+  * nfs_wait_on_request - Wait for a request to complete.
+  * @req: request to wait upon.
+  *
+- * Interruptible by signals only if mounted with intr flag.
++ * Interruptible by fatal signals only.
+  * The user is responsible for holding a count on the request.
+  */
+ int
+ nfs_wait_on_request(struct nfs_page *req)
+ {
+-	struct rpc_clnt *clnt = NFS_CLIENT(req->wb_context->path.dentry->d_inode);
+-	sigset_t oldmask;
+ 	int ret = 0;
+ 
+ 	if (!test_bit(PG_BUSY, &req->wb_flags))
+ 		goto out;
+-	/*
+-	 * Note: the call to rpc_clnt_sigmask() suffices to ensure that we
+-	 *	 are not interrupted if intr flag is not set
+-	 */
+-	rpc_clnt_sigmask(clnt, &oldmask);
+ 	ret = out_of_line_wait_on_bit(&req->wb_flags, PG_BUSY,
+-			nfs_wait_bit_interruptible, TASK_INTERRUPTIBLE);
+-	rpc_clnt_sigunmask(clnt, &oldmask);
++			nfs_wait_bit_killable, TASK_KILLABLE);
+ out:
+ 	return ret;
+ }
+@@ -421,6 +414,7 @@ int nfs_scan_list(struct nfs_inode *nfsi,
  				goto out;
  			idx_start = req->wb_index + 1;
  			if (nfs_set_page_tag_locked(req)) {
@@ -730903,7 +735012,7 @@
  	}
  	return 0;
 diff --git a/fs/nfs/super.c b/fs/nfs/super.c
-index 0b0c72a..22c49c0 100644
+index 0b0c72a..7f4505f 100644
 --- a/fs/nfs/super.c
 +++ b/fs/nfs/super.c
 @@ -45,6 +45,8 @@
@@ -730994,7 +735103,15 @@
  /*
   * Deliver file system statistics to userspace
   */
-@@ -455,8 +479,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
+@@ -424,7 +448,6 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
+ 		const char *nostr;
+ 	} nfs_info[] = {
+ 		{ NFS_MOUNT_SOFT, ",soft", ",hard" },
+-		{ NFS_MOUNT_INTR, ",intr", ",nointr" },
+ 		{ NFS_MOUNT_NOCTO, ",nocto", "" },
+ 		{ NFS_MOUNT_NOAC, ",noac", "" },
+ 		{ NFS_MOUNT_NONLM, ",nolock", "" },
+@@ -455,8 +478,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
  	}
  	seq_printf(m, ",proto=%s",
  		   rpc_peeraddr2str(nfss->client, RPC_DISPLAY_PROTO));
@@ -731005,7 +735122,7 @@
  	seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor));
  }
  
-@@ -469,8 +493,9 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+@@ -469,8 +492,9 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
  
  	nfs_show_mount_options(m, nfss, 0);
  
@@ -731017,7 +735134,7 @@
  
  	return 0;
  }
-@@ -507,7 +532,7 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
+@@ -507,7 +531,7 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
  	seq_printf(m, ",namelen=%d", nfss->namelen);
  
  #ifdef CONFIG_NFS_V4
@@ -731026,7 +735143,7 @@
  		seq_printf(m, "\n\tnfsv4:\t");
  		seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
  		seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
-@@ -575,16 +600,40 @@ static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
+@@ -575,16 +599,40 @@ static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
  }
  
  /*
@@ -731072,7 +735189,7 @@
  	}
  	}
  
-@@ -592,6 +641,40 @@ static int nfs_verify_server_address(struct sockaddr *addr)
+@@ -592,6 +640,40 @@ static int nfs_verify_server_address(struct sockaddr *addr)
  }
  
  /*
@@ -731113,7 +735230,7 @@
   * Error-check and convert a string of mount options from user space into
   * a data structure
   */
-@@ -599,6 +682,7 @@ static int nfs_parse_mount_options(char *raw,
+@@ -599,6 +681,7 @@ static int nfs_parse_mount_options(char *raw,
  				   struct nfs_parsed_mount_data *mnt)
  {
  	char *p, *string;
@@ -731121,7 +735238,18 @@
  
  	if (!raw) {
  		dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
-@@ -701,7 +785,7 @@ static int nfs_parse_mount_options(char *raw,
+@@ -624,10 +707,7 @@ static int nfs_parse_mount_options(char *raw,
+ 			mnt->flags &= ~NFS_MOUNT_SOFT;
+ 			break;
+ 		case Opt_intr:
+-			mnt->flags |= NFS_MOUNT_INTR;
+-			break;
+ 		case Opt_nointr:
+-			mnt->flags &= ~NFS_MOUNT_INTR;
+ 			break;
+ 		case Opt_posix:
+ 			mnt->flags |= NFS_MOUNT_POSIX;
+@@ -701,7 +781,7 @@ static int nfs_parse_mount_options(char *raw,
  				return 0;
  			if (option < 0 || option > 65535)
  				return 0;
@@ -731130,7 +735258,7 @@
  			break;
  		case Opt_rsize:
  			if (match_int(args, &mnt->rsize))
-@@ -763,13 +847,6 @@ static int nfs_parse_mount_options(char *raw,
+@@ -763,13 +843,6 @@ static int nfs_parse_mount_options(char *raw,
  				return 0;
  			mnt->mount_server.port = option;
  			break;
@@ -731144,7 +735272,7 @@
  		case Opt_mountvers:
  			if (match_int(args, &option))
  				return 0;
-@@ -777,13 +854,6 @@ static int nfs_parse_mount_options(char *raw,
+@@ -777,13 +850,6 @@ static int nfs_parse_mount_options(char *raw,
  				return 0;
  			mnt->mount_server.version = option;
  			break;
@@ -731158,7 +735286,7 @@
  		case Opt_nfsvers:
  			if (match_int(args, &option))
  				return 0;
-@@ -927,24 +997,32 @@ static int nfs_parse_mount_options(char *raw,
+@@ -927,24 +993,32 @@ static int nfs_parse_mount_options(char *raw,
  			string = match_strdup(args);
  			if (string == NULL)
  				goto out_nomem;
@@ -731197,7 +735325,7 @@
  			kfree(string);
  			break;
  
-@@ -957,6 +1035,8 @@ static int nfs_parse_mount_options(char *raw,
+@@ -957,6 +1031,8 @@ static int nfs_parse_mount_options(char *raw,
  		}
  	}
  
@@ -731206,7 +735334,7 @@
  	return 1;
  
  out_nomem:
-@@ -987,7 +1067,8 @@ out_unknown:
+@@ -987,7 +1063,8 @@ out_unknown:
  static int nfs_try_mount(struct nfs_parsed_mount_data *args,
  			 struct nfs_fh *root_fh)
  {
@@ -731216,7 +735344,7 @@
  	int status;
  
  	if (args->mount_server.version == 0) {
-@@ -997,25 +1078,32 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
+@@ -997,25 +1074,32 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
  			args->mount_server.version = NFS_MNT_VERSION;
  	}
  
@@ -731257,7 +735385,7 @@
  			   args->nfs_server.export_path,
  			   args->mount_server.version,
  			   args->mount_server.protocol,
-@@ -1023,8 +1111,8 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
+@@ -1023,8 +1107,8 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
  	if (status == 0)
  		return 0;
  
@@ -731268,7 +735396,7 @@
  	return status;
  }
  
-@@ -1043,9 +1131,6 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
+@@ -1043,9 +1127,6 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
   *
   * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
   *   mountproto=tcp after mountproto=udp, and so on
@@ -731278,7 +735406,7 @@
   */
  static int nfs_validate_mount_data(void *options,
  				   struct nfs_parsed_mount_data *args,
-@@ -1069,9 +1154,7 @@ static int nfs_validate_mount_data(void *options,
+@@ -1069,9 +1150,7 @@ static int nfs_validate_mount_data(void *options,
  	args->acdirmin		= 30;
  	args->acdirmax		= 60;
  	args->mount_server.protocol = XPRT_TRANSPORT_UDP;
@@ -731288,7 +735416,7 @@
  
  	switch (data->version) {
  	case 1:
-@@ -1102,9 +1185,6 @@ static int nfs_validate_mount_data(void *options,
+@@ -1102,9 +1181,6 @@ static int nfs_validate_mount_data(void *options,
  			memset(mntfh->data + mntfh->size, 0,
  			       sizeof(mntfh->data) - mntfh->size);
  
@@ -731298,7 +735426,7 @@
  		/*
  		 * Translate to nfs_parsed_mount_data, which nfs_fill_super
  		 * can deal with.
-@@ -1119,7 +1199,14 @@ static int nfs_validate_mount_data(void *options,
+@@ -1119,7 +1195,14 @@ static int nfs_validate_mount_data(void *options,
  		args->acregmax		= data->acregmax;
  		args->acdirmin		= data->acdirmin;
  		args->acdirmax		= data->acdirmax;
@@ -731314,7 +735442,7 @@
  		if (!(data->flags & NFS_MOUNT_TCP))
  			args->nfs_server.protocol = XPRT_TRANSPORT_UDP;
  		/* N.B. caller will free nfs_server.hostname in all cases */
-@@ -1322,15 +1409,50 @@ static int nfs_set_super(struct super_block *s, void *data)
+@@ -1322,15 +1405,50 @@ static int nfs_set_super(struct super_block *s, void *data)
  	return ret;
  }
  
@@ -731368,7 +735496,7 @@
  		return 0;
  	/* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */
  	if (old->flags & NFS_MOUNT_UNSHARED)
-@@ -1400,6 +1522,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
+@@ -1400,6 +1518,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
  
  out:
  	kfree(data.nfs_server.hostname);
@@ -731376,7 +735504,7 @@
  	return error;
  
  out_err_nosb:
-@@ -1528,12 +1651,35 @@ static void nfs4_fill_super(struct super_block *sb)
+@@ -1528,12 +1647,35 @@ static void nfs4_fill_super(struct super_block *sb)
  }
  
  /*
@@ -731412,7 +735540,7 @@
  	struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
  	char *c;
  
-@@ -1554,18 +1700,21 @@ static int nfs4_validate_mount_data(void *options,
+@@ -1554,18 +1696,21 @@ static int nfs4_validate_mount_data(void *options,
  
  	switch (data->version) {
  	case 1:
@@ -731440,7 +735568,7 @@
  		switch (data->auth_flavourlen) {
  		case 0:
  			args->auth_flavors[0] = RPC_AUTH_UNIX;
-@@ -1623,6 +1772,9 @@ static int nfs4_validate_mount_data(void *options,
+@@ -1623,6 +1768,9 @@ static int nfs4_validate_mount_data(void *options,
  						&args->nfs_server.address))
  			return -EINVAL;
  
@@ -731450,7 +735578,7 @@
  		switch (args->auth_flavor_len) {
  		case 0:
  			args->auth_flavors[0] = RPC_AUTH_UNIX;
-@@ -1643,21 +1795,16 @@ static int nfs4_validate_mount_data(void *options,
+@@ -1643,21 +1791,16 @@ static int nfs4_validate_mount_data(void *options,
  		len = c - dev_name;
  		if (len > NFS4_MAXNAMLEN)
  			return -ENAMETOOLONG;
@@ -731580,7 +735708,7 @@
  		rpc_put_task(task);
  	return 1;
 diff --git a/fs/nfs/write.c b/fs/nfs/write.c
-index 51cc1bd..5ac5b27 100644
+index 51cc1bd..522efff 100644
 --- a/fs/nfs/write.c
 +++ b/fs/nfs/write.c
 @@ -196,7 +196,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
@@ -731639,6 +735767,15 @@
  	return 0;
  }
  
+@@ -490,7 +488,7 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
+ /*
+  * Wait for a request to complete.
+  *
+- * Interruptible by signals only if mounted with intr flag.
++ * Interruptible by fatal signals only.
+  */
+ static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, unsigned int npages)
+ {
 @@ -596,7 +594,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
  		spin_lock(&inode->i_lock);
  		req = nfs_page_find_request_locked(page);
@@ -738063,10 +742200,37 @@
 +#endif
 +	device_del(&disk->dev);
  }
+diff --git a/fs/proc/array.c b/fs/proc/array.c
+index eb97f28..b380313 100644
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -141,12 +141,7 @@ static const char *task_state_array[] = {
+ 
+ static inline const char *get_task_state(struct task_struct *tsk)
+ {
+-	unsigned int state = (tsk->state & (TASK_RUNNING |
+-					    TASK_INTERRUPTIBLE |
+-					    TASK_UNINTERRUPTIBLE |
+-					    TASK_STOPPED |
+-					    TASK_TRACED)) |
+-					   tsk->exit_state;
++	unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state;
+ 	const char **p = &task_state_array[0];
+ 
+ 	while (state) {
 diff --git a/fs/proc/base.c b/fs/proc/base.c
-index 7411bfb..91fa8e6 100644
+index 7411bfb..9fa9708 100644
 --- a/fs/proc/base.c
 +++ b/fs/proc/base.c
+@@ -199,7 +199,7 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf
+ 	(task == current || \
+ 	(task->parent == current && \
+ 	(task->ptrace & PT_PTRACED) && \
+-	 (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \
++	 (task_is_stopped_or_traced(task)) && \
+ 	 security_ptrace(current,task) == 0))
+ 
+ struct mm_struct *mm_for_maps(struct task_struct *task)
 @@ -310,6 +310,77 @@ static int proc_pid_schedstat(struct task_struct *task, char *buffer)
  }
  #endif
@@ -738397,6 +742561,22 @@
  	if (!max)
  		max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes);
  
+diff --git a/fs/readdir.c b/fs/readdir.c
+index efe52e6..4e026e5 100644
+--- a/fs/readdir.c
++++ b/fs/readdir.c
+@@ -30,7 +30,10 @@ int vfs_readdir(struct file *file, filldir_t filler, void *buf)
+ 	if (res)
+ 		goto out;
+ 
+-	mutex_lock(&inode->i_mutex);
++	res = mutex_lock_killable(&inode->i_mutex);
++	if (res)
++		goto out;
++
+ 	res = -ENOENT;
+ 	if (!IS_DEADDIR(inode)) {
+ 		res = file->f_op->readdir(file, buf, filler);
 diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile
 index 6673ee8..4faf8c4 100644
 --- a/fs/smbfs/Makefile
@@ -738425,6 +742605,19 @@
 -	@echo >> proto2.h ""
 -	cproto -E "gcc -E" -e -v -I $(TOPDIR)/include -DMAKING_PROTO -D__KERNEL__ $(SRC) >> proto2.h
 -	mv proto2.h proto.h
+diff --git a/fs/smbfs/request.c b/fs/smbfs/request.c
+index ca4b2d5..45f4593 100644
+--- a/fs/smbfs/request.c
++++ b/fs/smbfs/request.c
+@@ -105,7 +105,7 @@ struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize)
+                 if (nfs_try_to_free_pages(server))
+ 			continue;
+ 
+-		if (signalled() && (server->flags & NFS_MOUNT_INTR))
++		if (fatal_signal_pending(current))
+ 			return ERR_PTR(-ERESTARTSYS);
+ 		current->policy = SCHED_YIELD;
+ 		schedule();
 diff --git a/fs/splice.c b/fs/splice.c
 index 6bdcb61..1577a73 100644
 --- a/fs/splice.c
@@ -740531,6 +744724,30 @@
 +
 +#endif
 +
+diff --git a/include/asm-arm/arch-omap/eac.h b/include/asm-arm/arch-omap/eac.h
+index 6662cb0..ccee3b0 100644
+--- a/include/asm-arm/arch-omap/eac.h
++++ b/include/asm-arm/arch-omap/eac.h
+@@ -31,7 +31,6 @@
+ #include <asm/arch/hardware.h>
+ #include <asm/irq.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ 
+ /* master codec clock source */
+diff --git a/include/asm-arm/arch-omap/omap-alsa.h b/include/asm-arm/arch-omap/omap-alsa.h
+index fcaf44c..faa0ed2 100644
+--- a/include/asm-arm/arch-omap/omap-alsa.h
++++ b/include/asm-arm/arch-omap/omap-alsa.h
+@@ -40,7 +40,6 @@
+ #ifndef __OMAP_ALSA_H
+ #define __OMAP_ALSA_H
+ 
+-#include <sound/driver.h>
+ #include <asm/arch/dma.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
 diff --git a/include/asm-arm/arch-omap/tps65010.h b/include/asm-arm/arch-omap/tps65010.h
 deleted file mode 100644
 index b9aa2b3..0000000
@@ -741241,6 +745458,18 @@
 + */
 +
 +#define VMALLOC_END       0xf0000000
+diff --git a/include/asm-arm/arch-pxa/audio.h b/include/asm-arm/arch-pxa/audio.h
+index 17eccd7..52bbe3b 100644
+--- a/include/asm-arm/arch-pxa/audio.h
++++ b/include/asm-arm/arch-pxa/audio.h
+@@ -1,7 +1,6 @@
+ #ifndef __ASM_ARCH_AUDIO_H__
+ #define __ASM_ARCH_AUDIO_H__
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ 
 diff --git a/include/asm-arm/arch-pxa/colibri.h b/include/asm-arm/arch-pxa/colibri.h
 new file mode 100644
 index 0000000..2ae373f
@@ -745695,18 +749924,6 @@
  }
  
  #endif	/* __KERNEL__ */
-diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
-index 75f2bfa..6ce9f3a 100644
---- a/include/asm-generic/tlb.h
-+++ b/include/asm-generic/tlb.h
-@@ -15,7 +15,6 @@
- 
- #include <linux/swap.h>
- #include <linux/quicklist.h>
--#include <asm/pgalloc.h>
- #include <asm/tlbflush.h>
- 
- /*
 diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
 index 9f584cc..f784d2f 100644
 --- a/include/asm-generic/vmlinux.lds.h
@@ -789581,13 +793798,14 @@
 -
 -#endif /* _ASM_X8664_PERCPU_H_ */
 diff --git a/include/asm-x86/pgalloc_32.h b/include/asm-x86/pgalloc_32.h
-index f2fc33c..10c2b45 100644
+index f2fc33c..7641e7b 100644
 --- a/include/asm-x86/pgalloc_32.h
 +++ b/include/asm-x86/pgalloc_32.h
-@@ -3,31 +3,33 @@
+@@ -3,31 +3,34 @@
  
  #include <linux/threads.h>
  #include <linux/mm.h>		/* for struct page */
++#include <linux/pagemap.h>
 +#include <asm/tlb.h>
 +#include <asm-generic/tlb.h>
  
@@ -789614,10 +793832,6 @@
 +	paravirt_alloc_pt(mm, __pa(pte) >> PAGE_SHIFT);
 +	set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE));
 +}
-+
-+static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte)
-+{
-+	unsigned long pfn = page_to_pfn(pte);
  
 -#define pmd_populate(mm, pmd, pte) 				\
 -do {								\
@@ -789626,13 +793840,17 @@
 -		((unsigned long long)page_to_pfn(pte) <<	\
 -			(unsigned long long) PAGE_SHIFT)));	\
 -} while (0)
++static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte)
++{
++	unsigned long pfn = page_to_pfn(pte);
++
 +	paravirt_alloc_pt(mm, pfn);
 +	set_pmd(pmd, __pmd(((pteval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE));
 +}
  
  /*
   * Allocate and free page tables.
-@@ -49,20 +51,55 @@ static inline void pte_free(struct page *pte)
+@@ -49,20 +52,40 @@ static inline void pte_free(struct page *pte)
  }
  
  
@@ -789641,11 +793859,7 @@
 -	paravirt_release_pt(page_to_pfn(pte));				\
 -	tlb_remove_page((tlb),(pte));					\
 -} while (0)
-+static inline void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte)
-+{
-+	paravirt_release_pt(page_to_pfn(pte));
-+	tlb_remove_page(tlb, pte);
-+}
++extern void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte);
  
  #ifdef CONFIG_X86_PAE
  /*
@@ -789667,18 +793881,7 @@
 +	free_page((unsigned long)pmd);
 +}
 +
-+static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
-+{
-+	/* This is called just after the pmd has been detached from
-+	   the pgd, which requires a full tlb flush to be recognized
-+	   by the CPU.  Rather than incurring multiple tlb flushes
-+	   while the address space is being pulled down, make the tlb
-+	   gathering machinery do a full flush when we're done. */
-+	tlb->fullmm = 1;
-+
-+	paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT);
-+	tlb_remove_page(tlb, virt_to_page(pmd));
-+}
++extern void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd);
 +
 +static inline void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd)
 +{
@@ -800034,6 +804237,18 @@
 +#endif
 +
  #endif /* __LINUX_COMPILER_H */
+diff --git a/include/linux/completion.h b/include/linux/completion.h
+index 33d6aaf..d2961b6 100644
+--- a/include/linux/completion.h
++++ b/include/linux/completion.h
+@@ -44,6 +44,7 @@ static inline void init_completion(struct completion *x)
+ 
+ extern void wait_for_completion(struct completion *);
+ extern int wait_for_completion_interruptible(struct completion *x);
++extern int wait_for_completion_killable(struct completion *x);
+ extern unsigned long wait_for_completion_timeout(struct completion *x,
+ 						   unsigned long timeout);
+ extern unsigned long wait_for_completion_interruptible_timeout(
 diff --git a/include/linux/connector.h b/include/linux/connector.h
 index 13fc454..da6dd95 100644
 --- a/include/linux/connector.h
@@ -807095,6 +811310,31 @@
      __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
  	= { __param_str_##name, perm, set, get, { arg } }
  
+diff --git a/include/linux/mutex.h b/include/linux/mutex.h
+index 6014797..05c5903 100644
+--- a/include/linux/mutex.h
++++ b/include/linux/mutex.h
+@@ -125,15 +125,20 @@ static inline int fastcall mutex_is_locked(struct mutex *lock)
+ extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
+ extern int __must_check mutex_lock_interruptible_nested(struct mutex *lock,
+ 					unsigned int subclass);
++extern int __must_check mutex_lock_killable_nested(struct mutex *lock,
++					unsigned int subclass);
+ 
+ #define mutex_lock(lock) mutex_lock_nested(lock, 0)
+ #define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)
++#define mutex_lock_killable(lock) mutex_lock_killable_nested(lock, 0)
+ #else
+ extern void fastcall mutex_lock(struct mutex *lock);
+ extern int __must_check fastcall mutex_lock_interruptible(struct mutex *lock);
++extern int __must_check fastcall mutex_lock_killable(struct mutex *lock);
+ 
+ # define mutex_lock_nested(lock, subclass) mutex_lock(lock)
+ # define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock)
++# define mutex_lock_killable_nested(lock, subclass) mutex_lock_killable(lock)
+ #endif
+ 
+ /*
 diff --git a/include/linux/mv643xx.h b/include/linux/mv643xx.h
 index d2ae618..69327b7 100644
 --- a/include/linux/mv643xx.h
@@ -808488,7 +812728,7 @@
  	spin_unlock_irqrestore(&npinfo->rx_lock, flags);
  
 diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
-index 2d15d4a..099ddb4 100644
+index 2d15d4a..a69ba80 100644
 --- a/include/linux/nfs_fs.h
 +++ b/include/linux/nfs_fs.h
 @@ -196,28 +196,67 @@ struct nfs_inode {
@@ -808592,6 +812832,22 @@
  extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr);
  extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags);
  extern void nfs_access_zap_cache(struct inode *inode);
+@@ -516,14 +556,7 @@ extern void * nfs_root_data(void);
+ 
+ #define nfs_wait_event(clnt, wq, condition)				\
+ ({									\
+-	int __retval = 0;						\
+-	if (clnt->cl_intr) {						\
+-		sigset_t oldmask;					\
+-		rpc_clnt_sigmask(clnt, &oldmask);			\
+-		__retval = wait_event_interruptible(wq, condition);	\
+-		rpc_clnt_sigunmask(clnt, &oldmask);			\
+-	} else								\
+-		wait_event(wq, condition);				\
++	int __retval = wait_event_killable(wq, condition);		\
+ 	__retval;							\
+ })
+ 
 diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
 index 0cac49b..3423c67 100644
 --- a/include/linux/nfs_fs_sb.h
@@ -808660,6 +812916,19 @@
  };
  
  /* Server capabilities */
+diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h
+index a3ade89..df7c6b7 100644
+--- a/include/linux/nfs_mount.h
++++ b/include/linux/nfs_mount.h
+@@ -48,7 +48,7 @@ struct nfs_mount_data {
+ /* bits in the flags field */
+ 
+ #define NFS_MOUNT_SOFT		0x0001	/* 1 */
+-#define NFS_MOUNT_INTR		0x0002	/* 1 */
++#define NFS_MOUNT_INTR		0x0002	/* 1 */ /* now unused, but ABI */
+ #define NFS_MOUNT_SECURE	0x0004	/* 1 */
+ #define NFS_MOUNT_POSIX		0x0008	/* 1 */
+ #define NFS_MOUNT_NOCTO		0x0010	/* 1 */
 diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
 index 30dbcc1..a1676e1 100644
 --- a/include/linux/nfs_page.h
@@ -809016,6 +813285,38 @@
  extern const struct of_device_id *of_match_device(
  	const struct of_device_id *matches, const struct of_device *dev);
  
+diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
+index db8a410..4b62a10 100644
+--- a/include/linux/pagemap.h
++++ b/include/linux/pagemap.h
+@@ -157,6 +157,7 @@ static inline pgoff_t linear_page_index(struct vm_area_struct *vma,
+ }
+ 
+ extern void FASTCALL(__lock_page(struct page *page));
++extern int FASTCALL(__lock_page_killable(struct page *page));
+ extern void FASTCALL(__lock_page_nosync(struct page *page));
+ extern void FASTCALL(unlock_page(struct page *page));
+ 
+@@ -171,6 +172,19 @@ static inline void lock_page(struct page *page)
+ }
+ 
+ /*
++ * lock_page_killable is like lock_page but can be interrupted by fatal
++ * signals.  It returns 0 if it locked the page and -EINTR if it was
++ * killed while waiting.
++ */
++static inline int lock_page_killable(struct page *page)
++{
++	might_sleep();
++	if (TestSetPageLocked(page))
++		return __lock_page_killable(page);
++	return 0;
++}
++
++/*
+  * lock_page_nosync should only be used if we can't pin the page's inode.
+  * Doesn't play quite so well with block device plugging.
+  */
 diff --git a/include/linux/pata_platform.h b/include/linux/pata_platform.h
 index 5799e8d..6a7a92d 100644
 --- a/include/linux/pata_platform.h
@@ -810649,7 +814950,7 @@
 +
  #endif /* _LINUX_SCATTERLIST_H */
 diff --git a/include/linux/sched.h b/include/linux/sched.h
-index cc14656..9d47976 100644
+index cc14656..6c33357 100644
 --- a/include/linux/sched.h
 +++ b/include/linux/sched.h
 @@ -27,6 +27,7 @@
@@ -810682,7 +814983,45 @@
  struct bio;
  
  /*
-@@ -230,6 +232,8 @@ static inline int select_nohz_load_balancer(int cpu)
+@@ -170,13 +172,35 @@ print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
+ #define TASK_RUNNING		0
+ #define TASK_INTERRUPTIBLE	1
+ #define TASK_UNINTERRUPTIBLE	2
+-#define TASK_STOPPED		4
+-#define TASK_TRACED		8
++#define __TASK_STOPPED		4
++#define __TASK_TRACED		8
+ /* in tsk->exit_state */
+ #define EXIT_ZOMBIE		16
+ #define EXIT_DEAD		32
+ /* in tsk->state again */
+ #define TASK_DEAD		64
++#define TASK_WAKEKILL		128
++
++/* Convenience macros for the sake of set_task_state */
++#define TASK_KILLABLE		(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
++#define TASK_STOPPED		(TASK_WAKEKILL | __TASK_STOPPED)
++#define TASK_TRACED		(TASK_WAKEKILL | __TASK_TRACED)
++
++/* Convenience macros for the sake of wake_up */
++#define TASK_NORMAL		(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
++#define TASK_ALL		(TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)
++
++/* get_task_state() */
++#define TASK_REPORT		(TASK_RUNNING | TASK_INTERRUPTIBLE | \
++				 TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
++				 __TASK_TRACED)
++
++#define task_is_traced(task)	((task->state & __TASK_TRACED) != 0)
++#define task_is_stopped(task)	((task->state & __TASK_STOPPED) != 0)
++#define task_is_stopped_or_traced(task)	\
++			((task->state & (__TASK_STOPPED | __TASK_TRACED)) != 0)
++#define task_contributes_to_load(task)	\
++				((task->state & TASK_UNINTERRUPTIBLE) != 0)
+ 
+ #define __set_task_state(tsk, state_value)		\
+ 	do { (tsk)->state = (state_value); } while (0)
+@@ -230,6 +254,8 @@ static inline int select_nohz_load_balancer(int cpu)
  }
  #endif
  
@@ -810691,7 +815030,7 @@
  /*
   * Only dump TASK_* tasks. (0 for all tasks)
   */
-@@ -257,13 +261,19 @@ extern void trap_init(void);
+@@ -257,13 +283,19 @@ extern void trap_init(void);
  extern void account_process_tick(struct task_struct *task, int user);
  extern void update_process_times(int user);
  extern void scheduler_tick(void);
@@ -810712,7 +815051,15 @@
  #else
  static inline void softlockup_tick(void)
  {
-@@ -552,18 +562,13 @@ struct user_struct {
+@@ -292,6 +324,7 @@ extern int in_sched_functions(unsigned long addr);
+ #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
+ extern signed long FASTCALL(schedule_timeout(signed long timeout));
+ extern signed long schedule_timeout_interruptible(signed long timeout);
++extern signed long schedule_timeout_killable(signed long timeout);
+ extern signed long schedule_timeout_uninterruptible(signed long timeout);
+ asmlinkage void schedule(void);
+ 
+@@ -552,18 +585,13 @@ struct user_struct {
  #ifdef CONFIG_FAIR_USER_SCHED
  	struct task_group *tg;
  #ifdef CONFIG_SYSFS
@@ -810733,7 +815080,7 @@
  
  extern struct user_struct *find_user(uid_t);
  
-@@ -827,6 +832,7 @@ struct sched_class {
+@@ -827,6 +855,7 @@ struct sched_class {
  	void (*enqueue_task) (struct rq *rq, struct task_struct *p, int wakeup);
  	void (*dequeue_task) (struct rq *rq, struct task_struct *p, int sleep);
  	void (*yield_task) (struct rq *rq);
@@ -810741,7 +815088,7 @@
  
  	void (*check_preempt_curr) (struct rq *rq, struct task_struct *p);
  
-@@ -842,11 +848,25 @@ struct sched_class {
+@@ -842,11 +871,25 @@ struct sched_class {
  	int (*move_one_task) (struct rq *this_rq, int this_cpu,
  			      struct rq *busiest, struct sched_domain *sd,
  			      enum cpu_idle_type idle);
@@ -810768,7 +815115,7 @@
  };
  
  struct load_weight {
-@@ -876,6 +896,8 @@ struct sched_entity {
+@@ -876,6 +919,8 @@ struct sched_entity {
  #ifdef CONFIG_SCHEDSTATS
  	u64			wait_start;
  	u64			wait_max;
@@ -810777,7 +815124,7 @@
  
  	u64			sleep_start;
  	u64			sleep_max;
-@@ -914,6 +936,21 @@ struct sched_entity {
+@@ -914,6 +959,21 @@ struct sched_entity {
  #endif
  };
  
@@ -810799,7 +815146,7 @@
  struct task_struct {
  	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
  	void *stack;
-@@ -930,16 +967,15 @@ struct task_struct {
+@@ -930,16 +990,15 @@ struct task_struct {
  #endif
  
  	int prio, static_prio, normal_prio;
@@ -810817,7 +815164,7 @@
  	/*
  	 * fpu_counter contains the number of consecutive context switches
  	 * that the FPU is used. If this is over a threshold, the lazy fpu
-@@ -956,7 +992,11 @@ struct task_struct {
+@@ -956,7 +1015,11 @@ struct task_struct {
  
  	unsigned int policy;
  	cpumask_t cpus_allowed;
@@ -810830,7 +815177,7 @@
  
  #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
  	struct sched_info sched_info;
-@@ -1046,6 +1086,11 @@ struct task_struct {
+@@ -1046,6 +1109,11 @@ struct task_struct {
  /* ipc stuff */
  	struct sysv_sem sysvsem;
  #endif
@@ -810842,7 +815189,7 @@
  /* CPU-specific state of this task */
  	struct thread_struct thread;
  /* filesystem information */
-@@ -1178,6 +1223,10 @@ struct task_struct {
+@@ -1178,6 +1246,10 @@ struct task_struct {
  	int make_it_fail;
  #endif
  	struct prop_local_single dirties;
@@ -810853,7 +815200,7 @@
  };
  
  /*
-@@ -1458,6 +1507,12 @@ extern unsigned int sysctl_sched_child_runs_first;
+@@ -1458,6 +1530,12 @@ extern unsigned int sysctl_sched_child_runs_first;
  extern unsigned int sysctl_sched_features;
  extern unsigned int sysctl_sched_migration_cost;
  extern unsigned int sysctl_sched_nr_migrate;
@@ -810866,7 +815213,23 @@
  
  int sched_nr_latency_handler(struct ctl_table *table, int write,
  		struct file *file, void __user *buffer, size_t *length,
-@@ -1850,29 +1905,33 @@ static inline int need_resched(void)
+@@ -1837,7 +1915,14 @@ static inline int signal_pending(struct task_struct *p)
+ {
+ 	return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
+ }
+-  
++
++extern int FASTCALL(__fatal_signal_pending(struct task_struct *p));
++
++static inline int fatal_signal_pending(struct task_struct *p)
++{
++	return signal_pending(p) && __fatal_signal_pending(p);
++}
++
+ static inline int need_resched(void)
+ {
+ 	return unlikely(test_thread_flag(TIF_NEED_RESCHED));
+@@ -1850,29 +1935,33 @@ static inline int need_resched(void)
   * cond_resched_lock() will drop the spinlock before scheduling,
   * cond_resched_softirq() will enable bhs before scheduling.
   */
@@ -811678,10 +816041,15 @@
  #endif
  
 diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
-index d9d5c5a..3e9addc 100644
+index d9d5c5a..129a86e 100644
 --- a/include/linux/sunrpc/clnt.h
 +++ b/include/linux/sunrpc/clnt.h
-@@ -46,6 +46,7 @@ struct rpc_clnt {
+@@ -41,11 +41,11 @@ struct rpc_clnt {
+ 	struct rpc_iostats *	cl_metrics;	/* per-client statistics */
+ 
+ 	unsigned int		cl_softrtry : 1,/* soft timeouts */
+-				cl_intr     : 1,/* interruptible */
+ 				cl_discrtry : 1,/* disconnect before retry */
  				cl_autobind : 1;/* use getport() */
  
  	struct rpc_rtt *	cl_rtt;		/* RTO estimator data */
@@ -811689,7 +816057,7 @@
  
  	int			cl_nodelen;	/* nodename length */
  	char 			cl_nodename[UNX_MAXNODENAME];
-@@ -54,6 +55,7 @@ struct rpc_clnt {
+@@ -54,6 +54,7 @@ struct rpc_clnt {
  	struct dentry *		cl_dentry;	/* inode */
  	struct rpc_clnt *	cl_parent;	/* Points to parent of clones */
  	struct rpc_rtt		cl_rtt_default;
@@ -811697,7 +816065,7 @@
  	struct rpc_program *	cl_program;
  	char			cl_inline_name[32];
  };
-@@ -99,7 +101,7 @@ struct rpc_create_args {
+@@ -99,7 +100,7 @@ struct rpc_create_args {
  	struct sockaddr		*address;
  	size_t			addrsize;
  	struct sockaddr		*saddress;
@@ -811706,7 +816074,15 @@
  	char			*servername;
  	struct rpc_program	*program;
  	u32			version;
-@@ -123,11 +125,10 @@ void		rpc_shutdown_client(struct rpc_clnt *);
+@@ -109,7 +110,6 @@ struct rpc_create_args {
+ 
+ /* Values for "flags" field */
+ #define RPC_CLNT_CREATE_HARDRTRY	(1UL << 0)
+-#define RPC_CLNT_CREATE_INTR		(1UL << 1)
+ #define RPC_CLNT_CREATE_AUTOBIND	(1UL << 2)
+ #define RPC_CLNT_CREATE_NONPRIVPORT	(1UL << 3)
+ #define RPC_CLNT_CREATE_NOPING		(1UL << 4)
+@@ -123,11 +123,10 @@ void		rpc_shutdown_client(struct rpc_clnt *);
  void		rpc_release_client(struct rpc_clnt *);
  
  int		rpcb_register(u32, u32, int, unsigned short, int *);
@@ -811720,7 +816096,13 @@
  int		rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg,
  			       int flags, const struct rpc_call_ops *tk_ops,
  			       void *calldata);
-@@ -142,7 +143,7 @@ void		rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
+@@ -136,13 +135,11 @@ int		rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg,
+ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
+ 			       int flags);
+ void		rpc_restart_call(struct rpc_task *);
+-void		rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
+-void		rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
+ void		rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
  size_t		rpc_max_payload(struct rpc_clnt *);
  void		rpc_force_rebind(struct rpc_clnt *);
  size_t		rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
@@ -811779,7 +816161,7 @@
  #endif /* __KERNEL__ */
  #endif /* _LINUX_SUNRPC_MSGPROT_H_ */
 diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
-index 8ea077d..ce3d1b1 100644
+index 8ea077d..f689f02 100644
 --- a/include/linux/sunrpc/sched.h
 +++ b/include/linux/sunrpc/sched.h
 @@ -56,8 +56,6 @@ struct rpc_task {
@@ -811825,7 +816207,23 @@
  
  /*
   * RPC task flags
-@@ -180,10 +189,10 @@ struct rpc_call_ops {
+@@ -128,7 +137,6 @@ struct rpc_call_ops {
+ #define RPC_TASK_DYNAMIC	0x0080		/* task was kmalloc'ed */
+ #define RPC_TASK_KILLED		0x0100		/* task was killed */
+ #define RPC_TASK_SOFT		0x0200		/* Use soft timeouts */
+-#define RPC_TASK_NOINTR		0x0400		/* uninterruptible task */
+ 
+ #define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
+ #define RPC_IS_SWAPPER(t)	((t)->tk_flags & RPC_TASK_SWAPPER)
+@@ -136,7 +144,6 @@ struct rpc_call_ops {
+ #define RPC_ASSASSINATED(t)	((t)->tk_flags & RPC_TASK_KILLED)
+ #define RPC_DO_CALLBACK(t)	((t)->tk_callback != NULL)
+ #define RPC_IS_SOFT(t)		((t)->tk_flags & RPC_TASK_SOFT)
+-#define RPC_TASK_UNINTERRUPTIBLE(t) ((t)->tk_flags & RPC_TASK_NOINTR)
+ 
+ #define RPC_TASK_RUNNING	0
+ #define RPC_TASK_QUEUED		1
+@@ -180,10 +187,10 @@ struct rpc_call_ops {
   * Note: if you change these, you must also change
   * the task initialization definitions below.
   */
@@ -811840,7 +816238,7 @@
  
  /*
   * RPC synchronization objects
-@@ -191,7 +200,7 @@ struct rpc_call_ops {
+@@ -191,7 +198,7 @@ struct rpc_call_ops {
  struct rpc_wait_queue {
  	spinlock_t		lock;
  	struct list_head	tasks[RPC_NR_PRIORITY];	/* task queue for each priority level */
@@ -811849,7 +816247,7 @@
  	unsigned char		maxpriority;		/* maximum priority (0 if queue is not a priority queue) */
  	unsigned char		priority;		/* current priority */
  	unsigned char		count;			/* # task groups remaining serviced so far */
-@@ -208,41 +217,13 @@ struct rpc_wait_queue {
+@@ -208,41 +215,13 @@ struct rpc_wait_queue {
   * performance of NFS operations such as read/write.
   */
  #define RPC_BATCH_COUNT			16
@@ -811967,18 +816365,6 @@
  
  /* kernel/power/main.c */
  extern struct blocking_notifier_head pm_chain_head;
-diff --git a/include/linux/swap.h b/include/linux/swap.h
-index 4f3838a..2c3ce4c 100644
---- a/include/linux/swap.h
-+++ b/include/linux/swap.h
-@@ -6,6 +6,7 @@
- #include <linux/mmzone.h>
- #include <linux/list.h>
- #include <linux/sched.h>
-+#include <linux/pagemap.h>
- 
- #include <asm/atomic.h>
- #include <asm/page.h>
 diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
 index 4f5047d..89faebf 100644
 --- a/include/linux/sysctl.h
@@ -812293,6 +816679,79 @@
  };
  
  #define MAX_UIO_MAPS 	5
+diff --git a/include/linux/wait.h b/include/linux/wait.h
+index 0e68628..1f4fb0a 100644
+--- a/include/linux/wait.h
++++ b/include/linux/wait.h
+@@ -152,14 +152,15 @@ int FASTCALL(out_of_line_wait_on_bit(void *, int, int (*)(void *), unsigned));
+ int FASTCALL(out_of_line_wait_on_bit_lock(void *, int, int (*)(void *), unsigned));
+ wait_queue_head_t *FASTCALL(bit_waitqueue(void *, int));
+ 
+-#define wake_up(x)			__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
+-#define wake_up_nr(x, nr)		__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr, NULL)
+-#define wake_up_all(x)			__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)
++#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)
++#define wake_up_nr(x, nr)		__wake_up(x, TASK_NORMAL, nr, NULL)
++#define wake_up_all(x)			__wake_up(x, TASK_NORMAL, 0, NULL)
++#define wake_up_locked(x)		__wake_up_locked((x), TASK_NORMAL)
++
+ #define wake_up_interruptible(x)	__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
+ #define wake_up_interruptible_nr(x, nr)	__wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
+ #define wake_up_interruptible_all(x)	__wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
+-#define	wake_up_locked(x)		__wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
+-#define wake_up_interruptible_sync(x)   __wake_up_sync((x),TASK_INTERRUPTIBLE, 1)
++#define wake_up_interruptible_sync(x)	__wake_up_sync((x), TASK_INTERRUPTIBLE, 1)
+ 
+ #define __wait_event(wq, condition) 					\
+ do {									\
+@@ -345,6 +346,47 @@ do {									\
+ 	__ret;								\
+ })
+ 
++#define __wait_event_killable(wq, condition, ret)			\
++do {									\
++	DEFINE_WAIT(__wait);						\
++									\
++	for (;;) {							\
++		prepare_to_wait(&wq, &__wait, TASK_KILLABLE);		\
++		if (condition)						\
++			break;						\
++		if (!fatal_signal_pending(current)) {			\
++			schedule();					\
++			continue;					\
++		}							\
++		ret = -ERESTARTSYS;					\
++		break;							\
++	}								\
++	finish_wait(&wq, &__wait);					\
++} while (0)
++
++/**
++ * wait_event_killable - sleep until a condition gets true
++ * @wq: the waitqueue to wait on
++ * @condition: a C expression for the event to wait for
++ *
++ * The process is put to sleep (TASK_KILLABLE) until the
++ * @condition evaluates to true or a signal is received.
++ * The @condition is checked each time the waitqueue @wq is woken up.
++ *
++ * wake_up() has to be called after changing any variable that could
++ * change the result of the wait condition.
++ *
++ * The function will return -ERESTARTSYS if it was interrupted by a
++ * signal and 0 if @condition evaluated to true.
++ */
++#define wait_event_killable(wq, condition)				\
++({									\
++	int __ret = 0;							\
++	if (!(condition))						\
++		__wait_event_killable(wq, condition, __ret);		\
++	__ret;								\
++})
++
+ /*
+  * Must be called with the spinlock in the wait_queue_head_t held.
+  */
 diff --git a/include/linux/wireless.h b/include/linux/wireless.h
 index 0987aa7..74e84ca 100644
 --- a/include/linux/wireless.h
@@ -818593,6 +823052,2520 @@
  	unsigned	WCE : 1;	/* state of disk WCE bit */
  	unsigned	RCD : 1;	/* state of disk RCD bit, unused */
  	unsigned	DPOFUA : 1;	/* state of disk DPOFUA bit */
+diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h
+index d04f9e7..d9aebdf 100644
+--- a/include/sound/ad1848.h
++++ b/include/sound/ad1848.h
+@@ -48,7 +48,7 @@
+ #define AD1848_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
+ #define AD1848_PIN_CTRL		0x0a	/* pin control */
+ #define AD1848_TEST_INIT	0x0b	/* test and initialization */
+-#define AD1848_MISC_INFO	0x0c	/* miscellaneaous information */
++#define AD1848_MISC_INFO	0x0c	/* miscellaneous information */
+ #define AD1848_LOOPBACK		0x0d	/* loopback control */
+ #define AD1848_DATA_UPR_CNT	0x0e	/* playback/capture upper base count */
+ #define AD1848_DATA_LWR_CNT	0x0f	/* playback/capture lower base count */
+diff --git a/include/sound/ainstr_fm.h b/include/sound/ainstr_fm.h
+deleted file mode 100644
+index c4afb1f..0000000
+--- a/include/sound/ainstr_fm.h
++++ /dev/null
+@@ -1,134 +0,0 @@
+-/*
+- *  Advanced Linux Sound Architecture
+- *
+- *  FM (OPL2/3) Instrument Format
+- *  Copyright (c) 2000 Uros Bizjak <uros at kss-loka.si>
+- *
+- *
+- *   This program 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 program 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
+- *
+- */
+-
+-#ifndef __SOUND_AINSTR_FM_H
+-#define __SOUND_AINSTR_FM_H
+-
+-#ifndef __KERNEL__
+-#include <asm/types.h>
+-#include <asm/byteorder.h>
+-#endif
+-
+-/*
+- *  share types (share ID 1)
+- */
+-
+-#define FM_SHARE_FILE		0
+-
+-/*
+- * FM operator
+- */
+-
+-struct fm_operator {
+-	unsigned char am_vib;
+-	unsigned char ksl_level;
+-	unsigned char attack_decay;
+-	unsigned char sustain_release;
+-	unsigned char wave_select;
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-#define FM_PATCH_OPL2	0x01		/* OPL2 2 operators FM instrument */
+-#define FM_PATCH_OPL3	0x02		/* OPL3 4 operators FM instrument */
+-
+-struct fm_instrument {
+-	unsigned int share_id[4];	/* share id - zero = no sharing */
+-	unsigned char type;		/* instrument type */
+-
+-	struct fm_operator op[4];
+-	unsigned char feedback_connection[2];
+-
+-	unsigned char echo_delay;
+-	unsigned char echo_atten;
+-	unsigned char chorus_spread;
+-	unsigned char trnsps;
+-	unsigned char fix_dur;
+-	unsigned char modes;
+-	unsigned char fix_key;
+-};
+-
+-/*
+- *
+- *    Kernel <-> user space
+- *    Hardware (CPU) independent section
+- *
+- *    * = zero or more
+- *    + = one or more
+- *
+- *    fm_xinstrument	FM_STRU_INSTR
+- *
+- */
+-
+-#define FM_STRU_INSTR	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+-
+-/*
+- * FM operator
+- */
+-
+-struct fm_xoperator {
+-	__u8 am_vib;
+-	__u8 ksl_level;
+-	__u8 attack_decay;
+-	__u8 sustain_release;
+-	__u8 wave_select;
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-struct fm_xinstrument {
+-	__u32 stype;			/* structure type */
+-
+-	__u32 share_id[4];		/* share id - zero = no sharing */
+-	__u8 type;			/* instrument type */
+-
+-	struct fm_xoperator op[4];		/* fm operators */
+-	__u8 feedback_connection[2];
+-
+-	__u8 echo_delay;
+-	__u8 echo_atten;
+-	__u8 chorus_spread;
+-	__u8 trnsps;
+-	__u8 fix_dur;
+-	__u8 modes;
+-	__u8 fix_key;
+-};
+-
+-#ifdef __KERNEL__
+-
+-#include "seq_instr.h"
+-
+-int snd_seq_fm_init(struct snd_seq_kinstr_ops * ops,
+-		    struct snd_seq_kinstr_ops * next);
+-
+-#endif
+-
+-/* typedefs for compatibility to user-space */
+-typedef struct fm_xoperator fm_xoperator_t;
+-typedef struct fm_xinstrument fm_xinstrument_t;
+-
+-#endif	/* __SOUND_AINSTR_FM_H */
+diff --git a/include/sound/ainstr_gf1.h b/include/sound/ainstr_gf1.h
+deleted file mode 100644
+index b62b665..0000000
+--- a/include/sound/ainstr_gf1.h
++++ /dev/null
+@@ -1,229 +0,0 @@
+-/*
+- *  Advanced Linux Sound Architecture
+- *
+- *  GF1 (GUS) Patch Instrument Format
+- *  Copyright (c) 1994-99 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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
+- *
+- */
+-
+-#ifndef __SOUND_AINSTR_GF1_H
+-#define __SOUND_AINSTR_GF1_H
+-
+-#ifndef __KERNEL__
+-#include <asm/types.h>
+-#include <asm/byteorder.h>
+-#endif
+-
+-/*
+- *  share types (share ID 1)
+- */
+-
+-#define GF1_SHARE_FILE			0
+-
+-/*
+- *  wave formats
+- */
+-
+-#define GF1_WAVE_16BIT			0x0001  /* 16-bit wave */
+-#define GF1_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+-#define GF1_WAVE_INVERT			0x0002  /* same as unsigned wave */
+-#define GF1_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+-#define GF1_WAVE_LOOP			0x0008  /* loop mode */
+-#define GF1_WAVE_BIDIR			0x0010  /* bidirectional mode */
+-#define GF1_WAVE_STEREO			0x0100	/* stereo mode */
+-#define GF1_WAVE_ULAW			0x0200	/* uLaw compression mode */
+-
+-/*
+- *  Wavetable definitions
+- */
+-
+-struct gf1_wave {
+-	unsigned int share_id[4];	/* share id - zero = no sharing */
+-	unsigned int format;		/* wave format */
+-
+-	struct {
+-		unsigned int number;	/* some other ID for this instrument */
+-		unsigned int memory;	/* begin of waveform in onboard memory */
+-		unsigned char *ptr;	/* pointer to waveform in system memory */
+-	} address;
+-
+-	unsigned int size;		/* size of waveform in samples */
+-	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned int loop_start;	/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned int loop_end;		/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+-
+-	unsigned char flags;		/* GF1 patch flags */
+-	unsigned char pad;
+-	unsigned int sample_rate;	/* sample rate in Hz */
+-	unsigned int low_frequency;	/* low frequency range */
+-	unsigned int high_frequency;	/* high frequency range */
+-	unsigned int root_frequency;	/* root frequency range */
+-	signed short tune;
+-	unsigned char balance;
+-	unsigned char envelope_rate[6];
+-	unsigned char envelope_offset[6];
+-	unsigned char tremolo_sweep;
+-	unsigned char tremolo_rate;
+-	unsigned char tremolo_depth;
+-	unsigned char vibrato_sweep;
+-	unsigned char vibrato_rate;
+-	unsigned char vibrato_depth;
+-	unsigned short scale_frequency;
+-	unsigned short scale_factor;	/* 0-2048 or 0-2 */
+-  
+-	struct gf1_wave *next;
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-#define IWFFFF_EXCLUDE_NONE		0x0000	/* exclusion mode - none */
+-#define IWFFFF_EXCLUDE_SINGLE		0x0001	/* exclude single - single note from the instrument group */
+-#define IWFFFF_EXCLUDE_MULTIPLE		0x0002	/* exclude multiple - stop only same note from this instrument */
+-
+-#define IWFFFF_EFFECT_NONE		0
+-#define IWFFFF_EFFECT_REVERB		1
+-#define IWFFFF_EFFECT_CHORUS		2
+-#define IWFFFF_EFFECT_ECHO		3
+-
+-struct gf1_instrument {
+-	unsigned short exclusion;
+-	unsigned short exclusion_group;	/* 0 - none, 1-65535 */
+-
+-	unsigned char effect1;		/* effect 1 */
+-	unsigned char effect1_depth;	/* 0-127 */
+-	unsigned char effect2;		/* effect 2 */
+-	unsigned char effect2_depth;	/* 0-127 */
+-
+-	struct gf1_wave *wave;		/* first waveform */
+-};
+-
+-/*
+- *
+- *    Kernel <-> user space
+- *    Hardware (CPU) independent section
+- *
+- *    * = zero or more
+- *    + = one or more
+- *
+- *    gf1_xinstrument		IWFFFF_STRU_INSTR
+- *      +gf1_xwave		IWFFFF_STRU_WAVE
+- *
+- */
+-
+-#define GF1_STRU_WAVE		__cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
+-#define GF1_STRU_INSTR		__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+-
+-/*
+- *  Wavetable definitions
+- */
+-
+-struct gf1_xwave {
+-	__u32 stype;			/* structure type */
+-
+-	__u32 share_id[4];		/* share id - zero = no sharing */
+-	__u32 format;			/* wave format */
+-
+-	__u32 size;			/* size of waveform in samples */
+-	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+-
+-	__u8 flags;			/* GF1 patch flags */
+-	__u8 pad;
+-	__u32 sample_rate;		/* sample rate in Hz */
+-	__u32 low_frequency;		/* low frequency range */
+-	__u32 high_frequency;		/* high frequency range */
+-	__u32 root_frequency;		/* root frequency range */
+-	__s16 tune;
+-	__u8 balance;
+-	__u8 envelope_rate[6];
+-	__u8 envelope_offset[6];
+-	__u8 tremolo_sweep;
+-	__u8 tremolo_rate;
+-	__u8 tremolo_depth;
+-	__u8 vibrato_sweep;
+-	__u8 vibrato_rate;
+-	__u8 vibrato_depth;
+-	__u16 scale_frequency;
+-	__u16 scale_factor;		/* 0-2048 or 0-2 */  
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-struct gf1_xinstrument {
+-	__u32 stype;
+-	
+-	__u16 exclusion;
+-	__u16 exclusion_group;		/* 0 - none, 1-65535 */
+-
+-	__u8 effect1;			/* effect 1 */
+-	__u8 effect1_depth;		/* 0-127 */
+-	__u8 effect2;			/* effect 2 */
+-	__u8 effect2_depth;		/* 0-127 */
+-};
+-
+-/*
+- *  Instrument info
+- */
+-
+-#define GF1_INFO_ENVELOPE		(1<<0)
+-#define GF1_INFO_TREMOLO		(1<<1)
+-#define GF1_INFO_VIBRATO		(1<<2)
+-
+-struct gf1_info {
+-	unsigned char flags;		/* supported wave flags */
+-	unsigned char pad[3];
+-	unsigned int features;		/* supported features */
+-	unsigned int max8_len;		/* maximum 8-bit wave length */
+-	unsigned int max16_len;		/* maximum 16-bit wave length */
+-};
+-
+-#ifdef __KERNEL__
+-
+-#include "seq_instr.h"
+-
+-struct snd_gf1_ops {
+-	void *private_data;
+-	int (*info)(void *private_data, struct gf1_info *info);
+-	int (*put_sample)(void *private_data, struct gf1_wave *wave,
+-	                  char __user *data, long len, int atomic);
+-	int (*get_sample)(void *private_data, struct gf1_wave *wave,
+-			  char __user *data, long len, int atomic);
+-	int (*remove_sample)(void *private_data, struct gf1_wave *wave,
+-			     int atomic);
+-	void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
+-	struct snd_seq_kinstr_ops kops;
+-};
+-
+-int snd_seq_gf1_init(struct snd_gf1_ops *ops,
+-		     void *private_data,
+-		     struct snd_seq_kinstr_ops *next);
+-
+-#endif
+-
+-/* typedefs for compatibility to user-space */
+-typedef struct gf1_xwave gf1_xwave_t;
+-typedef struct gf1_xinstrument gf1_xinstrument_t;
+-
+-#endif /* __SOUND_AINSTR_GF1_H */
+diff --git a/include/sound/ainstr_iw.h b/include/sound/ainstr_iw.h
+deleted file mode 100644
+index 11bd250..0000000
+--- a/include/sound/ainstr_iw.h
++++ /dev/null
+@@ -1,384 +0,0 @@
+-/*
+- *  Advanced Linux Sound Architecture
+- *
+- *  InterWave FFFF Instrument Format
+- *  Copyright (c) 1994-99 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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
+- *
+- */
+-
+-#ifndef __SOUND_AINSTR_IW_H
+-#define __SOUND_AINSTR_IW_H
+-
+-#ifndef __KERNEL__
+-#include <asm/types.h>
+-#include <asm/byteorder.h>
+-#endif
+-
+-/*
+- *  share types (share ID 1)
+- */
+-
+-#define IWFFFF_SHARE_FILE		0
+-
+-/*
+- *  wave formats
+- */
+-
+-#define IWFFFF_WAVE_16BIT		0x0001  /* 16-bit wave */
+-#define IWFFFF_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+-#define IWFFFF_WAVE_INVERT		0x0002  /* same as unsigned wave */
+-#define IWFFFF_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+-#define IWFFFF_WAVE_LOOP		0x0008  /* loop mode */
+-#define IWFFFF_WAVE_BIDIR		0x0010  /* bidirectional mode */
+-#define IWFFFF_WAVE_ULAW		0x0020  /* uLaw compressed wave */
+-#define IWFFFF_WAVE_RAM			0x0040  /* wave is _preloaded_ in RAM (it is used for ROM simulation) */
+-#define IWFFFF_WAVE_ROM			0x0080  /* wave is in ROM */
+-#define IWFFFF_WAVE_STEREO		0x0100	/* wave is stereo */
+-
+-/*
+- *  Wavetable definitions
+- */
+-
+-struct iwffff_wave {
+-	unsigned int share_id[4];	/* share id - zero = no sharing */
+-	unsigned int format;		/* wave format */
+-
+-	struct {
+-		unsigned int number;	/* some other ID for this wave */
+-		unsigned int memory;	/* begin of waveform in onboard memory */
+-		unsigned char *ptr;	/* pointer to waveform in system memory */
+-	} address;
+-
+-	unsigned int size;		/* size of waveform in samples */
+-	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned int loop_start;	/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned int loop_end;		/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+-	unsigned int sample_ratio;	/* sample ratio (44100 * 1024 / rate) */
+-	unsigned char attenuation;	/* 0 - 127 (no corresponding midi controller) */
+-	unsigned char low_note;		/* lower frequency range for this waveform */
+-	unsigned char high_note;	/* higher frequency range for this waveform */
+-	unsigned char pad;
+-  
+-	struct iwffff_wave *next;
+-};
+-
+-/*
+- *  Layer
+- */
+-
+-#define IWFFFF_LFO_SHAPE_TRIANGLE	0
+-#define IWFFFF_LFO_SHAPE_POSTRIANGLE	1
+-
+-struct iwffff_lfo {
+-	unsigned short freq;		/* (0-2047) 0.01Hz - 21.5Hz */
+-	signed short depth;		/* volume +- (0-255) 0.48675dB/step */
+-	signed short sweep;		/* 0 - 950 deciseconds */
+-	unsigned char shape;		/* see to IWFFFF_LFO_SHAPE_XXXX */
+-	unsigned char delay;		/* 0 - 255 deciseconds */
+-};
+-
+-#define IWFFFF_ENV_FLAG_RETRIGGER	0x0001	/* flag - retrigger */
+-
+-#define IWFFFF_ENV_MODE_ONE_SHOT	0x0001	/* mode - one shot */
+-#define IWFFFF_ENV_MODE_SUSTAIN		0x0002	/* mode - sustain */
+-#define IWFFFF_ENV_MODE_NO_SUSTAIN	0x0003	/* mode - no sustain */
+-
+-#define IWFFFF_ENV_INDEX_VELOCITY	0x0001	/* index - velocity */
+-#define IWFFFF_ENV_INDEX_FREQUENCY	0x0002	/* index - frequency */
+-
+-struct iwffff_env_point {
+-	unsigned short offset;
+-	unsigned short rate;
+-};
+-
+-struct iwffff_env_record {
+-	unsigned short nattack;
+-	unsigned short nrelease;
+-	unsigned short sustain_offset;
+-	unsigned short sustain_rate;
+-	unsigned short release_rate;
+-	unsigned char hirange;
+-	unsigned char pad;
+-	struct iwffff_env_record *next;
+-	/* points are stored here */
+-	/* count of points = nattack + nrelease */
+-};
+-
+-struct iwffff_env {
+-	unsigned char flags;
+-  	unsigned char mode;
+-  	unsigned char index;
+-	unsigned char pad;
+-	struct iwffff_env_record *record;
+-};
+-
+-#define IWFFFF_LAYER_FLAG_RETRIGGER	0x0001	/* retrigger */
+-
+-#define IWFFFF_LAYER_VELOCITY_TIME	0x0000	/* velocity mode = time */
+-#define IWFFFF_LAYER_VELOCITY_RATE	0x0001	/* velocity mode = rate */
+-
+-#define IWFFFF_LAYER_EVENT_KUP		0x0000	/* layer event - key up */
+-#define IWFFFF_LAYER_EVENT_KDOWN	0x0001	/* layer event - key down */
+-#define IWFFFF_LAYER_EVENT_RETRIG	0x0002	/* layer event - retrigger */
+-#define IWFFFF_LAYER_EVENT_LEGATO	0x0003	/* layer event - legato */
+-
+-struct iwffff_layer {
+-	unsigned char flags;
+-	unsigned char velocity_mode;
+-      	unsigned char layer_event;
+-	unsigned char low_range;	/* range for layer based */
+-	unsigned char high_range;	/* on either velocity or frequency */
+-	unsigned char pan;		/* pan offset from CC1 (0 left - 127 right) */
+-	unsigned char pan_freq_scale;	/* position based on frequency (0-127) */
+-	unsigned char attenuation;	/* 0-127 (no corresponding midi controller) */
+-	struct iwffff_lfo tremolo;		/* tremolo effect */
+-	struct iwffff_lfo vibrato;		/* vibrato effect */
+-	unsigned short freq_scale;	/* 0-2048, 1024 is equal to semitone scaling */
+-	unsigned char freq_center;	/* center for keyboard frequency scaling */
+-	unsigned char pad;
+-	struct iwffff_env penv;		/* pitch envelope */
+-	struct iwffff_env venv;		/* volume envelope */
+-
+-	struct iwffff_wave *wave;
+-	struct iwffff_layer *next;
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-#define IWFFFF_EXCLUDE_NONE		0x0000	/* exclusion mode - none */
+-#define IWFFFF_EXCLUDE_SINGLE		0x0001	/* exclude single - single note from the instrument group */
+-#define IWFFFF_EXCLUDE_MULTIPLE		0x0002	/* exclude multiple - stop only same note from this instrument */
+-
+-#define IWFFFF_LAYER_NONE		0x0000	/* not layered */
+-#define IWFFFF_LAYER_ON			0x0001	/* layered */
+-#define IWFFFF_LAYER_VELOCITY		0x0002	/* layered by velocity */
+-#define IWFFFF_LAYER_FREQUENCY		0x0003	/* layered by frequency */
+-
+-#define IWFFFF_EFFECT_NONE		0
+-#define IWFFFF_EFFECT_REVERB		1
+-#define IWFFFF_EFFECT_CHORUS		2
+-#define IWFFFF_EFFECT_ECHO		3
+-
+-struct iwffff_instrument {
+-	unsigned short exclusion;
+-	unsigned short layer_type;
+-	unsigned short exclusion_group;	/* 0 - none, 1-65535 */
+-
+-	unsigned char effect1;		/* effect 1 */
+-	unsigned char effect1_depth;	/* 0-127 */
+-	unsigned char effect2;		/* effect 2 */
+-	unsigned char effect2_depth;	/* 0-127 */
+-
+-	struct iwffff_layer *layer;		/* first layer */
+-};
+-
+-/*
+- *
+- *    Kernel <-> user space
+- *    Hardware (CPU) independent section
+- *
+- *    * = zero or more
+- *    + = one or more
+- *
+- *    iwffff_xinstrument		IWFFFF_STRU_INSTR
+- *      +iwffff_xlayer			IWFFFF_STRU_LAYER
+- *        *iwffff_xenv_record		IWFFFF_STRU_ENV_RECT (tremolo)
+- *        *iwffff_xenv_record		IWFFFF_STRU_EVN_RECT (vibrato)
+- *          +iwffff_xwave		IWFFFF_STRU_WAVE
+- *
+- */
+-
+-#define IWFFFF_STRU_WAVE	__cpu_to_be32(('W'<<24)|('A'<<16)|('V'<<8)|'E')
+-#define IWFFFF_STRU_ENV_RECP	__cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'P')
+-#define IWFFFF_STRU_ENV_RECV	__cpu_to_be32(('E'<<24)|('N'<<16)|('R'<<8)|'V')
+-#define IWFFFF_STRU_LAYER 	__cpu_to_be32(('L'<<24)|('A'<<16)|('Y'<<8)|'R')
+-#define IWFFFF_STRU_INSTR 	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+-
+-/*
+- *  Wavetable definitions
+- */
+-
+-struct iwffff_xwave {
+-	__u32 stype;			/* structure type */
+-
+-	__u32 share_id[4];		/* share id - zero = no sharing */
+-
+-	__u32 format;			/* wave format */
+-	__u32 offset;			/* offset to ROM (address) */
+-
+-	__u32 size;			/* size of waveform in samples */
+-	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+-	__u32 sample_ratio;		/* sample ratio (44100 * 1024 / rate) */
+-	__u8 attenuation;		/* 0 - 127 (no corresponding midi controller) */
+-	__u8 low_note;			/* lower frequency range for this waveform */
+-	__u8 high_note;			/* higher frequency range for this waveform */
+-	__u8 pad;
+-};
+-
+-/*
+- *  Layer
+- */
+-
+-struct iwffff_xlfo {
+-	__u16 freq;			/* (0-2047) 0.01Hz - 21.5Hz */
+-	__s16 depth;			/* volume +- (0-255) 0.48675dB/step */
+-	__s16 sweep;			/* 0 - 950 deciseconds */
+-	__u8 shape;			/* see to ULTRA_IW_LFO_SHAPE_XXXX */
+-	__u8 delay;			/* 0 - 255 deciseconds */
+-};
+-
+-struct iwffff_xenv_point {
+-	__u16 offset;
+-	__u16 rate;
+-};
+-
+-struct iwffff_xenv_record {
+-	__u32 stype;
+-	__u16 nattack;
+-	__u16 nrelease;
+-	__u16 sustain_offset;
+-	__u16 sustain_rate;
+-	__u16 release_rate;
+-	__u8 hirange;
+-	__u8 pad;
+-	/* points are stored here.. */
+-	/* count of points = nattack + nrelease */
+-};
+-
+-struct iwffff_xenv {
+-	__u8 flags;
+-  	__u8 mode;
+-  	__u8 index;
+-	__u8 pad;
+-};
+-
+-struct iwffff_xlayer {
+-	__u32 stype;
+-	__u8 flags;
+-	__u8 velocity_mode;
+-      	__u8 layer_event;
+-	__u8 low_range;			/* range for layer based */
+-	__u8 high_range;		/* on either velocity or frequency */
+-	__u8 pan;			/* pan offset from CC1 (0 left - 127 right) */
+-	__u8 pan_freq_scale;		/* position based on frequency (0-127) */
+-	__u8 attenuation;		/* 0-127 (no corresponding midi controller) */
+-	struct iwffff_xlfo tremolo;		/* tremolo effect */
+-	struct iwffff_xlfo vibrato;		/* vibrato effect */
+-	__u16 freq_scale;		/* 0-2048, 1024 is equal to semitone scaling */
+-	__u8 freq_center;		/* center for keyboard frequency scaling */
+-	__u8 pad;
+-	struct iwffff_xenv penv;		/* pitch envelope */
+-	struct iwffff_xenv venv;		/* volume envelope */
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-struct iwffff_xinstrument {
+-	__u32 stype;
+-	
+-	__u16 exclusion;
+-	__u16 layer_type;
+-	__u16 exclusion_group;		/* 0 - none, 1-65535 */
+-
+-	__u8 effect1;			/* effect 1 */
+-	__u8 effect1_depth;		/* 0-127 */
+-	__u8 effect2;			/* effect 2 */
+-	__u8 effect2_depth;		/* 0-127 */
+-};
+-
+-/*
+- *  ROM support
+- *    InterWave ROMs are Little-Endian (x86)
+- */
+-
+-#define IWFFFF_ROM_HDR_SIZE	512
+-
+-struct iwffff_rom_header {
+-	__u8 iwave[8];
+-	__u8 revision;
+-	__u8 series_number;
+-	__u8 series_name[16];
+-	__u8 date[10];
+-	__u16 vendor_revision_major;
+-	__u16 vendor_revision_minor;
+-	__u32 rom_size;
+-	__u8 copyright[128];
+-	__u8 vendor_name[64];
+-	__u8 description[128];
+-};
+-
+-/*
+- *  Instrument info
+- */
+-
+-#define IWFFFF_INFO_LFO_VIBRATO		(1<<0)
+-#define IWFFFF_INFO_LFO_VIBRATO_SHAPE	(1<<1)
+-#define IWFFFF_INFO_LFO_TREMOLO		(1<<2)
+-#define IWFFFF_INFO_LFO_TREMOLO_SHAPE	(1<<3)
+-
+-struct iwffff_info {
+-	unsigned int format;		/* supported format bits */
+-	unsigned int effects;		/* supported effects (1 << IWFFFF_EFFECT*) */
+-	unsigned int lfos;		/* LFO effects */
+-	unsigned int max8_len;		/* maximum 8-bit wave length */
+-	unsigned int max16_len;		/* maximum 16-bit wave length */
+-};
+-
+-#ifdef __KERNEL__
+-
+-#include "seq_instr.h"
+-
+-struct snd_iwffff_ops {
+-	void *private_data;
+-	int (*info)(void *private_data, struct iwffff_info *info);
+-	int (*put_sample)(void *private_data, struct iwffff_wave *wave,
+-	                  char __user *data, long len, int atomic);
+-	int (*get_sample)(void *private_data, struct iwffff_wave *wave,
+-			  char __user *data, long len, int atomic);
+-	int (*remove_sample)(void *private_data, struct iwffff_wave *wave,
+-			     int atomic);
+-	void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
+-	struct snd_seq_kinstr_ops kops;
+-};
+-
+-int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
+-			void *private_data,
+-                        struct snd_seq_kinstr_ops *next);
+-
+-#endif
+-
+-/* typedefs for compatibility to user-space */
+-typedef struct iwffff_xwave iwffff_xwave_t;
+-typedef struct iwffff_xlfo iwffff_xlfo_t;
+-typedef struct iwffff_xenv_point iwffff_xenv_point_t;
+-typedef struct iwffff_xenv_record iwffff_xenv_record_t;
+-typedef struct iwffff_xenv iwffff_xenv_t;
+-typedef struct iwffff_xlayer iwffff_xlayer_t;
+-typedef struct iwffff_xinstrument iwffff_xinstrument_t;
+-typedef struct iwffff_rom_header iwffff_rom_header_t;
+-typedef struct iwffff_info iwffff_info_t;
+-
+-#endif /* __SOUND_AINSTR_IW_H */
+diff --git a/include/sound/ainstr_simple.h b/include/sound/ainstr_simple.h
+deleted file mode 100644
+index da08e72..0000000
+--- a/include/sound/ainstr_simple.h
++++ /dev/null
+@@ -1,159 +0,0 @@
+-/*
+- *  Advanced Linux Sound Architecture
+- *
+- *  Simple (MOD player) Instrument Format
+- *  Copyright (c) 1994-99 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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
+- *
+- */
+-
+-#ifndef __SOUND_AINSTR_SIMPLE_H
+-#define __SOUND_AINSTR_SIMPLE_H
+-
+-#ifndef __KERNEL__
+-#include <asm/types.h>
+-#include <asm/byteorder.h>
+-#endif
+-
+-/*
+- *  share types (share ID 1)
+- */
+-
+-#define SIMPLE_SHARE_FILE		0
+-
+-/*
+- *  wave formats
+- */
+-
+-#define SIMPLE_WAVE_16BIT		0x0001  /* 16-bit wave */
+-#define SIMPLE_WAVE_UNSIGNED		0x0002  /* unsigned wave */
+-#define SIMPLE_WAVE_INVERT		0x0002  /* same as unsigned wave */
+-#define SIMPLE_WAVE_BACKWARD		0x0004  /* backward mode (maybe used for reverb or ping-ping loop) */
+-#define SIMPLE_WAVE_LOOP		0x0008  /* loop mode */
+-#define SIMPLE_WAVE_BIDIR		0x0010  /* bidirectional mode */
+-#define SIMPLE_WAVE_STEREO		0x0100	/* stereo wave */
+-#define SIMPLE_WAVE_ULAW		0x0200	/* uLaw compression mode */
+-
+-/*
+- *  instrument effects
+- */
+-
+-#define SIMPLE_EFFECT_NONE		0
+-#define SIMPLE_EFFECT_REVERB		1
+-#define SIMPLE_EFFECT_CHORUS		2
+-#define SIMPLE_EFFECT_ECHO		3
+-
+-/*
+- *  instrument info
+- */
+-
+-struct simple_instrument_info {
+-	unsigned int format;		/* supported format bits */
+-	unsigned int effects;		/* supported effects (1 << SIMPLE_EFFECT_*) */
+-	unsigned int max8_len;		/* maximum 8-bit wave length */
+-	unsigned int max16_len;		/* maximum 16-bit wave length */
+-};
+-
+-/*
+- *  Instrument
+- */
+-
+-struct simple_instrument {
+-	unsigned int share_id[4];	/* share id - zero = no sharing */
+-	unsigned int format;		/* wave format */
+-
+-	struct {
+-		unsigned int number;	/* some other ID for this instrument */
+-		unsigned int memory;	/* begin of waveform in onboard memory */
+-		unsigned char *ptr;	/* pointer to waveform in system memory */
+-	} address;
+-
+-	unsigned int size;		/* size of waveform in samples */
+-	unsigned int start;		/* start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned int loop_start;	/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned int loop_end;		/* loop end offset in samples * 16 (lowest 4 bits - fraction) */
+-	unsigned short loop_repeat;	/* loop repeat - 0 = forever */
+-
+-	unsigned char effect1;		/* effect 1 */
+-	unsigned char effect1_depth;	/* 0-127 */
+-	unsigned char effect2;		/* effect 2 */
+-	unsigned char effect2_depth;	/* 0-127 */
+-};
+-
+-/*
+- *
+- *    Kernel <-> user space
+- *    Hardware (CPU) independent section
+- *
+- *    * = zero or more
+- *    + = one or more
+- *
+- *    simple_xinstrument	SIMPLE_STRU_INSTR
+- *
+- */
+-
+-#define SIMPLE_STRU_INSTR	__cpu_to_be32(('I'<<24)|('N'<<16)|('S'<<8)|'T')
+-
+-/*
+- *  Instrument
+- */
+-
+-struct simple_xinstrument {
+-	__u32 stype;
+-
+-	__u32 share_id[4];		/* share id - zero = no sharing */
+-	__u32 format;			/* wave format */
+-
+-	__u32 size;			/* size of waveform in samples */
+-	__u32 start;			/* start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u32 loop_start;		/* bits loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u32 loop_end;			/* loop start offset in samples * 16 (lowest 4 bits - fraction) */
+-	__u16 loop_repeat;		/* loop repeat - 0 = forever */
+-	
+-	__u8 effect1;			/* effect 1 */
+-	__u8 effect1_depth;		/* 0-127 */
+-	__u8 effect2;			/* effect 2 */
+-	__u8 effect2_depth;		/* 0-127 */
+-};
+-
+-#ifdef __KERNEL__
+-
+-#include "seq_instr.h"
+-
+-struct snd_simple_ops {
+-	void *private_data;
+-	int (*info)(void *private_data, struct simple_instrument_info *info);
+-	int (*put_sample)(void *private_data, struct simple_instrument *instr,
+-	                  char __user *data, long len, int atomic);
+-	int (*get_sample)(void *private_data, struct simple_instrument *instr,
+-			  char __user *data, long len, int atomic);
+-	int (*remove_sample)(void *private_data, struct simple_instrument *instr,
+-			     int atomic);
+-	void (*notify)(void *private_data, struct snd_seq_kinstr *instr, int what);
+-	struct snd_seq_kinstr_ops kops;
+-};
+-
+-int snd_seq_simple_init(struct snd_simple_ops *ops,
+-			void *private_data,
+-			struct snd_seq_kinstr_ops *next);
+-
+-#endif
+-
+-/* typedefs for compatibility to user-space */
+-typedef struct simple_xinstrument simple_xinstrument_t;
+-
+-#endif /* __SOUND_AINSTR_SIMPLE_H */
+diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h
+index 891cf1a..6153b91 100644
+--- a/include/sound/ak4xxx-adda.h
++++ b/include/sound/ak4xxx-adda.h
+@@ -68,7 +68,7 @@ struct snd_akm4xxx {
+ 	enum {
+ 		SND_AK4524, SND_AK4528, SND_AK4529,
+ 		SND_AK4355, SND_AK4358, SND_AK4381,
+-		SND_AK5365
++		SND_AK5365, NON_AKM
+ 	} type;
+ 
+ 	/* (array) information of combined codecs */
+diff --git a/include/sound/asequencer.h b/include/sound/asequencer.h
+index 64daccb..1505e6d 100644
+--- a/include/sound/asequencer.h
++++ b/include/sound/asequencer.h
+@@ -110,18 +110,7 @@
+ #define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED	66	/* ports connected */
+ #define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67	/* ports disconnected */
+ 
+-/** synthesizer events
+- * event data type = snd_seq_eve_sample_control
+- */
+-#define SNDRV_SEQ_EVENT_SAMPLE		70	/* sample select */
+-#define SNDRV_SEQ_EVENT_SAMPLE_CLUSTER	71	/* sample cluster select */
+-#define SNDRV_SEQ_EVENT_SAMPLE_START	72	/* voice start */
+-#define SNDRV_SEQ_EVENT_SAMPLE_STOP	73	/* voice stop */
+-#define SNDRV_SEQ_EVENT_SAMPLE_FREQ	74	/* playback frequency */
+-#define SNDRV_SEQ_EVENT_SAMPLE_VOLUME	75	/* volume and balance */
+-#define SNDRV_SEQ_EVENT_SAMPLE_LOOP	76	/* sample loop */
+-#define SNDRV_SEQ_EVENT_SAMPLE_POSITION	77	/* sample position */
+-#define SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1	78	/* private (hardware dependent) event */
++/* 70-89:  synthesizer events - obsoleted */
+ 
+ /** user-defined events with fixed length
+  * event data type = any
+@@ -137,28 +126,7 @@
+ #define SNDRV_SEQ_EVENT_USR8		98
+ #define SNDRV_SEQ_EVENT_USR9		99
+ 
+-/** instrument layer
+- * variable length data can be passed directly to the driver
+- */
+-#define SNDRV_SEQ_EVENT_INSTR_BEGIN	100	/* begin of instrument management */
+-#define SNDRV_SEQ_EVENT_INSTR_END	101	/* end of instrument management */
+-#define SNDRV_SEQ_EVENT_INSTR_INFO	102	/* instrument interface info */
+-#define SNDRV_SEQ_EVENT_INSTR_INFO_RESULT 103	/* result */
+-#define SNDRV_SEQ_EVENT_INSTR_FINFO	104	/* get format info */
+-#define SNDRV_SEQ_EVENT_INSTR_FINFO_RESULT 105	/* get format info */
+-#define SNDRV_SEQ_EVENT_INSTR_RESET	106	/* reset instrument memory */
+-#define SNDRV_SEQ_EVENT_INSTR_STATUS	107	/* instrument interface status */
+-#define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108	/* result */
+-#define SNDRV_SEQ_EVENT_INSTR_PUT	109	/* put instrument to port */
+-#define SNDRV_SEQ_EVENT_INSTR_GET	110	/* get instrument from port */
+-#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111	/* result */
+-#define SNDRV_SEQ_EVENT_INSTR_FREE	112	/* free instrument(s) */
+-#define SNDRV_SEQ_EVENT_INSTR_LIST	113	/* instrument list */
+-#define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114	/* result */
+-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER	115	/* cluster parameters */
+-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116	/* get cluster parameters */
+-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */
+-#define SNDRV_SEQ_EVENT_INSTR_CHANGE	118	/* instrument change */
++/* 100-118: instrument layer - obsoleted */
+ /* 119-129: reserved */
+ 
+ /* 130-139: variable length events
+@@ -258,78 +226,6 @@ struct snd_seq_ev_ext {
+ 	void *ptr;		/* pointer to data (note: maybe 64-bit) */
+ } __attribute__((packed));
+ 
+-/* Instrument cluster type */
+-typedef unsigned int snd_seq_instr_cluster_t;
+-
+-/* Instrument type */
+-struct snd_seq_instr {
+-	snd_seq_instr_cluster_t cluster;
+-	unsigned int std;		/* the upper byte means a private instrument (owner - client #) */
+-	unsigned short bank;
+-	unsigned short prg;
+-};
+-
+-	/* sample number */
+-struct snd_seq_ev_sample {
+-	unsigned int std;
+-	unsigned short bank;
+-	unsigned short prg;
+-};
+-
+-	/* sample cluster */
+-struct snd_seq_ev_cluster {
+-	snd_seq_instr_cluster_t cluster;
+-};
+-
+-	/* sample position */
+-typedef unsigned int snd_seq_position_t; /* playback position (in samples) * 16 */
+-
+-	/* sample stop mode */
+-enum {
+-	SAMPLE_STOP_IMMEDIATELY = 0,	/* terminate playing immediately */
+-	SAMPLE_STOP_VENVELOPE = 1,	/* finish volume envelope */
+-	SAMPLE_STOP_LOOP = 2		/* terminate loop and finish wave */
+-};
+-
+-	/* sample frequency */
+-typedef int snd_seq_frequency_t; /* playback frequency in HZ * 16 */
+-
+-	/* sample volume control; if any value is set to -1 == do not change */
+-struct snd_seq_ev_volume {
+-	signed short volume;	/* range: 0-16383 */
+-	signed short lr;	/* left-right balance; range: 0-16383 */
+-	signed short fr;	/* front-rear balance; range: 0-16383 */
+-	signed short du;	/* down-up balance; range: 0-16383 */
+-};
+-
+-	/* simple loop redefinition */
+-struct snd_seq_ev_loop {
+-	unsigned int start;	/* loop start (in samples) * 16 */
+-	unsigned int end;	/* loop end (in samples) * 16 */
+-};
+-
+-struct snd_seq_ev_sample_control {
+-	unsigned char channel;
+-	unsigned char unused1, unused2, unused3;	/* pad */
+-	union {
+-		struct snd_seq_ev_sample sample;
+-		struct snd_seq_ev_cluster cluster;
+-		snd_seq_position_t position;
+-		int stop_mode;
+-		snd_seq_frequency_t frequency;
+-		struct snd_seq_ev_volume volume;
+-		struct snd_seq_ev_loop loop;
+-		unsigned char raw8[8];
+-	} param;
+-};
+-
+-
+-
+-/* INSTR_BEGIN event */
+-struct snd_seq_ev_instr_begin {
+-	int timeout;		/* zero = forever, otherwise timeout in ms */
+-};
+-
+ struct snd_seq_result {
+ 	int event;		/* processed event type */
+ 	int result;
+@@ -399,8 +295,6 @@ struct snd_seq_event {
+ 		struct snd_seq_addr addr;
+ 		struct snd_seq_connect connect;
+ 		struct snd_seq_result result;
+-		struct snd_seq_ev_instr_begin instr_begin;
+-		struct snd_seq_ev_sample_control sample;
+ 		struct snd_seq_ev_quote quote;
+ 	} data;
+ };
+@@ -441,8 +335,6 @@ struct snd_seq_event_bounce {
+ #define snd_seq_ev_is_user_type(ev)	((ev)->type >= 90 && (ev)->type < 99)
+ /* fixed length events: 0-99 */
+ #define snd_seq_ev_is_fixed_type(ev)	((ev)->type < 100)
+-/* instrument layer events: 100-129 */
+-#define snd_seq_ev_is_instr_type(ev)	((ev)->type >= 100 && (ev)->type < 130)
+ /* variable length events: 130-139 */
+ #define snd_seq_ev_is_variable_type(ev)	((ev)->type >= 130 && (ev)->type < 140)
+ /* reserved for kernel */
+@@ -738,136 +630,6 @@ struct snd_seq_query_subs {
+ 
+ 
+ /*
+- *  Instrument abstraction layer
+- *     - based on events
+- */
+-
+-/* instrument types */
+-#define SNDRV_SEQ_INSTR_ATYPE_DATA	0	/* instrument data */
+-#define SNDRV_SEQ_INSTR_ATYPE_ALIAS	1	/* instrument alias */
+-
+-/* instrument ASCII identifiers */
+-#define SNDRV_SEQ_INSTR_ID_DLS1		"DLS1"
+-#define SNDRV_SEQ_INSTR_ID_DLS2		"DLS2"
+-#define SNDRV_SEQ_INSTR_ID_SIMPLE	"Simple Wave"
+-#define SNDRV_SEQ_INSTR_ID_SOUNDFONT	"SoundFont"
+-#define SNDRV_SEQ_INSTR_ID_GUS_PATCH	"GUS Patch"
+-#define SNDRV_SEQ_INSTR_ID_INTERWAVE	"InterWave FFFF"
+-#define SNDRV_SEQ_INSTR_ID_OPL2_3	"OPL2/3 FM"
+-#define SNDRV_SEQ_INSTR_ID_OPL4		"OPL4"
+-
+-/* instrument types */
+-#define SNDRV_SEQ_INSTR_TYPE0_DLS1	(1<<0)	/* MIDI DLS v1 */
+-#define SNDRV_SEQ_INSTR_TYPE0_DLS2	(1<<1)	/* MIDI DLS v2 */
+-#define SNDRV_SEQ_INSTR_TYPE1_SIMPLE	(1<<0)	/* Simple Wave */
+-#define SNDRV_SEQ_INSTR_TYPE1_SOUNDFONT	(1<<1)	/* EMU SoundFont */
+-#define SNDRV_SEQ_INSTR_TYPE1_GUS_PATCH	(1<<2)	/* Gravis UltraSound Patch */
+-#define SNDRV_SEQ_INSTR_TYPE1_INTERWAVE	(1<<3)	/* InterWave FFFF */
+-#define SNDRV_SEQ_INSTR_TYPE2_OPL2_3	(1<<0)	/* Yamaha OPL2/3 FM */
+-#define SNDRV_SEQ_INSTR_TYPE2_OPL4	(1<<1)	/* Yamaha OPL4 */
+-
+-/* put commands */
+-#define SNDRV_SEQ_INSTR_PUT_CMD_CREATE	0
+-#define SNDRV_SEQ_INSTR_PUT_CMD_REPLACE	1
+-#define SNDRV_SEQ_INSTR_PUT_CMD_MODIFY	2
+-#define SNDRV_SEQ_INSTR_PUT_CMD_ADD	3
+-#define SNDRV_SEQ_INSTR_PUT_CMD_REMOVE	4
+-
+-/* get commands */
+-#define SNDRV_SEQ_INSTR_GET_CMD_FULL	0
+-#define SNDRV_SEQ_INSTR_GET_CMD_PARTIAL	1
+-
+-/* query flags */
+-#define SNDRV_SEQ_INSTR_QUERY_FOLLOW_ALIAS (1<<0)
+-
+-/* free commands */
+-#define SNDRV_SEQ_INSTR_FREE_CMD_ALL		0
+-#define SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE	1
+-#define SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER	2
+-#define SNDRV_SEQ_INSTR_FREE_CMD_SINGLE		3
+-
+-/* size of ROM/RAM */
+-typedef unsigned int snd_seq_instr_size_t;
+-
+-/* INSTR_INFO */
+-
+-struct snd_seq_instr_info {
+-	int result;			/* operation result */
+-	unsigned int formats[8];	/* bitmap of supported formats */
+-	int ram_count;			/* count of RAM banks */
+-	snd_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */
+-	int rom_count;			/* count of ROM banks */
+-	snd_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */
+-	char reserved[128];
+-};
+-
+-/* INSTR_STATUS */
+-
+-struct snd_seq_instr_status {
+-	int result;			/* operation result */
+-	snd_seq_instr_size_t free_ram[16]; /* free RAM in banks */
+-	int instrument_count;		/* count of downloaded instruments */
+-	char reserved[128];
+-};
+-
+-/* INSTR_FORMAT_INFO */
+-
+-struct snd_seq_instr_format_info {
+-	char format[16];		/* format identifier - SNDRV_SEQ_INSTR_ID_* */	
+-	unsigned int len;		/* max data length (without this structure) */
+-};
+-
+-struct snd_seq_instr_format_info_result {
+-	int result;			/* operation result */
+-	char format[16];		/* format identifier */
+-	unsigned int len;		/* filled data length (without this structure) */
+-};
+-
+-/* instrument data */
+-struct snd_seq_instr_data {
+-	char name[32];			/* instrument name */
+-	char reserved[16];		/* for the future use */
+-	int type;			/* instrument type */
+-	union {
+-		char format[16];	/* format identifier */
+-		struct snd_seq_instr alias;
+-	} data;
+-};
+-
+-/* INSTR_PUT/GET, data are stored in one block (extended), header + data */
+-
+-struct snd_seq_instr_header {
+-	union {
+-		struct snd_seq_instr instr;
+-		snd_seq_instr_cluster_t cluster;
+-	} id;				/* instrument identifier */
+-	unsigned int cmd;		/* get/put/free command */
+-	unsigned int flags;		/* query flags (only for get) */
+-	unsigned int len;		/* real instrument data length (without header) */
+-	int result;			/* operation result */
+-	char reserved[16];		/* for the future */
+-	struct snd_seq_instr_data data; /* instrument data (for put/get result) */
+-};
+-
+-/* INSTR_CLUSTER_SET */
+-
+-struct snd_seq_instr_cluster_set {
+-	snd_seq_instr_cluster_t cluster; /* cluster identifier */
+-	char name[32];			/* cluster name */
+-	int priority;			/* cluster priority */
+-	char reserved[64];		/* for the future use */
+-};
+-
+-/* INSTR_CLUSTER_GET */
+-
+-struct snd_seq_instr_cluster_get {
+-	snd_seq_instr_cluster_t cluster; /* cluster identifier */
+-	char name[32];			/* cluster name */
+-	int priority;			/* cluster priority */
+-	char reserved[64];		/* for the future use */
+-};
+-
+-/*
+  *  IOCTL commands
+  */
+ 
+diff --git a/include/sound/asound.h b/include/sound/asound.h
+index af9d11d..3eaf155 100644
+--- a/include/sound/asound.h
++++ b/include/sound/asound.h
+@@ -95,7 +95,7 @@ enum {
+ 	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
+ 
+ 	/* Don't forget to change the following: */
+-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
++	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_HDA
+ };
+ 
+ struct snd_hwdep_info {
+@@ -138,7 +138,7 @@ enum {
+  *                                                                           *
+  *****************************************************************************/
+ 
+-#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 8)
++#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 9)
+ 
+ typedef unsigned long snd_pcm_uframes_t;
+ typedef signed long snd_pcm_sframes_t;
+@@ -354,8 +354,8 @@ struct snd_pcm_hw_params {
+ 
+ enum {
+ 	SNDRV_PCM_TSTAMP_NONE = 0,
+-	SNDRV_PCM_TSTAMP_MMAP,
+-	SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_MMAP,
++	SNDRV_PCM_TSTAMP_ENABLE,
++	SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_ENABLE,
+ };
+ 
+ struct snd_pcm_sw_params {
+@@ -363,7 +363,7 @@ struct snd_pcm_sw_params {
+ 	unsigned int period_step;
+ 	unsigned int sleep_min;			/* min ticks to sleep */
+ 	snd_pcm_uframes_t avail_min;		/* min avail frames for wakeup */
+-	snd_pcm_uframes_t xfer_align;		/* xfer size need to be a multiple */
++	snd_pcm_uframes_t xfer_align;		/* obsolete: xfer size need to be a multiple */
+ 	snd_pcm_uframes_t start_threshold;	/* min hw_avail frames for automatic start */
+ 	snd_pcm_uframes_t stop_threshold;	/* min avail frames for automatic stop */
+ 	snd_pcm_uframes_t silence_threshold;	/* min distance from noise for silence filling */
+@@ -435,9 +435,16 @@ struct snd_xfern {
+ };
+ 
+ enum {
++	SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0,	/* gettimeofday equivalent */
++	SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,	/* posix_clock_monotonic equivalent */
++	SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
++};
++
++enum {
+ 	SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
+ 	SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info),
+ 	SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int),
++	SNDRV_PCM_IOCTL_TTSTAMP = _IOW('A', 0x03, int),
+ 	SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params),
+ 	SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params),
+ 	SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
+@@ -689,7 +696,7 @@ struct snd_timer_tread {
+  *                                                                          *
+  ****************************************************************************/
+ 
+-#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 4)
++#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 5)
+ 
+ struct snd_ctl_card_info {
+ 	int card;			/* card number */
+@@ -738,8 +745,7 @@ typedef int __bitwise snd_ctl_elem_iface_t;
+ #define SNDRV_CTL_ELEM_ACCESS_OWNER		(1<<10)	/* write lock owner */
+ #define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK	(1<<28)	/* kernel use a TLV callback */ 
+ #define SNDRV_CTL_ELEM_ACCESS_USER		(1<<29) /* user space element */
+-#define SNDRV_CTL_ELEM_ACCESS_DINDIRECT		(1<<30)	/* indirect access for matrix dimensions in the info structure */
+-#define SNDRV_CTL_ELEM_ACCESS_INDIRECT		(1<<31)	/* indirect access for element value in the value structure */
++/* bits 30 and 31 are obsoleted (for indirect access) */
+ 
+ /* for further details see the ACPI and PCI power management specification */
+ #define SNDRV_CTL_POWER_D0		0x0000	/* full On */
+@@ -793,30 +799,30 @@ struct snd_ctl_elem_info {
+ 	} value;
+ 	union {
+ 		unsigned short d[4];		/* dimensions */
+-		unsigned short *d_ptr;		/* indirect */
++		unsigned short *d_ptr;		/* indirect - obsoleted */
+ 	} dimen;
+ 	unsigned char reserved[64-4*sizeof(unsigned short)];
+ };
+ 
+ struct snd_ctl_elem_value {
+ 	struct snd_ctl_elem_id id;	/* W: element ID */
+-	unsigned int indirect: 1;	/* W: use indirect pointer (xxx_ptr member) */
++	unsigned int indirect: 1;	/* W: indirect access - obsoleted */
+         union {
+ 		union {
+ 			long value[128];
+-			long *value_ptr;
++			long *value_ptr;	/* obsoleted */
+ 		} integer;
+ 		union {
+ 			long long value[64];
+-			long long *value_ptr;
++			long long *value_ptr;	/* obsoleted */
+ 		} integer64;
+ 		union {
+ 			unsigned int item[128];
+-			unsigned int *item_ptr;
++			unsigned int *item_ptr;	/* obsoleted */
+ 		} enumerated;
+ 		union {
+ 			unsigned char data[512];
+-			unsigned char *data_ptr;
++			unsigned char *data_ptr;	/* obsoleted */
+ 		} bytes;
+ 		struct snd_aes_iec958 iec958;
+         } value;                /* RO */
+diff --git a/include/sound/asound_fm.h b/include/sound/asound_fm.h
+index 8fbcab7..c2a4b96 100644
+--- a/include/sound/asound_fm.h
++++ b/include/sound/asound_fm.h
+@@ -104,6 +104,8 @@ struct snd_dm_fm_params {
+ #define SNDRV_DM_FM_IOCTL_SET_MODE	_IOW('H', 0x25, int)
+ /* for OPL3 only */
+ #define SNDRV_DM_FM_IOCTL_SET_CONNECTION	_IOW('H', 0x26, int)
++/* SBI patch management */
++#define SNDRV_DM_FM_IOCTL_CLEAR_PATCHES	_IO ('H', 0x40)
+ 
+ #define SNDRV_DM_FM_OSS_IOCTL_RESET		0x20
+ #define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE		0x21
+@@ -112,4 +114,21 @@ struct snd_dm_fm_params {
+ #define SNDRV_DM_FM_OSS_IOCTL_SET_MODE		0x24
+ #define SNDRV_DM_FM_OSS_IOCTL_SET_OPL		0x25
+ 
++/*
++ * Patch Record - fixed size for write
++ */
++
++#define FM_KEY_SBI	"SBI\032"
++#define FM_KEY_2OP	"2OP\032"
++#define FM_KEY_4OP	"4OP\032"
++
++struct sbi_patch {
++	unsigned char prog;
++	unsigned char bank;
++	char key[4];
++	char name[25];
++	char extension[7];
++	unsigned char data[32];
++};
++
+ #endif /* __SOUND_ASOUND_FM_H */
+diff --git a/include/sound/core.h b/include/sound/core.h
+index 6954836..4fc0235 100644
+--- a/include/sound/core.h
++++ b/include/sound/core.h
+@@ -22,12 +22,22 @@
+  *
+  */
+ 
++#include <linux/module.h>
+ #include <linux/sched.h>		/* wake_up() */
+ #include <linux/mutex.h>		/* struct mutex */
+ #include <linux/rwsem.h>		/* struct rw_semaphore */
+ #include <linux/pm.h>			/* pm_message_t */
+ #include <linux/device.h>
+ 
++/* number of supported soundcards */
++#ifdef CONFIG_SND_DYNAMIC_MINORS
++#define SNDRV_CARDS 32
++#else
++#define SNDRV_CARDS 8		/* don't change - minor numbers */
++#endif
++
++#define CONFIG_SND_MAJOR	116	/* standard configuration */
++
+ /* forward declarations */
+ #ifdef CONFIG_PCI
+ struct pci_dev;
+diff --git a/include/sound/cs4231-regs.h b/include/sound/cs4231-regs.h
+index f149026..e8d1f3e 100644
+--- a/include/sound/cs4231-regs.h
++++ b/include/sound/cs4231-regs.h
+@@ -45,7 +45,7 @@
+ #define CS4231_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
+ #define CS4231_PIN_CTRL		0x0a	/* pin control */
+ #define CS4231_TEST_INIT	0x0b	/* test and initialization */
+-#define CS4231_MISC_INFO	0x0c	/* miscellaneaous information */
++#define CS4231_MISC_INFO	0x0c	/* miscellaneous information */
+ #define CS4231_LOOPBACK		0x0d	/* loopback control */
+ #define CS4231_PLY_UPR_CNT	0x0e	/* playback upper base count */
+ #define CS4231_PLY_LWR_CNT	0x0f	/* playback lower base count */
+diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h
+index 6b40ee6..e3005a6 100644
+--- a/include/sound/cs46xx.h
++++ b/include/sound/cs46xx.h
+@@ -1708,9 +1708,6 @@ struct snd_cs46xx {
+ 
+ 	struct gameport *gameport;
+ 
+-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+-	int current_gpio;
+-#endif
+ #ifdef CONFIG_SND_CS46XX_NEW_DSP
+ 	struct mutex spos_mutex;
+ 
+diff --git a/include/sound/driver.h b/include/sound/driver.h
+index 5ccb6c5..f035943 100644
+--- a/include/sound/driver.h
++++ b/include/sound/driver.h
+@@ -1,51 +1 @@
+-#ifndef __SOUND_DRIVER_H
+-#define __SOUND_DRIVER_H
+-
+-/*
+- *  Main header file for the ALSA driver
+- *  Copyright (c) 1994-2000 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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
+- *
+- */
+-
+-#ifdef ALSA_BUILD
+-#include "config.h"
+-#endif
+-
+-
+-/* number of supported soundcards */
+-#ifdef CONFIG_SND_DYNAMIC_MINORS
+-#define SNDRV_CARDS 32
+-#else
+-#define SNDRV_CARDS 8		/* don't change - minor numbers */
+-#endif
+-
+-#ifndef CONFIG_SND_MAJOR	/* standard configuration */
+-#define CONFIG_SND_MAJOR	116
+-#endif
+-
+-#ifndef CONFIG_SND_DEBUG
+-#undef CONFIG_SND_DEBUG_MEMORY
+-#endif
+-
+-#ifdef ALSA_BUILD
+-#include "adriver.h"
+-#endif
+-
+-#include <linux/module.h>
+-
+-#endif /* __SOUND_DRIVER_H */
++#warning "This file is deprecated"
+diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
+index 441aa06..7b7b9b1 100644
+--- a/include/sound/emu10k1.h
++++ b/include/sound/emu10k1.h
+@@ -1120,6 +1120,99 @@
+ /************************************************************************************************/
+ /* EMU1010m HANA Destinations									*/
+ /************************************************************************************************/
++/* Hana, original 1010,1212,1820 using Alice2
++ * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
++ * 0x01, 0x10-0x1f: 32 Elink channels to Audio Dock
++ * 0x01, 0x00: Dock DAC 1 Left
++ * 0x01, 0x04: Dock DAC 1 Right
++ * 0x01, 0x08: Dock DAC 2 Left
++ * 0x01, 0x0c: Dock DAC 2 Right
++ * 0x01, 0x10: Dock DAC 3 Left
++ * 0x01, 0x12: PHONES Left
++ * 0x01, 0x14: Dock DAC 3 Right
++ * 0x01, 0x16: PHONES Right
++ * 0x01, 0x18: Dock DAC 4 Left
++ * 0x01, 0x1a: S/PDIF Left
++ * 0x01, 0x1c: Dock DAC 4 Right
++ * 0x01, 0x1e: S/PDIF Right
++ * 0x02, 0x00: Hana S/PDIF Left
++ * 0x02, 0x01: Hana S/PDIF Right
++ * 0x03, 0x00: Hanoa DAC Left
++ * 0x03, 0x01: Hanoa DAC Right
++ * 0x04, 0x00-0x07: Hana ADAT
++ * 0x05, 0x00: I2S0 Left to Alice2
++ * 0x05, 0x01: I2S0 Right to Alice2
++ * 0x06, 0x00: I2S0 Left to Alice2
++ * 0x06, 0x01: I2S0 Right to Alice2
++ * 0x07, 0x00: I2S0 Left to Alice2
++ * 0x07, 0x01: I2S0 Right to Alice2
++ *
++ * Hana2 never released, but used Tina
++ * Not needed.
++ *
++ * Hana3, rev2 1010,1212,1616 using Tina
++ * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina
++ * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock
++ * 0x01, 0x00: Dock DAC 1 Left
++ * 0x01, 0x04: Dock DAC 1 Right
++ * 0x01, 0x08: Dock DAC 2 Left
++ * 0x01, 0x0c: Dock DAC 2 Right
++ * 0x01, 0x10: Dock DAC 3 Left
++ * 0x01, 0x12: Dock S/PDIF Left
++ * 0x01, 0x14: Dock DAC 3 Right
++ * 0x01, 0x16: Dock S/PDIF Right
++ * 0x01, 0x18-0x1f: Dock ADAT 0-7
++ * 0x02, 0x00: Hana3 S/PDIF Left
++ * 0x02, 0x01: Hana3 S/PDIF Right
++ * 0x03, 0x00: Hanoa DAC Left
++ * 0x03, 0x01: Hanoa DAC Right
++ * 0x04, 0x00-0x07: Hana3 ADAT 0-7
++ * 0x05, 0x00-0x0f: 16 EMU32B channels to Tina
++ * 0x06-0x07: Not used
++ *
++ * HanaLite, rev1 0404 using Alice2
++ * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
++ * 0x01: Not used
++ * 0x02, 0x00: S/PDIF Left
++ * 0x02, 0x01: S/PDIF Right
++ * 0x03, 0x00: DAC Left
++ * 0x03, 0x01: DAC Right
++ * 0x04-0x07: Not used
++ *
++ * HanaLiteLite, rev2 0404 using Alice2
++ * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2
++ * 0x01: Not used
++ * 0x02, 0x00: S/PDIF Left
++ * 0x02, 0x01: S/PDIF Right
++ * 0x03, 0x00: DAC Left
++ * 0x03, 0x01: DAC Right
++ * 0x04-0x07: Not used
++ *
++ * Mana, Cardbus 1616 using Tina2
++ * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina2
++ * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock
++ * 0x01, 0x00: Dock DAC 1 Left
++ * 0x01, 0x04: Dock DAC 1 Right
++ * 0x01, 0x08: Dock DAC 2 Left
++ * 0x01, 0x0c: Dock DAC 2 Right
++ * 0x01, 0x10: Dock DAC 3 Left
++ * 0x01, 0x12: Dock S/PDIF Left
++ * 0x01, 0x14: Dock DAC 3 Right
++ * 0x01, 0x16: Dock S/PDIF Right
++ * 0x01, 0x18-0x1f: Dock ADAT 0-7
++ * 0x02: Not used
++ * 0x03, 0x00: Mana DAC Left
++ * 0x03, 0x01: Mana DAC Right
++ * 0x04, 0x00-0x0f: 16 EMU32B channels to Tina2
++ * 0x05-0x07: Not used
++ *
++ *
++ */
+ /* 32-bit destinations of signal in the Hana FPGA. Destinations are either
+  * physical outputs of Hana, or outputs going to Alice2 (audigy) for capture
+  * - 16 x EMU_DST_ALICE2_EMU32_X.
+@@ -1206,9 +1299,122 @@
+ #define EMU_DST_ALICE_I2S2_LEFT		0x0700	/* Alice2 I2S2 Left */
+ #define EMU_DST_ALICE_I2S2_RIGHT	0x0701	/* Alice2 I2S2 Right */
+ 
++/* Additional destinations for 1616(M)/Microdock */
++/* Microdock S/PDIF OUT Left, 1st or 48kHz only */
++#define EMU_DST_MDOCK_SPDIF_LEFT1	0x0112
++/* Microdock S/PDIF OUT Left, 2nd or 96kHz */
++#define EMU_DST_MDOCK_SPDIF_LEFT2	0x0113
++/* Microdock S/PDIF OUT Right, 1st or 48kHz only */
++#define EMU_DST_MDOCK_SPDIF_RIGHT1	0x0116
++/* Microdock S/PDIF OUT Right, 2nd or 96kHz  */
++#define EMU_DST_MDOCK_SPDIF_RIGHT2	0x0117
++/* Microdock S/PDIF ADAT 8 channel out +8 to +f */
++#define EMU_DST_MDOCK_ADAT		0x0118
++
++/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
++#define EMU_DST_MANA_DAC_LEFT		0x0300
++/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */
++#define EMU_DST_MANA_DAC_RIGHT		0x0301
++
+ /************************************************************************************************/
+ /* EMU1010m HANA Sources									*/
+ /************************************************************************************************/
++/* Hana, original 1010,1212,1820 using Alice2
++ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00,0x00-0x1f: Silence
++ * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
++ * 0x01, 0x00: Dock Mic A
++ * 0x01, 0x04: Dock Mic B
++ * 0x01, 0x08: Dock ADC 1 Left
++ * 0x01, 0x0c: Dock ADC 1 Right
++ * 0x01, 0x10: Dock ADC 2 Left
++ * 0x01, 0x14: Dock ADC 2 Right
++ * 0x01, 0x18: Dock ADC 3 Left
++ * 0x01, 0x1c: Dock ADC 3 Right
++ * 0x02, 0x00: Hana ADC Left
++ * 0x02, 0x01: Hana ADC Right
++ * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
++ * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
++ * 0x04, 0x00-0x07: Hana ADAT
++ * 0x05, 0x00: Hana S/PDIF Left
++ * 0x05, 0x01: Hana S/PDIF Right
++ * 0x06-0x07: Not used
++ *
++ * Hana2 never released, but used Tina
++ * Not needed.
++ *
++ * Hana3, rev2 1010,1212,1616 using Tina
++ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00,0x00-0x1f: Silence
++ * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
++ * 0x01, 0x00: Dock Mic A
++ * 0x01, 0x04: Dock Mic B
++ * 0x01, 0x08: Dock ADC 1 Left
++ * 0x01, 0x0c: Dock ADC 1 Right
++ * 0x01, 0x10: Dock ADC 2 Left
++ * 0x01, 0x12: Dock S/PDIF Left
++ * 0x01, 0x14: Dock ADC 2 Right
++ * 0x01, 0x16: Dock S/PDIF Right
++ * 0x01, 0x18-0x1f: Dock ADAT 0-7
++ * 0x01, 0x18: Dock ADC 3 Left
++ * 0x01, 0x1c: Dock ADC 3 Right
++ * 0x02, 0x00: Hanoa ADC Left
++ * 0x02, 0x01: Hanoa ADC Right
++ * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output
++ * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output
++ * 0x04, 0x00-0x07: Hana3 ADAT
++ * 0x05, 0x00: Hana3 S/PDIF Left
++ * 0x05, 0x01: Hana3 S/PDIF Right
++ * 0x06-0x07: Not used
++ *
++ * HanaLite, rev1 0404 using Alice2
++ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00,0x00-0x1f: Silence
++ * 0x01: Not used
++ * 0x02, 0x00: ADC Left
++ * 0x02, 0x01: ADC Right
++ * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
++ * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
++ * 0x04: Not used
++ * 0x05, 0x00: S/PDIF Left
++ * 0x05, 0x01: S/PDIF Right
++ * 0x06-0x07: Not used
++ *
++ * HanaLiteLite, rev2 0404 using Alice2
++ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00,0x00-0x1f: Silence
++ * 0x01: Not used
++ * 0x02, 0x00: ADC Left
++ * 0x02, 0x01: ADC Right
++ * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output
++ * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output
++ * 0x04: Not used
++ * 0x05, 0x00: S/PDIF Left
++ * 0x05, 0x01: S/PDIF Right
++ * 0x06-0x07: Not used
++ *
++ * Mana, Cardbus 1616 using Tina2
++ * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz
++ * 0x00,0x00-0x1f: Silence
++ * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock
++ * 0x01, 0x00: Dock Mic A
++ * 0x01, 0x04: Dock Mic B
++ * 0x01, 0x08: Dock ADC 1 Left
++ * 0x01, 0x0c: Dock ADC 1 Right
++ * 0x01, 0x10: Dock ADC 2 Left
++ * 0x01, 0x12: Dock S/PDIF Left
++ * 0x01, 0x14: Dock ADC 2 Right
++ * 0x01, 0x16: Dock S/PDIF Right
++ * 0x01, 0x18-0x1f: Dock ADAT 0-7
++ * 0x01, 0x18: Dock ADC 3 Left
++ * 0x01, 0x1c: Dock ADC 3 Right
++ * 0x02: Not used
++ * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output
++ * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output
++ * 0x04-0x07: Not used
++ *
++ */
++
+ /* 32-bit sources of signal in the Hana FPGA. The sources are routed to
+  * destinations using mixer control for each destination - see emumixer.c
+  * Sources are either physical inputs of FPGA,
+@@ -1263,6 +1469,19 @@
+ #define EMU_SRC_HANA_SPDIF_LEFT2	0x0502	/* Hana SPDIF Left, 2nd or 96kHz */
+ #define EMU_SRC_HANA_SPDIF_RIGHT1	0x0501	/* Hana SPDIF Right, 1st or 48kHz only */
+ #define EMU_SRC_HANA_SPDIF_RIGHT2	0x0503	/* Hana SPDIF Right, 2nd or 96kHz */
++
++/* Additional inputs for 1616(M)/Microdock */
++/* Microdock S/PDIF Left, 1st or 48kHz only */
++#define EMU_SRC_MDOCK_SPDIF_LEFT1	0x0112
++/* Microdock S/PDIF Left, 2nd or 96kHz */
++#define EMU_SRC_MDOCK_SPDIF_LEFT2	0x0113
++/* Microdock S/PDIF Right, 1st or 48kHz only */
++#define EMU_SRC_MDOCK_SPDIF_RIGHT1	0x0116
++/* Microdock S/PDIF Right, 2nd or 96kHz */
++#define EMU_SRC_MDOCK_SPDIF_RIGHT2	0x0117
++/* Microdock ADAT 8 channel in +8 to +f */
++#define EMU_SRC_MDOCK_ADAT		0x0118
++
+ /* 0x600 and 0x700 no used */
+ 
+ /* ------------------- STRUCTURES -------------------- */
+@@ -1423,6 +1642,14 @@ struct snd_emu10k1_midi {
+ 	void (*interrupt)(struct snd_emu10k1 *emu, unsigned int status);
+ };
+ 
++enum {
++	EMU_MODEL_SB,
++	EMU_MODEL_EMU1010,
++	EMU_MODEL_EMU1010B,
++	EMU_MODEL_EMU1616,
++	EMU_MODEL_EMU0404,
++};
++
+ struct snd_emu_chip_details {
+ 	u32 vendor;
+ 	u32 device;
+@@ -1439,7 +1666,7 @@ struct snd_emu_chip_details {
+ 	unsigned char spdif_bug;    /* Has Spdif phasing bug */
+ 	unsigned char ac97_chip;    /* Has an AC97 chip: 1 = mandatory, 2 = optional */
+ 	unsigned char ecard;        /* APS EEPROM */
+-	unsigned char emu1010;     /* EMU 1010m card */
++	unsigned char emu_model;     /* EMU model type */
+ 	unsigned char spi_dac;      /* SPI interface for DAC */
+ 	unsigned char i2c_adc;      /* I2C interface for ADC */
+ 	unsigned char adc_1361t;    /* Use Philips 1361T ADC */
+@@ -1515,6 +1742,8 @@ struct snd_emu10k1 {
+ 	spinlock_t reg_lock;
+ 	spinlock_t emu_lock;
+ 	spinlock_t voice_lock;
++	spinlock_t spi_lock; /* serialises access to spi port */
++	spinlock_t i2c_lock; /* serialises access to i2c port */
+ 
+ 	struct snd_emu10k1_voice voices[NUM_G];
+ 	struct snd_emu10k1_voice p16v_voices[4];
+diff --git a/include/sound/gus.h b/include/sound/gus.h
+index e5433d8..841bb8d 100644
+--- a/include/sound/gus.h
++++ b/include/sound/gus.h
+@@ -27,13 +27,8 @@
+ #include "timer.h"
+ #include "seq_midi_emul.h"
+ #include "seq_device.h"
+-#include "ainstr_iw.h"
+-#include "ainstr_gf1.h"
+-#include "ainstr_simple.h"
+ #include <asm/io.h>
+ 
+-#define SNDRV_SEQ_DEV_ID_GUS			"gus-synth"
+-
+ /* IO ports */
+ 
+ #define GUSP(gus, x)			((gus)->gf1.port + SNDRV_g_u_s_##x)
+@@ -234,16 +229,6 @@ struct snd_gus_port {
+ 
+ struct snd_gus_voice;
+ 
+-struct snd_gus_sample_ops {
+-	void (*sample_start)(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position);
+-	void (*sample_stop)(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode);
+-	void (*sample_freq)(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq);
+-	void (*sample_volume)(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume);
+-	void (*sample_loop)(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop);
+-	void (*sample_pos)(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position);
+-	void (*sample_private1)(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data);
+-};
+-
+ #define SNDRV_GF1_VOICE_TYPE_PCM	0
+ #define SNDRV_GF1_VOICE_TYPE_SYNTH 	1
+ #define SNDRV_GF1_VOICE_TYPE_MIDI	2
+@@ -284,12 +269,8 @@ struct snd_gus_voice {
+ 
+ 	struct snd_gus_sample_ops *sample_ops;
+ 
+-	struct snd_seq_instr instr;
+-
+ 	/* running status / registers */
+ 
+-	struct snd_seq_ev_volume sample_volume;
+-
+ 	unsigned short fc_register;
+ 	unsigned short fc_lfo;
+ 	unsigned short gf1_volume;
+@@ -382,10 +363,6 @@ struct snd_gf1 {
+ 
+ 	int seq_client;
+ 	struct snd_gus_port seq_ports[4];
+-	struct snd_seq_kinstr_list *ilist;
+-	struct snd_iwffff_ops iwffff_ops;
+-	struct snd_gf1_ops gf1_ops;
+-	struct snd_simple_ops simple_ops;
+ 
+ 	/* timer */
+ 
+@@ -458,8 +435,6 @@ struct snd_gus_card {
+ 	struct snd_rawmidi_substream *midi_substream_output;
+ 	struct snd_rawmidi_substream *midi_substream_input;
+ 
+-	struct snd_seq_device *seq_dev;
+-
+ 	spinlock_t reg_lock;
+ 	spinlock_t voice_alloc;
+ 	spinlock_t active_voice_lock;
+@@ -647,48 +622,10 @@ void snd_gus_irq_profile_init(struct snd_gus_card *gus);
+ 
+ int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi **rrawmidi);
+ 
+-#if 0
+-extern void snd_engine_instrument_register(unsigned short mode,
+-		struct _SND_INSTRUMENT_VOICE_COMMANDS *voice_cmds,
+-		struct _SND_INSTRUMENT_NOTE_COMMANDS *note_cmds,
+-	      	struct _SND_INSTRUMENT_CHANNEL_COMMANDS *channel_cmds);
+-extern int snd_engine_instrument_register_ask(unsigned short mode);
+-#endif
+-
+ /* gus_dram.c */
+ int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr,
+ 		       unsigned int addr, unsigned int size);
+ int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr,
+ 		      unsigned int addr, unsigned int size, int rom);
+ 
+-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+-
+-/* gus_sample.c */
+-void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p);
+-
+-/* gus_simple.c */
+-void snd_gf1_simple_init(struct snd_gus_voice *voice);
+-
+-/* gus_instr.c */
+-int snd_gus_iwffff_put_sample(void *private_data, struct iwffff_wave *wave,
+-			      char __user *data, long len, int atomic);
+-int snd_gus_iwffff_get_sample(void *private_data, struct iwffff_wave *wave,
+-			      char __user *data, long len, int atomic);
+-int snd_gus_iwffff_remove_sample(void *private_data, struct iwffff_wave *wave,
+-				 int atomic);
+-int snd_gus_gf1_put_sample(void *private_data, struct gf1_wave *wave,
+-			   char __user *data, long len, int atomic);
+-int snd_gus_gf1_get_sample(void *private_data, struct gf1_wave *wave,
+-			   char __user *data, long len, int atomic);
+-int snd_gus_gf1_remove_sample(void *private_data, struct gf1_wave *wave,
+-			      int atomic);
+-int snd_gus_simple_put_sample(void *private_data, struct simple_instrument *instr,
+-			      char __user *data, long len, int atomic);
+-int snd_gus_simple_get_sample(void *private_data, struct simple_instrument *instr,
+-			      char __user *data, long len, int atomic);
+-int snd_gus_simple_remove_sample(void *private_data, struct simple_instrument *instr,
+-				 int atomic);
+-
+-#endif /* CONFIG_SND_SEQUENCER */
+-
+ #endif /* __SOUND_GUS_H */
+diff --git a/include/sound/info.h b/include/sound/info.h
+index fecbb1f..8ae72e7 100644
+--- a/include/sound/info.h
++++ b/include/sound/info.h
+@@ -100,8 +100,10 @@ int snd_info_minor_unregister(void);
+ extern struct snd_info_entry *snd_seq_root;
+ #ifdef CONFIG_SND_OSSEMUL
+ extern struct snd_info_entry *snd_oss_root;
++void snd_card_info_read_oss(struct snd_info_buffer *buffer);
+ #else
+ #define snd_oss_root NULL
++static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
+ #endif
+ 
+ int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3)));
+diff --git a/include/sound/opl3.h b/include/sound/opl3.h
+index 1d14b3f..a0c5feb 100644
+--- a/include/sound/opl3.h
++++ b/include/sound/opl3.h
+@@ -51,19 +51,16 @@
+  *
+  */
+ 
+-#include "driver.h"
+-#include <linux/time.h>
+-#include <linux/mutex.h>
+-#include "core.h"
+-#include "hwdep.h"
+-#include "timer.h"
+-#include "seq_midi_emul.h"
++#include <sound/core.h>
++#include <sound/hwdep.h>
++#include <sound/timer.h>
++#include <sound/seq_midi_emul.h>
+ #ifdef CONFIG_SND_SEQUENCER_OSS
+-#include "seq_oss.h"
+-#include "seq_oss_legacy.h"
++#include <sound/seq_oss.h>
++#include <sound/seq_oss_legacy.h>
+ #endif
+-#include "seq_device.h"
+-#include "ainstr_fm.h"
++#include <sound/seq_device.h>
++#include <sound/asound_fm.h>
+ 
+ /*
+  *    Register numbers for the global registers
+@@ -240,6 +237,47 @@
+ struct snd_opl3;
+ 
+ /*
++ * Instrument record, aka "Patch"
++ */
++
++/* FM operator */
++struct fm_operator {
++	unsigned char am_vib;
++	unsigned char ksl_level;
++	unsigned char attack_decay;
++	unsigned char sustain_release;
++	unsigned char wave_select;
++} __attribute__((packed));
++
++/* Instrument data */
++struct fm_instrument {
++	struct fm_operator op[4];
++	unsigned char feedback_connection[2];
++	unsigned char echo_delay;
++	unsigned char echo_atten;
++	unsigned char chorus_spread;
++	unsigned char trnsps;
++	unsigned char fix_dur;
++	unsigned char modes;
++	unsigned char fix_key;
++};
++
++/* type */
++#define FM_PATCH_OPL2	0x01		/* OPL2 2 operators FM instrument */
++#define FM_PATCH_OPL3	0x02		/* OPL3 4 operators FM instrument */
++
++/* Instrument record */
++struct fm_patch {
++	unsigned char prog;
++	unsigned char bank;
++	unsigned char type;
++	struct fm_instrument inst;
++	char name[24];
++	struct fm_patch *next;
++};
++
++
++/*
+  * A structure to keep track of each hardware voice
+  */
+ struct snd_opl3_voice {
+@@ -277,9 +315,9 @@ struct snd_opl3 {
+ 	void *private_data;
+ 	void (*private_free)(struct snd_opl3 *);
+ 
++	struct snd_hwdep *hwdep;
+ 	spinlock_t reg_lock;
+ 	struct snd_card *card;		/* The card that this belongs to */
+-	int used;			/* usage flag - exclusive */
+ 	unsigned char fm_mode;		/* OPL mode, see SNDRV_DM_FM_MODE_XXX */
+ 	unsigned char rhythm;		/* percussion mode flag */
+ 	unsigned char max_voices;	/* max number of voices */
+@@ -297,8 +335,8 @@ struct snd_opl3 {
+ 	struct snd_midi_channel_set * oss_chset;
+ #endif
+  
+-	struct snd_seq_kinstr_ops fm_ops;
+-	struct snd_seq_kinstr_list *ilist;
++#define OPL3_PATCH_HASH_SIZE	32
++	struct fm_patch *patch_table[OPL3_PATCH_HASH_SIZE];
+ 
+ 	struct snd_opl3_voice voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */
+ 	int use_time;			/* allocation counter */
+@@ -312,7 +350,6 @@ struct snd_opl3 {
+ 	int sys_timer_status;		/* system timer run status */
+ 	spinlock_t sys_timer_lock;	/* Lock for system timer access */
+ #endif
+-	struct mutex access_mutex;	/* locking */
+ };
+ 
+ /* opl3.c */
+@@ -333,8 +370,19 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, int device, int seq_device,
+ int snd_opl3_open(struct snd_hwdep * hw, struct file *file);
+ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
+ 		   unsigned int cmd, unsigned long arg);
++long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
++		    loff_t *offset);
+ int snd_opl3_release(struct snd_hwdep * hw, struct file *file);
+ 
+ void snd_opl3_reset(struct snd_opl3 * opl3);
+ 
++int snd_opl3_load_patch(struct snd_opl3 *opl3,
++			int prog, int bank, int type,
++			const char *name,
++			const unsigned char *ext,
++			const unsigned char *data);
++struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
++				     int create_patch);
++void snd_opl3_clear_patches(struct snd_opl3 *opl3);
++
+ #endif /* __SOUND_OPL3_H */
+diff --git a/include/sound/pcm.h b/include/sound/pcm.h
+index 5e9cc46..51d58cc 100644
+--- a/include/sound/pcm.h
++++ b/include/sound/pcm.h
+@@ -274,7 +274,6 @@ struct snd_pcm_runtime {
+ 	snd_pcm_uframes_t period_size;	/* period size */
+ 	unsigned int periods;		/* periods */
+ 	snd_pcm_uframes_t buffer_size;	/* buffer size */
+-	unsigned int tick_time;		/* tick time */
+ 	snd_pcm_uframes_t min_align;	/* Min alignment for the format */
+ 	size_t byte_align;
+ 	unsigned int frame_bits;
+@@ -286,8 +285,6 @@ struct snd_pcm_runtime {
+ 	/* -- SW params -- */
+ 	int tstamp_mode;		/* mmap timestamp is updated */
+   	unsigned int period_step;
+-	unsigned int sleep_min;		/* min ticks to sleep */
+-	snd_pcm_uframes_t xfer_align;	/* xfer size need to be a multiple */
+ 	snd_pcm_uframes_t start_threshold;
+ 	snd_pcm_uframes_t stop_threshold;
+ 	snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
+@@ -306,7 +303,6 @@ struct snd_pcm_runtime {
+ 
+ 	/* -- locking / scheduling -- */
+ 	wait_queue_head_t sleep;
+-	struct timer_list tick_timer;
+ 	struct fasync_struct *fasync;
+ 
+ 	/* -- private section -- */
+@@ -323,6 +319,7 @@ struct snd_pcm_runtime {
+ 
+ 	/* -- timer -- */
+ 	unsigned int timer_resolution;	/* timer resolution */
++	int tstamp_type;		/* timestamp type */
+ 
+ 	/* -- DMA -- */           
+ 	unsigned char *dma_area;	/* DMA area */
+@@ -810,7 +807,6 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc
+ #define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min
+ #define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min
+ #define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min
+-#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min
+ 
+ 
+ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
+@@ -908,9 +904,6 @@ int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream);
+ int snd_pcm_playback_xrun_asap(struct snd_pcm_substream *substream);
+ int snd_pcm_capture_xrun_asap(struct snd_pcm_substream *substream);
+ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr);
+-void snd_pcm_tick_prepare(struct snd_pcm_substream *substream);
+-void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks);
+-void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream);
+ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
+ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream,
+ 				    const void __user *buf,
+@@ -952,6 +945,15 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
+ void snd_pcm_timer_init(struct snd_pcm_substream *substream);
+ void snd_pcm_timer_done(struct snd_pcm_substream *substream);
+ 
++static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
++				   struct timespec *tv)
++{
++	if (runtime->tstamp_type == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
++		do_posix_clock_monotonic_gettime(tv);
++	else
++		getnstimeofday(tv);
++}
++
+ /*
+  *  Memory
+  */
+diff --git a/include/sound/seq_instr.h b/include/sound/seq_instr.h
+deleted file mode 100644
+index 93b0c51..0000000
+--- a/include/sound/seq_instr.h
++++ /dev/null
+@@ -1,110 +0,0 @@
+-#ifndef __SOUND_SEQ_INSTR_H
+-#define __SOUND_SEQ_INSTR_H
+-
+-/*
+- *  Main kernel header file for the ALSA sequencer
+- *  Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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 "seq_kernel.h"
+-
+-/* Instrument cluster */
+-struct snd_seq_kcluster {
+-	snd_seq_instr_cluster_t cluster;
+-	char name[32];
+-	int priority;
+-	struct snd_seq_kcluster *next;
+-};
+-
+-/* return pointer to private data */
+-#define KINSTR_DATA(kinstr)	(void *)(((char *)kinstr) + sizeof(struct snd_seq_kinstr))
+-
+-/* Instrument structure */
+-struct snd_seq_kinstr {
+-	struct snd_seq_instr instr;
+-	char name[32];
+-	int type;			/* instrument type */
+-	int use;			/* use count */
+-	int busy;			/* not useable */
+-	int add_len;			/* additional length */
+-	struct snd_seq_kinstr_ops *ops;	/* operations */
+-	struct snd_seq_kinstr *next;
+-};
+-
+-#define SNDRV_SEQ_INSTR_HASH_SIZE		32
+-
+-/* Instrument flags */
+-#define SNDRV_SEQ_INSTR_FLG_DIRECT	(1<<0)	/* accept only direct events */
+-
+-/* List of all instruments */
+-struct snd_seq_kinstr_list {
+-	struct snd_seq_kinstr *hash[SNDRV_SEQ_INSTR_HASH_SIZE];
+-	int count;			/* count of all instruments */
+-	
+-	struct snd_seq_kcluster *chash[SNDRV_SEQ_INSTR_HASH_SIZE];
+-	int ccount;			/* count of all clusters */
+-
+-	int owner;			/* current owner of the instrument list */
+-	unsigned int flags;
+-
+-	spinlock_t lock;
+-	spinlock_t ops_lock;
+-	struct mutex ops_mutex;
+-	unsigned long ops_flags;
+-};
+-
+-#define SNDRV_SEQ_INSTR_NOTIFY_REMOVE	0
+-#define SNDRV_SEQ_INSTR_NOTIFY_CHANGE	1
+-
+-struct snd_seq_kinstr_ops {
+-	void *private_data;
+-	long add_len;			/* additional length */
+-	char *instr_type;
+-	int (*info)(void *private_data, char *info_data, long len);
+-	int (*put)(void *private_data, struct snd_seq_kinstr *kinstr,
+-		   char __user *instr_data, long len, int atomic, int cmd);
+-	int (*get)(void *private_data, struct snd_seq_kinstr *kinstr,
+-		   char __user *instr_data, long len, int atomic, int cmd);
+-	int (*get_size)(void *private_data, struct snd_seq_kinstr *kinstr, long *size);
+-	int (*remove)(void *private_data, struct snd_seq_kinstr *kinstr, int atomic);
+-	void (*notify)(void *private_data, struct snd_seq_kinstr *kinstr, int what);
+-	struct snd_seq_kinstr_ops *next;
+-};
+-
+-
+-/* instrument operations */
+-struct snd_seq_kinstr_list *snd_seq_instr_list_new(void);
+-void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list);
+-int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
+-				 struct snd_seq_instr_header *ifree,
+-				 int client,
+-				 int atomic);
+-struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
+-					  struct snd_seq_instr *instr,
+-					  int exact,
+-					  int follow_alias);
+-void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
+-			    struct snd_seq_kinstr *instr);
+-int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
+-			struct snd_seq_kinstr_list *list,
+-			struct snd_seq_event *ev,
+-			int client,
+-			int atomic,
+-			int hop);
+-
+-#endif /* __SOUND_SEQ_INSTR_H */
+diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
+index 2b1ae8e..a105b01 100644
+--- a/include/sound/soc-dapm.h
++++ b/include/sound/soc-dapm.h
+@@ -22,7 +22,7 @@
+ #define SND_SOC_NOPM	-1
+ 
+ /*
+- * SoC dynamic audio power managment
++ * SoC dynamic audio power management
+  *
+  * We can have upto 4 power domains
+  * 	1. Codec domain - VREF, VMID
+@@ -131,18 +131,34 @@
+ 	.shift = wshift, .invert = winvert}
+ 
+ /* dapm kcontrol types */
+-#define SOC_DAPM_SINGLE(xname, reg, shift, mask, invert) \
++#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
+ {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ 	.info = snd_soc_info_volsw, \
+ 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+-	.private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+-#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, \
++	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
++#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, max, invert, \
+ 	power) \
+ {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ 	.info = snd_soc_info_volsw, \
+  	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+  	.private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
+- 		 ((mask) << 16) | ((invert) << 24) }
++		((max) << 16) | ((invert) << 24) }
++#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
++{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
++	.info = snd_soc_info_volsw, \
++	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.tlv.p = (tlv_array), \
++	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
++	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
++#define SOC_DAPM_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, \
++	power, tlv_array) \
++{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
++	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.tlv.p = (tlv_array), \
++	.info = snd_soc_info_volsw, \
++	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
++	.private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
++		((max) << 16) | ((invert) << 24) }
+ #define SOC_DAPM_ENUM(xname, xenum) \
+ {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ 	.info = snd_soc_info_enum_double, \
+@@ -199,6 +215,7 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev);
+ /* dapm events */
+ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
+ 	int event);
++int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event);
+ 
+ /* dapm sys fs - used by the core */
+ int snd_soc_dapm_sys_add(struct device *dev);
+@@ -272,7 +289,7 @@ struct snd_soc_dapm_widget {
+ 
+ 	/* external events */
+ 	unsigned short event_flags;		/* flags to specify event types */
+-	int (*event)(struct snd_soc_dapm_widget*, int);
++	int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
+ 
+ 	/* kcontrols that relate to this widget */
+ 	int num_kcontrols;
+diff --git a/include/sound/soc.h b/include/sound/soc.h
+index f47ef1f..e6ea6f7 100644
+--- a/include/sound/soc.h
++++ b/include/sound/soc.h
+@@ -16,38 +16,63 @@
+ #include <linux/platform_device.h>
+ #include <linux/types.h>
+ #include <linux/workqueue.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/control.h>
+ #include <sound/ac97_codec.h>
+ 
+-#define SND_SOC_VERSION "0.13.1"
++#define SND_SOC_VERSION "0.13.2"
+ 
+ /*
+  * Convenience kcontrol builders
+  */
+-#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) |\
+-	((shift) << 12) | ((mask) << 16) | ((invert) << 24))
+-#define SOC_SINGLE_VALUE_EXT(reg,mask,invert) ((reg) | ((mask) << 16) |\
++#define SOC_SINGLE_VALUE(reg, shift, max, invert) ((reg) | ((shift) << 8) |\
++	((shift) << 12) | ((max) << 16) | ((invert) << 24))
++#define SOC_SINGLE_VALUE_EXT(reg, max, invert) ((reg) | ((max) << 16) |\
+ 	((invert) << 31))
+-#define SOC_SINGLE(xname, reg, shift, mask, invert) \
++#define SOC_SINGLE(xname, reg, shift, max, invert) \
+ {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+ 	.put = snd_soc_put_volsw, \
+-	.private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+-#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
++	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
++#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
++{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
++	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
++		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.tlv.p = (tlv_array), \
++	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
++	.put = snd_soc_put_volsw, \
++	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
++#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
+ {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+ 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
+ 	.put = snd_soc_put_volsw, \
+ 	.private_value = (reg) | ((shift_left) << 8) | \
+-		((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
+-#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) \
++		((shift_right) << 12) | ((max) << 16) | ((invert) << 24) }
++#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, max, invert) \
+ {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ 	.info = snd_soc_info_volsw_2r, \
+ 	.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
+ 	.private_value = (reg_left) | ((shift) << 8)  | \
+-		((mask) << 12) | ((invert) << 20) | ((reg_right) << 24) }
++		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
++#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
++{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
++	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
++		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.tlv.p = (tlv_array), \
++	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
++	.put = snd_soc_put_volsw, \
++	.private_value = (reg) | ((shift_left) << 8) | \
++		((shift_right) << 12) | ((max) << 16) | ((invert) << 24) }
++#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \
++{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
++	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
++		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
++	.tlv.p = (tlv_array), \
++	.info = snd_soc_info_volsw_2r, \
++	.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
++	.private_value = (reg_left) | ((shift) << 8)  | \
++		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+ #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
+ {	.reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+ 	.mask = xmask, .texts = xtexts }
+@@ -105,9 +130,21 @@
+ #define SND_SOC_DAIFMT_GATED		(1 << 4)	/* clock is gated when not Tx/Rx */
+ 
+ /*
++ * DAI Sync
++ * Synchronous LR (Left Right) clocks and Frame signals.
++ */
++#define SND_SOC_DAIFMT_SYNC		(0 << 5)	/* Tx FRM = Rx FRM */
++#define SND_SOC_DAIFMT_ASYNC		(1 << 5)	/* Tx FRM ~ Rx FRM */
++
++/*
++ * TDM
++ */
++#define SND_SOC_DAIFMT_TDM		(1 << 6)
++
++/*
+  * DAI hardware signal inversions
+  */
+-#define SND_SOC_DAIFMT_NB_NF		(0 << 8)	/* normal bit clock + frame */
++#define SND_SOC_DAIFMT_NB_NF		(0 << 8)	/* normal bclk + frm */
+ #define SND_SOC_DAIFMT_NB_IF		(1 << 8)	/* normal bclk + inv frm */
+ #define SND_SOC_DAIFMT_IB_NF		(2 << 8)	/* invert bclk + nor frm */
+ #define SND_SOC_DAIFMT_IB_IF		(3 << 8)	/* invert bclk + frm */
+@@ -410,6 +447,9 @@ struct snd_soc_dai_link  {
+ 
+ 	/* codec/machine specific init - e.g. add machine controls */
+ 	int (*init)(struct snd_soc_codec *codec);
++
++	/* DAI pcm */
++	struct snd_pcm *pcm;
+ };
+ 
+ /* SoC machine */
+@@ -426,6 +466,9 @@ struct snd_soc_machine {
+ 	int (*resume_pre)(struct platform_device *pdev);
+ 	int (*resume_post)(struct platform_device *pdev);
+ 
++	/* callbacks */
++	int (*dapm_event)(struct snd_soc_machine *, int event);
++
+ 	/* CPU <--> Codec DAI links  */
+ 	struct snd_soc_dai_link *dai_link;
+ 	int num_links;
+diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h
+index e8eeb3a..b62ce3e 100644
+--- a/include/sound/tea575x-tuner.h
++++ b/include/sound/tea575x-tuner.h
+@@ -30,6 +30,7 @@ struct snd_tea575x;
+ struct snd_tea575x_ops {
+ 	void (*write)(struct snd_tea575x *tea, unsigned int val);
+ 	unsigned int (*read)(struct snd_tea575x *tea);
++	void (*mute)(struct snd_tea575x *tea, unsigned int mute);
+ };
+ 
+ struct snd_tea575x {
+diff --git a/include/sound/trident.h b/include/sound/trident.h
+index 9752243..9f191a0 100644
+--- a/include/sound/trident.h
++++ b/include/sound/trident.h
+@@ -26,19 +26,12 @@
+ #include "pcm.h"
+ #include "mpu401.h"
+ #include "ac97_codec.h"
+-#include "seq_midi_emul.h"
+-#include "seq_device.h"
+ #include "util_mem.h"
+-//#include "ainstr_iw.h"
+-//#include "ainstr_gf1.h"
+-#include "ainstr_simple.h"
+ 
+ #define TRIDENT_DEVICE_ID_DX		((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX)
+ #define TRIDENT_DEVICE_ID_NX		((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX)
+ #define TRIDENT_DEVICE_ID_SI7018	((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018)
+ 
+-#define SNDRV_SEQ_DEV_ID_TRIDENT			"trident-synth"
+-
+ #define SNDRV_TRIDENT_VOICE_TYPE_PCM		0
+ #define SNDRV_TRIDENT_VOICE_TYPE_SYNTH		1
+ #define SNDRV_TRIDENT_VOICE_TYPE_MIDI		2
+@@ -257,16 +250,6 @@ struct snd_trident;
+ struct snd_trident_voice;
+ struct snd_trident_pcm_mixer;
+ 
+-struct snd_trident_sample_ops {
+-	void (*sample_start)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_position_t position);
+-	void (*sample_stop)(struct snd_trident *gus, struct snd_trident_voice *voice, int mode);
+-	void (*sample_freq)(struct snd_trident *gus, struct snd_trident_voice *voice, snd_seq_frequency_t freq);
+-	void (*sample_volume)(struct snd_trident *gus, struct snd_trident_voice *voice, struct snd_seq_ev_volume *volume);
+-	void (*sample_loop)(struct snd_trident *card, struct snd_trident_voice *voice, struct snd_seq_ev_loop *loop);
+-	void (*sample_pos)(struct snd_trident *card, struct snd_trident_voice *voice, snd_seq_position_t position);
+-	void (*sample_private1)(struct snd_trident *card, struct snd_trident_voice *voice, unsigned char *data);
+-};
+-
+ struct snd_trident_port {
+ 	struct snd_midi_channel_set * chset;
+ 	struct snd_trident * trident;
+@@ -300,7 +283,6 @@ struct snd_trident_voice {
+ 	unsigned char port;
+ 	unsigned char index;
+ 
+-	struct snd_seq_instr instr;
+ 	struct snd_trident_sample_ops *sample_ops;
+ 
+ 	/* channel parameters */
+@@ -354,9 +336,6 @@ struct snd_4dwave {
+ 	int seq_client;
+ 
+ 	struct snd_trident_port seq_ports[4];
+-	struct snd_simple_ops simple_ops;
+-	struct snd_seq_kinstr_list *ilist;
+-
+ 	struct snd_trident_voice voices[64];	
+ 
+ 	int ChanSynthCount;		/* number of allocated synth channels */
+@@ -416,7 +395,6 @@ struct snd_trident {
+ 	struct snd_pcm *foldback;	/* Foldback PCM */
+ 	struct snd_pcm *spdif;	/* SPDIF PCM */
+ 	struct snd_rawmidi *rmidi;
+-	struct snd_seq_device *seq_dev;
+ 
+ 	struct snd_ac97_bus *ac97_bus;
+ 	struct snd_ac97 *ac97;
+diff --git a/include/sound/version.h b/include/sound/version.h
+index a9781eb..fac66c4 100644
+--- a/include/sound/version.h
++++ b/include/sound/version.h
+@@ -1,3 +1,3 @@
+ /* include/version.h.  Generated by alsa/ksync script.  */
+-#define CONFIG_SND_VERSION "1.0.15"
+-#define CONFIG_SND_DATE " (Tue Nov 20 19:16:42 2007 UTC)"
++#define CONFIG_SND_VERSION "1.0.16rc2"
++#define CONFIG_SND_DATE " (Thu Jan 31 16:40:16 2008 UTC)"
 diff --git a/include/xen/page.h b/include/xen/page.h
 index c0c8fcb..031ef22 100644
 --- a/include/xen/page.h
@@ -818618,7 +825591,7 @@
  #define __pte_ma(x)	((pte_t) { (x) } )
  #endif	/* CONFIG_X86_PAE */
 diff --git a/init/Kconfig b/init/Kconfig
-index b9d11a8..0d0bbf2 100644
+index b9d11a8..dcc96a8 100644
 --- a/init/Kconfig
 +++ b/init/Kconfig
 @@ -1,3 +1,11 @@
@@ -818650,7 +825623,7 @@
  	default y
  	help
  	  This option creates deprecated symlinks such as the
-@@ -762,3 +771,31 @@ source "block/Kconfig"
+@@ -762,3 +771,39 @@ source "block/Kconfig"
  
  config PREEMPT_NOTIFIERS
  	bool
@@ -818658,6 +825631,14 @@
 +choice
 +	prompt "RCU implementation type:"
 +	default CLASSIC_RCU
++	help
++	  This allows you to choose either the classic RCU implementation
++	  that is designed for best read-side performance on non-realtime
++	  systems, or the preemptible RCU implementation for best latency
++	  on realtime systems.  Note that some kernel preemption modes
++	  will restrict your choice.
++
++	  Select the default if you are unsure.
 +
 +config CLASSIC_RCU
 +	bool "Classic RCU"
@@ -819428,6 +826409,139 @@
   * ABBA deadlock.
   */
  
+diff --git a/kernel/exit.c b/kernel/exit.c
+index 549c055..bfb1c0e 100644
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -249,7 +249,7 @@ static int has_stopped_jobs(struct pid *pgrp)
+ 	struct task_struct *p;
+ 
+ 	do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
+-		if (p->state != TASK_STOPPED)
++		if (!task_is_stopped(p))
+ 			continue;
+ 		retval = 1;
+ 		break;
+@@ -614,7 +614,7 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced)
+ 		p->parent = p->real_parent;
+ 		add_parent(p);
+ 
+-		if (p->state == TASK_TRACED) {
++		if (task_is_traced(p)) {
+ 			/*
+ 			 * If it was at a trace stop, turn it into
+ 			 * a normal stop since it's no longer being
+@@ -1563,60 +1563,51 @@ repeat:
+ 			}
+ 			allowed = 1;
+ 
+-			switch (p->state) {
+-			case TASK_TRACED:
+-				/*
+-				 * When we hit the race with PTRACE_ATTACH,
+-				 * we will not report this child.  But the
+-				 * race means it has not yet been moved to
+-				 * our ptrace_children list, so we need to
+-				 * set the flag here to avoid a spurious ECHILD
+-				 * when the race happens with the only child.
+-				 */
+-				flag = 1;
+-				if (!my_ptrace_child(p))
+-					continue;
+-				/*FALLTHROUGH*/
+-			case TASK_STOPPED:
++			if (task_is_stopped_or_traced(p)) {
+ 				/*
+ 				 * It's stopped now, so it might later
+ 				 * continue, exit, or stop again.
++				 *
++				 * When we hit the race with PTRACE_ATTACH, we
++				 * will not report this child.  But the race
++				 * means it has not yet been moved to our
++				 * ptrace_children list, so we need to set the
++				 * flag here to avoid a spurious ECHILD when
++				 * the race happens with the only child.
+ 				 */
+ 				flag = 1;
+-				if (!(options & WUNTRACED) &&
+-				    !my_ptrace_child(p))
+-					continue;
++
++				if (!my_ptrace_child(p)) {
++					if (task_is_traced(p))
++						continue;
++					if (!(options & WUNTRACED))
++						continue;
++				}
++
+ 				retval = wait_task_stopped(p, ret == 2,
+-							   (options & WNOWAIT),
+-							   infop,
+-							   stat_addr, ru);
++						(options & WNOWAIT), infop,
++						stat_addr, ru);
+ 				if (retval == -EAGAIN)
+ 					goto repeat;
+ 				if (retval != 0) /* He released the lock.  */
+ 					goto end;
+-				break;
+-			default:
+-			// case EXIT_DEAD:
+-				if (p->exit_state == EXIT_DEAD)
++			} else if (p->exit_state == EXIT_DEAD) {
++				continue;
++			} else if (p->exit_state == EXIT_ZOMBIE) {
++				/*
++				 * Eligible but we cannot release it yet:
++				 */
++				if (ret == 2)
++					goto check_continued;
++				if (!likely(options & WEXITED))
+ 					continue;
+-			// case EXIT_ZOMBIE:
+-				if (p->exit_state == EXIT_ZOMBIE) {
+-					/*
+-					 * Eligible but we cannot release
+-					 * it yet:
+-					 */
+-					if (ret == 2)
+-						goto check_continued;
+-					if (!likely(options & WEXITED))
+-						continue;
+-					retval = wait_task_zombie(
+-						p, (options & WNOWAIT),
+-						infop, stat_addr, ru);
+-					/* He released the lock.  */
+-					if (retval != 0)
+-						goto end;
+-					break;
+-				}
++				retval = wait_task_zombie(p,
++						(options & WNOWAIT), infop,
++						stat_addr, ru);
++				/* He released the lock.  */
++				if (retval != 0)
++					goto end;
++			} else {
+ check_continued:
+ 				/*
+ 				 * It's running now, so it might later
+@@ -1625,12 +1616,11 @@ check_continued:
+ 				flag = 1;
+ 				if (!unlikely(options & WCONTINUED))
+ 					continue;
+-				retval = wait_task_continued(
+-					p, (options & WNOWAIT),
+-					infop, stat_addr, ru);
++				retval = wait_task_continued(p,
++						(options & WNOWAIT), infop,
++						stat_addr, ru);
+ 				if (retval != 0) /* He released the lock.  */
+ 					goto end;
+-				break;
+ 			}
+ 		}
+ 		if (!flag) {
 diff --git a/kernel/extable.c b/kernel/extable.c
 index 7fe2628..a26cb2e 100644
 --- a/kernel/extable.c
@@ -821061,6 +828175,81 @@
  #ifdef CONFIG_MODVERSIONS
  /* Generate the signature for struct module here, too, for modversions. */
  void struct_module(struct module *mod) { return; }
+diff --git a/kernel/mutex.c b/kernel/mutex.c
+index d7fe50c..d9ec9b6 100644
+--- a/kernel/mutex.c
++++ b/kernel/mutex.c
+@@ -166,9 +166,12 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
+ 		 * got a signal? (This code gets eliminated in the
+ 		 * TASK_UNINTERRUPTIBLE case.)
+ 		 */
+-		if (unlikely(state == TASK_INTERRUPTIBLE &&
+-						signal_pending(task))) {
+-			mutex_remove_waiter(lock, &waiter, task_thread_info(task));
++		if (unlikely((state == TASK_INTERRUPTIBLE &&
++					signal_pending(task)) ||
++			      (state == TASK_KILLABLE &&
++					fatal_signal_pending(task)))) {
++			mutex_remove_waiter(lock, &waiter,
++					    task_thread_info(task));
+ 			mutex_release(&lock->dep_map, 1, ip);
+ 			spin_unlock_mutex(&lock->wait_lock, flags);
+ 
+@@ -211,6 +214,14 @@ mutex_lock_nested(struct mutex *lock, unsigned int subclass)
+ EXPORT_SYMBOL_GPL(mutex_lock_nested);
+ 
+ int __sched
++mutex_lock_killable_nested(struct mutex *lock, unsigned int subclass)
++{
++	might_sleep();
++	return __mutex_lock_common(lock, TASK_KILLABLE, subclass, _RET_IP_);
++}
++EXPORT_SYMBOL_GPL(mutex_lock_killable_nested);
++
++int __sched
+ mutex_lock_interruptible_nested(struct mutex *lock, unsigned int subclass)
+ {
+ 	might_sleep();
+@@ -272,6 +283,9 @@ __mutex_unlock_slowpath(atomic_t *lock_count)
+  * mutex_lock_interruptible() and mutex_trylock().
+  */
+ static int fastcall noinline __sched
++__mutex_lock_killable_slowpath(atomic_t *lock_count);
++
++static noinline int fastcall __sched
+ __mutex_lock_interruptible_slowpath(atomic_t *lock_count);
+ 
+ /***
+@@ -294,6 +308,14 @@ int fastcall __sched mutex_lock_interruptible(struct mutex *lock)
+ 
+ EXPORT_SYMBOL(mutex_lock_interruptible);
+ 
++int fastcall __sched mutex_lock_killable(struct mutex *lock)
++{
++	might_sleep();
++	return __mutex_fastpath_lock_retval
++			(&lock->count, __mutex_lock_killable_slowpath);
++}
++EXPORT_SYMBOL(mutex_lock_killable);
++
+ static void fastcall noinline __sched
+ __mutex_lock_slowpath(atomic_t *lock_count)
+ {
+@@ -303,6 +325,14 @@ __mutex_lock_slowpath(atomic_t *lock_count)
+ }
+ 
+ static int fastcall noinline __sched
++__mutex_lock_killable_slowpath(atomic_t *lock_count)
++{
++	struct mutex *lock = container_of(lock_count, struct mutex, count);
++
++	return __mutex_lock_common(lock, TASK_KILLABLE, 0, _RET_IP_);
++}
++
++static noinline int fastcall __sched
+ __mutex_lock_interruptible_slowpath(atomic_t *lock_count)
+ {
+ 	struct mutex *lock = container_of(lock_count, struct mutex, count);
 diff --git a/kernel/panic.c b/kernel/panic.c
 index da4d6ba..d9e90cf 100644
 --- a/kernel/panic.c
@@ -821451,8 +828640,33 @@
  /* Preferred image size in bytes (default 500 MB) */
  extern unsigned long image_size;
  extern int in_suspend;
+diff --git a/kernel/power/process.c b/kernel/power/process.c
+index 6533923..7c2118f 100644
+--- a/kernel/power/process.c
++++ b/kernel/power/process.c
+@@ -86,9 +86,9 @@ static void fake_signal_wake_up(struct task_struct *p, int resume)
+ 
+ static void send_fake_signal(struct task_struct *p)
+ {
+-	if (p->state == TASK_STOPPED)
++	if (task_is_stopped(p))
+ 		force_sig_specific(SIGSTOP, p);
+-	fake_signal_wake_up(p, p->state == TASK_STOPPED);
++	fake_signal_wake_up(p, task_is_stopped(p));
+ }
+ 
+ static int has_mm(struct task_struct *p)
+@@ -182,7 +182,7 @@ static int try_to_freeze_tasks(int freeze_user_space)
+ 			if (frozen(p) || !freezeable(p))
+ 				continue;
+ 
+-			if (p->state == TASK_TRACED && frozen(p->parent)) {
++			if (task_is_traced(p) && frozen(p->parent)) {
+ 				cancel_freezing(p);
+ 				continue;
+ 			}
 diff --git a/kernel/printk.c b/kernel/printk.c
-index 89011bf..58bbec6 100644
+index 89011bf..29ae1e9 100644
 --- a/kernel/printk.c
 +++ b/kernel/printk.c
 @@ -36,6 +36,13 @@
@@ -821469,6 +828683,19 @@
  #define __LOG_BUF_LEN	(1 << CONFIG_LOG_BUF_SHIFT)
  
  /* printk's without a loglevel use this.. */
+@@ -448,10 +455,10 @@ static int __init ignore_loglevel_setup(char *str)
+ 	ignore_loglevel = 1;
+ 	printk(KERN_INFO "debug: ignoring loglevel setting.\n");
+ 
+-	return 1;
++	return 0;
+ }
+ 
+-__setup("ignore_loglevel", ignore_loglevel_setup);
++early_param("ignore_loglevel", ignore_loglevel_setup);
+ 
+ /*
+  * Write out chars from start to end - 1 inclusive
 @@ -573,11 +580,6 @@ static int __init printk_time_setup(char *str)
  
  __setup("time", printk_time_setup);
@@ -821809,9 +829036,39 @@
  	entry->proc_fops = &proc_profile_operations;
  	entry->size = (1+prof_len) * sizeof(atomic_t);
 diff --git a/kernel/ptrace.c b/kernel/ptrace.c
-index c25db86..e6e9b8b 100644
+index c25db86..b0d4ab4 100644
 --- a/kernel/ptrace.c
 +++ b/kernel/ptrace.c
+@@ -51,7 +51,7 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
+ void ptrace_untrace(struct task_struct *child)
+ {
+ 	spin_lock(&child->sighand->siglock);
+-	if (child->state == TASK_TRACED) {
++	if (task_is_traced(child)) {
+ 		if (child->signal->flags & SIGNAL_STOP_STOPPED) {
+ 			child->state = TASK_STOPPED;
+ 		} else {
+@@ -79,7 +79,7 @@ void __ptrace_unlink(struct task_struct *child)
+ 		add_parent(child);
+ 	}
+ 
+-	if (child->state == TASK_TRACED)
++	if (task_is_traced(child))
+ 		ptrace_untrace(child);
+ }
+ 
+@@ -103,9 +103,9 @@ int ptrace_check_attach(struct task_struct *child, int kill)
+ 	    && child->signal != NULL) {
+ 		ret = 0;
+ 		spin_lock_irq(&child->sighand->siglock);
+-		if (child->state == TASK_STOPPED) {
++		if (task_is_stopped(child)) {
+ 			child->state = TASK_TRACED;
+-		} else if (child->state != TASK_TRACED && !kill) {
++		} else if (!task_is_traced(child) && !kill) {
+ 			ret = -ESRCH;
+ 		}
+ 		spin_unlock_irq(&child->sighand->siglock);
 @@ -366,12 +366,73 @@ static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data)
  	return error;
  }
@@ -824572,7 +831829,7 @@
  
  static int init_test_thread(int id)
 diff --git a/kernel/sched.c b/kernel/sched.c
-index e76b11c..ba4c880 100644
+index e76b11c..9474b23 100644
 --- a/kernel/sched.c
 +++ b/kernel/sched.c
 @@ -22,6 +22,8 @@
@@ -825220,19 +832477,49 @@
 -	update_load_sub(&rq->load, p->se.load.weight);
 -}
 -
- static void inc_nr_running(struct task_struct *p, struct rq *rq)
+-static void inc_nr_running(struct task_struct *p, struct rq *rq)
++static void inc_nr_running(struct rq *rq)
  {
  	rq->nr_running++;
 -	inc_load(rq, p);
  }
  
- static void dec_nr_running(struct task_struct *p, struct rq *rq)
+-static void dec_nr_running(struct task_struct *p, struct rq *rq)
++static void dec_nr_running(struct rq *rq)
  {
  	rq->nr_running--;
 -	dec_load(rq, p);
  }
  
  static void set_load_weight(struct task_struct *p)
+@@ -1003,11 +1350,11 @@ static int effective_prio(struct task_struct *p)
+  */
+ static void activate_task(struct rq *rq, struct task_struct *p, int wakeup)
+ {
+-	if (p->state == TASK_UNINTERRUPTIBLE)
++	if (task_contributes_to_load(p))
+ 		rq->nr_uninterruptible--;
+ 
+ 	enqueue_task(rq, p, wakeup);
+-	inc_nr_running(p, rq);
++	inc_nr_running(rq);
+ }
+ 
+ /*
+@@ -1015,11 +1362,11 @@ static void activate_task(struct rq *rq, struct task_struct *p, int wakeup)
+  */
+ static void deactivate_task(struct rq *rq, struct task_struct *p, int sleep)
+ {
+-	if (p->state == TASK_UNINTERRUPTIBLE)
++	if (task_contributes_to_load(p))
+ 		rq->nr_uninterruptible++;
+ 
+ 	dequeue_task(rq, p, sleep);
+-	dec_nr_running(p, rq);
++	dec_nr_running(rq);
+ }
+ 
+ /**
 @@ -1039,7 +1386,7 @@ unsigned long weighted_cpuload(const int cpu)
  
  static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu)
@@ -825477,7 +832764,17 @@
  out:
  	task_rq_unlock(rq, &flags);
  
-@@ -1691,7 +1929,7 @@ static void __sched_fork(struct task_struct *p)
+@@ -1657,8 +1895,7 @@ out:
+ 
+ int fastcall wake_up_process(struct task_struct *p)
+ {
+-	return try_to_wake_up(p, TASK_STOPPED | TASK_TRACED |
+-				 TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0);
++	return try_to_wake_up(p, TASK_ALL, 0);
+ }
+ EXPORT_SYMBOL(wake_up_process);
+ 
+@@ -1691,7 +1928,7 @@ static void __sched_fork(struct task_struct *p)
  	p->se.wait_max			= 0;
  #endif
  
@@ -825486,8 +832783,12 @@
  	p->se.on_rq = 0;
  
  #ifdef CONFIG_PREEMPT_NOTIFIERS
-@@ -1771,6 +2009,10 @@ void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
- 		inc_nr_running(p, rq);
+@@ -1768,9 +2005,13 @@ void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
+ 		 * management (if any):
+ 		 */
+ 		p->sched_class->task_new(rq, p);
+-		inc_nr_running(p, rq);
++		inc_nr_running(rq);
  	}
  	check_preempt_curr(rq, p);
 +#ifdef CONFIG_SMP
@@ -825497,7 +832798,7 @@
  	task_rq_unlock(rq, &flags);
  }
  
-@@ -1891,6 +2133,11 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
+@@ -1891,6 +2132,11 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
  	prev_state = prev->state;
  	finish_arch_switch(prev);
  	finish_lock_switch(rq, prev);
@@ -825509,7 +832810,7 @@
  	fire_sched_in_preempt_notifiers(current);
  	if (mm)
  		mmdrop(mm);
-@@ -2124,11 +2371,13 @@ static void double_rq_unlock(struct rq *rq1, struct rq *rq2)
+@@ -2124,11 +2370,13 @@ static void double_rq_unlock(struct rq *rq1, struct rq *rq2)
  /*
   * double_lock_balance - lock the busiest runqueue, this_rq is locked already.
   */
@@ -825524,7 +832825,7 @@
  	if (unlikely(!irqs_disabled())) {
  		/* printk() doesn't work good under rq->lock */
  		spin_unlock(&this_rq->lock);
-@@ -2139,9 +2388,11 @@ static void double_lock_balance(struct rq *this_rq, struct rq *busiest)
+@@ -2139,9 +2387,11 @@ static void double_lock_balance(struct rq *this_rq, struct rq *busiest)
  			spin_unlock(&this_rq->lock);
  			spin_lock(&busiest->lock);
  			spin_lock(&this_rq->lock);
@@ -825536,7 +832837,7 @@
  }
  
  /*
-@@ -3485,12 +3736,14 @@ void scheduler_tick(void)
+@@ -3485,12 +3735,14 @@ void scheduler_tick(void)
  	/*
  	 * Let rq->clock advance by at least TICK_NSEC:
  	 */
@@ -825554,7 +832855,7 @@
  	spin_unlock(&rq->lock);
  
  #ifdef CONFIG_SMP
-@@ -3636,6 +3889,8 @@ need_resched_nonpreemptible:
+@@ -3636,6 +3888,8 @@ need_resched_nonpreemptible:
  
  	schedule_debug(prev);
  
@@ -825563,7 +832864,7 @@
  	/*
  	 * Do the rq-clock update outside the rq lock:
  	 */
-@@ -3654,6 +3909,11 @@ need_resched_nonpreemptible:
+@@ -3654,6 +3908,11 @@ need_resched_nonpreemptible:
  		switch_count = &prev->nvcsw;
  	}
  
@@ -825575,7 +832876,7 @@
  	if (unlikely(!rq->nr_running))
  		idle_balance(cpu, rq);
  
-@@ -3668,14 +3928,20 @@ need_resched_nonpreemptible:
+@@ -3668,14 +3927,20 @@ need_resched_nonpreemptible:
  		++*switch_count;
  
  		context_switch(rq, prev, next); /* unlocks the rq */
@@ -825600,7 +832901,7 @@
  	preempt_enable_no_resched();
  	if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
  		goto need_resched;
-@@ -3691,10 +3957,9 @@ EXPORT_SYMBOL(schedule);
+@@ -3691,10 +3956,9 @@ EXPORT_SYMBOL(schedule);
  asmlinkage void __sched preempt_schedule(void)
  {
  	struct thread_info *ti = current_thread_info();
@@ -825612,7 +832913,7 @@
  	/*
  	 * If there is a non-zero preempt_count or interrupts are disabled,
  	 * we do not want to preempt the current task. Just return..
-@@ -3710,14 +3975,10 @@ asmlinkage void __sched preempt_schedule(void)
+@@ -3710,14 +3974,10 @@ asmlinkage void __sched preempt_schedule(void)
  		 * clear ->lock_depth so that schedule() doesnt
  		 * auto-release the semaphore:
  		 */
@@ -825627,7 +832928,7 @@
  		sub_preempt_count(PREEMPT_ACTIVE);
  
  		/*
-@@ -3738,10 +3999,9 @@ EXPORT_SYMBOL(preempt_schedule);
+@@ -3738,10 +3998,9 @@ EXPORT_SYMBOL(preempt_schedule);
  asmlinkage void __sched preempt_schedule_irq(void)
  {
  	struct thread_info *ti = current_thread_info();
@@ -825639,7 +832940,7 @@
  	/* Catch callers which need to be fixed */
  	BUG_ON(ti->preempt_count || !irqs_disabled());
  
-@@ -3753,16 +4013,12 @@ asmlinkage void __sched preempt_schedule_irq(void)
+@@ -3753,16 +4012,12 @@ asmlinkage void __sched preempt_schedule_irq(void)
  		 * clear ->lock_depth so that schedule() doesnt
  		 * auto-release the semaphore:
  		 */
@@ -825656,7 +832957,56 @@
  		sub_preempt_count(PREEMPT_ACTIVE);
  
  		/*
-@@ -4019,6 +4275,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
+@@ -3868,8 +4123,7 @@ void complete(struct completion *x)
+ 
+ 	spin_lock_irqsave(&x->wait.lock, flags);
+ 	x->done++;
+-	__wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
+-			 1, 0, NULL);
++	__wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
+ 	spin_unlock_irqrestore(&x->wait.lock, flags);
+ }
+ EXPORT_SYMBOL(complete);
+@@ -3880,8 +4134,7 @@ void complete_all(struct completion *x)
+ 
+ 	spin_lock_irqsave(&x->wait.lock, flags);
+ 	x->done += UINT_MAX/2;
+-	__wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
+-			 0, 0, NULL);
++	__wake_up_common(&x->wait, TASK_NORMAL, 0, 0, NULL);
+ 	spin_unlock_irqrestore(&x->wait.lock, flags);
+ }
+ EXPORT_SYMBOL(complete_all);
+@@ -3895,8 +4148,10 @@ do_wait_for_common(struct completion *x, long timeout, int state)
+ 		wait.flags |= WQ_FLAG_EXCLUSIVE;
+ 		__add_wait_queue_tail(&x->wait, &wait);
+ 		do {
+-			if (state == TASK_INTERRUPTIBLE &&
+-			    signal_pending(current)) {
++			if ((state == TASK_INTERRUPTIBLE &&
++			     signal_pending(current)) ||
++			    (state == TASK_KILLABLE &&
++			     fatal_signal_pending(current))) {
+ 				__remove_wait_queue(&x->wait, &wait);
+ 				return -ERESTARTSYS;
+ 			}
+@@ -3956,6 +4211,15 @@ wait_for_completion_interruptible_timeout(struct completion *x,
+ }
+ EXPORT_SYMBOL(wait_for_completion_interruptible_timeout);
+ 
++int __sched wait_for_completion_killable(struct completion *x)
++{
++	long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE);
++	if (t == -ERESTARTSYS)
++		return t;
++	return 0;
++}
++EXPORT_SYMBOL(wait_for_completion_killable);
++
+ static long __sched
+ sleep_on_common(wait_queue_head_t *q, int state, long timeout)
+ {
+@@ -4019,6 +4283,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
  	unsigned long flags;
  	int oldprio, on_rq, running;
  	struct rq *rq;
@@ -825664,7 +833014,7 @@
  
  	BUG_ON(prio < 0 || prio > MAX_PRIO);
  
-@@ -4044,18 +4301,10 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
+@@ -4044,18 +4309,10 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
  	if (on_rq) {
  		if (running)
  			p->sched_class->set_curr_task(rq);
@@ -825686,7 +833036,7 @@
  	}
  	task_rq_unlock(rq, &flags);
  }
-@@ -4087,10 +4336,8 @@ void set_user_nice(struct task_struct *p, long nice)
+@@ -4087,10 +4344,8 @@ void set_user_nice(struct task_struct *p, long nice)
  		goto out_unlock;
  	}
  	on_rq = p->se.on_rq;
@@ -825698,7 +833048,7 @@
  
  	p->static_prio = NICE_TO_PRIO(nice);
  	set_load_weight(p);
-@@ -4100,7 +4347,6 @@ void set_user_nice(struct task_struct *p, long nice)
+@@ -4100,7 +4355,6 @@ void set_user_nice(struct task_struct *p, long nice)
  
  	if (on_rq) {
  		enqueue_task(rq, p, 0);
@@ -825706,7 +833056,7 @@
  		/*
  		 * If the task increased its priority or is running and
  		 * lowered its priority, then reschedule its CPU:
-@@ -4258,6 +4504,7 @@ int sched_setscheduler(struct task_struct *p, int policy,
+@@ -4258,6 +4512,7 @@ int sched_setscheduler(struct task_struct *p, int policy,
  {
  	int retval, oldprio, oldpolicy = -1, on_rq, running;
  	unsigned long flags;
@@ -825714,7 +833064,7 @@
  	struct rq *rq;
  
  	/* may grab non-irq protected spin_locks */
-@@ -4351,18 +4598,10 @@ recheck:
+@@ -4351,18 +4606,10 @@ recheck:
  	if (on_rq) {
  		if (running)
  			p->sched_class->set_curr_task(rq);
@@ -825736,7 +833086,7 @@
  	}
  	__task_rq_unlock(rq);
  	spin_unlock_irqrestore(&p->pi_lock, flags);
-@@ -4490,13 +4729,13 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask)
+@@ -4490,13 +4737,13 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask)
  	struct task_struct *p;
  	int retval;
  
@@ -825752,7 +833102,7 @@
  		return -ESRCH;
  	}
  
-@@ -4536,7 +4775,7 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask)
+@@ -4536,7 +4783,7 @@ long sched_setaffinity(pid_t pid, cpumask_t new_mask)
  	}
  out_unlock:
  	put_task_struct(p);
@@ -825761,7 +833111,7 @@
  	return retval;
  }
  
-@@ -4593,7 +4832,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask)
+@@ -4593,7 +4840,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask)
  	struct task_struct *p;
  	int retval;
  
@@ -825770,7 +833120,7 @@
  	read_lock(&tasklist_lock);
  
  	retval = -ESRCH;
-@@ -4609,7 +4848,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask)
+@@ -4609,7 +4856,7 @@ long sched_getaffinity(pid_t pid, cpumask_t *mask)
  
  out_unlock:
  	read_unlock(&tasklist_lock);
@@ -825779,7 +833129,7 @@
  
  	return retval;
  }
-@@ -4683,7 +4922,8 @@ static void __cond_resched(void)
+@@ -4683,7 +4930,8 @@ static void __cond_resched(void)
  	} while (need_resched());
  }
  
@@ -825789,7 +833139,7 @@
  {
  	if (need_resched() && !(preempt_count() & PREEMPT_ACTIVE) &&
  					system_state == SYSTEM_RUNNING) {
-@@ -4692,7 +4932,8 @@ int __sched cond_resched(void)
+@@ -4692,7 +4940,8 @@ int __sched cond_resched(void)
  	}
  	return 0;
  }
@@ -825799,7 +833149,7 @@
  
  /*
   * cond_resched_lock() - if a reschedule is pending, drop the given lock,
-@@ -4704,19 +4945,15 @@ EXPORT_SYMBOL(cond_resched);
+@@ -4704,19 +4953,15 @@ EXPORT_SYMBOL(cond_resched);
   */
  int cond_resched_lock(spinlock_t *lock)
  {
@@ -825825,7 +833175,7 @@
  		ret = 1;
  		spin_lock(lock);
  	}
-@@ -4890,7 +5127,7 @@ out_unlock:
+@@ -4890,7 +5135,7 @@ out_unlock:
  
  static const char stat_nam[] = "RSDTtZX";
  
@@ -825834,7 +833184,7 @@
  {
  	unsigned long free = 0;
  	unsigned state;
-@@ -4920,8 +5157,7 @@ static void show_task(struct task_struct *p)
+@@ -4920,8 +5165,7 @@ static void show_task(struct task_struct *p)
  	printk(KERN_CONT "%5lu %5d %6d\n", free,
  		task_pid_nr(p), task_pid_nr(p->real_parent));
  
@@ -825844,7 +833194,7 @@
  }
  
  void show_state_filter(unsigned long state_filter)
-@@ -4943,7 +5179,7 @@ void show_state_filter(unsigned long state_filter)
+@@ -4943,7 +5187,7 @@ void show_state_filter(unsigned long state_filter)
  		 */
  		touch_nmi_watchdog();
  		if (!state_filter || (p->state & state_filter))
@@ -825853,7 +833203,7 @@
  	} while_each_thread(g, p);
  
  	touch_all_softlockup_watchdogs();
-@@ -4992,11 +5228,8 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu)
+@@ -4992,11 +5236,8 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu)
  	spin_unlock_irqrestore(&rq->lock, flags);
  
  	/* Set the preempt count _outside_ the spinlocks! */
@@ -825866,7 +833216,7 @@
  	/*
  	 * The idle tasks have their own, simple scheduling class:
  	 */
-@@ -5077,7 +5310,13 @@ int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask)
+@@ -5077,7 +5318,13 @@ int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask)
  		goto out;
  	}
  
@@ -825881,7 +833231,7 @@
  	/* Can the task run on the task's current CPU? If so, we're done */
  	if (cpu_isset(task_cpu(p), new_mask))
  		goto out;
-@@ -5569,9 +5808,6 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
+@@ -5569,9 +5816,6 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
  	struct rq *rq;
  
  	switch (action) {
@@ -825891,7 +833241,7 @@
  
  	case CPU_UP_PREPARE:
  	case CPU_UP_PREPARE_FROZEN:
-@@ -5590,6 +5826,15 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
+@@ -5590,6 +5834,15 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
  	case CPU_ONLINE_FROZEN:
  		/* Strictly unnecessary, as first user will wake it. */
  		wake_up_process(cpu_rq(cpu)->migration_thread);
@@ -825907,7 +833257,7 @@
  		break;
  
  #ifdef CONFIG_HOTPLUG_CPU
-@@ -5640,10 +5885,18 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
+@@ -5640,10 +5893,18 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
  		}
  		spin_unlock_irq(&rq->lock);
  		break;
@@ -825929,7 +833279,7 @@
  	}
  	return NOTIFY_OK;
  }
-@@ -5831,11 +6084,76 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
+@@ -5831,11 +6092,76 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
  	return 1;
  }
  
@@ -826008,7 +833358,7 @@
  {
  	struct rq *rq = cpu_rq(cpu);
  	struct sched_domain *tmp;
-@@ -5860,6 +6178,7 @@ static void cpu_attach_domain(struct sched_domain *sd, int cpu)
+@@ -5860,6 +6186,7 @@ static void cpu_attach_domain(struct sched_domain *sd, int cpu)
  
  	sched_domain_debug(sd, cpu);
  
@@ -826016,7 +833366,7 @@
  	rcu_assign_pointer(rq->sd, sd);
  }
  
-@@ -6228,6 +6547,7 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd)
+@@ -6228,6 +6555,7 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd)
  static int build_sched_domains(const cpumask_t *cpu_map)
  {
  	int i;
@@ -826024,7 +833374,7 @@
  #ifdef CONFIG_NUMA
  	struct sched_group **sched_group_nodes = NULL;
  	int sd_allnodes = 0;
-@@ -6244,6 +6564,12 @@ static int build_sched_domains(const cpumask_t *cpu_map)
+@@ -6244,6 +6572,12 @@ static int build_sched_domains(const cpumask_t *cpu_map)
  	sched_group_nodes_bycpu[first_cpu(*cpu_map)] = sched_group_nodes;
  #endif
  
@@ -826037,7 +833387,7 @@
  	/*
  	 * Set up domains for cpus specified by the cpu_map.
  	 */
-@@ -6460,7 +6786,7 @@ static int build_sched_domains(const cpumask_t *cpu_map)
+@@ -6460,7 +6794,7 @@ static int build_sched_domains(const cpumask_t *cpu_map)
  #else
  		sd = &per_cpu(phys_domains, i);
  #endif
@@ -826046,7 +833396,7 @@
  	}
  
  	return 0;
-@@ -6518,7 +6844,7 @@ static void detach_destroy_domains(const cpumask_t *cpu_map)
+@@ -6518,7 +6852,7 @@ static void detach_destroy_domains(const cpumask_t *cpu_map)
  	unregister_sched_domain_sysctl();
  
  	for_each_cpu_mask(i, *cpu_map)
@@ -826055,7 +833405,7 @@
  	synchronize_sched();
  	arch_destroy_sched_domains(cpu_map);
  }
-@@ -6548,6 +6874,8 @@ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new)
+@@ -6548,6 +6882,8 @@ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new)
  {
  	int i, j;
  
@@ -826064,7 +833414,7 @@
  	/* always unregister in case we don't destroy any domains */
  	unregister_sched_domain_sysctl();
  
-@@ -6588,6 +6916,8 @@ match2:
+@@ -6588,6 +6924,8 @@ match2:
  	ndoms_cur = ndoms_new;
  
  	register_sched_domain_sysctl();
@@ -826073,7 +833423,7 @@
  }
  
  #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
-@@ -6595,10 +6925,10 @@ static int arch_reinit_sched_domains(void)
+@@ -6595,10 +6933,10 @@ static int arch_reinit_sched_domains(void)
  {
  	int err;
  
@@ -826086,7 +833436,7 @@
  
  	return err;
  }
-@@ -6709,12 +7039,12 @@ void __init sched_init_smp(void)
+@@ -6709,12 +7047,12 @@ void __init sched_init_smp(void)
  {
  	cpumask_t non_isolated_cpus;
  
@@ -826101,7 +833451,7 @@
  	/* XXX: Theoretical race here - CPU may be hotplugged now */
  	hotcpu_notifier(update_sched_domains, 0);
  
-@@ -6722,6 +7052,21 @@ void __init sched_init_smp(void)
+@@ -6722,6 +7060,21 @@ void __init sched_init_smp(void)
  	if (set_cpus_allowed(current, non_isolated_cpus) < 0)
  		BUG();
  	sched_init_granularity();
@@ -826123,7 +833473,7 @@
  }
  #else
  void __init sched_init_smp(void)
-@@ -6746,13 +7091,87 @@ static void init_cfs_rq(struct cfs_rq *cfs_rq, struct rq *rq)
+@@ -6746,13 +7099,87 @@ static void init_cfs_rq(struct cfs_rq *cfs_rq, struct rq *rq)
  	cfs_rq->min_vruntime = (u64)(-(1LL << 20));
  }
  
@@ -826212,7 +833562,7 @@
  		struct rq *rq;
  
  		rq = cpu_rq(i);
-@@ -6761,52 +7180,39 @@ void __init sched_init(void)
+@@ -6761,52 +7188,39 @@ void __init sched_init(void)
  		rq->nr_running = 0;
  		rq->clock = 1;
  		init_cfs_rq(&rq->cfs, rq);
@@ -826281,7 +833631,7 @@
  	}
  
  	set_load_weight(&init_task);
-@@ -6975,12 +7381,187 @@ void set_curr_task(int cpu, struct task_struct *p)
+@@ -6975,12 +7389,187 @@ void set_curr_task(int cpu, struct task_struct *p)
  
  #ifdef CONFIG_FAIR_GROUP_SCHED
  
@@ -826469,7 +833819,7 @@
  	struct rq *rq;
  	int i;
  
-@@ -6994,97 +7575,89 @@ struct task_group *sched_create_group(void)
+@@ -6994,97 +7583,89 @@ struct task_group *sched_create_group(void)
  	tg->se = kzalloc(sizeof(se) * NR_CPUS, GFP_KERNEL);
  	if (!tg->se)
  		goto err;
@@ -826605,7 +833955,7 @@
  }
  
  /* change task's runqueue when it moves between groups.
-@@ -7100,11 +7673,6 @@ void sched_move_task(struct task_struct *tsk)
+@@ -7100,11 +7681,6 @@ void sched_move_task(struct task_struct *tsk)
  
  	rq = task_rq_lock(tsk, &flags);
  
@@ -826617,7 +833967,7 @@
  	update_rq_clock(rq);
  
  	running = task_current(rq, tsk);
-@@ -7116,7 +7684,7 @@ void sched_move_task(struct task_struct *tsk)
+@@ -7116,7 +7692,7 @@ void sched_move_task(struct task_struct *tsk)
  			tsk->sched_class->put_prev_task(rq, tsk);
  	}
  
@@ -826626,7 +833976,7 @@
  
  	if (on_rq) {
  		if (unlikely(running))
-@@ -7124,53 +7692,82 @@ void sched_move_task(struct task_struct *tsk)
+@@ -7124,53 +7700,82 @@ void sched_move_task(struct task_struct *tsk)
  		enqueue_task(rq, tsk, 0);
  	}
  
@@ -826725,7 +834075,7 @@
  	return 0;
  }
  
-@@ -7179,6 +7776,31 @@ unsigned long sched_group_shares(struct task_group *tg)
+@@ -7179,6 +7784,31 @@ unsigned long sched_group_shares(struct task_group *tg)
  	return tg->shares;
  }
  
@@ -826757,7 +834107,7 @@
  #endif	/* CONFIG_FAIR_GROUP_SCHED */
  
  #ifdef CONFIG_FAIR_CGROUP_SCHED
-@@ -7254,12 +7876,30 @@ static u64 cpu_shares_read_uint(struct cgroup *cgrp, struct cftype *cft)
+@@ -7254,12 +7884,30 @@ static u64 cpu_shares_read_uint(struct cgroup *cgrp, struct cftype *cft)
  	return (u64) tg->shares;
  }
  
@@ -826819,7 +834169,7 @@
  	p->se.sum_sleep_runtime			= 0;
  	p->se.block_max				= 0;
 diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
-index da7c061..72e25c7 100644
+index da7c061..6c091d6 100644
 --- a/kernel/sched_fair.c
 +++ b/kernel/sched_fair.c
 @@ -20,6 +20,8 @@
@@ -826885,6 +834235,15 @@
  	}
  #endif
  }
+@@ -511,7 +520,7 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
+ 
+ 	if (!initial) {
+ 		/* sleeps upto a single latency don't count. */
+-		if (sched_feat(NEW_FAIR_SLEEPERS) && entity_is_task(se))
++		if (sched_feat(NEW_FAIR_SLEEPERS))
+ 			vruntime -= sysctl_sched_latency;
+ 
+ 		/* ensure we never gain time by being placed backwards. */
 @@ -642,13 +651,29 @@ static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)
  	cfs_rq->curr = NULL;
  }
@@ -827197,7 +834556,20 @@
   * Preempt the current task with a newly woken task if needed:
   */
  static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
-@@ -876,6 +1115,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
+@@ -867,7 +1106,11 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
+ 	}
+ 
+ 	gran = sysctl_sched_wakeup_granularity;
+-	if (unlikely(se->load.weight != NICE_0_LOAD))
++	/*
++	 * More easily preempt - nice tasks, while not making
++	 * it harder for + nice tasks.
++	 */
++	if (unlikely(se->load.weight > NICE_0_LOAD))
+ 		gran = calc_delta_fair(gran, &se->load);
+ 
+ 	if (pse->vruntime + gran < se->vruntime)
+@@ -876,6 +1119,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p)
  
  static struct task_struct *pick_next_task_fair(struct rq *rq)
  {
@@ -827205,7 +834577,7 @@
  	struct cfs_rq *cfs_rq = &rq->cfs;
  	struct sched_entity *se;
  
-@@ -887,7 +1127,10 @@ static struct task_struct *pick_next_task_fair(struct rq *rq)
+@@ -887,7 +1131,10 @@ static struct task_struct *pick_next_task_fair(struct rq *rq)
  		cfs_rq = group_cfs_rq(se);
  	} while (cfs_rq);
  
@@ -827217,7 +834589,7 @@
  }
  
  /*
-@@ -944,25 +1187,6 @@ static struct task_struct *load_balance_next_fair(void *arg)
+@@ -944,25 +1191,6 @@ static struct task_struct *load_balance_next_fair(void *arg)
  	return __load_balance_iterator(cfs_rq, cfs_rq->rb_load_balance_curr);
  }
  
@@ -827243,7 +834615,7 @@
  static unsigned long
  load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
  		  unsigned long max_load_move,
-@@ -972,28 +1196,45 @@ load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
+@@ -972,28 +1200,45 @@ load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
  	struct cfs_rq *busy_cfs_rq;
  	long rem_load_move = max_load_move;
  	struct rq_iterator cfs_rq_iterator;
@@ -827300,7 +834672,7 @@
  #else
  # define maxload rem_load_move
  #endif
-@@ -1002,11 +1243,33 @@ load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
+@@ -1002,11 +1247,33 @@ load_balance_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
  		 * load_balance_[start|next]_fair iterators
  		 */
  		cfs_rq_iterator.arg = busy_cfs_rq;
@@ -827335,7 +834707,7 @@
  		if (rem_load_move <= 0)
  			break;
  	}
-@@ -1042,14 +1305,14 @@ move_one_task_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
+@@ -1042,14 +1309,14 @@ move_one_task_fair(struct rq *this_rq, int this_cpu, struct rq *busiest,
  /*
   * scheduler tick hitting a task of our scheduling class:
   */
@@ -827352,7 +834724,7 @@
  	}
  }
  
-@@ -1087,6 +1350,42 @@ static void task_new_fair(struct rq *rq, struct task_struct *p)
+@@ -1087,6 +1354,42 @@ static void task_new_fair(struct rq *rq, struct task_struct *p)
  	resched_task(rq->curr);
  }
  
@@ -827395,7 +834767,7 @@
  /* Account for a task changing its policy or group.
   *
   * This routine is mostly called to set cfs_rq->curr field when a task
-@@ -1108,6 +1407,9 @@ static const struct sched_class fair_sched_class = {
+@@ -1108,6 +1411,9 @@ static const struct sched_class fair_sched_class = {
  	.enqueue_task		= enqueue_task_fair,
  	.dequeue_task		= dequeue_task_fair,
  	.yield_task		= yield_task_fair,
@@ -827405,7 +834777,7 @@
  
  	.check_preempt_curr	= check_preempt_wakeup,
  
-@@ -1122,6 +1424,9 @@ static const struct sched_class fair_sched_class = {
+@@ -1122,6 +1428,9 @@ static const struct sched_class fair_sched_class = {
  	.set_curr_task          = set_curr_task_fair,
  	.task_tick		= task_tick_fair,
  	.task_new		= task_new_fair,
@@ -827415,7 +834787,7 @@
  };
  
  #ifdef CONFIG_SCHED_DEBUG
-@@ -1132,7 +1437,9 @@ static void print_cfs_stats(struct seq_file *m, int cpu)
+@@ -1132,7 +1441,9 @@ static void print_cfs_stats(struct seq_file *m, int cpu)
  #ifdef CONFIG_FAIR_GROUP_SCHED
  	print_cfs_rq(m, cpu, &cpu_rq(cpu)->cfs);
  #endif
@@ -828770,9 +836142,37 @@
 +	.switched_to		= switched_to_rt,
  };
 diff --git a/kernel/signal.c b/kernel/signal.c
-index afa4f78..bf49ce6 100644
+index afa4f78..4333b6d 100644
 --- a/kernel/signal.c
 +++ b/kernel/signal.c
+@@ -456,15 +456,15 @@ void signal_wake_up(struct task_struct *t, int resume)
+ 	set_tsk_thread_flag(t, TIF_SIGPENDING);
+ 
+ 	/*
+-	 * For SIGKILL, we want to wake it up in the stopped/traced case.
+-	 * We don't check t->state here because there is a race with it
++	 * For SIGKILL, we want to wake it up in the stopped/traced/killable
++	 * case. We don't check t->state here because there is a race with it
+ 	 * executing another processor and just now entering stopped state.
+ 	 * By using wake_up_state, we ensure the process will wake up and
+ 	 * handle its death signal.
+ 	 */
+ 	mask = TASK_INTERRUPTIBLE;
+ 	if (resume)
+-		mask |= TASK_STOPPED | TASK_TRACED;
++		mask |= TASK_WAKEKILL;
+ 	if (!wake_up_state(t, mask))
+ 		kick_process(t);
+ }
+@@ -620,7 +620,7 @@ static void handle_stop_signal(int sig, struct task_struct *p)
+ 			 * Wake up the stopped thread _after_ setting
+ 			 * TIF_SIGPENDING
+ 			 */
+-			state = TASK_STOPPED;
++			state = __TASK_STOPPED;
+ 			if (sig_user_defined(t, SIGCONT) && !sigismember(&t->blocked, SIGCONT)) {
+ 				set_tsk_thread_flag(t, TIF_SIGPENDING);
+ 				state |= TASK_INTERRUPTIBLE;
 @@ -733,13 +733,13 @@ static void print_fatal_signal(struct pt_regs *regs, int signr)
  		current->comm, task_pid_nr(current), signr);
  
@@ -828789,6 +836189,46 @@
  			printk("%02x ", insn);
  		}
  	}
+@@ -838,7 +838,7 @@ static inline int wants_signal(int sig, struct task_struct *p)
+ 		return 0;
+ 	if (sig == SIGKILL)
+ 		return 1;
+-	if (p->state & (TASK_STOPPED | TASK_TRACED))
++	if (task_is_stopped_or_traced(p))
+ 		return 0;
+ 	return task_curr(p) || !signal_pending(p);
+ }
+@@ -994,6 +994,12 @@ void zap_other_threads(struct task_struct *p)
+ 	}
+ }
+ 
++int fastcall __fatal_signal_pending(struct task_struct *tsk)
++{
++	return sigismember(&tsk->pending.signal, SIGKILL);
++}
++EXPORT_SYMBOL(__fatal_signal_pending);
++
+ /*
+  * Must be called under rcu_read_lock() or with tasklist_lock read-held.
+  */
+@@ -1441,7 +1447,7 @@ void do_notify_parent(struct task_struct *tsk, int sig)
+ 	BUG_ON(sig == -1);
+ 
+  	/* do_notify_parent_cldstop should have been called instead.  */
+- 	BUG_ON(tsk->state & (TASK_STOPPED|TASK_TRACED));
++ 	BUG_ON(task_is_stopped_or_traced(tsk));
+ 
+ 	BUG_ON(!tsk->ptrace &&
+ 	       (tsk->group_leader != tsk || !thread_group_empty(tsk)));
+@@ -1729,7 +1735,7 @@ static int do_signal_stop(int signr)
+ 			 * so this check has no races.
+ 			 */
+ 			if (!t->exit_state &&
+-			    !(t->state & (TASK_STOPPED|TASK_TRACED))) {
++			    !task_is_stopped_or_traced(t)) {
+ 				stop_count++;
+ 				signal_wake_up(t, 0);
+ 			}
 diff --git a/kernel/softirq.c b/kernel/softirq.c
 index bd89bc4..d7837d4 100644
 --- a/kernel/softirq.c
@@ -830211,7 +837651,7 @@
   * Display the information collected so far:
   * # cat /proc/timer_stats
 diff --git a/kernel/timer.c b/kernel/timer.c
-index 2a00c22..23f7ead 100644
+index 2a00c22..9fbb472 100644
 --- a/kernel/timer.c
 +++ b/kernel/timer.c
 @@ -58,59 +58,57 @@ EXPORT_SYMBOL(jiffies_64);
@@ -830446,7 +837886,21 @@
  	raise_softirq(TIMER_SOFTIRQ);
  	softlockup_tick();
  }
-@@ -1222,7 +1221,7 @@ static struct lock_class_key base_lock_keys[NR_CPUS];
+@@ -1100,6 +1099,13 @@ signed long __sched schedule_timeout_interruptible(signed long timeout)
+ }
+ EXPORT_SYMBOL(schedule_timeout_interruptible);
+ 
++signed long __sched schedule_timeout_killable(signed long timeout)
++{
++	__set_current_state(TASK_KILLABLE);
++	return schedule_timeout(timeout);
++}
++EXPORT_SYMBOL(schedule_timeout_killable);
++
+ signed long __sched schedule_timeout_uninterruptible(signed long timeout)
+ {
+ 	__set_current_state(TASK_UNINTERRUPTIBLE);
+@@ -1222,7 +1228,7 @@ static struct lock_class_key base_lock_keys[NR_CPUS];
  static int __cpuinit init_timers_cpu(int cpu)
  {
  	int j;
@@ -830455,7 +837909,7 @@
  	static char __cpuinitdata tvec_base_done[NR_CPUS];
  
  	if (!tvec_base_done[cpu]) {
-@@ -1277,7 +1276,7 @@ static int __cpuinit init_timers_cpu(int cpu)
+@@ -1277,7 +1283,7 @@ static int __cpuinit init_timers_cpu(int cpu)
  }
  
  #ifdef CONFIG_HOTPLUG_CPU
@@ -830464,7 +837918,7 @@
  {
  	struct timer_list *timer;
  
-@@ -1291,8 +1290,8 @@ static void migrate_timer_list(tvec_base_t *new_base, struct list_head *head)
+@@ -1291,8 +1297,8 @@ static void migrate_timer_list(tvec_base_t *new_base, struct list_head *head)
  
  static void __cpuinit migrate_timers(int cpu)
  {
@@ -830731,6 +838185,19 @@
  }
  
  void switch_uid(struct user_struct *new_user)
+diff --git a/kernel/wait.c b/kernel/wait.c
+index 444ddbf..f987688 100644
+--- a/kernel/wait.c
++++ b/kernel/wait.c
+@@ -215,7 +215,7 @@ void fastcall __wake_up_bit(wait_queue_head_t *wq, void *word, int bit)
+ {
+ 	struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit);
+ 	if (waitqueue_active(wq))
+-		__wake_up(wq, TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE, 1, &key);
++		__wake_up(wq, TASK_NORMAL, 1, &key);
+ }
+ EXPORT_SYMBOL(__wake_up_bit);
+ 
 diff --git a/kernel/workqueue.c b/kernel/workqueue.c
 index 8db0b59..52db48e 100644
 --- a/kernel/workqueue.c
@@ -832748,6 +840215,77 @@
  	default "1"
  
  config VIRT_TO_BUS
+diff --git a/mm/filemap.c b/mm/filemap.c
+index f4d0cde..89ce6fe 100644
+--- a/mm/filemap.c
++++ b/mm/filemap.c
+@@ -185,6 +185,12 @@ static int sync_page(void *word)
+ 	return 0;
+ }
+ 
++static int sync_page_killable(void *word)
++{
++	sync_page(word);
++	return fatal_signal_pending(current) ? -EINTR : 0;
++}
++
+ /**
+  * __filemap_fdatawrite_range - start writeback on mapping dirty pages in range
+  * @mapping:	address space structure to write
+@@ -589,6 +595,14 @@ void fastcall __lock_page(struct page *page)
+ }
+ EXPORT_SYMBOL(__lock_page);
+ 
++int fastcall __lock_page_killable(struct page *page)
++{
++	DEFINE_WAIT_BIT(wait, &page->flags, PG_locked);
++
++	return __wait_on_bit_lock(page_waitqueue(page), &wait,
++					sync_page_killable, TASK_KILLABLE);
++}
++
+ /*
+  * Variant of lock_page that does not require the caller to hold a reference
+  * on the page's mapping.
+@@ -980,7 +994,8 @@ page_ok:
+ 
+ page_not_up_to_date:
+ 		/* Get exclusive access to the page ... */
+-		lock_page(page);
++		if (lock_page_killable(page))
++			goto readpage_eio;
+ 
+ 		/* Did it get truncated before we got the lock? */
+ 		if (!page->mapping) {
+@@ -1008,7 +1023,8 @@ readpage:
+ 		}
+ 
+ 		if (!PageUptodate(page)) {
+-			lock_page(page);
++			if (lock_page_killable(page))
++				goto readpage_eio;
+ 			if (!PageUptodate(page)) {
+ 				if (page->mapping == NULL) {
+ 					/*
+@@ -1019,15 +1035,16 @@ readpage:
+ 					goto find_page;
+ 				}
+ 				unlock_page(page);
+-				error = -EIO;
+ 				shrink_readahead_size_eio(filp, ra);
+-				goto readpage_error;
++				goto readpage_eio;
+ 			}
+ 			unlock_page(page);
+ 		}
+ 
+ 		goto page_ok;
+ 
++readpage_eio:
++		error = -EIO;
+ readpage_error:
+ 		/* UHHUH! A synchronous read error occurred. Report it */
+ 		desc->error = error;
 diff --git a/mm/memory.c b/mm/memory.c
 index 4b0144b..d902d0e 100644
 --- a/mm/memory.c
@@ -913873,7 +921411,7 @@
  	return err;
  }
 diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
-index 1ea2755..bcd9abd 100644
+index 1ea2755..eca941c 100644
 --- a/net/sunrpc/auth.c
 +++ b/net/sunrpc/auth.c
 @@ -51,6 +51,7 @@ rpcauth_register(const struct rpc_authops *ops)
@@ -913941,25 +921479,7 @@
  
  struct rpc_cred *
  rpcauth_bindcred(struct rpc_task *task)
-@@ -378,6 +385,7 @@ rpcauth_bindcred(struct rpc_task *task)
- 		.group_info = current->group_info,
- 	};
- 	struct rpc_cred *ret;
-+	sigset_t oldset;
- 	int flags = 0;
- 
- 	dprintk("RPC: %5u looking up %s cred\n",
-@@ -385,7 +393,9 @@ rpcauth_bindcred(struct rpc_task *task)
- 	get_group_info(acred.group_info);
- 	if (task->tk_flags & RPC_TASK_ROOTCREDS)
- 		flags |= RPCAUTH_LOOKUP_ROOTCREDS;
-+	rpc_clnt_sigmask(task->tk_client, &oldset);
- 	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
-+	rpc_clnt_sigunmask(task->tk_client, &oldset);
- 	if (!IS_ERR(ret))
- 		task->tk_msg.rpc_cred = ret;
- 	else
-@@ -435,6 +445,7 @@ need_lock:
+@@ -435,6 +442,7 @@ need_lock:
  out_destroy:
  	cred->cr_ops->crdestroy(cred);
  }
@@ -914015,7 +921535,7 @@
  	struct cache_detail *cd = ((struct handle*)m->private)->cd;
  	read_unlock(&cd->hash_lock);
 diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
-index 76be83e..924916c 100644
+index 76be83e..0998e6d 100644
 --- a/net/sunrpc/clnt.c
 +++ b/net/sunrpc/clnt.c
 @@ -30,6 +30,7 @@
@@ -914161,7 +921681,7 @@
  	/*
  	 * By default, kernel RPC client connects from a reserved port.
  	 * CAP_NET_BIND_SERVICE will not be set for unprivileged requesters,
-@@ -275,8 +308,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
+@@ -275,13 +308,12 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
  	if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
  		xprt->resvport = 0;
  
@@ -914171,7 +921691,22 @@
  	if (IS_ERR(clnt))
  		return clnt;
  
-@@ -322,7 +354,7 @@ rpc_clone_client(struct rpc_clnt *clnt)
+ 	if (!(args->flags & RPC_CLNT_CREATE_NOPING)) {
+-		int err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
++		int err = rpc_ping(clnt, RPC_TASK_SOFT);
+ 		if (err != 0) {
+ 			rpc_shutdown_client(clnt);
+ 			return ERR_PTR(err);
+@@ -292,8 +324,6 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
+ 	if (args->flags & RPC_CLNT_CREATE_HARDRTRY)
+ 		clnt->cl_softrtry = 0;
+ 
+-	if (args->flags & RPC_CLNT_CREATE_INTR)
+-		clnt->cl_intr = 1;
+ 	if (args->flags & RPC_CLNT_CREATE_AUTOBIND)
+ 		clnt->cl_autobind = 1;
+ 	if (args->flags & RPC_CLNT_CREATE_DISCRTRY)
+@@ -322,7 +352,7 @@ rpc_clone_client(struct rpc_clnt *clnt)
  	new->cl_autobind = 0;
  	INIT_LIST_HEAD(&new->cl_tasks);
  	spin_lock_init(&new->cl_lock);
@@ -914180,7 +921715,7 @@
  	new->cl_metrics = rpc_alloc_iostats(clnt);
  	if (new->cl_metrics == NULL)
  		goto out_no_stats;
-@@ -345,6 +377,7 @@ out_no_clnt:
+@@ -345,6 +375,7 @@ out_no_clnt:
  	dprintk("RPC:       %s: returned error %d\n", __FUNCTION__, err);
  	return ERR_PTR(err);
  }
@@ -914188,7 +921723,7 @@
  
  /*
   * Properly shut down an RPC client, terminating all outstanding
-@@ -363,6 +396,7 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
+@@ -363,6 +394,7 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
  
  	rpc_release_client(clnt);
  }
@@ -914196,7 +921731,16 @@
  
  /*
   * Free an RPC client
-@@ -467,6 +501,7 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
+@@ -459,7 +491,7 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
+ 	clnt->cl_prog     = program->number;
+ 	clnt->cl_vers     = version->number;
+ 	clnt->cl_stats    = program->stats;
+-	err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
++	err = rpc_ping(clnt, RPC_TASK_SOFT);
+ 	if (err != 0) {
+ 		rpc_shutdown_client(clnt);
+ 		clnt = ERR_PTR(err);
+@@ -467,6 +499,7 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
  out:
  	return clnt;
  }
@@ -914204,47 +921748,61 @@
  
  /*
   * Default callback for async RPC calls
-@@ -498,12 +533,12 @@ static void rpc_save_sigmask(sigset_t *oldset, int intr)
- 	sigprocmask(SIG_BLOCK, &sigmask, oldset);
- }
+@@ -480,77 +513,34 @@ static const struct rpc_call_ops rpc_default_ops = {
+ 	.rpc_call_done = rpc_default_callback,
+ };
  
+-/*
+- *	Export the signal mask handling for synchronous code that
+- *	sleeps on RPC calls
++/**
++ * rpc_run_task - Allocate a new RPC task, then run rpc_execute against it
++ * @task_setup_data: pointer to task initialisation data
+  */
+-#define RPC_INTR_SIGNALS (sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTERM))
+-
+-static void rpc_save_sigmask(sigset_t *oldset, int intr)
+-{
+-	unsigned long	sigallow = sigmask(SIGKILL);
+-	sigset_t sigmask;
+-
+-	/* Block all signals except those listed in sigallow */
+-	if (intr)
+-		sigallow |= RPC_INTR_SIGNALS;
+-	siginitsetinv(&sigmask, sigallow);
+-	sigprocmask(SIG_BLOCK, &sigmask, oldset);
+-}
+-
 -static inline void rpc_task_sigmask(struct rpc_task *task, sigset_t *oldset)
-+static void rpc_task_sigmask(struct rpc_task *task, sigset_t *oldset)
- {
- 	rpc_save_sigmask(oldset, !RPC_TASK_UNINTERRUPTIBLE(task));
- }
- 
+-{
+-	rpc_save_sigmask(oldset, !RPC_TASK_UNINTERRUPTIBLE(task));
+-}
+-
 -static inline void rpc_restore_sigmask(sigset_t *oldset)
-+static void rpc_restore_sigmask(sigset_t *oldset)
- {
- 	sigprocmask(SIG_SETMASK, oldset, NULL);
- }
-@@ -512,45 +547,49 @@ void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset)
- {
- 	rpc_save_sigmask(oldset, clnt->cl_intr);
- }
-+EXPORT_SYMBOL_GPL(rpc_clnt_sigmask);
- 
- void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
- {
- 	rpc_restore_sigmask(oldset);
- }
-+EXPORT_SYMBOL_GPL(rpc_clnt_sigunmask);
- 
+-{
+-	sigprocmask(SIG_SETMASK, oldset, NULL);
+-}
+-
+-void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset)
+-{
+-	rpc_save_sigmask(oldset, clnt->cl_intr);
+-}
+-
+-void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
+-{
+-	rpc_restore_sigmask(oldset);
+-}
+-
 -static
 -struct rpc_task *rpc_do_run_task(struct rpc_clnt *clnt,
 -		struct rpc_message *msg,
 -		int flags,
 -		const struct rpc_call_ops *ops,
 -		void *data)
-+/**
-+ * rpc_run_task - Allocate a new RPC task, then run rpc_execute against it
-+ * @task_setup_data: pointer to task initialisation data
-+ */
 +struct rpc_task *rpc_run_task(const struct rpc_task_setup *task_setup_data)
  {
  	struct rpc_task *task, *ret;
- 	sigset_t oldset;
+-	sigset_t oldset;
  
 -	task = rpc_new_task(clnt, flags, ops, data);
 +	task = rpc_new_task(task_setup_data);
@@ -914272,14 +921830,7 @@
 +		goto out;
  	}
  	atomic_inc(&task->tk_count);
--	rpc_execute(task);
-+	/* Mask signals on synchronous RPC calls and RPCSEC_GSS upcalls */
-+	if (!RPC_IS_ASYNC(task)) {
-+		rpc_task_sigmask(task, &oldset);
-+		rpc_execute(task);
-+		rpc_restore_sigmask(&oldset);
-+	} else
-+		rpc_execute(task);
+ 	rpc_execute(task);
  	ret = task;
  out:
 -	rpc_restore_sigmask(&oldset);
@@ -914289,7 +921840,7 @@
  
  /**
   * rpc_call_sync - Perform a synchronous RPC call
-@@ -561,17 +600,24 @@ out:
+@@ -561,17 +551,24 @@ out:
  int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
  {
  	struct rpc_task	*task;
@@ -914315,7 +921866,7 @@
  
  /**
   * rpc_call_async - Perform an asynchronous RPC call
-@@ -586,45 +632,28 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
+@@ -586,45 +583,28 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
  	       const struct rpc_call_ops *tk_ops, void *data)
  {
  	struct rpc_task	*task;
@@ -914373,7 +921924,7 @@
  
  /**
   * rpc_peeraddr - extract remote peer address from clnt's xprt
-@@ -653,7 +682,8 @@ EXPORT_SYMBOL_GPL(rpc_peeraddr);
+@@ -653,7 +633,8 @@ EXPORT_SYMBOL_GPL(rpc_peeraddr);
   * @format: address format
   *
   */
@@ -914383,7 +921934,7 @@
  {
  	struct rpc_xprt *xprt = clnt->cl_xprt;
  
-@@ -671,6 +701,7 @@ rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize
+@@ -671,6 +652,7 @@ rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize
  	if (xprt->ops->set_buffer_size)
  		xprt->ops->set_buffer_size(xprt, sndsize, rcvsize);
  }
@@ -914391,7 +921942,7 @@
  
  /*
   * Return size of largest payload RPC client can support, in bytes
-@@ -710,6 +741,7 @@ rpc_restart_call(struct rpc_task *task)
+@@ -710,6 +692,7 @@ rpc_restart_call(struct rpc_task *task)
  
  	task->tk_action = call_start;
  }
@@ -914399,7 +921950,7 @@
  
  /*
   * 0.  Initial state
-@@ -1137,7 +1169,7 @@ call_status(struct rpc_task *task)
+@@ -1137,7 +1120,7 @@ call_status(struct rpc_task *task)
  	case -ETIMEDOUT:
  		task->tk_action = call_timeout;
  		if (task->tk_client->cl_discrtry)
@@ -914408,7 +921959,7 @@
  		break;
  	case -ECONNREFUSED:
  	case -ENOTCONN:
-@@ -1260,7 +1292,7 @@ out_retry:
+@@ -1260,7 +1243,7 @@ out_retry:
  	req->rq_received = req->rq_private_buf.len = 0;
  	task->tk_status = 0;
  	if (task->tk_client->cl_discrtry)
@@ -914417,7 +921968,7 @@
  }
  
  /*
-@@ -1517,9 +1549,15 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int
+@@ -1517,9 +1500,15 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int
  		.rpc_proc = &rpcproc_null,
  		.rpc_cred = cred,
  	};
@@ -914556,7 +922107,7 @@
  /*
   * populate the filesystem
 diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
-index a05493a..fa5b8f2 100644
+index a05493a..3164a08 100644
 --- a/net/sunrpc/rpcb_clnt.c
 +++ b/net/sunrpc/rpcb_clnt.c
 @@ -55,45 +55,6 @@ enum {
@@ -914646,7 +922197,7 @@
  	.rpc_call_done		= rpcb_getport_done,
  	.rpc_release		= rpcb_map_release,
  };
-@@ -162,12 +109,13 @@ static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status)
+@@ -162,18 +109,18 @@ static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status)
  }
  
  static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
@@ -914662,7 +922213,14 @@
  		.servername	= hostname,
  		.program	= &rpcb_program,
  		.version	= version,
-@@ -230,7 +178,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+ 		.authflavor	= RPC_AUTH_UNIX,
+-		.flags		= (RPC_CLNT_CREATE_NOPING |
+-				   RPC_CLNT_CREATE_INTR),
++		.flags		= RPC_CLNT_CREATE_NOPING,
+ 	};
+ 
+ 	switch (srvaddr->sa_family) {
+@@ -230,7 +177,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
  			prog, vers, prot, port);
  
  	rpcb_clnt = rpcb_create("localhost", (struct sockaddr *) &sin,
@@ -914671,7 +922229,7 @@
  	if (IS_ERR(rpcb_clnt))
  		return PTR_ERR(rpcb_clnt);
  
-@@ -252,13 +200,15 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+@@ -252,13 +199,15 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
   * @vers: RPC version number to bind
   * @prot: transport protocol to use to make this request
   *
@@ -914690,7 +922248,7 @@
  {
  	struct rpcbind_args map = {
  		.r_prog		= prog,
-@@ -272,14 +222,13 @@ int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog,
+@@ -272,14 +221,13 @@ int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog,
  		.rpc_resp	= &map.r_port,
  	};
  	struct rpc_clnt	*rpcb_clnt;
@@ -914707,7 +922265,7 @@
  	if (IS_ERR(rpcb_clnt))
  		return PTR_ERR(rpcb_clnt);
  
-@@ -295,6 +244,24 @@ int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog,
+@@ -295,6 +243,24 @@ int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog,
  }
  EXPORT_SYMBOL_GPL(rpcb_getport_sync);
  
@@ -914732,7 +922290,7 @@
  /**
   * rpcb_getport_async - obtain the port for a given RPC service on a given host
   * @task: task that is waiting for portmapper request
-@@ -305,12 +272,14 @@ EXPORT_SYMBOL_GPL(rpcb_getport_sync);
+@@ -305,12 +271,14 @@ EXPORT_SYMBOL_GPL(rpcb_getport_sync);
  void rpcb_getport_async(struct rpc_task *task)
  {
  	struct rpc_clnt *clnt = task->tk_client;
@@ -914749,7 +922307,7 @@
  	int status;
  	struct rpcb_info *info;
  
-@@ -340,10 +309,10 @@ void rpcb_getport_async(struct rpc_task *task)
+@@ -340,10 +308,10 @@ void rpcb_getport_async(struct rpc_task *task)
  		goto bailout_nofree;
  	}
  
@@ -914762,7 +922320,7 @@
  	case AF_INET:
  		info = rpcb_next_version;
  		break;
-@@ -368,7 +337,7 @@ void rpcb_getport_async(struct rpc_task *task)
+@@ -368,7 +336,7 @@ void rpcb_getport_async(struct rpc_task *task)
  	dprintk("RPC: %5u %s: trying rpcbind version %u\n",
  		task->tk_pid, __FUNCTION__, bind_version);
  
@@ -914771,7 +922329,7 @@
  				bind_version, 0);
  	if (IS_ERR(rpcb_clnt)) {
  		status = PTR_ERR(rpcb_clnt);
-@@ -390,12 +359,10 @@ void rpcb_getport_async(struct rpc_task *task)
+@@ -390,12 +358,10 @@ void rpcb_getport_async(struct rpc_task *task)
  	map->r_port = 0;
  	map->r_xprt = xprt_get(xprt);
  	map->r_netid = rpc_peeraddr2str(clnt, RPC_DISPLAY_NETID);
@@ -914786,7 +922344,7 @@
  	rpc_release_client(rpcb_clnt);
  	if (IS_ERR(child)) {
  		status = -EIO;
-@@ -518,7 +485,7 @@ static int rpcb_decode_getaddr(struct rpc_rqst *req, __be32 *p,
+@@ -518,7 +484,7 @@ static int rpcb_decode_getaddr(struct rpc_rqst *req, __be32 *p,
  	 * Simple sanity check.  The smallest possible universal
  	 * address is an IPv4 address string containing 11 bytes.
  	 */
@@ -914795,7 +922353,7 @@
  		goto out_err;
  
  	/*
-@@ -569,7 +536,7 @@ out_err:
+@@ -569,7 +535,7 @@ out_err:
  #define RPCB_boolean_sz		(1u)
  
  #define RPCB_netid_sz		(1+XDR_QUADLEN(RPCBIND_MAXNETIDLEN))
@@ -914805,7 +922363,7 @@
  
  #define RPCB_mappingargs_sz	RPCB_program_sz+RPCB_version_sz+	\
 diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
-index c98873f..40ce6f6 100644
+index c98873f..4c66912 100644
 --- a/net/sunrpc/sched.c
 +++ b/net/sunrpc/sched.c
 @@ -45,7 +45,7 @@ static void			 rpc_release_task(struct rpc_task *task);
@@ -914858,7 +922416,7 @@
  	rpc_reset_waitqueue_priority(queue);
  #ifdef RPC_DEBUG
  	queue->name = qname;
-@@ -236,14 +236,14 @@ static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const c
+@@ -236,18 +236,18 @@ static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const c
  
  void rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname)
  {
@@ -914874,11 +922432,23 @@
 -EXPORT_SYMBOL(rpc_init_wait_queue);
 +EXPORT_SYMBOL_GPL(rpc_init_wait_queue);
  
- static int rpc_wait_bit_interruptible(void *word)
+-static int rpc_wait_bit_interruptible(void *word)
++static int rpc_wait_bit_killable(void *word)
  {
-@@ -303,7 +303,7 @@ int __rpc_wait_for_completion_task(struct rpc_task *task, int (*action)(void *))
+-	if (signal_pending(current))
++	if (fatal_signal_pending(current))
+ 		return -ERESTARTSYS;
+ 	schedule();
+ 	return 0;
+@@ -299,11 +299,11 @@ static void rpc_mark_complete_task(struct rpc_task *task)
+ int __rpc_wait_for_completion_task(struct rpc_task *task, int (*action)(void *))
+ {
+ 	if (action == NULL)
+-		action = rpc_wait_bit_interruptible;
++		action = rpc_wait_bit_killable;
  	return wait_on_bit(&task->tk_runstate, RPC_TASK_ACTIVE,
- 			action, TASK_INTERRUPTIBLE);
+-			action, TASK_INTERRUPTIBLE);
++			action, TASK_KILLABLE);
  }
 -EXPORT_SYMBOL(__rpc_wait_for_completion_task);
 +EXPORT_SYMBOL_GPL(__rpc_wait_for_completion_task);
@@ -914977,7 +922547,20 @@
  
  void rpc_release_calldata(const struct rpc_call_ops *ops, void *calldata)
  {
-@@ -808,40 +814,49 @@ EXPORT_SYMBOL_GPL(rpc_free);
+@@ -690,10 +696,9 @@ static void __rpc_execute(struct rpc_task *task)
+ 
+ 		/* sync task: sleep here */
+ 		dprintk("RPC: %5u sync task going to sleep\n", task->tk_pid);
+-		/* Note: Caller should be using rpc_clnt_sigmask() */
+ 		status = out_of_line_wait_on_bit(&task->tk_runstate,
+-				RPC_TASK_QUEUED, rpc_wait_bit_interruptible,
+-				TASK_INTERRUPTIBLE);
++				RPC_TASK_QUEUED, rpc_wait_bit_killable,
++				TASK_KILLABLE);
+ 		if (status == -ERESTARTSYS) {
+ 			/*
+ 			 * When a sync task receives a signal, it exits with
+@@ -808,40 +813,47 @@ EXPORT_SYMBOL_GPL(rpc_free);
  /*
   * Creation and deletion of RPC task structures
   */
@@ -915023,8 +922606,7 @@
 +		if (task->tk_client->cl_softrtry)
  			task->tk_flags |= RPC_TASK_SOFT;
 -		if (!clnt->cl_intr)
-+		if (!task->tk_client->cl_intr)
- 			task->tk_flags |= RPC_TASK_NOINTR;
+-			task->tk_flags |= RPC_TASK_NOINTR;
  	}
  
 -	BUG_ON(task->tk_ops == NULL);
@@ -915044,7 +922626,7 @@
  
  	/* starting timestamp */
  	task->tk_start = jiffies;
-@@ -866,18 +881,22 @@ static void rpc_free_task(struct rcu_head *rcu)
+@@ -866,18 +878,22 @@ static void rpc_free_task(struct rcu_head *rcu)
  /*
   * Create a new task for the specified client.
   */
@@ -915075,7 +922657,7 @@
  out:
  	return task;
  }
-@@ -903,7 +922,7 @@ void rpc_put_task(struct rpc_task *task)
+@@ -903,7 +919,7 @@ void rpc_put_task(struct rpc_task *task)
  		call_rcu_bh(&task->u.tk_rcu, rpc_free_task);
  	rpc_release_calldata(tk_ops, calldata);
  }
@@ -915084,7 +922666,7 @@
  
  static void rpc_release_task(struct rpc_task *task)
  {
-@@ -960,6 +979,7 @@ void rpc_killall_tasks(struct rpc_clnt *clnt)
+@@ -960,6 +976,7 @@ void rpc_killall_tasks(struct rpc_clnt *clnt)
  	}
  	spin_unlock(&clnt->cl_lock);
  }
@@ -915092,7 +922674,7 @@
  
  int rpciod_up(void)
  {
-@@ -1039,6 +1059,11 @@ rpc_init_mempool(void)
+@@ -1039,6 +1056,11 @@ rpc_init_mempool(void)
  		goto err_nomem;
  	if (!rpciod_start())
  		goto err_nomem;
@@ -928676,87 +936258,43569 @@
  
  	return rc;
  }
-diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
-index b48c729..8849041 100644
---- a/sound/oss/waveartist.c
-+++ b/sound/oss/waveartist.c
-@@ -835,7 +835,7 @@ static struct audio_driver waveartist_audio_driver = {
- static irqreturn_t
- waveartist_intr(int irq, void *dev_id)
+diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h
+index 541b908..e087894 100644
+--- a/sound/aoa/aoa.h
++++ b/sound/aoa/aoa.h
+@@ -10,8 +10,6 @@
+ #define __AOA_H
+ #include <asm/prom.h>
+ #include <linux/module.h>
+-/* So apparently there's a reason for requiring driver.h to be included first! */
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/asound.h>
+ #include <sound/control.h>
+diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c
+index 71e3f93..6a3837d 100644
+--- a/sound/aoa/codecs/snd-aoa-codec-onyx.c
++++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c
+@@ -138,6 +138,13 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
+ 	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ 	s8 l, r;
+ 
++	if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT ||
++	    ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT)
++		return -EINVAL;
++	if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT ||
++	    ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
++		return -EINVAL;
++
+ 	mutex_lock(&onyx->mutex);
+ 	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
+ 	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
+@@ -206,6 +213,9 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
+ 	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ 	u8 v, n;
+ 
++	if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
++	    ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
++		return -EINVAL;
+ 	mutex_lock(&onyx->mutex);
+ 	onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ 	n = v;
+@@ -272,6 +282,8 @@ static void onyx_set_capture_source(struct onyx *onyx, int mic)
+ static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
+ 	struct snd_ctl_elem_value *ucontrol)
  {
--	wavnc_info *devc = (wavnc_info *)dev_id;
-+	wavnc_info *devc = dev_id;
- 	int	   irqstatus, status;
++	if (ucontrol->value.enumerated.item[0] > 1)
++		return -EINVAL;
+ 	onyx_set_capture_source(snd_kcontrol_chip(kcontrol),
+ 				ucontrol->value.enumerated.item[0]);
+ 	return 1;
+diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
+index 70c3416..7a16a33 100644
+--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
++++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
+@@ -248,6 +248,13 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct tas *tas = snd_kcontrol_chip(kcontrol);
  
- 	spin_lock(&waveartist_lock);
-diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
-index b4a38a3..4bb9764 100644
---- a/sound/pci/intel8x0.c
-+++ b/sound/pci/intel8x0.c
-@@ -711,11 +711,13 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
- static void fill_nocache(void *buf, int size, int nocache)
++	if (ucontrol->value.integer.value[0] < 0 ||
++	    ucontrol->value.integer.value[0] > 177)
++		return -EINVAL;
++	if (ucontrol->value.integer.value[1] < 0 ||
++	    ucontrol->value.integer.value[1] > 177)
++		return -EINVAL;
++
+ 	mutex_lock(&tas->mtx);
+ 	if (tas->cached_volume_l == ucontrol->value.integer.value[0]
+ 	 && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
+@@ -401,6 +408,10 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
  {
- 	size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
--	change_page_attr(virt_to_page(buf), size, nocache ? PAGE_KERNEL_NOCACHE : PAGE_KERNEL);
--	global_flush_tlb();
-+	if (nocache)
-+		set_pages_uc(virt_to_page(buf), size);
-+	else
-+		set_pages_wb(virt_to_page(buf), size);
- }
- #else
--#define fill_nocache(buf,size,nocache)
-+#define fill_nocache(buf, size, nocache) do { ; } while (0)
+ 	struct tas *tas = snd_kcontrol_chip(kcontrol);
+ 
++	if (ucontrol->value.integer.value[0] < 0 ||
++	    ucontrol->value.integer.value[0] > TAS3004_DRC_MAX)
++		return -EINVAL;
++
+ 	mutex_lock(&tas->mtx);
+ 	if (tas->drc_range == ucontrol->value.integer.value[0]) {
+ 		mutex_unlock(&tas->mtx);
+@@ -447,7 +458,7 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
+ 		return 0;
+ 	}
+ 
+-	tas->drc_enabled = ucontrol->value.integer.value[0];
++	tas->drc_enabled = !!ucontrol->value.integer.value[0];
+ 	if (tas->hw_enabled)
+ 		tas3004_set_drc(tas);
+ 	mutex_unlock(&tas->mtx);
+@@ -494,6 +505,8 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
+ 	struct tas *tas = snd_kcontrol_chip(kcontrol);
+ 	int oldacr;
+ 
++	if (ucontrol->value.enumerated.item[0] > 1)
++		return -EINVAL;
+ 	mutex_lock(&tas->mtx);
+ 	oldacr = tas->acr;
+ 
+@@ -562,6 +575,9 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct tas *tas = snd_kcontrol_chip(kcontrol);
+ 
++	if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN ||
++	    ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX)
++		return -EINVAL;
+ 	mutex_lock(&tas->mtx);
+ 	if (tas->treble == ucontrol->value.integer.value[0]) {
+ 		mutex_unlock(&tas->mtx);
+@@ -610,6 +626,9 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct tas *tas = snd_kcontrol_chip(kcontrol);
+ 
++	if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN ||
++	    ucontrol->value.integer.value[0] > TAS3004_BASS_MAX)
++		return -EINVAL;
+ 	mutex_lock(&tas->mtx);
+ 	if (tas->bass == ucontrol->value.integer.value[0]) {
+ 		mutex_unlock(&tas->mtx);
+diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+index 8b2ba99..dea7abb 100644
+--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
++++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+@@ -600,7 +600,7 @@ static int n##_control_put(struct snd_kcontrol *kcontrol,		\
+ 	struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);	\
+ 	if (gpio->methods && gpio->methods->get_##n)			\
+ 		gpio->methods->set_##n(gpio,				\
+-			ucontrol->value.integer.value[0]);		\
++			!!ucontrol->value.integer.value[0]);		\
+ 	return 1;							\
+ }									\
+ static struct snd_kcontrol_new n##_ctl = {				\
+diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+index efb9441..e6beb92 100644
+--- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c
++++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+@@ -11,7 +11,6 @@
+ #include <linux/interrupt.h>
+ #include <linux/dma-mapping.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ 
+ #include <asm/macio.h>
+diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
+index c6b42f9..59bacd3 100644
+--- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
++++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
+@@ -8,9 +8,6 @@
+ 
+ #include <asm/io.h>
+ #include <linux/delay.h>
+-/* So apparently there's a reason for requiring driver.h
+- * to be included first, even if I don't know it... */
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <asm/macio.h>
+ #include <linux/pci.h>
+@@ -194,6 +191,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
+ 	hw->period_bytes_max = 16384;
+ 	hw->periods_min = 3;
+ 	hw->periods_max = MAX_DBDMA_COMMANDS;
++	err = snd_pcm_hw_constraint_integer(pi->substream->runtime,
++					    SNDRV_PCM_HW_PARAM_PERIODS);
++	if (err < 0) {
++		result = err;
++		goto out_unlock;
++	}
+ 	list_for_each_entry(cii, &sdev->codec_list, list) {
+ 		if (cii->codec->open) {
+ 			err = cii->codec->open(cii, pi->substream);
+@@ -990,6 +993,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
+ 		if (dev->pcm->card != card) {
+ 			printk(KERN_ERR
+ 			       "Can't attach same bus to different cards!\n");
++			err = -EINVAL;
+ 			goto out_put_ci_module;
+ 		}
+ 		err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
+index 3b73ba7..b0a4744 100644
+--- a/sound/arm/aaci.c
++++ b/sound/arm/aaci.c
+@@ -23,7 +23,6 @@
+ #include <asm/irq.h>
+ #include <asm/sizes.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/initval.h>
+ #include <sound/ac97_codec.h>
+diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c
+index ca3bf4e..9d1e666 100644
+--- a/sound/arm/devdma.c
++++ b/sound/arm/devdma.c
+@@ -12,7 +12,6 @@
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ 
+diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
+index 55c6c82..5d86e68 100644
+--- a/sound/arm/pxa2xx-ac97.c
++++ b/sound/arm/pxa2xx-ac97.c
+@@ -18,7 +18,6 @@
+ #include <linux/wait.h>
+ #include <linux/delay.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/ac97_codec.h>
+@@ -352,6 +351,7 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
+ 	snprintf(card->longname, sizeof(card->longname),
+ 		 "%s (%s)", dev->dev.driver->name, card->mixername);
+ 
++	snd_card_set_dev(card, &dev->dev);
+ 	ret = snd_card_register(card);
+ 	if (ret == 0) {
+ 		platform_set_drvdata(dev, card);
+diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
+index e8cf904..0ede9e4 100644
+--- a/sound/arm/pxa2xx-pcm.c
++++ b/sound/arm/pxa2xx-pcm.c
+@@ -16,7 +16,6 @@
+ #include <linux/slab.h>
+ #include <linux/dma-mapping.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
+index 81c64b0..0eff33c 100644
+--- a/sound/arm/sa11xx-uda1341.c
++++ b/sound/arm/sa11xx-uda1341.c
+@@ -59,7 +59,6 @@
+ * 
+ ***************************************************************************************************/
+ 
+-#include <sound/driver.h>
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+ #include <linux/init.h>
+diff --git a/sound/core/control.c b/sound/core/control.c
+index df0774c..01a1a5a 100644
+--- a/sound/core/control.c
++++ b/sound/core/control.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/threads.h>
+ #include <linux/interrupt.h>
+ #include <linux/slab.h>
+@@ -232,8 +231,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
+ 	access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
+ 		 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
+ 				      SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+-		 		      SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
+-		 		      SNDRV_CTL_ELEM_ACCESS_INDIRECT|
+ 		 		      SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
+ 		 		      SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
+ 	kctl.info = ncontrol->info;
+@@ -692,7 +689,7 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+ 	struct snd_kcontrol *kctl;
+ 	struct snd_kcontrol_volatile *vd;
+ 	unsigned int index_offset;
+-	int result, indirect;
++	int result;
+ 
+ 	down_read(&card->controls_rwsem);
+ 	kctl = snd_ctl_find_id(card, &control->id);
+@@ -701,17 +698,12 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+ 	} else {
+ 		index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ 		vd = &kctl->vd[index_offset];
+-		indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+-		if (control->indirect != indirect) {
+-			result = -EACCES;
+-		} else {
+-			if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) {
+-				snd_ctl_build_ioff(&control->id, kctl, index_offset);
+-				result = kctl->get(kctl, control);
+-			} else {
+-				result = -EPERM;
+-			}
+-		}
++		if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
++		    kctl->get != NULL) {
++			snd_ctl_build_ioff(&control->id, kctl, index_offset);
++			result = kctl->get(kctl, control);
++		} else
++			result = -EPERM;
+ 	}
+ 	up_read(&card->controls_rwsem);
+ 	return result;
+@@ -748,7 +740,7 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
+ 	struct snd_kcontrol *kctl;
+ 	struct snd_kcontrol_volatile *vd;
+ 	unsigned int index_offset;
+-	int result, indirect;
++	int result;
+ 
+ 	down_read(&card->controls_rwsem);
+ 	kctl = snd_ctl_find_id(card, &control->id);
+@@ -757,23 +749,19 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
+ 	} else {
+ 		index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ 		vd = &kctl->vd[index_offset];
+-		indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0;
+-		if (control->indirect != indirect) {
+-			result = -EACCES;
++		if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
++		    kctl->put == NULL ||
++		    (file && vd->owner && vd->owner != file)) {
++			result = -EPERM;
+ 		} else {
+-			if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
+-			    kctl->put == NULL ||
+-			    (file && vd->owner != NULL && vd->owner != file)) {
+-				result = -EPERM;
+-			} else {
+-				snd_ctl_build_ioff(&control->id, kctl, index_offset);
+-				result = kctl->put(kctl, control);
+-			}
+-			if (result > 0) {
+-				up_read(&card->controls_rwsem);
+-				snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id);
+-				return 0;
+-			}
++			snd_ctl_build_ioff(&control->id, kctl, index_offset);
++			result = kctl->put(kctl, control);
++		}
++		if (result > 0) {
++			up_read(&card->controls_rwsem);
++			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
++				       &control->id);
++			return 0;
+ 		}
+ 	}
+ 	up_read(&card->controls_rwsem);
+diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
+index 9311ca3..6101259 100644
+--- a/sound/core/control_compat.c
++++ b/sound/core/control_compat.c
+@@ -219,7 +219,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
+ 				    struct snd_ctl_elem_value32 __user *data32,
+ 				    int *typep, int *countp)
+ {
+-	int i, type, count, size;
++	int i, type, size;
++	int uninitialized_var(count);
+ 	unsigned int indirect;
+ 
+ 	if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
+diff --git a/sound/core/device.c b/sound/core/device.c
+index ea1a062..202dac0 100644
+--- a/sound/core/device.c
++++ b/sound/core/device.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+ #include <linux/errno.h>
+diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
+index bfd9d18..6d6589f 100644
+--- a/sound/core/hwdep.c
++++ b/sound/core/hwdep.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/major.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+diff --git a/sound/core/info.c b/sound/core/info.c
+index 1ffd29b..9977ec2 100644
+--- a/sound/core/info.c
++++ b/sound/core/info.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+ #include <linux/smp_lock.h>
+diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
+index 435c939..e35789a 100644
+--- a/sound/core/info_oss.c
++++ b/sound/core/info_oss.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+ #include <linux/string.h>
+@@ -66,8 +65,6 @@ int snd_oss_info_register(int dev, int num, char *string)
+ 
+ EXPORT_SYMBOL(snd_oss_info_register);
+ 
+-extern void snd_card_info_read_oss(struct snd_info_buffer *buffer);
+-
+ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev)
+ {
+ 	int idx, ok = -1;
+diff --git a/sound/core/init.c b/sound/core/init.c
+index 2cb7099..e3338d6 100644
+--- a/sound/core/init.c
++++ b/sound/core/init.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+ #include <linux/file.h>
+@@ -43,6 +42,40 @@ EXPORT_SYMBOL(snd_cards);
+ 
+ static DEFINE_MUTEX(snd_card_mutex);
+ 
++static char *slots[SNDRV_CARDS];
++module_param_array(slots, charp, NULL, 0444);
++MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
++
++/* return non-zero if the given index is already reserved for another
++ * module via slots option
++ */
++static int module_slot_mismatch(struct module *module, int idx)
++{
++#ifdef MODULE
++	char *s1, *s2;
++	if (!module || !module->name || !slots[idx])
++		return 0;
++	s1 = slots[idx];
++	s2 = module->name;
++	/* compare module name strings
++	 * hyphens are handled as equivalent with underscore
++	 */
++	for (;;) {
++		char c1 = *s1++;
++		char c2 = *s2++;
++		if (c1 == '-')
++			c1 = '_';
++		if (c2 == '-')
++			c2 = '_';
++		if (c1 != c2)
++			return 1;
++		if (!c1)
++			break;
++	}
++#endif
++	return 0;
++}
++
+ #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
+ EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
+@@ -115,6 +148,8 @@ struct snd_card *snd_card_new(int idx, const char *xid,
+ 		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+ 			/* idx == -1 == 0xffff means: take any free slot */
+ 			if (~snd_cards_lock & idx & 1<<idx2) {
++				if (module_slot_mismatch(module, idx2))
++					continue;
+ 				idx = idx2;
+ 				if (idx >= snd_ecards_limit)
+ 					snd_ecards_limit = idx + 1;
+@@ -304,8 +339,8 @@ int snd_card_disconnect(struct snd_card *card)
+ 		list_add(&mfile->shutdown_list, &shutdown_files);
+ 		spin_unlock(&shutdown_lock);
+ 
+-		fops_get(&snd_shutdown_f_ops);
+ 		mfile->file->f_op = &snd_shutdown_f_ops;
++		fops_get(mfile->file->f_op);
+ 		
+ 		mfile = mfile->next;
+ 	}
+diff --git a/sound/core/isadma.c b/sound/core/isadma.c
+index eb173ce..79f0f16 100644
+--- a/sound/core/isadma.c
++++ b/sound/core/isadma.c
+@@ -26,7 +26,6 @@
+ 
+ #undef HAVE_REALLY_SLOW_DMA_CONTROLLER
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <asm/dma.h>
+ 
+diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
+index 9b4992e..920e578 100644
+--- a/sound/core/memalloc.c
++++ b/sound/core/memalloc.c
+@@ -568,6 +568,7 @@ static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer,
+ 				if (pci_set_dma_mask(pci, mask) < 0 ||
+ 				    pci_set_consistent_dma_mask(pci, mask) < 0) {
+ 					printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
++					pci_dev_put(pci);
+ 					return count;
+ 				}
+ 			}
+diff --git a/sound/core/memory.c b/sound/core/memory.c
+index 25b0f05..1161158 100644
+--- a/sound/core/memory.c
++++ b/sound/core/memory.c
+@@ -20,9 +20,9 @@
+  *
+  */
+ 
+-#include <linux/module.h>
+ #include <asm/io.h>
+ #include <asm/uaccess.h>
++#include <sound/core.h>
+ 
+ /**
+  * copy_to_user_fromio - copy data from mmio-space to user-space
+diff --git a/sound/core/misc.c b/sound/core/misc.c
+index 6cabab8..102d1c3 100644
+--- a/sound/core/misc.c
++++ b/sound/core/misc.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+ #include <linux/ioport.h>
+diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
+index d6a04c2..9ded30d 100644
+--- a/sound/core/oss/copy.c
++++ b/sound/core/oss/copy.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
+index 3ece39f..f874f6c 100644
+--- a/sound/core/oss/io.c
++++ b/sound/core/oss/io.c
+@@ -19,7 +19,6 @@
+  *
+  */
+   
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
+index 06f96a3..da3dbd4 100644
+--- a/sound/core/oss/linear.c
++++ b/sound/core/oss/linear.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
+index c5a5ab9..75daed2 100644
+--- a/sound/core/oss/mixer_oss.c
++++ b/sound/core/oss/mixer_oss.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
+index 848db82..77f9619 100644
+--- a/sound/core/oss/mulaw.c
++++ b/sound/core/oss/mulaw.c
+@@ -21,7 +21,6 @@
+  *
+  */
+   
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
+index d0c4ceb..4c601b1 100644
+--- a/sound/core/oss/pcm_oss.c
++++ b/sound/core/oss/pcm_oss.c
+@@ -26,7 +26,6 @@
+ #define OSS_DEBUG
  #endif
  
- /*
-diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
-index 272ae38..bb7d744 100644
---- a/sound/ppc/keywest.c
-+++ b/sound/ppc/keywest.c
-@@ -34,8 +34,6 @@
- static struct pmac_keywest *keywest_ctx;
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+@@ -985,10 +984,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
+ 		sw_params->stop_threshold = runtime->buffer_size;
+ 	sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+ 	sw_params->period_step = 1;
+-	sw_params->sleep_min = 0;
+ 	sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ 		1 : runtime->period_size;
+-	sw_params->xfer_align = 1;
+ 	if (atomic_read(&substream->mmap_count) ||
+ 	    substream->oss.setup.nosilence) {
+ 		sw_params->silence_threshold = 0;
+@@ -1624,6 +1621,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
+ 					snd_pcm_format_set_silence(runtime->format,
+ 								   runtime->oss.buffer,
+ 								   size1);
++					size1 /= runtime->channels; /* frames */
+ 					fs = snd_enter_user();
+ 					snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
+ 					snd_leave_user(fs);
+diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
+index 14095a9..bec9413 100644
+--- a/sound/core/oss/pcm_plugin.c
++++ b/sound/core/oss/pcm_plugin.c
+@@ -24,7 +24,6 @@
+ #define PLUGIN_DEBUG
+ #endif
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+ #include <linux/vmalloc.h>
+diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
+index 9eb2679..14dfb31 100644
+--- a/sound/core/oss/rate.c
++++ b/sound/core/oss/rate.c
+@@ -19,7 +19,6 @@
+  *
+  */
+   
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
+index de3ffde..da7ab7a 100644
+--- a/sound/core/oss/route.c
++++ b/sound/core/oss/route.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+diff --git a/sound/core/pcm.c b/sound/core/pcm.c
+index cf9b949..9dd9bc7 100644
+--- a/sound/core/pcm.c
++++ b/sound/core/pcm.c
+@@ -19,7 +19,6 @@
+  *
+  */
  
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+@@ -228,7 +227,7 @@ static char *snd_pcm_subformat_names[] = {
  
--#define I2C_DRIVERID_KEYWEST	0xFEBA
+ static char *snd_pcm_tstamp_mode_names[] = {
+ 	TSTAMP(NONE),
+-	TSTAMP(MMAP),
++	TSTAMP(ENABLE),
+ };
+ 
+ static const char *snd_pcm_stream_name(int stream)
+@@ -359,7 +358,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
+ 	snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);	
+ 	snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);	
+ 	snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);	
+-	snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);
+ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+ 	if (substream->oss.oss) {
+ 		snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
+@@ -387,9 +385,7 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
+ 	}
+ 	snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
+ 	snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
+-	snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min);
+ 	snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
+-	snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
+ 	snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
+ 	snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
+ 	snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
+@@ -765,12 +761,6 @@ static int snd_pcm_dev_free(struct snd_device *device)
+ 	return snd_pcm_free(pcm);
+ }
+ 
+-static void snd_pcm_tick_timer_func(unsigned long data)
+-{
+-	struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data;
+-	snd_pcm_tick_elapsed(substream);
+-}
+-
+ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
+ 			     struct file *file,
+ 			     struct snd_pcm_substream **rsubstream)
+@@ -877,9 +867,6 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
+ 	memset((void*)runtime->control, 0, size);
+ 
+ 	init_waitqueue_head(&runtime->sleep);
+-	init_timer(&runtime->tick_timer);
+-	runtime->tick_timer.function = snd_pcm_tick_timer_func;
+-	runtime->tick_timer.data = (unsigned long) substream;
+ 
+ 	runtime->status->state = SNDRV_PCM_STATE_OPEN;
+ 
+diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
+index 2b53979..49aa693 100644
+--- a/sound/core/pcm_compat.c
++++ b/sound/core/pcm_compat.c
+@@ -484,6 +484,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
+ 	case SNDRV_PCM_IOCTL_PVERSION:
+ 	case SNDRV_PCM_IOCTL_INFO:
+ 	case SNDRV_PCM_IOCTL_TSTAMP:
++	case SNDRV_PCM_IOCTL_TTSTAMP:
+ 	case SNDRV_PCM_IOCTL_HWSYNC:
+ 	case SNDRV_PCM_IOCTL_PREPARE:
+ 	case SNDRV_PCM_IOCTL_RESET:
+diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
+index 806f1fb..1533f03 100644
+--- a/sound/core/pcm_lib.c
++++ b/sound/core/pcm_lib.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+@@ -145,11 +144,11 @@ static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substre
+ {
+ 	snd_pcm_uframes_t pos;
+ 
++	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
++		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
+ 	pos = substream->ops->pointer(substream);
+ 	if (pos == SNDRV_PCM_POS_XRUN)
+ 		return pos; /* XRUN */
+-	if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
+-		getnstimeofday((struct timespec *)&runtime->status->tstamp);
+ #ifdef CONFIG_SND_DEBUG
+ 	if (pos >= runtime->buffer_size) {
+ 		snd_printk(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
+@@ -1139,7 +1138,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
+ 
+ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+ {
+-	static int pow2_sizes[] = {
++	static unsigned int pow2_sizes[] = {
+ 		1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
+ 		1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
+ 		1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
+@@ -1451,108 +1450,13 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
+ 
+ EXPORT_SYMBOL(snd_pcm_lib_ioctl);
+ 
+-/*
+- *  Conditions
+- */
 -
- static int keywest_attach_adapter(struct i2c_adapter *adapter);
- static int keywest_detach_client(struct i2c_client *client);
+-static void snd_pcm_system_tick_set(struct snd_pcm_substream *substream, 
+-				    unsigned long ticks)
+-{
+-	struct snd_pcm_runtime *runtime = substream->runtime;
+-	if (ticks == 0)
+-		del_timer(&runtime->tick_timer);
+-	else {
+-		ticks += (1000000 / HZ) - 1;
+-		ticks /= (1000000 / HZ);
+-		mod_timer(&runtime->tick_timer, jiffies + ticks);
+-	}
+-}
+-
+-/* Temporary alias */
+-void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks)
+-{
+-	snd_pcm_system_tick_set(substream, ticks);
+-}
+-
+-void snd_pcm_tick_prepare(struct snd_pcm_substream *substream)
+-{
+-	struct snd_pcm_runtime *runtime = substream->runtime;
+-	snd_pcm_uframes_t frames = ULONG_MAX;
+-	snd_pcm_uframes_t avail, dist;
+-	unsigned int ticks;
+-	u_int64_t n;
+-	u_int32_t r;
+-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+-		if (runtime->silence_size >= runtime->boundary) {
+-			frames = 1;
+-		} else if (runtime->silence_size > 0 &&
+-			   runtime->silence_filled < runtime->buffer_size) {
+-			snd_pcm_sframes_t noise_dist;
+-			noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+-			if (noise_dist > (snd_pcm_sframes_t)runtime->silence_threshold)
+-				frames = noise_dist - runtime->silence_threshold;
+-		}
+-		avail = snd_pcm_playback_avail(runtime);
+-	} else {
+-		avail = snd_pcm_capture_avail(runtime);
+-	}
+-	if (avail < runtime->control->avail_min) {
+-		snd_pcm_sframes_t n = runtime->control->avail_min - avail;
+-		if (n > 0 && frames > (snd_pcm_uframes_t)n)
+-			frames = n;
+-	}
+-	if (avail < runtime->buffer_size) {
+-		snd_pcm_sframes_t n = runtime->buffer_size - avail;
+-		if (n > 0 && frames > (snd_pcm_uframes_t)n)
+-			frames = n;
+-	}
+-	if (frames == ULONG_MAX) {
+-		snd_pcm_tick_set(substream, 0);
+-		return;
+-	}
+-	dist = runtime->status->hw_ptr - runtime->hw_ptr_base;
+-	/* Distance to next interrupt */
+-	dist = runtime->period_size - dist % runtime->period_size;
+-	if (dist <= frames) {
+-		snd_pcm_tick_set(substream, 0);
+-		return;
+-	}
+-	/* the base time is us */
+-	n = frames;
+-	n *= 1000000;
+-	div64_32(&n, runtime->tick_time * runtime->rate, &r);
+-	ticks = n + (r > 0 ? 1 : 0);
+-	if (ticks < runtime->sleep_min)
+-		ticks = runtime->sleep_min;
+-	snd_pcm_tick_set(substream, (unsigned long) ticks);
+-}
+-
+-void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream)
+-{
+-	struct snd_pcm_runtime *runtime;
+-	unsigned long flags;
+-	
+-	snd_assert(substream != NULL, return);
+-	runtime = substream->runtime;
+-	snd_assert(runtime != NULL, return);
+-
+-	snd_pcm_stream_lock_irqsave(substream, flags);
+-	if (!snd_pcm_running(substream) ||
+-	    snd_pcm_update_hw_ptr(substream) < 0)
+-		goto _end;
+-	if (runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+- _end:
+-	snd_pcm_stream_unlock_irqrestore(substream, flags);
+-}
+-
+ /**
+  * snd_pcm_period_elapsed - update the pcm status for the next period
+  * @substream: the pcm substream instance
+  *
+  * This function is called from the interrupt handler when the
+  * PCM has processed the period size.  It will update the current
+- * pointer, set up the tick, wake up sleepers, etc.
++ * pointer, wake up sleepers, etc.
+  *
+  * Even if more than one periods have elapsed since the last call, you
+  * have to call this only once.
+@@ -1576,8 +1480,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+ 
+ 	if (substream->timer_running)
+ 		snd_timer_interrupt(substream->timer, 1);
+-	if (runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+  _end:
+ 	snd_pcm_stream_unlock_irqrestore(substream, flags);
+ 	if (runtime->transfer_ack_end)
+@@ -1587,6 +1489,71 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+ 
+ EXPORT_SYMBOL(snd_pcm_period_elapsed);
+ 
++/*
++ * Wait until avail_min data becomes available
++ * Returns a negative error code if any error occurs during operation.
++ * The available space is stored on availp.  When err = 0 and avail = 0
++ * on the capture stream, it indicates the stream is in DRAINING state.
++ */
++static int wait_for_avail_min(struct snd_pcm_substream *substream,
++			      snd_pcm_uframes_t *availp)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
++	wait_queue_t wait;
++	int err = 0;
++	snd_pcm_uframes_t avail = 0;
++	long tout;
++
++	init_waitqueue_entry(&wait, current);
++	add_wait_queue(&runtime->sleep, &wait);
++	for (;;) {
++		if (signal_pending(current)) {
++			err = -ERESTARTSYS;
++			break;
++		}
++		set_current_state(TASK_INTERRUPTIBLE);
++		snd_pcm_stream_unlock_irq(substream);
++		tout = schedule_timeout(msecs_to_jiffies(10000));
++		snd_pcm_stream_lock_irq(substream);
++		switch (runtime->status->state) {
++		case SNDRV_PCM_STATE_SUSPENDED:
++			err = -ESTRPIPE;
++			goto _endloop;
++		case SNDRV_PCM_STATE_XRUN:
++			err = -EPIPE;
++			goto _endloop;
++		case SNDRV_PCM_STATE_DRAINING:
++			if (is_playback)
++				err = -EPIPE;
++			else 
++				avail = 0; /* indicate draining */
++			goto _endloop;
++		case SNDRV_PCM_STATE_OPEN:
++		case SNDRV_PCM_STATE_SETUP:
++		case SNDRV_PCM_STATE_DISCONNECTED:
++			err = -EBADFD;
++			goto _endloop;
++		}
++		if (!tout) {
++			snd_printd("%s write error (DMA or IRQ trouble?)\n",
++				   is_playback ? "playback" : "capture");
++			err = -EIO;
++			break;
++		}
++		if (is_playback)
++			avail = snd_pcm_playback_avail(runtime);
++		else
++			avail = snd_pcm_capture_avail(runtime);
++		if (avail >= runtime->control->avail_min)
++			break;
++	}
++ _endloop:
++	remove_wait_queue(&runtime->sleep, &wait);
++	*availp = avail;
++	return err;
++}
++	
+ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
+ 				      unsigned int hwoff,
+ 				      unsigned long data, unsigned int off,
+@@ -1624,8 +1591,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
  
-@@ -43,7 +41,6 @@ struct i2c_driver keywest_driver = {
- 	.driver = {
- 		.name = "PMac Keywest Audio",
- 	},
--	.id = I2C_DRIVERID_KEYWEST,
- 	.attach_adapter = &keywest_attach_adapter,
- 	.detach_client = &keywest_detach_client,
+ 	if (size == 0)
+ 		return 0;
+-	if (size > runtime->xfer_align)
+-		size -= size % runtime->xfer_align;
+ 
+ 	snd_pcm_stream_lock_irq(substream);
+ 	switch (runtime->status->state) {
+@@ -1648,84 +1613,18 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
+ 		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
+ 		snd_pcm_uframes_t avail;
+ 		snd_pcm_uframes_t cont;
+-		if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
++		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ 			snd_pcm_update_hw_ptr(substream);
+ 		avail = snd_pcm_playback_avail(runtime);
+-		if (((avail < runtime->control->avail_min && size > avail) ||
+-		   (size >= runtime->xfer_align && avail < runtime->xfer_align))) {
+-			wait_queue_t wait;
+-			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
+-			long tout;
+-
++		if (!avail) {
+ 			if (nonblock) {
+ 				err = -EAGAIN;
+ 				goto _end_unlock;
+ 			}
+-
+-			init_waitqueue_entry(&wait, current);
+-			add_wait_queue(&runtime->sleep, &wait);
+-			while (1) {
+-				if (signal_pending(current)) {
+-					state = SIGNALED;
+-					break;
+-				}
+-				set_current_state(TASK_INTERRUPTIBLE);
+-				snd_pcm_stream_unlock_irq(substream);
+-				tout = schedule_timeout(10 * HZ);
+-				snd_pcm_stream_lock_irq(substream);
+-				if (tout == 0) {
+-					if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
+-					    runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
+-						state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+-						break;
+-					}
+-				}
+-				switch (runtime->status->state) {
+-				case SNDRV_PCM_STATE_XRUN:
+-				case SNDRV_PCM_STATE_DRAINING:
+-					state = ERROR;
+-					goto _end_loop;
+-				case SNDRV_PCM_STATE_SUSPENDED:
+-					state = SUSPENDED;
+-					goto _end_loop;
+-				case SNDRV_PCM_STATE_SETUP:
+-					state = DROPPED;
+-					goto _end_loop;
+-				default:
+-					break;
+-				}
+-				avail = snd_pcm_playback_avail(runtime);
+-				if (avail >= runtime->control->avail_min) {
+-					state = READY;
+-					break;
+-				}
+-			}
+-		       _end_loop:
+-			remove_wait_queue(&runtime->sleep, &wait);
+-
+-			switch (state) {
+-			case ERROR:
+-				err = -EPIPE;
+-				goto _end_unlock;
+-			case SUSPENDED:
+-				err = -ESTRPIPE;
+-				goto _end_unlock;
+-			case SIGNALED:
+-				err = -ERESTARTSYS;
+-				goto _end_unlock;
+-			case EXPIRED:
+-				snd_printd("playback write error (DMA or IRQ trouble?)\n");
+-				err = -EIO;
+-				goto _end_unlock;
+-			case DROPPED:
+-				err = -EBADFD;
++			err = wait_for_avail_min(substream, &avail);
++			if (err < 0)
+ 				goto _end_unlock;
+-			default:
+-				break;
+-			}
+ 		}
+-		if (avail > runtime->xfer_align)
+-			avail -= avail % runtime->xfer_align;
+ 		frames = size > avail ? avail : size;
+ 		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+ 		if (frames > cont)
+@@ -1763,9 +1662,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
+ 			if (err < 0)
+ 				goto _end_unlock;
+ 		}
+-		if (runtime->sleep_min &&
+-		    runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+-			snd_pcm_tick_prepare(substream);
+ 	}
+  _end_unlock:
+ 	snd_pcm_stream_unlock_irq(substream);
+@@ -1893,8 +1789,6 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
+ 
+ 	if (size == 0)
+ 		return 0;
+-	if (size > runtime->xfer_align)
+-		size -= size % runtime->xfer_align;
+ 
+ 	snd_pcm_stream_lock_irq(substream);
+ 	switch (runtime->status->state) {
+@@ -1924,91 +1818,25 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
+ 		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
+ 		snd_pcm_uframes_t avail;
+ 		snd_pcm_uframes_t cont;
+-		if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
++		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ 			snd_pcm_update_hw_ptr(substream);
+-	      __draining:
+ 		avail = snd_pcm_capture_avail(runtime);
+-		if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+-			if (avail < runtime->xfer_align) {
+-				err = -EPIPE;
++		if (!avail) {
++			if (runtime->status->state ==
++			    SNDRV_PCM_STATE_DRAINING) {
++				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ 				goto _end_unlock;
+ 			}
+-		} else if ((avail < runtime->control->avail_min && size > avail) ||
+-			   (size >= runtime->xfer_align && avail < runtime->xfer_align)) {
+-			wait_queue_t wait;
+-			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;
+-			long tout;
+-
+ 			if (nonblock) {
+ 				err = -EAGAIN;
+ 				goto _end_unlock;
+ 			}
+-
+-			init_waitqueue_entry(&wait, current);
+-			add_wait_queue(&runtime->sleep, &wait);
+-			while (1) {
+-				if (signal_pending(current)) {
+-					state = SIGNALED;
+-					break;
+-				}
+-				set_current_state(TASK_INTERRUPTIBLE);
+-				snd_pcm_stream_unlock_irq(substream);
+-				tout = schedule_timeout(10 * HZ);
+-				snd_pcm_stream_lock_irq(substream);
+-				if (tout == 0) {
+-					if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
+-					    runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
+-						state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
+-						break;
+-					}
+-				}
+-				switch (runtime->status->state) {
+-				case SNDRV_PCM_STATE_XRUN:
+-					state = ERROR;
+-					goto _end_loop;
+-				case SNDRV_PCM_STATE_SUSPENDED:
+-					state = SUSPENDED;
+-					goto _end_loop;
+-				case SNDRV_PCM_STATE_DRAINING:
+-					goto __draining;
+-				case SNDRV_PCM_STATE_SETUP:
+-					state = DROPPED;
+-					goto _end_loop;
+-				default:
+-					break;
+-				}
+-				avail = snd_pcm_capture_avail(runtime);
+-				if (avail >= runtime->control->avail_min) {
+-					state = READY;
+-					break;
+-				}
+-			}
+-		       _end_loop:
+-			remove_wait_queue(&runtime->sleep, &wait);
+-
+-			switch (state) {
+-			case ERROR:
+-				err = -EPIPE;
+-				goto _end_unlock;
+-			case SUSPENDED:
+-				err = -ESTRPIPE;
+-				goto _end_unlock;
+-			case SIGNALED:
+-				err = -ERESTARTSYS;
+-				goto _end_unlock;
+-			case EXPIRED:
+-				snd_printd("capture read error (DMA or IRQ trouble?)\n");
+-				err = -EIO;
+-				goto _end_unlock;
+-			case DROPPED:
+-				err = -EBADFD;
++			err = wait_for_avail_min(substream, &avail);
++			if (err < 0)
+ 				goto _end_unlock;
+-			default:
+-				break;
+-			}
++			if (!avail)
++				continue; /* draining */
+ 		}
+-		if (avail > runtime->xfer_align)
+-			avail -= avail % runtime->xfer_align;
+ 		frames = size > avail ? avail : size;
+ 		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+ 		if (frames > cont)
+@@ -2040,9 +1868,6 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
+ 		offset += frames;
+ 		size -= frames;
+ 		xfer += frames;
+-		if (runtime->sleep_min &&
+-		    runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+-			snd_pcm_tick_prepare(substream);
+ 	}
+  _end_unlock:
+ 	snd_pcm_stream_unlock_irq(substream);
+diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
+index a13e38c..ff07b4a 100644
+--- a/sound/core/pcm_memory.c
++++ b/sound/core/pcm_memory.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/time.h>
+ #include <linux/init.h>
+diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
+index dd9aa51..89b7f54 100644
+--- a/sound/core/pcm_misc.c
++++ b/sound/core/pcm_misc.c
+@@ -19,7 +19,6 @@
+  *
+  */
+   
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+@@ -75,7 +74,7 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
+ 	},
+ 	[SNDRV_PCM_FORMAT_U24_BE] = {
+ 		.width = 24, .phys = 32, .le = 0, .signd = 0,
+-		.silence = { 0x80, 0x00, 0x00 },
++		.silence = { 0x00, 0x80, 0x00, 0x00 },
+ 	},
+ 	[SNDRV_PCM_FORMAT_S32_LE] = {
+ 		.width = 32, .phys = 32, .le = 1, .signd = 1,
+diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
+index fb3dde4..6244911 100644
+--- a/sound/core/pcm_native.c
++++ b/sound/core/pcm_native.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/mm.h>
+ #include <linux/file.h>
+ #include <linux/slab.h>
+@@ -413,7 +412,6 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
+ 	runtime->period_size = params_period_size(params);
+ 	runtime->periods = params_periods(params);
+ 	runtime->buffer_size = params_buffer_size(params);
+-	runtime->tick_time = params_tick_time(params);
+ 	runtime->info = params->info;
+ 	runtime->rate_num = params->rate_num;
+ 	runtime->rate_den = params->rate_den;
+@@ -433,9 +431,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
+ 	/* Default sw params */
+ 	runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+ 	runtime->period_step = 1;
+-	runtime->sleep_min = 0;
+ 	runtime->control->avail_min = runtime->period_size;
+-	runtime->xfer_align = runtime->period_size;
+ 	runtime->start_threshold = 1;
+ 	runtime->stop_threshold = runtime->buffer_size;
+ 	runtime->silence_threshold = 0;
+@@ -532,9 +528,6 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
+ 		return -EINVAL;
+ 	if (params->avail_min == 0)
+ 		return -EINVAL;
+-	if (params->xfer_align == 0 ||
+-	    params->xfer_align % runtime->min_align != 0)
+-		return -EINVAL;
+ 	if (params->silence_size >= runtime->boundary) {
+ 		if (params->silence_threshold != 0)
+ 			return -EINVAL;
+@@ -546,20 +539,14 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
+ 	}
+ 	snd_pcm_stream_lock_irq(substream);
+ 	runtime->tstamp_mode = params->tstamp_mode;
+-	runtime->sleep_min = params->sleep_min;
+ 	runtime->period_step = params->period_step;
+ 	runtime->control->avail_min = params->avail_min;
+ 	runtime->start_threshold = params->start_threshold;
+ 	runtime->stop_threshold = params->stop_threshold;
+ 	runtime->silence_threshold = params->silence_threshold;
+ 	runtime->silence_size = params->silence_size;
+-	runtime->xfer_align = params->xfer_align;
+         params->boundary = runtime->boundary;
+ 	if (snd_pcm_running(substream)) {
+-		if (runtime->sleep_min)
+-			snd_pcm_tick_prepare(substream);
+-		else
+-			snd_pcm_tick_set(substream, 0);
+ 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ 		    runtime->silence_size > 0)
+ 			snd_pcm_playback_silence(substream, ULONG_MAX);
+@@ -595,12 +582,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
+ 	status->trigger_tstamp = runtime->trigger_tstamp;
+ 	if (snd_pcm_running(substream)) {
+ 		snd_pcm_update_hw_ptr(substream);
+-		if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
++		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ 			status->tstamp = runtime->status->tstamp;
+-		else
+-			getnstimeofday(&status->tstamp);
+-	} else
+-		getnstimeofday(&status->tstamp);
++			goto _tstamp_end;
++		}
++	}
++	snd_pcm_gettime(runtime, &status->tstamp);
++ _tstamp_end:
+ 	status->appl_ptr = runtime->control->appl_ptr;
+ 	status->hw_ptr = runtime->status->hw_ptr;
+ 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+@@ -688,7 +676,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
+ 	if (runtime->trigger_master == NULL)
+ 		return;
+ 	if (runtime->trigger_master == substream) {
+-		getnstimeofday(&runtime->trigger_tstamp);
++		snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ 	} else {
+ 		snd_pcm_trigger_tstamp(runtime->trigger_master);
+ 		runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
+@@ -875,8 +863,6 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
+ 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ 	    runtime->silence_size > 0)
+ 		snd_pcm_playback_silence(substream, ULONG_MAX);
+-	if (runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+ 	if (substream->timer)
+ 		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
+ 				 &runtime->trigger_tstamp);
+@@ -930,7 +916,6 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
+ 			snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
+ 					 &runtime->trigger_tstamp);
+ 		runtime->status->state = state;
+-		snd_pcm_tick_set(substream, 0);
+ 	}
+ 	wake_up(&runtime->sleep);
+ }
+@@ -1014,12 +999,9 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
+ 			snd_timer_notify(substream->timer,
+ 					 SNDRV_TIMER_EVENT_MPAUSE,
+ 					 &runtime->trigger_tstamp);
+-		snd_pcm_tick_set(substream, 0);
+ 		wake_up(&runtime->sleep);
+ 	} else {
+ 		runtime->status->state = SNDRV_PCM_STATE_RUNNING;
+-		if (runtime->sleep_min)
+-			snd_pcm_tick_prepare(substream);
+ 		if (substream->timer)
+ 			snd_timer_notify(substream->timer,
+ 					 SNDRV_TIMER_EVENT_MCONTINUE,
+@@ -1074,7 +1056,6 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
+ 				 &runtime->trigger_tstamp);
+ 	runtime->status->suspended_state = runtime->status->state;
+ 	runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
+-	snd_pcm_tick_set(substream, 0);
+ 	wake_up(&runtime->sleep);
+ }
+ 
+@@ -1177,8 +1158,6 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
+ 		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
+ 				 &runtime->trigger_tstamp);
+ 	runtime->status->state = runtime->status->suspended_state;
+-	if (runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+ }
+ 
+ static struct action_ops snd_pcm_action_resume = {
+@@ -1395,10 +1374,10 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
+ 	} else {
+ 		/* stop running stream */
+ 		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
+-			int state = snd_pcm_capture_avail(runtime) > 0 ?
++			int new_state = snd_pcm_capture_avail(runtime) > 0 ?
+ 				SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
+-			snd_pcm_do_stop(substream, state);
+-			snd_pcm_post_stop(substream, state);
++			snd_pcm_do_stop(substream, new_state);
++			snd_pcm_post_stop(substream, new_state);
+ 		}
+ 	}
+ 	return 0;
+@@ -2007,8 +1986,6 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
+ 	}
+ 
+ 	/* FIXME: this belong to lowlevel */
+-	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME,
+-				     1000000 / HZ, 1000000 / HZ);
+ 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ 
+ 	return 0;
+@@ -2244,15 +2221,10 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
+ 	}
+ 	if (frames > (snd_pcm_uframes_t)hw_avail)
+ 		frames = hw_avail;
+-	else
+-		frames -= frames % runtime->xfer_align;
+ 	appl_ptr = runtime->control->appl_ptr - frames;
+ 	if (appl_ptr < 0)
+ 		appl_ptr += runtime->boundary;
+ 	runtime->control->appl_ptr = appl_ptr;
+-	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+-	    runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+ 	ret = frames;
+  __end:
+ 	snd_pcm_stream_unlock_irq(substream);
+@@ -2294,15 +2266,10 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
+ 	}
+ 	if (frames > (snd_pcm_uframes_t)hw_avail)
+ 		frames = hw_avail;
+-	else
+-		frames -= frames % runtime->xfer_align;
+ 	appl_ptr = runtime->control->appl_ptr - frames;
+ 	if (appl_ptr < 0)
+ 		appl_ptr += runtime->boundary;
+ 	runtime->control->appl_ptr = appl_ptr;
+-	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+-	    runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+ 	ret = frames;
+  __end:
+ 	snd_pcm_stream_unlock_irq(substream);
+@@ -2345,15 +2312,10 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
+ 	}
+ 	if (frames > (snd_pcm_uframes_t)avail)
+ 		frames = avail;
+-	else
+-		frames -= frames % runtime->xfer_align;
+ 	appl_ptr = runtime->control->appl_ptr + frames;
+ 	if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+ 		appl_ptr -= runtime->boundary;
+ 	runtime->control->appl_ptr = appl_ptr;
+-	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+-	    runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+ 	ret = frames;
+  __end:
+ 	snd_pcm_stream_unlock_irq(substream);
+@@ -2396,15 +2358,10 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
+ 	}
+ 	if (frames > (snd_pcm_uframes_t)avail)
+ 		frames = avail;
+-	else
+-		frames -= frames % runtime->xfer_align;
+ 	appl_ptr = runtime->control->appl_ptr + frames;
+ 	if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+ 		appl_ptr -= runtime->boundary;
+ 	runtime->control->appl_ptr = appl_ptr;
+-	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
+-	    runtime->sleep_min)
+-		snd_pcm_tick_prepare(substream);
+ 	ret = frames;
+  __end:
+ 	snd_pcm_stream_unlock_irq(substream);
+@@ -2519,6 +2476,21 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
+ 		return -EFAULT;
+ 	return 0;
+ }
++
++static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	int arg;
++	
++	if (get_user(arg, _arg))
++		return -EFAULT;
++	if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
++		return -EINVAL;
++	runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
++	if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
++		runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
++	return 0;
++}
+ 		
+ static int snd_pcm_common_ioctl1(struct file *file,
+ 				 struct snd_pcm_substream *substream,
+@@ -2531,8 +2503,10 @@ static int snd_pcm_common_ioctl1(struct file *file,
+ 		return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
+ 	case SNDRV_PCM_IOCTL_INFO:
+ 		return snd_pcm_info_user(substream, arg);
+-	case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */
++	case SNDRV_PCM_IOCTL_TSTAMP:	/* just for compatibility */
+ 		return 0;
++	case SNDRV_PCM_IOCTL_TTSTAMP:
++		return snd_pcm_tstamp(substream, arg);
+ 	case SNDRV_PCM_IOCTL_HW_REFINE:
+ 		return snd_pcm_hw_refine_user(substream, arg);
+ 	case SNDRV_PCM_IOCTL_HW_PARAMS:
+@@ -3018,26 +2992,23 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
+ /*
+  * mmap status record
+  */
+-static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area,
+-						unsigned long address, int *type)
++static int snd_pcm_mmap_status_fault(struct vm_area_struct *area,
++						struct vm_fault *vmf)
+ {
+ 	struct snd_pcm_substream *substream = area->vm_private_data;
+ 	struct snd_pcm_runtime *runtime;
+-	struct page * page;
+ 	
+ 	if (substream == NULL)
+-		return NOPAGE_SIGBUS;
++		return VM_FAULT_SIGBUS;
+ 	runtime = substream->runtime;
+-	page = virt_to_page(runtime->status);
+-	get_page(page);
+-	if (type)
+-		*type = VM_FAULT_MINOR;
+-	return page;
++	vmf->page = virt_to_page(runtime->status);
++	get_page(vmf->page);
++	return 0;
+ }
+ 
+ static struct vm_operations_struct snd_pcm_vm_ops_status =
+ {
+-	.nopage =	snd_pcm_mmap_status_nopage,
++	.fault =	snd_pcm_mmap_status_fault,
  };
-diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
-index abac628..dab22cc 100644
---- a/sound/soc/codecs/cs4270.c
-+++ b/sound/soc/codecs/cs4270.c
-@@ -234,7 +234,7 @@ static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-  * lower three bits are determined via the AD2, AD1, and AD0 pins
-  * (respectively).
+ 
+ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
+@@ -3061,26 +3032,23 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file
+ /*
+  * mmap control record
   */
--static unsigned short normal_i2c[] = {
-+static const unsigned short normal_i2c[] = {
- 	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END
+-static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area,
+-						 unsigned long address, int *type)
++static int snd_pcm_mmap_control_fault(struct vm_area_struct *area,
++						struct vm_fault *vmf)
+ {
+ 	struct snd_pcm_substream *substream = area->vm_private_data;
+ 	struct snd_pcm_runtime *runtime;
+-	struct page * page;
+ 	
+ 	if (substream == NULL)
+-		return NOPAGE_SIGBUS;
++		return VM_FAULT_SIGBUS;
+ 	runtime = substream->runtime;
+-	page = virt_to_page(runtime->control);
+-	get_page(page);
+-	if (type)
+-		*type = VM_FAULT_MINOR;
+-	return page;
++	vmf->page = virt_to_page(runtime->control);
++	get_page(vmf->page);
++	return 0;
+ }
+ 
+ static struct vm_operations_struct snd_pcm_vm_ops_control =
+ {
+-	.nopage =	snd_pcm_mmap_control_nopage,
++	.fault =	snd_pcm_mmap_control_fault,
  };
- I2C_CLIENT_INSMOD;
-diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
-index d5a8fc2..f1f6b94 100644
---- a/sound/soc/s3c24xx/neo1973_wm8753.c
-+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
-@@ -573,7 +573,7 @@ static struct snd_soc_device neo1973_snd_devdata = {
  
- static struct i2c_client client_template;
+ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
+@@ -3117,10 +3085,10 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
+ #endif /* coherent mmap */
+ 
+ /*
+- * nopage callback for mmapping a RAM page
++ * fault callback for mmapping a RAM page
+  */
+-static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area,
+-					     unsigned long address, int *type)
++static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
++						struct vm_fault *vmf)
+ {
+ 	struct snd_pcm_substream *substream = area->vm_private_data;
+ 	struct snd_pcm_runtime *runtime;
+@@ -3130,33 +3098,30 @@ static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area,
+ 	size_t dma_bytes;
+ 	
+ 	if (substream == NULL)
+-		return NOPAGE_SIGBUS;
++		return VM_FAULT_SIGBUS;
+ 	runtime = substream->runtime;
+-	offset = area->vm_pgoff << PAGE_SHIFT;
+-	offset += address - area->vm_start;
+-	snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS);
++	offset = vmf->pgoff << PAGE_SHIFT;
+ 	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+ 	if (offset > dma_bytes - PAGE_SIZE)
+-		return NOPAGE_SIGBUS;
++		return VM_FAULT_SIGBUS;
+ 	if (substream->ops->page) {
+ 		page = substream->ops->page(substream, offset);
+-		if (! page)
+-			return NOPAGE_OOM; /* XXX: is this really due to OOM? */
++		if (!page)
++			return VM_FAULT_SIGBUS;
+ 	} else {
+ 		vaddr = runtime->dma_area + offset;
+ 		page = virt_to_page(vaddr);
+ 	}
+ 	get_page(page);
+-	if (type)
+-		*type = VM_FAULT_MINOR;
+-	return page;
++	vmf->page = page;
++	return 0;
+ }
+ 
+ static struct vm_operations_struct snd_pcm_vm_ops_data =
+ {
+ 	.open =		snd_pcm_mmap_data_open,
+ 	.close =	snd_pcm_mmap_data_close,
+-	.nopage =	snd_pcm_mmap_data_nopage,
++	.fault =	snd_pcm_mmap_data_fault,
+ };
+ 
+ /*
+diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
+index 23aa9a2..033a024 100644
+--- a/sound/core/pcm_timer.c
++++ b/sound/core/pcm_timer.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
+index b8e700b..f7ea728 100644
+--- a/sound/core/rawmidi.c
++++ b/sound/core/rawmidi.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <linux/major.h>
+ #include <linux/init.h>
+@@ -912,7 +911,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
+ }
+ 
+ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
+-				     unsigned char *buf, long count, int kernel)
++				     unsigned char __user *userbuf,
++				     unsigned char *kernelbuf, long count)
+ {
+ 	unsigned long flags;
+ 	long result = 0, count1;
+@@ -925,11 +925,11 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
+ 		spin_lock_irqsave(&runtime->lock, flags);
+ 		if (count1 > (int)runtime->avail)
+ 			count1 = runtime->avail;
+-		if (kernel) {
+-			memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1);
+-		} else {
++		if (kernelbuf)
++			memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
++		if (userbuf) {
+ 			spin_unlock_irqrestore(&runtime->lock, flags);
+-			if (copy_to_user((char __user *)buf + result,
++			if (copy_to_user(userbuf + result,
+ 					 runtime->buffer + runtime->appl_ptr, count1)) {
+ 				return result > 0 ? result : -EFAULT;
+ 			}
+@@ -949,7 +949,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
+ 			     unsigned char *buf, long count)
+ {
+ 	snd_rawmidi_input_trigger(substream, 1);
+-	return snd_rawmidi_kernel_read1(substream, buf, count, 1);
++	return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
+ }
+ 
+ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
+@@ -990,8 +990,9 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
+ 		}
+ 		spin_unlock_irq(&runtime->lock);
+ 		count1 = snd_rawmidi_kernel_read1(substream,
+-						  (unsigned char __force *)buf,
+-						  count, 0);
++						  (unsigned char __user *)buf,
++						  NULL/*kernelbuf*/,
++						  count);
+ 		if (count1 < 0)
+ 			return result > 0 ? result : count1;
+ 		result += count1;
+@@ -1132,13 +1133,15 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
+ }
+ 
+ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
+-				      const unsigned char *buf, long count, int kernel)
++				      const unsigned char __user *userbuf,
++				      const unsigned char *kernelbuf,
++				      long count)
+ {
+ 	unsigned long flags;
+ 	long count1, result;
+ 	struct snd_rawmidi_runtime *runtime = substream->runtime;
+ 
+-	snd_assert(buf != NULL, return -EINVAL);
++	snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL);
+ 	snd_assert(runtime->buffer != NULL, return -EINVAL);
+ 
+ 	result = 0;
+@@ -1155,12 +1158,13 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
+ 			count1 = count;
+ 		if (count1 > (long)runtime->avail)
+ 			count1 = runtime->avail;
+-		if (kernel) {
+-			memcpy(runtime->buffer + runtime->appl_ptr, buf, count1);
+-		} else {
++		if (kernelbuf)
++			memcpy(runtime->buffer + runtime->appl_ptr,
++			       kernelbuf + result, count1);
++		else if (userbuf) {
+ 			spin_unlock_irqrestore(&runtime->lock, flags);
+ 			if (copy_from_user(runtime->buffer + runtime->appl_ptr,
+-					   (char __user *)buf, count1)) {
++					   userbuf + result, count1)) {
+ 				spin_lock_irqsave(&runtime->lock, flags);
+ 				result = result > 0 ? result : -EFAULT;
+ 				goto __end;
+@@ -1171,7 +1175,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
+ 		runtime->appl_ptr %= runtime->buffer_size;
+ 		runtime->avail -= count1;
+ 		result += count1;
+-		buf += count1;
+ 		count -= count1;
+ 	}
+       __end:
+@@ -1185,7 +1188,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
+ long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
+ 			      const unsigned char *buf, long count)
+ {
+-	return snd_rawmidi_kernel_write1(substream, buf, count, 1);
++	return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
+ }
+ 
+ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
+@@ -1225,9 +1228,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
+ 			spin_lock_irq(&runtime->lock);
+ 		}
+ 		spin_unlock_irq(&runtime->lock);
+-		count1 = snd_rawmidi_kernel_write1(substream,
+-						   (unsigned char __force *)buf,
+-						   count, 0);
++		count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
+ 		if (count1 < 0)
+ 			return result > 0 ? result : count1;
+ 		result += count1;
+diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
+index 7cd5e8f..97b30fb 100644
+--- a/sound/core/rtctimer.c
++++ b/sound/core/rtctimer.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/moduleparam.h>
+diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
+index ceef14a..0695937 100644
+--- a/sound/core/seq/Makefile
++++ b/sound/core/seq/Makefile
+@@ -3,7 +3,6 @@
+ # Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+ #
+ 
+-obj-$(CONFIG_SND) += instr/
+ ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
+   obj-$(CONFIG_SND_SEQUENCER) += oss/
+ endif
+@@ -15,7 +14,6 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
+ snd-seq-midi-objs := seq_midi.o
+ snd-seq-midi-emul-objs := seq_midi_emul.o
+ snd-seq-midi-event-objs := seq_midi_event.o
+-snd-seq-instr-objs := seq_instr.o
+ snd-seq-dummy-objs := seq_dummy.o
+ snd-seq-virmidi-objs := seq_virmidi.o
+ 
+@@ -36,9 +34,7 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+ # Toplevel Module Dependency
+ obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
+ obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
+-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
+-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o
++obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
++obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
+ obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
+ obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
+-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
+diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile
+deleted file mode 100644
+index 6089603..0000000
+--- a/sound/core/seq/instr/Makefile
++++ /dev/null
+@@ -1,23 +0,0 @@
+-#
+-# Makefile for ALSA
+-# Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+-#
+-
+-snd-ainstr-fm-objs := ainstr_fm.o
+-snd-ainstr-simple-objs := ainstr_simple.o
+-snd-ainstr-gf1-objs := ainstr_gf1.o
+-snd-ainstr-iw-objs := ainstr_iw.o
+-
+-#
+-# this function returns:
+-#   "m" - CONFIG_SND_SEQUENCER is m
+-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+-#   otherwise parameter #1 value
+-#
+-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+-
+-# Toplevel Module Dependency
+-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
+-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
+-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
+-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
+diff --git a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c
+deleted file mode 100644
+index f80fab8..0000000
+--- a/sound/core/seq/instr/ainstr_fm.c
++++ /dev/null
+@@ -1,155 +0,0 @@
+-/*
+- *  FM (OPL2/3) Instrument routines
+- *  Copyright (c) 2000 Uros Bizjak <uros at kss-loka.si>
+- *
+- *   This program 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 program 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 <sound/core.h>
+-#include <sound/ainstr_fm.h>
+-#include <sound/initval.h>
+-#include <asm/uaccess.h>
+-
+-MODULE_AUTHOR("Uros Bizjak <uros at kss-loka.si>");
+-MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
+-MODULE_LICENSE("GPL");
+-
+-static int snd_seq_fm_put(void *private_data, struct snd_seq_kinstr *instr,
+-			  char __user *instr_data, long len, int atomic, int cmd)
+-{
+-	struct fm_instrument *ip;
+-	struct fm_xinstrument ix;
+-	int idx;
+-
+-	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+-		return -EINVAL;
+-	/* copy instrument data */
+-	if (len < (long)sizeof(ix))
+-		return -EINVAL;
+-	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+-		return -EFAULT;
+-	if (ix.stype != FM_STRU_INSTR)
+-		return -EINVAL;
+-	ip = (struct fm_instrument *)KINSTR_DATA(instr);
+-	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+-	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+-	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+-	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+-	ip->type = ix.type;
+-	for (idx = 0; idx < 4; idx++) {
+-		ip->op[idx].am_vib = ix.op[idx].am_vib;
+-		ip->op[idx].ksl_level = ix.op[idx].ksl_level;
+-		ip->op[idx].attack_decay = ix.op[idx].attack_decay;
+-		ip->op[idx].sustain_release = ix.op[idx].sustain_release;
+-		ip->op[idx].wave_select = ix.op[idx].wave_select;
+-	}
+-	for (idx = 0; idx < 2; idx++) {
+-		ip->feedback_connection[idx] = ix.feedback_connection[idx];
+-	}
+-	ip->echo_delay = ix.echo_delay;
+-	ip->echo_atten = ix.echo_atten;
+-	ip->chorus_spread = ix.chorus_spread;
+-	ip->trnsps = ix.trnsps;
+-	ip->fix_dur = ix.fix_dur;
+-	ip->modes = ix.modes;
+-	ip->fix_key = ix.fix_key;
+-	return 0;
+-}
+-
+-static int snd_seq_fm_get(void *private_data, struct snd_seq_kinstr *instr,
+-			  char __user *instr_data, long len, int atomic,
+-			  int cmd)
+-{
+-	struct fm_instrument *ip;
+-	struct fm_xinstrument ix;
+-	int idx;
+-	
+-	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+-		return -EINVAL;
+-	if (len < (long)sizeof(ix))
+-		return -ENOMEM;
+-	memset(&ix, 0, sizeof(ix));
+-	ip = (struct fm_instrument *)KINSTR_DATA(instr);
+-	ix.stype = FM_STRU_INSTR;
+-	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+-	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+-	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+-	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+-	ix.type = ip->type;
+-	for (idx = 0; idx < 4; idx++) {
+-		ix.op[idx].am_vib = ip->op[idx].am_vib;
+-		ix.op[idx].ksl_level = ip->op[idx].ksl_level;
+-		ix.op[idx].attack_decay = ip->op[idx].attack_decay;
+-		ix.op[idx].sustain_release = ip->op[idx].sustain_release;
+-		ix.op[idx].wave_select = ip->op[idx].wave_select;
+-	}
+-	for (idx = 0; idx < 2; idx++) {
+-		ix.feedback_connection[idx] = ip->feedback_connection[idx];
+-	}
+-	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+-		return -EFAULT;
+-	ix.echo_delay = ip->echo_delay;
+-	ix.echo_atten = ip->echo_atten;
+-	ix.chorus_spread = ip->chorus_spread;
+-	ix.trnsps = ip->trnsps;
+-	ix.fix_dur = ip->fix_dur;
+-	ix.modes = ip->modes;
+-	ix.fix_key = ip->fix_key;
+-	return 0;
+-}
+-
+-static int snd_seq_fm_get_size(void *private_data, struct snd_seq_kinstr *instr,
+-			       long *size)
+-{
+-	*size = sizeof(struct fm_xinstrument);
+-	return 0;
+-}
+-
+-int snd_seq_fm_init(struct snd_seq_kinstr_ops *ops,
+-		    struct snd_seq_kinstr_ops *next)
+-{
+-	memset(ops, 0, sizeof(*ops));
+-	// ops->private_data = private_data;
+-	ops->add_len = sizeof(struct fm_instrument);
+-	ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
+-	ops->put = snd_seq_fm_put;
+-	ops->get = snd_seq_fm_get;
+-	ops->get_size = snd_seq_fm_get_size;
+-	// ops->remove = snd_seq_fm_remove;
+-	// ops->notify = snd_seq_fm_notify;
+-	ops->next = next;
+-	return 0;
+-}
+-
+-/*
+- *  Init part
+- */
+-
+-static int __init alsa_ainstr_fm_init(void)
+-{
+-	return 0;
+-}
+-
+-static void __exit alsa_ainstr_fm_exit(void)
+-{
+-}
+-
+-module_init(alsa_ainstr_fm_init)
+-module_exit(alsa_ainstr_fm_exit)
+-
+-EXPORT_SYMBOL(snd_seq_fm_init);
+diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c
+deleted file mode 100644
+index 4940026..0000000
+--- a/sound/core/seq/instr/ainstr_gf1.c
++++ /dev/null
+@@ -1,359 +0,0 @@
+-/*
+- *   GF1 (GUS) Patch - Instrument routines
+- *   Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *   This program 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 program 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/slab.h>
+-#include <sound/core.h>
+-#include <sound/ainstr_gf1.h>
+-#include <sound/initval.h>
+-#include <asm/uaccess.h>
+-
+-MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>");
+-MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
+-MODULE_LICENSE("GPL");
+-
+-static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
+-{
+-	unsigned int result = size;
+-	
+-	if (format & GF1_WAVE_16BIT)
+-		result <<= 1;
+-	if (format & GF1_WAVE_STEREO)
+-		result <<= 1;
+-	return format;
+-}
+-
+-static int snd_seq_gf1_copy_wave_from_stream(struct snd_gf1_ops *ops,
+-					     struct gf1_instrument *ip,
+-					     char __user **data,
+-					     long *len,
+-					     int atomic)
+-{
+-	struct gf1_wave *wp, *prev;
+-	struct gf1_xwave xp;
+-	int err;
+-	gfp_t gfp_mask;
+-	unsigned int real_size;
+-	
+-	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+-	if (*len < (long)sizeof(xp))
+-		return -EINVAL;
+-	if (copy_from_user(&xp, *data, sizeof(xp)))
+-		return -EFAULT;
+-	*data += sizeof(xp);
+-	*len -= sizeof(xp);
+-	wp = kzalloc(sizeof(*wp), gfp_mask);
+-	if (wp == NULL)
+-		return -ENOMEM;
+-	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+-	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+-	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+-	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+-	wp->format = le32_to_cpu(xp.format);
+-	wp->size = le32_to_cpu(xp.size);
+-	wp->start = le32_to_cpu(xp.start);
+-	wp->loop_start = le32_to_cpu(xp.loop_start);
+-	wp->loop_end = le32_to_cpu(xp.loop_end);
+-	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+-	wp->flags = xp.flags;
+-	wp->sample_rate = le32_to_cpu(xp.sample_rate);
+-	wp->low_frequency = le32_to_cpu(xp.low_frequency);
+-	wp->high_frequency = le32_to_cpu(xp.high_frequency);
+-	wp->root_frequency = le32_to_cpu(xp.root_frequency);
+-	wp->tune = le16_to_cpu(xp.tune);
+-	wp->balance = xp.balance;
+-	memcpy(wp->envelope_rate, xp.envelope_rate, 6);
+-	memcpy(wp->envelope_offset, xp.envelope_offset, 6);
+-	wp->tremolo_sweep = xp.tremolo_sweep;
+-	wp->tremolo_rate = xp.tremolo_rate;
+-	wp->tremolo_depth = xp.tremolo_depth;
+-	wp->vibrato_sweep = xp.vibrato_sweep;
+-	wp->vibrato_rate = xp.vibrato_rate;
+-	wp->vibrato_depth = xp.vibrato_depth;
+-	wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
+-	wp->scale_factor = le16_to_cpu(xp.scale_factor);
+-	real_size = snd_seq_gf1_size(wp->size, wp->format);
+-	if ((long)real_size > *len) {
+-		kfree(wp);
+-		return -ENOMEM;
+-	}
+-	if (ops->put_sample) {
+-		err = ops->put_sample(ops->private_data, wp,
+-				      *data, real_size, atomic);
+-		if (err < 0) {
+-			kfree(wp);
+-			return err;
+-		}
+-	}
+-	*data += real_size;
+-	*len -= real_size;
+-	prev = ip->wave;
+-	if (prev) {
+-		while (prev->next) prev = prev->next;
+-		prev->next = wp;
+-	} else {
+-		ip->wave = wp;
+-	}
+-	return 0;
+-}
+-
+-static void snd_seq_gf1_wave_free(struct snd_gf1_ops *ops,
+-				  struct gf1_wave *wave,
+-				  int atomic)
+-{
+-	if (ops->remove_sample)
+-		ops->remove_sample(ops->private_data, wave, atomic);
+-	kfree(wave);
+-}
+-
+-static void snd_seq_gf1_instr_free(struct snd_gf1_ops *ops,
+-				   struct gf1_instrument *ip,
+-				   int atomic)
+-{
+-	struct gf1_wave *wave;
+-	
+-	while ((wave = ip->wave) != NULL) {
+-		ip->wave = wave->next;
+-		snd_seq_gf1_wave_free(ops, wave, atomic);
+-	}
+-}
+-
+-static int snd_seq_gf1_put(void *private_data, struct snd_seq_kinstr *instr,
+-			   char __user *instr_data, long len, int atomic,
+-			   int cmd)
+-{
+-	struct snd_gf1_ops *ops = private_data;
+-	struct gf1_instrument *ip;
+-	struct gf1_xinstrument ix;
+-	int err;
+-	gfp_t gfp_mask;
+-
+-	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+-		return -EINVAL;
+-	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+-	/* copy instrument data */
+-	if (len < (long)sizeof(ix))
+-		return -EINVAL;
+-	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+-		return -EFAULT;
+-	if (ix.stype != GF1_STRU_INSTR)
+-		return -EINVAL;
+-	instr_data += sizeof(ix);
+-	len -= sizeof(ix);
+-	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+-	ip->exclusion = le16_to_cpu(ix.exclusion);
+-	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+-	ip->effect1 = ix.effect1;
+-	ip->effect1_depth = ix.effect1_depth;
+-	ip->effect2 = ix.effect2;
+-	ip->effect2_depth = ix.effect2_depth;
+-	/* copy layers */
+-	while (len > (long)sizeof(__u32)) {
+-		__u32 stype;
+-
+-		if (copy_from_user(&stype, instr_data, sizeof(stype)))
+-			return -EFAULT;
+-		if (stype != GF1_STRU_WAVE) {
+-			snd_seq_gf1_instr_free(ops, ip, atomic);
+-			return -EINVAL;
+-		}
+-		err = snd_seq_gf1_copy_wave_from_stream(ops,
+-							ip,
+-							&instr_data,
+-							&len,
+-							atomic);
+-		if (err < 0) {
+-			snd_seq_gf1_instr_free(ops, ip, atomic);
+-			return err;
+-		}
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_gf1_copy_wave_to_stream(struct snd_gf1_ops *ops,
+-					   struct gf1_instrument *ip,
+-					   char __user **data,
+-					   long *len,
+-					   int atomic)
+-{
+-	struct gf1_wave *wp;
+-	struct gf1_xwave xp;
+-	int err;
+-	unsigned int real_size;
+-	
+-	for (wp = ip->wave; wp; wp = wp->next) {
+-		if (*len < (long)sizeof(xp))
+-			return -ENOMEM;
+-		memset(&xp, 0, sizeof(xp));
+-		xp.stype = GF1_STRU_WAVE;
+-		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+-		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+-		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+-		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+-		xp.format = cpu_to_le32(wp->format);
+-		xp.size = cpu_to_le32(wp->size);
+-		xp.start = cpu_to_le32(wp->start);
+-		xp.loop_start = cpu_to_le32(wp->loop_start);
+-		xp.loop_end = cpu_to_le32(wp->loop_end);
+-		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+-		xp.flags = wp->flags;
+-		xp.sample_rate = cpu_to_le32(wp->sample_rate);
+-		xp.low_frequency = cpu_to_le32(wp->low_frequency);
+-		xp.high_frequency = cpu_to_le32(wp->high_frequency);
+-		xp.root_frequency = cpu_to_le32(wp->root_frequency);
+-		xp.tune = cpu_to_le16(wp->tune);
+-		xp.balance = wp->balance;
+-		memcpy(xp.envelope_rate, wp->envelope_rate, 6);
+-		memcpy(xp.envelope_offset, wp->envelope_offset, 6);
+-		xp.tremolo_sweep = wp->tremolo_sweep;
+-		xp.tremolo_rate = wp->tremolo_rate;
+-		xp.tremolo_depth = wp->tremolo_depth;
+-		xp.vibrato_sweep = wp->vibrato_sweep;
+-		xp.vibrato_rate = wp->vibrato_rate;
+-		xp.vibrato_depth = wp->vibrato_depth;
+-		xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
+-		xp.scale_factor = cpu_to_le16(wp->scale_factor);
+-		if (copy_to_user(*data, &xp, sizeof(xp)))
+-			return -EFAULT;
+-		*data += sizeof(xp);
+-		*len -= sizeof(xp);
+-		real_size = snd_seq_gf1_size(wp->size, wp->format);
+-		if (*len < (long)real_size)
+-			return -ENOMEM;
+-		if (ops->get_sample) {
+-			err = ops->get_sample(ops->private_data, wp,
+-					      *data, real_size, atomic);
+-			if (err < 0)
+-				return err;
+-		}
+-		*data += wp->size;
+-		*len -= wp->size;
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_gf1_get(void *private_data, struct snd_seq_kinstr *instr,
+-			   char __user *instr_data, long len, int atomic,
+-			   int cmd)
+-{
+-	struct snd_gf1_ops *ops = private_data;
+-	struct gf1_instrument *ip;
+-	struct gf1_xinstrument ix;
+-	
+-	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+-		return -EINVAL;
+-	if (len < (long)sizeof(ix))
+-		return -ENOMEM;
+-	memset(&ix, 0, sizeof(ix));
+-	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+-	ix.stype = GF1_STRU_INSTR;
+-	ix.exclusion = cpu_to_le16(ip->exclusion);
+-	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+-	ix.effect1 = cpu_to_le16(ip->effect1);
+-	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+-	ix.effect2 = ip->effect2;
+-	ix.effect2_depth = ip->effect2_depth;
+-	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+-		return -EFAULT;
+-	instr_data += sizeof(ix);
+-	len -= sizeof(ix);
+-	return snd_seq_gf1_copy_wave_to_stream(ops,
+-					       ip,
+-					       &instr_data,
+-					       &len,
+-					       atomic);
+-}
+-
+-static int snd_seq_gf1_get_size(void *private_data, struct snd_seq_kinstr *instr,
+-				long *size)
+-{
+-	long result;
+-	struct gf1_instrument *ip;
+-	struct gf1_wave *wp;
+-
+-	*size = 0;
+-	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+-	result = sizeof(struct gf1_xinstrument);
+-	for (wp = ip->wave; wp; wp = wp->next) {
+-		result += sizeof(struct gf1_xwave);
+-		result += wp->size;
+-	}
+-	*size = result;
+-	return 0;
+-}
+-
+-static int snd_seq_gf1_remove(void *private_data,
+-			      struct snd_seq_kinstr *instr,
+-                              int atomic)
+-{
+-	struct snd_gf1_ops *ops = private_data;
+-	struct gf1_instrument *ip;
+-
+-	ip = (struct gf1_instrument *)KINSTR_DATA(instr);
+-	snd_seq_gf1_instr_free(ops, ip, atomic);
+-	return 0;
+-}
+-
+-static void snd_seq_gf1_notify(void *private_data,
+-			       struct snd_seq_kinstr *instr,
+-			       int what)
+-{
+-	struct snd_gf1_ops *ops = private_data;
+-
+-	if (ops->notify)
+-		ops->notify(ops->private_data, instr, what);
+-}
+-
+-int snd_seq_gf1_init(struct snd_gf1_ops *ops,
+-		     void *private_data,
+-		     struct snd_seq_kinstr_ops *next)
+-{
+-	memset(ops, 0, sizeof(*ops));
+-	ops->private_data = private_data;
+-	ops->kops.private_data = ops;
+-	ops->kops.add_len = sizeof(struct gf1_instrument);
+-	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
+-	ops->kops.put = snd_seq_gf1_put;
+-	ops->kops.get = snd_seq_gf1_get;
+-	ops->kops.get_size = snd_seq_gf1_get_size;
+-	ops->kops.remove = snd_seq_gf1_remove;
+-	ops->kops.notify = snd_seq_gf1_notify;
+-	ops->kops.next = next;
+-	return 0;
+-}
+-
+-/*
+- *  Init part
+- */
+-
+-static int __init alsa_ainstr_gf1_init(void)
+-{
+-	return 0;
+-}
+-
+-static void __exit alsa_ainstr_gf1_exit(void)
+-{
+-}
+-
+-module_init(alsa_ainstr_gf1_init)
+-module_exit(alsa_ainstr_gf1_exit)
+-
+-EXPORT_SYMBOL(snd_seq_gf1_init);
+diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c
+deleted file mode 100644
+index 6c40eb7..0000000
+--- a/sound/core/seq/instr/ainstr_iw.c
++++ /dev/null
+@@ -1,623 +0,0 @@
+-/*
+- *   IWFFFF - AMD InterWave (tm) - Instrument routines
+- *   Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *   This program 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 program 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/slab.h>
+-#include <sound/core.h>
+-#include <sound/ainstr_iw.h>
+-#include <sound/initval.h>
+-#include <asm/uaccess.h>
+-
+-MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>");
+-MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
+-MODULE_LICENSE("GPL");
+-
+-static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
+-{
+-	unsigned int result = size;
+-	
+-	if (format & IWFFFF_WAVE_16BIT)
+-		result <<= 1;
+-	if (format & IWFFFF_WAVE_STEREO)
+-		result <<= 1;
+-	return result;
+-}
+-
+-static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp,
+-						struct iwffff_xlfo *fx)
+-{
+-	fp->freq = le16_to_cpu(fx->freq);
+-	fp->depth = le16_to_cpu(fx->depth);
+-	fp->sweep = le16_to_cpu(fx->sweep);
+-	fp->shape = fx->shape;
+-	fp->delay = fx->delay;
+-}
+-
+-static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
+-					       struct iwffff_layer *lp,
+-					       struct iwffff_env *ep,
+-					       struct iwffff_xenv *ex,
+-					       char __user **data,
+-					       long *len,
+-					       gfp_t gfp_mask)
+-{
+-	__u32 stype;
+-	struct iwffff_env_record *rp, *rp_last;
+-	struct iwffff_xenv_record rx;
+-	struct iwffff_env_point *pp;
+-	struct iwffff_xenv_point px;
+-	int points_size, idx;
+-
+-	ep->flags = ex->flags;
+-	ep->mode = ex->mode;
+-	ep->index = ex->index;
+-	rp_last = NULL;
+-	while (1) {
+-		if (*len < (long)sizeof(__u32))
+-			return -EINVAL;
+-		if (copy_from_user(&stype, *data, sizeof(stype)))
+-			return -EFAULT;
+-		if (stype == IWFFFF_STRU_WAVE)
+-			return 0;
+-		if (req_stype != stype) {
+-			if (stype == IWFFFF_STRU_ENV_RECP ||
+-			    stype == IWFFFF_STRU_ENV_RECV)
+-				return 0;
+-		}
+-		if (*len < (long)sizeof(rx))
+-			return -EINVAL;
+-		if (copy_from_user(&rx, *data, sizeof(rx)))
+-			return -EFAULT;
+-		*data += sizeof(rx);
+-		*len -= sizeof(rx);
+-		points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
+-		if (points_size > *len)
+-			return -EINVAL;
+-		rp = kzalloc(sizeof(*rp) + points_size, gfp_mask);
+-		if (rp == NULL)
+-			return -ENOMEM;
+-		rp->nattack = le16_to_cpu(rx.nattack);
+-		rp->nrelease = le16_to_cpu(rx.nrelease);
+-		rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
+-		rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
+-		rp->release_rate = le16_to_cpu(rx.release_rate);
+-		rp->hirange = rx.hirange;
+-		pp = (struct iwffff_env_point *)(rp + 1);
+-		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+-			if (copy_from_user(&px, *data, sizeof(px)))
+-				return -EFAULT;
+-			*data += sizeof(px);
+-			*len -= sizeof(px);
+-			pp->offset = le16_to_cpu(px.offset);
+-			pp->rate = le16_to_cpu(px.rate);
+-		}
+-		if (ep->record == NULL) {
+-			ep->record = rp;
+-		} else {
+-			rp_last = rp;
+-		}
+-		rp_last = rp;
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops,
+-						struct iwffff_layer *lp,
+-					        char __user **data,
+-					        long *len,
+-					        int atomic)
+-{
+-	struct iwffff_wave *wp, *prev;
+-	struct iwffff_xwave xp;
+-	int err;
+-	gfp_t gfp_mask;
+-	unsigned int real_size;
+-	
+-	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+-	if (*len < (long)sizeof(xp))
+-		return -EINVAL;
+-	if (copy_from_user(&xp, *data, sizeof(xp)))
+-		return -EFAULT;
+-	*data += sizeof(xp);
+-	*len -= sizeof(xp);
+-	wp = kzalloc(sizeof(*wp), gfp_mask);
+-	if (wp == NULL)
+-		return -ENOMEM;
+-	wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
+-	wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
+-	wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
+-	wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
+-	wp->format = le32_to_cpu(xp.format);
+-	wp->address.memory = le32_to_cpu(xp.offset);
+-	wp->size = le32_to_cpu(xp.size);
+-	wp->start = le32_to_cpu(xp.start);
+-	wp->loop_start = le32_to_cpu(xp.loop_start);
+-	wp->loop_end = le32_to_cpu(xp.loop_end);
+-	wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
+-	wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
+-	wp->attenuation = xp.attenuation;
+-	wp->low_note = xp.low_note;
+-	wp->high_note = xp.high_note;
+-	real_size = snd_seq_iwffff_size(wp->size, wp->format);
+-	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+-		if ((long)real_size > *len) {
+-			kfree(wp);
+-			return -ENOMEM;
+-		}
+-	}
+-	if (ops->put_sample) {
+-		err = ops->put_sample(ops->private_data, wp,
+-				      *data, real_size, atomic);
+-		if (err < 0) {
+-			kfree(wp);
+-			return err;
+-		}
+-	}
+-	if (!(wp->format & IWFFFF_WAVE_ROM)) {
+-		*data += real_size;
+-		*len -= real_size;
+-	}
+-	prev = lp->wave;
+-	if (prev) {
+-		while (prev->next) prev = prev->next;
+-		prev->next = wp;
+-	} else {
+-		lp->wave = wp;
+-	}
+-	return 0;
+-}
+-
+-static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops,
+-				    struct iwffff_env *env,
+-				    int atomic)
+-{
+-	struct iwffff_env_record *rec;
+-	
+-	while ((rec = env->record) != NULL) {
+-		env->record = rec->next;
+-		kfree(rec);
+-	}
+-}
+-				    
+-static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops,
+-				     struct iwffff_wave *wave,
+-				     int atomic)
+-{
+-	if (ops->remove_sample)
+-		ops->remove_sample(ops->private_data, wave, atomic);
+-	kfree(wave);
+-}
+-
+-static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops,
+-                                      struct iwffff_instrument *ip,
+-                                      int atomic)
+-{
+-	struct iwffff_layer *layer;
+-	struct iwffff_wave *wave;
+-	
+-	while ((layer = ip->layer) != NULL) {
+-		ip->layer = layer->next;
+-		snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
+-		snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
+-		while ((wave = layer->wave) != NULL) {
+-			layer->wave = wave->next;
+-			snd_seq_iwffff_wave_free(ops, wave, atomic);
+-		}
+-		kfree(layer);
+-	}
+-}
+-
+-static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr,
+-			      char __user *instr_data, long len, int atomic,
+-			      int cmd)
+-{
+-	struct snd_iwffff_ops *ops = private_data;
+-	struct iwffff_instrument *ip;
+-	struct iwffff_xinstrument ix;
+-	struct iwffff_layer *lp, *prev_lp;
+-	struct iwffff_xlayer lx;
+-	int err;
+-	gfp_t gfp_mask;
+-
+-	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+-		return -EINVAL;
+-	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+-	/* copy instrument data */
+-	if (len < (long)sizeof(ix))
+-		return -EINVAL;
+-	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+-		return -EFAULT;
+-	if (ix.stype != IWFFFF_STRU_INSTR)
+-		return -EINVAL;
+-	instr_data += sizeof(ix);
+-	len -= sizeof(ix);
+-	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+-	ip->exclusion = le16_to_cpu(ix.exclusion);
+-	ip->layer_type = le16_to_cpu(ix.layer_type);
+-	ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
+-	ip->effect1 = ix.effect1;
+-	ip->effect1_depth = ix.effect1_depth;
+-	ip->effect2 = ix.effect2;
+-	ip->effect2_depth = ix.effect2_depth;
+-	/* copy layers */
+-	prev_lp = NULL;
+-	while (len > 0) {
+-		if (len < (long)sizeof(struct iwffff_xlayer)) {
+-			snd_seq_iwffff_instr_free(ops, ip, atomic);
+-			return -EINVAL;
+-		}
+-		if (copy_from_user(&lx, instr_data, sizeof(lx)))
+-			return -EFAULT;
+-		instr_data += sizeof(lx);
+-		len -= sizeof(lx);
+-		if (lx.stype != IWFFFF_STRU_LAYER) {
+-			snd_seq_iwffff_instr_free(ops, ip, atomic);
+-			return -EINVAL;
+-		}
+-		lp = kzalloc(sizeof(*lp), gfp_mask);
+-		if (lp == NULL) {
+-			snd_seq_iwffff_instr_free(ops, ip, atomic);
+-			return -ENOMEM;
+-		}
+-		if (prev_lp) {
+-			prev_lp->next = lp;
+-		} else {
+-			ip->layer = lp;
+-		}
+-		prev_lp = lp;
+-		lp->flags = lx.flags;
+-		lp->velocity_mode = lx.velocity_mode;
+-		lp->layer_event = lx.layer_event;
+-		lp->low_range = lx.low_range;
+-		lp->high_range = lx.high_range;
+-		lp->pan = lx.pan;
+-		lp->pan_freq_scale = lx.pan_freq_scale;
+-		lp->attenuation = lx.attenuation;
+-		snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
+-		snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
+-		lp->freq_scale = le16_to_cpu(lx.freq_scale);
+-		lp->freq_center = lx.freq_center;
+-		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
+-							  lp,
+-							  &lp->penv, &lx.penv,
+-						          &instr_data, &len,
+-						          gfp_mask);
+-		if (err < 0) {
+-			snd_seq_iwffff_instr_free(ops, ip, atomic);
+-			return err;
+-		}
+-		err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
+-							  lp,
+-							  &lp->venv, &lx.venv,
+-						          &instr_data, &len,
+-						          gfp_mask);
+-		if (err < 0) {
+-			snd_seq_iwffff_instr_free(ops, ip, atomic);
+-			return err;
+-		}
+-		while (len > (long)sizeof(__u32)) {
+-			__u32 stype;
+-
+-			if (copy_from_user(&stype, instr_data, sizeof(stype)))
+-				return -EFAULT;
+-			if (stype != IWFFFF_STRU_WAVE)
+-				break;
+-			err = snd_seq_iwffff_copy_wave_from_stream(ops,
+-								   lp,
+-							    	   &instr_data,
+-								   &len,
+-								   atomic);
+-			if (err < 0) {
+-				snd_seq_iwffff_instr_free(ops, ip, atomic);
+-				return err;
+-			}
+-		}
+-	}
+-	return 0;
+-}
+-
+-static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx,
+-					      struct iwffff_lfo *fp)
+-{
+-	fx->freq = cpu_to_le16(fp->freq);
+-	fx->depth = cpu_to_le16(fp->depth);
+-	fx->sweep = cpu_to_le16(fp->sweep);
+-	fp->shape = fx->shape;
+-	fp->delay = fx->delay;
+-}
+-
+-static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
+-					     struct iwffff_layer *lp,
+-					     struct iwffff_xenv *ex,
+-					     struct iwffff_env *ep,
+-					     char __user **data,
+-					     long *len)
+-{
+-	struct iwffff_env_record *rp;
+-	struct iwffff_xenv_record rx;
+-	struct iwffff_env_point *pp;
+-	struct iwffff_xenv_point px;
+-	int points_size, idx;
+-
+-	ex->flags = ep->flags;
+-	ex->mode = ep->mode;
+-	ex->index = ep->index;
+-	for (rp = ep->record; rp; rp = rp->next) {
+-		if (*len < (long)sizeof(rx))
+-			return -ENOMEM;
+-		memset(&rx, 0, sizeof(rx));
+-		rx.stype = req_stype;
+-		rx.nattack = cpu_to_le16(rp->nattack);
+-		rx.nrelease = cpu_to_le16(rp->nrelease);
+-		rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
+-		rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
+-		rx.release_rate = cpu_to_le16(rp->release_rate);
+-		rx.hirange = cpu_to_le16(rp->hirange);
+-		if (copy_to_user(*data, &rx, sizeof(rx)))
+-			return -EFAULT;
+-		*data += sizeof(rx);
+-		*len -= sizeof(rx);
+-		points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+-		if (*len < points_size)
+-			return -ENOMEM;
+-		pp = (struct iwffff_env_point *)(rp + 1);
+-		for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
+-			px.offset = cpu_to_le16(pp->offset);
+-			px.rate = cpu_to_le16(pp->rate);
+-			if (copy_to_user(*data, &px, sizeof(px)))
+-				return -EFAULT;
+-			*data += sizeof(px);
+-			*len -= sizeof(px);
+-		}
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops,
+-					      struct iwffff_layer *lp,
+-					      char __user **data,
+-					      long *len,
+-					      int atomic)
+-{
+-	struct iwffff_wave *wp;
+-	struct iwffff_xwave xp;
+-	int err;
+-	unsigned int real_size;
+-	
+-	for (wp = lp->wave; wp; wp = wp->next) {
+-		if (*len < (long)sizeof(xp))
+-			return -ENOMEM;
+-		memset(&xp, 0, sizeof(xp));
+-		xp.stype = IWFFFF_STRU_WAVE;
+-		xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
+-		xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
+-		xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
+-		xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
+-		xp.format = cpu_to_le32(wp->format);
+-		if (wp->format & IWFFFF_WAVE_ROM)
+-			xp.offset = cpu_to_le32(wp->address.memory);
+-		xp.size = cpu_to_le32(wp->size);
+-		xp.start = cpu_to_le32(wp->start);
+-		xp.loop_start = cpu_to_le32(wp->loop_start);
+-		xp.loop_end = cpu_to_le32(wp->loop_end);
+-		xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
+-		xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
+-		xp.attenuation = wp->attenuation;
+-		xp.low_note = wp->low_note;
+-		xp.high_note = wp->high_note;
+-		if (copy_to_user(*data, &xp, sizeof(xp)))
+-			return -EFAULT;
+-		*data += sizeof(xp);
+-		*len -= sizeof(xp);
+-		real_size = snd_seq_iwffff_size(wp->size, wp->format);
+-		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+-			if (*len < (long)real_size)
+-				return -ENOMEM;
+-		}
+-		if (ops->get_sample) {
+-			err = ops->get_sample(ops->private_data, wp,
+-					      *data, real_size, atomic);
+-			if (err < 0)
+-				return err;
+-		}
+-		if (!(wp->format & IWFFFF_WAVE_ROM)) {
+-			*data += real_size;
+-			*len -= real_size;
+-		}
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr,
+-			      char __user *instr_data, long len, int atomic, int cmd)
+-{
+-	struct snd_iwffff_ops *ops = private_data;
+-	struct iwffff_instrument *ip;
+-	struct iwffff_xinstrument ix;
+-	struct iwffff_layer *lp;
+-	struct iwffff_xlayer lx;
+-	char __user *layer_instr_data;
+-	int err;
+-	
+-	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+-		return -EINVAL;
+-	if (len < (long)sizeof(ix))
+-		return -ENOMEM;
+-	memset(&ix, 0, sizeof(ix));
+-	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+-	ix.stype = IWFFFF_STRU_INSTR;
+-	ix.exclusion = cpu_to_le16(ip->exclusion);
+-	ix.layer_type = cpu_to_le16(ip->layer_type);
+-	ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
+-	ix.effect1 = cpu_to_le16(ip->effect1);
+-	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+-	ix.effect2 = ip->effect2;
+-	ix.effect2_depth = ip->effect2_depth;
+-	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+-		return -EFAULT;
+-	instr_data += sizeof(ix);
+-	len -= sizeof(ix);
+-	for (lp = ip->layer; lp; lp = lp->next) {
+-		if (len < (long)sizeof(lx))
+-			return -ENOMEM;
+-		memset(&lx, 0, sizeof(lx));
+-		lx.stype = IWFFFF_STRU_LAYER;
+-		lx.flags = lp->flags;
+-		lx.velocity_mode = lp->velocity_mode;
+-		lx.layer_event = lp->layer_event;
+-		lx.low_range = lp->low_range;
+-		lx.high_range = lp->high_range;
+-		lx.pan = lp->pan;
+-		lx.pan_freq_scale = lp->pan_freq_scale;
+-		lx.attenuation = lp->attenuation;
+-		snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
+-		snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
+-		layer_instr_data = instr_data;
+-		instr_data += sizeof(lx);
+-		len -= sizeof(lx);
+-		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
+-							lp,
+-							&lx.penv, &lp->penv,
+-						        &instr_data, &len);
+-		if (err < 0)
+-			return err;
+-		err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
+-							lp,
+-							&lx.venv, &lp->venv,
+-						        &instr_data, &len);
+-		if (err < 0)
+-			return err;
+-		/* layer structure updating is now finished */
+-		if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
+-			return -EFAULT;
+-		err = snd_seq_iwffff_copy_wave_to_stream(ops,
+-							 lp,
+-							 &instr_data,
+-							 &len,
+-							 atomic);
+-		if (err < 0)
+-			return err;
+-	}
+-	return 0;
+-}
+-
+-static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep)
+-{
+-	long result = 0;
+-	struct iwffff_env_record *rp;
+-
+-	for (rp = ep->record; rp; rp = rp->next) {
+-		result += sizeof(struct iwffff_xenv_record);
+-		result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
+-	}
+-	return 0;
+-}
+-
+-static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp)
+-{
+-	long result = 0;
+-	struct iwffff_wave *wp;
+-	
+-	for (wp = lp->wave; wp; wp = wp->next) {
+-		result += sizeof(struct iwffff_xwave);
+-		if (!(wp->format & IWFFFF_WAVE_ROM))
+-			result += wp->size;
+-	}
+-	return result;
+-}
+-
+-static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr,
+-				   long *size)
+-{
+-	long result;
+-	struct iwffff_instrument *ip;
+-	struct iwffff_layer *lp;
+-
+-	*size = 0;
+-	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+-	result = sizeof(struct iwffff_xinstrument);
+-	for (lp = ip->layer; lp; lp = lp->next) {
+-		result += sizeof(struct iwffff_xlayer);
+-		result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
+-		result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
+-		result += snd_seq_iwffff_wave_size_in_stream(lp);
+-	}
+-	*size = result;
+-	return 0;
+-}
+-
+-static int snd_seq_iwffff_remove(void *private_data,
+-				 struct snd_seq_kinstr *instr,
+-                                 int atomic)
+-{
+-	struct snd_iwffff_ops *ops = private_data;
+-	struct iwffff_instrument *ip;
+-
+-	ip = (struct iwffff_instrument *)KINSTR_DATA(instr);
+-	snd_seq_iwffff_instr_free(ops, ip, atomic);
+-	return 0;
+-}
+-
+-static void snd_seq_iwffff_notify(void *private_data,
+-				  struct snd_seq_kinstr *instr,
+-                                  int what)
+-{
+-	struct snd_iwffff_ops *ops = private_data;
+-
+-	if (ops->notify)
+-		ops->notify(ops->private_data, instr, what);
+-}
+-
+-int snd_seq_iwffff_init(struct snd_iwffff_ops *ops,
+-			void *private_data,
+-			struct snd_seq_kinstr_ops *next)
+-{
+-	memset(ops, 0, sizeof(*ops));
+-	ops->private_data = private_data;
+-	ops->kops.private_data = ops;
+-	ops->kops.add_len = sizeof(struct iwffff_instrument);
+-	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
+-	ops->kops.put = snd_seq_iwffff_put;
+-	ops->kops.get = snd_seq_iwffff_get;
+-	ops->kops.get_size = snd_seq_iwffff_get_size;
+-	ops->kops.remove = snd_seq_iwffff_remove;
+-	ops->kops.notify = snd_seq_iwffff_notify;
+-	ops->kops.next = next;
+-	return 0;
+-}
+-
+-/*
+- *  Init part
+- */
+-
+-static int __init alsa_ainstr_iw_init(void)
+-{
+-	return 0;
+-}
+-
+-static void __exit alsa_ainstr_iw_exit(void)
+-{
+-}
+-
+-module_init(alsa_ainstr_iw_init)
+-module_exit(alsa_ainstr_iw_exit)
+-
+-EXPORT_SYMBOL(snd_seq_iwffff_init);
+diff --git a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c
+deleted file mode 100644
+index 78f68be..0000000
+--- a/sound/core/seq/instr/ainstr_simple.c
++++ /dev/null
+@@ -1,215 +0,0 @@
+-/*
+- *   Simple (MOD player) - Instrument routines
+- *   Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *   This program 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 program 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/slab.h>
+-#include <sound/core.h>
+-#include <sound/ainstr_simple.h>
+-#include <sound/initval.h>
+-#include <asm/uaccess.h>
+-
+-MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>");
+-MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
+-MODULE_LICENSE("GPL");
+-
+-static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
+-{
+-	unsigned int result = size;
+-	
+-	if (format & SIMPLE_WAVE_16BIT)
+-		result <<= 1;
+-	if (format & SIMPLE_WAVE_STEREO)
+-		result <<= 1;
+-	return result;
+-}
+-
+-static void snd_seq_simple_instr_free(struct snd_simple_ops *ops,
+-				      struct simple_instrument *ip,
+-				      int atomic)
+-{
+-	if (ops->remove_sample)
+-		ops->remove_sample(ops->private_data, ip, atomic);
+-}
+-
+-static int snd_seq_simple_put(void *private_data, struct snd_seq_kinstr *instr,
+-			      char __user *instr_data, long len,
+-			      int atomic, int cmd)
+-{
+-	struct snd_simple_ops *ops = private_data;
+-	struct simple_instrument *ip;
+-	struct simple_xinstrument ix;
+-	int err;
+-	gfp_t gfp_mask;
+-	unsigned int real_size;
+-
+-	if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
+-		return -EINVAL;
+-	gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
+-	/* copy instrument data */
+-	if (len < (long)sizeof(ix))
+-		return -EINVAL;
+-	if (copy_from_user(&ix, instr_data, sizeof(ix)))
+-		return -EFAULT;
+-	if (ix.stype != SIMPLE_STRU_INSTR)
+-		return -EINVAL;
+-	instr_data += sizeof(ix);
+-	len -= sizeof(ix);
+-	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+-	ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
+-	ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
+-	ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
+-	ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
+-	ip->format = le32_to_cpu(ix.format);
+-	ip->size = le32_to_cpu(ix.size);
+-	ip->start = le32_to_cpu(ix.start);
+-	ip->loop_start = le32_to_cpu(ix.loop_start);
+-	ip->loop_end = le32_to_cpu(ix.loop_end);
+-	ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
+-	ip->effect1 = ix.effect1;
+-	ip->effect1_depth = ix.effect1_depth;
+-	ip->effect2 = ix.effect2;
+-	ip->effect2_depth = ix.effect2_depth;
+-	real_size = snd_seq_simple_size(ip->size, ip->format);
+-	if (len < (long)real_size)
+-		return -EINVAL;
+-	if (ops->put_sample) {
+-		err = ops->put_sample(ops->private_data, ip,
+-				      instr_data, real_size, atomic);
+-		if (err < 0)
+-			return err;
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_simple_get(void *private_data, struct snd_seq_kinstr *instr,
+-			      char __user *instr_data, long len,
+-			      int atomic, int cmd)
+-{
+-	struct snd_simple_ops *ops = private_data;
+-	struct simple_instrument *ip;
+-	struct simple_xinstrument ix;
+-	int err;
+-	unsigned int real_size;
+-	
+-	if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
+-		return -EINVAL;
+-	if (len < (long)sizeof(ix))
+-		return -ENOMEM;
+-	memset(&ix, 0, sizeof(ix));
+-	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+-	ix.stype = SIMPLE_STRU_INSTR;
+-	ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
+-	ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
+-	ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
+-	ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
+-	ix.format = cpu_to_le32(ip->format);
+-	ix.size = cpu_to_le32(ip->size);
+-	ix.start = cpu_to_le32(ip->start);
+-	ix.loop_start = cpu_to_le32(ip->loop_start);
+-	ix.loop_end = cpu_to_le32(ip->loop_end);
+-	ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
+-	ix.effect1 = cpu_to_le16(ip->effect1);
+-	ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
+-	ix.effect2 = ip->effect2;
+-	ix.effect2_depth = ip->effect2_depth;
+-	if (copy_to_user(instr_data, &ix, sizeof(ix)))
+-		return -EFAULT;
+-	instr_data += sizeof(ix);
+-	len -= sizeof(ix);
+-	real_size = snd_seq_simple_size(ip->size, ip->format);
+-	if (len < (long)real_size)
+-		return -ENOMEM;
+-	if (ops->get_sample) {
+-		err = ops->get_sample(ops->private_data, ip,
+-				      instr_data, real_size, atomic);
+-		if (err < 0)
+-			return err;
+-	}
+-	return 0;
+-}
+-
+-static int snd_seq_simple_get_size(void *private_data, struct snd_seq_kinstr *instr,
+-				   long *size)
+-{
+-	struct simple_instrument *ip;
+-
+-	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+-	*size = sizeof(struct simple_xinstrument) + snd_seq_simple_size(ip->size, ip->format);
+-	return 0;
+-}
+-
+-static int snd_seq_simple_remove(void *private_data,
+-			         struct snd_seq_kinstr *instr,
+-                                 int atomic)
+-{
+-	struct snd_simple_ops *ops = private_data;
+-	struct simple_instrument *ip;
+-
+-	ip = (struct simple_instrument *)KINSTR_DATA(instr);
+-	snd_seq_simple_instr_free(ops, ip, atomic);
+-	return 0;
+-}
+-
+-static void snd_seq_simple_notify(void *private_data,
+-			          struct snd_seq_kinstr *instr,
+-                                  int what)
+-{
+-	struct snd_simple_ops *ops = private_data;
+-
+-	if (ops->notify)
+-		ops->notify(ops->private_data, instr, what);
+-}
+-
+-int snd_seq_simple_init(struct snd_simple_ops *ops,
+-		        void *private_data,
+-		        struct snd_seq_kinstr_ops *next)
+-{
+-	memset(ops, 0, sizeof(*ops));
+-	ops->private_data = private_data;
+-	ops->kops.private_data = ops;
+-	ops->kops.add_len = sizeof(struct simple_instrument);
+-	ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
+-	ops->kops.put = snd_seq_simple_put;
+-	ops->kops.get = snd_seq_simple_get;
+-	ops->kops.get_size = snd_seq_simple_get_size;
+-	ops->kops.remove = snd_seq_simple_remove;
+-	ops->kops.notify = snd_seq_simple_notify;
+-	ops->kops.next = next;
+-	return 0;
+-}
+-
+-/*
+- *  Init part
+- */
+-
+-static int __init alsa_ainstr_simple_init(void)
+-{
+-	return 0;
+-}
+-
+-static void __exit alsa_ainstr_simple_exit(void)
+-{
+-}
+-
+-module_init(alsa_ainstr_simple_init)
+-module_exit(alsa_ainstr_simple_exit)
+-
+-EXPORT_SYMBOL(snd_seq_simple_init);
+diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
+index bc09923..777796e 100644
+--- a/sound/core/seq/oss/seq_oss.c
++++ b/sound/core/seq/oss/seq_oss.c
+@@ -20,7 +20,6 @@
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/moduleparam.h>
+ #include <linux/mutex.h>
+diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
+index 9a8567c..bf8d2b4 100644
+--- a/sound/core/seq/oss/seq_oss_device.h
++++ b/sound/core/seq/oss/seq_oss_device.h
+@@ -21,7 +21,6 @@
+ #ifndef __SEQ_OSS_DEVICE_H
+ #define __SEQ_OSS_DEVICE_H
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/wait.h>
+ #include <linux/slab.h>
+diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
+index 1878208..ee0f840 100644
+--- a/sound/core/seq/seq.c
++++ b/sound/core/seq/seq.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/moduleparam.h>
+ #include <sound/core.h>
+diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
+index 2e3fa25..f97c1ba 100644
+--- a/sound/core/seq/seq_clientmgr.c
++++ b/sound/core/seq/seq_clientmgr.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+@@ -130,8 +129,6 @@ static struct snd_seq_client *clientptr(int clientid)
+ 	return clienttab[clientid];
+ }
+ 
+-extern int seq_client_load[];
+-
+ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
+ {
+ 	unsigned long flags;
+@@ -966,8 +963,7 @@ static int check_event_type_and_length(struct snd_seq_event *ev)
+ 			return -EINVAL;
+ 		break;
+ 	case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
+-		if (! snd_seq_ev_is_instr_type(ev) ||
+-		    ! snd_seq_ev_is_direct(ev))
++		if (! snd_seq_ev_is_direct(ev))
+ 			return -EINVAL;
+ 		break;
+ 	}
+diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
+index 5e04e20..20f0a72 100644
+--- a/sound/core/seq/seq_clientmgr.h
++++ b/sound/core/seq/seq_clientmgr.h
+@@ -98,4 +98,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
+ int snd_seq_client_notify_subscription(int client, int port,
+ 				       struct snd_seq_port_subscribe *info, int evtype);
+ 
++extern int seq_client_load[15];
++
+ #endif
+diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
+index 37852cd..155dc7d 100644
+--- a/sound/core/seq/seq_device.c
++++ b/sound/core/seq/seq_device.c
+@@ -36,7 +36,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+ #include <sound/info.h>
+diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
+index e55488d..f3bdc54 100644
+--- a/sound/core/seq/seq_dummy.c
++++ b/sound/core/seq/seq_dummy.c
+@@ -18,7 +18,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/moduleparam.h>
+diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
+index 6b055ae..3a94ed0 100644
+--- a/sound/core/seq/seq_fifo.c
++++ b/sound/core/seq/seq_fifo.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <linux/slab.h>
+ #include "seq_fifo.h"
+diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
+index 8a7fe5c..201f810 100644
+--- a/sound/core/seq/seq_info.c
++++ b/sound/core/seq/seq_info.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+ 
+diff --git a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c
+deleted file mode 100644
+index 9a6fd56..0000000
+--- a/sound/core/seq/seq_instr.c
++++ /dev/null
+@@ -1,655 +0,0 @@
+-/*
+- *   Generic Instrument routines for ALSA sequencer
+- *   Copyright (c) 1999 by Jaroslav Kysela <perex at perex.cz>
+- *
+- *   This program 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 program 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/slab.h>
+-#include <sound/core.h>
+-#include "seq_clientmgr.h"
+-#include <sound/seq_instr.h>
+-#include <sound/initval.h>
+-
+-MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>");
+-MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
+-MODULE_LICENSE("GPL");
+-
+-
+-static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list)
+-{
+-	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+-		spin_lock_irqsave(&list->ops_lock, list->ops_flags);
+-	} else {
+-		mutex_lock(&list->ops_mutex);
+-	}
+-}
+-
+-static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list)
+-{
+-	if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
+-		spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
+-	} else {
+-		mutex_unlock(&list->ops_mutex);
+-	}
+-}
+-
+-static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic)
+-{
+-	struct snd_seq_kinstr *instr;
+-	
+-	instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
+-	if (instr == NULL)
+-		return NULL;
+-	instr->add_len = add_len;
+-	return instr;
+-}
+-
+-static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic)
+-{
+-	int result = 0;
+-
+-	if (instr == NULL)
+-		return -EINVAL;
+-	if (instr->ops && instr->ops->remove)
+-		result = instr->ops->remove(instr->ops->private_data, instr, 1);
+-	if (!result)
+-		kfree(instr);
+-	return result;
+-}
+-
+-struct snd_seq_kinstr_list *snd_seq_instr_list_new(void)
+-{
+-	struct snd_seq_kinstr_list *list;
+-
+-	list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL);
+-	if (list == NULL)
+-		return NULL;
+-	spin_lock_init(&list->lock);
+-	spin_lock_init(&list->ops_lock);
+-	mutex_init(&list->ops_mutex);
+-	list->owner = -1;
+-	return list;
+-}
+-
+-void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
+-{
+-	struct snd_seq_kinstr_list *list;
+-	struct snd_seq_kinstr *instr;
+-	struct snd_seq_kcluster *cluster;
+-	int idx;
+-	unsigned long flags;
+-
+-	if (list_ptr == NULL)
+-		return;
+-	list = *list_ptr;
+-	*list_ptr = NULL;
+-	if (list == NULL)
+-		return;
+-	
+-	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {		
+-		while ((instr = list->hash[idx]) != NULL) {
+-			list->hash[idx] = instr->next;
+-			list->count--;
+-			spin_lock_irqsave(&list->lock, flags);
+-			while (instr->use) {
+-				spin_unlock_irqrestore(&list->lock, flags);
+-				schedule_timeout_uninterruptible(1);
+-				spin_lock_irqsave(&list->lock, flags);
+-			}				
+-			spin_unlock_irqrestore(&list->lock, flags);
+-			if (snd_seq_instr_free(instr, 0)<0)
+-				snd_printk(KERN_WARNING "instrument free problem\n");
+-		}
+-		while ((cluster = list->chash[idx]) != NULL) {
+-			list->chash[idx] = cluster->next;
+-			list->ccount--;
+-			kfree(cluster);
+-		}
+-	}
+-	kfree(list);
+-}
+-
+-static int instr_free_compare(struct snd_seq_kinstr *instr,
+-			      struct snd_seq_instr_header *ifree,
+-			      unsigned int client)
+-{
+-	switch (ifree->cmd) {
+-	case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
+-		/* all, except private for other clients */
+-		if ((instr->instr.std & 0xff000000) == 0)
+-			return 0;
+-		if (((instr->instr.std >> 24) & 0xff) == client)
+-			return 0;
+-		return 1;
+-	case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
+-		/* all my private instruments */
+-		if ((instr->instr.std & 0xff000000) == 0)
+-			return 1;
+-		if (((instr->instr.std >> 24) & 0xff) == client)
+-			return 0;
+-		return 1;
+-	case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
+-		/* all my private instruments */
+-		if ((instr->instr.std & 0xff000000) == 0) {
+-			if (instr->instr.cluster == ifree->id.cluster)
+-				return 0;
+-			return 1;
+-		}
+-		if (((instr->instr.std >> 24) & 0xff) == client) {
+-			if (instr->instr.cluster == ifree->id.cluster)
+-				return 0;
+-		}
+-		return 1;
+-	}
+-	return 1;
+-}
+-
+-int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
+-			         struct snd_seq_instr_header *ifree,
+-			         int client,
+-			         int atomic)
+-{
+-	struct snd_seq_kinstr *instr, *prev, *next, *flist;
+-	int idx;
+-	unsigned long flags;
+-
+-	snd_instr_lock_ops(list);
+-	for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
+-		spin_lock_irqsave(&list->lock, flags);
+-		instr = list->hash[idx];
+-		prev = flist = NULL;
+-		while (instr) {
+-			while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
+-				prev = instr;
+-				instr = instr->next;
+-			}
+-			if (instr == NULL)
+-				continue;
+-			if (instr->ops && instr->ops->notify)
+-				instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+-			next = instr->next;
+-			if (prev == NULL) {
+-				list->hash[idx] = next;
+-			} else {
+-				prev->next = next;
+-			}
+-			list->count--;
+-			instr->next = flist;
+-			flist = instr;
+-			instr = next;
+-		}
+-		spin_unlock_irqrestore(&list->lock, flags);
+-		while (flist) {
+-			instr = flist;
+-			flist = instr->next;
+-			while (instr->use) {
+-				schedule_timeout_uninterruptible(1);
+-				barrier();
+-			}
+-			if (snd_seq_instr_free(instr, atomic)<0)
+-				snd_printk(KERN_WARNING "instrument free problem\n");
+-			instr = next;
+-		}
+-	}
+-	snd_instr_unlock_ops(list);
+-	return 0;	
+-}
+-
+-static int compute_hash_instr_key(struct snd_seq_instr *instr)
+-{
+-	int result;
+-	
+-	result = instr->bank | (instr->prg << 16);
+-	result += result >> 24;
+-	result += result >> 16;
+-	result += result >> 8;
+-	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+-}
+-
+-#if 0
+-static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
+-{
+-	int result;
+-	
+-	result = cluster;
+-	result += result >> 24;
+-	result += result >> 16;
+-	result += result >> 8;
+-	return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
+-}
+-#endif
+-
+-static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact)
+-{
+-	if (exact) {
+-		if (i1->cluster != i2->cluster ||
+-		    i1->bank != i2->bank ||
+-		    i1->prg != i2->prg)
+-			return 1;
+-		if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
+-			return 1;
+-		if (!(i1->std & i2->std))
+-			return 1;
+-		return 0;
+-	} else {
+-		unsigned int client_check;
+-		
+-		if (i2->cluster && i1->cluster != i2->cluster)
+-			return 1;
+-		client_check = i2->std & 0xff000000;
+-		if (client_check) {
+-			if ((i1->std & 0xff000000) != client_check)
+-				return 1;
+-		} else {
+-			if ((i1->std & i2->std) != i2->std)
+-				return 1;
+-		}
+-		return i1->bank != i2->bank || i1->prg != i2->prg;
+-	}
+-}
+-
+-struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list,
+-					  struct snd_seq_instr *instr,
+-					  int exact,
+-					  int follow_alias)
+-{
+-	unsigned long flags;
+-	int depth = 0;
+-	struct snd_seq_kinstr *result;
+-
+-	if (list == NULL || instr == NULL)
+-		return NULL;
+-	spin_lock_irqsave(&list->lock, flags);
+-      __again:
+-	result = list->hash[compute_hash_instr_key(instr)];
+-	while (result) {
+-		if (!compare_instr(&result->instr, instr, exact)) {
+-			if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
+-				instr = (struct snd_seq_instr *)KINSTR_DATA(result);
+-				if (++depth > 10)
+-					goto __not_found;
+-				goto __again;
+-			}
+-			result->use++;
+-			spin_unlock_irqrestore(&list->lock, flags);
+-			return result;
+-		}
+-		result = result->next;
+-	}
+-      __not_found:
+-	spin_unlock_irqrestore(&list->lock, flags);
+-	return NULL;
+-}
+-
+-void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list,
+-			    struct snd_seq_kinstr *instr)
+-{
+-	unsigned long flags;
+-
+-	if (list == NULL || instr == NULL)
+-		return;
+-	spin_lock_irqsave(&list->lock, flags);
+-	if (instr->use <= 0) {
+-		snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
+-	} else {
+-		instr->use--;
+-	}
+-	spin_unlock_irqrestore(&list->lock, flags);
+-}
+-
+-static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops,
+-					    char *instr_type)
+-{
+-	while (ops) {
+-		if (!strcmp(ops->instr_type, instr_type))
+-			return ops;
+-		ops = ops->next;
+-	}
+-	return NULL;
+-}
+-
+-static int instr_result(struct snd_seq_event *ev,
+-			int type, int result,
+-			int atomic)
+-{
+-	struct snd_seq_event sev;
+-	
+-	memset(&sev, 0, sizeof(sev));
+-	sev.type = SNDRV_SEQ_EVENT_RESULT;
+-	sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
+-	            SNDRV_SEQ_PRIORITY_NORMAL;
+-	sev.source = ev->dest;
+-	sev.dest = ev->source;
+-	sev.data.result.event = type;
+-	sev.data.result.result = result;
+-#if 0
+-	printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
+-				type, result,
+-				sev.queue,
+-				sev.source.client, sev.source.port,
+-				sev.dest.client, sev.dest.port);
+-#endif
+-	return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
+-}
+-
+-static int instr_begin(struct snd_seq_kinstr_ops *ops,
+-		       struct snd_seq_kinstr_list *list,
+-		       struct snd_seq_event *ev,
+-		       int atomic, int hop)
+-{
+-	unsigned long flags;
+-
+-	spin_lock_irqsave(&list->lock, flags);
+-	if (list->owner >= 0 && list->owner != ev->source.client) {
+-		spin_unlock_irqrestore(&list->lock, flags);
+-		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
+-	}
+-	list->owner = ev->source.client;
+-	spin_unlock_irqrestore(&list->lock, flags);
+-	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
+-}
+-
+-static int instr_end(struct snd_seq_kinstr_ops *ops,
+-		     struct snd_seq_kinstr_list *list,
+-		     struct snd_seq_event *ev,
+-		     int atomic, int hop)
+-{
+-	unsigned long flags;
+-
+-	/* TODO: timeout handling */
+-	spin_lock_irqsave(&list->lock, flags);
+-	if (list->owner == ev->source.client) {
+-		list->owner = -1;
+-		spin_unlock_irqrestore(&list->lock, flags);
+-		return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
+-	}
+-	spin_unlock_irqrestore(&list->lock, flags);
+-	return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
+-}
+-
+-static int instr_info(struct snd_seq_kinstr_ops *ops,
+-		      struct snd_seq_kinstr_list *list,
+-		      struct snd_seq_event *ev,
+-		      int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-static int instr_format_info(struct snd_seq_kinstr_ops *ops,
+-			     struct snd_seq_kinstr_list *list,
+-			     struct snd_seq_event *ev,
+-			     int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-static int instr_reset(struct snd_seq_kinstr_ops *ops,
+-		       struct snd_seq_kinstr_list *list,
+-		       struct snd_seq_event *ev,
+-		       int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-static int instr_status(struct snd_seq_kinstr_ops *ops,
+-			struct snd_seq_kinstr_list *list,
+-			struct snd_seq_event *ev,
+-			int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-static int instr_put(struct snd_seq_kinstr_ops *ops,
+-		     struct snd_seq_kinstr_list *list,
+-		     struct snd_seq_event *ev,
+-		     int atomic, int hop)
+-{
+-	unsigned long flags;
+-	struct snd_seq_instr_header put;
+-	struct snd_seq_kinstr *instr;
+-	int result = -EINVAL, len, key;
+-
+-	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+-		goto __return;
+-
+-	if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
+-		goto __return;
+-	if (copy_from_user(&put, (void __user *)ev->data.ext.ptr,
+-			   sizeof(struct snd_seq_instr_header))) {
+-		result = -EFAULT;
+-		goto __return;
+-	}
+-	snd_instr_lock_ops(list);
+-	if (put.id.instr.std & 0xff000000) {	/* private instrument */
+-		put.id.instr.std &= 0x00ffffff;
+-		put.id.instr.std |= (unsigned int)ev->source.client << 24;
+-	}
+-	if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
+-		snd_seq_instr_free_use(list, instr);
+-		snd_instr_unlock_ops(list);
+-		result = -EBUSY;
+-		goto __return;
+-	}
+-	ops = instr_ops(ops, put.data.data.format);
+-	if (ops == NULL) {
+-		snd_instr_unlock_ops(list);
+-		goto __return;
+-	}
+-	len = ops->add_len;
+-	if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
+-		len = sizeof(struct snd_seq_instr);
+-	instr = snd_seq_instr_new(len, atomic);
+-	if (instr == NULL) {
+-		snd_instr_unlock_ops(list);
+-		result = -ENOMEM;
+-		goto __return;
+-	}
+-	instr->ops = ops;
+-	instr->instr = put.id.instr;
+-	strlcpy(instr->name, put.data.name, sizeof(instr->name));
+-	instr->type = put.data.type;
+-	if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
+-		result = ops->put(ops->private_data,
+-				  instr,
+-				  (void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header),
+-				  ev->data.ext.len - sizeof(struct snd_seq_instr_header),
+-				  atomic,
+-				  put.cmd);
+-		if (result < 0) {
+-			snd_seq_instr_free(instr, atomic);
+-			snd_instr_unlock_ops(list);
+-			goto __return;
+-		}
+-	}
+-	key = compute_hash_instr_key(&instr->instr);
+-	spin_lock_irqsave(&list->lock, flags);
+-	instr->next = list->hash[key];
+-	list->hash[key] = instr;
+-	list->count++;
+-	spin_unlock_irqrestore(&list->lock, flags);
+-	snd_instr_unlock_ops(list);
+-	result = 0;
+-      __return:
+-	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
+-	return result;
+-}
+-
+-static int instr_get(struct snd_seq_kinstr_ops *ops,
+-		     struct snd_seq_kinstr_list *list,
+-		     struct snd_seq_event *ev,
+-		     int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-static int instr_free(struct snd_seq_kinstr_ops *ops,
+-		      struct snd_seq_kinstr_list *list,
+-		      struct snd_seq_event *ev,
+-		      int atomic, int hop)
+-{
+-	struct snd_seq_instr_header ifree;
+-	struct snd_seq_kinstr *instr, *prev;
+-	int result = -EINVAL;
+-	unsigned long flags;
+-	unsigned int hash;
+-
+-	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
+-		goto __return;
+-
+-	if (ev->data.ext.len < sizeof(struct snd_seq_instr_header))
+-		goto __return;
+-	if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr,
+-			   sizeof(struct snd_seq_instr_header))) {
+-		result = -EFAULT;
+-		goto __return;
+-	}
+-	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
+-	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
+-	    ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
+-	    	result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
+-	    	goto __return;
+-	}
+-	if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
+-		if (ifree.id.instr.std & 0xff000000) {
+-			ifree.id.instr.std &= 0x00ffffff;
+-			ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
+-		}
+-		hash = compute_hash_instr_key(&ifree.id.instr);
+-		snd_instr_lock_ops(list);
+-		spin_lock_irqsave(&list->lock, flags);
+-		instr = list->hash[hash];
+-		prev = NULL;
+-		while (instr) {
+-			if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
+-				goto __free_single;
+-			prev = instr;
+-			instr = instr->next;
+-		}
+-		result = -ENOENT;
+-		spin_unlock_irqrestore(&list->lock, flags);
+-		snd_instr_unlock_ops(list);
+-		goto __return;
+-		
+-	      __free_single:
+-		if (prev) {
+-			prev->next = instr->next;
+-		} else {
+-			list->hash[hash] = instr->next;
+-		}
+-		if (instr->ops && instr->ops->notify)
+-			instr->ops->notify(instr->ops->private_data, instr,
+-					   SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+-		while (instr->use) {
+-			spin_unlock_irqrestore(&list->lock, flags);
+-			schedule_timeout_uninterruptible(1);
+-			spin_lock_irqsave(&list->lock, flags);
+-		}				
+-		spin_unlock_irqrestore(&list->lock, flags);
+-		result = snd_seq_instr_free(instr, atomic);
+-		snd_instr_unlock_ops(list);
+-		goto __return;
+-	}
+-
+-      __return:
+-	instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
+-	return result;
+-}
+-
+-static int instr_list(struct snd_seq_kinstr_ops *ops,
+-		      struct snd_seq_kinstr_list *list,
+-		      struct snd_seq_event *ev,
+-		      int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-static int instr_cluster(struct snd_seq_kinstr_ops *ops,
+-			 struct snd_seq_kinstr_list *list,
+-			 struct snd_seq_event *ev,
+-			 int atomic, int hop)
+-{
+-	return -ENXIO;
+-}
+-
+-int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops,
+-			struct snd_seq_kinstr_list *list,
+-			struct snd_seq_event *ev,
+-			int client,
+-			int atomic,
+-			int hop)
+-{
+-	int direct = 0;
+-
+-	snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
+-	if (snd_seq_ev_is_direct(ev)) {
+-		direct = 1;
+-		switch (ev->type) {
+-		case SNDRV_SEQ_EVENT_INSTR_BEGIN:
+-			return instr_begin(ops, list, ev, atomic, hop);
+-		case SNDRV_SEQ_EVENT_INSTR_END:
+-			return instr_end(ops, list, ev, atomic, hop);
+-		}
+-	}
+-	if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
+-		return -EINVAL;
+-	switch (ev->type) {
+-	case SNDRV_SEQ_EVENT_INSTR_INFO:
+-		return instr_info(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_FINFO:
+-		return instr_format_info(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_RESET:
+-		return instr_reset(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_STATUS:
+-		return instr_status(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_PUT:
+-		return instr_put(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_GET:
+-		return instr_get(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_FREE:
+-		return instr_free(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_LIST:
+-		return instr_list(ops, list, ev, atomic, hop);
+-	case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
+-		return instr_cluster(ops, list, ev, atomic, hop);
+-	}
+-	return -EINVAL;
+-}
+-			
+-/*
+- *  Init part
+- */
+-
+-static int __init alsa_seq_instr_init(void)
+-{
+-	return 0;
+-}
+-
+-static void __exit alsa_seq_instr_exit(void)
+-{
+-}
+-
+-module_init(alsa_seq_instr_init)
+-module_exit(alsa_seq_instr_exit)
+-
+-EXPORT_SYMBOL(snd_seq_instr_list_new);
+-EXPORT_SYMBOL(snd_seq_instr_list_free);
+-EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
+-EXPORT_SYMBOL(snd_seq_instr_find);
+-EXPORT_SYMBOL(snd_seq_instr_free_use);
+-EXPORT_SYMBOL(snd_seq_instr_event);
+diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
+index 1a34941..54f921e 100644
+--- a/sound/core/seq/seq_lock.c
++++ b/sound/core/seq/seq_lock.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include "seq_lock.h"
+ 
+diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
+index a72a194..0cf6ac4 100644
+--- a/sound/core/seq/seq_memory.c
++++ b/sound/core/seq/seq_memory.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
+index 5929aaf..99b3536 100644
+--- a/sound/core/seq/seq_midi.c
++++ b/sound/core/seq/seq_midi.c
+@@ -26,7 +26,6 @@ Possible options for midisynth module:
+ */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/errno.h>
+diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
+index 17b3e6f..07c6631 100644
+--- a/sound/core/seq/seq_midi_emul.c
++++ b/sound/core/seq/seq_midi_emul.c
+@@ -29,7 +29,6 @@
+  * code in here.  If there is it should be reported as a bug.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+@@ -229,13 +228,6 @@ snd_midi_process_event(struct snd_midi_op *ops,
+ 	case SNDRV_SEQ_EVENT_PORT_START:
+ 	case SNDRV_SEQ_EVENT_PORT_EXIT:
+ 	case SNDRV_SEQ_EVENT_PORT_CHANGE:
+-	case SNDRV_SEQ_EVENT_SAMPLE:
+-	case SNDRV_SEQ_EVENT_SAMPLE_START:
+-	case SNDRV_SEQ_EVENT_SAMPLE_STOP:
+-	case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
+-	case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
+-	case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
+-	case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
+ 	case SNDRV_SEQ_EVENT_ECHO:
+ 	not_yet:
+ 	default:
+diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
+index b6820a5..8284f17 100644
+--- a/sound/core/seq/seq_midi_event.c
++++ b/sound/core/seq/seq_midi_event.c
+@@ -19,7 +19,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
+index b6e23ad..1c32a53 100644
+--- a/sound/core/seq/seq_ports.c
++++ b/sound/core/seq/seq_ports.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <linux/slab.h>
+ #include "seq_system.h"
+diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
+index 0744186..85969db 100644
+--- a/sound/core/seq/seq_prioq.c
++++ b/sound/core/seq/seq_prioq.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
+index 9b87bb0..4a48c6e 100644
+--- a/sound/core/seq/seq_queue.c
++++ b/sound/core/seq/seq_queue.c
+@@ -35,7 +35,6 @@
+  *     - Addition of experimental sync support.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
+index b201b76..77884e6 100644
+--- a/sound/core/seq/seq_system.c
++++ b/sound/core/seq/seq_system.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+ #include "seq_system.h"
+diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
+index 8716352..d8fcd62 100644
+--- a/sound/core/seq/seq_timer.c
++++ b/sound/core/seq/seq_timer.c
+@@ -20,20 +20,12 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <linux/slab.h>
+ #include "seq_timer.h"
+ #include "seq_queue.h"
+ #include "seq_info.h"
+ 
+-extern int seq_default_timer_class;
+-extern int seq_default_timer_sclass;
+-extern int seq_default_timer_card;
+-extern int seq_default_timer_device;
+-extern int seq_default_timer_subdevice;
+-extern int seq_default_timer_resolution;
+-
+ /* allowed sequencer timer frequencies, in Hz */
+ #define MIN_FREQUENCY		10
+ #define MAX_FREQUENCY		6250
+diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
+index e9ee154..88dfb71 100644
+--- a/sound/core/seq/seq_timer.h
++++ b/sound/core/seq/seq_timer.h
+@@ -138,4 +138,11 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigne
+ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
+ snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
+ 
++extern int seq_default_timer_class;
++extern int seq_default_timer_sclass;
++extern int seq_default_timer_card;
++extern int seq_default_timer_device;
++extern int seq_default_timer_subdevice;
++extern int seq_default_timer_resolution;
++
+ #endif
+diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
+index e11790f..86e7739 100644
+--- a/sound/core/seq/seq_virmidi.c
++++ b/sound/core/seq/seq_virmidi.c
+@@ -35,7 +35,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/wait.h>
+ #include <linux/slab.h>
+diff --git a/sound/core/sound.c b/sound/core/sound.c
+index 7b486c4..00cca4d 100644
+--- a/sound/core/sound.c
++++ b/sound/core/sound.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
+index dc73313..7be5154 100644
+--- a/sound/core/sound_oss.c
++++ b/sound/core/sound_oss.c
+@@ -19,8 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+-
+ #ifdef CONFIG_SND_OSSEMUL
+ 
+ #if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
+diff --git a/sound/core/timer.c b/sound/core/timer.c
+index e7dc56c..aece465 100644
+--- a/sound/core/timer.c
++++ b/sound/core/timer.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+@@ -44,11 +43,14 @@
+ #endif
+ 
+ static int timer_limit = DEFAULT_TIMER_LIMIT;
++static int timer_tstamp_monotonic = 1;
+ MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>, Takashi Iwai <tiwai at suse.de>");
+ MODULE_DESCRIPTION("ALSA timer interface");
+ MODULE_LICENSE("GPL");
+ module_param(timer_limit, int, 0444);
+ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
++module_param(timer_tstamp_monotonic, int, 0444);
++MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
+ 
+ struct snd_timer_user {
+ 	struct snd_timer_instance *timeri;
+@@ -381,7 +383,10 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
+ 	struct snd_timer_instance *ts;
+ 	struct timespec tstamp;
+ 
+-	getnstimeofday(&tstamp);
++	if (timer_tstamp_monotonic)
++		do_posix_clock_monotonic_gettime(&tstamp);
++	else
++		getnstimeofday(&tstamp);
+ 	snd_assert(event >= SNDRV_TIMER_EVENT_START &&
+ 		   event <= SNDRV_TIMER_EVENT_PAUSE, return);
+ 	if (event == SNDRV_TIMER_EVENT_START ||
+@@ -1182,8 +1187,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
+ 		spin_unlock(&tu->qlock);
+ 		return;
+ 	}
+-	if (tu->last_resolution != resolution || ticks > 0)
+-		getnstimeofday(&tstamp);
++	if (tu->last_resolution != resolution || ticks > 0) {
++		if (timer_tstamp_monotonic)
++			do_posix_clock_monotonic_gettime(&tstamp);
++		else
++			getnstimeofday(&tstamp);
++	}
+ 	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
+ 	    tu->last_resolution != resolution) {
+ 		r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
+diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
+index 83529b0..75d4fe0 100644
+--- a/sound/drivers/Kconfig
++++ b/sound/drivers/Kconfig
+@@ -120,4 +120,16 @@ config SND_PORTMAN2X4
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called snd-portman2x4.
+ 
++config SND_ML403_AC97CR
++	tristate "Xilinx ML403 AC97 Controller Reference"
++	depends on SND && XILINX_VIRTEX
++	select SND_AC97_CODEC
++	help
++	  Say Y here to include support for the
++	  opb_ac97_controller_ref_v1_00_a ip core found in Xilinx' ML403
++	  reference design.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called snd-ml403_ac97cr.
++
+ endmenu
+diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
+index 80aeff5..8e55300 100644
+--- a/sound/drivers/Makefile
++++ b/sound/drivers/Makefile
+@@ -9,6 +9,7 @@ snd-mts64-objs := mts64.o
+ snd-portman2x4-objs := portman2x4.o
+ snd-serial-u16550-objs := serial-u16550.o
+ snd-virmidi-objs := virmidi.o
++snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
+ 
+ # Toplevel Module Dependency
+ obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
+@@ -17,5 +18,6 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
+ obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
+ obj-$(CONFIG_SND_MTS64) += snd-mts64.o
+ obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
++obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
+ 
+ obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
+diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
+index e008f3c..a240eae 100644
+--- a/sound/drivers/dummy.c
++++ b/sound/drivers/dummy.c
+@@ -18,7 +18,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/platform_device.h>
+diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
+new file mode 100644
+index 0000000..05a871a
+--- /dev/null
++++ b/sound/drivers/ml403-ac97cr.c
+@@ -0,0 +1,1352 @@
++/*
++ * ALSA driver for Xilinx ML403 AC97 Controller Reference
++ *   IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i)
++ *   IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i)
++ *
++ *  Copyright (c) by 2007  Joachim Foerster <JOFT at gmx.de>
++ *
++ *   This program 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 program 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
++ *
++ */
++
++/* Some notes / status of this driver:
++ *
++ * - Don't wonder about some strange implementations of things - especially the
++ * (heavy) shadowing of codec registers, with which I tried to reduce read
++ * accesses to a minimum, because after a variable amount of accesses, the AC97
++ * controller doesn't raise the register access finished bit anymore ...
++ *
++ * - Playback support seems to be pretty stable - no issues here.
++ * - Capture support "works" now, too. Overruns don't happen any longer so often.
++ *   But there might still be some ...
++ */
++
++#include <linux/init.h>
++#include <linux/moduleparam.h>
++
++#include <linux/platform_device.h>
++
++#include <linux/ioport.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++
++/* HZ */
++#include <linux/param.h>
++/* jiffies, time_*() */
++#include <linux/jiffies.h>
++/* schedule_timeout*() */
++#include <linux/sched.h>
++/* spin_lock*() */
++#include <linux/spinlock.h>
++/* struct mutex, mutex_init(), mutex_*lock() */
++#include <linux/mutex.h>
++
++/* snd_printk(), snd_printd() */
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/initval.h>
++#include <sound/ac97_codec.h>
++
++#include "pcm-indirect2.h"
++
++
++#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr"
++
++MODULE_AUTHOR("Joachim Foerster <JOFT at gmx.de>");
++MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference");
++MODULE_LICENSE("GPL");
++MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}");
++
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
++static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
++
++module_param_array(index, int, NULL, 0444);
++MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference.");
++module_param_array(id, charp, NULL, 0444);
++MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference.");
++module_param_array(enable, bool, NULL, 0444);
++MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference.");
++
++/* Special feature options */
++/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec
++				   * register, while RAF bit is not set
++				   */
++/* Debug options for code which may be removed completely in a final version */
++#ifdef CONFIG_SND_DEBUG
++/*#define CODEC_STAT*/            /* turn on some minimal "statistics"
++				   * about codec register usage
++				   */
++#define SND_PCM_INDIRECT2_STAT    /* turn on some "statistics" about the
++				   * process of copying bytes from the
++				   * intermediate buffer to the hardware
++				   * fifo and the other way round
++				   */
++#endif
++
++/* Definition of a "level/facility dependent" printk(); may be removed
++ * completely in a final version
++ */
++#undef PDEBUG
++#ifdef CONFIG_SND_DEBUG
++/* "facilities" for PDEBUG */
++#define UNKNOWN       (1<<0)
++#define CODEC_SUCCESS (1<<1)
++#define CODEC_FAKE    (1<<2)
++#define INIT_INFO     (1<<3)
++#define INIT_FAILURE  (1<<4)
++#define WORK_INFO     (1<<5)
++#define WORK_FAILURE  (1<<6)
++
++#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE)
++
++#define PDEBUG(fac, fmt, args...) do { \
++		if (fac & PDEBUG_FACILITIES) \
++			snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \
++				   fmt, ##args); \
++	} while (0)
++#else
++#define PDEBUG(fac, fmt, args...) /* nothing */
++#endif
++
++
++
++/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */
++#define CODEC_TIMEOUT_ON_INIT       5	/* timeout for checking for codec
++					 * readiness (after insmod)
++					 */
++#ifndef CODEC_WRITE_CHECK_RAF
++#define CODEC_WAIT_AFTER_WRITE    100	/* general, static wait after a write
++					 * access to a codec register, may be
++					 * 0 to completely remove wait
++					 */
++#else
++#define CODEC_TIMEOUT_AFTER_WRITE   5	/* timeout after a write access to a
++					 * codec register, if RAF bit is used
++					 */
++#endif
++#define CODEC_TIMEOUT_AFTER_READ    5	/* timeout after a read access to a
++					 * codec register (checking RAF bit)
++					 */
++
++/* Infrastructure for codec register shadowing */
++#define LM4550_REG_OK        (1<<0)   /* register exists */
++#define LM4550_REG_DONEREAD  (1<<1)   /* read register once, value should be
++				       * the same currently in the register
++				       */
++#define LM4550_REG_NOSAVE    (1<<2)   /* values written to this register will
++				       * not be saved in the register
++				       */
++#define LM4550_REG_NOSHADOW  (1<<3)   /* don't do register shadowing, use plain
++				       * hardware access
++				       */
++#define LM4550_REG_READONLY  (1<<4)   /* register is read only */
++#define LM4550_REG_FAKEPROBE (1<<5)   /* fake write _and_ read actions during
++				       * probe() correctly
++				       */
++#define LM4550_REG_FAKEREAD  (1<<6)   /* fake read access, always return
++				       * default value
++				       */
++#define LM4550_REG_ALLFAKE   (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE)
++
++struct lm4550_reg {
++	u16 value;
++	u16 flag;
++	u16 wmask;
++	u16 def;
++};
++
++struct lm4550_reg lm4550_regfile[64] = {
++	[AC97_RESET / 2]              = {.flag = LM4550_REG_OK \
++						| LM4550_REG_NOSAVE \
++						| LM4550_REG_FAKEREAD,
++					 .def = 0x0D50},
++	[AC97_MASTER / 2]             = {.flag = LM4550_REG_OK
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8000},
++	[AC97_HEADPHONE / 2]          = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8000},
++	[AC97_MASTER_MONO / 2]        = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x801F,
++					 .def = 0x8000},
++	[AC97_PC_BEEP / 2]            = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x801E,
++					 .def = 0x0},
++	[AC97_PHONE / 2]              = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x801F,
++					 .def = 0x8008},
++	[AC97_MIC / 2]                = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x805F,
++					 .def = 0x8008},
++	[AC97_LINE / 2]               = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8808},
++	[AC97_CD / 2]                 = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8808},
++	[AC97_VIDEO / 2]              = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8808},
++	[AC97_AUX / 2]                = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8808},
++	[AC97_PCM / 2]                = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x9F1F,
++					 .def = 0x8008},
++	[AC97_REC_SEL / 2]            = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x707,
++					 .def = 0x0},
++	[AC97_REC_GAIN / 2]           = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .wmask = 0x8F0F,
++					 .def = 0x8000},
++	[AC97_GENERAL_PURPOSE / 2]    = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .def = 0x0,
++					 .wmask = 0xA380},
++	[AC97_3D_CONTROL / 2]         = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEREAD \
++						| LM4550_REG_READONLY,
++					 .def = 0x0101},
++	[AC97_POWERDOWN / 2]          = {.flag = LM4550_REG_OK \
++						| LM4550_REG_NOSHADOW \
++						| LM4550_REG_NOSAVE,
++					 .wmask = 0xFF00},
++					/* may not write ones to
++					 * REF/ANL/DAC/ADC bits
++					 * FIXME: Is this ok?
++					 */
++	[AC97_EXTENDED_ID / 2]        = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEREAD \
++						| LM4550_REG_READONLY,
++					 .def = 0x0201}, /* primary codec */
++	[AC97_EXTENDED_STATUS / 2]    = {.flag = LM4550_REG_OK \
++						| LM4550_REG_NOSHADOW \
++						| LM4550_REG_NOSAVE,
++					 .wmask = 0x1},
++	[AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .def = 0xBB80,
++					 .wmask = 0xFFFF},
++	[AC97_PCM_LR_ADC_RATE / 2]    = {.flag = LM4550_REG_OK \
++						| LM4550_REG_FAKEPROBE,
++					 .def = 0xBB80,
++					 .wmask = 0xFFFF},
++	[AC97_VENDOR_ID1 / 2]         = {.flag = LM4550_REG_OK \
++						| LM4550_REG_READONLY \
++						| LM4550_REG_FAKEREAD,
++					 .def = 0x4E53},
++	[AC97_VENDOR_ID2 / 2]         = {.flag = LM4550_REG_OK \
++						| LM4550_REG_READONLY \
++						| LM4550_REG_FAKEREAD,
++					 .def = 0x4350}
++};
++
++#define LM4550_RF_OK(reg)    (lm4550_regfile[reg / 2].flag & LM4550_REG_OK)
++
++static void lm4550_regfile_init(void)
++{
++	int i;
++	for (i = 0; i < 64; i++)
++		if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE)
++			lm4550_regfile[i].value = lm4550_regfile[i].def;
++}
++
++static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97)
++{
++	int i;
++	for (i = 0; i < 64; i++)
++		if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) &&
++		    (lm4550_regfile[i].value != lm4550_regfile[i].def)) {
++			PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_"
++			       "init(): reg=0x%x value=0x%x / %d is different "
++			       "from def=0x%x / %d\n",
++			       i, lm4550_regfile[i].value,
++			       lm4550_regfile[i].value, lm4550_regfile[i].def,
++			       lm4550_regfile[i].def);
++			snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value);
++			lm4550_regfile[i].flag |= LM4550_REG_DONEREAD;
++		}
++}
++
++
++/* direct registers */
++#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x)
++
++#define CR_REG_PLAYFIFO         0x00
++#define   CR_PLAYDATA(a)        ((a) & 0xFFFF)
++
++#define CR_REG_RECFIFO          0x04
++#define   CR_RECDATA(a)         ((a) & 0xFFFF)
++
++#define CR_REG_STATUS           0x08
++#define   CR_RECOVER            (1<<7)
++#define   CR_PLAYUNDER          (1<<6)
++#define   CR_CODECREADY         (1<<5)
++#define   CR_RAF                (1<<4)
++#define   CR_RECEMPTY           (1<<3)
++#define   CR_RECFULL            (1<<2)
++#define   CR_PLAYHALF           (1<<1)
++#define   CR_PLAYFULL           (1<<0)
++
++#define CR_REG_RESETFIFO        0x0C
++#define   CR_RECRESET           (1<<1)
++#define   CR_PLAYRESET          (1<<0)
++
++#define CR_REG_CODEC_ADDR       0x10
++/* UG082 says:
++ * #define   CR_CODEC_ADDR(a)  ((a) << 1)
++ * #define   CR_CODEC_READ     (1<<0)
++ * #define   CR_CODEC_WRITE    (0<<0)
++ */
++/* RefDesign example says: */
++#define   CR_CODEC_ADDR(a)      ((a) << 0)
++#define   CR_CODEC_READ         (1<<7)
++#define   CR_CODEC_WRITE        (0<<7)
++
++#define CR_REG_CODEC_DATAREAD   0x14
++#define   CR_CODEC_DATAREAD(v)  ((v) & 0xFFFF)
++
++#define CR_REG_CODEC_DATAWRITE  0x18
++#define   CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF)
++
++#define CR_FIFO_SIZE            32
++
++struct snd_ml403_ac97cr {
++	/* lock for access to (controller) registers */
++	spinlock_t reg_lock;
++	/* mutex for the whole sequence of accesses to (controller) registers
++	 * which affect codec registers
++	 */
++	struct mutex cdc_mutex;
++
++	int irq; /* for playback */
++	int enable_irq;	/* for playback */
++
++	int capture_irq;
++	int enable_capture_irq;
++
++	struct resource *res_port;
++	void *port;
++
++	struct snd_ac97 *ac97;
++	int ac97_fake;
++#ifdef CODEC_STAT
++	int ac97_read;
++	int ac97_write;
++#endif
++
++	struct platform_device *pfdev;
++	struct snd_card *card;
++	struct snd_pcm *pcm;
++	struct snd_pcm_substream *playback_substream;
++	struct snd_pcm_substream *capture_substream;
++
++	struct snd_pcm_indirect2 ind_rec; /* for playback */
++	struct snd_pcm_indirect2 capture_ind2_rec;
++};
++
++static struct snd_pcm_hardware snd_ml403_ac97cr_playback = {
++	.info =	            (SNDRV_PCM_INFO_MMAP |
++			     SNDRV_PCM_INFO_INTERLEAVED |
++			     SNDRV_PCM_INFO_MMAP_VALID),
++	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
++	.rates =	    (SNDRV_PCM_RATE_CONTINUOUS |
++			     SNDRV_PCM_RATE_8000_48000),
++	.rate_min =	    4000,
++	.rate_max =	    48000,
++	.channels_min =     2,
++	.channels_max =     2,
++	.buffer_bytes_max = (128*1024),
++	.period_bytes_min = CR_FIFO_SIZE/2,
++	.period_bytes_max = (64*1024),
++	.periods_min =      2,
++	.periods_max =      (128*1024)/(CR_FIFO_SIZE/2),
++	.fifo_size =	    0,
++};
++
++static struct snd_pcm_hardware snd_ml403_ac97cr_capture = {
++	.info =	            (SNDRV_PCM_INFO_MMAP |
++			     SNDRV_PCM_INFO_INTERLEAVED |
++			     SNDRV_PCM_INFO_MMAP_VALID),
++	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
++	.rates =            (SNDRV_PCM_RATE_CONTINUOUS |
++			     SNDRV_PCM_RATE_8000_48000),
++	.rate_min =         4000,
++	.rate_max =         48000,
++	.channels_min =     2,
++	.channels_max =     2,
++	.buffer_bytes_max = (128*1024),
++	.period_bytes_min = CR_FIFO_SIZE/2,
++	.period_bytes_max = (64*1024),
++	.periods_min =      2,
++	.periods_max =      (128*1024)/(CR_FIFO_SIZE/2),
++	.fifo_size =	    0,
++};
++
++static size_t
++snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream,
++				    struct snd_pcm_indirect2 *rec)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	int copied_words = 0;
++	u32 full = 0;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	spin_lock(&ml403_ac97cr->reg_lock);
++	while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
++			CR_PLAYFULL)) != CR_PLAYFULL) {
++		out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0);
++		copied_words++;
++	}
++	rec->hw_ready = 0;
++	spin_unlock(&ml403_ac97cr->reg_lock);
++
++	return (size_t) (copied_words * 2);
++}
++
++static size_t
++snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream,
++				    struct snd_pcm_indirect2 *rec,
++				    size_t bytes)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	u16 *src;
++	int copied_words = 0;
++	u32 full = 0;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++	src = (u16 *)(substream->runtime->dma_area + rec->sw_data);
++
++	spin_lock(&ml403_ac97cr->reg_lock);
++	while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
++			 CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) {
++		out_be32(CR_REG(ml403_ac97cr, PLAYFIFO),
++			 CR_PLAYDATA(src[copied_words]));
++		copied_words++;
++		bytes = bytes - 2;
++	}
++	if (full != CR_PLAYFULL)
++		rec->hw_ready = 1;
++	else
++		rec->hw_ready = 0;
++	spin_unlock(&ml403_ac97cr->reg_lock);
++
++	return (size_t) (copied_words * 2);
++}
++
++static size_t
++snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream,
++				   struct snd_pcm_indirect2 *rec)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	int copied_words = 0;
++	u32 empty = 0;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	spin_lock(&ml403_ac97cr->reg_lock);
++	while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
++			 CR_RECEMPTY)) != CR_RECEMPTY) {
++		volatile u32 trash;
++
++		trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO)));
++		/* Hmmmm, really necessary? Don't want call to in_be32()
++		 * to be optimised away!
++		 */
++		trash++;
++		copied_words++;
++	}
++	rec->hw_ready = 0;
++	spin_unlock(&ml403_ac97cr->reg_lock);
++
++	return (size_t) (copied_words * 2);
++}
++
++static size_t
++snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream,
++				   struct snd_pcm_indirect2 *rec, size_t bytes)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	u16 *dst;
++	int copied_words = 0;
++	u32 empty = 0;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++	dst = (u16 *)(substream->runtime->dma_area + rec->sw_data);
++
++	spin_lock(&ml403_ac97cr->reg_lock);
++	while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
++			  CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) {
++		dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr,
++							      RECFIFO)));
++		copied_words++;
++		bytes = bytes - 2;
++	}
++	if (empty != CR_RECEMPTY)
++		rec->hw_ready = 1;
++	else
++		rec->hw_ready = 0;
++	spin_unlock(&ml403_ac97cr->reg_lock);
++
++	return (size_t) (copied_words * 2);
++}
++
++static snd_pcm_uframes_t
++snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	struct snd_pcm_indirect2 *ind2_rec = NULL;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	if (substream == ml403_ac97cr->playback_substream)
++		ind2_rec = &ml403_ac97cr->ind_rec;
++	if (substream == ml403_ac97cr->capture_substream)
++		ind2_rec = &ml403_ac97cr->capture_ind2_rec;
++
++	if (ind2_rec != NULL)
++		return snd_pcm_indirect2_pointer(substream, ind2_rec);
++	return (snd_pcm_uframes_t) 0;
++}
++
++static int
++snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream,
++				      int cmd)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	int err = 0;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++		PDEBUG(WORK_INFO, "trigger(playback): START\n");
++		ml403_ac97cr->ind_rec.hw_ready = 1;
++
++		/* clear play FIFO */
++		out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET);
++
++		/* enable play irq */
++		ml403_ac97cr->enable_irq = 1;
++		enable_irq(ml403_ac97cr->irq);
++		break;
++	case SNDRV_PCM_TRIGGER_STOP:
++		PDEBUG(WORK_INFO, "trigger(playback): STOP\n");
++		ml403_ac97cr->ind_rec.hw_ready = 0;
++#ifdef SND_PCM_INDIRECT2_STAT
++		snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec);
++#endif
++		/* disable play irq */
++		disable_irq_nosync(ml403_ac97cr->irq);
++		ml403_ac97cr->enable_irq = 0;
++		break;
++	default:
++		err = -EINVAL;
++		break;
++	}
++	PDEBUG(WORK_INFO, "trigger(playback): (done)\n");
++	return err;
++}
++
++static int
++snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream,
++				      int cmd)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	int err = 0;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++		PDEBUG(WORK_INFO, "trigger(capture): START\n");
++		ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
++
++		/* clear record FIFO */
++		out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET);
++
++		/* enable record irq */
++		ml403_ac97cr->enable_capture_irq = 1;
++		enable_irq(ml403_ac97cr->capture_irq);
++		break;
++	case SNDRV_PCM_TRIGGER_STOP:
++		PDEBUG(WORK_INFO, "trigger(capture): STOP\n");
++		ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
++#ifdef SND_PCM_INDIRECT2_STAT
++		snd_pcm_indirect2_stat(substream,
++				       &ml403_ac97cr->capture_ind2_rec);
++#endif
++		/* disable capture irq */
++		disable_irq_nosync(ml403_ac97cr->capture_irq);
++		ml403_ac97cr->enable_capture_irq = 0;
++		break;
++	default:
++		err = -EINVAL;
++		break;
++	}
++	PDEBUG(WORK_INFO, "trigger(capture): (done)\n");
++	return err;
++}
++
++static int
++snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	struct snd_pcm_runtime *runtime;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++	runtime = substream->runtime;
++
++	PDEBUG(WORK_INFO,
++	       "prepare(): period_bytes=%d, minperiod_bytes=%d\n",
++	       snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
++
++	/* set sampling rate */
++	snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE,
++			  runtime->rate);
++	PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate);
++
++	/* init struct for intermediate buffer */
++	memset(&ml403_ac97cr->ind_rec, 0,
++	       sizeof(struct snd_pcm_indirect2));
++	ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE;
++	ml403_ac97cr->ind_rec.sw_buffer_size =
++		snd_pcm_lib_buffer_bytes(substream);
++	ml403_ac97cr->ind_rec.min_periods = -1;
++	ml403_ac97cr->ind_rec.min_multiple =
++		snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
++	PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, "
++	       "sw_buffer_size=%d, min_multiple=%d\n",
++	       CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size,
++	       ml403_ac97cr->ind_rec.min_multiple);
++	return 0;
++}
++
++static int
++snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	struct snd_pcm_runtime *runtime;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++	runtime = substream->runtime;
++
++	PDEBUG(WORK_INFO,
++	       "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n",
++	       snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
++
++	/* set sampling rate */
++	snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE,
++			  runtime->rate);
++	PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate);
++
++	/* init struct for intermediate buffer */
++	memset(&ml403_ac97cr->capture_ind2_rec, 0,
++	       sizeof(struct snd_pcm_indirect2));
++	ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE;
++	ml403_ac97cr->capture_ind2_rec.sw_buffer_size =
++		snd_pcm_lib_buffer_bytes(substream);
++	ml403_ac97cr->capture_ind2_rec.min_multiple =
++		snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
++	PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, "
++	       "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE,
++	       ml403_ac97cr->capture_ind2_rec.sw_buffer_size,
++	       ml403_ac97cr->capture_ind2_rec.min_multiple);
++	return 0;
++}
++
++static int snd_ml403_ac97cr_hw_free(struct snd_pcm_substream *substream)
++{
++	PDEBUG(WORK_INFO, "hw_free()\n");
++	return snd_pcm_lib_free_pages(substream);
++}
++
++static int
++snd_ml403_ac97cr_hw_params(struct snd_pcm_substream *substream,
++			   struct snd_pcm_hw_params *hw_params)
++{
++	PDEBUG(WORK_INFO, "hw_params(): desired buffer bytes=%d, desired "
++	       "period bytes=%d\n",
++	       params_buffer_bytes(hw_params), params_period_bytes(hw_params));
++	return snd_pcm_lib_malloc_pages(substream,
++					params_buffer_bytes(hw_params));
++}
++
++static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	struct snd_pcm_runtime *runtime;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++	runtime = substream->runtime;
++
++	PDEBUG(WORK_INFO, "open(playback)\n");
++	ml403_ac97cr->playback_substream = substream;
++	runtime->hw = snd_ml403_ac97cr_playback;
++
++	snd_pcm_hw_constraint_step(runtime, 0,
++				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++				   CR_FIFO_SIZE / 2);
++	return 0;
++}
++
++static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	struct snd_pcm_runtime *runtime;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++	runtime = substream->runtime;
++
++	PDEBUG(WORK_INFO, "open(capture)\n");
++	ml403_ac97cr->capture_substream = substream;
++	runtime->hw = snd_ml403_ac97cr_capture;
++
++	snd_pcm_hw_constraint_step(runtime, 0,
++				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++				   CR_FIFO_SIZE / 2);
++	return 0;
++}
++
++static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	PDEBUG(WORK_INFO, "close(playback)\n");
++	ml403_ac97cr->playback_substream = NULL;
++	return 0;
++}
++
++static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++
++	ml403_ac97cr = snd_pcm_substream_chip(substream);
++
++	PDEBUG(WORK_INFO, "close(capture)\n");
++	ml403_ac97cr->capture_substream = NULL;
++	return 0;
++}
++
++static struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = {
++	.open = snd_ml403_ac97cr_playback_open,
++	.close = snd_ml403_ac97cr_playback_close,
++	.ioctl = snd_pcm_lib_ioctl,
++	.hw_params = snd_ml403_ac97cr_hw_params,
++	.hw_free = snd_ml403_ac97cr_hw_free,
++	.prepare = snd_ml403_ac97cr_pcm_playback_prepare,
++	.trigger = snd_ml403_ac97cr_pcm_playback_trigger,
++	.pointer = snd_ml403_ac97cr_pcm_pointer,
++};
++
++static struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = {
++	.open = snd_ml403_ac97cr_capture_open,
++	.close = snd_ml403_ac97cr_capture_close,
++	.ioctl = snd_pcm_lib_ioctl,
++	.hw_params = snd_ml403_ac97cr_hw_params,
++	.hw_free = snd_ml403_ac97cr_hw_free,
++	.prepare = snd_ml403_ac97cr_pcm_capture_prepare,
++	.trigger = snd_ml403_ac97cr_pcm_capture_trigger,
++	.pointer = snd_ml403_ac97cr_pcm_pointer,
++};
++
++static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	struct platform_device *pfdev;
++	int cmp_irq;
++
++	ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id;
++	if (ml403_ac97cr == NULL)
++		return IRQ_NONE;
++
++	pfdev = ml403_ac97cr->pfdev;
++
++	/* playback interrupt */
++	cmp_irq = platform_get_irq(pfdev, 0);
++	if (irq == cmp_irq) {
++		if (ml403_ac97cr->enable_irq)
++			snd_pcm_indirect2_playback_interrupt(
++				ml403_ac97cr->playback_substream,
++				&ml403_ac97cr->ind_rec,
++				snd_ml403_ac97cr_playback_ind2_copy,
++				snd_ml403_ac97cr_playback_ind2_zero);
++		else
++			goto __disable_irq;
++	} else {
++		/* record interrupt */
++		cmp_irq = platform_get_irq(pfdev, 1);
++		if (irq == cmp_irq) {
++			if (ml403_ac97cr->enable_capture_irq)
++				snd_pcm_indirect2_capture_interrupt(
++					ml403_ac97cr->capture_substream,
++					&ml403_ac97cr->capture_ind2_rec,
++					snd_ml403_ac97cr_capture_ind2_copy,
++					snd_ml403_ac97cr_capture_ind2_null);
++			else
++				goto __disable_irq;
++		} else
++			return IRQ_NONE;
++	}
++	return IRQ_HANDLED;
++
++__disable_irq:
++	PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try "
++	       "to disable it _really_!\n", irq);
++	disable_irq_nosync(irq);
++	return IRQ_HANDLED;
++}
++
++static unsigned short
++snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
++#ifdef CODEC_STAT
++	u32 stat;
++	u32 rafaccess = 0;
++#endif
++	unsigned long end_time;
++	u16 value = 0;
++
++	if (!LM4550_RF_OK(reg)) {
++		snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++			   "access to unknown/unused codec register 0x%x "
++			   "ignored!\n", reg);
++		return 0;
++	}
++	/* check if we can fake/answer this access from our shadow register */
++	if ((lm4550_regfile[reg / 2].flag &
++	     (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) &&
++	    !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
++		if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) {
++			PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
++			       "reg=0x%x, val=0x%x / %d\n",
++			       reg, lm4550_regfile[reg / 2].def,
++			       lm4550_regfile[reg / 2].def);
++			return lm4550_regfile[reg / 2].def;
++		} else if ((lm4550_regfile[reg / 2].flag &
++			    LM4550_REG_FAKEPROBE) &&
++			   ml403_ac97cr->ac97_fake) {
++			PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
++			       "reg=0x%x, val=0x%x / %d (probe)\n",
++			       reg, lm4550_regfile[reg / 2].value,
++			       lm4550_regfile[reg / 2].value);
++			return lm4550_regfile[reg / 2].value;
++		} else {
++#ifdef CODEC_STAT
++			PDEBUG(CODEC_FAKE, "codec_read(): read access "
++			       "answered by shadow register 0x%x (value=0x%x "
++			       "/ %d) (cw=%d cr=%d)\n",
++			       reg, lm4550_regfile[reg / 2].value,
++			       lm4550_regfile[reg / 2].value,
++			       ml403_ac97cr->ac97_write,
++			       ml403_ac97cr->ac97_read);
++#else
++			PDEBUG(CODEC_FAKE, "codec_read(): read access "
++			       "answered by shadow register 0x%x (value=0x%x "
++			       "/ %d)\n",
++			       reg, lm4550_regfile[reg / 2].value,
++			       lm4550_regfile[reg / 2].value);
++#endif
++			return lm4550_regfile[reg / 2].value;
++		}
++	}
++	/* if we are here, we _have_ to access the codec really, no faking */
++	if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
++		return 0;
++#ifdef CODEC_STAT
++	ml403_ac97cr->ac97_read++;
++#endif
++	spin_lock(&ml403_ac97cr->reg_lock);
++	out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
++		 CR_CODEC_ADDR(reg) | CR_CODEC_READ);
++	spin_unlock(&ml403_ac97cr->reg_lock);
++	end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ);
++	do {
++		spin_lock(&ml403_ac97cr->reg_lock);
++#ifdef CODEC_STAT
++		rafaccess++;
++		stat = in_be32(CR_REG(ml403_ac97cr, STATUS));
++		if ((stat & CR_RAF) == CR_RAF) {
++			value = CR_CODEC_DATAREAD(
++				in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
++			PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, "
++			       "value=0x%x / %d (STATUS=0x%x)\n",
++			       reg, value, value, stat);
++#else
++		if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
++		     CR_RAF) == CR_RAF) {
++			value = CR_CODEC_DATAREAD(
++				in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
++			PDEBUG(CODEC_SUCCESS, "codec_read(): (done) "
++			       "reg=0x%x, value=0x%x / %d\n",
++			       reg, value, value);
++#endif
++			lm4550_regfile[reg / 2].value = value;
++			lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
++			spin_unlock(&ml403_ac97cr->reg_lock);
++			mutex_unlock(&ml403_ac97cr->cdc_mutex);
++			return value;
++		}
++		spin_unlock(&ml403_ac97cr->reg_lock);
++		schedule_timeout_uninterruptible(1);
++	} while (time_after(end_time, jiffies));
++	/* read the DATAREAD register anyway, see comment below */
++	spin_lock(&ml403_ac97cr->reg_lock);
++	value =
++	    CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
++	spin_unlock(&ml403_ac97cr->reg_lock);
++#ifdef CODEC_STAT
++	snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++		   "timeout while codec read! "
++		   "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) "
++		   "(cw=%d, cr=%d)\n",
++		   reg, stat, value, value, rafaccess,
++		   ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read);
++#else
++	snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++		   "timeout while codec read! "
++		   "(reg=0x%x, DATAREAD=0x%x / %d)\n",
++		   reg, value, value);
++#endif
++	/* BUG: This is PURE speculation! But after _most_ read timeouts the
++	 * value in the register is ok!
++	 */
++	lm4550_regfile[reg / 2].value = value;
++	lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
++	mutex_unlock(&ml403_ac97cr->cdc_mutex);
++	return value;
++}
++
++static void
++snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg,
++			     unsigned short val)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
++
++#ifdef CODEC_STAT
++	u32 stat;
++	u32 rafaccess = 0;
++#endif
++#ifdef CODEC_WRITE_CHECK_RAF
++	unsigned long end_time;
++#endif
++
++	if (!LM4550_RF_OK(reg)) {
++		snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++			   "access to unknown/unused codec register 0x%x "
++			   "ignored!\n", reg);
++		return;
++	}
++	if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) {
++		snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++			   "write access to read only codec register 0x%x "
++			   "ignored!\n", reg);
++		return;
++	}
++	if ((val & lm4550_regfile[reg / 2].wmask) != val) {
++		snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++			   "write access to codec register 0x%x "
++			   "with bad value 0x%x / %d!\n",
++			   reg, val, val);
++		val = val & lm4550_regfile[reg / 2].wmask;
++	}
++	if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) &&
++	     ml403_ac97cr->ac97_fake) &&
++	    !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
++		PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, "
++		       "val=0x%x / %d\n", reg, val, val);
++		lm4550_regfile[reg / 2].value = (val &
++						lm4550_regfile[reg / 2].wmask);
++		return;
++	}
++	if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
++		return;
++#ifdef CODEC_STAT
++	ml403_ac97cr->ac97_write++;
++#endif
++	spin_lock(&ml403_ac97cr->reg_lock);
++	out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE),
++		 CR_CODEC_DATAWRITE(val));
++	out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
++		 CR_CODEC_ADDR(reg) | CR_CODEC_WRITE);
++	spin_unlock(&ml403_ac97cr->reg_lock);
++#ifdef CODEC_WRITE_CHECK_RAF
++	/* check CR_CODEC_RAF bit to see if write access to register is done;
++	 * loop until bit is set or timeout happens
++	 */
++	end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE;
++	do {
++		spin_lock(&ml403_ac97cr->reg_lock);
++#ifdef CODEC_STAT
++		rafaccess++;
++		stat = in_be32(CR_REG(ml403_ac97cr, STATUS))
++		if ((stat & CR_RAF) == CR_RAF) {
++#else
++		if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
++		     CR_RAF) == CR_RAF) {
++#endif
++			PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
++			       "reg=0x%x, value=%d / 0x%x\n",
++			       reg, val, val);
++			if (!(lm4550_regfile[reg / 2].flag &
++			      LM4550_REG_NOSHADOW) &&
++			    !(lm4550_regfile[reg / 2].flag &
++			      LM4550_REG_NOSAVE))
++				lm4550_regfile[reg / 2].value = val;
++			lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
++			spin_unlock(&ml403_ac97cr->reg_lock);
++			mutex_unlock(&ml403_ac97cr->cdc_mutex);
++			return;
++		}
++		spin_unlock(&ml403_ac97cr->reg_lock);
++		schedule_timeout_uninterruptible(1);
++	} while (time_after(end_time, jiffies));
++#ifdef CODEC_STAT
++	snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++		   "timeout while codec write "
++		   "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) "
++		   "(cw=%d, cr=%d)\n",
++		   reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write,
++		   ml403_ac97cr->ac97_read);
++#else
++	snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
++		   "timeout while codec write (reg=0x%x, val=0x%x / %d)\n",
++		   reg, val, val);
++#endif
++#else /* CODEC_WRITE_CHECK_RAF */
++#if CODEC_WAIT_AFTER_WRITE > 0
++	/* officially, in AC97 spec there is no possibility for a AC97
++	 * controller to determine, if write access is done or not - so: How
++	 * is Xilinx able to provide a RAF bit for write access?
++	 * => very strange, thus just don't check RAF bit (compare with
++	 * Xilinx's example app in EDK 8.1i) and wait
++	 */
++	schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE);
++#endif
++	PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
++	       "reg=0x%x, value=%d / 0x%x (no RAF check)\n",
++	       reg, val, val);
++#endif
++	mutex_unlock(&ml403_ac97cr->cdc_mutex);
++	return;
++}
++
++static int __devinit
++snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr)
++{
++	unsigned long end_time;
++	PDEBUG(INIT_INFO, "chip_init():\n");
++	end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT;
++	do {
++		if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) {
++			/* clear both hardware FIFOs */
++			out_be32(CR_REG(ml403_ac97cr, RESETFIFO),
++				 CR_RECRESET | CR_PLAYRESET);
++			PDEBUG(INIT_INFO, "chip_init(): (done)\n");
++			return 0;
++		}
++		schedule_timeout_uninterruptible(1);
++	} while (time_after(end_time, jiffies));
++	snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
++		   "timeout while waiting for codec, "
++		   "not ready!\n");
++	return -EBUSY;
++}
++
++static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr)
++{
++	PDEBUG(INIT_INFO, "free():\n");
++	/* irq release */
++	if (ml403_ac97cr->irq >= 0)
++		free_irq(ml403_ac97cr->irq, ml403_ac97cr);
++	if (ml403_ac97cr->capture_irq >= 0)
++		free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr);
++	/* give back "port" */
++	if (ml403_ac97cr->port != NULL)
++		iounmap(ml403_ac97cr->port);
++	kfree(ml403_ac97cr);
++	PDEBUG(INIT_INFO, "free(): (done)\n");
++	return 0;
++}
++
++static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data;
++	PDEBUG(INIT_INFO, "dev_free():\n");
++	return snd_ml403_ac97cr_free(ml403_ac97cr);
++}
++
++static int __devinit
++snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
++			struct snd_ml403_ac97cr **rml403_ac97cr)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr;
++	int err;
++	static struct snd_device_ops ops = {
++		.dev_free = snd_ml403_ac97cr_dev_free,
++	};
++	struct resource *resource;
++	int irq;
++
++	*rml403_ac97cr = NULL;
++	ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL);
++	if (ml403_ac97cr == NULL)
++		return -ENOMEM;
++	spin_lock_init(&ml403_ac97cr->reg_lock);
++	mutex_init(&ml403_ac97cr->cdc_mutex);
++	ml403_ac97cr->card = card;
++	ml403_ac97cr->pfdev = pfdev;
++	ml403_ac97cr->irq = -1;
++	ml403_ac97cr->enable_irq = 0;
++	ml403_ac97cr->capture_irq = -1;
++	ml403_ac97cr->enable_capture_irq = 0;
++	ml403_ac97cr->port = NULL;
++	ml403_ac97cr->res_port = NULL;
++
++	PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n");
++	resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
++	/* get "port" */
++	ml403_ac97cr->port = ioremap_nocache(resource->start,
++					     (resource->end) -
++					     (resource->start) + 1);
++	if (ml403_ac97cr->port == NULL) {
++		snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
++			   "unable to remap memory region (%x to %x)\n",
++			   resource->start, resource->end);
++		snd_ml403_ac97cr_free(ml403_ac97cr);
++		return -EBUSY;
++	}
++	snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
++		   "remap controller memory region to "
++		   "0x%x done\n", (unsigned int)ml403_ac97cr->port);
++	/* get irq */
++	irq = platform_get_irq(pfdev, 0);
++	if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
++			pfdev->dev.bus_id, (void *)ml403_ac97cr)) {
++		snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
++			   "unable to grab IRQ %d\n",
++			   irq);
++		snd_ml403_ac97cr_free(ml403_ac97cr);
++		return -EBUSY;
++	}
++	ml403_ac97cr->irq = irq;
++	snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
++		   "request (playback) irq %d done\n",
++		   ml403_ac97cr->irq);
++	irq = platform_get_irq(pfdev, 1);
++	if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
++			pfdev->dev.bus_id, (void *)ml403_ac97cr)) {
++		snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
++			   "unable to grab IRQ %d\n",
++			   irq);
++		snd_ml403_ac97cr_free(ml403_ac97cr);
++		return -EBUSY;
++	}
++	ml403_ac97cr->capture_irq = irq;
++	snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
++		   "request (capture) irq %d done\n",
++		   ml403_ac97cr->capture_irq);
++
++	err = snd_ml403_ac97cr_chip_init(ml403_ac97cr);
++	if (err < 0) {
++		snd_ml403_ac97cr_free(ml403_ac97cr);
++		return err;
++	}
++
++	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops);
++	if (err < 0) {
++		PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n");
++		snd_ml403_ac97cr_free(ml403_ac97cr);
++		return err;
++	}
++
++	snd_card_set_dev(card, &pfdev->dev);
++
++	*rml403_ac97cr = ml403_ac97cr;
++	return 0;
++}
++
++static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97)
++{
++	struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
++	PDEBUG(INIT_INFO, "mixer_free():\n");
++	ml403_ac97cr->ac97 = NULL;
++	PDEBUG(INIT_INFO, "mixer_free(): (done)\n");
++}
++
++static int __devinit
++snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr)
++{
++	struct snd_ac97_bus *bus;
++	struct snd_ac97_template ac97;
++	int err;
++	static struct snd_ac97_bus_ops ops = {
++		.write = snd_ml403_ac97cr_codec_write,
++		.read = snd_ml403_ac97cr_codec_read,
++	};
++	PDEBUG(INIT_INFO, "mixer():\n");
++	err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus);
++	if (err < 0)
++		return err;
++
++	memset(&ac97, 0, sizeof(ac97));
++	ml403_ac97cr->ac97_fake = 1;
++	lm4550_regfile_init();
++#ifdef CODEC_STAT
++	ml403_ac97cr->ac97_read = 0;
++	ml403_ac97cr->ac97_write = 0;
++#endif
++	ac97.private_data = ml403_ac97cr;
++	ac97.private_free = snd_ml403_ac97cr_mixer_free;
++	ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM |
++	    AC97_SCAP_NO_SPDIF;
++	err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97);
++	ml403_ac97cr->ac97_fake = 0;
++	lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97);
++	PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err);
++	return err;
++}
++
++static int __devinit
++snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device,
++		     struct snd_pcm **rpcm)
++{
++	struct snd_pcm *pcm;
++	int err;
++
++	if (rpcm)
++		*rpcm = NULL;
++	err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1,
++			  &pcm);
++	if (err < 0)
++		return err;
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++			&snd_ml403_ac97cr_playback_ops);
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++			&snd_ml403_ac97cr_capture_ops);
++	pcm->private_data = ml403_ac97cr;
++	pcm->info_flags = 0;
++	strcpy(pcm->name, "ML403AC97CR DAC/ADC");
++	ml403_ac97cr->pcm = pcm;
++
++	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
++					  snd_dma_continuous_data(GFP_KERNEL),
++					  64 * 1024,
++					  128 * 1024);
++	if (rpcm)
++		*rpcm = pcm;
++	return 0;
++}
++
++static int __devinit snd_ml403_ac97cr_probe(struct platform_device *pfdev)
++{
++	struct snd_card *card;
++	struct snd_ml403_ac97cr *ml403_ac97cr = NULL;
++	int err;
++	int dev = pfdev->id;
++
++	if (dev >= SNDRV_CARDS)
++		return -ENODEV;
++	if (!enable[dev])
++		return -ENOENT;
++
++	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
++	if (card == NULL)
++		return -ENOMEM;
++	err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
++	if (err < 0) {
++		PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
++		snd_card_free(card);
++		return err;
++	}
++	PDEBUG(INIT_INFO, "probe(): create done\n");
++	card->private_data = ml403_ac97cr;
++	err = snd_ml403_ac97cr_mixer(ml403_ac97cr);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++	PDEBUG(INIT_INFO, "probe(): mixer done\n");
++	err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++	PDEBUG(INIT_INFO, "probe(): PCM done\n");
++	strcpy(card->driver, SND_ML403_AC97CR_DRIVER);
++	strcpy(card->shortname, "ML403 AC97 Controller Reference");
++	sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i",
++		card->shortname, card->driver,
++		(unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq,
++		ml403_ac97cr->capture_irq, dev + 1);
++
++	snd_card_set_dev(card, &pfdev->dev);
++
++	err = snd_card_register(card);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++	platform_set_drvdata(pfdev, card);
++	PDEBUG(INIT_INFO, "probe(): (done)\n");
++	return 0;
++}
++
++static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
++{
++	snd_card_free(platform_get_drvdata(pfdev));
++	platform_set_drvdata(pfdev, NULL);
++	return 0;
++}
++
++static struct platform_driver snd_ml403_ac97cr_driver = {
++	.probe = snd_ml403_ac97cr_probe,
++	.remove = snd_ml403_ac97cr_remove,
++	.driver = {
++		.name = SND_ML403_AC97CR_DRIVER,
++	},
++};
++
++static int __init alsa_card_ml403_ac97cr_init(void)
++{
++	return platform_driver_register(&snd_ml403_ac97cr_driver);
++}
++
++static void __exit alsa_card_ml403_ac97cr_exit(void)
++{
++	platform_driver_unregister(&snd_ml403_ac97cr_driver);
++}
++
++module_init(alsa_card_ml403_ac97cr_init)
++module_exit(alsa_card_ml403_ac97cr_exit)
+diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
+index 1fc95da..5b996f3 100644
+--- a/sound/drivers/mpu401/mpu401.c
++++ b/sound/drivers/mpu401/mpu401.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/pnp.h>
+ #include <linux/err.h>
+diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
+index b57f2d5..5993864 100644
+--- a/sound/drivers/mpu401/mpu401_uart.c
++++ b/sound/drivers/mpu401/mpu401_uart.c
+@@ -28,7 +28,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
+index 40eb026..b5e1a71 100644
+--- a/sound/drivers/mtpav.c
++++ b/sound/drivers/mtpav.c
+@@ -50,7 +50,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/err.h>
+diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
+index dcc90f9..87ba1dd 100644
+--- a/sound/drivers/mts64.c
++++ b/sound/drivers/mts64.c
+@@ -18,7 +18,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/parport.h>
+@@ -461,13 +460,14 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
+ {
+ 	struct mts64 *mts = snd_kcontrol_chip(kctl);
+ 	int changed = 0;
++	int val = !!uctl->value.integer.value[0];
+ 
+ 	spin_lock_irq(&mts->lock);
+-	if (mts->smpte_switch == uctl->value.integer.value[0])
++	if (mts->smpte_switch == val)
+ 		goto __out;
+ 
+ 	changed = 1;
+-	mts->smpte_switch = uctl->value.integer.value[0];
++	mts->smpte_switch = val;
+ 	if (mts->smpte_switch) {
+ 		mts64_smpte_start(mts->pardev->port,
+ 				  mts->time[0], mts->time[1],
+@@ -541,12 +541,13 @@ static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
+ {
+ 	struct mts64 *mts = snd_kcontrol_chip(kctl);
+ 	int idx = kctl->private_value;
++	unsigned int time = uctl->value.integer.value[0] % 60;
+ 	int changed = 0;
+ 
+ 	spin_lock_irq(&mts->lock);
+-	if (mts->time[idx] != uctl->value.integer.value[0]) {
++	if (mts->time[idx] != time) {
+ 		changed = 1;
+-		mts->time[idx] = uctl->value.integer.value[0];
++		mts->time[idx] = time;
+ 	}
+ 	spin_unlock_irq(&mts->lock);
+ 
+@@ -636,6 +637,8 @@ static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
+ 	struct mts64 *mts = snd_kcontrol_chip(kctl);
+ 	int changed = 0;
+ 
++	if (uctl->value.enumerated.item[0] >= 5)
++		return -EINVAL;
+ 	spin_lock_irq(&mts->lock);
+ 	if (mts->fps != uctl->value.enumerated.item[0]) {
+ 		changed = 1;
+@@ -662,7 +665,7 @@ static int __devinit snd_mts64_ctl_create(struct snd_card *card,
+ 					  struct mts64 *mts) 
+ {
+ 	int err, i;
+-	static struct snd_kcontrol_new *control[] = {
++	static struct snd_kcontrol_new *control[] __devinitdata = {
+ 		&mts64_ctl_smpte_switch,
+ 		&mts64_ctl_smpte_time_hours,
+ 		&mts64_ctl_smpte_time_minutes,
+@@ -1004,6 +1007,8 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
+ 
+ 	platform_set_drvdata(pdev, card);
+ 
++	snd_card_set_dev(card, &pdev->dev);
++
+ 	/* At this point card will be usable */
+ 	if ((err = snd_card_register(card)) < 0) {
+ 		snd_printd("Cannot register card\n");
+diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
+index a2b9ce0..ebe4359 100644
+--- a/sound/drivers/opl3/opl3_lib.c
++++ b/sound/drivers/opl3/opl3_lib.c
+@@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_opl3 *opl3)
+ 	snd_assert(opl3 != NULL, return -ENXIO);
+ 	if (opl3->private_free)
+ 		opl3->private_free(opl3);
++	snd_opl3_clear_patches(opl3);
+ 	release_and_free_resource(opl3->res_l_port);
+ 	release_and_free_resource(opl3->res_r_port);
+ 	kfree(opl3);
+@@ -360,7 +361,6 @@ int snd_opl3_new(struct snd_card *card,
+ 	opl3->hardware = hardware;
+ 	spin_lock_init(&opl3->reg_lock);
+ 	spin_lock_init(&opl3->timer_lock);
+-	mutex_init(&opl3->access_mutex);
+ 
+ 	if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
+ 		snd_opl3_free(opl3);
+@@ -496,6 +496,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
+ 		return err;
+ 	}
+ 	hw->private_data = opl3;
++	hw->exclusive = 1;
+ #ifdef CONFIG_SND_OSSEMUL
+ 	if (device == 0) {
+ 		hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
+@@ -521,8 +522,10 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
+ 	/* operators - only ioctl */
+ 	hw->ops.open = snd_opl3_open;
+ 	hw->ops.ioctl = snd_opl3_ioctl;
++	hw->ops.write = snd_opl3_write;
+ 	hw->ops.release = snd_opl3_release;
+ 
++	opl3->hwdep = hw;
+ 	opl3->seq_dev_num = seq_device;
+ #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+ 	if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
+diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
+index 3557b6e..cebcb8b 100644
+--- a/sound/drivers/opl3/opl3_midi.c
++++ b/sound/drivers/opl3/opl3_midi.c
+@@ -289,8 +289,6 @@ static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
+ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ {
+ 	struct snd_opl3 *opl3;
+-	struct snd_seq_instr wanted;
+-	struct snd_seq_kinstr *kinstr;
+ 	int instr_4op;
+ 
+ 	int voice;
+@@ -306,11 +304,13 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 	unsigned char voice_offset;
+ 	unsigned short opl3_reg;
+ 	unsigned char reg_val;
++	unsigned char prg, bank;
+ 
+ 	int key = note;
+ 	unsigned char fnum, blocknum;
+ 	int i;
+ 
++	struct fm_patch *patch;
+ 	struct fm_instrument *fm;
+ 	unsigned long flags;
+ 
+@@ -320,19 +320,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 	snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",
+ 		   chan->number, chan->midi_program, note, vel);
+ #endif
+-	wanted.cluster = 0;
+-	wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
+ 
+ 	/* in SYNTH mode, application takes care of voices */
+ 	/* in SEQ mode, drum voice numbers are notes on drum channel */
+ 	if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
+ 		if (chan->drum_channel) {
+ 			/* percussion instruments are located in bank 128 */
+-			wanted.bank = 128;
+-			wanted.prg = note;
++			bank = 128;
++			prg = note;
+ 		} else {
+-			wanted.bank = chan->gm_bank_select;
+-			wanted.prg = chan->midi_program;
++			bank = chan->gm_bank_select;
++			prg = chan->midi_program;
+ 		}
+ 	} else {
+ 		/* Prepare for OSS mode */
+@@ -340,8 +338,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 			return;
+ 
+ 		/* OSS instruments are located in bank 127 */
+-		wanted.bank = 127;
+-		wanted.prg = chan->midi_program;
++		bank = 127;
++		prg = chan->midi_program;
+ 	}
+ 
+ 	spin_lock_irqsave(&opl3->voice_lock, flags);
+@@ -353,15 +351,14 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 	}
+ 
+  __extra_prg:
+-	kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0);
+-	if (kinstr == NULL) {
++	patch = snd_opl3_find_patch(opl3, prg, bank, 0);
++	if (!patch) {
+ 		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+ 		return;
+ 	}
+ 
+-	fm = KINSTR_DATA(kinstr);
+-
+-	switch (fm->type) {
++	fm = &patch->inst;
++	switch (patch->type) {
+ 	case FM_PATCH_OPL2:
+ 		instr_4op = 0;
+ 		break;
+@@ -371,14 +368,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 			break;
+ 		}
+ 	default:
+-		snd_seq_instr_free_use(opl3->ilist, kinstr);
+ 		spin_unlock_irqrestore(&opl3->voice_lock, flags);
+ 		return;
+ 	}
+-
+ #ifdef DEBUG_MIDI
+ 	snd_printk("  --> OPL%i instrument: %s\n",
+-		   instr_4op ? 3 : 2, kinstr->name);
++		   instr_4op ? 3 : 2, patch->name);
+ #endif
+ 	/* in SYNTH mode, application takes care of voices */
+ 	/* in SEQ mode, allocate voice on free OPL3 channel */
+@@ -569,8 +564,6 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 	/* get extra pgm, but avoid possible loops */
+ 	extra_prg = (extra_prg) ? 0 : fm->modes;
+ 
+-	snd_seq_instr_free_use(opl3->ilist, kinstr);
+-
+ 	/* do the bookkeeping */
+ 	vp->time = opl3->use_time++;
+ 	vp->note = key;
+@@ -601,12 +594,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
+ 	/* allocate extra program if specified in patch library */
+ 	if (extra_prg) {
+ 		if (extra_prg > 128) {
+-			wanted.bank = 128;
++			bank = 128;
+ 			/* percussions start at 35 */
+-			wanted.prg = extra_prg - 128 + 35 - 1;
++			prg = extra_prg - 128 + 35 - 1;
+ 		} else {
+-			wanted.bank = 0;
+-			wanted.prg = extra_prg - 1;
++			bank = 0;
++			prg = extra_prg - 1;
+ 		}
+ #ifdef DEBUG_MIDI
+ 		snd_printk(" *** allocating extra program\n");
+diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
+index 5fd3a4c..239347f 100644
+--- a/sound/drivers/opl3/opl3_oss.c
++++ b/sound/drivers/opl3/opl3_oss.c
+@@ -195,17 +195,6 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
+ 
+ /* load patch */
+ 
+-/* offsets for SBI params */
+-#define AM_VIB		0
+-#define KSL_LEVEL	2
+-#define ATTACK_DECAY	4
+-#define SUSTAIN_RELEASE	6
+-#define WAVE_SELECT	8
+-
+-/* offset for SBI instrument */
+-#define CONNECTION	10
+-#define OFFSET_4OP	11
+-
+ /* from sound_config.h */
+ #define SBFM_MAXINSTR	256
+ 
+@@ -213,112 +202,42 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
+ 				       const char __user *buf, int offs, int count)
+ {
+ 	struct snd_opl3 *opl3;
+-	int err = -EINVAL;
++	struct sbi_instrument sbi;
++	char name[32];
++	int err, type;
+ 
+ 	snd_assert(arg != NULL, return -ENXIO);
+ 	opl3 = arg->private_data;
+ 
+-	if ((format == FM_PATCH) || (format == OPL3_PATCH)) {
+-		struct sbi_instrument sbi;
++	if (format == FM_PATCH)
++		type = FM_PATCH_OPL2;
++	else if (format == OPL3_PATCH)
++		type = FM_PATCH_OPL3;
++	else
++		return -EINVAL;
+ 
+-		size_t size;
+-		struct snd_seq_instr_header *put;
+-		struct snd_seq_instr_data *data;
+-		struct fm_xinstrument *xinstr;
++	if (count < (int)sizeof(sbi)) {
++		snd_printk("FM Error: Patch record too short\n");
++		return -EINVAL;
++	}
++	if (copy_from_user(&sbi, buf, sizeof(sbi)))
++		return -EFAULT;
+ 
+-		struct snd_seq_event ev;
+-		int i;
++	if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
++		snd_printk("FM Error: Invalid instrument number %d\n",
++			   sbi.channel);
++		return -EINVAL;
++	}
+ 
+-		mm_segment_t fs;
++	memset(name, 0, sizeof(name));
++	sprintf(name, "Chan%d", sbi.channel);
+ 
+-		if (count < (int)sizeof(sbi)) {
+-			snd_printk("FM Error: Patch record too short\n");
+-			return -EINVAL;
+-		}
+-		if (copy_from_user(&sbi, buf, sizeof(sbi)))
+-			return -EFAULT;
++	err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL,
++				  sbi.operators);
++	if (err < 0)
++		return err;
+ 
+-		if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
+-			snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel);
+-			return -EINVAL;
+-		}
+-
+-		size = sizeof(*put) + sizeof(struct fm_xinstrument);
+-		put = kzalloc(size, GFP_KERNEL);
+-		if (put == NULL)
+-			return -ENOMEM;
+-		/* build header */
+-		data = &put->data;
+-		data->type = SNDRV_SEQ_INSTR_ATYPE_DATA;
+-		strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3);
+-		/* build data section */
+-		xinstr = (struct fm_xinstrument *)(data + 1);
+-		xinstr->stype = FM_STRU_INSTR;
+-        
+-		for (i = 0; i < 2; i++) {
+-			xinstr->op[i].am_vib = sbi.operators[AM_VIB + i];
+-			xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i];
+-			xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i];
+-			xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i];
+-			xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i];
+-		}
+-		xinstr->feedback_connection[0] = sbi.operators[CONNECTION];
+-
+-		if (format == OPL3_PATCH) {
+-			xinstr->type = FM_PATCH_OPL3;
+-			for (i = 0; i < 2; i++) {
+-				xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i];
+-				xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i];
+-				xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i];
+-				xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i];
+-				xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i];
+-			}
+-			xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION];
+-		} else {
+-			xinstr->type = FM_PATCH_OPL2;
+-		}
+-
+-		put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
+-		put->id.instr.bank = 127;
+-		put->id.instr.prg = sbi.channel;
+-		put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE;
+-
+-		memset (&ev, 0, sizeof(ev));
+-		ev.source.client = SNDRV_SEQ_CLIENT_OSS;
+-		ev.dest = arg->addr; 
+-
+-		ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR;
+-		ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
+-
+-		fs = snd_enter_user();
+-	__again:
+-		ev.type = SNDRV_SEQ_EVENT_INSTR_PUT;
+-		ev.data.ext.len = size;
+-		ev.data.ext.ptr = put;
+-
+-		err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
+-				    opl3->seq_client, 0, 0);
+-		if (err == -EBUSY) {
+-			struct snd_seq_instr_header remove;
+-
+-			memset (&remove, 0, sizeof(remove));
+-			remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE;
+-			remove.id.instr = put->id.instr;
+-
+-			/* remove instrument */
+-			ev.type = SNDRV_SEQ_EVENT_INSTR_FREE;
+-			ev.data.ext.len = sizeof(remove);
+-			ev.data.ext.ptr = &remove;
+-
+-			snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
+-					    opl3->seq_client, 0, 0);
+-			goto __again;
+-		}
+-		snd_leave_user(fs);
+-
+-		kfree(put);
+-	}
+-	return err;
++	return sizeof(sbi);
+ }
+ 
+ /* ioctl */
+diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
+index 96762c9..2d33f53 100644
+--- a/sound/drivers/opl3/opl3_seq.c
++++ b/sound/drivers/opl3/opl3_seq.c
+@@ -51,14 +51,15 @@ void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
+ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
+ {
+ 	int idx;
++	struct snd_hwdep *hwdep = opl3->hwdep;
+ 
+-	mutex_lock(&opl3->access_mutex);
+-	if (opl3->used) {
+-		mutex_unlock(&opl3->access_mutex);
++	mutex_lock(&hwdep->open_mutex);
++	if (hwdep->used) {
++		mutex_unlock(&hwdep->open_mutex);
+ 		return -EBUSY;
+ 	}
+-	opl3->used++;
+-	mutex_unlock(&opl3->access_mutex);
++	hwdep->used++;
++	mutex_unlock(&hwdep->open_mutex);
+ 
+ 	snd_opl3_reset(opl3);
+ 
+@@ -81,6 +82,7 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
+ void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
+ {
+ 	unsigned long flags;
++	struct snd_hwdep *hwdep;
+ 
+ 	/* Stop system timer */
+ 	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+@@ -91,9 +93,11 @@ void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
+ 	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
+ 
+ 	snd_opl3_reset(opl3);
+-	mutex_lock(&opl3->access_mutex);
+-	opl3->used--;
+-	mutex_unlock(&opl3->access_mutex);
++	hwdep = opl3->hwdep;
++	mutex_lock(&hwdep->open_mutex);
++	hwdep->used--;
++	mutex_unlock(&hwdep->open_mutex);
++	wake_up(&hwdep->open_wait);
+ }
+ 
+ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
+@@ -152,15 +156,7 @@ static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
+ {
+ 	struct snd_opl3 *opl3 = private_data;
+ 
+-	if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN &&
+-	    ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) {
+-		if (direct) {
+-			snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev,
+-					    opl3->seq_client, atomic, hop);
+-		}
+-	} else {
+-		snd_midi_process_event(&opl3_ops, ev, opl3->chset);
+-	}
++	snd_midi_process_event(&opl3_ops, ev, opl3->chset);
+ 	return 0;
+ }
+ 
+@@ -249,16 +245,6 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
+ 		return err;
+ 	}
+ 
+-	/* initialize instrument list */
+-	opl3->ilist = snd_seq_instr_list_new();
+-	if (opl3->ilist == NULL) {
+-		snd_seq_delete_kernel_client(client);
+-		opl3->seq_client = -1;
+-		return -ENOMEM;
+-	}
+-	opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+-	snd_seq_fm_init(&opl3->fm_ops, NULL);
+-
+ 	/* setup system timer */
+ 	init_timer(&opl3->tlist);
+ 	opl3->tlist.function = snd_opl3_timer_func;
+@@ -287,8 +273,6 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
+ 		snd_seq_delete_kernel_client(opl3->seq_client);
+ 		opl3->seq_client = -1;
+ 	}
+-	if (opl3->ilist)
+-		snd_seq_instr_list_free(&opl3->ilist);
+ 	return 0;
+ }
+ 
+diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
+index a4b3543..a7bf7a4 100644
+--- a/sound/drivers/opl3/opl3_synth.c
++++ b/sound/drivers/opl3/opl3_synth.c
+@@ -76,16 +76,6 @@ static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection);
+  */
+ int snd_opl3_open(struct snd_hwdep * hw, struct file *file)
+ {
+-	struct snd_opl3 *opl3 = hw->private_data;
+-
+-	mutex_lock(&opl3->access_mutex);
+-	if (opl3->used) {
+-		mutex_unlock(&opl3->access_mutex);
+-		return -EAGAIN;
+-	}
+-	opl3->used++;
+-	mutex_unlock(&opl3->access_mutex);
+-
+ 	return 0;
+ }
+ 
+@@ -165,6 +155,10 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
+ #endif
+ 		return snd_opl3_set_connection(opl3, (int) arg);
+ 
++	case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES:
++		snd_opl3_clear_patches(opl3);
++		return 0;
++
+ #ifdef CONFIG_SND_DEBUG
+ 	default:
+ 		snd_printk("unknown IOCTL: 0x%x\n", cmd);
+@@ -181,12 +175,172 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file)
+ 	struct snd_opl3 *opl3 = hw->private_data;
+ 
+ 	snd_opl3_reset(opl3);
+-	mutex_lock(&opl3->access_mutex);
+-	opl3->used--;
+-	mutex_unlock(&opl3->access_mutex);
++	return 0;
++}
++
++/*
++ * write the device - load patches
++ */
++long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
++		    loff_t *offset)
++{
++	struct snd_opl3 *opl3 = hw->private_data;
++	long result = 0;
++	int err = 0;
++	struct sbi_patch inst;
++
++	while (count >= sizeof(inst)) {
++		unsigned char type;
++		if (copy_from_user(&inst, buf, sizeof(inst)))
++			return -EFAULT;
++		if (!memcmp(inst.key, FM_KEY_SBI, 4) ||
++		    !memcmp(inst.key, FM_KEY_2OP, 4))
++			type = FM_PATCH_OPL2;
++		else if (!memcmp(inst.key, FM_KEY_4OP, 4))
++			type = FM_PATCH_OPL3;
++		else /* invalid type */
++			break;
++		err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type,
++					  inst.name, inst.extension,
++					  inst.data);
++		if (err < 0)
++			break;
++		result += sizeof(inst);
++		count -= sizeof(inst);
++	}
++	return result > 0 ? result : err;
++}
++
++
++/*
++ * Patch management
++ */
++
++/* offsets for SBI params */
++#define AM_VIB		0
++#define KSL_LEVEL	2
++#define ATTACK_DECAY	4
++#define SUSTAIN_RELEASE	6
++#define WAVE_SELECT	8
++
++/* offset for SBI instrument */
++#define CONNECTION	10
++#define OFFSET_4OP	11
++
++/*
++ * load a patch, obviously.
++ *
++ * loaded on the given program and bank numbers with the given type
++ * (FM_PATCH_OPLx).
++ * data is the pointer of SBI record _without_ header (key and name).
++ * name is the name string of the patch.
++ * ext is the extension data of 7 bytes long (stored in name of SBI
++ * data up to offset 25), or NULL to skip.
++ * return 0 if successful or a negative error code.
++ */
++int snd_opl3_load_patch(struct snd_opl3 *opl3,
++			int prog, int bank, int type,
++			const char *name,
++			const unsigned char *ext,
++			const unsigned char *data)
++{
++	struct fm_patch *patch;
++	int i;
++
++	patch = snd_opl3_find_patch(opl3, prog, bank, 1);
++	if (!patch)
++		return -ENOMEM;
++
++	patch->type = type;
++
++	for (i = 0; i < 2; i++) {
++		patch->inst.op[i].am_vib = data[AM_VIB + i];
++		patch->inst.op[i].ksl_level = data[KSL_LEVEL + i];
++		patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i];
++		patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i];
++		patch->inst.op[i].wave_select = data[WAVE_SELECT + i];
++	}
++	patch->inst.feedback_connection[0] = data[CONNECTION];
++
++	if (type == FM_PATCH_OPL3) {
++		for (i = 0; i < 2; i++) {
++			patch->inst.op[i+2].am_vib =
++				data[OFFSET_4OP + AM_VIB + i];
++			patch->inst.op[i+2].ksl_level =
++				data[OFFSET_4OP + KSL_LEVEL + i];
++			patch->inst.op[i+2].attack_decay =
++				data[OFFSET_4OP + ATTACK_DECAY + i];
++			patch->inst.op[i+2].sustain_release =
++				data[OFFSET_4OP + SUSTAIN_RELEASE + i];
++			patch->inst.op[i+2].wave_select =
++				data[OFFSET_4OP + WAVE_SELECT + i];
++		}
++		patch->inst.feedback_connection[1] =
++			data[OFFSET_4OP + CONNECTION];
++	}
++
++	if (ext) {
++		patch->inst.echo_delay = ext[0];
++		patch->inst.echo_atten = ext[1];
++		patch->inst.chorus_spread = ext[2];
++		patch->inst.trnsps = ext[3];
++		patch->inst.fix_dur = ext[4];
++		patch->inst.modes = ext[5];
++		patch->inst.fix_key = ext[6];
++	}
++
++	if (name)
++		strlcpy(patch->name, name, sizeof(patch->name));
+ 
+ 	return 0;
+ }
++EXPORT_SYMBOL(snd_opl3_load_patch);
++
++/*
++ * find a patch with the given program and bank numbers, returns its pointer
++ * if no matching patch is found and create_patch is set, it creates a
++ * new patch object.
++ */
++struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
++				     int create_patch)
++{
++	/* pretty dumb hash key */
++	unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE;
++	struct fm_patch *patch;
++
++	for (patch = opl3->patch_table[key]; patch; patch = patch->next) {
++		if (patch->prog == prog && patch->bank == bank)
++			return patch;
++	}
++	if (!create_patch)
++		return NULL;
++
++	patch = kzalloc(sizeof(*patch), GFP_KERNEL);
++	if (!patch)
++		return NULL;
++	patch->prog = prog;
++	patch->bank = bank;
++	patch->next = opl3->patch_table[key];
++	opl3->patch_table[key] = patch;
++	return patch;
++}
++EXPORT_SYMBOL(snd_opl3_find_patch);
++
++/*
++ * Clear all patches of the given OPL3 instance
++ */
++void snd_opl3_clear_patches(struct snd_opl3 *opl3)
++{
++	int i;
++	for (i = 0; i <  OPL3_PATCH_HASH_SIZE; i++) {
++		struct fm_patch *patch, *next;
++		for (patch = opl3->patch_table[i]; patch; patch = next) {
++			next = patch->next;
++			kfree(patch);
++		}
++	}
++	memset(opl3->patch_table, 0, sizeof(opl3->patch_table));
++}
+ 
+ /* ------------------------------ */
+ 
+diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
+new file mode 100644
+index 0000000..3c93c23
+--- /dev/null
++++ b/sound/drivers/pcm-indirect2.c
+@@ -0,0 +1,573 @@
++/*
++ * Helper functions for indirect PCM data transfer to a simple FIFO in
++ * hardware (small, no possibility to read "hardware io position",
++ * updating position done by interrupt, ...)
++ *
++ *  Copyright (c) by 2007  Joachim Foerster <JOFT at gmx.de>
++ *
++ *  Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
++ *
++ *  Copyright (c) by Takashi Iwai <tiwai at suse.de>
++ *                   Jaroslav Kysela <perex at suse.cz>
++ *
++ *   This program 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 program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++/* snd_printk/d() */
++#include <sound/core.h>
++/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
++ * snd_pcm_period_elapsed() */
++#include <sound/pcm.h>
++
++#include "pcm-indirect2.h"
++
++#ifdef SND_PCM_INDIRECT2_STAT
++/* jiffies */
++#include <linux/jiffies.h>
++
++void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
++			    struct snd_pcm_indirect2 *rec)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	int i;
++	int j;
++	int k;
++	int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
++
++	snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
++		   "irq_occured: %d\n",
++		   rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
++	snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
++		   rec->min_multiple);
++	snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
++		   "firstzerotime: %lu\n",
++		 rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
++	snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
++		   "length: %d s\n",
++		 rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
++	snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
++		   "rate: %d Bytes/s = %d Frames/s|Hz\n",
++		   seconds, rec->bytes2hw / seconds,
++		   rec->bytes2hw / 2 / 2 / seconds);
++	snd_printk(KERN_DEBUG
++		   "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
++		   rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
++		   runtime->rate,
++		   rec->zeros2hw / (rec->hw_buffer_size / 2),
++		   (rec->hw_buffer_size / 2));
++	snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
++		   rec->pointer_calls, rec->lastdifftime);
++	snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
++		   rec->sw_data);
++	snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
++	k = 0;
++	for (j = 0; j < 8; j++) {
++		for (i = j * 8; i < (j + 1) * 8; i++)
++			if (rec->byte_sizes[i] != 0) {
++				snd_printk(KERN_DEBUG "%u: %u",
++					   i, rec->byte_sizes[i]);
++				k++;
++			}
++		if (((k % 8) == 0) && (k != 0)) {
++			snd_printk(KERN_DEBUG "\n");
++			k = 0;
++		}
++	}
++	snd_printk(KERN_DEBUG "\n");
++	snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
++	for (j = 0; j < 8; j++) {
++		k = 0;
++		for (i = j * 8; i < (j + 1) * 8; i++)
++			if (rec->zero_sizes[i] != 0)
++				snd_printk(KERN_DEBUG "%u: %u",
++					   i, rec->zero_sizes[i]);
++			else
++				k++;
++		if (!k)
++			snd_printk(KERN_DEBUG "\n");
++	}
++	snd_printk(KERN_DEBUG "\n");
++	snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
++	for (j = 0; j < 8; j++) {
++		if (rec->min_adds[j] != 0)
++			snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
++	}
++	snd_printk(KERN_DEBUG "\n");
++	snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
++	for (j = 0; j < 8; j++) {
++		if (rec->mul_adds[j] != 0)
++			snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
++	}
++	snd_printk(KERN_DEBUG "\n");
++	snd_printk(KERN_DEBUG
++		   "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
++		   rec->zero_times_saved, rec->zero_times_notsaved);
++	/* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
++	i = 0;
++	for (j = 0; j < 3750; j++) {
++		if (rec->zero_times[j] != 0) {
++			snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
++			i++;
++		}
++		if (((i % 8) == 0) && (i != 0))
++			snd_printk(KERN_DEBUG "\n");
++	}
++	snd_printk(KERN_DEBUG "\n"); */
++	return;
++}
++#endif
++
++/*
++ * _internal_ helper function for playback/capture transfer function
++ */
++static void
++snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
++				       struct snd_pcm_indirect2 *rec,
++				       int isplay, int iscopy,
++				       unsigned int bytes)
++{
++	if (rec->min_periods >= 0) {
++		if (iscopy) {
++			rec->sw_io += bytes;
++			if (rec->sw_io >= rec->sw_buffer_size)
++				rec->sw_io -= rec->sw_buffer_size;
++		} else if (isplay) {
++			/* If application does not write data in multiples of
++			 * a period, move sw_data to the next correctly aligned
++			 * position, so that sw_io can converge to it (in the
++			 * next step).
++			 */
++			if (!rec->check_alignment) {
++				if (rec->bytes2hw %
++				    snd_pcm_lib_period_bytes(substream)) {
++					unsigned bytes2hw_aligned =
++					    (1 +
++					     (rec->bytes2hw /
++					      snd_pcm_lib_period_bytes
++					      (substream))) *
++					    snd_pcm_lib_period_bytes
++					    (substream);
++					rec->sw_data =
++					    bytes2hw_aligned %
++					    rec->sw_buffer_size;
++#ifdef SND_PCM_INDIRECT2_STAT
++					snd_printk(KERN_DEBUG
++						   "STAT: @re-align: aligned "
++						   "bytes2hw to next period "
++						   "size boundary: %d "
++						   "(instead of %d)\n",
++						   bytes2hw_aligned,
++						   rec->bytes2hw);
++					snd_printk(KERN_DEBUG
++						   "STAT: @re-align: sw_data "
++						   "moves to: %d\n",
++						   rec->sw_data);
++#endif
++				}
++				rec->check_alignment = 1;
++			}
++			/* We are at the end and are copying zeros into the
++			 * fifo.
++			 * Now, we have to make sure that sw_io is increased
++			 * until the position of sw_data: Filling the fifo with
++			 * the first zeros means, the last bytes were played.
++			 */
++			if (rec->sw_io != rec->sw_data) {
++				unsigned int diff;
++				if (rec->sw_data > rec->sw_io)
++					diff = rec->sw_data - rec->sw_io;
++				else
++					diff = (rec->sw_buffer_size -
++						rec->sw_io) +
++						rec->sw_data;
++				if (bytes >= diff)
++					rec->sw_io = rec->sw_data;
++				else {
++					rec->sw_io += bytes;
++					if (rec->sw_io >= rec->sw_buffer_size)
++						rec->sw_io -=
++						    rec->sw_buffer_size;
++				}
++			}
++		}
++		rec->min_period_count += bytes;
++		if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
++			rec->min_periods += (rec->min_period_count /
++					     (rec->hw_buffer_size / 2));
++#ifdef SND_PCM_INDIRECT2_STAT
++			if ((rec->min_period_count /
++			     (rec->hw_buffer_size / 2)) > 7)
++				snd_printk(KERN_DEBUG
++					   "STAT: more than 7 (%d) min_adds "
++					   "at once - too big to save!\n",
++					   (rec->min_period_count /
++					    (rec->hw_buffer_size / 2)));
++			else
++				rec->min_adds[(rec->min_period_count /
++					       (rec->hw_buffer_size / 2))]++;
++#endif
++			rec->min_period_count = (rec->min_period_count %
++						 (rec->hw_buffer_size / 2));
++		}
++	} else if (isplay && iscopy)
++		rec->min_periods = 0;
++}
++
++/*
++ * helper function for playback/capture pointer callback
++ */
++snd_pcm_uframes_t
++snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
++			  struct snd_pcm_indirect2 *rec)
++{
++#ifdef SND_PCM_INDIRECT2_STAT
++	rec->pointer_calls++;
++#endif
++	return bytes_to_frames(substream->runtime, rec->sw_io);
++}
++
++/*
++ * _internal_ helper function for playback interrupt callback
++ */
++static void
++snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
++				    struct snd_pcm_indirect2 *rec,
++				    snd_pcm_indirect2_copy_t copy,
++				    snd_pcm_indirect2_zero_t zero)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
++
++	/* runtime->control->appl_ptr: position where ALSA will write next time
++	 * rec->appl_ptr: position where ALSA was last time
++	 * diff: obviously ALSA wrote that much bytes into the intermediate
++	 * buffer since we checked last time
++	 */
++	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
++
++	if (diff) {
++#ifdef SND_PCM_INDIRECT2_STAT
++		rec->lastdifftime = jiffies;
++#endif
++		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
++			diff += runtime->boundary;
++		/* number of bytes "added" by ALSA increases the number of
++		 * bytes which are ready to "be transfered to HW"/"played"
++		 * Then, set rec->appl_ptr to not count bytes twice next time.
++		 */
++		rec->sw_ready += (int)frames_to_bytes(runtime, diff);
++		rec->appl_ptr = appl_ptr;
++	}
++	if (rec->hw_ready && (rec->sw_ready <= 0)) {
++		unsigned int bytes;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if (rec->firstzerotime == 0) {
++			rec->firstzerotime = jiffies;
++			snd_printk(KERN_DEBUG
++				   "STAT: @firstzerotime: mul_elapsed: %d, "
++				   "min_period_count: %d\n",
++				   rec->mul_elapsed, rec->min_period_count);
++			snd_printk(KERN_DEBUG
++				   "STAT: @firstzerotime: sw_io: %d, "
++				   "sw_data: %d, appl_ptr: %u\n",
++				   rec->sw_io, rec->sw_data,
++				   (unsigned int)appl_ptr);
++		}
++		if ((jiffies - rec->firstzerotime) < 3750) {
++			rec->zero_times[(jiffies - rec->firstzerotime)]++;
++			rec->zero_times_saved++;
++		} else
++			rec->zero_times_notsaved++;
++#endif
++		bytes = zero(substream, rec);
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		rec->zeros2hw += bytes;
++		if (bytes < 64)
++			rec->zero_sizes[bytes]++;
++		else
++			snd_printk(KERN_DEBUG
++				   "STAT: %d zero Bytes copied to hardware at "
++				   "once - too big to save!\n",
++				   bytes);
++#endif
++		snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
++						       bytes);
++		return;
++	}
++	while (rec->hw_ready && (rec->sw_ready > 0)) {
++		/* sw_to_end: max. number of bytes that can be read/take from
++		 * the current position (sw_data) in _one_ step
++		 */
++		unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
++
++		/* bytes: number of bytes we have available (for reading) */
++		unsigned int bytes = rec->sw_ready;
++
++		if (sw_to_end < bytes)
++			bytes = sw_to_end;
++		if (!bytes)
++			break;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if (rec->firstbytetime == 0)
++			rec->firstbytetime = jiffies;
++		rec->lastbytetime = jiffies;
++#endif
++		/* copy bytes from intermediate buffer position sw_data to the
++		 * HW and return number of bytes actually written
++		 * Furthermore, set hw_ready to 0, if the fifo isn't empty
++		 * now => more could be transfered to fifo
++		 */
++		bytes = copy(substream, rec, bytes);
++		rec->bytes2hw += bytes;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if (bytes < 64)
++			rec->byte_sizes[bytes]++;
++		else
++			snd_printk(KERN_DEBUG
++				   "STAT: %d Bytes copied to hardware at once "
++				   "- too big to save!\n",
++				   bytes);
++#endif
++		/* increase sw_data by the number of actually written bytes
++		 * (= number of taken bytes from intermediate buffer)
++		 */
++		rec->sw_data += bytes;
++		if (rec->sw_data == rec->sw_buffer_size)
++			rec->sw_data = 0;
++		/* now sw_data is the position where ALSA is going to write
++		 * in the intermediate buffer next time = position we are going
++		 * to read from next time
++		 */
++
++		snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
++						       bytes);
++
++		/* we read bytes from intermediate buffer, so we need to say
++		 * that the number of bytes ready for transfer are decreased
++		 * now
++		 */
++		rec->sw_ready -= bytes;
++	}
++	return;
++}
++
++/*
++ * helper function for playback interrupt routine
++ */
++void
++snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
++				     struct snd_pcm_indirect2 *rec,
++				     snd_pcm_indirect2_copy_t copy,
++				     snd_pcm_indirect2_zero_t zero)
++{
++#ifdef SND_PCM_INDIRECT2_STAT
++	rec->irq_occured++;
++#endif
++	/* hardware played some bytes, so there is room again (in fifo) */
++	rec->hw_ready = 1;
++
++	/* don't call ack() now, instead call transfer() function directly
++	 * (normally called by ack() )
++	 */
++	snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
++
++	if (rec->min_periods >= rec->min_multiple) {
++#ifdef SND_PCM_INDIRECT2_STAT
++		if ((rec->min_periods / rec->min_multiple) > 7)
++			snd_printk(KERN_DEBUG
++				   "STAT: more than 7 (%d) mul_adds - too big "
++				   "to save!\n",
++				   (rec->min_periods / rec->min_multiple));
++		else
++			rec->mul_adds[(rec->min_periods /
++				       rec->min_multiple)]++;
++		rec->mul_elapsed_real += (rec->min_periods /
++					  rec->min_multiple);
++		rec->mul_elapsed++;
++#endif
++		rec->min_periods = (rec->min_periods % rec->min_multiple);
++		snd_pcm_period_elapsed(substream);
++	}
++}
++
++/*
++ * _internal_ helper function for capture interrupt callback
++ */
++static void
++snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
++				   struct snd_pcm_indirect2 *rec,
++				   snd_pcm_indirect2_copy_t copy,
++				   snd_pcm_indirect2_zero_t null)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
++	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
++
++	if (diff) {
++#ifdef SND_PCM_INDIRECT2_STAT
++		rec->lastdifftime = jiffies;
++#endif
++		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
++			diff += runtime->boundary;
++		rec->sw_ready -= frames_to_bytes(runtime, diff);
++		rec->appl_ptr = appl_ptr;
++	}
++	/* if hardware has something, but the intermediate buffer is full
++	 * => skip contents of buffer
++	 */
++	if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
++		unsigned int bytes;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if (rec->firstzerotime == 0) {
++			rec->firstzerotime = jiffies;
++			snd_printk(KERN_DEBUG "STAT: (capture) "
++				   "@firstzerotime: mul_elapsed: %d, "
++				   "min_period_count: %d\n",
++				   rec->mul_elapsed, rec->min_period_count);
++			snd_printk(KERN_DEBUG "STAT: (capture) "
++				   "@firstzerotime: sw_io: %d, sw_data: %d, "
++				   "appl_ptr: %u\n",
++				   rec->sw_io, rec->sw_data,
++				   (unsigned int)appl_ptr);
++		}
++		if ((jiffies - rec->firstzerotime) < 3750) {
++			rec->zero_times[(jiffies - rec->firstzerotime)]++;
++			rec->zero_times_saved++;
++		} else
++			rec->zero_times_notsaved++;
++#endif
++		bytes = null(substream, rec);
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		rec->zeros2hw += bytes;
++		if (bytes < 64)
++			rec->zero_sizes[bytes]++;
++		else
++			snd_printk(KERN_DEBUG
++				   "STAT: (capture) %d zero Bytes copied to "
++				   "hardware at once - too big to save!\n",
++				   bytes);
++#endif
++		snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
++						       bytes);
++		/* report an overrun */
++		rec->sw_io = SNDRV_PCM_POS_XRUN;
++		return;
++	}
++	while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
++		/* sw_to_end: max. number of bytes that we can write to the
++		 *  intermediate buffer (until it's end)
++		 */
++		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
++
++		/* bytes: max. number of bytes, which may be copied to the
++		 *  intermediate buffer without overflow (in _one_ step)
++		 */
++		size_t bytes = rec->sw_buffer_size - rec->sw_ready;
++
++		/* limit number of bytes (for transfer) by available room in
++		 * the intermediate buffer
++		 */
++		if (sw_to_end < bytes)
++			bytes = sw_to_end;
++		if (!bytes)
++			break;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if (rec->firstbytetime == 0)
++			rec->firstbytetime = jiffies;
++		rec->lastbytetime = jiffies;
++#endif
++		/* copy bytes from the intermediate buffer (position sw_data)
++		 * to the HW at most and return number of bytes actually copied
++		 * from HW
++		 * Furthermore, set hw_ready to 0, if the fifo is empty now.
++		 */
++		bytes = copy(substream, rec, bytes);
++		rec->bytes2hw += bytes;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if (bytes < 64)
++			rec->byte_sizes[bytes]++;
++		else
++			snd_printk(KERN_DEBUG
++				   "STAT: (capture) %d Bytes copied to "
++				   "hardware at once - too big to save!\n",
++				   bytes);
++#endif
++		/* increase sw_data by the number of actually copied bytes from
++		 * HW
++		 */
++		rec->sw_data += bytes;
++		if (rec->sw_data == rec->sw_buffer_size)
++			rec->sw_data = 0;
++
++		snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
++						       bytes);
++
++		/* number of bytes in the intermediate buffer, which haven't
++		 * been fetched by ALSA yet.
++		 */
++		rec->sw_ready += bytes;
++	}
++	return;
++}
++
++/*
++ * helper function for capture interrupt routine
++ */
++void
++snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
++				    struct snd_pcm_indirect2 *rec,
++				    snd_pcm_indirect2_copy_t copy,
++				    snd_pcm_indirect2_zero_t null)
++{
++#ifdef SND_PCM_INDIRECT2_STAT
++	rec->irq_occured++;
++#endif
++	/* hardware recorded some bytes, so there is something to read from the
++	 * record fifo:
++	 */
++	rec->hw_ready = 1;
++
++	/* don't call ack() now, instead call transfer() function directly
++	 * (normally called by ack() )
++	 */
++	snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
++
++	if (rec->min_periods >= rec->min_multiple) {
++
++#ifdef SND_PCM_INDIRECT2_STAT
++		if ((rec->min_periods / rec->min_multiple) > 7)
++			snd_printk(KERN_DEBUG
++				   "STAT: more than 7 (%d) mul_adds - "
++				   "too big to save!\n",
++				   (rec->min_periods / rec->min_multiple));
++		else
++			rec->mul_adds[(rec->min_periods /
++				       rec->min_multiple)]++;
++		rec->mul_elapsed_real += (rec->min_periods /
++					  rec->min_multiple);
++		rec->mul_elapsed++;
++#endif
++		rec->min_periods = (rec->min_periods % rec->min_multiple);
++		snd_pcm_period_elapsed(substream);
++	}
++}
+diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h
+new file mode 100644
+index 0000000..2ea6e46
+--- /dev/null
++++ b/sound/drivers/pcm-indirect2.h
+@@ -0,0 +1,140 @@
++/*
++ * Helper functions for indirect PCM data transfer to a simple FIFO in
++ * hardware (small, no possibility to read "hardware io position",
++ * updating position done by interrupt, ...)
++ *
++ *  Copyright (c) by 2007  Joachim Foerster <JOFT at gmx.de>
++ *
++ *  Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
++ *
++ *  Copyright (c) by Takashi Iwai <tiwai at suse.de>
++ *                   Jaroslav Kysela <perex at suse.cz>
++ *
++ *   This program 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 program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef __SOUND_PCM_INDIRECT2_H
++#define __SOUND_PCM_INDIRECT2_H
++
++/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */
++#include <sound/pcm.h>
++
++/* Debug options for code which may be removed completely in a final version */
++#ifdef CONFIG_SND_DEBUG
++#define SND_PCM_INDIRECT2_STAT    /* turn on some "statistics" about the
++				   * process of copying bytes from the
++				   * intermediate buffer to the hardware
++				   * fifo and the other way round
++				   */
++#endif
++
++struct snd_pcm_indirect2 {
++	unsigned int hw_buffer_size;  /* Byte size of hardware buffer */
++	int hw_ready;		      /* playback: 1 = hw fifo has room left,
++				       * 0 = hw fifo is full
++				       */
++	unsigned int min_multiple;
++	int min_periods;	      /* counts number of min. periods until
++				       * min_multiple is reached
++				       */
++	int min_period_count;	      /* counts bytes to count number of
++				       * min. periods
++				       */
++
++	unsigned int sw_buffer_size;  /* Byte size of software buffer */
++
++	/* sw_data: position in intermediate buffer, where we will read (or
++	 *          write) from/to next time (to transfer data to/from HW)
++	 */
++	unsigned int sw_data;         /* Offset to next dst (or src) in sw
++				       * ring buffer
++				       */
++	/* easiest case (playback):
++	 * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the
++	 * exception that sw_data is "behind" by the number if bytes ALSA wrote
++	 * to the intermediate buffer last time.
++	 * A call to ack() callback synchronizes both indirectly.
++	 */
++
++	/* We have no real sw_io pointer here. Usually sw_io is pointing to the
++	 * current playback/capture position _inside_ the hardware. Devices
++	 * with plain FIFOs often have no possibility to publish this position.
++	 * So we say: if sw_data is updated, that means bytes were copied to
++	 * the hardware, we increase sw_io by that amount, because there have
++	 * to be as much bytes which were played. So sw_io will stay behind
++	 * sw_data all the time and has to converge to sw_data at the end of
++	 * playback.
++	 */
++	unsigned int sw_io;           /* Current software pointer in bytes */
++
++	/* sw_ready: number of bytes ALSA copied to the intermediate buffer, so
++	 * it represents the number of bytes which wait for transfer to the HW
++	 */
++	int sw_ready;		  /* Bytes ready to be transferred to/from hw */
++
++	/* appl_ptr: last known position of ALSA (where ALSA is going to write
++	 * next time into the intermediate buffer
++	 */
++	snd_pcm_uframes_t appl_ptr;   /* Last seen appl_ptr */
++
++	unsigned int bytes2hw;
++	int check_alignment;
++
++#ifdef SND_PCM_INDIRECT2_STAT
++	unsigned int zeros2hw;
++	unsigned int mul_elapsed;
++	unsigned int mul_elapsed_real;
++	unsigned long firstbytetime;
++	unsigned long lastbytetime;
++	unsigned long firstzerotime;
++	unsigned int byte_sizes[64];
++	unsigned int zero_sizes[64];
++	unsigned int min_adds[8];
++	unsigned int mul_adds[8];
++	unsigned int zero_times[3750];	/* = 15s */
++	unsigned int zero_times_saved;
++	unsigned int zero_times_notsaved;
++	unsigned int irq_occured;
++	unsigned int pointer_calls;
++	unsigned int lastdifftime;
++#endif
++};
++
++typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream,
++					   struct snd_pcm_indirect2 *rec,
++					   size_t bytes);
++typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream,
++					   struct snd_pcm_indirect2 *rec);
++
++#ifdef SND_PCM_INDIRECT2_STAT
++void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
++				   struct snd_pcm_indirect2 *rec);
++#endif
++
++snd_pcm_uframes_t
++snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
++			  struct snd_pcm_indirect2 *rec);
++void
++snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
++				     struct snd_pcm_indirect2 *rec,
++				     snd_pcm_indirect2_copy_t copy,
++				     snd_pcm_indirect2_zero_t zero);
++void
++snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
++				    struct snd_pcm_indirect2 *rec,
++				    snd_pcm_indirect2_copy_t copy,
++				    snd_pcm_indirect2_zero_t null);
++
++#endif /* __SOUND_PCM_INDIRECT2_H */
+diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
+index 1b83287..b1c047e 100644
+--- a/sound/drivers/portman2x4.c
++++ b/sound/drivers/portman2x4.c
+@@ -37,7 +37,6 @@
+  *      - ported from alsa 0.5 to 1.0
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/parport.h>
+@@ -797,6 +796,8 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
+ 
+ 	platform_set_drvdata(pdev, card);
+ 
++	snd_card_set_dev(card, &pdev->dev);
++
+ 	/* At this point card will be usable */
+ 	if ((err = snd_card_register(card)) < 0) {
+ 		snd_printd("Cannot register card\n");
+diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
+index 65de3a7..d8aab9d 100644
+--- a/sound/drivers/serial-u16550.c
++++ b/sound/drivers/serial-u16550.c
+@@ -30,7 +30,6 @@
+  *      More documentation can be found in serial-u16550.txt.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/err.h>
+@@ -43,6 +42,7 @@
+ #include <sound/initval.h>
+ 
+ #include <linux/serial_reg.h>
++#include <linux/jiffies.h>
+ 
+ #include <asm/io.h>
+ 
+@@ -455,7 +455,7 @@ static void snd_uart16550_do_open(struct snd_uart16550 * uart)
+ 		    | UART_IER_THRI	/* Enable Transmitter holding register empty interrupt */
+ 		    ;
+ 	}
+-	outb(byte, uart->base + UART_IER);	/* Interupt enable Register */
++	outb(byte, uart->base + UART_IER);	/* Interrupt enable Register */
+ 
+ 	inb(uart->base + UART_LSR);	/* Clear any pre-existing overrun indication */
+ 	inb(uart->base + UART_IIR);	/* Clear any pre-existing transmit interrupt */
+@@ -473,7 +473,7 @@ static void snd_uart16550_do_close(struct snd_uart16550 * uart)
+ 
+ 	outb((0 & UART_IER_RDI)		/* Disable Receiver data interrupt */
+ 	     |(0 & UART_IER_THRI)	/* Disable Transmitter holding register empty interrupt */
+-	     ,uart->base + UART_IER);	/* Interupt enable Register */
++	     ,uart->base + UART_IER);	/* Interrupt enable Register */
+ 
+ 	switch (uart->adaptor) {
+ 	default:
+@@ -653,7 +653,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
+ 	char first;
+ 	static unsigned long lasttime = 0;
+ 	
+-	/* Interupts are disabled during the updating of the tx_buff,
++	/* Interrupts are disabled during the updating of the tx_buff,
+ 	 * since it is 'bad' to have two processes updating the same
+ 	 * variables (ie buff_in & buff_out)
+ 	 */
+@@ -694,7 +694,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
+ 			    (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS ||
+ 			     uart->adaptor == SNDRV_SERIAL_GENERIC) &&
+ 			    (uart->prev_out != substream->number ||
+-			     jiffies-lasttime > 3*HZ)) {
++			     time_after(jiffies, lasttime + 3*HZ))) {
+ 
+ 				if (snd_uart16550_buffer_can_write(uart, 3)) {
+ 					/* Roland Soundcanvas part selection */
+diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
+index 915c867..f79e361 100644
+--- a/sound/drivers/virmidi.c
++++ b/sound/drivers/virmidi.c
+@@ -41,7 +41,6 @@
+  * - Run application using a midi device (eg. /dev/snd/midiC1D0)
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/wait.h>
+ #include <linux/err.h>
+diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c
+index 7a22134..9529e3b 100644
+--- a/sound/drivers/vx/vx_cmd.c
++++ b/sound/drivers/vx/vx_cmd.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/vx_core.h>
+diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
+index ed19bc1..9953886 100644
+--- a/sound/drivers/vx/vx_core.c
++++ b/sound/drivers/vx/vx_core.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
+index 9a8154c..1dfe694 100644
+--- a/sound/drivers/vx/vx_hwdep.c
++++ b/sound/drivers/vx/vx_hwdep.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/device.h>
+ #include <linux/firmware.h>
+ #include <linux/vmalloc.h>
+diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
+index b8fcd79..5a34732 100644
+--- a/sound/drivers/vx/vx_mixer.c
++++ b/sound/drivers/vx/vx_mixer.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/tlv.h>
+@@ -439,14 +438,19 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
+ {
+ 	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
+ 	int codec = kcontrol->id.index;
++	unsigned int val[2], vmax;
++
++	vmax = chip->hw->output_level_max;
++	val[0] = ucontrol->value.integer.value[0];
++	val[1] = ucontrol->value.integer.value[1];
++	if (val[0] > vmax || val[1] > vmax)
++		return -EINVAL;
+ 	mutex_lock(&chip->mixer_mutex);
+-	if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] ||
+-	    ucontrol->value.integer.value[1] != chip->output_level[codec][1]) {
+-		vx_set_analog_output_level(chip, codec,
+-					   ucontrol->value.integer.value[0],
+-					   ucontrol->value.integer.value[1]);
+-		chip->output_level[codec][0] = ucontrol->value.integer.value[0];
+-		chip->output_level[codec][1] = ucontrol->value.integer.value[1];
++	if (val[0] != chip->output_level[codec][0] ||
++	    val[1] != chip->output_level[codec][1]) {
++		vx_set_analog_output_level(chip, codec, val[0], val[1]);
++		chip->output_level[codec][0] = val[0];
++		chip->output_level[codec][1] = val[1];
+ 		mutex_unlock(&chip->mixer_mutex);
+ 		return 1;
+ 	}
+@@ -506,6 +510,14 @@ static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
+ static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
++
++	if (chip->type >= VX_TYPE_VXPOCKET) {
++		if (ucontrol->value.enumerated.item[0] > 2)
++			return -EINVAL;
++	} else {
++		if (ucontrol->value.enumerated.item[0] > 1)
++			return -EINVAL;
++	}
+ 	mutex_lock(&chip->mixer_mutex);
+ 	if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) {
+ 		chip->audio_source_target = ucontrol->value.enumerated.item[0];
+@@ -554,6 +566,9 @@ static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
+ static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
++
++	if (ucontrol->value.enumerated.item[0] > 2)
++		return -EINVAL;
+ 	mutex_lock(&chip->mixer_mutex);
+ 	if (chip->clock_mode != ucontrol->value.enumerated.item[0]) {
+ 		chip->clock_mode = ucontrol->value.enumerated.item[0];
+@@ -603,12 +618,17 @@ static int vx_audio_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
+ 	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
+ 	int audio = kcontrol->private_value & 0xff;
+ 	int capture = (kcontrol->private_value >> 8) & 1;
++	unsigned int val[2];
+ 
++	val[0] = ucontrol->value.integer.value[0];
++	val[1] = ucontrol->value.integer.value[1];
++	if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
++		return -EINVAL;
+ 	mutex_lock(&chip->mixer_mutex);
+-	if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] ||
+-	    ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) {
+-		vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]);
+-		vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]);
++	if (val[0] != chip->audio_gain[capture][audio] ||
++	    val[1] != chip->audio_gain[capture][audio+1]) {
++		vx_set_audio_gain(chip, audio, capture, val[0]);
++		vx_set_audio_gain(chip, audio+1, capture, val[1]);
+ 		mutex_unlock(&chip->mixer_mutex);
+ 		return 1;
+ 	}
+@@ -632,13 +652,19 @@ static int vx_audio_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
+ {
+ 	struct vx_core *chip = snd_kcontrol_chip(kcontrol);
+ 	int audio = kcontrol->private_value & 0xff;
++	unsigned int val[2];
++
++	val[0] = ucontrol->value.integer.value[0];
++	val[1] = ucontrol->value.integer.value[1];
++	if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
++		return -EINVAL;
+ 
+ 	mutex_lock(&chip->mixer_mutex);
+-	if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] ||
+-	    ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) {
+-		vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0],
++	if (val[0] != chip->audio_monitor[audio] ||
++	    val[1] != chip->audio_monitor[audio+1]) {
++		vx_set_monitor_level(chip, audio, val[0],
+ 				     chip->audio_monitor_active[audio]);
+-		vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1],
++		vx_set_monitor_level(chip, audio+1, val[1],
+ 				     chip->audio_monitor_active[audio+1]);
+ 		mutex_unlock(&chip->mixer_mutex);
+ 		return 1;
+@@ -669,8 +695,10 @@ static int vx_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
+ 	mutex_lock(&chip->mixer_mutex);
+ 	if (ucontrol->value.integer.value[0] != chip->audio_active[audio] ||
+ 	    ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) {
+-		vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]);
+-		vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]);
++		vx_set_audio_switch(chip, audio,
++				    !!ucontrol->value.integer.value[0]);
++		vx_set_audio_switch(chip, audio+1,
++				    !!ucontrol->value.integer.value[1]);
+ 		mutex_unlock(&chip->mixer_mutex);
+ 		return 1;
+ 	}
+@@ -699,9 +727,9 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
+ 	if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] ||
+ 	    ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) {
+ 		vx_set_monitor_level(chip, audio, chip->audio_monitor[audio],
+-				     ucontrol->value.integer.value[0]);
++				     !!ucontrol->value.integer.value[0]);
+ 		vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1],
+-				     ucontrol->value.integer.value[1]);
++				     !!ucontrol->value.integer.value[1]);
+ 		mutex_unlock(&chip->mixer_mutex);
+ 		return 1;
+ 	}
+diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
+index 7e65a10..fdbf865 100644
+--- a/sound/drivers/vx/vx_pcm.c
++++ b/sound/drivers/vx/vx_pcm.c
+@@ -45,7 +45,6 @@
+  *  - scheduled action on the stream.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+ #include <linux/delay.h>
+diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
+index 7400306..fb8932a 100644
+--- a/sound/drivers/vx/vx_uer.c
++++ b/sound/drivers/vx/vx_uer.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <sound/core.h>
+ #include <sound/vx_core.h>
+diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
+index 744366b..e57e9cb 100644
+--- a/sound/i2c/cs8427.c
++++ b/sound/i2c/cs8427.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
+index 1e58a96..b1e74e4 100644
+--- a/sound/i2c/i2c.c
++++ b/sound/i2c/i2c.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
+index b074fdd..bfa5d2c 100644
+--- a/sound/i2c/l3/uda1341.c
++++ b/sound/i2c/l3/uda1341.c
+@@ -19,7 +19,6 @@
+ 
+ /* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */
+ 
+-#include <sound/driver.h>
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
+diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
+index facde46..15061bd 100644
+--- a/sound/i2c/other/ak4114.c
++++ b/sound/i2c/other/ak4114.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <sound/core.h>
+diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
+index ee1585a..f350835 100644
+--- a/sound/i2c/other/ak4117.c
++++ b/sound/i2c/other/ak4117.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <sound/core.h>
+diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
+index de03f68..35fbbf2 100644
+--- a/sound/i2c/other/ak4xxx-adda.c
++++ b/sound/i2c/other/ak4xxx-adda.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -293,6 +292,11 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
+ 	case SND_AK5365:
+ 		/* FIXME: any init sequence? */
+ 		return;
++	case NON_AKM:
++		/* fake value for non-akm codecs using akm infrastructure
++		 * (e.g. of ice1724) - certainly FIXME
++		 */
++		return;
+ 	default:
+ 		snd_BUG();
+ 		return;
+@@ -377,8 +381,11 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
+ static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
+ 				  struct snd_ctl_elem_value *ucontrol)
+ {
+-	return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
+-			  ucontrol->value.integer.value[0]);
++	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
++	unsigned int val = ucontrol->value.integer.value[0];
++	if (val > mask)
++		return -EINVAL;
++	return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value), val);
+ }
+ 
+ static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
+@@ -409,11 +416,16 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
+ 					 struct snd_ctl_elem_value *ucontrol)
+ {
+ 	int addr = AK_GET_ADDR(kcontrol->private_value);
++	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
++	unsigned int val[2];
+ 	int change;
+ 
+-	change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
+-	change |= put_ak_reg(kcontrol, addr + 1,
+-			     ucontrol->value.integer.value[1]);
++	val[0] = ucontrol->value.integer.value[0];
++	val[1] = ucontrol->value.integer.value[1];
++	if (val[0] > mask || val[1] > mask)
++		return -EINVAL;
++	change = put_ak_reg(kcontrol, addr, val[0]);
++	change |= put_ak_reg(kcontrol, addr + 1, val[1]);
+ 	return change;
+ }
+ 
+@@ -508,6 +520,18 @@ static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
+ 
+ #define AK5365_NUM_INPUTS 5
+ 
++static int ak4xxx_capture_num_inputs(struct snd_akm4xxx *ak, int mixer_ch)
++{
++	int num_names;
++	const char **input_names;
++
++	input_names = ak->adc_info[mixer_ch].input_names;
++	num_names = 0;
++	while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
++		++num_names;
++	return num_names;
++}
++
+ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
+ 				      struct snd_ctl_elem_info *uinfo)
+ {
+@@ -516,18 +540,16 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
+ 	const char **input_names;
+ 	int  num_names, idx;
+ 
+-	input_names = ak->adc_info[mixer_ch].input_names;
+-
+-	num_names = 0;
+-	while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
+-		++num_names;
+-	
++	num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
++	if (!num_names)
++		return -EINVAL;
+ 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ 	uinfo->count = 1;
+ 	uinfo->value.enumerated.items = num_names;
+ 	idx = uinfo->value.enumerated.item;
+ 	if (idx >= num_names)
+ 		return -EINVAL;
++	input_names = ak->adc_info[mixer_ch].input_names;
+ 	strncpy(uinfo->value.enumerated.name, input_names[idx],
+ 		sizeof(uinfo->value.enumerated.name));
+ 	return 0;
+@@ -551,10 +573,15 @@ static int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol,
+ 				     struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
++	int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
+ 	int chip = AK_GET_CHIP(kcontrol->private_value);
+ 	int addr = AK_GET_ADDR(kcontrol->private_value);
+ 	int mask = AK_GET_MASK(kcontrol->private_value);
+ 	unsigned char oval, val;
++	int num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
++
++	if (ucontrol->value.enumerated.item[0] >= num_names)
++		return -EINVAL;
+ 
+ 	oval = snd_akm4xxx_get(ak, chip, addr);
+ 	val = oval & ~mask;
+diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c
+index 00c83d8..797d3a6 100644
+--- a/sound/i2c/other/pt2258.c
++++ b/sound/i2c/other/pt2258.c
+@@ -19,7 +19,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/tlv.h>
+@@ -113,6 +112,8 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
+ 
+ 	val0 = 79 - ucontrol->value.integer.value[0];
+ 	val1 = 79 - ucontrol->value.integer.value[1];
++	if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79)
++		return -EINVAL;
+ 	if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
+ 		return 0;
+ 
+diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
+index 37c47fb..87e3aef 100644
+--- a/sound/i2c/other/tea575x-tuner.c
++++ b/sound/i2c/other/tea575x-tuner.c
+@@ -20,7 +20,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -159,6 +158,10 @@ static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
+ 			struct video_audio v;
+ 			if(copy_from_user(&v, arg, sizeof(v))) 
+ 				return -EFAULT;	
++			if (tea->ops->mute)
++				tea->ops->mute(tea,
++					       (v.flags &
++						VIDEO_AUDIO_MUTE) ? 1 : 0);
+ 			if(v.audio) 
+ 				return -EINVAL;
+ 			return 0;
+@@ -206,6 +209,10 @@ void snd_tea575x_init(struct snd_tea575x *tea)
+ 	tea->freq = 90500 * 16;		/* 90.5Mhz default */
+ 
+ 	snd_tea575x_set_freq(tea);
++
++	/* mute on init */
++	if (tea->ops->mute)
++		tea->ops->mute(tea, 1);
+ }
+ 
+ void snd_tea575x_exit(struct snd_tea575x *tea)
+diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c
+index 9bab744..0e3a9f2 100644
+--- a/sound/i2c/tea6330t.c
++++ b/sound/i2c/tea6330t.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
+index fc88a31..68f1260 100644
+--- a/sound/isa/ad1816a/ad1816a.c
++++ b/sound/isa/ad1816a/ad1816a.c
+@@ -18,7 +18,6 @@
+     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+ #include <linux/wait.h>
+@@ -61,20 +60,6 @@ module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard.");
+ module_param_array(enable, bool, NULL, 0444);
+ MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
+-module_param_array(port, long, NULL, 0444);
+-MODULE_PARM_DESC(port, "Port # for ad1816a driver.");
+-module_param_array(mpu_port, long, NULL, 0444);
+-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver.");
+-module_param_array(fm_port, long, NULL, 0444);
+-MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver.");
+-module_param_array(irq, int, NULL, 0444);
+-MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver.");
+-module_param_array(mpu_irq, int, NULL, 0444);
+-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver.");
+-module_param_array(dma1, int, NULL, 0444);
+-MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver.");
+-module_param_array(dma2, int, NULL, 0444);
+-MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver.");
+ module_param_array(clockfreq, int, NULL, 0444);
+ MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
+ 
+@@ -117,16 +102,12 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
+ 					  const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+ 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->dev == NULL) {
+-		kfree(cfg);
++	if (acard->dev == NULL)
+ 		return -EBUSY;
+-	}
++
+ 	acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
+ 	if (acard->devmpu == NULL) {
+ 		mpu_port[dev] = -1;
+@@ -134,25 +115,10 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
+ 	}
+ 
+ 	pdev = acard->dev;
+-	pnp_init_resource_table(cfg);
+-
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], port[dev], 16);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
++
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
+-		kfree(cfg);
+ 		return -EBUSY;
+ 	}
+ 
+@@ -162,20 +128,11 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
+ 	dma2[dev] = pnp_dma(pdev, 1);
+ 	irq[dev] = pnp_irq(pdev, 0);
+ 
+-	if (acard->devmpu == NULL) {
+-		kfree(cfg);
++	if (acard->devmpu == NULL)
+ 		return 0;
+-	}
+-	pdev = acard->devmpu;
+-	pnp_init_resource_table(cfg);
+ 
+-	if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
+-	if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
++	pdev = acard->devmpu;
+ 
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
+@@ -186,7 +143,6 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
+ 		mpu_irq[dev] = pnp_irq(pdev, 0);
+ 	}
+ 
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
+index cf18fe4..4b8dfe2 100644
+--- a/sound/isa/ad1816a/ad1816a_lib.c
++++ b/sound/isa/ad1816a/ad1816a_lib.c
+@@ -17,7 +17,6 @@
+     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
+index a4710b5..5f5271e 100644
+--- a/sound/isa/ad1848/ad1848.c
++++ b/sound/isa/ad1848/ad1848.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
+index a901cd1..630c90f 100644
+--- a/sound/isa/ad1848/ad1848_lib.c
++++ b/sound/isa/ad1848/ad1848_lib.c
+@@ -20,7 +20,6 @@
+  */
+ 
+ #define SNDRV_MAIN_OBJECT_FILE
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -213,7 +212,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
+ 	for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
+ 		udelay(100);
+ 
+-	snd_printdd("(1) timeout = %d\n", timeout);
++	snd_printdd("(1) timeout = %ld\n", timeout);
+ 
+ #ifdef CONFIG_SND_DEBUG
+ 	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
+diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
+index d687207..efa8c80 100644
+--- a/sound/isa/adlib.c
++++ b/sound/isa/adlib.c
+@@ -2,7 +2,6 @@
+  * AdLib FM card driver.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/als100.c b/sound/isa/als100.c
+index f2bcfb2..f1ce30f 100644
+--- a/sound/isa/als100.c
++++ b/sound/isa/als100.c
+@@ -20,7 +20,6 @@
+     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/wait.h>
+ #include <linux/time.h>
+@@ -63,20 +62,6 @@ module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for als100 based soundcard.");
+ module_param_array(enable, bool, NULL, 0444);
+ MODULE_PARM_DESC(enable, "Enable als100 based soundcard.");
+-module_param_array(port, long, NULL, 0444);
+-MODULE_PARM_DESC(port, "Port # for als100 driver.");
+-module_param_array(mpu_port, long, NULL, 0444);
+-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver.");
+-module_param_array(fm_port, long, NULL, 0444);
+-MODULE_PARM_DESC(fm_port, "FM port # for als100 driver.");
+-module_param_array(irq, int, NULL, 0444);
+-MODULE_PARM_DESC(irq, "IRQ # for als100 driver.");
+-module_param_array(mpu_irq, int, NULL, 0444);
+-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver.");
+-module_param_array(dma8, int, NULL, 0444);
+-MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver.");
+-module_param_array(dma16, int, NULL, 0444);
+-MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver.");
+ 
+ struct snd_card_als100 {
+ 	int dev_no;
+@@ -111,38 +96,20 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
+ 					 const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+ 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->dev == NULL) {
+-		kfree(cfg);
++	if (acard->dev == NULL)
+ 		return -ENODEV;
+-	}
++
+ 	acard->devmpu = pnp_request_card_device(card, id->devs[1].id, acard->dev);
+ 	acard->devopl = pnp_request_card_device(card, id->devs[2].id, acard->dev);
+ 
+ 	pdev = acard->dev;
+ 
+-	pnp_init_resource_table(cfg);
+-
+-	/* override resources */
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-	if (dma8[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
+-	if (dma16[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 	port[dev] = pnp_port_start(pdev, 0);
+@@ -152,13 +119,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
+ 
+ 	pdev = acard->devmpu;
+ 	if (pdev != NULL) {
+-		pnp_init_resource_table(cfg);
+-		if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
+-		if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
+-			pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
+-		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-			snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0)
+ 			goto __mpu_error;
+@@ -176,11 +136,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
+ 
+ 	pdev = acard->devopl;
+ 	if (pdev != NULL) {
+-		pnp_init_resource_table(cfg);
+-		if (fm_port[dev] != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4);
+-		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-			snd_printk(KERN_ERR PFX "OPL3 the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0)
+ 			goto __fm_error;
+@@ -195,7 +150,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
+ 	     	fm_port[dev] = -1;
+ 	}
+ 
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
+index b615538..154e728 100644
+--- a/sound/isa/azt2320.c
++++ b/sound/isa/azt2320.c
+@@ -29,7 +29,6 @@
+     activation method (full-duplex audio!).
+ */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+@@ -72,22 +71,6 @@ module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
+ module_param_array(enable, bool, NULL, 0444);
+ MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
+-module_param_array(port, long, NULL, 0444);
+-MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
+-module_param_array(wss_port, long, NULL, 0444);
+-MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
+-module_param_array(mpu_port, long, NULL, 0444);
+-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
+-module_param_array(fm_port, long, NULL, 0444);
+-MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
+-module_param_array(irq, int, NULL, 0444);
+-MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
+-module_param_array(mpu_irq, int, NULL, 0444);
+-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
+-module_param_array(dma1, int, NULL, 0444);
+-MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
+-module_param_array(dma2, int, NULL, 0444);
+-MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
+ 
+ struct snd_card_azt2320 {
+ 	int dev_no;
+@@ -121,43 +104,19 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
+ 					  const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+-
+ 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->dev == NULL) {
+-		kfree(cfg);
++	if (acard->dev == NULL)
+ 		return -ENODEV;
+-	}
+ 
+ 	acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
+ 
+ 	pdev = acard->dev;
+-	pnp_init_resource_table(cfg);
+-
+-	/* override resources */
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+-	if (wss_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+ 
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 	port[dev] = pnp_port_start(pdev, 0);
+@@ -169,13 +128,6 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
+ 
+ 	pdev = acard->devmpu;
+ 	if (pdev != NULL) {
+-		pnp_init_resource_table(cfg);
+-		if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
+-		if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
+-			pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
+-		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-			snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0)
+ 			goto __mpu_error;
+@@ -191,7 +143,6 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
+ 	     	mpu_port[dev] = -1;
+ 	}
+ 
+-	kfree (cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
+index f471f8a..4d198ec 100644
+--- a/sound/isa/cmi8330.c
++++ b/sound/isa/cmi8330.c
+@@ -43,7 +43,6 @@
+  *  full control over both mixers.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -286,39 +285,21 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
+ 				     const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+ 	acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->cap == NULL) {
+-		kfree(cfg);
++	if (acard->cap == NULL)
+ 		return -EBUSY;
+-	}
++
+ 	acard->play = pnp_request_card_device(card, id->devs[1].id, NULL);
+-	if (acard->play == NULL) {
+-		kfree(cfg);
++	if (acard->play == NULL)
+ 		return -EBUSY;
+-	}
+ 
+ 	pdev = acard->cap;
+-	pnp_init_resource_table(cfg);
+-	/* allocate AD1848 resources */
+-	if (wssport[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], wssport[dev], 8);
+-	if (wssdma[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], wssdma[dev], 1);
+-	if (wssirq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], wssirq[dev], 1);
+-
+-	err = pnp_manual_config_dev(pdev, cfg, 0);
+-	if (err < 0)
+-		snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP manual resources are invalid, using auto config\n");
++
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP configure failure\n");
+-		kfree(cfg);
+ 		return -EBUSY;
+ 	}
+ 	wssport[dev] = pnp_port_start(pdev, 0);
+@@ -327,23 +308,10 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
+ 
+ 	/* allocate SB16 resources */
+ 	pdev = acard->play;
+-	pnp_init_resource_table(cfg);
+-	if (sbport[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], sbport[dev], 16);
+-	if (sbdma8[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], sbdma8[dev], 1);
+-	if (sbdma16[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], sbdma16[dev], 1);
+-	if (sbirq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], sbirq[dev], 1);
+-
+-	err = pnp_manual_config_dev(pdev, cfg, 0);
+-	if (err < 0)
+-		snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP manual resources are invalid, using auto config\n");
++
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n");
+-		kfree(cfg);
+ 		return -EBUSY;
+ 	}
+ 	sbport[dev] = pnp_port_start(pdev, 0);
+@@ -351,7 +319,6 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
+ 	sbdma16[dev] = pnp_dma(pdev, 1);
+ 	sbirq[dev] = pnp_irq(pdev, 0);
+ 
+-	kfree(cfg);
+ 	return 0;
+ }
+ #endif
+diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
+index 13db684..e9462b9 100644
+--- a/sound/isa/cs423x/cs4231.c
++++ b/sound/isa/cs423x/cs4231.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
+index a5eb965..0aa8649 100644
+--- a/sound/isa/cs423x/cs4231_lib.c
++++ b/sound/isa/cs423x/cs4231_lib.c
+@@ -24,7 +24,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/pm.h>
+ #include <linux/init.h>
+@@ -333,7 +332,6 @@ void snd_cs4231_mce_down(struct snd_cs4231 *chip)
+ 	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
+ 		return;
+ 	}
+-	snd_cs4231_busy_wait(chip);
+ 
+ 	/*
+ 	 * Wait for (possible -- during init auto-calibration may not be set)
+diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
+index 5784b43..dbe63db 100644
+--- a/sound/isa/cs423x/cs4236.c
++++ b/sound/isa/cs423x/cs4236.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -270,29 +269,9 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
+ MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids);
+ 
+ /* WSS initialization */
+-static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev,
+-					     struct pnp_resource_table *cfg)
++static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
+ {
+-	int err;
+-
+-	pnp_init_resource_table(cfg);
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 4);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT && fm_port[dev] > 0)
+-		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+-	if (sb_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], sb_port[dev], 16);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev] < 0 ? 4 : dma2[dev], 1);
+-	err = pnp_manual_config_dev(pdev, cfg, 0);
+-	if (err < 0)
+-		snd_printk(KERN_ERR IDENT " WSS PnP manual resources are invalid, using auto config\n");
+-	err = pnp_activate_dev(pdev);
+-	if (err < 0) {
++	if (pnp_activate_dev(pdev) < 0) {
+ 		printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n");
+ 		return -EBUSY;
+ 	}
+@@ -311,19 +290,9 @@ static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev,
+ }
+ 
+ /* CTRL initialization */
+-static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev,
+-					      struct pnp_resource_table *cfg)
++static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
+ {
+-	int err;
+-
+-	pnp_init_resource_table(cfg);
+-	if (cport[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], cport[dev], 8);
+-	err = pnp_manual_config_dev(pdev, cfg, 0);
+-	if (err < 0)
+-		snd_printk(KERN_ERR IDENT " CTRL PnP manual resources are invalid, using auto config\n");
+-	err = pnp_activate_dev(pdev);
+-	if (err < 0) {
++	if (pnp_activate_dev(pdev) < 0) {
+ 		printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");
+ 		return -EBUSY;
+ 	}
+@@ -333,21 +302,9 @@ static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev,
+ }
+ 
+ /* MPU initialization */
+-static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev,
+-					     struct pnp_resource_table *cfg)
++static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
+ {
+-	int err;
+-
+-	pnp_init_resource_table(cfg);
+-	if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
+-	if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0)
+-		pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
+-	err = pnp_manual_config_dev(pdev, cfg, 0);
+-	if (err < 0)
+-		snd_printk(KERN_ERR IDENT " MPU401 PnP manual resources are invalid, using auto config\n");
+-	err = pnp_activate_dev(pdev);
+-	if (err < 0) {
++	if (pnp_activate_dev(pdev) < 0) {
+ 		printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
+ 		mpu_port[dev] = SNDRV_AUTO_PORT;
+ 		mpu_irq[dev] = SNDRV_AUTO_IRQ;
+@@ -368,15 +325,8 @@ static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev,
+ static int __devinit snd_card_cs4232_pnp(int dev, struct snd_card_cs4236 *acard,
+ 					 struct pnp_dev *pdev)
+ {
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+-
+-	if (!cfg)
+-		return -ENOMEM;
+-	if (snd_cs423x_pnp_init_wss(dev, acard->wss, cfg) < 0) {
+-		kfree(cfg);
++	if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
+ 		return -EBUSY;
+-	}
+-	kfree(cfg);
+ 	cport[dev] = -1;
+ 	return 0;
+ }
+@@ -386,43 +336,33 @@ static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard
+ 					  struct pnp_card_link *card,
+ 					  const struct pnp_card_device_id *id)
+ {
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+-
+-	if (!cfg)
+-		return -ENOMEM;
+-
+ 	acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);
+ 	if (acard->wss == NULL)
+-		goto error;
++		return -EBUSY;
+ 	acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL);
+ 	if (acard->ctrl == NULL)
+-		goto error;
++		return -EBUSY;
+ 	if (id->devs[2].id[0]) {
+ 		acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);
+ 		if (acard->mpu == NULL)
+-			goto error;
++			return -EBUSY;
+ 	}
+ 
+ 	/* WSS initialization */
+-	if (snd_cs423x_pnp_init_wss(dev, acard->wss, cfg) < 0)
+-		goto error;
++	if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
++		return -EBUSY;
+ 
+ 	/* CTRL initialization */
+ 	if (acard->ctrl && cport[dev] > 0) {
+-		if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl, cfg) < 0)
+-			goto error;
++		if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl) < 0)
++			return -EBUSY;
+ 	}
+ 	/* MPU initialization */
+ 	if (acard->mpu && mpu_port[dev] > 0) {
+-		if (snd_cs423x_pnp_init_mpu(dev, acard->mpu, cfg) < 0)
+-			goto error;
++		if (snd_cs423x_pnp_init_mpu(dev, acard->mpu) < 0)
++			return -EBUSY;
+ 	}
+-	kfree(cfg);
+ 	return 0;
+-
+- error:
+-	kfree(cfg);
+-	return -EBUSY;
+ }
+ #endif /* CONFIG_PNP */
+ 
+diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
+index 6bd0644..de71910 100644
+--- a/sound/isa/cs423x/cs4236_lib.c
++++ b/sound/isa/cs423x/cs4236_lib.c
+@@ -79,7 +79,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c
+index ce57d52..a0242c3 100644
+--- a/sound/isa/dt019x.c
++++ b/sound/isa/dt019x.c
+@@ -21,7 +21,6 @@
+     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/wait.h>
+ #include <linux/pnp.h>
+@@ -56,18 +55,6 @@ module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard.");
+ module_param_array(enable, bool, NULL, 0444);
+ MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard.");
+-module_param_array(port, long, NULL, 0444);
+-MODULE_PARM_DESC(port, "Port # for dt019x driver.");
+-module_param_array(mpu_port, long, NULL, 0444);
+-MODULE_PARM_DESC(mpu_port, "MPU-401 port # for dt019x driver.");
+-module_param_array(fm_port, long, NULL, 0444);
+-MODULE_PARM_DESC(fm_port, "FM port # for dt019x driver.");
+-module_param_array(irq, int, NULL, 0444);
+-MODULE_PARM_DESC(irq, "IRQ # for dt019x driver.");
+-module_param_array(mpu_irq, int, NULL, 0444);
+-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for dt019x driver.");
+-module_param_array(dma8, int, NULL, 0444);
+-MODULE_PARM_DESC(dma8, "8-bit DMA # for dt019x driver.");
+ 
+ struct snd_card_dt019x {
+ 	struct pnp_dev *dev;
+@@ -95,36 +82,20 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
+ 					 const struct pnp_card_device_id *pid)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+-
+ 	acard->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
+-	if (acard->dev == NULL) {
+-		kfree (cfg);
++	if (acard->dev == NULL)
+ 		return -ENODEV;
+-	}
++
+ 	acard->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
+ 	acard->devopl = pnp_request_card_device(card, pid->devs[2].id, NULL);
+ 
+ 	pdev = acard->dev;
+-	pnp_init_resource_table(cfg);
+-
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-	if (dma8[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+ 
+-	if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-		snd_printk(KERN_ERR PFX "DT-019X AUDIO the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR PFX "DT-019X AUDIO pnp configure failure\n");
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 
+@@ -135,15 +106,7 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
+ 			port[dev],irq[dev],dma8[dev]);
+ 
+ 	pdev = acard->devmpu;
+-
+ 	if (pdev != NULL) {
+-		pnp_init_resource_table(cfg);
+-		if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
+-		if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
+-			pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
+-		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-			snd_printk(KERN_ERR PFX "DT-019X MPU401 the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0) {
+ 			pnp_release_card_device(pdev);
+@@ -162,11 +125,6 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
+ 
+ 	pdev = acard->devopl;
+ 	if (pdev != NULL) {
+-		pnp_init_resource_table(cfg);
+-		if (fm_port[dev] != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4);
+-		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-			snd_printk(KERN_ERR PFX "DT-019X OPL3 the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0) {
+ 			pnp_release_card_device(pdev);
+@@ -181,7 +139,6 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
+ 		fm_port[dev] = -1;
+ 	}
+ 
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
+index 74bbc92..f88639e 100644
+--- a/sound/isa/es1688/es1688.c
++++ b/sound/isa/es1688/es1688.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
+index 5c26d49..1e1e575 100644
+--- a/sound/isa/es1688/es1688_lib.c
++++ b/sound/isa/es1688/es1688_lib.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/delay.h>
+diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
+index c1af28f..90498e4 100644
+--- a/sound/isa/es18xx.c
++++ b/sound/isa/es18xx.c
+@@ -77,7 +77,6 @@
+  *   needed for ZV, so maybe the datasheet is entirely wrong here.
+  */
+  
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -163,7 +162,7 @@ struct snd_audiodrive {
+ #define ES18XX_DUPLEX_SAME 0x0010	/* Playback and record must share the same rate */
+ #define ES18XX_NEW_RATE	0x0020	/* More precise rate setting */
+ #define ES18XX_AUXB	0x0040	/* AuxB mixer control */
+-#define ES18XX_HWV	0x0080	/* Has seperate hardware volume mixer controls*/
++#define ES18XX_HWV	0x0080	/* Has separate hardware volume mixer controls*/
+ #define ES18XX_MONO	0x0100	/* Mono_in mixer control */
+ #define ES18XX_I2S	0x0200	/* I2S mixer control */
+ #define ES18XX_MUTEREC	0x0400	/* Record source can be muted */
+@@ -1442,6 +1441,8 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip)
+ 		snd_es18xx_write(chip, 0xB2, 0x50);
+ 		/* Enable MPU and hardware volume interrupt */
+ 		snd_es18xx_mixer_write(chip, 0x64, 0x42);
++		/* Enable ESS wavetable input */
++		snd_es18xx_mixer_bits(chip, 0x48, 0x10, 0x10);
+ 	}
+ 	else {
+ 		int irqmask, dma1mask, dma2mask;
+@@ -2035,31 +2036,9 @@ static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
+ MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
+ 
+ /* PnP main device initialization */
+-static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
+-						  struct pnp_resource_table *cfg)
++static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
+ {
+-	int err;
+-
+-	pnp_init_resource_table(cfg);
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+-	if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	if (pnp_device_is_isapnp(pdev)) {
+-		err = pnp_manual_config_dev(pdev, cfg, 0);
+-		if (err < 0)
+-			snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
+-	}
+-	err = pnp_activate_dev(pdev);
+-	if (err < 0) {
++	if (pnp_activate_dev(pdev) < 0) {
+ 		snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
+ 		return -EBUSY;
+ 	}
+@@ -2087,16 +2066,9 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
+ static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+ 					struct pnp_dev *pdev)
+ {
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+-
+-	if (!cfg)
+-		return -ENOMEM;
+ 	acard->dev = pdev;
+-	if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
+-		kfree(cfg);
++	if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+ 		return -EBUSY;
+-	}
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+@@ -2125,33 +2097,24 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
+ 					struct pnp_card_link *card,
+ 					const struct pnp_card_device_id *id)
+ {
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+-
+-	if (!cfg)
+-		return -ENOMEM;
+ 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->dev == NULL) {
+-		kfree(cfg);
++	if (acard->dev == NULL)
+ 		return -EBUSY;
+-	}
++
+ 	acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
+-	if (acard->devc == NULL) {
+-		kfree(cfg);
++	if (acard->devc == NULL)
+ 		return -EBUSY;
+-	}
++
+ 	/* Control port initialization */
+ 	if (pnp_activate_dev(acard->devc) < 0) {
+-		kfree(cfg);
+ 		snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
+ 		return -EAGAIN;
+ 	}
+ 	snd_printdd("pnp: port=0x%llx\n",
+ 			(unsigned long long)pnp_port_start(acard->devc, 0));
+-	if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
+-		kfree(cfg);
++	if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0)
+ 		return -EBUSY;
+-	}
+-	kfree(cfg);
++
+ 	return 0;
+ }
+ #endif /* CONFIG_PNP */
+diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
+index df3d59f..6cd4ee0 100644
+--- a/sound/isa/gus/Makefile
++++ b/sound/isa/gus/Makefile
+@@ -9,7 +9,6 @@ snd-gus-lib-objs := gus_main.o \
+ 		    gus_pcm.o gus_mixer.o \
+ 		    gus_uart.o \
+ 		    gus_reset.o
+-snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o
+ 
+ snd-gusclassic-objs := gusclassic.o
+ snd-gusextreme-objs := gusextreme.o
+@@ -17,20 +16,9 @@ snd-gusmax-objs := gusmax.o
+ snd-interwave-objs := interwave.o
+ snd-interwave-stb-objs := interwave-stb.o
+ 
+-#
+-# this function returns:
+-#   "m" - CONFIG_SND_SEQUENCER is m
+-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+-#   otherwise parameter #1 value
+-#
+-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+-
+ # Toplevel Module Dependency
+ obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
+ obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o
+ obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o
+ obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o
+ obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o
+-obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o
+-
+-obj-m := $(sort $(obj-m))
+diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
+index fc90514..f45f611 100644
+--- a/sound/isa/gus/gus_dma.c
++++ b/sound/isa/gus/gus_dma.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/dma.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
+index 9eaa932..fd2e2e2 100644
+--- a/sound/isa/gus/gus_dram.c
++++ b/sound/isa/gus/gus_dram.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/gus.h>
+diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c
+index bf137ea..4dc9caf 100644
+--- a/sound/isa/gus/gus_instr.c
++++ b/sound/isa/gus/gus_instr.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/gus.h>
+diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
+index 3d4f899..ca79878 100644
+--- a/sound/isa/gus/gus_io.c
++++ b/sound/isa/gus/gus_io.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
+index cd9a6f1..041894d 100644
+--- a/sound/isa/gus/gus_irq.c
++++ b/sound/isa/gus/gus_irq.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/info.h>
+ #include <sound/gus.h>
+diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
+index b14d5d6..cccc16c 100644
+--- a/sound/isa/gus/gus_main.c
++++ b/sound/isa/gus/gus_main.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/delay.h>
+@@ -104,12 +103,6 @@ static int snd_gus_free(struct snd_gus_card *gus)
+ {
+ 	if (gus->gf1.res_port2 == NULL)
+ 		goto __hw_end;
+-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+-	if (gus->seq_dev) {
+-		snd_device_free(gus->card, gus->seq_dev);
+-		gus->seq_dev = NULL;
+-	}
+-#endif
+ 	snd_gf1_stop(gus);
+ 	snd_gus_init_dma_irq(gus, 0);
+       __hw_end:
+@@ -408,14 +401,6 @@ static int snd_gus_check_version(struct snd_gus_card * gus)
+ 	return 0;
+ }
+ 
+-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+-static void snd_gus_seq_dev_free(struct snd_seq_device *seq_dev)
+-{
+-	struct snd_gus_card *gus = seq_dev->private_data;
+-	gus->seq_dev = NULL;
+-}
+-#endif
+-
+ int snd_gus_initialize(struct snd_gus_card *gus)
+ {
+ 	int err;
+@@ -430,15 +415,6 @@ int snd_gus_initialize(struct snd_gus_card *gus)
+ 	}
+ 	if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
+ 		return err;
+-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+-	if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS,
+-			       sizeof(struct snd_gus_card *), &gus->seq_dev) >= 0) {
+-		strcpy(gus->seq_dev->name, "GUS");
+-		*(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus;
+-		gus->seq_dev->private_data = gus;
+-		gus->seq_dev->private_free = snd_gus_seq_dev_free;
+-	}
+-#endif
+ 	snd_gf1_start(gus);
+ 	gus->initialized = 1;
+ 	return 0;
+diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
+index bcf4656..661205c 100644
+--- a/sound/isa/gus/gus_mem.c
++++ b/sound/isa/gus/gus_mem.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
+index f69a447..2803e22 100644
+--- a/sound/isa/gus/gus_mem_proc.c
++++ b/sound/isa/gus/gus_mem_proc.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+ #include <sound/gus.h>
+diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
+index a96253e..ebdb334 100644
+--- a/sound/isa/gus/gus_mixer.c
++++ b/sound/isa/gus/gus_mixer.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/wait.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
+index a7971f5..99731dc 100644
+--- a/sound/isa/gus/gus_pcm.c
++++ b/sound/isa/gus/gus_pcm.c
+@@ -25,7 +25,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/dma.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
+index 20cfdb8..3d1fed0 100644
+--- a/sound/isa/gus/gus_reset.c
++++ b/sound/isa/gus/gus_reset.c
+@@ -18,7 +18,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/time.h>
+diff --git a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c
+deleted file mode 100644
+index cba0829..0000000
+--- a/sound/isa/gus/gus_sample.c
++++ /dev/null
+@@ -1,165 +0,0 @@
+-/*
+- *  Routines for Gravis UltraSound soundcards - Sample support
+- *  Copyright (c) by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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/time.h>
+-#include <sound/core.h>
+-#include <sound/gus.h>
+-
+-/*
+- *
+- */
+-
+-static void select_instrument(struct snd_gus_card * gus, struct snd_gus_voice * v)
+-{
+-	struct snd_seq_kinstr *instr;
+-
+-#if 0
+-	printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n",
+-					v->instr.cluster,
+-					v->instr.std,
+-					v->instr.bank,
+-					v->instr.prg);
+-#endif
+-	instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1);
+-	if (instr != NULL) {
+-		if (instr->ops) {
+-			if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
+-				snd_gf1_simple_init(v);
+-		}
+-		snd_seq_instr_free_use(gus->gf1.ilist, instr);
+-	}
+-}
+-
+-/*
+- *
+- */
+-
+-static void event_sample(struct snd_seq_event *ev, struct snd_gus_port *p,
+-			 struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_stop)
+-		v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
+-	v->instr.std = ev->data.sample.param.sample.std;
+-	if (v->instr.std & 0xff000000) {        /* private instrument */
+-		v->instr.std &= 0x00ffffff;
+-		v->instr.std |= (unsigned int)ev->source.client << 24;
+-	}                                                
+-	v->instr.bank = ev->data.sample.param.sample.bank;
+-	v->instr.prg = ev->data.sample.param.sample.prg;
+-	select_instrument(p->gus, v);
+-}
+-
+-static void event_cluster(struct snd_seq_event *ev, struct snd_gus_port *p,
+-			  struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_stop)
+-		v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY);
+-	v->instr.cluster = ev->data.sample.param.cluster.cluster;
+-	select_instrument(p->gus, v);
+-}
+-
+-static void event_start(struct snd_seq_event *ev, struct snd_gus_port *p,
+-			struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_start)
+-		v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position);
+-}
+-
+-static void event_stop(struct snd_seq_event *ev, struct snd_gus_port *p,
+-		       struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_stop)
+-		v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode);
+-}
+-
+-static void event_freq(struct snd_seq_event *ev, struct snd_gus_port *p,
+-		       struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_freq)
+-		v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency);
+-}
+-
+-static void event_volume(struct snd_seq_event *ev, struct snd_gus_port *p,
+-			 struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_volume)
+-		v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume);
+-}
+-
+-static void event_loop(struct snd_seq_event *ev, struct snd_gus_port *p,
+-		       struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_loop)
+-		v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop);
+-}
+-
+-static void event_position(struct snd_seq_event *ev, struct snd_gus_port *p,
+-			   struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_pos)
+-		v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position);
+-}
+-
+-static void event_private1(struct snd_seq_event *ev, struct snd_gus_port *p,
+-			   struct snd_gus_voice *v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_private1)
+-		v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8);
+-}
+-
+-typedef void (gus_sample_event_handler_t)(struct snd_seq_event *ev,
+-					  struct snd_gus_port *p,
+-					  struct snd_gus_voice *v);
+-static gus_sample_event_handler_t *gus_sample_event_handlers[9] = {
+-	event_sample,
+-	event_cluster,
+-	event_start,
+-	event_stop,
+-	event_freq,
+-	event_volume,
+-	event_loop,
+-	event_position,
+-	event_private1
+-};
+-
+-void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p)
+-{
+-	int idx, voice;
+-	struct snd_gus_card *gus = p->gus;
+-	struct snd_gus_voice *v;
+-	unsigned long flags;
+-	
+-	idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+-	if (idx < 0 || idx > 8)
+-		return;
+-	for (voice = 0; voice < 32; voice++) {
+-		v = &gus->gf1.voices[voice];
+-		if (v->use && v->client == ev->source.client &&
+-		    v->port == ev->source.port &&
+-		    v->index == ev->data.sample.channel) {
+-		    	spin_lock_irqsave(&gus->event_lock, flags);
+-			gus_sample_event_handlers[idx](ev, p, v);
+-			spin_unlock_irqrestore(&gus->event_lock, flags);
+-			return;
+-		}
+-	}
+-}
+diff --git a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c
+deleted file mode 100644
+index 39d121e..0000000
+--- a/sound/isa/gus/gus_simple.c
++++ /dev/null
+@@ -1,634 +0,0 @@
+-/*
+- *  Routines for Gravis UltraSound soundcards - Simple instrument handlers
+- *  Copyright (c) by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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/time.h>
+-#include <sound/core.h>
+-#include <sound/gus.h>
+-#include "gus_tables.h"
+-
+-/*
+- *
+- */
+-
+-static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice);
+-static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice);
+-static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice);
+-
+-static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position);
+-static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode);
+-static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq);
+-static void sample_volume(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume);
+-static void sample_loop(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop);
+-static void sample_pos(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position);
+-static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data);
+-
+-static struct snd_gus_sample_ops sample_ops = {
+-	sample_start,
+-	sample_stop,
+-	sample_freq,
+-	sample_volume,
+-	sample_loop,
+-	sample_pos,
+-	sample_private1
+-};
+-
+-#if 0
+-
+-static void note_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int wait);
+-static void note_wait(struct snd_gus_card *gus, struct snd_gus_voice *voice);
+-static void note_off(struct snd_gus_card *gus, struct snd_gus_voice *voice);
+-static void note_volume(struct snd_gus_card *card, struct snd_gus_voice *voice);
+-static void note_pitchbend(struct snd_gus_card *card, struct snd_gus_voice *voice);
+-static void note_vibrato(struct snd_gus_card *card, struct snd_gus_voice *voice);
+-static void note_tremolo(struct snd_gus_card *card, struct snd_gus_voice *voice);
+-
+-static struct snd_gus_note_handlers note_commands = {
+-	note_stop,
+-	note_wait,
+-	note_off,
+-	note_volume,
+-	note_pitchbend,
+-	note_vibrato,
+-	note_tremolo
+-};
+-
+-static void chn_trigger_down(struct snd_gus_card *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority );
+-static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note );
+-static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 );
+-
+-static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = {
+-  chn_trigger_down,
+-  chn_trigger_up,
+-  chn_control
+-};
+-
+-#endif
+-
+-static void do_volume_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice);
+-static void do_pan_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice);
+-
+-/*
+- *
+- */
+-
+-static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice)
+-{
+-	spin_lock(&gus->event_lock);
+-	snd_gf1_stop_voice(gus, voice->number);
+-	spin_lock(&gus->reg_lock);
+-	snd_gf1_select_voice(gus, voice->number);
+-	snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
+-	spin_unlock(&gus->reg_lock);
+-	voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+-	spin_unlock(&gus->event_lock);
+-}
+-
+-static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice)
+-{
+-	spin_lock(&gus->event_lock);
+-	if (voice->flags & SNDRV_GF1_VFLG_RUNNING)
+-		do_volume_envelope(gus, voice);
+-	else
+-		snd_gf1_stop_voice(gus, voice->number);
+-	spin_unlock(&gus->event_lock);
+-}
+-
+-static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice)
+-{
+-	spin_lock(&gus->event_lock);
+-	if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) ==
+-	                    (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1))
+-		do_pan_envelope(gus, voice);
+-	spin_unlock(&gus->event_lock);
+-}
+-
+-/*
+- *
+- */
+-
+-static void do_volume_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice)
+-{
+-	unsigned short next, rate, old_volume;
+-	int program_next_ramp;
+-	unsigned long flags;
+-  
+-	if (!gus->gf1.volume_ramp) {
+-		spin_lock_irqsave(&gus->reg_lock, flags);
+-		snd_gf1_select_voice(gus, voice->number);
+-		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume);
+-		/* printk("gf1_volume = 0x%x\n", voice->gf1_volume); */
+-		spin_unlock_irqrestore(&gus->reg_lock, flags);
+-		return;
+-	}
+-	program_next_ramp = 0;
+-	rate = next = 0;
+-	while (1) {
+-		program_next_ramp = 0;
+-		rate = next = 0;
+-		switch (voice->venv_state) {
+-		case VENV_BEFORE:
+-			voice->venv_state = VENV_ATTACK;
+-			voice->venv_value_next = 0;
+-			spin_lock_irqsave(&gus->reg_lock, flags);
+-			snd_gf1_select_voice(gus, voice->number);
+-			snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+-			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
+-			spin_unlock_irqrestore(&gus->reg_lock, flags);
+-			break;
+-		case VENV_ATTACK:
+-			voice->venv_state = VENV_SUSTAIN;
+-			program_next_ramp++;
+-			next = 255;
+-			rate = gus->gf1.volume_ramp;
+-			break;
+-		case VENV_SUSTAIN:
+-			voice->venv_state = VENV_RELEASE;
+-			spin_lock_irqsave(&gus->reg_lock, flags);
+-			snd_gf1_select_voice(gus, voice->number);
+-			snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+- 			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255);
+-			spin_unlock_irqrestore(&gus->reg_lock, flags);
+-			return;
+-		case VENV_RELEASE:
+-			voice->venv_state = VENV_DONE;
+-			program_next_ramp++;
+-			next = 0;
+-			rate = gus->gf1.volume_ramp;
+-			break;
+-		case VENV_DONE:
+-			snd_gf1_stop_voice(gus, voice->number);
+-			voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+-			return;
+-		case VENV_VOLUME:
+-			program_next_ramp++;
+-			next = voice->venv_value_next;
+-			rate = gus->gf1.volume_ramp;
+-			voice->venv_state = voice->venv_state_prev;
+-			break;
+-		}
+-		voice->venv_value_next = next;
+-		if (!program_next_ramp)
+-			continue;
+-		spin_lock_irqsave(&gus->reg_lock, flags);
+-		snd_gf1_select_voice(gus, voice->number);
+-		snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+-		old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8;
+-		if (!rate) {
+-			spin_unlock_irqrestore(&gus->reg_lock, flags);			
+-			continue;
+-		}
+-		next = (((int)voice->gf1_volume * (int)next) / 255) >> 8;
+-		if (old_volume < SNDRV_GF1_MIN_OFFSET)
+-			old_volume = SNDRV_GF1_MIN_OFFSET;
+-		if (next < SNDRV_GF1_MIN_OFFSET)
+-			next = SNDRV_GF1_MIN_OFFSET;
+-		if (next > SNDRV_GF1_MAX_OFFSET)
+-			next = SNDRV_GF1_MAX_OFFSET;
+-		if (old_volume == next) {
+-			spin_unlock_irqrestore(&gus->reg_lock, flags);
+-			continue;
+-		}
+-		voice->volume_control &= ~0xc3;
+-		voice->volume_control |= 0x20;
+-		if (old_volume > next) {
+-			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next);
+-			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume);
+-			voice->volume_control |= 0x40;
+-		} else {
+-			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume);
+-			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next);
+-		}
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate);
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
+-		if (!gus->gf1.enh_mode) {
+-			snd_gf1_delay(gus);
+-			snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control);
+-		}
+-		spin_unlock_irqrestore(&gus->reg_lock, flags);			
+-		return;
+-	}
+-}
+-
+-static void do_pan_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice)
+-{
+-	unsigned long flags;
+-	unsigned char old_pan;
+-
+-#if 0
+-	snd_gf1_select_voice(gus, voice->number);
+-	printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
+-		voice->number,
+-		voice->flags,
+-		voice->gf1_pan,
+-		snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f);
+-#endif
+-	if (gus->gf1.enh_mode) {
+-		voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
+-		return;
+-	}
+-	if (!gus->gf1.smooth_pan) {
+-		spin_lock_irqsave(&gus->reg_lock, flags);			
+-		snd_gf1_select_voice(gus, voice->number);
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
+-		spin_unlock_irqrestore(&gus->reg_lock, flags);
+-		return;
+-	}
+-	if (!(voice->flags & SNDRV_GF1_VFLG_PAN))		/* before */
+-		voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN;
+-	spin_lock_irqsave(&gus->reg_lock, flags);			
+-	snd_gf1_select_voice(gus, voice->number);
+-	old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f;
+-	if (old_pan > voice->gf1_pan )
+-		old_pan--;
+-	if (old_pan < voice->gf1_pan)
+-		old_pan++;
+-	snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan);
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);
+-	if (old_pan == voice->gf1_pan)			/* the goal was reached */
+-		voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN);
+-#if 0
+-	snd_gf1_select_voice(gus, voice->number);
+-	printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n",
+-	       voice->number,
+-	       voice->flags,
+-	       voice->gf1_pan,
+-	       snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f);
+-#endif
+-}
+-
+-static void set_enhanced_pan(struct snd_gus_card *gus, struct snd_gus_voice *voice, unsigned short pan)
+-{
+-	unsigned long flags;
+-	unsigned short vlo, vro;
+-  
+-	vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan);
+-	vro = SNDRV_GF1_ATTEN(pan);
+-	if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) {
+-		vlo >>= 1;
+-		vro >>= 1;
+-	}
+-	vlo <<= 4;
+-	vro <<= 4;
+-#if 0
+-	printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n",
+-			vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT),
+-			vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT));
+-#endif
+-	spin_lock_irqsave(&gus->reg_lock, flags);			
+-	snd_gf1_select_voice(gus, voice->number);
+-        snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo);
+-	snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro);
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);			
+-	voice->vlo = vlo;
+-	voice->vro = vro;
+-}
+-
+-/*
+- *
+- */
+-
+-static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position)
+-{
+-	unsigned long flags;
+-	unsigned int begin, addr, addr_end, addr_start;
+-	int w_16;
+-	struct simple_instrument *simple;
+-	struct snd_seq_kinstr *instr;
+-
+-	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+-	if (instr == NULL)
+-		return;
+-	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+-	simple = KINSTR_DATA(instr);
+-	begin = simple->address.memory << 4;
+-	w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0;
+-	addr_start = simple->loop_start;
+-	if (simple->format & SIMPLE_WAVE_LOOP) {
+-		addr_end = simple->loop_end;
+-	} else {
+-		addr_end = (simple->size << 4) - (w_16 ? 40 : 24);
+-	}
+-	if (simple->format & SIMPLE_WAVE_BACKWARD) {
+-		addr = simple->loop_end;
+-		if (position < simple->loop_end)
+-			addr -= position;
+-	} else {
+-		addr = position;
+-	}
+-	voice->control = 0x00;
+-	voice->mode = 0x20;		/* enable offset registers */
+-	if (simple->format & SIMPLE_WAVE_16BIT)
+-		voice->control |= 0x04;
+-	if (simple->format & SIMPLE_WAVE_BACKWARD)
+-		voice->control |= 0x40;
+-	if (simple->format & SIMPLE_WAVE_LOOP) {
+-		voice->control |= 0x08;
+-	} else {
+-		voice->control |= 0x20;
+-	}
+-	if (simple->format & SIMPLE_WAVE_BIDIR)
+-		voice->control |= 0x10;
+-	if (simple->format & SIMPLE_WAVE_ULAW)
+-		voice->mode |= 0x40;
+-	if (w_16) {
+-		addr = ((addr << 1) & ~0x1f) | (addr & 0x0f);
+-		addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f);
+-		addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f);
+-	}
+-	addr += begin;
+-	addr_start += begin;
+-	addr_end += begin;
+-	snd_gf1_stop_voice(gus, voice->number);	
+-	spin_lock_irqsave(&gus->reg_lock, flags);
+-	snd_gf1_select_voice(gus, voice->number);
+-	snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
+-	voice->venv_state = VENV_BEFORE;
+-	voice->volume_control = 0x03;
+-	snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
+-	snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
+-	snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
+-	if (!gus->gf1.enh_mode) {
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan);
+-	} else {
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo);
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo);
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro);
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro);
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator);
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume);
+-		snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume);
+-	}
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);
+-	do_volume_envelope(gus, voice);
+-	spin_lock_irqsave(&gus->reg_lock, flags);
+-	snd_gf1_select_voice(gus, voice->number);
+-	if (gus->gf1.enh_mode)
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode);
+-	snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control);
+-	if (!gus->gf1.enh_mode) {
+-		snd_gf1_delay(gus);
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control );
+-	}
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);
+-#if 0
+-	snd_gf1_print_voice_registers(gus);
+-#endif
+-	voice->flags |= SNDRV_GF1_VFLG_RUNNING;
+-	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+-}
+-
+-static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode)
+-{
+-	unsigned char control;
+-	unsigned long flags;
+-
+-	if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING))
+-		return;
+-	switch (mode) {
+-	default:
+-		if (gus->gf1.volume_ramp > 0) {
+-			if (voice->venv_state < VENV_RELEASE) {
+-				voice->venv_state = VENV_RELEASE;
+-				do_volume_envelope(gus, voice);
+-			}
+-		}
+-		if (mode != SAMPLE_STOP_VENVELOPE) {
+-			snd_gf1_stop_voice(gus, voice->number);
+-			spin_lock_irqsave(&gus->reg_lock, flags);
+-			snd_gf1_select_voice(gus, voice->number);
+-			snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME);
+-			spin_unlock_irqrestore(&gus->reg_lock, flags);
+-			voice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+-		}
+-		break;
+-	case SAMPLE_STOP_LOOP:		/* disable loop only */
+-		spin_lock_irqsave(&gus->reg_lock, flags);
+-		snd_gf1_select_voice(gus, voice->number);
+-		control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
+-		control &= ~(0x83 | 0x04);
+-		control |= 0x20;
+-		snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control);
+-		spin_unlock_irqrestore(&gus->reg_lock, flags);
+-		break;
+-	}
+-}
+-
+-static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq)
+-{
+-	unsigned long flags;
+-
+-	spin_lock_irqsave(&gus->reg_lock, flags);
+-	voice->fc_register = snd_gf1_translate_freq(gus, freq);
+-	snd_gf1_select_voice(gus, voice->number);
+-	snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo);
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);
+-}
+-
+-static void sample_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume)
+-{
+-	if (volume->volume >= 0) {
+-		volume->volume &= 0x3fff;
+-		voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4;
+-		voice->venv_state_prev = VENV_SUSTAIN;
+-		voice->venv_state = VENV_VOLUME;
+-		do_volume_envelope(gus, voice);
+-        }
+-	if (volume->lr >= 0) {
+-		volume->lr &= 0x3fff;
+-		if (!gus->gf1.enh_mode) {
+-			voice->gf1_pan = (volume->lr >> 10) & 15;
+-			if (!gus->gf1.full_range_pan) {
+-				if (voice->gf1_pan == 0)
+-					voice->gf1_pan++;
+-				if (voice->gf1_pan == 15)
+-					voice->gf1_pan--;
+-			}
+-			voice->flags &= ~SNDRV_GF1_VFLG_PAN;	/* before */
+-			do_pan_envelope(gus, voice);
+-		} else {
+-			set_enhanced_pan(gus, voice, volume->lr >> 7);
+-		}
+-	}
+-}
+-
+-static void sample_loop(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop)
+-{
+-	unsigned long flags;
+-	int w_16 = voice->control & 0x04;
+-	unsigned int begin, addr_start, addr_end;
+-	struct simple_instrument *simple;
+-	struct snd_seq_kinstr *instr;
+-
+-#if 0
+-	printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
+-#endif
+-	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+-	if (instr == NULL)
+-		return;
+-	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+-	simple = KINSTR_DATA(instr);
+-	begin = simple->address.memory;
+-	addr_start = loop->start;
+-	addr_end = loop->end;
+-	addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin;
+-	addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin;
+-	spin_lock_irqsave(&gus->reg_lock, flags);
+-	snd_gf1_select_voice(gus, voice->number);
+-	snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16);
+-	snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16);
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);
+-	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+-}
+-
+-static void sample_pos(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position)
+-{
+-	unsigned long flags;
+-	int w_16 = voice->control & 0x04;
+-	unsigned int begin, addr;
+-	struct simple_instrument *simple;
+-	struct snd_seq_kinstr *instr;
+-
+-#if 0
+-	printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end);
+-#endif
+-	instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1);
+-	if (instr == NULL)
+-		return;
+-	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+-	simple = KINSTR_DATA(instr);
+-	begin = simple->address.memory;
+-	addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin;
+-	spin_lock_irqsave(&gus->reg_lock, flags);
+-	snd_gf1_select_voice(gus, voice->number);
+-	snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16);
+-	spin_unlock_irqrestore(&gus->reg_lock, flags);
+-	snd_seq_instr_free_use(gus->gf1.ilist, instr);
+-}
+-
+-#if 0
+-
+-static unsigned char get_effects_mask( ultra_card_t *card, int value )
+-{
+-  if ( value > 7 ) return 0;
+-  if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE )
+-    return card -> gf1.effects -> chip.interwave.voice_output[ value ];
+-  return 0;
+-}
+-
+-#endif
+-
+-static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data)
+-{
+-#if 0
+-  unsigned long flags;
+-  unsigned char uc;
+-
+-  switch ( *data ) {
+-    case ULTRA_PRIV1_IW_EFFECT:
+-      uc = get_effects_mask( card, ultra_get_byte( data, 4 ) );
+-      uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 );
+-      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) );
+-      uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 );
+-      voice -> data.simple.effect_accumulator = uc;
+-      voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4;
+-      if ( !card -> gf1.enh_mode ) return;
+-      if ( voice -> flags & VFLG_WAIT_FOR_START ) return;
+-      if ( voice -> flags & VFLG_RUNNING )
+-        {
+-          CLI( &flags );
+-          gf1_select_voice( card, voice -> number );
+-          ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator );
+-          ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume );
+-          STI( &flags );
+-        }
+-      break;
+-   case ULTRA_PRIV1_IW_LFO:
+-     ultra_lfo_command( card, voice -> number, data );
+-  }
+-#endif
+-}
+-
+-#if 0
+-
+-/*
+- *
+- */
+-
+-static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait )
+-{
+-}
+-
+-static void note_wait( ultra_card_t *card, ultra_voice_t *voice )
+-{
+-}
+-
+-static void note_off( ultra_card_t *card, ultra_voice_t *voice )
+-{
+-}
+-
+-static void note_volume( ultra_card_t *card, ultra_voice_t *voice )
+-{
+-}
+-
+-static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice )
+-{
+-}
+-
+-static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice )
+-{
+-}
+-
+-static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice )
+-{
+-}
+-
+-/*
+- *
+- */
+- 
+-static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority )
+-{
+-}
+-
+-static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note )
+-{
+-}
+-
+-static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 )
+-{
+-}
+-
+-/*
+- *
+- */
+- 
+-#endif
+-
+-void snd_gf1_simple_init(struct snd_gus_voice *voice)
+-{
+-	voice->handler_wave = interrupt_wave;
+-	voice->handler_volume = interrupt_volume;
+-	voice->handler_effect = interrupt_effect;
+-	voice->volume_change = NULL;
+-	voice->sample_ops = &sample_ops;
+-}
+diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c
+deleted file mode 100644
+index 2c20517..0000000
+--- a/sound/isa/gus/gus_synth.c
++++ /dev/null
+@@ -1,314 +0,0 @@
+-/*
+- *  Routines for Gravis UltraSound soundcards - Synthesizer
+- *  Copyright (c) by Jaroslav Kysela <perex at perex.cz>
+- *
+- *
+- *   This program 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 program 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/time.h>
+-#include <sound/core.h>
+-#include <sound/gus.h>
+-#include <sound/seq_device.h>
+-
+-MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>");
+-MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer");
+-MODULE_LICENSE("GPL");
+-
+-/*
+- *
+- */
+-
+-static void snd_gus_synth_free_voices(struct snd_gus_card * gus, int client, int port)
+-{
+-	int idx;
+-	struct snd_gus_voice * voice;
+-	
+-	for (idx = 0; idx < 32; idx++) {
+-		voice = &gus->gf1.voices[idx];
+-		if (voice->use && voice->client == client && voice->port == port)
+-			snd_gf1_free_voice(gus, voice);
+-	}
+-}
+-
+-static int snd_gus_synth_use(void *private_data, struct snd_seq_port_subscribe *info)
+-{
+-	struct snd_gus_port * port = private_data;
+-	struct snd_gus_card * gus = port->gus;
+-	struct snd_gus_voice * voice;
+-	unsigned int idx;
+-
+-	if (info->voices > 32)
+-		return -EINVAL;
+-	mutex_lock(&gus->register_mutex);
+-	if (!snd_gus_use_inc(gus)) {
+-		mutex_unlock(&gus->register_mutex);
+-		return -EFAULT;
+-	}
+-	for (idx = 0; idx < info->voices; idx++) {
+-		voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+-		if (voice == NULL) {
+-			snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
+-			snd_gus_use_dec(gus);
+-			mutex_unlock(&gus->register_mutex);
+-			return -EBUSY;
+-		}
+-		voice->index = idx;
+-	}
+-	mutex_unlock(&gus->register_mutex);
+-	return 0;
+-}
+-
+-static int snd_gus_synth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
+-{
+-	struct snd_gus_port * port = private_data;
+-	struct snd_gus_card * gus = port->gus;
+-
+-	mutex_lock(&gus->register_mutex);
+-	snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port);
+-	snd_gus_use_dec(gus);
+-	mutex_unlock(&gus->register_mutex);
+-	return 0;
+-}
+-
+-/*
+- *
+- */
+-
+-static void snd_gus_synth_free_private_instruments(struct snd_gus_port *p, int client)
+-{
+-	struct snd_seq_instr_header ifree;
+-
+-	memset(&ifree, 0, sizeof(ifree));
+-	ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+-	snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
+-}
+- 
+-static int snd_gus_synth_event_input(struct snd_seq_event *ev, int direct,
+-				     void *private_data, int atomic, int hop)
+-{
+-	struct snd_gus_port * p = private_data;
+-	
+-	snd_assert(p != NULL, return -EINVAL);
+-	if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+-	    ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+-		snd_gus_sample_event(ev, p);
+-		return 0;
+-	}
+-	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+-	    ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+-		if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+-			snd_gus_synth_free_private_instruments(p, ev->data.addr.client);
+-			return 0;
+-		}
+-	}
+-	if (direct) {
+-		if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+-			snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops,
+-					    p->gus->gf1.ilist,
+-					    ev,
+-					    p->gus->gf1.seq_client,
+-					    atomic, hop);
+-			return 0;
+-		}
+-	}
+-	return 0;
+-}
+-
+-static void snd_gus_synth_instr_notify(void *private_data,
+-				       struct snd_seq_kinstr *instr,
+-				       int what)
+-{
+-	unsigned int idx;
+-	struct snd_gus_card *gus = private_data;
+-	struct snd_gus_voice *pvoice;
+-	unsigned long flags;
+-	
+-	spin_lock_irqsave(&gus->event_lock, flags);
+-	for (idx = 0; idx < 32; idx++) {
+-		pvoice = &gus->gf1.voices[idx];
+-		if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+-			if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+-				pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY);
+-			} else {
+-				snd_gf1_stop_voice(gus, pvoice->number);
+-				pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING;
+-			}
+-		}
+-	}
+-	spin_unlock_irqrestore(&gus->event_lock, flags);
+-}
+-
+-/*
+- *
+- */
+-
+-static void snd_gus_synth_free_port(void *private_data)
+-{
+-	struct snd_gus_port * p = private_data;
+-	
+-	if (p)
+-		snd_midi_channel_free_set(p->chset);
+-}
+-
+-static int snd_gus_synth_create_port(struct snd_gus_card * gus, int idx)
+-{
+-	struct snd_gus_port * p;
+-	struct snd_seq_port_callback callbacks;
+-	char name[32];
+-	int result;
+-	
+-	p = &gus->gf1.seq_ports[idx];
+-	p->chset = snd_midi_channel_alloc_set(16);
+-	if (p->chset == NULL)
+-		return -ENOMEM;
+-	p->chset->private_data = p;
+-	p->gus = gus;
+-	p->client = gus->gf1.seq_client;
+-
+-	memset(&callbacks, 0, sizeof(callbacks));
+-	callbacks.owner = THIS_MODULE;
+-	callbacks.use = snd_gus_synth_use;
+-	callbacks.unuse = snd_gus_synth_unuse;
+-	callbacks.event_input = snd_gus_synth_event_input;
+-	callbacks.private_free = snd_gus_synth_free_port;
+-	callbacks.private_data = p;
+-	
+-	sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx);
+-	p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client,
+-						   &callbacks,
+-						   SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+-						   SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+-						   SNDRV_SEQ_PORT_TYPE_SYNTH |
+-						   SNDRV_SEQ_PORT_TYPE_HARDWARE |
+-						   SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
+-						   16, 0,
+-						   name);
+-	if (p->chset->port < 0) {
+-		result = p->chset->port;
+-		snd_gus_synth_free_port(p);
+-		return result;
+-	}
+-	p->port = p->chset->port;
+-	return 0;
+-}						 
+-
+-/*
+- *
+- */
+-
+-static int snd_gus_synth_new_device(struct snd_seq_device *dev)
+-{
+-	struct snd_gus_card *gus;
+-	int client, i;
+-	struct snd_seq_port_subscribe sub;
+-	struct snd_iwffff_ops *iwops;
+-	struct snd_gf1_ops *gf1ops;
+-	struct snd_simple_ops *simpleops;
+-
+-	gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+-	if (gus == NULL)
+-		return -EINVAL;
+-
+-	mutex_init(&gus->register_mutex);
+-	gus->gf1.seq_client = -1;
+-	
+-	/* allocate new client */
+-	client = gus->gf1.seq_client =
+-		snd_seq_create_kernel_client(gus->card, 1, gus->interwave ?
+-					     "AMD InterWave" : "GF1");
+-	if (client < 0)
+-		return client;
+-
+-	for (i = 0; i < 4; i++)
+-		snd_gus_synth_create_port(gus, i);
+-		
+-	gus->gf1.ilist = snd_seq_instr_list_new();
+-	if (gus->gf1.ilist == NULL) {
+-		snd_seq_delete_kernel_client(client);	
+-		gus->gf1.seq_client = -1;
+-		return -ENOMEM;
+-	}
+-	gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+-
+-	simpleops = &gus->gf1.simple_ops;
+-	snd_seq_simple_init(simpleops, gus, NULL);
+-	simpleops->put_sample = snd_gus_simple_put_sample;
+-	simpleops->get_sample = snd_gus_simple_get_sample;
+-	simpleops->remove_sample = snd_gus_simple_remove_sample;
+-	simpleops->notify = snd_gus_synth_instr_notify;
+-
+-	gf1ops = &gus->gf1.gf1_ops;
+-	snd_seq_gf1_init(gf1ops, gus, &simpleops->kops);
+-	gf1ops->put_sample = snd_gus_gf1_put_sample;
+-	gf1ops->get_sample = snd_gus_gf1_get_sample;
+-	gf1ops->remove_sample = snd_gus_gf1_remove_sample;
+-	gf1ops->notify = snd_gus_synth_instr_notify;
+-
+-	iwops = &gus->gf1.iwffff_ops;
+-	snd_seq_iwffff_init(iwops, gus, &gf1ops->kops);
+-	iwops->put_sample = snd_gus_iwffff_put_sample;
+-	iwops->get_sample = snd_gus_iwffff_get_sample;
+-	iwops->remove_sample = snd_gus_iwffff_remove_sample;
+-	iwops->notify = snd_gus_synth_instr_notify;
+-
+-	memset(&sub, 0, sizeof(sub));
+-	sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+-	sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+-	sub.dest.client = client;
+-	sub.dest.port = 0;
+-	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+-
+-	return 0;
+-}
+-
+-static int snd_gus_synth_delete_device(struct snd_seq_device *dev)
+-{
+-	struct snd_gus_card *gus;
+-
+-	gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+-	if (gus == NULL)
+-		return -EINVAL;
+-
+-	if (gus->gf1.seq_client >= 0) {
+-		snd_seq_delete_kernel_client(gus->gf1.seq_client);	
+-		gus->gf1.seq_client = -1;
+-	}
+-	if (gus->gf1.ilist)
+-		snd_seq_instr_list_free(&gus->gf1.ilist);
+-	return 0;
+-}
+-
+-static int __init alsa_gus_synth_init(void)
+-{
+-	static struct snd_seq_dev_ops ops = {
+-		snd_gus_synth_new_device,
+-		snd_gus_synth_delete_device
+-	};
+-
+-	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops,
+-					      sizeof(struct snd_gus_card *));
+-}
+-
+-static void __exit alsa_gus_synth_exit(void)
+-{
+-	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS);
+-}
+-
+-module_init(alsa_gus_synth_init)
+-module_exit(alsa_gus_synth_exit)
+diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c
+index 99eac57..c537271 100644
+--- a/sound/isa/gus/gus_timer.c
++++ b/sound/isa/gus/gus_timer.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/gus.h>
+diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
+index e6fd9b0..f0af3f7 100644
+--- a/sound/isa/gus/gus_uart.c
++++ b/sound/isa/gus/gus_uart.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/time.h>
+diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
+index 71a6774..c3c028a 100644
+--- a/sound/isa/gus/gus_volume.c
++++ b/sound/isa/gus/gus_volume.c
+@@ -18,7 +18,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/gus.h>
+diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
+index 29e422b..8f914b3 100644
+--- a/sound/isa/gus/gusclassic.c
++++ b/sound/isa/gus/gusclassic.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
+index fc59536..da13185 100644
+--- a/sound/isa/gus/gusextreme.c
++++ b/sound/isa/gus/gusextreme.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
+index 4922f5d..f87c623 100644
+--- a/sound/isa/gus/gusmax.c
++++ b/sound/isa/gus/gusmax.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
+index 2091c50..ca0d7ac 100644
+--- a/sound/isa/gus/interwave.c
++++ b/sound/isa/gus/interwave.c
+@@ -22,7 +22,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -560,50 +559,27 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
+ 				       const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+ 	iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (iwcard->dev == NULL) {
+-		kfree(cfg);
++	if (iwcard->dev == NULL)
+ 		return -EBUSY;
+-	}
++
+ #ifdef SNDRV_STB
+ 	iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL);
+-	if (iwcard->devtc == NULL) {
+-		kfree(cfg);
++	if (iwcard->devtc == NULL)
+ 		return -EBUSY;
+-	}
+ #endif
+ 	/* Synth & Codec initialization */
+ 	pdev = iwcard->dev;
+-	pnp_init_resource_table(cfg);
+-	if (port[dev] != SNDRV_AUTO_PORT) {
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-		pnp_resource_change(&cfg->port_resource[1], port[dev] + 0x100, 12);
+-		pnp_resource_change(&cfg->port_resource[2], port[dev] + 0x10c, 4);
+-	}
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+-	if (dma2[dev] < 0)
+-		pnp_resource_change(&cfg->dma_resource[1], 4, 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-        if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR "InterWave - Synth - the requested resources are invalid, using auto config\n");
++
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+-		kfree(cfg);
+ 		snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n");
+ 		return err;
+ 	}
+ 	if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) ||
+ 	    pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) {
+-		kfree(cfg);
+ 		snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n");
+ 		return -ENOENT;
+ 	}
+@@ -620,21 +596,15 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
+ #ifdef SNDRV_STB
+ 	/* Tone Control initialization */
+ 	pdev = iwcard->devtc;
+-	pnp_init_resource_table(cfg);
+-	if (port_tc[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port_tc[dev], 1);
+-        if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR "InterWave - ToneControl - the requested resources are invalid, using auto config\n");
++
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+-		kfree(cfg);
+ 		snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n");
+ 		return err;
+ 	}
+ 	port_tc[dev] = pnp_port_start(pdev, 0);
+ 	snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
+ #endif
+-	kfree(cfg);
+ 	return 0;
+ }
+ #endif /* CONFIG_PNP */
+diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
+index 59af9ab..854a9f7 100644
+--- a/sound/isa/opl3sa2.c
++++ b/sound/isa/opl3sa2.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -610,39 +609,8 @@ static int snd_opl3sa2_resume(struct snd_card *card)
+ static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
+ 				     struct pnp_dev *pdev)
+ {
+-	struct pnp_resource_table * cfg;
+-	int err;
+-
+-	cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+-	if (!cfg) {
+-		snd_printk(KERN_ERR PFX "cannot allocate pnp cfg\n");
+-		return -ENOMEM;
+-	}
+-	/* PnP initialization */
+-	pnp_init_resource_table(cfg);
+-	if (sb_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], sb_port[dev], 16);
+-	if (wss_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], wss_port[dev], 8);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4);
+-	if (midi_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[3], midi_port[dev], 2);
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[4], port[dev], 2);
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	err = pnp_manual_config_dev(pdev, cfg, 0);
+-	if (err < 0)
+-		snd_printk(KERN_WARNING "PnP manual resources are invalid, using auto config\n");
+-	err = pnp_activate_dev(pdev);
+-	if (err < 0) {
+-		kfree(cfg);
+-		snd_printk(KERN_ERR "PnP configure failure (out of resources?) err = %d\n", err);
++	if (pnp_activate_dev(pdev) < 0) {
++		snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n");
+ 		return -EBUSY;
+ 	}
+ 	sb_port[dev] = pnp_port_start(pdev, 0);
+@@ -657,7 +625,6 @@ static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
+ 		pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]);
+ 	snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
+ 		pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]);
+-	kfree(cfg);
+ 	return 0;
+ }
+ #endif /* CONFIG_PNP */
+diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
+index d295936..2a1e2f5 100644
+--- a/sound/isa/opti9xx/miro.c
++++ b/sound/isa/opti9xx/miro.c
+@@ -22,7 +22,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -483,6 +482,10 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
+ 
+ 		/* equalizer elements */
+ 
++		if (left < -0x7f || left > 0x7f ||
++		    right < -0x7f || right > 0x7f)
++			return -EINVAL;
++
+ 		if (left_old > 0x80) 
+ 			left_old = 0x80 - left_old;
+ 		if (right_old > 0x80) 
+@@ -520,6 +523,10 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
+ 
+ 		/* non-equalizer elements */
+ 
++		if (left < 0 || left > 0x20 ||
++		    right < 0 || right > 0x20)
++			return -EINVAL;
++
+ 		left_old = 0x20 - left_old;
+ 		right_old = 0x20 - right_old;
+ 
+@@ -662,7 +669,7 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
+ 	return 0;
+ }
+ 
+-static int snd_miro_mixer(struct snd_miro *miro)
++static int __devinit snd_miro_mixer(struct snd_miro *miro)
+ {
+ 	struct snd_card *card;
+ 	unsigned int idx;
+diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
+index ee1a824..fe1afc1 100644
+--- a/sound/isa/opti9xx/opti92x-ad1848.c
++++ b/sound/isa/opti9xx/opti92x-ad1848.c
+@@ -23,7 +23,6 @@
+ */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+@@ -1595,7 +1594,7 @@ OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0
+ }
+ };
+                                         
+-static int snd_opti93x_mixer(struct snd_opti93x *chip)
++static int __devinit snd_opti93x_mixer(struct snd_opti93x *chip)
+ {
+ 	struct snd_card *card;
+ 	struct snd_kcontrol_new knew;
+@@ -1690,53 +1689,19 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
+ 					  const struct pnp_card_device_id *pid)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+ 	chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
+-	if (chip->dev == NULL) {
+-		kfree(cfg);
++	if (chip->dev == NULL)
+ 		return -EBUSY;
+-	}
++
+ 	chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
+ 
+ 	pdev = chip->dev;
+-	pnp_init_resource_table(cfg);
+ 
+-#ifdef OPTi93X
+-	if (port != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port + 4, 4);
+-#else
+-	if (pid->driver_data != 0x0924 && port != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], port, 4);
+-#endif	/* OPTi93X */
+-	if (irq != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq, 1);
+-	if (dma1 != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1, 1);
+-#if defined(CS4231) || defined(OPTi93X)
+-	if (dma2 != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2, 1);
+-#else
+-#ifdef snd_opti9xx_fixup_dma2
+-	snd_opti9xx_fixup_dma2(pdev);
+-#endif
+-#endif	/* CS4231 || OPTi93X */
+-#ifdef OPTi93X
+-	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], fm_port, 4);
+-#else
+-	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], fm_port, 4);
+-#endif
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 
+@@ -1756,15 +1721,6 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
+ 
+ 	pdev = chip->devmpu;
+ 	if (pdev && mpu_port > 0) {
+-		pnp_init_resource_table(cfg);
+-
+-		if (mpu_port != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], mpu_port, 2);
+-		if (mpu_irq != SNDRV_AUTO_IRQ)
+-			pnp_resource_change(&cfg->irq_resource[0], mpu_irq, 1);
+-
+-		if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-			snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0) {
+ 			snd_printk(KERN_ERR "AUDIO pnp configure failure\n");
+@@ -1775,7 +1731,6 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
+ 			mpu_irq = pnp_irq(pdev, 0);
+ 		}
+ 	}
+-	kfree(cfg);
+ 	return pid->driver_data;
+ }
+ #endif	/* CONFIG_PNP */
+diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
+index 4eea84c..b35be7d 100644
+--- a/sound/isa/sb/emu8000.c
++++ b/sound/isa/sb/emu8000.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/wait.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h
+index 2ac77f1..7e87c34 100644
+--- a/sound/isa/sb/emu8000_local.h
++++ b/sound/isa/sb/emu8000_local.h
+@@ -21,7 +21,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/wait.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c
+index d4b2187..c8c8e21 100644
+--- a/sound/isa/sb/es968.c
++++ b/sound/isa/sb/es968.c
+@@ -20,7 +20,6 @@
+     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+ #include <linux/pnp.h>
+@@ -49,12 +48,6 @@ module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for es968 based soundcard.");
+ module_param_array(enable, bool, NULL, 0444);
+ MODULE_PARM_DESC(enable, "Enable es968 based soundcard.");
+-module_param_array(port, long, NULL, 0444);
+-MODULE_PARM_DESC(port, "Port # for es968 driver.");
+-module_param_array(irq, int, NULL, 0444);
+-MODULE_PARM_DESC(irq, "IRQ # for es968 driver.");
+-module_param_array(dma8, int, NULL, 0444);
+-MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver.");
+ 
+ struct snd_card_es968 {
+ 	struct pnp_dev *dev;
+@@ -86,40 +79,23 @@ static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard,
+ 					const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ 	int err;
+-	if (!cfg)
+-		return -ENOMEM;
++
+ 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->dev == NULL) {
+-		kfree(cfg);
++	if (acard->dev == NULL)
+ 		return -ENODEV;
+-	}
+ 
+ 	pdev = acard->dev;
+ 
+-	pnp_init_resource_table(cfg);
+-
+-	/* override resources */
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-	if (dma8[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+-		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 	port[dev] = pnp_port_start(pdev, 0);
+ 	dma8[dev] = pnp_dma(pdev, 1);
+ 	irq[dev] = pnp_irq(pdev, 0);
+ 
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
+index e7f9edd..2c201f7 100644
+--- a/sound/isa/sb/sb16.c
++++ b/sound/isa/sb/sb16.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/dma.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+@@ -257,44 +256,21 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
+ 				       const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg) 
+-		return -ENOMEM; 
+ 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->dev == NULL) { 
+-		kfree(cfg); 
++	if (acard->dev == NULL)
+ 		return -ENODEV; 
+-	} 
++
+ #ifdef SNDRV_SBAWE_EMU8000
+ 	acard->devwt = pnp_request_card_device(card, id->devs[1].id, acard->dev);
+ #endif
+ 	/* Audio initialization */
+ 	pdev = acard->dev;
+ 
+-	pnp_init_resource_table(cfg); 
+-	 
+-	/* override resources */ 
+-
+-	if (port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+-	if (mpu_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], mpu_port[dev], 2);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4);
+-	if (dma8[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
+-	if (dma16[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1);
+-	if (irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0) 
+-		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); 
+ 	err = pnp_activate_dev(pdev); 
+ 	if (err < 0) { 
+ 		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); 
+-		kfree(cfg);
+ 		return err; 
+ 	} 
+ 	port[dev] = pnp_port_start(pdev, 0);
+@@ -311,17 +287,6 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
+ 	/* WaveTable initialization */
+ 	pdev = acard->devwt;
+ 	if (pdev != NULL) {
+-		pnp_init_resource_table(cfg); 
+-	 
+-		/* override resources */ 
+-
+-		if (awe_port[dev] != SNDRV_AUTO_PORT) {
+-			pnp_resource_change(&cfg->port_resource[0], awe_port[dev], 4);
+-			pnp_resource_change(&cfg->port_resource[1], awe_port[dev] + 0x400, 4);
+-			pnp_resource_change(&cfg->port_resource[2], awe_port[dev] + 0x800, 4);
+-		}
+-		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) 
+-			snd_printk(KERN_ERR PFX "WaveTable the requested resources are invalid, using auto config\n"); 
+ 		err = pnp_activate_dev(pdev); 
+ 		if (err < 0) { 
+ 			goto __wt_error; 
+@@ -339,7 +304,6 @@ __wt_error:
+ 		awe_port[dev] = -1;
+ 	}
+ #endif
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
+index 3682059..bed29ca 100644
+--- a/sound/isa/sb/sb16_csp.c
++++ b/sound/isa/sb/sb16_csp.c
+@@ -23,7 +23,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+@@ -118,7 +117,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
+ int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
+ {
+ 	struct snd_sb_csp *p;
+-	int version, err;
++	int uninitialized_var(version);
++	int err;
+ 	struct snd_hwdep *hw;
+ 
+ 	if (rhwdep)
+diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
+index c06754f..f7e8192 100644
+--- a/sound/isa/sb/sb16_main.c
++++ b/sound/isa/sb/sb16_main.c
+@@ -33,7 +33,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include <linux/init.h>
+diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
+index f933aef..336a342 100644
+--- a/sound/isa/sb/sb8.c
++++ b/sound/isa/sb/sb8.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
+index bee894b..6304c3a 100644
+--- a/sound/isa/sb/sb8_main.c
++++ b/sound/isa/sb/sb8_main.c
+@@ -30,7 +30,6 @@
+  *   Cleaned up and rewrote lowlevel routines.
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <asm/dma.h>
+ #include <linux/init.h>
+diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c
+index e56e563..988a8b7 100644
+--- a/sound/isa/sb/sb8_midi.c
++++ b/sound/isa/sb/sb8_midi.c
+@@ -26,7 +26,6 @@
+  *   Added full duplex UART mode for DSP version 2.0 and later.
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
+index 176193c..d63c1af 100644
+--- a/sound/isa/sb/sb_common.c
++++ b/sound/isa/sb/sb_common.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
+index 03241cd..91d1422 100644
+--- a/sound/isa/sb/sb_mixer.c
++++ b/sound/isa/sb/sb_mixer.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/time.h>
+diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
+index 94daf83..da3d152 100644
+--- a/sound/isa/sc6000.c
++++ b/sound/isa/sc6000.c
+@@ -23,7 +23,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/isa.h>
+@@ -390,7 +389,7 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
+ 
+ 	err = sc6000_init_mss(vport, config, vmss_port, mss_config);
+ 	if (err < 0) {
+-		snd_printk(KERN_ERR "Can not initialize"
++		snd_printk(KERN_ERR "Can not initialize "
+ 			   "Microsoft Sound System mode.\n");
+ 		return -ENODEV;
+ 	}
+diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
+index 922519d..a07274e 100644
+--- a/sound/isa/sgalaxy.c
++++ b/sound/isa/sgalaxy.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
+index 1cb921d..06ad786 100644
+--- a/sound/isa/sscape.c
++++ b/sound/isa/sscape.c
+@@ -21,7 +21,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/isa.h>
+diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
+index 83c2fc4..3a6c6fe 100644
+--- a/sound/isa/wavefront/wavefront.c
++++ b/sound/isa/wavefront/wavefront.c
+@@ -19,7 +19,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/slab.h>
+@@ -104,21 +103,15 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
+ 		   const struct pnp_card_device_id *id)
+ {
+ 	struct pnp_dev *pdev;
+-	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ 	int err;
+ 
+-	if (!cfg)
+-		return -ENOMEM;
+-
+ 	/* Check for each logical device. */
+ 
+ 	/* CS4232 chip (aka "windows sound system") is logical device 0 */
+ 
+ 	acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);
+-	if (acard->wss == NULL) {
+-		kfree(cfg);
++	if (acard->wss == NULL)
+ 		return -EBUSY;
+-	}
+ 
+ 	/* there is a game port at logical device 1, but we ignore it completely */
+ 
+@@ -133,26 +126,20 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
+ 
+ 	if (use_cs4232_midi[dev]) {
+ 		acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);
+-		if (acard->mpu == NULL) {
+-			kfree(cfg);
++		if (acard->mpu == NULL)
+ 			return -EBUSY;
+-		}
+ 	}
+ 
+ 	/* The ICS2115 synth is logical device 4 */
+ 
+ 	acard->synth = pnp_request_card_device(card, id->devs[3].id, NULL);
+-	if (acard->synth == NULL) {
+-		kfree(cfg);
++	if (acard->synth == NULL)
+ 		return -EBUSY;
+-	}
+ 
+ 	/* PCM/FM initialization */
+ 
+ 	pdev = acard->wss;
+ 
+-	pnp_init_resource_table(cfg);
+-
+ 	/* An interesting note from the Tropez+ FAQ:
+ 
+ 	   Q. [Ports] Why is the base address of the WSS I/O ports off by 4?
+@@ -165,23 +152,9 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
+ 
+ 	*/
+ 
+-	if (cs4232_pcm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[0], cs4232_pcm_port[dev], 4);
+-	if (fm_port[dev] != SNDRV_AUTO_PORT)
+-		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+-	if (dma1[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+-	if (dma2[dev] != SNDRV_AUTO_DMA)
+-		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+-	if (cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ)
+-		pnp_resource_change(&cfg->irq_resource[0], cs4232_pcm_irq[dev], 1);
+-
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR "PnP WSS the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR "PnP WSS pnp configure failure\n");
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 
+@@ -195,22 +168,9 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
+ 
+ 	pdev = acard->synth;
+ 	
+-	pnp_init_resource_table(cfg);
+-
+-	if (ics2115_port[dev] != SNDRV_AUTO_PORT) {
+-		pnp_resource_change(&cfg->port_resource[0], ics2115_port[dev], 16);
+-	}
+-		
+-	if (ics2115_port[dev] != SNDRV_AUTO_IRQ) {
+-		pnp_resource_change(&cfg->irq_resource[0], ics2115_irq[dev], 1);
+-	}
+-
+-	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-		snd_printk(KERN_ERR "PnP ICS2115 the requested resources are invalid, using auto config\n");
+ 	err = pnp_activate_dev(pdev);
+ 	if (err < 0) {
+ 		snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n");
+-		kfree(cfg);
+ 		return err;
+ 	}
+ 
+@@ -226,15 +186,6 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
+ 
+ 		pdev = acard->mpu;
+ 
+-		pnp_init_resource_table(cfg);
+-
+-		if (cs4232_mpu_port[dev] != SNDRV_AUTO_PORT)
+-			pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_port[dev], 2);
+-		if (cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+-			pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_irq[dev], 1);
+-
+-		if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+-			snd_printk(KERN_ERR "PnP MPU401 the requested resources are invalid, using auto config\n");
+ 		err = pnp_activate_dev(pdev);
+ 		if (err < 0) {
+ 			snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n");
+@@ -258,7 +209,6 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
+ 		    ics2115_port[dev], 
+ 		    ics2115_irq[dev]);
+ 	
+-	kfree(cfg);
+ 	return 0;
+ }
+ 
+diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
+index fc95a87..2efaa7f 100644
+--- a/sound/isa/wavefront/wavefront_fx.c
++++ b/sound/isa/wavefront/wavefront_fx.c
+@@ -16,7 +16,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
+index cb34600..a33384a 100644
+--- a/sound/isa/wavefront/wavefront_midi.c
++++ b/sound/isa/wavefront/wavefront_midi.c
+@@ -47,7 +47,6 @@
+  *  
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
+index a1ebb7c..95eeca1 100644
+--- a/sound/isa/wavefront/wavefront_synth.c
++++ b/sound/isa/wavefront/wavefront_synth.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/interrupt.h>
+ #include <linux/init.h>
+diff --git a/sound/last.c b/sound/last.c
+index 282b0cd..bdd0857 100644
+--- a/sound/last.c
++++ b/sound/last.c
+@@ -20,7 +20,6 @@
+  */
+ 
+ #define SNDRV_MAIN_OBJECT_FILE
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+ 
+diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
+index 24460a5..ee0741f 100644
+--- a/sound/mips/au1x00.c
++++ b/sound/mips/au1x00.c
+@@ -36,7 +36,6 @@
+ 
+ #include <linux/ioport.h>
+ #include <linux/interrupt.h>
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/version.h>
+diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
+index b48c729..8849041 100644
+--- a/sound/oss/waveartist.c
++++ b/sound/oss/waveartist.c
+@@ -835,7 +835,7 @@ static struct audio_driver waveartist_audio_driver = {
+ static irqreturn_t
+ waveartist_intr(int irq, void *dev_id)
+ {
+-	wavnc_info *devc = (wavnc_info *)dev_id;
++	wavnc_info *devc = dev_id;
+ 	int	   irqstatus, status;
+ 
+ 	spin_lock(&waveartist_lock);
+diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
+index ff705c6..99f5483 100644
+--- a/sound/parisc/harmony.c
++++ b/sound/parisc/harmony.c
+@@ -45,7 +45,6 @@
+ #include <linux/spinlock.h>
+ #include <linux/dma-mapping.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/control.h>
+diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
+index 356bf21..812085d 100644
+--- a/sound/pci/Kconfig
++++ b/sound/pci/Kconfig
+@@ -183,6 +183,30 @@ config SND_CMIPCI
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called snd-cmipci.
+ 
++config SND_OXYGEN_LIB
++        tristate
++	depends on SND
++	select SND_PCM
++	select SND_MPU401_UART
++
++config SND_OXYGEN
++	tristate "C-Media 8788 (Oxygen)"
++	depends on SND
++	select SND_OXYGEN_LIB
++	help
++	  Say Y here to include support for sound cards based on the
++	  C-Media CMI8788 (Oxygen HD Audio) chip:
++	   * Asound A-8788
++	   * AuzenTech X-Meridian
++	   * Bgears b-Enspirer
++	   * Club3D Theatron DTS
++	   * HT-Omega Claro
++	   * Razer Barracuda AC-1
++	   * Sondigo Inferno
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called snd-oxygen.
++
+ config SND_CS4281
+ 	tristate "Cirrus Logic (Sound Fusion) CS4281"
+ 	depends on SND
+@@ -623,6 +647,17 @@ config SND_HDSPM
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called snd-hdspm.
+ 
++config SND_HIFIER
++	tristate "TempoTec HiFier Fantasia"
++	depends on SND
++	select SND_OXYGEN_LIB
++	help
++	  Say Y here to include support for the MediaTek/TempoTec HiFier
++	  Fantasia sound card.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called snd-hifier.
++
+ config SND_ICE1712
+ 	tristate "ICEnsemble ICE1712 (Envy24)"
+ 	depends on SND
+@@ -802,6 +837,16 @@ config SND_RME9652
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called snd-rme9652.
+ 
++config SND_SIS7019
++	tristate "SiS 7019 Audio Accelerator"
++	depends on SND && X86 && !X86_64
++	select SND_AC97_CODEC
++	help
++	  Say Y here to include support for the SiS 7019 Audio Accelerator.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called snd-sis7019.
++
+ config SND_SONICVIBES
+ 	tristate "S3 SonicVibes"
+ 	depends on SND
+@@ -850,6 +895,17 @@ config SND_VIA82XX_MODEM
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called snd-via82xx-modem.
+ 
++config SND_VIRTUOSO
++	tristate "Asus Virtuoso 200 (Xonar)"
++	depends on SND
++	select SND_OXYGEN_LIB
++	help
++	  Say Y here to include support for sound cards based on the
++	  Asus AV200 chip, i.e., Xonar D2 and Xonar D2X.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called snd-virtuoso.
++
+ config SND_VX222
+ 	tristate "Digigram VX222"
+ 	depends on SND
+diff --git a/sound/pci/Makefile b/sound/pci/Makefile
+index 09ddc82..2d42fd2 100644
+--- a/sound/pci/Makefile
++++ b/sound/pci/Makefile
+@@ -23,6 +23,7 @@ snd-intel8x0m-objs := intel8x0m.o
+ snd-maestro3-objs := maestro3.o
+ snd-rme32-objs := rme32.o
+ snd-rme96-objs := rme96.o
++snd-sis7019-objs := sis7019.o
+ snd-sonicvibes-objs := sonicvibes.o
+ snd-via82xx-objs := via82xx.o
+ snd-via82xx-modem-objs := via82xx_modem.o
+@@ -48,6 +49,7 @@ obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o
+ obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
+ obj-$(CONFIG_SND_RME32) += snd-rme32.o
+ obj-$(CONFIG_SND_RME96) += snd-rme96.o
++obj-$(CONFIG_SND_SIS7019) += snd-sis7019.o
+ obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
+ obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
+ obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
+@@ -66,6 +68,7 @@ obj-$(CONFIG_SND) += \
+ 	korg1212/ \
+ 	mixart/ \
+ 	nm256/ \
++	oxygen/ \
+ 	pcxhr/ \
+ 	riptide/ \
+ 	rme9652/ \
+diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
+index 6a9966d..45fd290 100644
+--- a/sound/pci/ac97/ac97_codec.c
++++ b/sound/pci/ac97/ac97_codec.c
+@@ -22,7 +22,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
+index 98c8b72..50c637e 100644
+--- a/sound/pci/ac97/ac97_patch.c
++++ b/sound/pci/ac97/ac97_patch.c
+@@ -133,6 +133,14 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ 	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ 	unsigned char mode = ucontrol->value.enumerated.item[0];
+ 
++	if (kcontrol->private_value) {
++		if (mode >= 2)
++			return -EINVAL;
++	} else {
++		if (mode >= 3)
++			return -EINVAL;
++	}
++
+ 	if (mode != ac97->channel_mode) {
+ 		ac97->channel_mode = mode;
+ 		if (ac97->build_ops->update_jacks)
+@@ -2142,8 +2150,7 @@ static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol,
+ 	struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ 	unsigned short val;
+ 
+-	if (ucontrol->value.enumerated.item[0] > 3
+-	    || ucontrol->value.enumerated.item[0] < 0)
++	if (ucontrol->value.enumerated.item[0] > 3)
+ 		return -EINVAL;
+ 	val = ctrl2reg[ucontrol->value.enumerated.item[0]]
+ 	      << AC97_AD198X_VREF_SHIFT;
+diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
+index 9cccc27..47bf8df 100644
+--- a/sound/pci/ac97/ac97_patch.h
++++ b/sound/pci/ac97/ac97_patch.h
+@@ -83,8 +83,10 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
+ 			     const char *s2, const char *suffix);
+ static void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src,
+ 				    const char *dst);
++#ifdef CONFIG_PM
+ static void snd_ac97_restore_status(struct snd_ac97 *ac97);
+ static void snd_ac97_restore_iec958(struct snd_ac97 *ac97);
++#endif
+ static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
+ 				     struct snd_ctl_elem_info *uinfo);
+ static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
+diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
+index 8cbc033..3674f35 100644
+--- a/sound/pci/ac97/ac97_pcm.c
++++ b/sound/pci/ac97/ac97_pcm.c
+@@ -23,7 +23,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
+index fed4a2c..060ea59 100644
+--- a/sound/pci/ac97/ac97_proc.c
++++ b/sound/pci/ac97/ac97_proc.c
+@@ -22,7 +22,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/mutex.h>
+ 
+diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
+index 722de45..c0c1633 100644
+--- a/sound/pci/ac97/ak4531_codec.c
++++ b/sound/pci/ac97/ak4531_codec.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
+index 98970d4..a66d515 100644
+--- a/sound/pci/ad1889.c
++++ b/sound/pci/ad1889.c
+@@ -40,7 +40,6 @@
+ #include <linux/compiler.h>
+ #include <linux/delay.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/initval.h>
+@@ -1055,7 +1054,7 @@ static struct pci_device_id snd_ad1889_ids[] = {
+ };
+ MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);
+ 
+-static struct pci_driver ad1889_pci = {
++static struct pci_driver ad1889_pci_driver = {
+ 	.name = "AD1889 Audio",
+ 	.id_table = snd_ad1889_ids,
+ 	.probe = snd_ad1889_probe,
+@@ -1065,13 +1064,13 @@ static struct pci_driver ad1889_pci = {
+ static int __init
+ alsa_ad1889_init(void)
+ {
+-	return pci_register_driver(&ad1889_pci);
++	return pci_register_driver(&ad1889_pci_driver);
+ }
+ 
+ static void __exit
+ alsa_ad1889_fini(void)
+ {
+-	pci_unregister_driver(&ad1889_pci);
++	pci_unregister_driver(&ad1889_pci_driver);
+ }
+ 
+ module_init(alsa_ad1889_init);
+diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
+index 4c2bd7a..6a905ed 100644
+--- a/sound/pci/ali5451/ali5451.c
++++ b/sound/pci/ali5451/ali5451.c
+@@ -25,7 +25,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/als300.c b/sound/pci/als300.c
+index 48cc39b..0e990a7 100644
+--- a/sound/pci/als300.c
++++ b/sound/pci/als300.c
+@@ -30,7 +30,6 @@
+  *  to keep track of what period we are in.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/moduleparam.h>
+diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
+index 1190ef3..27ce613 100644
+--- a/sound/pci/als4000.c
++++ b/sound/pci/als4000.c
+@@ -63,7 +63,6 @@
+  * - power management? (card can do voice wakeup according to datasheet!!)
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
+index 89184a4..4594186 100644
+--- a/sound/pci/atiixp.c
++++ b/sound/pci/atiixp.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -560,7 +559,7 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
+ 	     ATI_REG_ISR_CODEC2_NOT_READY)
+ #define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
+ 
+-static int ac97_probing_bugs(struct pci_dev *pci)
++static int __devinit ac97_probing_bugs(struct pci_dev *pci)
+ {
+ 	const struct snd_pci_quirk *q;
+ 
+@@ -574,7 +573,7 @@ static int ac97_probing_bugs(struct pci_dev *pci)
+ 	return -1;
+ }
+ 
+-static int snd_atiixp_codec_detect(struct atiixp *chip)
++static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
+ {
+ 	int timeout;
+ 
+diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
+index ce752f8..a67a869 100644
+--- a/sound/pci/atiixp_modem.c
++++ b/sound/pci/atiixp_modem.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
+index 5ccf0b1..4aad35b 100644
+--- a/sound/pci/au88x0/au88x0.h
++++ b/sound/pci/au88x0/au88x0.h
+@@ -18,7 +18,6 @@
+ #define __SOUND_AU88X0_H
+ 
+ #ifdef __KERNEL__
+-#include <sound/driver.h>
+ #include <linux/pci.h>
+ #include <asm/io.h>
+ #include <sound/core.h>
+diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
+index 4a336ea..333c62d 100644
+--- a/sound/pci/au88x0/au88x0_core.c
++++ b/sound/pci/au88x0/au88x0_core.c
+@@ -2395,7 +2395,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
+ 	if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1))
+ 		return IRQ_NONE;
+ 
+-	// This is the Interrrupt Enable flag we set before (consistency check).
++	// This is the Interrupt Enable flag we set before (consistency check).
+ 	if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE))
+ 		return IRQ_NONE;
+ 
+diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
+index a07d1de..bc212f4 100644
+--- a/sound/pci/au88x0/au88x0_game.c
++++ b/sound/pci/au88x0/au88x0_game.c
+@@ -30,7 +30,6 @@
+  * driver. (email: mjander at embedded.cl).
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
+index c96da1d..c92f493 100644
+--- a/sound/pci/au88x0/au88x0_mixer.c
++++ b/sound/pci/au88x0/au88x0_mixer.c
+@@ -5,7 +5,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
+index 8db3d3e..0dc8d25 100644
+--- a/sound/pci/au88x0/au88x0_mpu401.c
++++ b/sound/pci/au88x0/au88x0_mpu401.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
+index 7b5baa1..526c6c5 100644
+--- a/sound/pci/au88x0/au88x0_pcm.c
++++ b/sound/pci/au88x0/au88x0_pcm.c
+@@ -21,7 +21,6 @@
+  * It remains stuck,and DMA transfers do not happen. 
+  */
+ #include <sound/asoundef.h>
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
+index 36d3666..4e71a55 100644
+--- a/sound/pci/azt3328.c
++++ b/sound/pci/azt3328.c
+@@ -115,7 +115,6 @@
+  *    code (but I'm not too optimistic that doing this is possible at all)
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
+index 2dba752..c9a2421 100644
+--- a/sound/pci/bt87x.c
++++ b/sound/pci/bt87x.c
+@@ -21,7 +21,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
+index 75da174..74175fc 100644
+--- a/sound/pci/ca0106/ca0106.h
++++ b/sound/pci/ca0106/ca0106.h
+@@ -272,7 +272,6 @@
+ #define SPCS_WORD_LENGTH_20A	0x0000000a	/* Word Length 20 bit				*/
+ #define SPCS_WORD_LENGTH_20	0x00000009	/* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */
+ #define SPCS_WORD_LENGTH_21	0x00000007	/* Word Length 21 bit				*/
+-#define SPCS_WORD_LENGTH_21	0x00000007	/* Word Length 21 bit				*/
+ #define SPCS_WORD_LENGTH_22	0x00000005	/* Word Length 22 bit				*/
+ #define SPCS_WORD_LENGTH_23	0x00000003	/* Word Length 23 bit				*/
+ #define SPCS_WORD_LENGTH_24	0x0000000b	/* Word Length 24 bit				*/
+diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
+index 31d8db9..176e0f0 100644
+--- a/sound/pci/ca0106/ca0106_main.c
++++ b/sound/pci/ca0106/ca0106_main.c
+@@ -135,7 +135,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
+index 3f9b5c5..af73686 100644
+--- a/sound/pci/ca0106/ca0106_mixer.c
++++ b/sound/pci/ca0106/ca0106_mixer.c
+@@ -60,7 +60,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
+index 61f2718..c62b7d1 100644
+--- a/sound/pci/ca0106/ca0106_proc.c
++++ b/sound/pci/ca0106/ca0106_proc.c
+@@ -60,7 +60,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
+index ad32eff..893ee4f 100644
+--- a/sound/pci/ca0106/ca_midi.c
++++ b/sound/pci/ca0106/ca_midi.c
+@@ -27,7 +27,6 @@
+  */
+ 
+ #include <linux/spinlock.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/rawmidi.h>
+ 
+diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
+index 1fa5f00..135f308 100644
+--- a/sound/pci/cmipci.c
++++ b/sound/pci/cmipci.c
+@@ -20,7 +20,6 @@
+ /* Does not work. Warning may block system in capture mode */
+ /* #define USE_VAR48KRATE */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -150,6 +149,8 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
+ #define CM_CH0_SRATE_176K	0x00000200
+ #define CM_CH0_SRATE_96K	0x00000200	/* model 055? */
+ #define CM_CH0_SRATE_88K	0x00000100
++#define CM_CH0_SRATE_128K	0x00000300
++#define CM_CH0_SRATE_MASK	0x00000300
+ 
+ #define CM_SPDIF_INVERSE2	0x00000080	/* model 055? */
+ #define CM_DBLSPDS		0x00000040	/* double SPDIF sample rate 88.2/96 */
+@@ -473,6 +474,7 @@ struct cmipci {
+ 	unsigned int can_ac3_sw: 1;
+ 	unsigned int can_ac3_hw: 1;
+ 	unsigned int can_multi_ch: 1;
++	unsigned int can_96k: 1;	/* samplerate above 48k */
+ 	unsigned int do_soft_ac3: 1;
+ 
+ 	unsigned int spdif_playback_avail: 1;	/* spdif ready? */
+@@ -603,8 +605,6 @@ static unsigned int snd_cmipci_rate_freq(unsigned int rate)
+ {
+ 	unsigned int i;
+ 
+-	if (rate > 48000)
+-		rate /= 2;
+ 	for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ 		if (rates[i] == rate)
+ 			return i;
+@@ -782,7 +782,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
+ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
+ 				 struct snd_pcm_substream *substream)
+ {
+-	unsigned int reg, freq, val;
++	unsigned int reg, freq, freq_ext, val;
+ 	unsigned int period_size;
+ 	struct snd_pcm_runtime *runtime = substream->runtime;
+ 
+@@ -830,7 +830,17 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
+ 	//snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+ 
+ 	/* set sample rate */
+-	freq = snd_cmipci_rate_freq(runtime->rate);
++	freq = 0;
++	freq_ext = 0;
++	if (runtime->rate > 48000)
++		switch (runtime->rate) {
++		case 88200:  freq_ext = CM_CH0_SRATE_88K; break;
++		case 96000:  freq_ext = CM_CH0_SRATE_96K; break;
++		case 128000: freq_ext = CM_CH0_SRATE_128K; break;
++		default:     snd_BUG(); break;
++		}
++	else
++		freq = snd_cmipci_rate_freq(runtime->rate);
+ 	val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
+ 	if (rec->ch) {
+ 		val &= ~CM_DSFC_MASK;
+@@ -851,19 +861,20 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
+ 		val &= ~CM_CH0FMT_MASK;
+ 		val |= rec->fmt << CM_CH0FMT_SHIFT;
+ 	}
+-	if (cm->chip_version == 68) {
+-		if (runtime->rate == 88200)
+-			val |= CM_CH0_SRATE_88K << (rec->ch * 2);
+-		else
+-			val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
+-		if (runtime->rate == 96000)
+-			val |= CM_CH0_SRATE_96K << (rec->ch * 2);
+-		else
+-			val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
++	if (cm->can_96k) {
++		val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
++		val |= freq_ext << (rec->ch * 2);
+ 	}
+ 	snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+ 	//snd_printd("cmipci: chformat = %08x\n", val);
+ 
++	if (!rec->is_dac && cm->chip_version) {
++		if (runtime->rate > 44100)
++			snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K);
++		else
++			snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K);
++	}
++
+ 	rec->running = 0;
+ 	spin_unlock_irq(&cm->reg_lock);
+ 
+@@ -1280,7 +1291,7 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
+ 	int rate = substream->runtime->rate;
+ 	int err, do_spdif, do_ac3 = 0;
+ 
+-	do_spdif = (rate >= 44100 &&
++	do_spdif = (rate >= 44100 && rate <= 96000 &&
+ 		    substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE &&
+ 		    substream->runtime->channels == 2);
+ 	if (do_spdif && cm->can_ac3_hw) 
+@@ -1336,10 +1347,8 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec)
+ 		val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
+ 		val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
+ 		val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
+-		if (cm->chip_version == 68) {
+-			val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
+-			val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
+-		}
++		if (cm->can_96k)
++			val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
+ 		snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+ 	
+ 		/* start stream (we don't need interrupts) */
+@@ -1391,6 +1400,17 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream)
+ 
+ 	spin_lock_irq(&cm->reg_lock);
+ 	snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
++	if (cm->can_96k) {
++		if (substream->runtime->rate > 48000)
++			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
++		else
++			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
++	}
++	if (snd_pcm_format_width(substream->runtime->format) > 16)
++		snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
++	else
++		snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
++
+ 	spin_unlock_irq(&cm->reg_lock);
+ 
+ 	return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
+@@ -1402,6 +1422,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
+ 
+ 	spin_lock_irq(&cm->reg_lock);
+ 	snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
++	snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
+ 	spin_unlock_irq(&cm->reg_lock);
+ 
+ 	return snd_cmipci_hw_free(subs);
+@@ -1553,7 +1574,8 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
+ 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ 				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
+ 				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+-	.formats =	        SNDRV_PCM_FMTBIT_S16_LE,
++	.formats =	        SNDRV_PCM_FMTBIT_S16_LE |
++				SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ 	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ 	.rate_min =		44100,
+ 	.rate_max =		48000,
+@@ -1567,6 +1589,14 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
+ 	.fifo_size =		0,
+ };
+ 
++static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
++			32000, 44100, 48000, 88200, 96000, 128000 };
++static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
++		.count = ARRAY_SIZE(rate_constraints),
++		.list = rate_constraints,
++		.mask = 0,
++};
++
+ /*
+  * check device open/close
+  */
+@@ -1636,6 +1666,13 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
+ 		runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ 				     SNDRV_PCM_RATE_96000;
+ 		runtime->hw.rate_max = 96000;
++	} else if (cm->chip_version == 55) {
++		err = snd_pcm_hw_constraint_list(runtime, 0,
++			SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
++		if (err < 0)
++			return err;
++		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
++		runtime->hw.rate_max = 128000;
+ 	}
+ 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+ 	cm->dig_pcm_status = cm->dig_status;
+@@ -1654,6 +1691,13 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream)
+ 	if (cm->chip_version == 68) {	// 8768 only supports 44k/48k recording
+ 		runtime->hw.rate_min = 41000;
+ 		runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
++	} else if (cm->chip_version == 55) {
++		err = snd_pcm_hw_constraint_list(runtime, 0,
++			SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
++		if (err < 0)
++			return err;
++		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
++		runtime->hw.rate_max = 128000;
+ 	}
+ 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+ 	return 0;
+@@ -1685,6 +1729,13 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
+ 		runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ 				     SNDRV_PCM_RATE_96000;
+ 		runtime->hw.rate_max = 96000;
++	} else if (cm->chip_version == 55) {
++		err = snd_pcm_hw_constraint_list(runtime, 0,
++			SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
++		if (err < 0)
++			return err;
++		runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
++		runtime->hw.rate_max = 128000;
+ 	}
+ 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
+ 	return 0;
+@@ -1704,7 +1755,7 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
+ 			runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ 			snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ 		}
+-		if (cm->chip_version == 68) {
++		if (cm->can_96k) {
+ 			runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ 					     SNDRV_PCM_RATE_96000;
+ 			runtime->hw.rate_max = 96000;
+@@ -1726,6 +1777,11 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream)
+ 	if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
+ 		return err;
+ 	runtime->hw = snd_cmipci_capture_spdif;
++	if (cm->can_96k && !(cm->chip_version == 68)) {
++		runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
++				     SNDRV_PCM_RATE_96000;
++		runtime->hw.rate_max = 96000;
++	}
+ 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000);
+ 	return 0;
+ }
+@@ -2594,10 +2650,8 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata =
+ };
+ 
+ /* card control switches */
+-static struct snd_kcontrol_new snd_cmipci_control_switches[] __devinitdata = {
+-	// DEFINE_CARD_SWITCH("Joystick", joystick), /* now module option */
+-	DEFINE_CARD_SWITCH("Modem", modem),
+-};
++static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata =
++DEFINE_CARD_SWITCH("Modem", modem);
+ 
+ 
+ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
+@@ -2678,9 +2732,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
+ 	}
+ 
+ 	/* card switches */
+-	sw = snd_cmipci_control_switches;
+-	for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) {
+-		err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm));
++	/*
++	 * newer chips don't have the register bits to force modem link
++	 * detection; the bit that was FLINKON now mutes CH1
++	 */
++	if (cm->chip_version < 39) {
++		err = snd_ctl_add(cm->card,
++				  snd_ctl_new1(&snd_cmipci_modem_switch, cm));
+ 		if (err < 0)
+ 			return err;
+ 	}
+@@ -2785,9 +2843,11 @@ static void __devinit query_chip(struct cmipci *cm)
+ 		} else if (detect & CM_CHIP_8768) {
+ 			cm->chip_version = 68;
+ 			cm->max_channels = 8;
++			cm->can_96k = 1;
+ 		} else {
+ 			cm->chip_version = 55;
+ 			cm->max_channels = 6;
++			cm->can_96k = 1;
+ 		}
+ 		cm->can_ac3_hw = 1;
+ 		cm->can_multi_ch = 1;
+diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
+index 9a55f4a..7556fd9 100644
+--- a/sound/pci/cs4281.c
++++ b/sound/pci/cs4281.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
+index 2699cb6..e876b32 100644
+--- a/sound/pci/cs46xx/cs46xx.c
++++ b/sound/pci/cs46xx/cs46xx.c
+@@ -25,7 +25,6 @@
+     reloading the module may solve this.
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/pci.h>
+ #include <linux/time.h>
+ #include <linux/init.h>
+diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
+index 2c7bfc9..87ddffc 100644
+--- a/sound/pci/cs46xx/cs46xx_lib.c
++++ b/sound/pci/cs46xx/cs46xx_lib.c
+@@ -8,7 +8,7 @@
+  *    - Sometimes the SPDIF input DSP tasks get's unsynchronized
+  *      and the SPDIF get somewhat "distorcionated", or/and left right channel
+  *      are swapped. To get around this problem when it happens, mute and unmute 
+- *      the SPDIF input mixer controll.
++ *      the SPDIF input mixer control.
+  *    - On the Hercules Game Theater XP the amplifier are sometimes turned
+  *      off on inadecuate moments which causes distorcions on sound.
+  *
+@@ -45,7 +45,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/pci.h>
+ #include <linux/pm.h>
+@@ -2084,71 +2083,6 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
+ #endif /* CONFIG_SND_CS46XX_NEW_DSP */
+ 
+ 
+-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+-static int snd_cs46xx_egpio_select_info(struct snd_kcontrol *kcontrol, 
+-                                        struct snd_ctl_elem_info *uinfo)
+-{
+-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+-	uinfo->count = 1;
+-	uinfo->value.integer.min = 0;
+-	uinfo->value.integer.max = 8;
+-	return 0;
+-}
+-
+-static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol, 
+-                                       struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+-	ucontrol->value.integer.value[0] = chip->current_gpio;
+-
+-	return 0;
+-}
+-
+-static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol, 
+-                                       struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+-	int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
+-	chip->current_gpio = ucontrol->value.integer.value[0];
+-
+-	return change;
+-}
+-
+-
+-static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol, 
+-                                       struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+-	int reg = kcontrol->private_value;
+-
+-	snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+-	ucontrol->value.integer.value[0] = 
+-		(snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
+-  
+-	return 0;
+-}
+-
+-static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol, 
+-                                       struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
+-	int reg = kcontrol->private_value;
+-	int val = snd_cs46xx_peekBA0(chip, reg);
+-	int oldval = val;
+-	snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+-
+-	if (ucontrol->value.integer.value[0])
+-		val |= (1 << chip->current_gpio);
+-	else
+-		val &= ~(1 << chip->current_gpio);
+-
+-	snd_cs46xx_pokeBA0(chip, reg,val);
+-	snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
+-
+-	return (oldval != val);
+-}
+-#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
+-
+ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
+ {
+ 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+@@ -2241,40 +2175,6 @@ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
+ },
+ 
+ #endif
+-#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+-{
+-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-	.name = "EGPIO select",
+-	.info = snd_cs46xx_egpio_select_info,
+-	.get = snd_cs46xx_egpio_select_get,
+-	.put = snd_cs46xx_egpio_select_put,
+-	.private_value = 0,
+-},
+-{
+-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-	.name = "EGPIO Input/Output",
+-	.info = snd_mixer_boolean_info,
+-	.get = snd_cs46xx_egpio_get,
+-	.put = snd_cs46xx_egpio_put,
+-	.private_value = BA0_EGPIODR,
+-},
+-{
+-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-	.name = "EGPIO CMOS/Open drain",
+-	.info = snd_mixer_boolean_info,
+-	.get = snd_cs46xx_egpio_get,
+-	.put = snd_cs46xx_egpio_put,
+-	.private_value = BA0_EGPIOPTR,
+-},
+-{
+-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-	.name = "EGPIO On/Off",
+-	.info = snd_mixer_boolean_info,
+-	.get = snd_cs46xx_egpio_get,
+-	.put = snd_cs46xx_egpio_put,
+-	.private_value = BA0_EGPIOSR,
+-},
+-#endif
+ };
+ 
+ #ifdef CONFIG_SND_CS46XX_NEW_DSP
+diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
+index 590b35d..ccc8bed 100644
+--- a/sound/pci/cs46xx/dsp_spos.c
++++ b/sound/pci/cs46xx/dsp_spos.c
+@@ -20,7 +20,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/pm.h>
+diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
+index eded4df..2873cfe 100644
+--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
++++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
+@@ -21,7 +21,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/pm.h>
+diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
+index 240a0a4..7ff8b68 100644
+--- a/sound/pci/cs5530.c
++++ b/sound/pci/cs5530.c
+@@ -36,7 +36,6 @@
+  *	same manner.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/moduleparam.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
+index 2b35889..1d8b160 100644
+--- a/sound/pci/cs5535audio/cs5535audio.c
++++ b/sound/pci/cs5535audio/cs5535audio.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/init.h>
+@@ -145,7 +144,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
+ 	return snd_cs5535audio_codec_read(cs5535au, reg);
+ }
+ 
+-static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
++static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
+ {
+ 	struct snd_card *card = cs5535au->card;
+ 	struct snd_ac97_bus *pbus;
+diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
+index 21df063..cdcda87 100644
+--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
++++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
+@@ -25,7 +25,6 @@
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/pci.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/initval.h>
+@@ -98,6 +97,8 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
+ 	struct snd_pcm_runtime *runtime = substream->runtime;
+ 
+ 	runtime->hw = snd_cs5535audio_playback;
++	runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC];
++	snd_pcm_limit_hw_rates(runtime);
+ 	cs5535au->playback_substream = substream;
+ 	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
+ 	if ((err = snd_pcm_hw_constraint_integer(runtime,
+@@ -343,6 +344,8 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
+ 	struct snd_pcm_runtime *runtime = substream->runtime;
+ 
+ 	runtime->hw = snd_cs5535audio_capture;
++	runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC];
++	snd_pcm_limit_hw_rates(runtime);
+ 	cs5535au->capture_substream = substream;
+ 	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
+ 	if ((err = snd_pcm_hw_constraint_integer(runtime,
+diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
+index 838708f..564c33b 100644
+--- a/sound/pci/cs5535audio/cs5535audio_pm.c
++++ b/sound/pci/cs5535audio/cs5535audio_pm.c
+@@ -22,7 +22,6 @@
+ #include <linux/slab.h>
+ #include <linux/pci.h>
+ #include <linux/delay.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/initval.h>
+diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
+index 87078d3..8c6db3a 100644
+--- a/sound/pci/echoaudio/darla20.c
++++ b/sound/pci/echoaudio/darla20.c
+@@ -36,7 +36,6 @@
+ #define BX_NUM		10
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
+index 42b48f9..04cbf3e 100644
+--- a/sound/pci/echoaudio/darla24.c
++++ b/sound/pci/echoaudio/darla24.c
+@@ -40,7 +40,6 @@
+ #define BX_NUM		10
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
+index 8dbb7ac..4022e43 100644
+--- a/sound/pci/echoaudio/echo3g.c
++++ b/sound/pci/echoaudio/echo3g.c
+@@ -47,7 +47,6 @@
+ #define BX_NUM		chip->bx_num
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
+index 499ee1a..90ec090 100644
+--- a/sound/pci/echoaudio/echoaudio.c
++++ b/sound/pci/echoaudio/echoaudio.c
+@@ -378,7 +378,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
+ 
+ 	DE_ACT(("pcm_digital_in_open\n"));
+ 	max_channels = num_digital_busses_in(chip) - substream->number;
+-	down(&chip->mode_mutex);
++	mutex_lock(&chip->mode_mutex);
+ 	if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ 		err = pcm_open(substream, max_channels);
+ 	else	/* If the card has ADAT, subtract the 6 channels
+@@ -405,7 +405,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
+ 		chip->can_set_rate=0;
+ 
+ din_exit:
+-	up(&chip->mode_mutex);
++	mutex_unlock(&chip->mode_mutex);
+ 	return err;
+ }
+ 
+@@ -420,7 +420,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
+ 
+ 	DE_ACT(("pcm_digital_out_open\n"));
+ 	max_channels = num_digital_busses_out(chip) - substream->number;
+-	down(&chip->mode_mutex);
++	mutex_lock(&chip->mode_mutex);
+ 	if (chip->digital_mode == DIGITAL_MODE_ADAT)
+ 		err = pcm_open(substream, max_channels);
+ 	else	/* If the card has ADAT, subtract the 6 channels
+@@ -447,7 +447,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
+ 	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+ 		chip->can_set_rate=0;
+ dout_exit:
+-	up(&chip->mode_mutex);
++	mutex_unlock(&chip->mode_mutex);
+ 	return err;
+ }
+ 
+@@ -1420,7 +1420,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
+ 	if (dmode != chip->digital_mode) {
+ 		/* mode_mutex is required to make this operation atomic wrt
+ 		pcm_digital_*_open() and set_input_clock() functions. */
+-		down(&chip->mode_mutex);
++		mutex_lock(&chip->mode_mutex);
+ 
+ 		/* Do not allow the user to change the digital mode when a pcm
+ 		device is open because it also changes the number of channels
+@@ -1439,7 +1439,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
+ 			if (changed >= 0)
+ 				changed = 1;	/* No errors */
+ 		}
+-		up(&chip->mode_mutex);
++		mutex_unlock(&chip->mode_mutex);
+ 	}
+ 	return changed;
+ }
+@@ -1566,12 +1566,12 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
+ 		return -EINVAL;
+ 	dclock = chip->clock_source_list[eclock];
+ 	if (chip->input_clock != dclock) {
+-		down(&chip->mode_mutex);
++		mutex_lock(&chip->mode_mutex);
+ 		spin_lock_irq(&chip->lock);
+ 		if ((changed = set_input_clock(chip, dclock)) == 0)
+ 			changed = 1;	/* no errors */
+ 		spin_unlock_irq(&chip->lock);
+-		up(&chip->mode_mutex);
++		mutex_unlock(&chip->mode_mutex);
+ 	}
+ 
+ 	if (changed < 0)
+@@ -1972,7 +1972,7 @@ static __devinit int snd_echo_create(struct snd_card *card,
+ 		return err;
+ 	}
+ 	atomic_set(&chip->opencount, 0);
+-	init_MUTEX(&chip->mode_mutex);
++	mutex_init(&chip->mode_mutex);
+ 	chip->can_set_rate = 1;
+ 	*rchip = chip;
+ 	/* Init done ! */
+diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
+index 7e88c96..1c88e05 100644
+--- a/sound/pci/echoaudio/echoaudio.h
++++ b/sound/pci/echoaudio/echoaudio.h
+@@ -361,7 +361,7 @@ struct echoaudio {
+ 	spinlock_t lock;
+ 	struct snd_pcm_substream *substream[DSP_MAXPIPES];
+ 	int last_period[DSP_MAXPIPES];
+-	struct semaphore mode_mutex;
++	struct mutex mode_mutex;
+ 	u16 num_digital_modes, digital_mode_list[6];
+ 	u16 num_clock_sources, clock_source_list[10];
+ 	atomic_t opencount;
+diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
+index fee2d48..c0e64b8 100644
+--- a/sound/pci/echoaudio/gina20.c
++++ b/sound/pci/echoaudio/gina20.c
+@@ -40,7 +40,6 @@
+ #define BX_NUM		14
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
+index d5eae47..c36a78d 100644
+--- a/sound/pci/echoaudio/gina24.c
++++ b/sound/pci/echoaudio/gina24.c
+@@ -46,7 +46,6 @@
+ #define BX_NUM		26
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
+index 40f601c..0a58a7c 100644
+--- a/sound/pci/echoaudio/indigo.c
++++ b/sound/pci/echoaudio/indigo.c
+@@ -38,7 +38,6 @@
+ #define BX_NUM		2
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
+index 771c538..2db24d2 100644
+--- a/sound/pci/echoaudio/indigodj.c
++++ b/sound/pci/echoaudio/indigodj.c
+@@ -38,7 +38,6 @@
+ #define BX_NUM		4
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
+index 49c550d..a60c0a0 100644
+--- a/sound/pci/echoaudio/indigoio.c
++++ b/sound/pci/echoaudio/indigoio.c
+@@ -39,7 +39,6 @@
+ #define BX_NUM		4
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
+index 8f5483a..5061946 100644
+--- a/sound/pci/echoaudio/layla20.c
++++ b/sound/pci/echoaudio/layla20.c
+@@ -45,7 +45,6 @@
+ #define BX_NUM		22
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
+index 0524667..e09e3ea 100644
+--- a/sound/pci/echoaudio/layla24.c
++++ b/sound/pci/echoaudio/layla24.c
+@@ -47,7 +47,6 @@
+ #define BX_NUM		32
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
+index 893c7c2..f3b9b45 100644
+--- a/sound/pci/echoaudio/mia.c
++++ b/sound/pci/echoaudio/mia.c
+@@ -45,7 +45,6 @@
+ #define BX_NUM		8
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
+index 3a5d5b0..b05bad9 100644
+--- a/sound/pci/echoaudio/mona.c
++++ b/sound/pci/echoaudio/mona.c
+@@ -44,7 +44,6 @@
+ #define BX_NUM		26
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
+index 9680caf..8354c1a 100644
+--- a/sound/pci/emu10k1/emu10k1.c
++++ b/sound/pci/emu10k1/emu10k1.c
+@@ -23,7 +23,6 @@
+  * 
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/time.h>
+diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
+index 01965bd..45088eb 100644
+--- a/sound/pci/emu10k1/emu10k1_callback.c
++++ b/sound/pci/emu10k1/emu10k1_callback.c
+@@ -35,9 +35,9 @@ struct best_voice {
+ /*
+  * prototypes
+  */
+-static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
++static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
+ 			  struct best_voice *best, int active_only);
+-static struct snd_emux_voice *get_voice(struct snd_emux *emu,
++static struct snd_emux_voice *get_voice(struct snd_emux *emux,
+ 					struct snd_emux_port *port);
+ static int start_voice(struct snd_emux_voice *vp);
+ static void trigger_voice(struct snd_emux_voice *vp);
+@@ -45,7 +45,6 @@ static void release_voice(struct snd_emux_voice *vp);
+ static void update_voice(struct snd_emux_voice *vp, int update);
+ static void terminate_voice(struct snd_emux_voice *vp);
+ static void free_voice(struct snd_emux_voice *vp);
+-
+ static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+ static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+ static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+@@ -75,9 +74,9 @@ static struct snd_emux_operators emu10k1_ops = {
+ };
+ 
+ void
+-snd_emu10k1_ops_setup(struct snd_emux *emu)
++snd_emu10k1_ops_setup(struct snd_emux *emux)
+ {
+-	emu->ops = emu10k1_ops;
++	emux->ops = emu10k1_ops;
+ }
+ 
+ 
+@@ -166,7 +165,11 @@ free_voice(struct snd_emux_voice *vp)
+ 	struct snd_emu10k1 *hw;
+ 	
+ 	hw = vp->hw;
+-	if (vp->ch >= 0) {
++	/* FIXME: emu10k1_synth is broken. */
++	/* This can get called with hw == 0 */
++	/* Problem apparent on plug, unplug then plug */
++	/* on the Audigy 2 ZS Notebook. */
++	if (hw && (vp->ch >= 0)) {
+ 		snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
+ 		snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+ 		// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
+diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
+index 97c41d7..9a9b977 100644
+--- a/sound/pci/emu10k1/emu10k1_main.c
++++ b/sound/pci/emu10k1/emu10k1_main.c
+@@ -33,7 +33,6 @@
+ 
+ #include <linux/sched.h>
+ #include <linux/kthread.h>
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -55,12 +54,14 @@
+ #define DOCK_FILENAME "emu/audio_dock.fw"
+ #define EMU1010B_FILENAME "emu/emu1010b.fw"
+ #define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
++#define EMU0404_FILENAME "emu/emu0404.fw"
+ #define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
+ 
+ MODULE_FIRMWARE(HANA_FILENAME);
+ MODULE_FIRMWARE(DOCK_FILENAME);
+ MODULE_FIRMWARE(EMU1010B_FILENAME);
+ MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
++MODULE_FIRMWARE(EMU0404_FILENAME);
+ MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
+ 
+ 
+@@ -258,7 +259,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+ 		 * GPIO7: Unknown
+ 		 */
+ 		outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
+-
+ 	}
+ 	if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
+ 		int size, n;
+@@ -274,7 +274,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+ 			emu->i2c_capture_volume[n][0]= 0xcf;
+ 			emu->i2c_capture_volume[n][1]= 0xcf;
+ 		}
+-
+ 	}
+ 
+ 	
+@@ -288,7 +287,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+ 		snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
+ 	}
+ 
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		outl(HCFG_AUTOMUTE_ASYNC |
+ 			HCFG_EMU32_SLAVE |
+ 			HCFG_AUDIOENABLE, emu->port + HCFG);
+@@ -318,7 +317,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+ 		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+ 
+ 	if (enable_ir) {	/* enable IR for SB Live */
+-		if (emu->card_capabilities->emu1010) {
++		if (emu->card_capabilities->emu_model) {
+ 			;  /* Disable all access to A_IOCFG for the emu1010 */
+ 		} else if (emu->card_capabilities->i2c_adc) {
+ 			;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+@@ -339,7 +338,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+  		}
+ 	}
+ 	
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		;  /* Disable all access to A_IOCFG for the emu1010 */
+ 	} else if (emu->card_capabilities->i2c_adc) {
+ 		;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+@@ -359,7 +358,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
+ 	outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
+ 
+ 	/* Enable analog/digital outs on audigy */
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		;  /* Disable all access to A_IOCFG for the emu1010 */
+ 	} else if (emu->card_capabilities->i2c_adc) {
+ 		;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+@@ -652,6 +651,8 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
+ 	value = inl(special_port);
+ 
+ 	snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
++	/* Delay to give time for ADC chip to switch on. It needs 113ms */
++	msleep(200);
+ 	return 0;
+ }
+ 
+@@ -661,6 +662,8 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
+ 	int n, i;
+ 	int reg;
+ 	int value;
++	unsigned int write_post;
++	unsigned long flags;
+ 	const struct firmware *fw_entry;
+ 
+ 	if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
+@@ -668,12 +671,6 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
+ 		return err;
+ 	}
+ 	snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
+-#if 0
+-	if (fw_entry->size != 0x133a4) {
+-		snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
+-		return -EINVAL;
+-	}
+-#endif
+ 
+ 	/* The FPGA is a Xilinx Spartan IIE XC2S50E */
+ 	/* GPIO7 -> FPGA PGMN
+@@ -681,9 +678,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
+ 	 * GPIO5 -> FPGA DIN
+ 	 * FPGA CONFIG OFF -> FPGA PGMN
+ 	 */
++	spin_lock_irqsave(&emu->emu_lock, flags);
+ 	outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
+-	udelay(1);
++	write_post = inl(emu->port + A_IOCFG);
++	udelay(100);
+ 	outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
++	write_post = inl(emu->port + A_IOCFG);
+ 	udelay(100); /* Allow FPGA memory to clean */
+ 	for(n = 0; n < fw_entry->size; n++) {
+ 		value=fw_entry->data[n];	
+@@ -693,18 +693,22 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
+ 				reg = reg | 0x20;
+ 			value = value >> 1;   
+ 			outl(reg, emu->port + A_IOCFG);
++			write_post = inl(emu->port + A_IOCFG);
+ 			outl(reg | 0x40, emu->port + A_IOCFG);
++			write_post = inl(emu->port + A_IOCFG);
+ 		}
+ 	}
+ 	/* After programming, set GPIO bit 4 high again. */
+ 	outl(0x10, emu->port + A_IOCFG);
+-	
++	write_post = inl(emu->port + A_IOCFG);
++	spin_unlock_irqrestore(&emu->emu_lock, flags);
+ 
+         release_firmware(fw_entry);
+ 	return 0;
+ }
+ 
+-int emu1010_firmware_thread(void *data) {
++static int emu1010_firmware_thread(void *data)
++{
+ 	struct snd_emu10k1 * emu = data;
+ 	int tmp,tmp2;
+ 	int reg;
+@@ -712,7 +716,7 @@ int emu1010_firmware_thread(void *data) {
+ 
+ 	for (;;) {
+ 		/* Delay to allow Audio Dock to settle */
+-		msleep(1000);
++		msleep_interruptible(1000);
+ 		if (kthread_should_stop())
+ 			break;
+ 		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
+@@ -722,17 +726,20 @@ int emu1010_firmware_thread(void *data) {
+ 			/* Return to Audio Dock programming mode */
+ 			snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
+ 			snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
+-			if (emu->card_capabilities->emu1010 == 1) {
++			if (emu->card_capabilities->emu_model ==
++			    EMU_MODEL_EMU1010) {
+ 				if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+-					return err;
++					continue;
+ 				}
+-			} else if (emu->card_capabilities->emu1010 == 2) {
++			} else if (emu->card_capabilities->emu_model ==
++				   EMU_MODEL_EMU1010B) {
+ 				if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+-					return err;
++					continue;
+ 				}
+-			} else if (emu->card_capabilities->emu1010 == 3) {
++			} else if (emu->card_capabilities->emu_model ==
++				   EMU_MODEL_EMU1616) {
+ 				if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+-					return err;
++					continue;
+ 				}
+ 			}
+ 
+@@ -745,8 +752,7 @@ int emu1010_firmware_thread(void *data) {
+ 			if ((reg & 0x1f) != 0x15) {
+ 				/* FPGA failed to be programmed */
+ 				snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
+-				return 0;
+-				return -ENODEV;
++				continue;
+ 			}
+ 			snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
+ 			snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
+@@ -757,9 +763,9 @@ int emu1010_firmware_thread(void *data) {
+ 			msleep(10);
+ 			/* Unmute all. Default is muted after a firmware load */
+ 			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+-			break;
+ 		}
+ 	}
++	snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
+ 	return 0;
+ }
+ 
+@@ -800,6 +806,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+ 	int tmp,tmp2;
+ 	int reg;
+ 	int err;
++	const char *filename = NULL;
+ 
+ 	snd_printk(KERN_INFO "emu1010: Special config.\n");
+ 	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+@@ -841,21 +848,31 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+ 		return -ENODEV;
+ 	}
+ 	snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
+-	if (emu->card_capabilities->emu1010 == 1) {
+-		if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
+-			snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
+-			return err;
+-		}
+-	} else if (emu->card_capabilities->emu1010 == 2) {
+-		if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) {
+-			snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME);
+-			return err;
+-		}
+-	} else if (emu->card_capabilities->emu1010 == 3) {
+-		if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) {
+-			snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME);
+-			return err;
+-		}
++	switch (emu->card_capabilities->emu_model) {
++	case EMU_MODEL_EMU1010:
++		filename = HANA_FILENAME;
++		break;
++	case EMU_MODEL_EMU1010B:
++		filename = EMU1010B_FILENAME;
++		break;
++	case EMU_MODEL_EMU1616:
++		filename = EMU1010_NOTEBOOK_FILENAME;
++		break;
++	case EMU_MODEL_EMU0404:
++		filename = EMU0404_FILENAME;
++		break;
++	default:
++		filename = NULL;
++		return -ENODEV;
++		break;
++	}
++	snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename);
++	err = snd_emu1010_load_firmware(emu, filename);
++	if (err != 0) {
++		snd_printk(
++			KERN_INFO "emu1010: Loading Firmware file %s failed\n",
++			filename);
++		return err;
+ 	}
+ 
+ 	/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+@@ -1074,10 +1091,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+ 	snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
+ 
+ 	/* Start Micro/Audio Dock firmware loader thread */
+-	emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread,
+-                                   emu,
+-                                   "emu1010_firmware");
+-	wake_up_process(emu->emu1010.firmware_thread);
++	if (!emu->emu1010.firmware_thread) {
++		emu->emu1010.firmware_thread =
++			kthread_create(emu1010_firmware_thread, emu,
++				       "emu1010_firmware");
++		wake_up_process(emu->emu1010.firmware_thread);
++	}
+ 
+ #if 0
+ 	snd_emu1010_fpga_link_dst_src_write(emu,
+@@ -1090,79 +1109,114 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+ 		EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
+ #endif
+ 	/* Default outputs */
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+-	emu->emu1010.output_source[0] = 21;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+-	emu->emu1010.output_source[1] = 22;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+-	emu->emu1010.output_source[2] = 23;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+-	emu->emu1010.output_source[3] = 24;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+-	emu->emu1010.output_source[4] = 25;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+-	emu->emu1010.output_source[5] = 26;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
+-	emu->emu1010.output_source[6] = 27;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
+-	emu->emu1010.output_source[7] = 28;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+-	emu->emu1010.output_source[8] = 21;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+-	emu->emu1010.output_source[9] = 22;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+-	emu->emu1010.output_source[10] = 21;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+-	emu->emu1010.output_source[11] = 22;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+-	emu->emu1010.output_source[12] = 21;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+-	emu->emu1010.output_source[13] = 22;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+-	emu->emu1010.output_source[14] = 21;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+-	emu->emu1010.output_source[15] = 22;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */
+-	emu->emu1010.output_source[16] = 21;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
+-	emu->emu1010.output_source[17] = 22;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
+-	emu->emu1010.output_source[18] = 23;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
+-	emu->emu1010.output_source[19] = 24;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
+-	emu->emu1010.output_source[20] = 25;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
+-	emu->emu1010.output_source[21] = 26;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
+-	emu->emu1010.output_source[22] = 27;
+-	snd_emu1010_fpga_link_dst_src_write(emu,
+-		EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
+-	emu->emu1010.output_source[23] = 28;
+-
++	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
++		/* 1616(M) cardbus default outputs */
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[0] = 17;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[1] = 18;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
++		emu->emu1010.output_source[2] = 19;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
++		emu->emu1010.output_source[3] = 20;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
++		emu->emu1010.output_source[4] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
++		emu->emu1010.output_source[5] = 22;
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[16] = 17;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[17] = 18;
++	} else {
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[0] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[1] = 22;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
++		emu->emu1010.output_source[2] = 23;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
++		emu->emu1010.output_source[3] = 24;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
++		emu->emu1010.output_source[4] = 25;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
++		emu->emu1010.output_source[5] = 26;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
++		emu->emu1010.output_source[6] = 27;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
++		emu->emu1010.output_source[7] = 28;
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[8] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[9] = 22;
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[10] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[11] = 22;
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[12] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[13] = 22;
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[14] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[15] = 22;
++		/* ALICE2 bus 0xa0 */
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0);
++		emu->emu1010.output_source[16] = 21;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
++		emu->emu1010.output_source[17] = 22;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
++		emu->emu1010.output_source[18] = 23;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
++		emu->emu1010.output_source[19] = 24;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
++		emu->emu1010.output_source[20] = 25;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
++		emu->emu1010.output_source[21] = 26;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
++		emu->emu1010.output_source[22] = 27;
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
++		emu->emu1010.output_source[23] = 28;
++	}
+ 	/* TEMP: Select SPDIF in/out */
+ 	//snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+ 
+@@ -1202,11 +1256,12 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
+ 		}
+ 		snd_emu10k1_free_efx(emu);
+        	}
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
+ 		/* Disable 48Volt power to Audio Dock */
+ 		snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
+-		kthread_stop(emu->emu1010.firmware_thread);
+ 	}
++	if (emu->emu1010.firmware_thread)
++		kthread_stop(emu->emu1010.firmware_thread);
+ 	if (emu->memhdr)
+ 		snd_util_memhdr_free(emu->memhdr);
+ 	if (emu->silent_page.area)
+@@ -1338,6 +1393,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
+ 	 .spi_dac = 1,
+ 	 .i2c_adc = 1,
+ 	 .spk71 = 1} ,
++	/* Tested by James at superbug.co.uk 4th Nov 2007. */
+ 	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
+ 	 .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", 
+ 	 .id = "EMU1010",
+@@ -1345,28 +1401,46 @@ static struct snd_emu_chip_details emu_chip_details[] = {
+ 	 .ca0108_chip = 1,
+ 	 .ca_cardbus_chip = 1,
+ 	 .spk71 = 1 ,
+-	 .emu1010 = 3} ,
++	 .emu_model = EMU_MODEL_EMU1616},
++	/* Tested by James at superbug.co.uk 4th Nov 2007. */
+ 	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
+ 	 .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", 
+ 	 .id = "EMU1010",
+ 	 .emu10k2_chip = 1,
+ 	 .ca0108_chip = 1,
+-	 .spk71 = 1 ,
+-	 .emu1010 = 2} ,
++	 .spk71 = 1,
++	 .emu_model = EMU_MODEL_EMU1010B},
++	/* Tested by James at superbug.co.uk 8th July 2005. */
++	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
++	 .driver = "Audigy2", .name = "E-mu 1010 [4001]",
++	 .id = "EMU1010",
++	 .emu10k2_chip = 1,
++	 .ca0102_chip = 1,
++	 .spk71 = 1,
++	 .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */
++	/* EMU0404b */
++	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
++	 .driver = "Audigy2", .name = "E-mu 0404b [4002]",
++	 .id = "EMU0404",
++	 .emu10k2_chip = 1,
++	 .ca0108_chip = 1,
++	 .spk71 = 1,
++	 .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
++	/* Tested by James at superbug.co.uk 20-3-2007. */
++	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
++	 .driver = "Audigy2", .name = "E-mu 0404 [4002]",
++	 .id = "EMU0404",
++	 .emu10k2_chip = 1,
++	 .ca0102_chip = 1,
++	 .spk71 = 1,
++	 .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
++	/* Audigy4 (Not PRO) SB0610 */
+ 	{.vendor = 0x1102, .device = 0x0008, 
+ 	 .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
+ 	 .id = "Audigy2",
+ 	 .emu10k2_chip = 1,
+ 	 .ca0108_chip = 1,
+ 	 .ac97_chip = 1} ,
+-	/* Tested by James at superbug.co.uk 8th July 2005. No sound available yet. */
+-	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
+-	 .driver = "Audigy2", .name = "E-mu 1010 [4001]", 
+-	 .id = "EMU1010",
+-	 .emu10k2_chip = 1,
+-	 .ca0102_chip = 1,
+-	 .spk71 = 1,
+-	 .emu1010 = 1} ,
+ 	/* Tested by James at superbug.co.uk 3rd July 2005 */
+ 	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
+ 	 .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
+@@ -1654,6 +1728,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
+ 	emu->card = card;
+ 	spin_lock_init(&emu->reg_lock);
+ 	spin_lock_init(&emu->emu_lock);
++	spin_lock_init(&emu->spi_lock);
++	spin_lock_init(&emu->i2c_lock);
+ 	spin_lock_init(&emu->voice_lock);
+ 	spin_lock_init(&emu->synth_lock);
+ 	spin_lock_init(&emu->memblk_lock);
+@@ -1794,7 +1870,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
+ 	if (emu->card_capabilities->ecard) {
+ 		if ((err = snd_emu10k1_ecard_init(emu)) < 0)
+ 			goto error;
+- 	} else if (emu->card_capabilities->emu1010) {
++	} else if (emu->card_capabilities->emu_model) {
+  		if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
+  			snd_emu10k1_free(emu);
+  			return err;
+@@ -1943,7 +2019,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
+ 		snd_emu10k1_cardbus_init(emu);
+ 	if (emu->card_capabilities->ecard)
+ 		snd_emu10k1_ecard_init(emu);
+-	else if (emu->card_capabilities->emu1010)
++	else if (emu->card_capabilities->emu_model)
+  		snd_emu10k1_emu1010_init(emu);
+ 	else
+ 		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
+index 204995a..ad7b714 100644
+--- a/sound/pci/emu10k1/emu10k1_synth.c
++++ b/sound/pci/emu10k1/emu10k1_synth.c
+@@ -30,7 +30,7 @@ MODULE_LICENSE("GPL");
+  */
+ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
+ {
+-	struct snd_emux *emu;
++	struct snd_emux *emux;
+ 	struct snd_emu10k1 *hw;
+ 	struct snd_emu10k1_synth_arg *arg;
+ 	unsigned long flags;
+@@ -46,53 +46,56 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
+ 	else if (arg->max_voices > 64)
+ 		arg->max_voices = 64;
+ 
+-	if (snd_emux_new(&emu) < 0)
++	if (snd_emux_new(&emux) < 0)
+ 		return -ENOMEM;
+ 
+-	snd_emu10k1_ops_setup(emu);
+-	emu->hw = hw = arg->hwptr;
+-	emu->max_voices = arg->max_voices;
+-	emu->num_ports = arg->seq_ports;
+-	emu->pitch_shift = -501;
+-	emu->memhdr = hw->memhdr;
+-	emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */
+-	emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */
+-	emu->linear_panning = 0;
+-	emu->hwdep_idx = 2; /* FIXED */
+-
+-	if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) {
+-		snd_emux_free(emu);
++	snd_emu10k1_ops_setup(emux);
++	hw = arg->hwptr;
++	emux->hw = hw;
++	emux->max_voices = arg->max_voices;
++	emux->num_ports = arg->seq_ports;
++	emux->pitch_shift = -501;
++	emux->memhdr = hw->memhdr;
++	/* maximum two ports */
++	emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
++	/* audigy has two external midis */
++	emux->midi_devidx = hw->audigy ? 2 : 1;
++	emux->linear_panning = 0;
++	emux->hwdep_idx = 2; /* FIXED */
++
++	if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) {
++		snd_emux_free(emux);
+ 		return -ENOMEM;
+ 	}
+ 
+ 	spin_lock_irqsave(&hw->voice_lock, flags);
+-	hw->synth = emu;
++	hw->synth = emux;
+ 	hw->get_synth_voice = snd_emu10k1_synth_get_voice;
+ 	spin_unlock_irqrestore(&hw->voice_lock, flags);
+ 
+-	dev->driver_data = emu;
++	dev->driver_data = emux;
+ 
+ 	return 0;
+ }
+ 
+ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
+ {
+-	struct snd_emux *emu;
++	struct snd_emux *emux;
+ 	struct snd_emu10k1 *hw;
+ 	unsigned long flags;
+ 
+ 	if (dev->driver_data == NULL)
+ 		return 0; /* not registered actually */
+ 
+-	emu = dev->driver_data;
++	emux = dev->driver_data;
+ 
+-	hw = emu->hw;
++	hw = emux->hw;
+ 	spin_lock_irqsave(&hw->voice_lock, flags);
+ 	hw->synth = NULL;
+ 	hw->get_synth_voice = NULL;
+ 	spin_unlock_irqrestore(&hw->voice_lock, flags);
+ 
+-	snd_emux_free(emu);
++	snd_emux_free(emux);
+ 	return 0;
+ }
+ 
+diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
+index 308ddc8..25f328f 100644
+--- a/sound/pci/emu10k1/emu10k1_synth_local.h
++++ b/sound/pci/emu10k1/emu10k1_synth_local.h
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/emu10k1_synth.h>
+diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
+index 1ec7eba..5512abd 100644
+--- a/sound/pci/emu10k1/emu10k1x.c
++++ b/sound/pci/emu10k1/emu10k1x.c
+@@ -29,7 +29,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/pci.h>
+@@ -1583,6 +1582,8 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
+ 	sprintf(card->longname, "%s at 0x%lx irq %i",
+ 		card->shortname, chip->port, chip->irq);
+ 
++	snd_card_set_dev(card, &pci->dev);
++
+ 	if ((err = snd_card_register(card)) < 0) {
+ 		snd_card_free(card);
+ 		return err;
+diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
+index 9bf1cd5..71dc4c8 100644
+--- a/sound/pci/emu10k1/emufx.c
++++ b/sound/pci/emu10k1/emufx.c
+@@ -28,7 +28,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/pci.h>
+ #include <linux/capability.h>
+ #include <linux/delay.h>
+@@ -666,7 +665,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
+ 		return NULL;
+ 	if (data[1] >= MAX_TLV_SIZE)
+ 		return NULL;
+-	tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL);
++	tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL);
+ 	if (!tlv)
+ 		return NULL;
+ 	memcpy(tlv, data, sizeof(data));
+@@ -1262,7 +1261,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
+ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+ 
+ 	/* emu1212 DSP 0 and DSP 1 Capture */
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		if (emu->card_capabilities->ca0108_chip) {
+ 			/* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
+ 			A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
+@@ -1516,7 +1515,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+ 
+ 	/* digital outputs */
+ 	/* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
+ 		snd_printk("EMU outputs on\n");
+ 		for (z = 0; z < 8; z++) {
+@@ -1564,7 +1563,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+ 	A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
+ #endif
+ 
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		if (emu->card_capabilities->ca0108_chip) {
+ 			snd_printk("EMU2 inputs on\n");
+ 			for (z = 0; z < 0x10; z++) {
+diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
+index ccacd7b..fd22120 100644
+--- a/sound/pci/emu10k1/emumixer.c
++++ b/sound/pci/emu10k1/emumixer.c
+@@ -30,7 +30,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+@@ -140,6 +139,61 @@ static char *emu1010_src_texts[] = {
+ 	"DSP 31",
+ };
+ 
++/* 1616(m) cardbus */
++
++static char *emu1616_src_texts[] = {
++	"Silence",
++	"Dock Mic A",
++	"Dock Mic B",
++	"Dock ADC1 Left",
++	"Dock ADC1 Right",
++	"Dock ADC2 Left",
++	"Dock ADC2 Right",
++	"Dock SPDIF Left",
++	"Dock SPDIF Right",
++	"ADAT 0",
++	"ADAT 1",
++	"ADAT 2",
++	"ADAT 3",
++	"ADAT 4",
++	"ADAT 5",
++	"ADAT 6",
++	"ADAT 7",
++	"DSP 0",
++	"DSP 1",
++	"DSP 2",
++	"DSP 3",
++	"DSP 4",
++	"DSP 5",
++	"DSP 6",
++	"DSP 7",
++	"DSP 8",
++	"DSP 9",
++	"DSP 10",
++	"DSP 11",
++	"DSP 12",
++	"DSP 13",
++	"DSP 14",
++	"DSP 15",
++	"DSP 16",
++	"DSP 17",
++	"DSP 18",
++	"DSP 19",
++	"DSP 20",
++	"DSP 21",
++	"DSP 22",
++	"DSP 23",
++	"DSP 24",
++	"DSP 25",
++	"DSP 26",
++	"DSP 27",
++	"DSP 28",
++	"DSP 29",
++	"DSP 30",
++	"DSP 31",
++};
++
++
+ /*
+  * List of data sources available for each destination
+  */
+@@ -199,6 +253,59 @@ static unsigned int emu1010_src_regs[] = {
+ 	EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+ };
+ 
++/* 1616(m) cardbus */
++static unsigned int emu1616_src_regs[] = {
++	EMU_SRC_SILENCE,
++	EMU_SRC_DOCK_MIC_A1,
++	EMU_SRC_DOCK_MIC_B1,
++	EMU_SRC_DOCK_ADC1_LEFT1,
++	EMU_SRC_DOCK_ADC1_RIGHT1,
++	EMU_SRC_DOCK_ADC2_LEFT1,
++	EMU_SRC_DOCK_ADC2_RIGHT1,
++	EMU_SRC_MDOCK_SPDIF_LEFT1,
++	EMU_SRC_MDOCK_SPDIF_RIGHT1,
++	EMU_SRC_MDOCK_ADAT,
++	EMU_SRC_MDOCK_ADAT+1,
++	EMU_SRC_MDOCK_ADAT+2,
++	EMU_SRC_MDOCK_ADAT+3,
++	EMU_SRC_MDOCK_ADAT+4,
++	EMU_SRC_MDOCK_ADAT+5,
++	EMU_SRC_MDOCK_ADAT+6,
++	EMU_SRC_MDOCK_ADAT+7,
++	EMU_SRC_ALICE_EMU32A,
++	EMU_SRC_ALICE_EMU32A+1,
++	EMU_SRC_ALICE_EMU32A+2,
++	EMU_SRC_ALICE_EMU32A+3,
++	EMU_SRC_ALICE_EMU32A+4,
++	EMU_SRC_ALICE_EMU32A+5,
++	EMU_SRC_ALICE_EMU32A+6,
++	EMU_SRC_ALICE_EMU32A+7,
++	EMU_SRC_ALICE_EMU32A+8,
++	EMU_SRC_ALICE_EMU32A+9,
++	EMU_SRC_ALICE_EMU32A+0xa,
++	EMU_SRC_ALICE_EMU32A+0xb,
++	EMU_SRC_ALICE_EMU32A+0xc,
++	EMU_SRC_ALICE_EMU32A+0xd,
++	EMU_SRC_ALICE_EMU32A+0xe,
++	EMU_SRC_ALICE_EMU32A+0xf,
++	EMU_SRC_ALICE_EMU32B,
++	EMU_SRC_ALICE_EMU32B+1,
++	EMU_SRC_ALICE_EMU32B+2,
++	EMU_SRC_ALICE_EMU32B+3,
++	EMU_SRC_ALICE_EMU32B+4,
++	EMU_SRC_ALICE_EMU32B+5,
++	EMU_SRC_ALICE_EMU32B+6,
++	EMU_SRC_ALICE_EMU32B+7,
++	EMU_SRC_ALICE_EMU32B+8,
++	EMU_SRC_ALICE_EMU32B+9,
++	EMU_SRC_ALICE_EMU32B+0xa,
++	EMU_SRC_ALICE_EMU32B+0xb,
++	EMU_SRC_ALICE_EMU32B+0xc,
++	EMU_SRC_ALICE_EMU32B+0xd,
++	EMU_SRC_ALICE_EMU32B+0xe,
++	EMU_SRC_ALICE_EMU32B+0xf,
++};
++
+ /*
+  * Data destinations - physical EMU outputs.
+  * Each destination has an enum mixer control to choose a data source
+@@ -230,6 +337,28 @@ static unsigned int emu1010_output_dst[] = {
+ 	EMU_DST_HANA_ADAT+7, /* 23 */
+ };
+ 
++/* 1616(m) cardbus */
++static unsigned int emu1616_output_dst[] = {
++	EMU_DST_DOCK_DAC1_LEFT1,
++	EMU_DST_DOCK_DAC1_RIGHT1,
++	EMU_DST_DOCK_DAC2_LEFT1,
++	EMU_DST_DOCK_DAC2_RIGHT1,
++	EMU_DST_DOCK_DAC3_LEFT1,
++	EMU_DST_DOCK_DAC3_RIGHT1,
++	EMU_DST_MDOCK_SPDIF_LEFT1,
++	EMU_DST_MDOCK_SPDIF_RIGHT1,
++	EMU_DST_MDOCK_ADAT,
++	EMU_DST_MDOCK_ADAT+1,
++	EMU_DST_MDOCK_ADAT+2,
++	EMU_DST_MDOCK_ADAT+3,
++	EMU_DST_MDOCK_ADAT+4,
++	EMU_DST_MDOCK_ADAT+5,
++	EMU_DST_MDOCK_ADAT+6,
++	EMU_DST_MDOCK_ADAT+7,
++	EMU_DST_MANA_DAC_LEFT,
++	EMU_DST_MANA_DAC_RIGHT,
++};
++
+ /*
+  * Data destinations - HANA outputs going to Alice2 (audigy) for
+  *   capture (EMU32 + I2S links)
+@@ -260,14 +389,26 @@ static unsigned int emu1010_input_dst[] = {
+ 	EMU_DST_ALICE_I2S2_RIGHT,
+ };
+ 
+-static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
++static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
++						struct snd_ctl_elem_info *uinfo)
+ {
++	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
++	char **items;
++
+ 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ 	uinfo->count = 1;
+-	uinfo->value.enumerated.items = 53;
++	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
++		uinfo->value.enumerated.items = 49;
++		items = emu1616_src_texts;
++	} else {
++		uinfo->value.enumerated.items = 53;
++		items = emu1010_src_texts;
++	}
+ 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+-		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+-	strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]);
++		uinfo->value.enumerated.item =
++			uinfo->value.enumerated.items - 1;
++	strcpy(uinfo->value.enumerated.name,
++	       items[uinfo->value.enumerated.item]);
+ 	return 0;
+ }
+ 
+@@ -279,7 +420,9 @@ static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
+ 
+ 	channel = (kcontrol->private_value) & 0xff;
+ 	/* Limit: emu1010_output_dst, emu->emu1010.output_source */
+-	if (channel >= 24)
++	if (channel >= 24 ||
++	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
++	     channel >= 18))
+ 		return -EINVAL;
+ 	ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
+ 	return 0;
+@@ -289,24 +432,30 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+-	int change = 0;
+ 	unsigned int val;
+ 	unsigned int channel;
+ 
+ 	val = ucontrol->value.enumerated.item[0];
+-	if (val >= 53)
++	if (val >= 53 ||
++	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
++	     val >= 49))
+ 		return -EINVAL;
+ 	channel = (kcontrol->private_value) & 0xff;
+ 	/* Limit: emu1010_output_dst, emu->emu1010.output_source */
+-	if (channel >= 24)
++	if (channel >= 24 ||
++	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
++	     channel >= 18))
+ 		return -EINVAL;
+-	if (emu->emu1010.output_source[channel] != val) {
+-		emu->emu1010.output_source[channel] = val;
+-		change = 1;
++	if (emu->emu1010.output_source[channel] == val)
++		return 0;
++	emu->emu1010.output_source[channel] = val;
++	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			emu1616_output_dst[channel], emu1616_src_regs[val]);
++	else
+ 		snd_emu1010_fpga_link_dst_src_write(emu,
+ 			emu1010_output_dst[channel], emu1010_src_regs[val]);
+-	}
+-	return change;
++	return 1;
+ }
+ 
+ static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
+@@ -327,24 +476,28 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+-	int change = 0;
+ 	unsigned int val;
+ 	unsigned int channel;
+ 
+ 	val = ucontrol->value.enumerated.item[0];
+-	if (val >= 53)
++	if (val >= 53 ||
++	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
++	     val >= 49))
+ 		return -EINVAL;
+ 	channel = (kcontrol->private_value) & 0xff;
+ 	/* Limit: emu1010_input_dst, emu->emu1010.input_source */
+ 	if (channel >= 22)
+ 		return -EINVAL;
+-	if (emu->emu1010.input_source[channel] != val) {
+-		emu->emu1010.input_source[channel] = val;
+-		change = 1;
++	if (emu->emu1010.input_source[channel] == val)
++		return 0;
++	emu->emu1010.input_source[channel] = val;
++	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
++		snd_emu1010_fpga_link_dst_src_write(emu,
++			emu1010_input_dst[channel], emu1616_src_regs[val]);
++	else
+ 		snd_emu1010_fpga_link_dst_src_write(emu,
+ 			emu1010_input_dst[channel], emu1010_src_regs[val]);
+-	}
+-	return change;
++	return 1;
+ }
+ 
+ #define EMU1010_SOURCE_OUTPUT(xname,chid) \
+@@ -384,6 +537,30 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
+ 	EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
+ };
+ 
++
++/* 1616(m) cardbus */
++static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = {
++	EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
++	EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
++	EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
++	EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
++	EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
++	EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
++	EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6),
++	EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe),
++	EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf),
++	EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10),
++	EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11),
++};
++
++
+ #define EMU1010_SOURCE_INPUT(xname,chid) \
+ {								\
+ 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+@@ -1793,7 +1970,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+ 			return err;
+ 	}
+ 
+-	if ( emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		;  /* Disable the snd_audigy_spdif_shared_spdif */
+ 	} else if (emu->audigy) {
+ 		if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
+@@ -1818,30 +1995,73 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+ 			return err;
+ 	}
+ 
+-	if ( emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
++		/* 1616(m) cardbus */
++		int i;
++
++		for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
++					     emu));
++			if (err < 0)
++				return err;
++		}
++		for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
++					     emu));
++			if (err < 0)
++				return err;
++		}
++		for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
++			if (err < 0)
++				return err;
++		}
++		for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
++			if (err < 0)
++				return err;
++		}
++		err = snd_ctl_add(card,
++			snd_ctl_new1(&snd_emu1010_internal_clock, emu));
++		if (err < 0)
++			return err;
++
++	} else if (emu->card_capabilities->emu_model) {
++		/* all other e-mu cards for now */
+ 		int i;
+ 
+ 		for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
+-			err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu));
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
++					     emu));
+ 			if (err < 0)
+ 				return err;
+ 		}
+ 		for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+-			err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu));
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
++					     emu));
+ 			if (err < 0)
+ 				return err;
+ 		}
+ 		for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
+-			err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+ 			if (err < 0)
+ 				return err;
+ 		}
+ 		for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
+-			err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
++			err = snd_ctl_add(card,
++				snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+ 			if (err < 0)
+ 				return err;
+ 		}
+-		err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu));
++		err = snd_ctl_add(card,
++			snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+ 		if (err < 0)
+ 			return err;
+ 	}
+diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
+index 04c7cf7..c4d76d1 100644
+--- a/sound/pci/emu10k1/emumpu401.c
++++ b/sound/pci/emu10k1/emumpu401.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
+index 5ce5bef..cf9276d 100644
+--- a/sound/pci/emu10k1/emupcm.c
++++ b/sound/pci/emu10k1/emupcm.c
+@@ -26,7 +26,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+@@ -358,7 +357,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
+ 	snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
+ 	snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
+ 	snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
+-	if (emu->card_capabilities->emu1010)
++	if (emu->card_capabilities->emu_model)
+ 		pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+ 	else 
+ 		pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+@@ -701,7 +700,7 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s
+ 	voice = evoice->number;
+ 
+ 	pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
+-	if (emu->card_capabilities->emu1010)
++	if (emu->card_capabilities->emu_model)
+ 		pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+ 	else 
+ 		pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+@@ -1232,7 +1231,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
+ 	runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ 	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+ 	spin_lock_irq(&emu->reg_lock);
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		/*  Nb. of channels has been increased to 16 */
+ 		/* TODO
+ 		 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
+@@ -1791,7 +1790,7 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s
+ 	/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
+ 	if (emu->audigy) {
+ 		emu->efx_voices_mask[0] = 0;
+-		if (emu->card_capabilities->emu1010)
++		if (emu->card_capabilities->emu_model)
+ 			/* Pavel Hofman - 32 voices will be used for
+ 			 * capture (write mode) -
+ 			 * each bit = corresponding voice
+diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
+index c3fb10e..f3caa3f 100644
+--- a/sound/pci/emu10k1/emuproc.c
++++ b/sound/pci/emu10k1/emuproc.c
+@@ -28,7 +28,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+@@ -245,7 +244,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
+ 	unsigned long flags;
+ 	u32 rate;
+ 
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		spin_lock_irqsave(&emu->emu_lock, flags);
+ 		snd_emu1010_fpga_read(emu, 0x38, &value);
+ 		spin_unlock_irqrestore(&emu->emu_lock, flags);
+@@ -585,7 +584,7 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
+ {
+ 	struct snd_info_entry *entry;
+ #ifdef CONFIG_SND_DEBUG
+-	if (emu->card_capabilities->emu1010) {
++	if (emu->card_capabilities->emu_model) {
+ 		if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) 
+ 			snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
+ 	}
+diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
+index 6702c15..b5a802b 100644
+--- a/sound/pci/emu10k1/io.c
++++ b/sound/pci/emu10k1/io.c
+@@ -25,7 +25,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/emu10k1.h>
+@@ -71,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i
+ 	unsigned long flags;
+ 	unsigned int mask;
+ 
++	if (!emu) {
++		snd_printk(KERN_ERR "ptr_write: emu is null!\n");
++		dump_stack();
++		return;
++	}
+ 	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+ 	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+ 
+@@ -135,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
+ 	unsigned int reset, set;
+ 	unsigned int reg, tmp;
+ 	int n, result;
++	int err = 0;
++
++	/* This function is not re-entrant, so protect against it. */
++	spin_lock(&emu->spi_lock);
+ 	if (emu->card_capabilities->ca0108_chip)
+ 		reg = 0x3c; /* PTR20, reg 0x3c */
+ 	else {
+ 		/* For other chip types the SPI register
+ 		 * is currently unknown. */
+-		return 1;
++		err = 1;
++		goto spi_write_exit;
++	}
++	if (data > 0xffff) {
++		/* Only 16bit values allowed */
++		err = 1;
++		goto spi_write_exit;
+ 	}
+-	if (data > 0xffff) /* Only 16bit values allowed */
+-		return 1;
+ 
+ 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
+ 	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
+@@ -161,11 +173,17 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
+ 			break;
+ 		}
+ 	}
+-	if (result) /* Timed out */
+-		return 1;
++	if (result) {
++		/* Timed out */
++		err = 1;
++		goto spi_write_exit;
++	}
+ 	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
+ 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
+-	return 0;
++	err = 0;
++spi_write_exit:
++	spin_unlock(&emu->spi_lock);
++	return err;
+ }
+ 
+ /* The ADC does not support i2c read, so only write is implemented */
+@@ -177,15 +195,17 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
+ 	int timeout = 0;
+ 	int status;
+ 	int retry;
++	int err = 0;
++
+ 	if ((reg > 0x7f) || (value > 0x1ff)) {
+ 		snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+ 		return -EINVAL;
+ 	}
+ 
++	/* This function is not re-entrant, so protect against it. */
++	spin_lock(&emu->i2c_lock);
++
+ 	tmp = reg << 25 | value << 16;
+-	// snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
+-	/* Not sure what this I2C channel controls. */
+-	/* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */
+ 
+ 	/* This controls the I2C connected to the WM8775 ADC Codec */
+ 	snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
+@@ -193,17 +213,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
+ 
+ 	for (retry = 0; retry < 10; retry++) {
+ 		/* Send the data to i2c */
+-		//tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0);
+-		//tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+ 		tmp = 0;
+ 		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+ 		snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
+ 
+ 		/* Wait till the transaction ends */
+ 		while (1) {
+-			udelay(10);
++			mdelay(1);
+ 			status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
+-                	// snd_printk("I2C:status=0x%x\n", status);
+ 			timeout++;
+ 			if ((status & I2C_A_ADC_START) == 0)
+ 				break;
+@@ -220,19 +237,26 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
+ 
+ 	if (retry == 10) {
+ 		snd_printk(KERN_ERR "Writing to ADC failed!\n");
+-		return -EINVAL;
++		snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
++			status, reg, value);
++		/* dump_stack(); */
++		err = -EINVAL;
+ 	}
+     
+-    	return 0;
++	spin_unlock(&emu->i2c_lock);
++	return err;
+ }
+ 
+ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+ {
++	unsigned long flags;
++
+ 	if (reg > 0x3f)
+ 		return 1;
+ 	reg += 0x40; /* 0x40 upwards are registers. */
+ 	if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
+ 		return 1;
++	spin_lock_irqsave(&emu->emu_lock, flags);
+ 	outl(reg, emu->port + A_IOCFG);
+ 	udelay(10);
+ 	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+@@ -240,20 +264,24 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+ 	outl(value, emu->port + A_IOCFG);
+ 	udelay(10);
+ 	outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
++	spin_unlock_irqrestore(&emu->emu_lock, flags);
+ 
+ 	return 0;
+ }
+ 
+ int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+ {
++	unsigned long flags;
+ 	if (reg > 0x3f)
+ 		return 1;
+ 	reg += 0x40; /* 0x40 upwards are registers. */
++	spin_lock_irqsave(&emu->emu_lock, flags);
+ 	outl(reg, emu->port + A_IOCFG);
+ 	udelay(10);
+ 	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+ 	udelay(10);
+ 	*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
++	spin_unlock_irqrestore(&emu->emu_lock, flags);
+ 
+ 	return 0;
+ }
+diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
+index 3c114b4..30bfed6 100644
+--- a/sound/pci/emu10k1/irq.c
++++ b/sound/pci/emu10k1/irq.c
+@@ -25,7 +25,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/emu10k1.h>
+@@ -35,9 +34,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
+ 	struct snd_emu10k1 *emu = dev_id;
+ 	unsigned int status, status2, orig_status, orig_status2;
+ 	int handled = 0;
++	int timeout = 0;
+ 
+-	while ((status = inl(emu->port + IPR)) != 0) {
+-		//snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status);
++	while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
++		timeout++;
+ 		orig_status = status;
+ 		handled = 1;
+ 		if ((status & 0xffffffff) == 0xffffffff) {
+@@ -201,5 +201,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
+ 		}
+ 		outl(orig_status, emu->port + IPR); /* ack all */
+ 	}
++	if (timeout == 1000)
++		snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
++
+ 	return IRQ_RETVAL(handled);
+ }
+diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
+index 48097c6..916c1db 100644
+--- a/sound/pci/emu10k1/memory.c
++++ b/sound/pci/emu10k1/memory.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/pci.h>
+ #include <linux/time.h>
+ #include <linux/mutex.h>
+diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
+index 9fd3135..749a21b 100644
+--- a/sound/pci/emu10k1/p16v.c
++++ b/sound/pci/emu10k1/p16v.c
+@@ -87,7 +87,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
+index 6295b2d..72321e9 100644
+--- a/sound/pci/emu10k1/timer.c
++++ b/sound/pci/emu10k1/timer.c
+@@ -25,7 +25,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/emu10k1.h>
+diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
+index 04fa849..958cb2a 100644
+--- a/sound/pci/emu10k1/voice.c
++++ b/sound/pci/emu10k1/voice.c
+@@ -28,7 +28,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <sound/core.h>
+ #include <sound/emu10k1.h>
+diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
+index b958f86..72d85a5 100644
+--- a/sound/pci/ens1370.c
++++ b/sound/pci/ens1370.c
+@@ -26,7 +26,6 @@
+  * by Kurt J. Bosch
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
+index fb25abe..1a314fa 100644
+--- a/sound/pci/es1938.c
++++ b/sound/pci/es1938.c
+@@ -47,7 +47,6 @@
+ */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/pci.h>
+@@ -227,6 +226,7 @@ struct es1938 {
+ 	unsigned int dma2_start;
+ 	unsigned int dma1_shift;
+ 	unsigned int dma2_shift;
++	unsigned int last_capture_dmaaddr;
+ 	unsigned int active;
+ 
+ 	spinlock_t reg_lock;
+@@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip)
+ 	outb(1, SLDM_REG(chip, DMAMASK));
+ 	outb(0x14, SLDM_REG(chip, DMAMODE));
+ 	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
++	chip->last_capture_dmaaddr = chip->dma1_start;
+ 	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
+ 	/* 3. Unmask DMA */
+ 	outb(0, SLDM_REG(chip, DMAMASK));
+@@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream)
+ 	return -EINVAL;
+ }
+ 
++/* during the incrementing of dma counters the DMA register reads sometimes
++   returns garbage. To ensure a valid hw pointer, the following checks which
++   should be very unlikely to fail are used:
++   - is the current DMA address in the valid DMA range ?
++   - is the sum of DMA address and DMA counter pointing to the last DMA byte ?
++   One can argue this could differ by one byte depending on which register is
++   updated first, so the implementation below allows for that.
++*/
+ static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream)
+ {
+ 	struct es1938 *chip = snd_pcm_substream_chip(substream);
+ 	size_t ptr;
++#if 0
+ 	size_t old, new;
+-#if 1
+ 	/* This stuff is *needed*, don't ask why - AB */
+ 	old = inw(SLDM_REG(chip, DMACOUNT));
+ 	while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
+ 		old = new;
+ 	ptr = chip->dma1_size - 1 - new;
+ #else
+-	ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
++	size_t count;
++	unsigned int diff;
++
++	ptr = inl(SLDM_REG(chip, DMAADDR));
++	count = inw(SLDM_REG(chip, DMACOUNT));
++	diff = chip->dma1_start + chip->dma1_size - ptr - count;
++
++	if (diff > 3 || ptr < chip->dma1_start
++	      || ptr >= chip->dma1_start+chip->dma1_size)
++	  ptr = chip->last_capture_dmaaddr;            /* bad, use last saved */
++	else
++	  chip->last_capture_dmaaddr = ptr;            /* good, remember it */
++
++	ptr -= chip->dma1_start;
+ #endif
+ 	return ptr >> chip->dma1_shift;
+ }
+diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
+index d69b11d..25ccfce 100644
+--- a/sound/pci/es1968.c
++++ b/sound/pci/es1968.c
+@@ -94,7 +94,6 @@
+  *	places.
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
+index 9939109..4c300e6 100644
+--- a/sound/pci/fm801.c
++++ b/sound/pci/fm801.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -979,6 +978,27 @@ static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
+ 	return val;
+ }
+ 
++static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
++					  unsigned int mute)
++{
++	struct fm801 *chip = tea->private_data;
++	unsigned short reg;
++
++	spin_lock_irq(&chip->reg_lock);
++
++	reg = inw(FM801_REG(chip, GPIO_CTRL));
++	if (mute)
++		/* 0xf800 (mute) */
++		reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
++	else
++		/* 0xf802 (unmute) */
++		reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
++	outw(reg, FM801_REG(chip, GPIO_CTRL));
++	udelay(1);
++
++	spin_unlock_irq(&chip->reg_lock);
++}
++
+ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
+ 	{
+ 		/* 1 = MediaForte 256-PCS */
+@@ -994,6 +1014,7 @@ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
+ 		/* 3 = MediaForte 64-PCR */
+ 		.write = snd_fm801_tea575x_64pcr_write,
+ 		.read = snd_fm801_tea575x_64pcr_read,
++		.mute = snd_fm801_tea575x_64pcr_mute,
+ 	}
+ };
+ #endif
+diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
+index ab0c726..9e0d8a1 100644
+--- a/sound/pci/hda/Makefile
++++ b/sound/pci/hda/Makefile
+@@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o
+ # since snd-hda-intel is the only driver using hda-codec,
+ # merge it into a single module although it was originally
+ # designed to be individual modules
+-snd-hda-intel-y += hda_codec.o
++snd-hda-intel-y += hda_codec.o vmaster.o
+ snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
+ snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
+ snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
+diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
+index 8cbe3bf..26812dc 100644
+--- a/sound/pci/hda/hda_codec.c
++++ b/sound/pci/hda/hda_codec.c
+@@ -19,7 +19,6 @@
+  *  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>
+@@ -55,6 +54,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
+ 	{ 0x10ec, "Realtek" },
+ 	{ 0x1057, "Motorola" },
+ 	{ 0x1106, "VIA" },
++	{ 0x111d, "IDT" },
+ 	{ 0x11d4, "Analog Devices" },
+ 	{ 0x13f6, "C-Media" },
+ 	{ 0x14f1, "Conexant" },
+@@ -429,6 +429,10 @@ find_codec_preset(struct hda_codec *codec)
+ 	for (tbl = hda_preset_tables; *tbl; tbl++) {
+ 		for (preset = *tbl; preset->id; preset++) {
+ 			u32 mask = preset->mask;
++			if (preset->afg && preset->afg != codec->afg)
++				continue;
++			if (preset->mfg && preset->mfg != codec->mfg)
++				continue;
+ 			if (!mask)
+ 				mask = ~0;
+ 			if (preset->id == (codec->vendor_id & mask) &&
+@@ -765,7 +769,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
+ /*
+  * query AMP capabilities for the given widget and direction
+  */
+-static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
++u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+ {
+ 	struct hda_amp_info *info;
+ 
+@@ -933,7 +937,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
+ 	caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ 	if (!caps) {
+ 		printk(KERN_WARNING "hda_codec: "
+-		       "num_steps = 0 for NID=0x%x\n", nid);
++		       "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
++		       kcontrol->id.name);
+ 		return -EINVAL;
+ 	}
+ 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+@@ -1012,6 +1017,66 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ 	return 0;
+ }
+ 
++/*
++ * set (static) TLV for virtual master volume; recalculated as max 0dB
++ */
++void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
++			     unsigned int *tlv)
++{
++	u32 caps;
++	int nums, step;
++
++	caps = query_amp_caps(codec, nid, dir);
++	nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
++	step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
++	step = (step + 1) * 25;
++	tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
++	tlv[1] = 2 * sizeof(unsigned int);
++	tlv[2] = -nums * step;
++	tlv[3] = step;
++}
++
++/* find a mixer control element with the given name */
++struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
++					    const char *name)
++{
++	struct snd_ctl_elem_id id;
++	memset(&id, 0, sizeof(id));
++	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
++	strcpy(id.name, name);
++	return snd_ctl_find_id(codec->bus->card, &id);
++}
++
++/* create a virtual master control and add slaves */
++int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
++			unsigned int *tlv, const char **slaves)
++{
++	struct snd_kcontrol *kctl;
++	const char **s;
++	int err;
++
++	kctl = snd_ctl_make_virtual_master(name, tlv);
++	if (!kctl)
++		return -ENOMEM;
++	err = snd_ctl_add(codec->bus->card, kctl);
++	if (err < 0)
++		return err;
++	
++	for (s = slaves; *s; s++) {
++		struct snd_kcontrol *sctl;
++
++		sctl = snd_hda_find_mixer_ctl(codec, *s);
++		if (!sctl) {
++			snd_printdd("Cannot find slave %s, skipped\n", *s);
++			continue;
++		}
++		err = snd_ctl_add_slave(kctl, sctl);
++		if (err < 0)
++			return err;
++	}
++	return 0;
++}
++
+ /* switch */
+ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
+ 				  struct snd_ctl_elem_info *uinfo)
+@@ -1434,7 +1499,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+ 			return err;
+ 	}
+ 	codec->spdif_ctls =
+-		snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
++		snd_hda_codec_read(codec, nid, 0,
++				   AC_VERB_GET_DIGI_CONVERT_1, 0);
+ 	codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+ 	return 0;
+ }
+@@ -1481,7 +1547,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
+ 	unsigned short val;
+ 	unsigned int sbits;
+ 
+-	val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
++	val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
+ 	sbits = convert_to_spdif_status(val);
+ 	ucontrol->value.iec958.status[0] = sbits;
+ 	ucontrol->value.iec958.status[1] = sbits >> 8;
+@@ -1532,7 +1598,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
+ 			return err;
+ 	}
+ 	codec->spdif_in_enable =
+-		snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) &
++		snd_hda_codec_read(codec, nid, 0,
++				   AC_VERB_GET_DIGI_CONVERT_1, 0) &
+ 		AC_DIG1_ENABLE;
+ 	return 0;
+ }
+@@ -1622,6 +1689,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ 
+ 	snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+ 			    power_state);
++	msleep(10); /* partial workaround for "azx_get_response timeout" */
+ 
+ 	nid = codec->start_nid;
+ 	for (i = 0; i < codec->num_nodes; i++, nid++) {
+@@ -2336,7 +2404,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
+ 	unsigned int mode;
+ 
+ 	mode = ucontrol->value.enumerated.item[0];
+-	snd_assert(mode < num_chmodes, return -EINVAL);
++	if (mode >= num_chmodes)
++		return -EINVAL;
+ 	if (*max_channelsp == chmode[mode].channels)
+ 		return 0;
+ 	/* change the current channel setting */
+@@ -2602,20 +2671,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
+ 				 struct auto_pin_cfg *cfg,
+ 				 hda_nid_t *ignore_nids)
+ {
+-	hda_nid_t nid, nid_start;
+-	int nodes;
++	hda_nid_t nid, end_nid;
+ 	short seq, assoc_line_out, assoc_speaker;
+ 	short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
+ 	short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
++	short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+ 
+ 	memset(cfg, 0, sizeof(*cfg));
+ 
+ 	memset(sequences_line_out, 0, sizeof(sequences_line_out));
+ 	memset(sequences_speaker, 0, sizeof(sequences_speaker));
++	memset(sequences_hp, 0, sizeof(sequences_hp));
+ 	assoc_line_out = assoc_speaker = 0;
+ 
+-	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+-	for (nid = nid_start; nid < nodes + nid_start; nid++) {
++	end_nid = codec->start_nid + codec->num_nodes;
++	for (nid = codec->start_nid; nid < end_nid; nid++) {
+ 		unsigned int wid_caps = get_wcaps(codec, nid);
+ 		unsigned int wid_type =
+ 			(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+@@ -2638,6 +2708,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
+ 		case AC_JACK_LINE_OUT:
+ 			seq = get_defcfg_sequence(def_conf);
+ 			assoc = get_defcfg_association(def_conf);
++
++			if (!(wid_caps & AC_WCAP_STEREO))
++				if (!cfg->mono_out_pin)
++					cfg->mono_out_pin = nid;
+ 			if (!assoc)
+ 				continue;
+ 			if (!assoc_line_out)
+@@ -2666,9 +2740,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
+ 			cfg->speaker_outs++;
+ 			break;
+ 		case AC_JACK_HP_OUT:
++			seq = get_defcfg_sequence(def_conf);
++			assoc = get_defcfg_association(def_conf);
+ 			if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+ 				continue;
+ 			cfg->hp_pins[cfg->hp_outs] = nid;
++			sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
+ 			cfg->hp_outs++;
+ 			break;
+ 		case AC_JACK_MIC_IN: {
+@@ -2712,7 +2789,24 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
+ 			      cfg->line_outs);
+ 	sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
+ 			      cfg->speaker_outs);
++	sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
++			      cfg->hp_outs);
+ 	
++	/* if we have only one mic, make it AUTO_PIN_MIC */
++	if (!cfg->input_pins[AUTO_PIN_MIC] &&
++	    cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
++		cfg->input_pins[AUTO_PIN_MIC] =
++			cfg->input_pins[AUTO_PIN_FRONT_MIC];
++		cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
++	}
++	/* ditto for line-in */
++	if (!cfg->input_pins[AUTO_PIN_LINE] &&
++	    cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
++		cfg->input_pins[AUTO_PIN_LINE] =
++			cfg->input_pins[AUTO_PIN_FRONT_LINE];
++		cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
++	}
++
+ 	/*
+ 	 * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+ 	 * as a primary output
+@@ -2766,6 +2860,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
+ 		   cfg->hp_outs, cfg->hp_pins[0],
+ 		   cfg->hp_pins[1], cfg->hp_pins[2],
+ 		   cfg->hp_pins[3], cfg->hp_pins[4]);
++	snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+ 	snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
+ 		   " cd=0x%x, aux=0x%x\n",
+ 		   cfg->input_pins[AUTO_PIN_MIC],
+diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
+index 2bce925..f148711 100644
+--- a/sound/pci/hda/hda_codec.h
++++ b/sound/pci/hda/hda_codec.h
+@@ -77,12 +77,16 @@ enum {
+ #define AC_VERB_GET_PIN_SENSE			0x0f09
+ #define AC_VERB_GET_BEEP_CONTROL		0x0f0a
+ #define AC_VERB_GET_EAPD_BTLENABLE		0x0f0c
+-#define AC_VERB_GET_DIGI_CONVERT		0x0f0d
++#define AC_VERB_GET_DIGI_CONVERT_1		0x0f0d
++#define AC_VERB_GET_DIGI_CONVERT_2		0x0f0e
+ #define AC_VERB_GET_VOLUME_KNOB_CONTROL		0x0f0f
+ /* f10-f1a: GPIO */
+ #define AC_VERB_GET_GPIO_DATA			0x0f15
+ #define AC_VERB_GET_GPIO_MASK			0x0f16
+ #define AC_VERB_GET_GPIO_DIRECTION		0x0f17
++#define AC_VERB_GET_GPIO_WAKE_MASK		0x0f18
++#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK	0x0f19
++#define AC_VERB_GET_GPIO_STICKY_MASK		0x0f1a
+ #define AC_VERB_GET_CONFIG_DEFAULT		0x0f1c
+ /* f20: AFG/MFG */
+ #define AC_VERB_GET_SUBSYSTEM_ID		0x0f20
+@@ -110,6 +114,9 @@ enum {
+ #define AC_VERB_SET_GPIO_DATA			0x715
+ #define AC_VERB_SET_GPIO_MASK			0x716
+ #define AC_VERB_SET_GPIO_DIRECTION		0x717
++#define AC_VERB_SET_GPIO_WAKE_MASK		0x718
++#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK	0x719
++#define AC_VERB_SET_GPIO_STICKY_MASK		0x71a
+ #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0	0x71c
+ #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1	0x71d
+ #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2	0x71e
+@@ -135,6 +142,7 @@ enum {
+ #define AC_PAR_PROC_CAP			0x10
+ #define AC_PAR_GPIO_CAP			0x11
+ #define AC_PAR_AMP_OUT_CAP		0x12
++#define AC_PAR_VOL_KNB_CAP		0x13
+ 
+ /*
+  * AC_VERB_PARAMETERS results (32bit)
+@@ -181,6 +189,27 @@ enum {
+ #define AC_SUPFMT_FLOAT32		(1<<1)
+ #define AC_SUPFMT_AC3			(1<<2)
+ 
++/* GP I/O count */
++#define AC_GPIO_IO_COUNT		(0xff<<0)
++#define AC_GPIO_O_COUNT			(0xff<<8)
++#define AC_GPIO_O_COUNT_SHIFT		8
++#define AC_GPIO_I_COUNT			(0xff<<16)
++#define AC_GPIO_I_COUNT_SHIFT		16
++#define AC_GPIO_UNSOLICITED		(1<<30)
++#define AC_GPIO_WAKE			(1<<31)
++
++/* Converter stream, channel */
++#define AC_CONV_CHANNEL			(0xf<<0)
++#define AC_CONV_STREAM			(0xf<<4)
++#define AC_CONV_STREAM_SHIFT		4
++
++/* Input converter SDI select */
++#define AC_SDI_SELECT			(0xf<<0)
++
++/* Unsolicited response */
++#define AC_UNSOL_TAG			(0x3f<<0)
++#define AC_UNSOL_ENABLED		(1<<7)
++
+ /* Pin widget capabilies */
+ #define AC_PINCAP_IMP_SENSE		(1<<0)	/* impedance sense capable */
+ #define AC_PINCAP_TRIG_REQ		(1<<1)	/* trigger required */
+@@ -189,6 +218,10 @@ enum {
+ #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 */
++/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
++ *       but is marked reserved in the Intel HDA specification.
++ */
++#define AC_PINCAP_LR_SWAP		(1<<7)	/* L/R swap */
+ #define AC_PINCAP_VREF			(0x37<<8)
+ #define AC_PINCAP_VREF_SHIFT		8
+ #define AC_PINCAP_EAPD			(1<<16)	/* EAPD capable */
+@@ -222,6 +255,9 @@ enum {
+ #define AC_PWRST_D3SUP			(1<<3)
+ 
+ /* Power state values */
++#define AC_PWRST_SETTING		(0xf<<0)
++#define AC_PWRST_ACTUAL			(0xf<<4)
++#define AC_PWRST_ACTUAL_SHIFT		4
+ #define AC_PWRST_D0			0x00
+ #define AC_PWRST_D1			0x01
+ #define AC_PWRST_D2			0x02
+@@ -230,10 +266,11 @@ enum {
+ /* Processing capabilies */
+ #define AC_PCAP_BENIGN			(1<<0)
+ #define AC_PCAP_NUM_COEF		(0xff<<8)
++#define AC_PCAP_NUM_COEF_SHIFT		8
+ 
+ /* Volume knobs capabilities */
+ #define AC_KNBCAP_NUM_STEPS		(0x7f<<0)
+-#define AC_KNBCAP_DELTA			(1<<8)
++#define AC_KNBCAP_DELTA			(1<<7)
+ 
+ /*
+  * Control Parameters
+@@ -266,6 +303,9 @@ enum {
+ #define AC_DIG1_PROFESSIONAL		(1<<6)
+ #define AC_DIG1_LEVEL			(1<<7)
+ 
++/* DIGITAL2 bits */
++#define AC_DIG2_CC			(0x7f<<0)
++
+ /* Pin widget control - 8bit */
+ #define AC_PINCTL_VREFEN		(0x7<<0)
+ #define AC_PINCTL_VREF_HIZ		0	/* Hi-Z */
+@@ -280,12 +320,22 @@ enum {
+ /* Unsolicited response - 8bit */
+ #define AC_USRSP_EN			(1<<7)
+ 
++/* Pin sense - 32bit */
++#define AC_PINSENSE_IMPEDANCE_MASK	(0x7fffffff)
++#define AC_PINSENSE_PRESENCE		(1<<31)
++
++/* EAPD/BTL enable - 32bit */
++#define AC_EAPDBTL_BALANCED		(1<<0)
++#define AC_EAPDBTL_EAPD			(1<<1)
++#define AC_EAPDBTL_LR_SWAP		(1<<2)
++
+ /* 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_MISC_NO_PRESENCE	(1<<0)
+ #define AC_DEFCFG_COLOR			(0xf<<12)
+ #define AC_DEFCFG_COLOR_SHIFT		12
+ #define AC_DEFCFG_CONN_TYPE		(0xf<<16)
+@@ -417,7 +467,7 @@ struct hda_bus_ops {
+ 	/* free the private data */
+ 	void (*private_free)(struct hda_bus *);
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+-	/* notify power-up/down from codec to contoller */
++	/* notify power-up/down from codec to controller */
+ 	void (*pm_notify)(struct hda_codec *codec);
+ #endif
+ };
+@@ -456,6 +506,9 @@ struct hda_bus {
+ 	struct hda_bus_unsolicited *unsol;
+ 
+ 	struct snd_info_entry *proc;
++
++	/* misc op flags */
++	unsigned int needs_damn_long_delay :1;
+ };
+ 
+ /*
+@@ -470,6 +523,7 @@ struct hda_codec_preset {
+ 	unsigned int subs;
+ 	unsigned int subs_mask;
+ 	unsigned int rev;
++	hda_nid_t afg, mfg;
+ 	const char *name;
+ 	int (*patch)(struct hda_codec *codec);
+ };
+diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
+index c957eb5..f9de7c4 100644
+--- a/sound/pci/hda/hda_generic.c
++++ b/sound/pci/hda/hda_generic.c
+@@ -20,7 +20,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
+index bafb7b0..2177d9a 100644
+--- a/sound/pci/hda/hda_hwdep.c
++++ b/sound/pci/hda/hda_hwdep.c
+@@ -18,7 +18,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
+index 3fa0f97..56f8a30 100644
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -34,7 +34,6 @@
+  * 
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -50,29 +49,32 @@
+ #include "hda_codec.h"
+ 
+ 
+-static int index = SNDRV_DEFAULT_IDX1;
+-static char *id = SNDRV_DEFAULT_STR1;
+-static char *model;
+-static int position_fix;
+-static int probe_mask = -1;
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
++static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
++static char *model[SNDRV_CARDS];
++static int position_fix[SNDRV_CARDS];
++static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+ static int single_cmd;
+ static int enable_msi;
+ 
+-module_param(index, int, 0444);
++module_param_array(index, int, NULL, 0444);
+ MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
+-module_param(id, charp, 0444);
++module_param_array(id, charp, NULL, 0444);
+ MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
+-module_param(model, charp, 0444);
++module_param_array(enable, bool, NULL, 0444);
++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(position_fix, int, 0444);
++module_param_array(position_fix, int, NULL, 0444);
+ MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
+ 		 "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+-module_param(probe_mask, int, 0444);
++module_param_array(probe_mask, int, NULL, 0444);
+ MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+ module_param(single_cmd, bool, 0444);
+ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
+ 		 "(for debugging only).");
+-module_param(enable_msi, int, 0);
++module_param(enable_msi, int, 0444);
+ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+ 
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+@@ -87,10 +89,6 @@ module_param(power_save_controller, bool, 0644);
+ MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
+ #endif
+ 
+-/* just for backward compatibility */
+-static int enable;
+-module_param(enable, bool, 0444);
+-
+ MODULE_LICENSE("GPL");
+ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
+ 			 "{Intel, ICH6M},"
+@@ -98,12 +96,20 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
+ 			 "{Intel, ESB2},"
+ 			 "{Intel, ICH8},"
+ 			 "{Intel, ICH9},"
++			 "{Intel, ICH10},"
++			 "{Intel, SCH},"
+ 			 "{ATI, SB450},"
+ 			 "{ATI, SB600},"
+ 			 "{ATI, RS600},"
+ 			 "{ATI, RS690},"
+ 			 "{ATI, RS780},"
+ 			 "{ATI, R600},"
++			 "{ATI, RV630},"
++			 "{ATI, RV610},"
++			 "{ATI, RV670},"
++			 "{ATI, RV635},"
++			 "{ATI, RV620},"
++			 "{ATI, RV770},"
+ 			 "{VIA, VT8251},"
+ 			 "{VIA, VT8237A},"
+ 			 "{SiS, SIS966},"
+@@ -370,6 +376,7 @@ struct azx {
+ /* driver types */
+ enum {
+ 	AZX_DRIVER_ICH,
++	AZX_DRIVER_SCH,
+ 	AZX_DRIVER_ATI,
+ 	AZX_DRIVER_ATIHDMI,
+ 	AZX_DRIVER_VIA,
+@@ -380,6 +387,7 @@ enum {
+ 
+ static char *driver_short_names[] __devinitdata = {
+ 	[AZX_DRIVER_ICH] = "HDA Intel",
++	[AZX_DRIVER_SCH] = "HDA Intel MID",
+ 	[AZX_DRIVER_ATI] = "HDA ATI SB",
+ 	[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
+ 	[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
+@@ -547,7 +555,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+ 
+  again:
+ 	timeout = jiffies + msecs_to_jiffies(1000);
+-	do {
++	for (;;) {
+ 		if (chip->polling_mode) {
+ 			spin_lock_irq(&chip->reg_lock);
+ 			azx_update_rirb(chip);
+@@ -555,8 +563,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+ 		}
+ 		if (!chip->rirb.cmds)
+ 			return chip->rirb.res; /* the last value */
+-		schedule_timeout_uninterruptible(1);
+-	} while (time_after_eq(timeout, jiffies));
++		if (time_after(jiffies, timeout))
++			break;
++		if (codec->bus->needs_damn_long_delay)
++			msleep(2); /* temporary workaround */
++		else {
++			udelay(10);
++			cond_resched();
++		}
++	}
+ 
+ 	if (chip->msi) {
+ 		snd_printk(KERN_WARNING "hda_intel: No response from codec, "
+@@ -618,8 +633,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
+ 		}
+ 		udelay(1);
+ 	}
+-	snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+-		   azx_readw(chip, IRS), val);
++	if (printk_ratelimit())
++		snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
++			   azx_readw(chip, IRS), val);
+ 	return -EIO;
+ }
+ 
+@@ -635,8 +651,9 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
+ 			return azx_readl(chip, IR);
+ 		udelay(1);
+ 	}
+-	snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+-		   azx_readw(chip, IRS));
++	if (printk_ratelimit())
++		snd_printd(SFX "get_response timeout: IRS=0x%x\n",
++			   azx_readw(chip, IRS));
+ 	return (unsigned int)-1;
+ }
+ 
+@@ -1031,7 +1048,8 @@ static unsigned int azx_max_codecs[] __devinitdata = {
+ 	[AZX_DRIVER_NVIDIA] = 3,	/* FIXME: correct? */
+ };
+ 
+-static int __devinit azx_codec_create(struct azx *chip, const char *model)
++static int __devinit azx_codec_create(struct azx *chip, const char *model,
++				      unsigned int codec_probe_mask)
+ {
+ 	struct hda_bus_template bus_temp;
+ 	int c, codecs, audio_codecs, err;
+@@ -1052,7 +1070,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
+ 
+ 	codecs = audio_codecs = 0;
+ 	for (c = 0; c < AZX_MAX_CODECS; c++) {
+-		if ((chip->codec_mask & (1 << c)) & probe_mask) {
++		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ 			struct hda_codec *codec;
+ 			err = snd_hda_codec_new(chip->bus, c, &codec);
+ 			if (err < 0)
+@@ -1065,7 +1083,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
+ 	if (!audio_codecs) {
+ 		/* probe additional slots if no codec is found */
+ 		for (; c < azx_max_codecs[chip->driver_type]; c++) {
+-			if ((chip->codec_mask & (1 << c)) & probe_mask) {
++			if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ 				err = snd_hda_codec_new(chip->bus, c, NULL);
+ 				if (err < 0)
+ 					continue;
+@@ -1676,18 +1694,18 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
+ 	{}
+ };
+ 
+-static void __devinit check_probe_mask(struct azx *chip)
++static void __devinit check_probe_mask(struct azx *chip, int dev)
+ {
+ 	const struct snd_pci_quirk *q;
+ 
+-	if (probe_mask == -1) {
++	if (probe_mask[dev] == -1) {
+ 		q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
+ 		if (q) {
+ 			printk(KERN_INFO
+ 			       "hda_intel: probe_mask set to 0x%x "
+ 			       "for device %04x:%04x\n",
+ 			       q->value, q->subvendor, q->subdevice);
+-			probe_mask = q->value;
++			probe_mask[dev] = q->value;
+ 		}
+ 	}
+ }
+@@ -1697,17 +1715,18 @@ static void __devinit check_probe_mask(struct azx *chip)
+  * constructor
+  */
+ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
+-				int driver_type,
++				int dev, int driver_type,
+ 				struct azx **rchip)
+ {
+ 	struct azx *chip;
+ 	int err;
++	unsigned short gcap;
+ 	static struct snd_device_ops ops = {
+ 		.dev_free = azx_dev_free,
+ 	};
+ 
+ 	*rchip = NULL;
+-	
++
+ 	err = pci_enable_device(pci);
+ 	if (err < 0)
+ 		return err;
+@@ -1727,8 +1746,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
+ 	chip->driver_type = driver_type;
+ 	chip->msi = enable_msi;
+ 
+-	chip->position_fix = check_position_fix(chip, position_fix);
+-	check_probe_mask(chip);
++	chip->position_fix = check_position_fix(chip, position_fix[dev]);
++	check_probe_mask(chip, dev);
+ 
+ 	chip->single_cmd = single_cmd;
+ 
+@@ -1769,25 +1788,40 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
+ 	pci_set_master(pci);
+ 	synchronize_irq(chip->irq);
+ 
+-	switch (chip->driver_type) {
+-	case AZX_DRIVER_ULI:
+-		chip->playback_streams = ULI_NUM_PLAYBACK;
+-		chip->capture_streams = ULI_NUM_CAPTURE;
+-		chip->playback_index_offset = ULI_PLAYBACK_INDEX;
+-		chip->capture_index_offset = ULI_CAPTURE_INDEX;
+-		break;
+-	case AZX_DRIVER_ATIHDMI:
+-		chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+-		chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+-		chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+-		chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+-		break;
+-	default:
+-		chip->playback_streams = ICH6_NUM_PLAYBACK;
+-		chip->capture_streams = ICH6_NUM_CAPTURE;
+-		chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
+-		chip->capture_index_offset = ICH6_CAPTURE_INDEX;
+-		break;
++	gcap = azx_readw(chip, GCAP);
++	snd_printdd("chipset global capabilities = 0x%x\n", gcap);
++
++	if (gcap) {
++		/* read number of streams from GCAP register instead of using
++		 * hardcoded value
++		 */
++		chip->playback_streams = (gcap & (0xF << 12)) >> 12;
++		chip->capture_streams = (gcap & (0xF << 8)) >> 8;
++		chip->playback_index_offset = (gcap & (0xF << 12)) >> 12;
++		chip->capture_index_offset = 0;
++	} else {
++		/* gcap didn't give any info, switching to old method */
++
++		switch (chip->driver_type) {
++		case AZX_DRIVER_ULI:
++			chip->playback_streams = ULI_NUM_PLAYBACK;
++			chip->capture_streams = ULI_NUM_CAPTURE;
++			chip->playback_index_offset = ULI_PLAYBACK_INDEX;
++			chip->capture_index_offset = ULI_CAPTURE_INDEX;
++			break;
++		case AZX_DRIVER_ATIHDMI:
++			chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
++			chip->capture_streams = ATIHDMI_NUM_CAPTURE;
++			chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
++			chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
++			break;
++		default:
++			chip->playback_streams = ICH6_NUM_PLAYBACK;
++			chip->capture_streams = ICH6_NUM_CAPTURE;
++			chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
++			chip->capture_index_offset = ICH6_CAPTURE_INDEX;
++			break;
++		}
+ 	}
+ 	chip->num_streams = chip->playback_streams + chip->capture_streams;
+ 	chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
+@@ -1869,17 +1903,25 @@ static void power_down_all_codecs(struct azx *chip)
+ static int __devinit azx_probe(struct pci_dev *pci,
+ 			       const struct pci_device_id *pci_id)
+ {
++	static int dev;
+ 	struct snd_card *card;
+ 	struct azx *chip;
+ 	int err;
+ 
+-	card = snd_card_new(index, id, THIS_MODULE, 0);
++	if (dev >= SNDRV_CARDS)
++		return -ENODEV;
++	if (!enable[dev]) {
++		dev++;
++		return -ENOENT;
++	}
++
++	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ 	if (!card) {
+ 		snd_printk(KERN_ERR SFX "Error creating card!\n");
+ 		return -ENOMEM;
+ 	}
+ 
+-	err = azx_create(card, pci, pci_id->driver_data, &chip);
++	err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
+ 	if (err < 0) {
+ 		snd_card_free(card);
+ 		return err;
+@@ -1887,7 +1929,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
+ 	card->private_data = chip;
+ 
+ 	/* create codec instances */
+-	err = azx_codec_create(chip, model);
++	err = azx_codec_create(chip, model[dev], probe_mask[dev]);
+ 	if (err < 0) {
+ 		snd_card_free(card);
+ 		return err;
+@@ -1919,6 +1961,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
+ 	chip->running = 1;
+ 	power_down_all_codecs(chip);
+ 
++	dev++;
+ 	return err;
+ }
+ 
+@@ -1936,12 +1979,21 @@ static struct pci_device_id azx_ids[] = {
+ 	{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
+ 	{ 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
+ 	{ 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
++	{ 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
++	{ 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
++	{ 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/
+ 	{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
+ 	{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
+ 	{ 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
+ 	{ 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
+-	{ 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
++	{ 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
+ 	{ 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
++	{ 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
++	{ 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
++	{ 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
++	{ 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
++	{ 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
++	{ 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
+ 	{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
+ 	{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
+ 	{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
+diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
+index 8c56c9c..ad0014a 100644
+--- a/sound/pci/hda/hda_local.h
++++ b/sound/pci/hda/hda_local.h
+@@ -90,6 +90,13 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
+ void snd_hda_codec_resume_amp(struct hda_codec *codec);
+ #endif
+ 
++void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
++			     unsigned int *tlv);
++struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
++					    const char *name);
++int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
++			unsigned int *tlv, const char **slaves);
++
+ /* amp value bits */
+ #define HDA_AMP_MUTE	0x80
+ #define HDA_AMP_UNMUTE	0x00
+@@ -325,6 +332,7 @@ struct auto_pin_cfg {
+ 	hda_nid_t input_pins[AUTO_PIN_LAST];
+ 	hda_nid_t dig_out_pin;
+ 	hda_nid_t dig_in_pin;
++	hda_nid_t mono_out_pin;
+ };
+ 
+ #define get_defcfg_connect(cfg) \
+@@ -363,10 +371,11 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
+ {
+ 	if (nid < codec->start_nid ||
+ 	    nid >= codec->start_nid + codec->num_nodes)
+-		return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
++		return 0;
+ 	return codec->wcaps[nid - codec->start_nid];
+ }
+ 
++u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
+ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+ 			      unsigned int caps);
+ 
+@@ -398,4 +407,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
+ 				 hda_nid_t nid);
+ #endif /* CONFIG_SND_HDA_POWER_SAVE */
+ 
++/*
++ * virtual master control
++ */
++struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
++						 const unsigned int *tlv);
++int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
++		      
+ #endif /* __SOUND_HDA_LOCAL_H */
+diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
+index e94944f..35a630d 100644
+--- a/sound/pci/hda/hda_proc.c
++++ b/sound/pci/hda/hda_proc.c
+@@ -21,7 +21,6 @@
+  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <sound/core.h>
+ #include "hda_codec.h"
+@@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg)
+ }
+ 
+ static void print_pin_caps(struct snd_info_buffer *buffer,
+-			   struct hda_codec *codec, hda_nid_t nid)
++			   struct hda_codec *codec, hda_nid_t nid,
++			   int *supports_vref)
+ {
+ 	static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
+ 	static char *jack_types[16] = {
+@@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
+ 		"SPDIF In", "Digitial In", "Reserved", "Other"
+ 	};
+ 	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+-	unsigned int caps;
++	unsigned int caps, val;
+ 
+ 	caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ 	snd_iprintf(buffer, "  Pincap 0x08%x:", caps);
+@@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
+ 		snd_iprintf(buffer, " EAPD");
+ 	if (caps & AC_PINCAP_PRES_DETECT)
+ 		snd_iprintf(buffer, " Detect");
++	if (caps & AC_PINCAP_BALANCE)
++		snd_iprintf(buffer, " Balanced");
++	if (caps & AC_PINCAP_LR_SWAP)
++		snd_iprintf(buffer, " R/L");
++	if (caps & AC_PINCAP_TRIG_REQ)
++		snd_iprintf(buffer, " Trigger");
++	if (caps & AC_PINCAP_IMP_SENSE)
++		snd_iprintf(buffer, " ImpSense");
+ 	snd_iprintf(buffer, "\n");
++	if (caps & AC_PINCAP_VREF) {
++		unsigned int vref =
++			(caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
++		snd_iprintf(buffer, "    Vref caps:");
++		if (vref & AC_PINCAP_VREF_HIZ)
++			snd_iprintf(buffer, " HIZ");
++		if (vref & AC_PINCAP_VREF_50)
++			snd_iprintf(buffer, " 50");
++		if (vref & AC_PINCAP_VREF_GRD)
++			snd_iprintf(buffer, " GRD");
++		if (vref & AC_PINCAP_VREF_80)
++			snd_iprintf(buffer, " 80");
++		if (vref & AC_PINCAP_VREF_100)
++			snd_iprintf(buffer, " 100");
++		snd_iprintf(buffer, "\n");
++		*supports_vref = 1;
++	} else
++		*supports_vref = 0;
++	if (caps & AC_PINCAP_EAPD) {
++		val = snd_hda_codec_read(codec, nid, 0,
++					 AC_VERB_GET_EAPD_BTLENABLE, 0);
++		snd_iprintf(buffer, "  EAPD 0x%x:", val);
++		if (val & AC_EAPDBTL_BALANCED)
++			snd_iprintf(buffer, " BALANCED");
++		if (val & AC_EAPDBTL_EAPD)
++			snd_iprintf(buffer, " EAPD");
++		if (val & AC_EAPDBTL_LR_SWAP)
++			snd_iprintf(buffer, " R/L");
++		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] %s at %s %s\n", caps,
+ 		    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
+@@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
+ 	snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
+ 		    get_jack_connection(caps),
+ 		    get_jack_color(caps));
++	/* Default association and sequence values refer to default grouping
++	 * of pin complexes and their sequence within the group. This is used
++	 * for priority and resource allocation.
++	 */
++	snd_iprintf(buffer, "    DefAssociation = 0x%x, Sequence = 0x%x\n",
++		    (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
++		    caps & AC_DEFCFG_SEQUENCE);
++	if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
++	    AC_DEFCFG_MISC_NO_PRESENCE) {
++		/* Miscellaneous bit indicates external hardware does not
++		 * support presence detection even if the pin complex
++		 * indicates it is supported.
++		 */
++		snd_iprintf(buffer, "    Misc = NO_PRESENCE\n");
++	}
++}
++
++static void print_pin_ctls(struct snd_info_buffer *buffer,
++			   struct hda_codec *codec, hda_nid_t nid,
++			   int supports_vref)
++{
++	unsigned int pinctls;
++
++	pinctls = snd_hda_codec_read(codec, nid, 0,
++				     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
++	snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
++	if (pinctls & AC_PINCTL_IN_EN)
++		snd_iprintf(buffer, " IN");
++	if (pinctls & AC_PINCTL_OUT_EN)
++		snd_iprintf(buffer, " OUT");
++	if (pinctls & AC_PINCTL_HP_EN)
++		snd_iprintf(buffer, " HP");
++	if (supports_vref) {
++		int vref = pinctls & AC_PINCTL_VREFEN;
++		switch (vref) {
++		case AC_PINCTL_VREF_HIZ:
++			snd_iprintf(buffer, " VREF_HIZ");
++			break;
++		case AC_PINCTL_VREF_50:
++			snd_iprintf(buffer, " VREF_50");
++			break;
++		case AC_PINCTL_VREF_GRD:
++			snd_iprintf(buffer, " VREF_GRD");
++			break;
++		case AC_PINCTL_VREF_80:
++			snd_iprintf(buffer, " VREF_80");
++			break;
++		case AC_PINCTL_VREF_100:
++			snd_iprintf(buffer, " VREF_100");
++			break;
++		}
++	}
++	snd_iprintf(buffer, "\n");
++}
++
++static void print_vol_knob(struct snd_info_buffer *buffer,
++			   struct hda_codec *codec, hda_nid_t nid)
++{
++	unsigned int cap = snd_hda_param_read(codec, nid,
++					      AC_PAR_VOL_KNB_CAP);
++	snd_iprintf(buffer, "  Volume-Knob: delta=%d, steps=%d, ",
++		    (cap >> 7) & 1, cap & 0x7f);
++	cap = snd_hda_codec_read(codec, nid, 0,
++				 AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
++	snd_iprintf(buffer, "direct=%d, val=%d\n",
++		    (cap >> 7) & 1, cap & 0x7f);
++}
++
++static void print_audio_io(struct snd_info_buffer *buffer,
++			   struct hda_codec *codec, hda_nid_t nid,
++			   unsigned int wid_type)
++{
++	int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
++	snd_iprintf(buffer,
++		    "  Converter: stream=%d, channel=%d\n",
++		    (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
++		    conv & AC_CONV_CHANNEL);
++
++	if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
++		int sdi = snd_hda_codec_read(codec, nid, 0,
++					     AC_VERB_GET_SDI_SELECT, 0);
++		snd_iprintf(buffer, "  SDI-Select: %d\n",
++			    sdi & AC_SDI_SELECT);
++	}
++}
++
++static void print_digital_conv(struct snd_info_buffer *buffer,
++			       struct hda_codec *codec, hda_nid_t nid)
++{
++	unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
++						AC_VERB_GET_DIGI_CONVERT_1, 0);
++	unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
++						AC_VERB_GET_DIGI_CONVERT_2, 0);
++	snd_iprintf(buffer, "  Digital:");
++	if (digi1 & AC_DIG1_ENABLE)
++		snd_iprintf(buffer, " Enabled");
++	if (digi1 & AC_DIG1_V)
++		snd_iprintf(buffer, " Validity");
++	if (digi1 & AC_DIG1_VCFG)
++		snd_iprintf(buffer, " ValidityCfg");
++	if (digi1 & AC_DIG1_EMPHASIS)
++		snd_iprintf(buffer, " Preemphasis");
++	if (digi1 & AC_DIG1_COPYRIGHT)
++		snd_iprintf(buffer, " Copyright");
++	if (digi1 & AC_DIG1_NONAUDIO)
++		snd_iprintf(buffer, " Non-Audio");
++	if (digi1 & AC_DIG1_PROFESSIONAL)
++		snd_iprintf(buffer, " Pro");
++	if (digi1 & AC_DIG1_LEVEL)
++		snd_iprintf(buffer, " GenLevel");
++	snd_iprintf(buffer, "\n");
++	snd_iprintf(buffer, "  Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
++}
++
++static const char *get_pwr_state(u32 state)
++{
++	static const char *buf[4] = {
++		"D0", "D1", "D2", "D3"
++	};
++	if (state < 4)
++		return buf[state];
++	return "UNKNOWN";
++}
++
++static void print_power_state(struct snd_info_buffer *buffer,
++			      struct hda_codec *codec, hda_nid_t nid)
++{
++	int pwr = snd_hda_codec_read(codec, nid, 0,
++				     AC_VERB_GET_POWER_STATE, 0);
++	snd_iprintf(buffer, "  Power: setting=%s, actual=%s\n",
++		    get_pwr_state(pwr & AC_PWRST_SETTING),
++		    get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
++				  AC_PWRST_ACTUAL_SHIFT));
++}
++
++static void print_unsol_cap(struct snd_info_buffer *buffer,
++			      struct hda_codec *codec, hda_nid_t nid)
++{
++	int unsol = snd_hda_codec_read(codec, nid, 0,
++				       AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
++	snd_iprintf(buffer,
++		    "  Unsolicited: tag=%02x, enabled=%d\n",
++		    unsol & AC_UNSOL_TAG,
++		    (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
++}
++
++static void print_proc_caps(struct snd_info_buffer *buffer,
++			    struct hda_codec *codec, hda_nid_t nid)
++{
++	unsigned int proc_caps = snd_hda_param_read(codec, nid,
++						    AC_PAR_PROC_CAP);
++	snd_iprintf(buffer, "  Processing caps: benign=%d, ncoeff=%d\n",
++		    proc_caps & AC_PCAP_BENIGN,
++		    (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
+ }
+ 
++static void print_conn_list(struct snd_info_buffer *buffer,
++			    struct hda_codec *codec, hda_nid_t nid,
++			    unsigned int wid_type, hda_nid_t *conn,
++			    int conn_len)
++{
++	int c, curr = -1;
++
++	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);
++	if (conn_len > 0) {
++		snd_iprintf(buffer, "    ");
++		for (c = 0; c < conn_len; c++) {
++			snd_iprintf(buffer, " 0x%02x", conn[c]);
++			if (c == curr)
++				snd_iprintf(buffer, "*");
++		}
++		snd_iprintf(buffer, "\n");
++	}
++}
++
++static void print_realtek_coef(struct snd_info_buffer *buffer,
++			       struct hda_codec *codec, hda_nid_t nid)
++{
++	int coeff = snd_hda_codec_read(codec, nid, 0,
++				       AC_VERB_GET_PROC_COEF, 0);
++	snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
++	coeff = snd_hda_codec_read(codec, nid, 0,
++				   AC_VERB_GET_COEF_INDEX, 0);
++	snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
++}
++
++static void print_gpio(struct snd_info_buffer *buffer,
++		       struct hda_codec *codec, hda_nid_t nid)
++{
++	unsigned int gpio =
++		snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
++	unsigned int enable, direction, wake, unsol, sticky, data;
++	int i, max;
++	snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
++		    "unsolicited=%d, wake=%d\n",
++		    gpio & AC_GPIO_IO_COUNT,
++		    (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
++		    (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
++		    (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
++		    (gpio & AC_GPIO_WAKE) ? 1 : 0);
++	max = gpio & AC_GPIO_IO_COUNT;
++	enable = snd_hda_codec_read(codec, nid, 0,
++				    AC_VERB_GET_GPIO_MASK, 0);
++	direction = snd_hda_codec_read(codec, nid, 0,
++				       AC_VERB_GET_GPIO_DIRECTION, 0);
++	wake = snd_hda_codec_read(codec, nid, 0,
++				  AC_VERB_GET_GPIO_WAKE_MASK, 0);
++	unsol  = snd_hda_codec_read(codec, nid, 0,
++				    AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
++	sticky = snd_hda_codec_read(codec, nid, 0,
++				    AC_VERB_GET_GPIO_STICKY_MASK, 0);
++	data = snd_hda_codec_read(codec, nid, 0,
++				  AC_VERB_GET_GPIO_DATA, 0);
++	for (i = 0; i < max; ++i)
++		snd_iprintf(buffer,
++			    "  IO[%d]: enable=%d, dir=%d, wake=%d, "
++			    "sticky=%d, data=%d\n", i,
++			    (enable & (1<<i)) ? 1 : 0,
++			    (direction & (1<<i)) ? 1 : 0,
++			    (wake & (1<<i)) ? 1 : 0,
++			    (sticky & (1<<i)) ? 1 : 0,
++			    (data & (1<<i)) ? 1 : 0);
++	/* FIXME: add GPO and GPI pin information */
++}
+ 
+ static void print_codec_info(struct snd_info_entry *entry,
+ 			     struct snd_info_buffer *buffer)
+@@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry,
+ 		snd_hda_power_down(codec);
+ 		return;
+ 	}
++
++	print_gpio(buffer, codec, codec->afg);
++
+ 	for (i = 0; i < nodes; i++, 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;
+-		int conn_len = 0; 
+ 		hda_nid_t conn[HDA_MAX_CONNECTIONS];
++		int conn_len = 0;
+ 
+ 		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
+ 			    get_wid_type_name(wid_type), wid_caps);
+@@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry,
+ 			snd_iprintf(buffer, " Amp-In");
+ 		if (wid_caps & AC_WCAP_OUT_AMP)
+ 			snd_iprintf(buffer, " Amp-Out");
++		if (wid_caps & AC_WCAP_STRIPE)
++			snd_iprintf(buffer, " Stripe");
++		if (wid_caps & AC_WCAP_LR_SWAP)
++			snd_iprintf(buffer, " R/L");
+ 		snd_iprintf(buffer, "\n");
+ 
++		/* volume knob is a special widget that always have connection
++		 * list
++		 */
++		if (wid_type == AC_WID_VOL_KNB)
++			wid_caps |= AC_WCAP_CONN_LIST;
++
+ 		if (wid_caps & AC_WCAP_CONN_LIST)
+ 			conn_len = snd_hda_get_connections(codec, nid, conn,
+ 							   HDA_MAX_CONNECTIONS);
+@@ -318,48 +594,49 @@ static void print_codec_info(struct snd_info_entry *entry,
+ 				       wid_caps & AC_WCAP_STEREO, 1);
+ 		}
+ 
+-		if (wid_type == AC_WID_PIN) {
+-			unsigned int pinctls;
+-			print_pin_caps(buffer, codec, nid);
+-			pinctls = snd_hda_codec_read(codec, nid, 0,
+-					     AC_VERB_GET_PIN_WIDGET_CONTROL,
+-						     0);
+-			snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
+-			if (pinctls & AC_PINCTL_IN_EN)
+-				snd_iprintf(buffer, " IN");
+-			if (pinctls & AC_PINCTL_OUT_EN)
+-				snd_iprintf(buffer, " OUT");
+-			if (pinctls & AC_PINCTL_HP_EN)
+-				snd_iprintf(buffer, " HP");
+-			snd_iprintf(buffer, "\n");
++		switch (wid_type) {
++		case AC_WID_PIN: {
++			int supports_vref;
++			print_pin_caps(buffer, codec, nid, &supports_vref);
++			print_pin_ctls(buffer, codec, nid, supports_vref);
++			break;
+ 		}
+-
+-		if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
+-		    (wid_caps & AC_WCAP_FORMAT_OVRD)) {
+-			snd_iprintf(buffer, "  PCM:\n");
+-			print_pcm_caps(buffer, codec, nid);
++		case AC_WID_VOL_KNB:
++			print_vol_knob(buffer, codec, nid);
++			break;
++		case AC_WID_AUD_OUT:
++		case AC_WID_AUD_IN:
++			print_audio_io(buffer, codec, nid, wid_type);
++			if (wid_caps & AC_WCAP_DIGITAL)
++				print_digital_conv(buffer, codec, nid);
++			if (wid_caps & AC_WCAP_FORMAT_OVRD) {
++				snd_iprintf(buffer, "  PCM:\n");
++				print_pcm_caps(buffer, codec, nid);
++			}
++			break;
+ 		}
+ 
++		if (wid_caps & AC_WCAP_UNSOL_CAP)
++			print_unsol_cap(buffer, codec, nid);
++
+ 		if (wid_caps & AC_WCAP_POWER)
+-			snd_iprintf(buffer, "  Power: 0x%x\n",
+-				    snd_hda_codec_read(codec, nid, 0,
+-						       AC_VERB_GET_POWER_STATE,
+-						       0));
+-
+-		if (wid_caps & AC_WCAP_CONN_LIST) {
+-			int c, curr = -1;
+-			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++) {
+-				snd_iprintf(buffer, " 0x%02x", conn[c]);
+-				if (c == curr)
+-					snd_iprintf(buffer, "*");
+-			}
+-			snd_iprintf(buffer, "\n");
+-		}
++			print_power_state(buffer, codec, nid);
++
++		if (wid_caps & AC_WCAP_DELAY)
++			snd_iprintf(buffer, "  Delay: %d samples\n",
++				    (wid_caps & AC_WCAP_DELAY) >>
++				    AC_WCAP_DELAY_SHIFT);
++
++		if (wid_caps & AC_WCAP_CONN_LIST)
++			print_conn_list(buffer, codec, nid, wid_type,
++					conn, conn_len);
++
++		if (wid_caps & AC_WCAP_PROC_WID)
++			print_proc_caps(buffer, codec, nid);
++
++		/* NID 0x20 == Realtek Define Registers */
++		if (codec->vendor_id == 0x10ec && nid == 0x20)
++			print_realtek_coef(buffer, codec, nid);
+ 	}
+ 	snd_hda_power_down(codec);
+ }
+diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
+index 196ad3c..19f0884 100644
+--- a/sound/pci/hda/patch_analog.c
++++ b/sound/pci/hda/patch_analog.c
+@@ -19,7 +19,6 @@
+  *  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>
+@@ -79,6 +78,11 @@ struct ad198x_spec {
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	struct hda_loopback_check loopback;
+ #endif
++	/* for virtual master */
++	hda_nid_t vmaster_nid;
++	u32 vmaster_tlv[4];
++	const char **slave_vols;
++	const char **slave_sws;
+ };
+ 
+ /*
+@@ -126,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec)
+ 	return 0;
+ }
+ 
++static const char *ad_slave_vols[] = {
++	"Front Playback Volume",
++	"Surround Playback Volume",
++	"Center Playback Volume",
++	"LFE Playback Volume",
++	"Side Playback Volume",
++	"Headphone Playback Volume",
++	"Mono Playback Volume",
++	"Speaker Playback Volume",
++	"IEC958 Playback Volume",
++	NULL
++};
++
++static const char *ad_slave_sws[] = {
++	"Front Playback Switch",
++	"Surround Playback Switch",
++	"Center Playback Switch",
++	"LFE Playback Switch",
++	"Side Playback Switch",
++	"Headphone Playback Switch",
++	"Mono Playback Switch",
++	"Speaker Playback Switch",
++	"IEC958 Playback Switch",
++	NULL
++};
++
+ static int ad198x_build_controls(struct hda_codec *codec)
+ {
+ 	struct ad198x_spec *spec = codec->spec;
+@@ -147,6 +177,27 @@ static int ad198x_build_controls(struct hda_codec *codec)
+ 		if (err < 0)
+ 			return err;
+ 	}
++
++	/* if we have no master control, let's create it */
++	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
++		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
++					HDA_OUTPUT, spec->vmaster_tlv);
++		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
++					  spec->vmaster_tlv,
++					  (spec->slave_vols ?
++					   spec->slave_vols : ad_slave_vols));
++		if (err < 0)
++			return err;
++	}
++	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
++		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
++					  NULL,
++					  (spec->slave_sws ?
++					   spec->slave_sws : ad_slave_sws));
++		if (err < 0)
++			return err;
++	}
++
+ 	return 0;
+ }
+ 
+@@ -370,7 +421,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
+ 	int invert = (kcontrol->private_value >> 8) & 1;
+ 	hda_nid_t nid = kcontrol->private_value & 0xff;
+ 	unsigned int eapd;
+-	eapd = ucontrol->value.integer.value[0];
++	eapd = !!ucontrol->value.integer.value[0];
+ 	if (invert)
+ 		eapd = !eapd;
+ 	if (eapd == spec->cur_eapd)
+@@ -833,27 +884,29 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
+ 
+ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
+-	SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
+-	SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
++	SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
++	SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
+ 	SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
+ 	SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
++	SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
++	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
+ 	SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
+-	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
++	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
+ 	SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
+ 	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
+@@ -872,6 +925,13 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
+ };
+ #endif
+ 
++static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
++{
++	unsigned int conf = snd_hda_codec_read(codec, nid, 0,
++					       AC_VERB_GET_CONFIG_DEFAULT, 0);
++	return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
++}
++
+ static int patch_ad1986a(struct hda_codec *codec)
+ {
+ 	struct ad198x_spec *spec;
+@@ -898,6 +958,7 @@ static int patch_ad1986a(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = ad1986a_loopbacks;
+ #endif
++	spec->vmaster_nid = 0x1b;
+ 
+ 	codec->patch_ops = ad198x_patch_ops;
+ 
+@@ -930,7 +991,8 @@ static int patch_ad1986a(struct hda_codec *codec)
+ 		spec->multiout.max_channels = 2;
+ 		spec->multiout.num_dacs = 1;
+ 		spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+-		spec->multiout.dig_out_nid = 0;
++		if (!is_jack_available(codec, 0x25))
++			spec->multiout.dig_out_nid = 0;
+ 		spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+ 		break;
+ 	case AD1986A_LAPTOP_AUTOMUTE:
+@@ -941,7 +1003,8 @@ static int patch_ad1986a(struct hda_codec *codec)
+ 		spec->multiout.max_channels = 2;
+ 		spec->multiout.num_dacs = 1;
+ 		spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+-		spec->multiout.dig_out_nid = 0;
++		if (!is_jack_available(codec, 0x25))
++			spec->multiout.dig_out_nid = 0;
+ 		spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+ 		codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
+ 		codec->patch_ops.init = ad1986a_hp_init;
+@@ -1020,6 +1083,8 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ 	struct ad198x_spec *spec = codec->spec;
+ 
++	if (ucontrol->value.enumerated.item[0] > 1)
++		return -EINVAL;
+ 	if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
+ 		spec->spdif_route = ucontrol->value.enumerated.item[0];
+ 		snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
+@@ -1138,6 +1203,7 @@ static int patch_ad1983(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = ad1983_loopbacks;
+ #endif
++	spec->vmaster_nid = 0x05;
+ 
+ 	codec->patch_ops = ad198x_patch_ops;
+ 
+@@ -1496,14 +1562,14 @@ static const char *ad1981_models[AD1981_MODELS] = {
+ };
+ 
+ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
++	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
+ 	/* All HP models */
+ 	SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
+-	/* HP nx6320 (reversed SSID, H/W bug) */
+-	SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
++	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
+ 	/* Lenovo Thinkpad T60/X60/Z6xx */
+ 	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
+-	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
+-	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
++	/* HP nx6320 (reversed SSID, H/W bug) */
++	SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+ 	{}
+ };
+ 
+@@ -1534,6 +1600,7 @@ static int patch_ad1981(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = ad1981_loopbacks;
+ #endif
++	spec->vmaster_nid = 0x05;
+ 
+ 	codec->patch_ops = ad198x_patch_ops;
+ 
+@@ -1908,7 +1975,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = {
+ 		.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",
+@@ -1965,6 +2031,8 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
+ 	int change;
+ 
+ 	val = ucontrol->value.enumerated.item[0];
++	if (val > 3)
++		return -EINVAL;
+ 	if (!val) {
+ 		sel = snd_hda_codec_read(codec, 0x1d, 0,
+ 					 AC_VERB_GET_AMP_GAIN_MUTE,
+@@ -2079,6 +2147,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
+ 	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ 	{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
++	/* Analog CD Input */
++	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ 
+ 	{ }
+ };
+@@ -2720,8 +2790,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = {
+ };
+ 
+ static struct snd_pci_quirk ad1988_cfg_tbl[] = {
+-	SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
+ 	SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
++	SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
+ 	{}
+ };
+ 
+@@ -2843,6 +2913,7 @@ static int patch_ad1988(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = ad1988_loopbacks;
+ #endif
++	spec->vmaster_nid = 0x04;
+ 
+ 	return 0;
+ }
+@@ -2919,7 +2990,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
+ 		.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",
+@@ -3009,6 +3079,20 @@ static struct hda_amp_list ad1884_loopbacks[] = {
+ };
+ #endif
+ 
++static const char *ad1884_slave_vols[] = {
++	"PCM Playback Volume",
++	"Mic Playback Volume",
++	"Mono Playback Volume",
++	"Front Mic Playback Volume",
++	"Mic Playback Volume",
++	"CD Playback Volume",
++	"Internal Mic Playback Volume",
++	"Docking Mic Playback Volume"
++	"Beep Playback Volume",
++	"IEC958 Playback Volume",
++	NULL
++};
++
+ static int patch_ad1884(struct hda_codec *codec)
+ {
+ 	struct ad198x_spec *spec;
+@@ -3036,6 +3120,9 @@ static int patch_ad1884(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = ad1884_loopbacks;
+ #endif
++	spec->vmaster_nid = 0x04;
++	/* we need to cover all playback volumes */
++	spec->slave_vols = ad1884_slave_vols;
+ 
+ 	codec->patch_ops = ad198x_patch_ops;
+ 
+@@ -3054,6 +3141,20 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = {
+ 	},
+ };
+ 
++
++/*
++ * Dell Precision T3400
++ */
++static struct hda_input_mux ad1984_dell_desktop_capture_source = {
++	.num_items = 3,
++	.items = {
++		{ "Front Mic", 0x0 },
++		{ "Line-In", 0x1 },
++		{ "Mix", 0x3 },
++	},
++};
++
++
+ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
+ 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ 	/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
+@@ -3078,7 +3179,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
+ 		.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",
+@@ -3087,6 +3187,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
+ 		.get = ad198x_mux_enum_get,
+ 		.put = ad198x_mux_enum_put,
+ 	},
++	/* SPDIF controls */
++	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
++		/* identical with ad1983 */
++		.info = ad1983_spdif_route_info,
++		.get = ad1983_spdif_route_get,
++		.put = ad1983_spdif_route_put,
++	},
+ 	{ } /* end */
+ };
+ 
+@@ -3104,6 +3214,44 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
+ 	{ } /* end */
+ };
+ 
++/*
++ * Dell Precision T3400
++ */
++static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
++	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
++	HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
++	HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
++	/*
++	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
++	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
++	*/
++	HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		/* The multiple "Capture Source" controls confuse alsamixer
++		 * So call somewhat different..
++		 */
++		/* .name = "Capture Source", */
++		.name = "Input Source",
++		.count = 2,
++		.info = ad198x_mux_enum_info,
++		.get = ad198x_mux_enum_get,
++		.put = ad198x_mux_enum_put,
++	},
++	{ } /* end */
++};
++
+ /* Digial MIC ADC NID 0x05 + 0x06 */
+ static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
+ 				   struct hda_codec *codec,
+@@ -3157,17 +3305,20 @@ static int ad1984_build_pcms(struct hda_codec *codec)
+ enum {
+ 	AD1984_BASIC,
+ 	AD1984_THINKPAD,
++	AD1984_DELL_DESKTOP,
+ 	AD1984_MODELS
+ };
+ 
+ static const char *ad1984_models[AD1984_MODELS] = {
+ 	[AD1984_BASIC]		= "basic",
+ 	[AD1984_THINKPAD]	= "thinkpad",
++	[AD1984_DELL_DESKTOP]	= "dell_desktop",
+ };
+ 
+ static struct snd_pci_quirk ad1984_cfg_tbl[] = {
+ 	/* Lenovo Thinkpad T61/X61 */
+ 	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
++	SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
+ 	{}
+ };
+ 
+@@ -3189,11 +3340,16 @@ static int patch_ad1984(struct hda_codec *codec)
+ 		codec->patch_ops.build_pcms = ad1984_build_pcms;
+ 		break;
+ 	case AD1984_THINKPAD:
+-		spec->multiout.dig_out_nid = 0;
++		spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
+ 		spec->input_mux = &ad1984_thinkpad_capture_source;
+ 		spec->mixers[0] = ad1984_thinkpad_mixers;
+ 		spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
+ 		break;
++	case AD1984_DELL_DESKTOP:
++		spec->multiout.dig_out_nid = 0;
++		spec->input_mux = &ad1984_dell_desktop_capture_source;
++		spec->mixers[0] = ad1984_dell_desktop_mixers;
++		break;
+ 	}
+ 	return 0;
+ }
+@@ -3267,7 +3423,6 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
+ 		.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",
+@@ -3468,6 +3623,7 @@ static int patch_ad1882(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = ad1882_loopbacks;
+ #endif
++	spec->vmaster_nid = 0x04;
+ 
+ 	codec->patch_ops = ad198x_patch_ops;
+ 
+diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
+index fbb8969..9a8bb4c 100644
+--- a/sound/pci/hda/patch_atihdmi.c
++++ b/sound/pci/hda/patch_atihdmi.c
+@@ -21,7 +21,6 @@
+  *  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>
+@@ -158,6 +157,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+ 	{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
+ 	{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
+ 	{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
+-	{ .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
++	{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+ 	{} /* terminator */
+ };
+diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
+index 6c54793..3d6097b 100644
+--- a/sound/pci/hda/patch_cmedia.c
++++ b/sound/pci/hda/patch_cmedia.c
+@@ -21,7 +21,6 @@
+  *  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>
+@@ -186,7 +185,6 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
+ 		.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",
+diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
+index 6aa0739..f6dd51c 100644
+--- a/sound/pci/hda/patch_conexant.c
++++ b/sound/pci/hda/patch_conexant.c
+@@ -20,7 +20,6 @@
+  *  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>
+@@ -65,6 +64,11 @@ struct conexant_spec {
+ 	hda_nid_t *adc_nids;
+ 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+ 
++	unsigned int cur_adc_idx;
++	hda_nid_t cur_adc;
++	unsigned int cur_adc_stream_tag;
++	unsigned int cur_adc_format;
++
+ 	/* capture source */
+ 	const struct hda_input_mux *input_mux;
+ 	hda_nid_t *capsrc_nids;
+@@ -218,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = {
+ 	/* NID is set in alc_build_pcms */
+ };
+ 
++static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
++				      struct hda_codec *codec,
++				      unsigned int stream_tag,
++				      unsigned int format,
++				      struct snd_pcm_substream *substream)
++{
++	struct conexant_spec *spec = codec->spec;
++	spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
++	spec->cur_adc_stream_tag = stream_tag;
++	spec->cur_adc_format = format;
++	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
++	return 0;
++}
++
++static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
++				      struct hda_codec *codec,
++				      struct snd_pcm_substream *substream)
++{
++	struct conexant_spec *spec = codec->spec;
++	snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
++	spec->cur_adc = 0;
++	return 0;
++}
++
++static struct hda_pcm_stream cx5051_pcm_analog_capture = {
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 2,
++	.nid = 0, /* fill later */
++	.ops = {
++		.prepare = cx5051_capture_pcm_prepare,
++		.cleanup = cx5051_capture_pcm_cleanup
++	},
++};
++
+ static int conexant_build_pcms(struct hda_codec *codec)
+ {
+ 	struct conexant_spec *spec = codec->spec;
+@@ -232,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec)
+ 		spec->multiout.max_channels;
+ 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ 		spec->multiout.dac_nids[0];
+-	info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
++	if (codec->vendor_id == 0x14f15051)
++		info->stream[SNDRV_PCM_STREAM_CAPTURE] =
++			cx5051_pcm_analog_capture;
++	else
++		info->stream[SNDRV_PCM_STREAM_CAPTURE] =
++			conexant_pcm_analog_capture;
+ 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
+ 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+ 
+@@ -373,7 +417,7 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
+ 	hda_nid_t nid = kcontrol->private_value & 0xff;
+ 	unsigned int eapd;
+ 
+-	eapd = ucontrol->value.integer.value[0];
++	eapd = !!ucontrol->value.integer.value[0];
+ 	if (invert)
+ 		eapd = !eapd;
+ 	if (eapd == spec->cur_eapd)
+@@ -454,7 +498,16 @@ static struct hda_input_mux cxt5045_capture_source = {
+ 	.num_items = 2,
+ 	.items = {
+ 		{ "IntMic", 0x1 },
+-		{ "LineIn", 0x2 },
++		{ "ExtMic", 0x2 },
++	}
++};
++
++static struct hda_input_mux cxt5045_capture_source_benq = {
++	.num_items = 3,
++	.items = {
++		{ "IntMic", 0x1 },
++		{ "ExtMic", 0x2 },
++		{ "LineIn", 0x3 },
+ 	}
+ };
+ 
+@@ -577,6 +630,15 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
+ 	{}
+ };
+ 
++static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
++	HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
++	HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
++	HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
++	HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
++
++	{}
++};
++
+ static struct hda_verb cxt5045_init_verbs[] = {
+ 	/* Line in, Mic */
+ 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+@@ -602,6 +664,30 @@ static struct hda_verb cxt5045_init_verbs[] = {
+ 	{ } /* end */
+ };
+ 
++static struct hda_verb cxt5045_benq_init_verbs[] = {
++	/* Int Mic, Mic */
++	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
++	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
++	/* Line In,HP, Amp  */
++	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
++	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++	{0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
++	/* Record selector: Int mic */
++	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
++	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
++	 AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
++	/* SPDIF route: PCM */
++	{0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
++	/* EAPD */
++	{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
++	{ } /* end */
++};
+ 
+ static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
+ 	/* pin sensing on HP jack */
+@@ -740,8 +826,10 @@ static int cxt5045_init(struct hda_codec *codec)
+ 
+ 
+ enum {
+-	CXT5045_LAPTOP,	 /* Laptops w/ EAPD support */
+-	CXT5045_FUJITSU, /* Laptops w/ EAPD support */ 
++	CXT5045_LAPTOP_HPSENSE,
++	CXT5045_LAPTOP_MICSENSE,
++	CXT5045_LAPTOP_HPMICSENSE,
++	CXT5045_BENQ,
+ #ifdef CONFIG_SND_DEBUG
+ 	CXT5045_TEST,
+ #endif
+@@ -749,23 +837,35 @@ enum {
+ };
+ 
+ static const char *cxt5045_models[CXT5045_MODELS] = {
+-	[CXT5045_LAPTOP]	= "laptop",
+-	[CXT5045_FUJITSU]	= "fujitsu",
++	[CXT5045_LAPTOP_HPSENSE]	= "laptop-hpsense",
++	[CXT5045_LAPTOP_MICSENSE]	= "laptop-micsense",
++	[CXT5045_LAPTOP_HPMICSENSE]	= "laptop-hpmicsense",
++	[CXT5045_BENQ]			= "benq",
+ #ifdef CONFIG_SND_DEBUG
+ 	[CXT5045_TEST]		= "test",
+ #endif
+ };
+ 
+ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
+-	SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
+-	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
+-	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
++	SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
++	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
++	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
++	SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE),
++	SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
++	SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
++	SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
++	SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
++	SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
++	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
+ 	{}
+ };
+ 
+@@ -803,7 +903,7 @@ static int patch_cxt5045(struct hda_codec *codec)
+ 						  cxt5045_models,
+ 						  cxt5045_cfg_tbl);
+ 	switch (board_config) {
+-	case CXT5045_LAPTOP:
++	case CXT5045_LAPTOP_HPSENSE:
+ 		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+ 		spec->input_mux = &cxt5045_capture_source;
+ 		spec->num_init_verbs = 2;
+@@ -811,20 +911,53 @@ static int patch_cxt5045(struct hda_codec *codec)
+ 		spec->mixers[0] = cxt5045_mixers;
+ 		codec->patch_ops.init = cxt5045_init;
+ 		break;
+-	case CXT5045_FUJITSU:
++	case CXT5045_LAPTOP_MICSENSE:
+ 		spec->input_mux = &cxt5045_capture_source;
+ 		spec->num_init_verbs = 2;
+ 		spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
+ 		spec->mixers[0] = cxt5045_mixers;
+ 		codec->patch_ops.init = cxt5045_init;
+ 		break;
++	default:
++	case CXT5045_LAPTOP_HPMICSENSE:
++		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
++		spec->input_mux = &cxt5045_capture_source;
++		spec->num_init_verbs = 3;
++		spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
++		spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
++		spec->mixers[0] = cxt5045_mixers;
++		codec->patch_ops.init = cxt5045_init;
++		break;
++	case CXT5045_BENQ:
++		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
++		spec->input_mux = &cxt5045_capture_source_benq;
++		spec->num_init_verbs = 1;
++		spec->init_verbs[0] = cxt5045_benq_init_verbs;
++		spec->mixers[0] = cxt5045_mixers;
++		spec->mixers[1] = cxt5045_benq_mixers;
++		spec->num_mixers = 2;
++		codec->patch_ops.init = cxt5045_init;
++		break;
+ #ifdef CONFIG_SND_DEBUG
+ 	case CXT5045_TEST:
+ 		spec->input_mux = &cxt5045_test_capture_source;
+ 		spec->mixers[0] = cxt5045_test_mixer;
+ 		spec->init_verbs[0] = cxt5045_test_init_verbs;
++		break;
++		
+ #endif	
+ 	}
++
++	/*
++	 * Fix max PCM level to 0 dB
++	 * (originall it has 0x2b steps with 0dB offset 0x14)
++	 */
++	snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
++				  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
++				  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
++				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
++				  (1 << AC_AMPCAP_MUTE_SHIFT));
++
+ 	return 0;
+ }
+ 
+@@ -933,13 +1066,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec)
+ static void cxt5047_hp_automic(struct hda_codec *codec)
+ {
+ 	static struct hda_verb mic_jack_on[] = {
+-		{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+-		{0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
++		{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++		{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ 		{}
+ 	};
+ 	static struct hda_verb mic_jack_off[] = {
+-		{0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+-		{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
++		{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++		{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ 		{}
+ 	};
+ 	unsigned int present;
+@@ -956,8 +1089,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
+ static void cxt5047_hp_unsol_event(struct hda_codec *codec,
+ 				  unsigned int res)
+ {
+-	res >>= 26;
+-	switch (res) {
++	switch (res >> 26) {
+ 	case CONEXANT_HP_EVENT:
+ 		cxt5047_hp_automute(codec);
+ 		break;
+@@ -1166,6 +1298,17 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
+ 		.get = conexant_mux_enum_get,
+ 		.put = conexant_mux_enum_put,
+ 	},
++	HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
++	HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
++	HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
++	HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
++	HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
++	HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
++	HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
++	HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
++
+ 	{ } /* end */
+ };
+ 
+@@ -1255,9 +1398,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
+ 
+ static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
++	SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
+ 	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
+ 	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
+-	SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
+ 	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
+ 	{}
+ };
+@@ -1324,10 +1467,260 @@ static int patch_cxt5047(struct hda_codec *codec)
+ 	return 0;
+ }
+ 
++/* Conexant 5051 specific */
++static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
++static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
++#define CXT5051_SPDIF_OUT	0x1C
++#define CXT5051_PORTB_EVENT	0x38
++#define CXT5051_PORTC_EVENT	0x39
++
++static struct hda_channel_mode cxt5051_modes[1] = {
++	{ 2, NULL },
++};
++
++static void cxt5051_update_speaker(struct hda_codec *codec)
++{
++	struct conexant_spec *spec = codec->spec;
++	unsigned int pinctl;
++	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
++	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
++			    pinctl);
++}
++
++/* turn on/off EAPD (+ mute HP) as a master switch */
++static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
++				    struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++
++	if (!cxt_eapd_put(kcontrol, ucontrol))
++		return 0;
++	cxt5051_update_speaker(codec);
++	return 1;
++}
++
++/* toggle input of built-in and mic jack appropriately */
++static void cxt5051_portb_automic(struct hda_codec *codec)
++{
++	unsigned int present;
++
++	present = snd_hda_codec_read(codec, 0x17, 0,
++				     AC_VERB_GET_PIN_SENSE, 0) &
++		AC_PINSENSE_PRESENCE;
++	snd_hda_codec_write(codec, 0x14, 0,
++			    AC_VERB_SET_CONNECT_SEL,
++			    present ? 0x01 : 0x00);
++}
++
++/* switch the current ADC according to the jack state */
++static void cxt5051_portc_automic(struct hda_codec *codec)
++{
++	struct conexant_spec *spec = codec->spec;
++	unsigned int present;
++	hda_nid_t new_adc;
++
++	present = snd_hda_codec_read(codec, 0x18, 0,
++				     AC_VERB_GET_PIN_SENSE, 0) &
++		AC_PINSENSE_PRESENCE;
++	if (present)
++		spec->cur_adc_idx = 1;
++	else
++		spec->cur_adc_idx = 0;
++	new_adc = spec->adc_nids[spec->cur_adc_idx];
++	if (spec->cur_adc && spec->cur_adc != new_adc) {
++		/* stream is running, let's swap the current ADC */
++		snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
++		spec->cur_adc = new_adc;
++		snd_hda_codec_setup_stream(codec, new_adc,
++					   spec->cur_adc_stream_tag, 0,
++					   spec->cur_adc_format);
++	}
++}
++
++/* mute internal speaker if HP is plugged */
++static void cxt5051_hp_automute(struct hda_codec *codec)
++{
++	struct conexant_spec *spec = codec->spec;
++
++	spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
++				     AC_VERB_GET_PIN_SENSE, 0) &
++		AC_PINSENSE_PRESENCE;
++	cxt5051_update_speaker(codec);
++}
++
++/* unsolicited event for HP jack sensing */
++static void cxt5051_hp_unsol_event(struct hda_codec *codec,
++				   unsigned int res)
++{
++	switch (res >> 26) {
++	case CONEXANT_HP_EVENT:
++		cxt5051_hp_automute(codec);
++		break;
++	case CXT5051_PORTB_EVENT:
++		cxt5051_portb_automic(codec);
++		break;
++	case CXT5051_PORTC_EVENT:
++		cxt5051_portc_automic(codec);
++		break;
++	}
++}
++
++static struct snd_kcontrol_new cxt5051_mixers[] = {
++	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
++	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
++	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
++	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
++	HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
++	HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
++	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = cxt_eapd_info,
++		.get = cxt_eapd_get,
++		.put = cxt5051_hp_master_sw_put,
++		.private_value = 0x1a,
++	},
++
++	{}
++};
++
++static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
++	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
++	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
++	HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
++	HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
++	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = cxt_eapd_info,
++		.get = cxt_eapd_get,
++		.put = cxt5051_hp_master_sw_put,
++		.private_value = 0x1a,
++	},
++
++	{}
++};
++
++static struct hda_verb cxt5051_init_verbs[] = {
++	/* Line in, Mic */
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
++	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
++	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
++	/* SPK  */
++	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
++	/* HP, Amp  */
++	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
++	/* DAC1 */	
++	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++	/* Record selector: Int mic */
++	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
++	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
++	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
++	/* SPDIF route: PCM */
++	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
++	/* EAPD */
++	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 
++	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
++	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
++	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
++	{ } /* end */
++};
++
++/* initialize jack-sensing, too */
++static int cxt5051_init(struct hda_codec *codec)
++{
++	conexant_init(codec);
++	if (codec->patch_ops.unsol_event) {
++		cxt5051_hp_automute(codec);
++		cxt5051_portb_automic(codec);
++		cxt5051_portc_automic(codec);
++	}
++	return 0;
++}
++
++
++enum {
++	CXT5051_LAPTOP,	 /* Laptops w/ EAPD support */
++	CXT5051_HP,	/* no docking */
++	CXT5051_MODELS
++};
++
++static const char *cxt5051_models[CXT5051_MODELS] = {
++	[CXT5051_LAPTOP]	= "laptop",
++	[CXT5051_HP]		= "hp",
++};
++
++static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
++	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
++		      CXT5051_LAPTOP),
++	SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
++	{}
++};
++
++static int patch_cxt5051(struct hda_codec *codec)
++{
++	struct conexant_spec *spec;
++	int board_config;
++
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	mutex_init(&spec->amp_mutex);
++	codec->spec = spec;
++
++	codec->patch_ops = conexant_patch_ops;
++	codec->patch_ops.init = cxt5051_init;
++
++	spec->multiout.max_channels = 2;
++	spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
++	spec->multiout.dac_nids = cxt5051_dac_nids;
++	spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
++	spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
++	spec->adc_nids = cxt5051_adc_nids;
++	spec->num_mixers = 1;
++	spec->mixers[0] = cxt5051_mixers;
++	spec->num_init_verbs = 1;
++	spec->init_verbs[0] = cxt5051_init_verbs;
++	spec->spdif_route = 0;
++	spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
++	spec->channel_mode = cxt5051_modes;
++	spec->cur_adc = 0;
++	spec->cur_adc_idx = 0;
++
++	board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
++						  cxt5051_models,
++						  cxt5051_cfg_tbl);
++	switch (board_config) {
++	case CXT5051_HP:
++		codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
++		spec->mixers[0] = cxt5051_hp_mixers;
++		break;
++	default:
++	case CXT5051_LAPTOP:
++		codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
++		break;
++	}
++
++	return 0;
++}
++
++
++/*
++ */
++
+ struct hda_codec_preset snd_hda_preset_conexant[] = {
+ 	{ .id = 0x14f15045, .name = "CX20549 (Venice)",
+ 	  .patch = patch_cxt5045 },
+ 	{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
+ 	  .patch = patch_cxt5047 },
++	{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
++	  .patch = patch_cxt5051 },
+ 	{} /* terminator */
+ };
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 1c50278..586d98f 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -23,7 +23,6 @@
+  *  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>
+@@ -92,9 +91,12 @@ enum {
+ 	ALC262_HP_BPC,
+ 	ALC262_HP_BPC_D7000_WL,
+ 	ALC262_HP_BPC_D7000_WF,
++	ALC262_HP_TC_T5735,
++	ALC262_HP_RP5700,
+ 	ALC262_BENQ_ED8,
+ 	ALC262_SONY_ASSAMD,
+ 	ALC262_BENQ_T31,
++	ALC262_ULTRA,
+ 	ALC262_AUTO,
+ 	ALC262_MODEL_LAST /* last tag */
+ };
+@@ -104,10 +106,21 @@ enum {
+ 	ALC268_3ST,
+ 	ALC268_TOSHIBA,
+ 	ALC268_ACER,
++	ALC268_DELL,
++#ifdef CONFIG_SND_DEBUG
++	ALC268_TEST,
++#endif
+ 	ALC268_AUTO,
+ 	ALC268_MODEL_LAST /* last tag */
+ };
+ 
++/* ALC269 models */
++enum {
++	ALC269_BASIC,
++	ALC269_AUTO,
++	ALC269_MODEL_LAST /* last tag */
++};
++
+ /* ALC861 models */
+ enum {
+ 	ALC861_3ST,
+@@ -144,6 +157,7 @@ enum {
+ 	ALC662_5ST_DIG,
+ 	ALC662_LENOVO_101E,
+ 	ALC662_ASUS_EEEPC_P701,
++	ALC662_ASUS_EEEPC_EP20,
+ 	ALC662_AUTO,
+ 	ALC662_MODEL_LAST,
+ };
+@@ -183,6 +197,8 @@ enum {
+ 	ALC883_HAIER_W66,		
+ 	ALC888_6ST_HP,
+ 	ALC888_3ST_HP,
++	ALC888_6ST_DELL,
++	ALC883_MITAC,
+ 	ALC883_AUTO,
+ 	ALC883_MODEL_LAST,
+ };
+@@ -204,6 +220,8 @@ struct alc_spec {
+ 	char *stream_name_analog;	/* analog PCM stream */
+ 	struct hda_pcm_stream *stream_analog_playback;
+ 	struct hda_pcm_stream *stream_analog_capture;
++	struct hda_pcm_stream *stream_analog_alt_playback;
++	struct hda_pcm_stream *stream_analog_alt_capture;
+ 
+ 	char *stream_name_digital;	/* digital PCM stream */
+ 	struct hda_pcm_stream *stream_digital_playback;
+@@ -214,6 +232,7 @@ struct alc_spec {
+ 					 * max_channels, dacs must be set
+ 					 * dig_out_nid and hp_nid are optional
+ 					 */
++	hda_nid_t alt_dac_nid;
+ 
+ 	/* capture */
+ 	unsigned int num_adc_nids;
+@@ -247,7 +266,11 @@ struct alc_spec {
+ 	/* for pin sensing */
+ 	unsigned int sense_updated: 1;
+ 	unsigned int jack_present: 1;
++	unsigned int master_sw: 1;
+ 
++	/* for virtual master */
++	hda_nid_t vmaster_nid;
++	u32 vmaster_tlv[4];
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	struct hda_loopback_check loopback;
+ #endif
+@@ -562,7 +585,7 @@ static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
+ 	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ 	long *valp = ucontrol->value.integer.value;
+ 	unsigned int val = snd_hda_codec_read(codec, nid, 0,
+-					      AC_VERB_GET_DIGI_CONVERT, 0x00);
++					      AC_VERB_GET_DIGI_CONVERT_1, 0x00);
+ 
+ 	*valp = (val & mask) != 0;
+ 	return 0;
+@@ -576,7 +599,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
+ 	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ 	long val = *ucontrol->value.integer.value;
+ 	unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+-						    AC_VERB_GET_DIGI_CONVERT,
++						    AC_VERB_GET_DIGI_CONVERT_1,
+ 						    0x00);
+ 
+ 	/* Set/unset the masked control bit(s) as needed */
+@@ -598,6 +621,59 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
+ 	  .private_value = nid | (mask<<16) }
+ #endif   /* CONFIG_SND_DEBUG */
+ 
++/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
++ * Again, this is only used in the ALC26x test models to help identify when
++ * the EAPD line must be asserted for features to work.
++ */
++#ifdef CONFIG_SND_DEBUG
++#define alc_eapd_ctrl_info	snd_ctl_boolean_mono_info
++
++static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol,
++			      struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	hda_nid_t nid = kcontrol->private_value & 0xffff;
++	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
++	long *valp = ucontrol->value.integer.value;
++	unsigned int val = snd_hda_codec_read(codec, nid, 0,
++					      AC_VERB_GET_EAPD_BTLENABLE, 0x00);
++
++	*valp = (val & mask) != 0;
++	return 0;
++}
++
++static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
++			      struct snd_ctl_elem_value *ucontrol)
++{
++	int change;
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	hda_nid_t nid = kcontrol->private_value & 0xffff;
++	unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
++	long val = *ucontrol->value.integer.value;
++	unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
++						    AC_VERB_GET_EAPD_BTLENABLE,
++						    0x00);
++
++	/* Set/unset the masked control bit(s) as needed */
++	change = (!val ? 0 : mask) != (ctrl_data & mask);
++	if (!val)
++		ctrl_data &= ~mask;
++	else
++		ctrl_data |= mask;
++	snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
++				  ctrl_data);
++
++	return change;
++}
++
++#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
++	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
++	  .info = alc_eapd_ctrl_info, \
++	  .get = alc_eapd_ctrl_get, \
++	  .put = alc_eapd_ctrl_put, \
++	  .private_value = nid | (mask<<16) }
++#endif   /* CONFIG_SND_DEBUG */
++
+ /*
+  * set up from the preset table
+  */
+@@ -739,7 +815,7 @@ static void alc_subsystem_id(struct hda_codec *codec,
+ 	/* check sum */
+ 	tmp = 0;
+ 	for (i = 1; i < 16; i++) {
+-		if ((ass >> i) && 1)
++		if ((ass >> i) & 1)
+ 			tmp++;
+ 	}
+ 	if (((ass >> 16) & 0xf) != tmp)
+@@ -828,10 +904,10 @@ do_sku:
+ 		break;
+ 	}
+ 	
+-	/* is laptop and enable the function "Mute internal speaker
++	/* is laptop or Desktop and enable the function "Mute internal speaker
+ 	 * when the external headphone out jack is plugged"
+ 	 */
+-	if (!(ass & 0x4) || !(ass & 0x8000))
++	if (!(ass & 0x8000))
+ 		return;
+ 	/*
+ 	 * 10~8 : Jack location
+@@ -841,9 +917,9 @@ do_sku:
+ 	 *	        when the external headphone out jack is plugged"
+ 	 */
+ 	if (!spec->autocfg.speaker_pins[0]) {
+-		if (spec->multiout.dac_nids[0])
++		if (spec->autocfg.line_out_pins[0])
+ 			spec->autocfg.speaker_pins[0] =
+-				spec->multiout.dac_nids[0];
++				spec->autocfg.line_out_pins[0];
+ 		else
+ 			return;
+ 	}
+@@ -1009,7 +1085,6 @@ static struct snd_kcontrol_new alc880_capture_mixer[] = {
+ 		.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",
+@@ -1031,7 +1106,6 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
+ 		.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",
+@@ -1226,7 +1300,6 @@ static struct snd_kcontrol_new alc880_z71v_mixer[] = {
+ };
+ 
+ 
+-/* FIXME! */
+ /*
+  * ALC880 F1734 model
+  *
+@@ -1242,8 +1315,8 @@ static hda_nid_t alc880_f1734_dac_nids[1] = {
+ static struct snd_kcontrol_new alc880_f1734_mixer[] = {
+ 	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+-	HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ 	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),
+@@ -1252,7 +1325,6 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = {
+ };
+ 
+ 
+-/* FIXME! */
+ /*
+  * ALC880 ASUS model
+  *
+@@ -1289,7 +1361,6 @@ static struct snd_kcontrol_new alc880_asus_mixer[] = {
+ 	{ } /* end */
+ };
+ 
+-/* FIXME! */
+ /*
+  * ALC880 ASUS W1V model
+  *
+@@ -1327,7 +1398,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
+ 		.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",
+@@ -1341,10 +1411,10 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
+ 
+ /* Uniwill */
+ static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
+-	HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
+-	HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ 	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+@@ -1384,16 +1454,49 @@ static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
+ };
+ 
+ static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
+-	HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
+-	HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ 	{ } /* end */
+ };
+ 
+ /*
++ * virtual master controls
++ */
++
++/*
++ * slave controls for virtual master
++ */
++static const char *alc_slave_vols[] = {
++	"Front Playback Volume",
++	"Surround Playback Volume",
++	"Center Playback Volume",
++	"LFE Playback Volume",
++	"Side Playback Volume",
++	"Headphone Playback Volume",
++	"Speaker Playback Volume",
++	"Mono Playback Volume",
++	"Line-Out Playback Volume",
++	NULL,
++};
++
++static const char *alc_slave_sws[] = {
++	"Front Playback Switch",
++	"Surround Playback Switch",
++	"Center Playback Switch",
++	"LFE Playback Switch",
++	"Side Playback Switch",
++	"Headphone Playback Switch",
++	"Speaker Playback Switch",
++	"Mono Playback Switch",
++	"IEC958 Playback Switch",
++	NULL,
++};
++
++/*
+  * build control elements
+  */
+ static int alc_build_controls(struct hda_codec *codec)
+@@ -1419,6 +1522,23 @@ static int alc_build_controls(struct hda_codec *codec)
+ 		if (err < 0)
+ 			return err;
+ 	}
++
++	/* if we have no master control, let's create it */
++	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
++		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
++					HDA_OUTPUT, spec->vmaster_tlv);
++		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
++					  spec->vmaster_tlv, alc_slave_vols);
++		if (err < 0)
++			return err;
++	}
++	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
++		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
++					  NULL, alc_slave_sws);
++		if (err < 0)
++			return err;
++	}
++
+ 	return 0;
+ }
+ 
+@@ -1790,7 +1910,6 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
+ 		alc880_uniwill_p53_dcvol_automute(codec);
+ }
+ 
+-/* FIXME! */
+ /*
+  * F1734 pin configuration:
+  * HP = 0x14, speaker-out = 0x15, mic = 0x18
+@@ -1819,7 +1938,6 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = {
+ 	{ }
+ };
+ 
+-/* FIXME! */
+ /*
+  * ASUS pin configuration:
+  * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
+@@ -1966,9 +2084,8 @@ static struct hda_channel_mode alc880_lg_ch_modes[3] = {
+ };
+ 
+ static struct snd_kcontrol_new alc880_lg_mixer[] = {
+-	/* FIXME: it's not really "master" but front channels */
+-	HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
+ 	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+@@ -2256,7 +2373,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ /*
+  * Analog capture
+  */
+-static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
++static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ 				      struct hda_codec *codec,
+ 				      unsigned int stream_tag,
+ 				      unsigned int format,
+@@ -2264,18 +2381,18 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ {
+ 	struct alc_spec *spec = codec->spec;
+ 
+-	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
++	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+ 				   stream_tag, 0, format);
+ 	return 0;
+ }
+ 
+-static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
++static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ 				      struct hda_codec *codec,
+ 				      struct snd_pcm_substream *substream)
+ {
+ 	struct alc_spec *spec = codec->spec;
+ 
+-	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
++	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+ 				   0, 0, 0);
+ 	return 0;
+ }
+@@ -2296,13 +2413,27 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = {
+ };
+ 
+ static struct hda_pcm_stream alc880_pcm_analog_capture = {
+-	.substreams = 2,
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 2,
++	/* NID is set in alc_build_pcms */
++};
++
++static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 2,
++	/* NID is set in alc_build_pcms */
++};
++
++static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
++	.substreams = 2, /* can be overridden */
+ 	.channels_min = 2,
+ 	.channels_max = 2,
+ 	/* NID is set in alc_build_pcms */
+ 	.ops = {
+-		.prepare = alc880_capture_pcm_prepare,
+-		.cleanup = alc880_capture_pcm_cleanup
++		.prepare = alc880_alt_capture_pcm_prepare,
++		.cleanup = alc880_alt_capture_pcm_cleanup
+ 	},
+ };
+ 
+@@ -2326,7 +2457,7 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = {
+ };
+ 
+ /* Used by alc_build_pcms to flag that a PCM has no playback stream */
+-static struct hda_pcm_stream alc_pcm_null_playback = {
++static struct hda_pcm_stream alc_pcm_null_stream = {
+ 	.substreams = 0,
+ 	.channels_min = 0,
+ 	.channels_max = 0,
+@@ -2383,17 +2514,32 @@ static int alc_build_pcms(struct hda_codec *codec)
+ 	 * model, configure a second analog capture-only PCM.
+ 	 */
+ 	/* Additional Analaog capture for index #2 */
+-	if (spec->num_adc_nids > 1 && spec->stream_analog_capture &&
+-	    spec->adc_nids) {
++	if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
++	    (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
+ 		codec->num_pcms = 3;
+ 		info = spec->pcm_rec + 2;
+ 		info->name = spec->stream_name_analog;
+-		/* No playback stream for second PCM */
+-		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
+-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+-		if (spec->stream_analog_capture) {
+-			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+-			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
++		if (spec->alt_dac_nid) {
++			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
++				*spec->stream_analog_alt_playback;
++			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
++				spec->alt_dac_nid;
++		} else {
++			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
++				alc_pcm_null_stream;
++			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
++		}
++		if (spec->num_adc_nids > 1) {
++			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
++				*spec->stream_analog_alt_capture;
++			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
++				spec->adc_nids[1];
++			info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
++				spec->num_adc_nids - 1;
++		} else {
++			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
++				alc_pcm_null_stream;
++			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
+ 		}
+ 	}
+ 
+@@ -2723,23 +2869,17 @@ static const char *alc880_models[ALC880_MODEL_LAST] = {
+ };
+ 
+ static struct snd_pci_quirk alc880_cfg_tbl[] = {
+-	/* Broken BIOS configuration */
+-	SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG),
+-	SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
+-
++	SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
+ 	SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
+-	SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
+ 	SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
+ 	SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
+ 	SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
+-
+ 	SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
+-
+ 	SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
+ 	SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
+ 	SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
+@@ -2754,54 +2894,50 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
+ 	SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
+ 	SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
+-	SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS),
+-
+-	SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
++	SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */
+ 	SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
++	SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
++	SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
+ 	SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
+ 	SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
+-	SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
+-	SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
+-	SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
+-	SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
+-	SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
+-	SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
+-	SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
+-	SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
+-	SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
++	SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
++	SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
+ 	SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
+-
++	SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
++	SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
+ 	SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
+ 	SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
+-	SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
+-
++	SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
++	SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
+-	SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
+ 	SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
++	SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
+ 	SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
+-
++	SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
+ 	SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
+ 	SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
+-	SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
+ 	SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
+-
+-	SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
+-	SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
+-	SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
++	SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
++	SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
++	SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
++	SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
++	SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
+ 	SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
++	SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
++	SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
+ 	SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
+ 	SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
+-	SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
+-	SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST),
+-
++	SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */
++	SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
++	SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
+ 	{}
+ };
+ 
+@@ -3511,6 +3647,7 @@ static int patch_alc880(struct hda_codec *codec)
+ 	spec->stream_name_analog = "ALC880 Analog";
+ 	spec->stream_analog_playback = &alc880_pcm_analog_playback;
+ 	spec->stream_analog_capture = &alc880_pcm_analog_capture;
++	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
+ 
+ 	spec->stream_name_digital = "ALC880 Digital";
+ 	spec->stream_digital_playback = &alc880_pcm_digital_playback;
+@@ -3535,6 +3672,8 @@ static int patch_alc880(struct hda_codec *codec)
+ 		}
+ 	}
+ 
++	spec->vmaster_nid = 0x0c;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC880_AUTO)
+ 		spec->init_hook = alc880_auto_init;
+@@ -3691,18 +3830,135 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++/* update HP, line and mono out pins according to the master switch */
++static void alc260_hp_master_update(struct hda_codec *codec,
++				    hda_nid_t hp, hda_nid_t line,
++				    hda_nid_t mono)
++{
++	struct alc_spec *spec = codec->spec;
++	unsigned int val = spec->master_sw ? PIN_HP : 0;
++	/* change HP and line-out pins */
++	snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
++			    val);
++	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
++			    val);
++	/* mono (speaker) depending on the HP jack sense */
++	val = (val && !spec->jack_present) ? PIN_OUT : 0;
++	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
++			    val);
++}
++
++static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
++				   struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct alc_spec *spec = codec->spec;
++	*ucontrol->value.integer.value = spec->master_sw;
++	return 0;
++}
++
++static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
++				   struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct alc_spec *spec = codec->spec;
++	int val = !!*ucontrol->value.integer.value;
++	hda_nid_t hp, line, mono;
++
++	if (val == spec->master_sw)
++		return 0;
++	spec->master_sw = val;
++	hp = (kcontrol->private_value >> 16) & 0xff;
++	line = (kcontrol->private_value >> 8) & 0xff;
++	mono = kcontrol->private_value & 0xff;
++	alc260_hp_master_update(codec, hp, line, mono);
++	return 1;
++}
++
++static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = snd_ctl_boolean_mono_info,
++		.get = alc260_hp_master_sw_get,
++		.put = alc260_hp_master_sw_put,
++		.private_value = (0x0f << 16) | (0x10 << 8) | 0x11
++	},
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
++			      HDA_OUTPUT),
++	HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct hda_verb alc260_hp_unsol_verbs[] = {
++	{0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
++	{},
++};
++
++static void alc260_hp_automute(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	unsigned int present;
++
++	present = snd_hda_codec_read(codec, 0x10, 0,
++				     AC_VERB_GET_PIN_SENSE, 0);
++	spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
++	alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
++}
++
++static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
++{
++	if ((res >> 26) == ALC880_HP_EVENT)
++		alc260_hp_automute(codec);
++}
++
+ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = snd_ctl_boolean_mono_info,
++		.get = alc260_hp_master_sw_get,
++		.put = alc260_hp_master_sw_put,
++		.private_value = (0x10 << 16) | (0x15 << 8) | 0x11
++	},
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+-	HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+-	HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+ 	{ } /* end */
+ };
+ 
++static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
++	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
++	{},
++};
++
++static void alc260_hp_3013_automute(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	unsigned int present;
++
++	present = snd_hda_codec_read(codec, 0x15, 0,
++				     AC_VERB_GET_PIN_SENSE, 0);
++	spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
++	alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
++}
++
++static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
++				       unsigned int res)
++{
++	if ((res >> 26) == ALC880_HP_EVENT)
++		alc260_hp_3013_automute(codec);
++}
++
+ /* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12, 
+  * HP jack = 0x14, CD audio =  0x16, internal speaker = 0x10.
+  */
+@@ -3812,7 +4068,6 @@ static struct snd_kcontrol_new alc260_capture_mixer[] = {
+ 		.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",
+@@ -3831,7 +4086,6 @@ static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
+ 		.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",
+@@ -4332,6 +4586,12 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
+ 	ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
+ 	ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
+ 
++	/* A switch allowing EAPD to be enabled.  Some laptops seem to use
++	 * this output to turn on an external amplifier.
++	 */
++	ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
++	ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
++
+ 	{ } /* end */
+ };
+ static struct hda_verb alc260_test_init_verbs[] = {
+@@ -4417,17 +4677,8 @@ static struct hda_verb alc260_test_init_verbs[] = {
+ };
+ #endif
+ 
+-static struct hda_pcm_stream alc260_pcm_analog_playback = {
+-	.substreams = 1,
+-	.channels_min = 2,
+-	.channels_max = 2,
+-};
+-
+-static struct hda_pcm_stream alc260_pcm_analog_capture = {
+-	.substreams = 1,
+-	.channels_min = 2,
+-	.channels_max = 2,
+-};
++#define alc260_pcm_analog_playback	alc880_pcm_analog_alt_playback
++#define alc260_pcm_analog_capture	alc880_pcm_analog_capture
+ 
+ #define alc260_pcm_digital_playback	alc880_pcm_digital_playback
+ #define alc260_pcm_digital_capture	alc880_pcm_digital_capture
+@@ -4744,8 +4995,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
+ 	SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
+ 	SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
+-	SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
+ 	SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
++	SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
+ 	{}
+ };
+ 
+@@ -4765,10 +5016,11 @@ static struct alc_config_preset alc260_presets[] = {
+ 		.input_mux = &alc260_capture_source,
+ 	},
+ 	[ALC260_HP] = {
+-		.mixers = { alc260_base_output_mixer,
++		.mixers = { alc260_hp_output_mixer,
+ 			    alc260_input_mixer,
+ 			    alc260_capture_alt_mixer },
+-		.init_verbs = { alc260_init_verbs },
++		.init_verbs = { alc260_init_verbs,
++				alc260_hp_unsol_verbs },
+ 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ 		.dac_nids = alc260_dac_nids,
+ 		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+@@ -4776,12 +5028,15 @@ static struct alc_config_preset alc260_presets[] = {
+ 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+ 		.channel_mode = alc260_modes,
+ 		.input_mux = &alc260_capture_source,
++		.unsol_event = alc260_hp_unsol_event,
++		.init_hook = alc260_hp_automute,
+ 	},
+ 	[ALC260_HP_3013] = {
+ 		.mixers = { alc260_hp_3013_mixer,
+ 			    alc260_input_mixer,
+ 			    alc260_capture_alt_mixer },
+-		.init_verbs = { alc260_hp_3013_init_verbs },
++		.init_verbs = { alc260_hp_3013_init_verbs,
++				alc260_hp_3013_unsol_verbs },
+ 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ 		.dac_nids = alc260_dac_nids,
+ 		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+@@ -4789,6 +5044,8 @@ static struct alc_config_preset alc260_presets[] = {
+ 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+ 		.channel_mode = alc260_modes,
+ 		.input_mux = &alc260_capture_source,
++		.unsol_event = alc260_hp_3013_unsol_event,
++		.init_hook = alc260_hp_3013_automute,
+ 	},
+ 	[ALC260_FUJITSU_S702X] = {
+ 		.mixers = { alc260_fujitsu_mixer,
+@@ -4906,6 +5163,8 @@ static int patch_alc260(struct hda_codec *codec)
+ 	spec->stream_digital_playback = &alc260_pcm_digital_playback;
+ 	spec->stream_digital_capture = &alc260_pcm_digital_capture;
+ 
++	spec->vmaster_nid = 0x08;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC260_AUTO)
+ 		spec->init_hook = alc260_auto_init;
+@@ -5106,15 +5365,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = {
+ };
+ 
+ static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
+-	HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT),
+-	HDA_BIND_MUTE   ("Master Switch", 0x0c, 0x02, HDA_INPUT),
+-	HDA_CODEC_MUTE  ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT),
+-	HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT),
+-	HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT),
+-	HDA_CODEC_MUTE  ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
++	HDA_BIND_MUTE   ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
++	HDA_CODEC_MUTE  ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_MUTE  ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
+ 	HDA_CODEC_MUTE  ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
+-	HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT),
++	HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
+ 	{ } /* end */
+ };
+@@ -5679,7 +5938,6 @@ static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
+ 		.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",
+@@ -5702,7 +5960,6 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
+ 		.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",
+@@ -5743,16 +6000,17 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
+ 
+ static struct snd_pci_quirk alc882_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
+-	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
+-	SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
+ 	SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
+ 	SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
+ 	SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
++	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+ 	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
+-	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
++	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
++	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
++	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
++	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
++	SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
+ 	{}
+ };
+ 
+@@ -5990,7 +6248,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
+ 	hda_nid_t nid;
+ 
+ 	nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
+-	if (nid) {
++	if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
+ 		err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ 				  "Mic Boost",
+ 				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+@@ -5998,7 +6256,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
+ 			return err;
+ 	}
+ 	nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
+-	if (nid) {
++	if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
+ 		err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ 				  "Front Mic Boost",
+ 				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+@@ -6061,6 +6319,7 @@ static int patch_alc882(struct hda_codec *codec)
+ 		case 0x106b1000: /* iMac 24 */
+ 			board_config = ALC885_IMAC24;
+ 			break;
++		case 0x106b00a1: /* Macbook */
+ 		case 0x106b2c00: /* Macbook Pro rev3 */
+ 			board_config = ALC885_MBP3;
+ 			break;
+@@ -6093,6 +6352,9 @@ static int patch_alc882(struct hda_codec *codec)
+ 	spec->stream_name_analog = "ALC882 Analog";
+ 	spec->stream_analog_playback = &alc882_pcm_analog_playback;
+ 	spec->stream_analog_capture = &alc882_pcm_analog_capture;
++	/* FIXME: setup DAC5 */
++	/*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
++	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
+ 
+ 	spec->stream_name_digital = "ALC882 Digital";
+ 	spec->stream_digital_playback = &alc882_pcm_digital_playback;
+@@ -6117,6 +6379,8 @@ static int patch_alc882(struct hda_codec *codec)
+ 		}
+ 	}
+ 
++	spec->vmaster_nid = 0x0c;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC882_AUTO)
+ 		spec->init_hook = alc882_auto_init;
+@@ -6340,6 +6604,36 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++static struct snd_kcontrol_new alc883_mitac_mixer[] = {
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
++	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
++	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,
++		/* .name = "Capture Source", */
++		.name = "Input Source",
++		.count = 2,
++		.info = alc883_mux_enum_info,
++		.get = alc883_mux_enum_get,
++		.put = alc883_mux_enum_put,
++	},
++	{ } /* end */
++};
++
+ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+@@ -6508,8 +6802,8 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
+ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+-	HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+@@ -6658,6 +6952,46 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++static struct snd_kcontrol_new alc888_6st_dell_mixer[] = {
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
++	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 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("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
++	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
++	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
++	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,
++		/* .name = "Capture Source", */
++		.name = "Input Source",
++		.count = 2,
++		.info = alc883_mux_enum_info,
++		.get = alc883_mux_enum_get,
++		.put = alc883_mux_enum_put,
++	},
++	{ } /* end */
++};
++
+ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+@@ -6772,6 +7106,67 @@ static struct hda_verb alc883_init_verbs[] = {
+ 	{ }
+ };
+ 
++/* toggle speaker-output according to the hp-jack state */
++static void alc883_mitac_hp_automute(struct hda_codec *codec)
++{
++	unsigned int present;
++
++	present = snd_hda_codec_read(codec, 0x15, 0,
++				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
++	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
++				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
++	snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
++				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
++}
++
++/* auto-toggle front mic */
++/*
++static void alc883_mitac_mic_automute(struct hda_codec *codec)
++{
++	unsigned int present;
++	unsigned char bits;
++
++	present = snd_hda_codec_read(codec, 0x18, 0,
++				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
++	bits = present ? HDA_AMP_MUTE : 0;
++	snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
++}
++*/
++
++static void alc883_mitac_automute(struct hda_codec *codec)
++{
++	alc883_mitac_hp_automute(codec);
++	/* alc883_mitac_mic_automute(codec); */
++}
++
++static void alc883_mitac_unsol_event(struct hda_codec *codec,
++					   unsigned int res)
++{
++	switch (res >> 26) {
++	case ALC880_HP_EVENT:
++		alc883_mitac_hp_automute(codec);
++		break;
++	case ALC880_MIC_EVENT:
++		/* alc883_mitac_mic_automute(codec); */
++		break;
++	}
++}
++
++static struct hda_verb alc883_mitac_verbs[] = {
++	/* HP */
++	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++	/* Subwoofer */
++	{0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
++	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++
++	/* enable unsolicited event */
++	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++	/* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
++
++	{ } /* end */
++};
++
+ static struct hda_verb alc883_tagra_verbs[] = {
+ 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+@@ -6843,6 +7238,15 @@ static struct hda_verb alc888_3st_hp_verbs[] = {
+ 	{ }
+ };
+ 
++static struct hda_verb alc888_6st_dell_verbs[] = {
++	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */
++	{0x15, AC_VERB_SET_CONNECT_SEL, 0x02},	/* Rear : output 1 (0x0e) */
++	{0x16, AC_VERB_SET_CONNECT_SEL, 0x01},	/* CLFE : output 2 (0x0d) */
++	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},	/* Side : output 3 (0x0f) */
++	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++	{ }
++};
++
+ static struct hda_verb alc888_3st_hp_2ch_init[] = {
+ 	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ 	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+@@ -7038,6 +7442,33 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
+ 	{ }
+ };
+ 
++static void alc888_6st_dell_front_automute(struct hda_codec *codec)
++{
++ 	unsigned int present;
++ 
++ 	present = snd_hda_codec_read(codec, 0x1b, 0,
++				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
++	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
++				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
++	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
++				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
++	snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
++				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
++	snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
++				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
++}
++
++static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
++					     unsigned int res)
++{
++	switch (res >> 26) {
++	case ALC880_HP_EVENT:
++		printk("hp_event\n");
++		alc888_6st_dell_front_automute(codec);
++		break;
++	}
++}
++
+ /*
+  * generic initialization of ADC, input mixers and output mixers
+  */
+@@ -7096,7 +7527,7 @@ static struct hda_verb alc883_auto_init_verbs[] = {
+ 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ 	/* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
+-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
++	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ 
+ 	{ }
+ };
+@@ -7111,7 +7542,6 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
+ 		.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",
+@@ -7130,6 +7560,7 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
+ /* pcm configuration: identiacal with ALC880 */
+ #define alc883_pcm_analog_playback	alc880_pcm_analog_playback
+ #define alc883_pcm_analog_capture	alc880_pcm_analog_capture
++#define alc883_pcm_analog_alt_capture	alc880_pcm_analog_alt_capture
+ #define alc883_pcm_digital_playback	alc880_pcm_digital_playback
+ #define alc883_pcm_digital_capture	alc880_pcm_digital_capture
+ 
+@@ -7154,53 +7585,58 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
+ 	[ALC883_HAIER_W66] 	= "haier-w66",
+ 	[ALC888_6ST_HP]		= "6stack-hp",
+ 	[ALC888_3ST_HP]		= "3stack-hp",
++	[ALC888_6ST_DELL]	= "6stack-dell",
++	[ALC883_MITAC]		= "mitac",
+ 	[ALC883_AUTO]		= "auto",
+ };
+ 
+ static struct snd_pci_quirk alc883_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
++	SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
++	SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
++	SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
++	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
++	SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
+ 	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
+-	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
+-	SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
++	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
++	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
++	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
++	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+ 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
++	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
++	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
+ 	SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
++	SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
+-	SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
++	SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
++	SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
++	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
+ 	SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
+-	SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
+-	SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
+-	SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
+-	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER),
++	SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+ 	SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
+ 	SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
+-	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+-	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
+ 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
+-	SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+ 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+-	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+-	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
+-	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
++	SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
++	SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+ 	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
+ 	SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
+-	SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+-	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+-	SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
++	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
+ 	{}
+ };
+ 
+@@ -7435,6 +7871,34 @@ static struct alc_config_preset alc883_presets[] = {
+ 		.need_dac_fix = 1,
+ 		.input_mux = &alc883_capture_source,
+ 	},
++	[ALC888_6ST_DELL] = {
++		.mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer },
++		.init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
++		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
++		.dac_nids = alc883_dac_nids,
++		.dig_out_nid = ALC883_DIGOUT_NID,
++		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
++		.adc_nids = alc883_adc_nids,
++		.dig_in_nid = ALC883_DIGIN_NID,
++		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
++		.channel_mode = alc883_sixstack_modes,
++		.input_mux = &alc883_capture_source,
++		.unsol_event = alc888_6st_dell_unsol_event,
++		.init_hook = alc888_6st_dell_front_automute,
++	},
++	[ALC883_MITAC] = {
++		.mixers = { alc883_mitac_mixer },
++		.init_verbs = { alc883_init_verbs, alc883_mitac_verbs },
++		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
++		.dac_nids = alc883_dac_nids,
++		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
++		.adc_nids = alc883_adc_nids,
++		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
++		.channel_mode = alc883_3ST_2ch_modes,
++		.input_mux = &alc883_capture_source,
++		.unsol_event = alc883_mitac_unsol_event,
++		.init_hook = alc883_mitac_automute,
++	},
+ };
+ 
+ 
+@@ -7582,6 +8046,7 @@ static int patch_alc883(struct hda_codec *codec)
+ 	spec->stream_name_analog = "ALC883 Analog";
+ 	spec->stream_analog_playback = &alc883_pcm_analog_playback;
+ 	spec->stream_analog_capture = &alc883_pcm_analog_capture;
++	spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
+ 
+ 	spec->stream_name_digital = "ALC883 Digital";
+ 	spec->stream_digital_playback = &alc883_pcm_digital_playback;
+@@ -7592,6 +8057,8 @@ static int patch_alc883(struct hda_codec *codec)
+ 		spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ 	}
+ 
++	spec->vmaster_nid = 0x0c;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC883_AUTO)
+ 		spec->init_hook = alc883_auto_init;
+@@ -7659,13 +8126,99 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++/* update HP, line and mono-out pins according to the master switch */
++static void alc262_hp_master_update(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	int val = spec->master_sw;
++
++	/* HP & line-out */
++	snd_hda_codec_write_cache(codec, 0x1b, 0,
++				  AC_VERB_SET_PIN_WIDGET_CONTROL,
++				  val ? PIN_HP : 0);
++	snd_hda_codec_write_cache(codec, 0x15, 0,
++				  AC_VERB_SET_PIN_WIDGET_CONTROL,
++				  val ? PIN_HP : 0);
++	/* mono (speaker) depending on the HP jack sense */
++	val = val && !spec->jack_present;
++	snd_hda_codec_write_cache(codec, 0x16, 0,
++				  AC_VERB_SET_PIN_WIDGET_CONTROL,
++				  val ? PIN_OUT : 0);
++}
++
++static void alc262_hp_bpc_automute(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	unsigned int presence;
++	presence = snd_hda_codec_read(codec, 0x1b, 0,
++				      AC_VERB_GET_PIN_SENSE, 0);
++	spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
++	alc262_hp_master_update(codec);
++}
++
++static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
++{
++	if ((res >> 26) != ALC880_HP_EVENT)
++		return;
++	alc262_hp_bpc_automute(codec);
++}
++
++static void alc262_hp_wildwest_automute(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	unsigned int presence;
++	presence = snd_hda_codec_read(codec, 0x15, 0,
++				      AC_VERB_GET_PIN_SENSE, 0);
++	spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
++	alc262_hp_master_update(codec);
++}
++
++static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
++					   unsigned int res)
++{
++	if ((res >> 26) != ALC880_HP_EVENT)
++		return;
++	alc262_hp_wildwest_automute(codec);
++}
++
++static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol,
++				   struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct alc_spec *spec = codec->spec;
++	*ucontrol->value.integer.value = spec->master_sw;
++	return 0;
++}
++
++static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
++				   struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct alc_spec *spec = codec->spec;
++	int val = !!*ucontrol->value.integer.value;
++
++	if (val == spec->master_sw)
++		return 0;
++	spec->master_sw = val;
++	alc262_hp_master_update(codec);
++	return 1;
++}
++
+ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = snd_ctl_boolean_mono_info,
++		.get = alc262_hp_master_sw_get,
++		.put = alc262_hp_master_sw_put,
++	},
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+-	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+-	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+-
++	HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
++			      HDA_OUTPUT),
++	HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
++			    HDA_OUTPUT),
+ 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+@@ -7684,12 +8237,21 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+ };
+ 
+ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = snd_ctl_boolean_mono_info,
++		.get = alc262_hp_master_sw_get,
++		.put = alc262_hp_master_sw_put,
++	},
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 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_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+-	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
++			      HDA_OUTPUT),
++	HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
++			    HDA_OUTPUT),
+ 	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT),
+@@ -7709,6 +8271,85 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++/* mute/unmute internal speaker according to the hp jack and mute state */
++static void alc262_hp_t5735_automute(struct hda_codec *codec, int force)
++{
++	struct alc_spec *spec = codec->spec;
++
++	if (force || !spec->sense_updated) {
++		unsigned int present;
++		present = snd_hda_codec_read(codec, 0x15, 0,
++					     AC_VERB_GET_PIN_SENSE, 0);
++		spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
++		spec->sense_updated = 1;
++	}
++	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE,
++				 spec->jack_present ? HDA_AMP_MUTE : 0);
++}
++
++static void alc262_hp_t5735_unsol_event(struct hda_codec *codec,
++					unsigned int res)
++{
++	if ((res >> 26) != ALC880_HP_EVENT)
++		return;
++	alc262_hp_t5735_automute(codec, 1);
++}
++
++static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
++{
++	alc262_hp_t5735_automute(codec, 1);
++}
++
++static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Speaker 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("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct hda_verb alc262_hp_t5735_verbs[] = {
++	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++
++	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++	{ }
++};
++
++static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
++	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
++	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct hda_verb alc262_hp_rp5700_verbs[] = {
++	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
++	{}
++};
++
++static struct hda_input_mux alc262_hp_rp5700_capture_source = {
++	.num_items = 1,
++	.items = {
++		{ "Line", 0x1 },
++	},
++};
++
+ /* bind hp and internal speaker mute (with plug check) */
+ static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
+ 				     struct snd_ctl_elem_value *ucontrol)
+@@ -8082,6 +8723,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
+ 	{}
+ };
+ 
++/* Samsung Q1 Ultra Vista model setup */
++static struct snd_kcontrol_new alc262_ultra_mixer[] = {
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
++	HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct hda_verb alc262_ultra_verbs[] = {
++	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
++	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++	/* Mic is on Node 0x19 */
++	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
++	{0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++	{0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++	{0x24, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++	{}
++};
++
++static struct hda_input_mux alc262_ultra_capture_source = {
++	.num_items = 1,
++	.items = {
++		{ "Mic", 0x1 },
++	},
++};
++
++/* mute/unmute internal speaker according to the hp jack and mute state */
++static void alc262_ultra_automute(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	unsigned int mute;
++	unsigned int present;
++
++	/* need to execute and sync at first */
++	snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
++	present = snd_hda_codec_read(codec, 0x15, 0,
++				     AC_VERB_GET_PIN_SENSE, 0);
++	spec->jack_present = (present & 0x80000000) != 0;
++	if (spec->jack_present) {
++		/* mute internal speaker */
++		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
++					 HDA_AMP_MUTE, HDA_AMP_MUTE);
++	} else {
++		/* unmute internal speaker if necessary */
++		mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
++		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
++					 HDA_AMP_MUTE, mute);
++	}
++}
++
++/* unsolicited event for HP jack sensing */
++static void alc262_ultra_unsol_event(struct hda_codec *codec,
++				       unsigned int res)
++{
++	if ((res >> 26) != ALC880_HP_EVENT)
++		return;
++	alc262_ultra_automute(codec);
++}
++
+ /* add playback controls from the parsed DAC table */
+ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
+ 					     const struct auto_pin_cfg *cfg)
+@@ -8269,7 +8976,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
+ 	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ 	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ 
+-	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
++	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ 	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ 
+@@ -8311,6 +9018,8 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
+ 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+ 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ 
++	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++
+ 	{ }
+ };
+ 
+@@ -8405,6 +9114,8 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
+         /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
+ 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
+ 
++	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++
+ 	{ }
+ };
+ 
+@@ -8484,39 +9195,49 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {
+ 	[ALC262_FUJITSU]	= "fujitsu",
+ 	[ALC262_HP_BPC]		= "hp-bpc",
+ 	[ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
++	[ALC262_HP_TC_T5735]	= "hp-tc-t5735",
++	[ALC262_HP_RP5700]	= "hp-rp5700",
+ 	[ALC262_BENQ_ED8]	= "benq",
+ 	[ALC262_BENQ_T31]	= "benq-t31",
+ 	[ALC262_SONY_ASSAMD]	= "sony-assamd",
++	[ALC262_ULTRA]		= "ultra",
+ 	[ALC262_AUTO]		= "auto",
+ };
+ 
+ static struct snd_pci_quirk alc262_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
+ 	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
+-	SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+ 	SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
+-	SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
+-	SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+-	SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
+-	SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+ 	SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC),
+ 	SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
+-	SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
+-	SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
+-	SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ 	SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
++	SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ 	SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
++	SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ 	SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
++	SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ 	SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
++	SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
++	SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
++		      ALC262_HP_TC_T5735),
++	SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
++	SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ 	SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
++	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
++	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
++	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+ 	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
+-	SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
++	SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
+ 	SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
+ 	SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
+-	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+-	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+-	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+-	SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
++	SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+ 	{}
+ };
+ 
+@@ -8579,6 +9300,8 @@ static struct alc_config_preset alc262_presets[] = {
+ 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+ 		.channel_mode = alc262_modes,
+ 		.input_mux = &alc262_HP_capture_source,
++		.unsol_event = alc262_hp_bpc_unsol_event,
++		.init_hook = alc262_hp_bpc_automute,
+ 	},
+ 	[ALC262_HP_BPC_D7000_WF] = {
+ 		.mixers = { alc262_HP_BPC_WildWest_mixer },
+@@ -8589,6 +9312,8 @@ static struct alc_config_preset alc262_presets[] = {
+ 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+ 		.channel_mode = alc262_modes,
+ 		.input_mux = &alc262_HP_D7000_capture_source,
++		.unsol_event = alc262_hp_wildwest_unsol_event,
++		.init_hook = alc262_hp_wildwest_automute,
+ 	},
+ 	[ALC262_HP_BPC_D7000_WL] = {
+ 		.mixers = { alc262_HP_BPC_WildWest_mixer,
+@@ -8600,7 +9325,30 @@ static struct alc_config_preset alc262_presets[] = {
+ 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+ 		.channel_mode = alc262_modes,
+ 		.input_mux = &alc262_HP_D7000_capture_source,
++		.unsol_event = alc262_hp_wildwest_unsol_event,
++		.init_hook = alc262_hp_wildwest_automute,
++	},
++	[ALC262_HP_TC_T5735] = {
++		.mixers = { alc262_hp_t5735_mixer },
++		.init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs },
++		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
++		.dac_nids = alc262_dac_nids,
++		.hp_nid = 0x03,
++		.num_channel_mode = ARRAY_SIZE(alc262_modes),
++		.channel_mode = alc262_modes,
++		.input_mux = &alc262_capture_source,
++		.unsol_event = alc262_hp_t5735_unsol_event,
++		.init_hook = alc262_hp_t5735_init_hook,
+ 	},
++	[ALC262_HP_RP5700] = {
++		.mixers = { alc262_hp_rp5700_mixer },
++		.init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
++		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
++		.dac_nids = alc262_dac_nids,
++		.num_channel_mode = ARRAY_SIZE(alc262_modes),
++		.channel_mode = alc262_modes,
++		.input_mux = &alc262_hp_rp5700_capture_source,
++        },
+ 	[ALC262_BENQ_ED8] = {
+ 		.mixers = { alc262_base_mixer },
+ 		.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
+@@ -8635,6 +9383,19 @@ static struct alc_config_preset alc262_presets[] = {
+ 		.unsol_event = alc262_hippo_unsol_event,
+ 		.init_hook = alc262_hippo_automute,
+ 	},	
++	[ALC262_ULTRA] = {
++		.mixers = { alc262_ultra_mixer },
++		.init_verbs = { alc262_init_verbs, alc262_ultra_verbs },
++		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
++		.dac_nids = alc262_dac_nids,
++		.hp_nid = 0x03,
++		.dig_out_nid = ALC262_DIGOUT_NID,
++		.num_channel_mode = ARRAY_SIZE(alc262_modes),
++		.channel_mode = alc262_modes,
++		.input_mux = &alc262_ultra_capture_source,
++		.unsol_event = alc262_ultra_unsol_event,
++		.init_hook = alc262_ultra_automute,
++	},
+ };
+ 
+ static int patch_alc262(struct hda_codec *codec)
+@@ -8716,6 +9477,8 @@ static int patch_alc262(struct hda_codec *codec)
+ 		}
+ 	}
+ 
++	spec->vmaster_nid = 0x0c;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC262_AUTO)
+ 		spec->init_hook = alc262_auto_init;
+@@ -8873,6 +9636,49 @@ static void alc268_acer_init_hook(struct hda_codec *codec)
+ 	alc268_acer_automute(codec, 1);
+ }
+ 
++static struct snd_kcontrol_new alc268_dell_mixer[] = {
++	/* output mixer control */
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
++	{ }
++};
++
++static struct hda_verb alc268_dell_verbs[] = {
++	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++	{ }
++};
++
++/* mute/unmute internal speaker according to the hp jack and mute state */
++static void alc268_dell_automute(struct hda_codec *codec)
++{
++	unsigned int present;
++	unsigned int mute;
++
++	present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0);
++	if (present & 0x80000000)
++		mute = HDA_AMP_MUTE;
++	else
++		mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
++	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
++				 HDA_AMP_MUTE, mute);
++}
++
++static void alc268_dell_unsol_event(struct hda_codec *codec,
++				    unsigned int res)
++{
++	if ((res >> 26) != ALC880_HP_EVENT)
++		return;
++	alc268_dell_automute(codec);
++}
++
++#define alc268_dell_init_hook	alc268_dell_automute
++
+ /*
+  * generic initialization of ADC, input mixers and output mixers
+  */
+@@ -8915,19 +9721,13 @@ static struct hda_verb alc268_base_init_verbs[] = {
+ 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+ 
+-	/* FIXME: use matrix-type input source selection */
+-	/* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
+-	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+-	/* Input mixer2 */
+-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
++	/* Unmute Selector 23h,24h and set the default input to mic-in */
++	
++	{0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++	{0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ 
+-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ 	{ }
+ };
+ 
+@@ -8972,29 +9772,14 @@ static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ 	struct alc_spec *spec = codec->spec;
+-	const struct hda_input_mux *imux = spec->input_mux;
++
+ 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 	static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
+ 	hda_nid_t nid = capture_mixers[adc_idx];
+-	unsigned int *cur_val = &spec->cur_mux[adc_idx];
+-	unsigned int i, idx;
+ 
+-	idx = ucontrol->value.enumerated.item[0];
+-	if (idx >= imux->num_items)
+-		idx = imux->num_items - 1;
+-	if (*cur_val == idx)
+-		return 0;
+-	for (i = 0; i < imux->num_items; i++) {
+-		unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
+-		snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
+-					 imux->items[i].index,
+-					 HDA_AMP_MUTE, v);
+-                snd_hda_codec_write_cache(codec, nid, 0,
+-					  AC_VERB_SET_CONNECT_SEL,
+-					  idx );
+-	}
+-	*cur_val = idx;
+-	return 1;
++	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
++				     nid,
++				     &spec->cur_mux[adc_idx]);
+ }
+ 
+ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
+@@ -9004,7 +9789,6 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
+ 		.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",
+@@ -9025,7 +9809,6 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
+ 		.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",
+@@ -9047,6 +9830,61 @@ static struct hda_input_mux alc268_capture_source = {
+ 	},
+ };
+ 
++#ifdef CONFIG_SND_DEBUG
++static struct snd_kcontrol_new alc268_test_mixer[] = {
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++
++	/* Volume widgets */
++	HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
++	HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
++	HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
++	HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
++	HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
++	HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
++	HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
++	HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
++	/* The below appears problematic on some hardwares */
++	/*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
++	HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
++
++	/* Modes for retasking pin widgets */
++	ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
++	ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
++	ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
++	ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
++
++	/* Controls for GPIO pins, assuming they are configured as outputs */
++	ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
++	ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
++	ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
++	ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
++
++	/* Switches to allow the digital SPDIF output pin to be enabled.
++	 * The ALC268 does not have an SPDIF input.
++	 */
++	ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
++
++	/* A switch allowing EAPD to be enabled.  Some laptops seem to use
++	 * this output to turn on an external amplifier.
++	 */
++	ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
++	ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
++
++	{ } /* end */
++};
++#endif
++
+ /* create input playback/capture controls for the given pin */
+ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+ 				    const char *ctlname, int idx)
+@@ -9194,6 +10032,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
+ /* pcm configuration: identiacal with ALC880 */
+ #define alc268_pcm_analog_playback	alc880_pcm_analog_playback
+ #define alc268_pcm_analog_capture	alc880_pcm_analog_capture
++#define alc268_pcm_analog_alt_capture	alc880_pcm_analog_alt_capture
+ #define alc268_pcm_digital_playback	alc880_pcm_digital_playback
+ 
+ /*
+@@ -9259,16 +10098,23 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
+ 	[ALC268_3ST]		= "3stack",
+ 	[ALC268_TOSHIBA]	= "toshiba",
+ 	[ALC268_ACER]		= "acer",
++	[ALC268_DELL]		= "dell",
++#ifdef CONFIG_SND_DEBUG
++	[ALC268_TEST]		= "test",
++#endif
+ 	[ALC268_AUTO]		= "auto",
+ };
+ 
+ static struct snd_pci_quirk alc268_cfg_tbl[] = {
++	SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
++	SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
++	SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
++	SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
++	SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
++	SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
+ 	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
+ 	SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
+ 	SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
+-	SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
+-	SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
+-	SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
+ 	SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
+ 	{}
+ };
+@@ -9317,6 +10163,35 @@ static struct alc_config_preset alc268_presets[] = {
+ 		.unsol_event = alc268_acer_unsol_event,
+ 		.init_hook = alc268_acer_init_hook,
+ 	},
++	[ALC268_DELL] = {
++		.mixers = { alc268_dell_mixer },
++		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
++				alc268_dell_verbs },
++		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
++		.dac_nids = alc268_dac_nids,
++		.hp_nid = 0x02,
++		.num_channel_mode = ARRAY_SIZE(alc268_modes),
++		.channel_mode = alc268_modes,
++		.unsol_event = alc268_dell_unsol_event,
++		.init_hook = alc268_dell_init_hook,
++		.input_mux = &alc268_capture_source,
++	},
++#ifdef CONFIG_SND_DEBUG
++	[ALC268_TEST] = {
++		.mixers = { alc268_test_mixer, alc268_capture_mixer },
++		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
++				alc268_volume_init_verbs },
++		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
++		.dac_nids = alc268_dac_nids,
++		.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
++		.adc_nids = alc268_adc_nids_alt,
++		.hp_nid = 0x03,
++		.dig_out_nid = ALC268_DIGOUT_NID,
++		.num_channel_mode = ARRAY_SIZE(alc268_modes),
++		.channel_mode = alc268_modes,
++		.input_mux = &alc268_capture_source,
++	},
++#endif
+ };
+ 
+ static int patch_alc268(struct hda_codec *codec)
+@@ -9361,34 +10236,34 @@ static int patch_alc268(struct hda_codec *codec)
+ 	spec->stream_name_analog = "ALC268 Analog";
+ 	spec->stream_analog_playback = &alc268_pcm_analog_playback;
+ 	spec->stream_analog_capture = &alc268_pcm_analog_capture;
++	spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
+ 
+ 	spec->stream_name_digital = "ALC268 Digital";
+ 	spec->stream_digital_playback = &alc268_pcm_digital_playback;
+ 
+-	if (board_config == ALC268_AUTO) {
+-		if (!spec->adc_nids && spec->input_mux) {
+-			/* check whether NID 0x07 is valid */
+-			unsigned int wcap = get_wcaps(codec, 0x07);
+-
+-			/* get type */
+-			wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+-			if (wcap != AC_WID_AUD_IN) {
+-				spec->adc_nids = alc268_adc_nids_alt;
+-				spec->num_adc_nids =
+-					ARRAY_SIZE(alc268_adc_nids_alt);
+-				spec->mixers[spec->num_mixers] =
++	if (!spec->adc_nids && spec->input_mux) {
++		/* check whether NID 0x07 is valid */
++		unsigned int wcap = get_wcaps(codec, 0x07);
++
++		/* get type */
++		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
++		if (wcap != AC_WID_AUD_IN) {
++			spec->adc_nids = alc268_adc_nids_alt;
++			spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
++			spec->mixers[spec->num_mixers] =
+ 					alc268_capture_alt_mixer;
+-				spec->num_mixers++;
+-			} else {
+-				spec->adc_nids = alc268_adc_nids;
+-				spec->num_adc_nids =
+-					ARRAY_SIZE(alc268_adc_nids);
+-				spec->mixers[spec->num_mixers] =
+-					alc268_capture_mixer;
+-				spec->num_mixers++;
+-			}
++			spec->num_mixers++;
++		} else {
++			spec->adc_nids = alc268_adc_nids;
++			spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
++			spec->mixers[spec->num_mixers] =
++				alc268_capture_mixer;
++			spec->num_mixers++;
+ 		}
+ 	}
++
++	spec->vmaster_nid = 0x02;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC268_AUTO)
+ 		spec->init_hook = alc268_auto_init;
+@@ -9397,6 +10272,360 @@ static int patch_alc268(struct hda_codec *codec)
+ }
+ 
+ /*
++ *  ALC269 channel source setting (2 channel)
++ */
++#define ALC269_DIGOUT_NID	ALC880_DIGOUT_NID
++
++#define alc269_dac_nids		alc260_dac_nids
++
++static hda_nid_t alc269_adc_nids[1] = {
++	/* ADC1 */
++	0x07,
++};
++
++#define alc269_modes		alc260_modes
++#define alc269_capture_source	alc880_lg_lw_capture_source
++
++static struct snd_kcontrol_new alc269_base_mixer[] = {
++	HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, 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("Mic Boost", 0x18, 0, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
++	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
++	{ } /* end */
++};
++
++/* capture mixer elements */
++static struct snd_kcontrol_new alc269_capture_mixer[] = {
++	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
++	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		/* The multiple "Capture Source" controls confuse alsamixer
++		 * So call somewhat different..
++		 */
++		/* .name = "Capture Source", */
++		.name = "Input Source",
++		.count = 1,
++		.info = alc_mux_enum_info,
++		.get = alc_mux_enum_get,
++		.put = alc_mux_enum_put,
++	},
++	{ } /* end */
++};
++
++/*
++ * generic initialization of ADC, input mixers and output mixers
++ */
++static struct hda_verb alc269_init_verbs[] = {
++	/*
++	 * Unmute ADC0 and set the default input to mic-in
++	 */
++	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++
++	/* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
++	 * analog-loopback mixer widget
++	 * Note: PASD motherboards uses the Line In 2 as the input for
++	 * front panel mic (mic 2)
++	 */
++	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
++	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
++	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
++	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
++
++	/*
++	 * Set up output mixers (0x0c - 0x0e)
++	 */
++	/* set vol=0 to output mixers */
++	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++
++	/* set up input amps for analog loopback */
++	/* Amp Indices: DAC = 0, mixer = 1 */
++	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++
++	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++
++	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++
++	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
++
++	/* FIXME: use matrix-type input source selection */
++	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
++	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
++	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
++
++	/* set EAPD */
++	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
++	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
++	{ }
++};
++
++/* add playback controls from the parsed DAC table */
++static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
++					     const struct auto_pin_cfg *cfg)
++{
++	hda_nid_t nid;
++	int err;
++
++	spec->multiout.num_dacs = 1;	/* only use one dac */
++	spec->multiout.dac_nids = spec->private_dac_nids;
++	spec->multiout.dac_nids[0] = 2;
++
++	nid = cfg->line_out_pins[0];
++	if (nid) {
++		err = add_control(spec, ALC_CTL_WIDGET_VOL,
++				  "Front Playback Volume",
++				  HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
++		if (err < 0)
++			return err;
++		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
++				  "Front Playback Switch",
++				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++		if (err < 0)
++			return err;
++	}
++
++	nid = cfg->speaker_pins[0];
++	if (nid) {
++		if (!cfg->line_out_pins[0]) {
++			err = add_control(spec, ALC_CTL_WIDGET_VOL,
++					  "Speaker Playback Volume",
++					  HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
++							      HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		}
++		if (nid == 0x16) {
++			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
++					  "Speaker Playback Switch",
++					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
++							      HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		} else {
++			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
++					  "Speaker Playback Switch",
++					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++							      HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		}
++	}
++	nid = cfg->hp_pins[0];
++	if (nid) {
++		/* spec->multiout.hp_nid = 2; */
++		if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
++			err = add_control(spec, ALC_CTL_WIDGET_VOL,
++					  "Headphone Playback Volume",
++					  HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
++							      HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		}
++		if (nid == 0x16) {
++			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
++					  "Headphone Playback Switch",
++					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
++							      HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		} else {
++			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
++					  "Headphone Playback Switch",
++					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++							      HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		}
++	}
++	return 0;
++}
++
++#define alc269_auto_create_analog_input_ctls \
++	alc880_auto_create_analog_input_ctls
++
++#ifdef CONFIG_SND_HDA_POWER_SAVE
++#define alc269_loopbacks	alc880_loopbacks
++#endif
++
++/* pcm configuration: identiacal with ALC880 */
++#define alc269_pcm_analog_playback	alc880_pcm_analog_playback
++#define alc269_pcm_analog_capture	alc880_pcm_analog_capture
++#define alc269_pcm_digital_playback	alc880_pcm_digital_playback
++#define alc269_pcm_digital_capture	alc880_pcm_digital_capture
++
++/*
++ * BIOS auto configuration
++ */
++static int alc269_parse_auto_config(struct hda_codec *codec)
++{
++	struct alc_spec *spec = codec->spec;
++	int err;
++	static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
++
++	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
++					   alc269_ignore);
++	if (err < 0)
++		return err;
++
++	err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
++	if (err < 0)
++		return err;
++	err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg);
++	if (err < 0)
++		return err;
++
++	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
++
++	if (spec->autocfg.dig_out_pin)
++		spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
++
++	if (spec->kctl_alloc)
++		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
++
++	spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
++	spec->num_mux_defs = 1;
++	spec->input_mux = &spec->private_imux;
++
++	err = alc_auto_add_mic_boost(codec);
++	if (err < 0)
++		return err;
++
++	return 1;
++}
++
++#define alc269_auto_init_multi_out	alc882_auto_init_multi_out
++#define alc269_auto_init_hp_out		alc882_auto_init_hp_out
++#define alc269_auto_init_analog_input	alc882_auto_init_analog_input
++
++
++/* init callback for auto-configuration model -- overriding the default init */
++static void alc269_auto_init(struct hda_codec *codec)
++{
++	alc269_auto_init_multi_out(codec);
++	alc269_auto_init_hp_out(codec);
++	alc269_auto_init_analog_input(codec);
++}
++
++/*
++ * configuration and preset
++ */
++static const char *alc269_models[ALC269_MODEL_LAST] = {
++	[ALC269_BASIC]		= "basic",
++};
++
++static struct snd_pci_quirk alc269_cfg_tbl[] = {
++	{}
++};
++
++static struct alc_config_preset alc269_presets[] = {
++	[ALC269_BASIC] = {
++		.mixers = { alc269_base_mixer },
++		.init_verbs = { alc269_init_verbs },
++		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
++		.dac_nids = alc269_dac_nids,
++		.hp_nid = 0x03,
++		.num_channel_mode = ARRAY_SIZE(alc269_modes),
++		.channel_mode = alc269_modes,
++		.input_mux = &alc269_capture_source,
++	},
++};
++
++static int patch_alc269(struct hda_codec *codec)
++{
++	struct alc_spec *spec;
++	int board_config;
++	int err;
++
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (spec == NULL)
++		return -ENOMEM;
++
++	codec->spec = spec;
++
++	board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
++						  alc269_models,
++						  alc269_cfg_tbl);
++
++	if (board_config < 0) {
++		printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
++		       "trying auto-probe from BIOS...\n");
++		board_config = ALC269_AUTO;
++	}
++
++	if (board_config == ALC269_AUTO) {
++		/* automatic parse from the BIOS config */
++		err = alc269_parse_auto_config(codec);
++		if (err < 0) {
++			alc_free(codec);
++			return err;
++		} else if (!err) {
++			printk(KERN_INFO
++			       "hda_codec: Cannot set up configuration "
++			       "from BIOS.  Using base mode...\n");
++			board_config = ALC269_BASIC;
++		}
++	}
++
++	if (board_config != ALC269_AUTO)
++		setup_preset(spec, &alc269_presets[board_config]);
++
++	spec->stream_name_analog = "ALC269 Analog";
++	spec->stream_analog_playback = &alc269_pcm_analog_playback;
++	spec->stream_analog_capture = &alc269_pcm_analog_capture;
++
++	spec->stream_name_digital = "ALC269 Digital";
++	spec->stream_digital_playback = &alc269_pcm_digital_playback;
++	spec->stream_digital_capture = &alc269_pcm_digital_capture;
++
++	spec->adc_nids = alc269_adc_nids;
++	spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
++	spec->mixers[spec->num_mixers] = alc269_capture_mixer;
++	spec->num_mixers++;
++
++	codec->patch_ops = alc_patch_ops;
++	if (board_config == ALC269_AUTO)
++		spec->init_hook = alc269_auto_init;
++#ifdef CONFIG_SND_HDA_POWER_SAVE
++	if (!spec->loopback.amplist)
++		spec->loopback.amplist = alc269_loopbacks;
++#endif
++
++	return 0;
++}
++
++/*
+  *  ALC861 channel source setting (2/6 channel selection for 3-stack)
+  */
+ 
+@@ -10213,7 +11442,6 @@ static struct snd_kcontrol_new alc861_capture_mixer[] = {
+ 		.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",
+@@ -10369,22 +11597,23 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
+ 	SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+ 	SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+-	SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
+-	SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
+ 	SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
++	SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
+ 	SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
+ 	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
+ 	/* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
+ 	 *        Any other models that need this preset?
+ 	 */
+ 	/* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
+-	SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+-	SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
++	SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
++	SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
+ 	SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
++	SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
++	SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
++	/* FIXME: the below seems conflict */
++	/* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
+ 	SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
+ 	SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
+-	SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
+-	SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
+ 	{}
+ };
+ 
+@@ -10543,6 +11772,8 @@ static int patch_alc861(struct hda_codec *codec)
+ 	spec->stream_digital_playback = &alc861_pcm_digital_playback;
+ 	spec->stream_digital_capture = &alc861_pcm_digital_capture;
+ 
++	spec->vmaster_nid = 0x03;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC861_AUTO)
+ 		spec->init_hook = alc861_auto_init;
+@@ -10697,7 +11928,6 @@ static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
+ 		.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",
+@@ -11102,21 +12332,20 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
+ };
+ 
+ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
++	SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
++	SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
+ 	SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
+ 	SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
+ 	SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
+ 	SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
+-	SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
+-
++	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
+ 	/*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
+ 	SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
+-	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
+-	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
+-	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
+ 	SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
+ 	SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
++	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
++	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
+ 	SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
+-	SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
+ 	{}
+ };
+ 
+@@ -11520,6 +12749,8 @@ static int patch_alc861vd(struct hda_codec *codec)
+ 	spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
+ 	spec->num_mixers++;
+ 
++	spec->vmaster_nid = 0x02;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 
+ 	if (board_config == ALC861VD_AUTO)
+@@ -11699,18 +12930,6 @@ static struct snd_kcontrol_new alc662_base_mixer[] = {
+ 	HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
+-
+-	/* Capture mixer control */
+-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		.name = "Capture Source",
+-		.count = 1,
+-		.info = alc_mux_enum_info,
+-		.get = alc_mux_enum_get,
+-		.put = alc_mux_enum_put,
+-	},
+ 	{ } /* end */
+ };
+ 
+@@ -11728,17 +12947,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
+ 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		/* .name = "Capture Source", */
+-		.name = "Input Source",
+-		.count = 1,
+-		.info = alc662_mux_enum_info,
+-		.get = alc662_mux_enum_get,
+-		.put = alc662_mux_enum_put,
+-	},
+ 	{ } /* end */
+ };
+ 
+@@ -11762,46 +12970,24 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
+ 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		/* .name = "Capture Source", */
+-		.name = "Input Source",
+-		.count = 1,
+-		.info = alc662_mux_enum_info,
+-		.get = alc662_mux_enum_get,
+-		.put = alc662_mux_enum_put,
+-	},
+ 	{ } /* end */
+ };
+ 
+ static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
+ 	HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ 	HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
+-	HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+-	HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		/* .name = "Capture Source", */
+-		.name = "Input Source",
+-		.count = 1,
+-		.info = alc662_mux_enum_info,
+-		.get = alc662_mux_enum_get,
+-		.put = alc662_mux_enum_put,
+-	},
+ 	{ } /* end */
+ };
+ 
+ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
+-	HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ 
+ 	HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ 	HDA_CODEC_MUTE("LineOut Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+@@ -11816,6 +13002,24 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
++	HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE("LineOut Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
++	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
++	HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
++	HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
++	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
++	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++	{ } /* end */
++};
++
+ static struct snd_kcontrol_new alc662_chmode_mixer[] = {
+ 	{
+ 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+@@ -11901,6 +13105,13 @@ static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
+ 	{}
+ };
+ 
++/* Set Unsolicited Event*/
++static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
++	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
++	{}
++};
++
+ /*
+  * generic initialization of ADC, input mixers and output mixers
+  */
+@@ -11957,14 +13168,13 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = {
+ 		.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 = 1,
+-		.info = alc882_mux_enum_info,
+-		.get = alc882_mux_enum_get,
+-		.put = alc882_mux_enum_put,
++		.info = alc662_mux_enum_info,
++		.get = alc662_mux_enum_get,
++		.put = alc662_mux_enum_put,
+ 	},
+ 	{ } /* end */
+ };
+@@ -12037,6 +13247,40 @@ static void alc662_eeepc_inithook(struct hda_codec *codec)
+ 	alc662_eeepc_mic_automute(codec);
+ }
+ 
++static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
++{
++	unsigned int mute;
++	unsigned int present;
++
++	snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
++	present = snd_hda_codec_read(codec, 0x14, 0,
++				     AC_VERB_GET_PIN_SENSE, 0);
++	present = (present & 0x80000000) != 0;
++	if (present) {
++		/* mute internal speaker */
++		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
++					 HDA_AMP_MUTE, HDA_AMP_MUTE);
++	} else {
++		/* unmute internal speaker if necessary */
++		mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
++		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
++					 HDA_AMP_MUTE, mute);
++	}
++}
++
++/* unsolicited event for HP jack sensing */
++static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec,
++					  unsigned int res)
++{
++	if ((res >> 26) == ALC880_HP_EVENT)
++		alc662_eeepc_ep20_automute(codec);
++}
++
++static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
++{
++	alc662_eeepc_ep20_automute(codec);
++}
++
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ #define alc662_loopbacks	alc880_loopbacks
+ #endif
+@@ -12057,12 +13301,15 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
+ 	[ALC662_3ST_6ch]	= "3stack-6ch",
+ 	[ALC662_5ST_DIG]	= "6stack-dig",
+ 	[ALC662_LENOVO_101E]	= "lenovo-101e",
++	[ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
++	[ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
+ 	[ALC662_AUTO]		= "auto",
+ };
+ 
+ static struct snd_pci_quirk alc662_cfg_tbl[] = {
+-	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
+ 	SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
++	SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
++	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
+ 	{}
+ };
+ 
+@@ -12149,6 +13396,21 @@ static struct alc_config_preset alc662_presets[] = {
+ 		.unsol_event = alc662_eeepc_unsol_event,
+ 		.init_hook = alc662_eeepc_inithook,
+ 	},
++	[ALC662_ASUS_EEEPC_EP20] = {
++		.mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
++			    alc662_chmode_mixer },
++		.init_verbs = { alc662_init_verbs,
++				alc662_eeepc_ep20_sue_init_verbs },
++		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
++		.dac_nids = alc662_dac_nids,
++		.num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
++		.adc_nids = alc662_adc_nids,
++		.num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
++		.channel_mode = alc662_3ST_6ch_modes,
++		.input_mux = &alc662_lenovo_101e_capture_source,
++		.unsol_event = alc662_eeepc_ep20_unsol_event,
++		.init_hook = alc662_eeepc_ep20_inithook,
++	},
+ 
+ };
+ 
+@@ -12308,6 +13570,7 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec)
+ 	struct alc_spec *spec = codec->spec;
+ 	int i;
+ 
++	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
+ 	for (i = 0; i <= HDA_SIDE; i++) {
+ 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
+@@ -12458,6 +13721,8 @@ static int patch_alc662(struct hda_codec *codec)
+ 		spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
+ 	}
+ 
++	spec->vmaster_nid = 0x02;
++
+ 	codec->patch_ops = alc_patch_ops;
+ 	if (board_config == ALC662_AUTO)
+ 		spec->init_hook = alc662_auto_init;
+@@ -12475,7 +13740,9 @@ static int patch_alc662(struct hda_codec *codec)
+ struct hda_codec_preset snd_hda_preset_realtek[] = {
+ 	{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
+ 	{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
++	{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
+ 	{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
++	{ .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
+ 	{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
+ 	  .patch = patch_alc861 },
+ 	{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
+@@ -12490,5 +13757,6 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
+ 	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+ 	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
+ 	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
++	{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
+ 	{} /* terminator */
+ };
+diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
+index 2a4b960..d22f5a6 100644
+--- a/sound/pci/hda/patch_si3054.c
++++ b/sound/pci/hda/patch_si3054.c
+@@ -22,7 +22,6 @@
+  *  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>
+@@ -287,7 +286,6 @@ static int patch_si3054(struct hda_codec *codec)
+ struct hda_codec_preset snd_hda_preset_si3054[] = {
+  	{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
+  	{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
+- 	{ .id = 0x11c11040, .name = "Si3054", .patch = patch_si3054 },
+  	{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
+  	{ .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
+  	{ .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
+diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
+index 0401223..caf48ed 100644
+--- a/sound/pci/hda/patch_sigmatel.c
++++ b/sound/pci/hda/patch_sigmatel.c
+@@ -24,7 +24,6 @@
+  *  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>
+@@ -35,7 +34,8 @@
+ #include "hda_local.h"
+ 
+ #define NUM_CONTROL_ALLOC	32
+-#define STAC_HP_EVENT		0x37
++#define STAC_PWR_EVENT		0x20
++#define STAC_HP_EVENT		0x30
+ 
+ enum {
+ 	STAC_REF,
+@@ -62,6 +62,16 @@ enum {
+ };
+ 
+ enum {
++	STAC_92HD73XX_REF,
++	STAC_92HD73XX_MODELS
++};
++
++enum {
++	STAC_92HD71BXX_REF,
++	STAC_92HD71BXX_MODELS
++};
++
++enum {
+ 	STAC_925x_REF,
+ 	STAC_M2_2,
+ 	STAC_MA6,
+@@ -97,6 +107,7 @@ enum {
+ 	STAC_D965_3ST,
+ 	STAC_D965_5ST,
+ 	STAC_DELL_3ST,
++	STAC_DELL_BIOS,
+ 	STAC_927X_MODELS
+ };
+ 
+@@ -110,11 +121,24 @@ struct sigmatel_spec {
+ 	unsigned int mic_switch: 1;
+ 	unsigned int alt_switch: 1;
+ 	unsigned int hp_detect: 1;
+-	unsigned int gpio_mute: 1;
+ 
+-	unsigned int gpio_mask, gpio_data;
++	/* gpio lines */
++	unsigned int gpio_mask;
++	unsigned int gpio_dir;
++	unsigned int gpio_data;
++	unsigned int gpio_mute;
++
++	/* analog loopback */
++	unsigned char aloopback_mask;
++	unsigned char aloopback_shift;
++
++	/* power management */
++	unsigned int num_pwrs;
++	hda_nid_t *pwr_nids;
+ 
+ 	/* playback */
++	struct hda_input_mux *mono_mux;
++	unsigned int cur_mmux;
+ 	struct hda_multi_out multiout;
+ 	hda_nid_t dac_nids[5];
+ 
+@@ -125,8 +149,10 @@ struct sigmatel_spec {
+ 	unsigned int num_muxes;
+ 	hda_nid_t *dmic_nids;
+ 	unsigned int num_dmics;
+-	hda_nid_t dmux_nid;
++	hda_nid_t *dmux_nids;
++	unsigned int num_dmuxes;
+ 	hda_nid_t dig_in_nid;
++	hda_nid_t mono_nid;
+ 
+ 	/* pin widgets */
+ 	hda_nid_t *pin_nids;
+@@ -140,7 +166,7 @@ struct sigmatel_spec {
+ 
+ 	/* capture source */
+ 	struct hda_input_mux *dinput_mux;
+-	unsigned int cur_dmux;
++	unsigned int cur_dmux[2];
+ 	struct hda_input_mux *input_mux;
+ 	unsigned int cur_mux[3];
+ 
+@@ -157,6 +183,10 @@ struct sigmatel_spec {
+ 	struct snd_kcontrol_new *kctl_alloc;
+ 	struct hda_input_mux private_dimux;
+ 	struct hda_input_mux private_imux;
++	struct hda_input_mux private_mono_mux;
++
++	/* virtual master */
++	unsigned int vmaster_tlv[4];
+ };
+ 
+ static hda_nid_t stac9200_adc_nids[1] = {
+@@ -171,6 +201,58 @@ static hda_nid_t stac9200_dac_nids[1] = {
+         0x02,
+ };
+ 
++static hda_nid_t stac92hd73xx_pwr_nids[8] = {
++	0x0a, 0x0b, 0x0c, 0xd, 0x0e,
++	0x0f, 0x10, 0x11
++};
++
++static hda_nid_t stac92hd73xx_adc_nids[2] = {
++	0x1a, 0x1b
++};
++
++#define STAC92HD73XX_NUM_DMICS	2
++static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
++	0x13, 0x14, 0
++};
++
++#define STAC92HD73_DAC_COUNT 5
++static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
++	0x15, 0x16, 0x17, 0x18, 0x19,
++};
++
++static hda_nid_t stac92hd73xx_mux_nids[4] = {
++	0x28, 0x29, 0x2a, 0x2b,
++};
++
++static hda_nid_t stac92hd73xx_dmux_nids[2] = {
++	0x20, 0x21,
++};
++
++static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
++	0x0a, 0x0d, 0x0f
++};
++
++static hda_nid_t stac92hd71bxx_adc_nids[2] = {
++	0x12, 0x13,
++};
++
++static hda_nid_t stac92hd71bxx_mux_nids[2] = {
++	0x1a, 0x1b
++};
++
++static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
++	0x1c,
++};
++
++static hda_nid_t stac92hd71bxx_dac_nids[2] = {
++	0x10, /*0x11, */
++};
++
++#define STAC92HD71BXX_NUM_DMICS	2
++static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
++	0x18, 0x19, 0
++};
++
+ static hda_nid_t stac925x_adc_nids[1] = {
+         0x03,
+ };
+@@ -188,6 +270,10 @@ static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
+ 	0x15, 0
+ };
+ 
++static hda_nid_t stac925x_dmux_nids[1] = {
++	0x14,
++};
++
+ static hda_nid_t stac922x_adc_nids[2] = {
+         0x06, 0x07,
+ };
+@@ -204,6 +290,15 @@ static hda_nid_t stac927x_mux_nids[3] = {
+         0x15, 0x16, 0x17
+ };
+ 
++static hda_nid_t stac927x_dmux_nids[1] = {
++	0x1b,
++};
++
++#define STAC927X_NUM_DMICS 2
++static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
++	0x13, 0x14, 0
++};
++
+ static hda_nid_t stac9205_adc_nids[2] = {
+         0x12, 0x13
+ };
+@@ -212,6 +307,10 @@ static hda_nid_t stac9205_mux_nids[2] = {
+         0x19, 0x1a
+ };
+ 
++static hda_nid_t stac9205_dmux_nids[1] = {
++	0x1d,
++};
++
+ #define STAC9205_NUM_DMICS	2
+ static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
+         0x17, 0x18, 0
+@@ -232,6 +331,17 @@ static hda_nid_t stac922x_pin_nids[10] = {
+ 	0x0f, 0x10, 0x11, 0x15, 0x1b,
+ };
+ 
++static hda_nid_t stac92hd73xx_pin_nids[12] = {
++	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
++	0x0f, 0x10, 0x11, 0x12, 0x13,
++	0x14, 0x22
++};
++
++static hda_nid_t stac92hd71bxx_pin_nids[10] = {
++	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
++	0x0f, 0x14, 0x18, 0x19, 0x1e,
++};
++
+ static hda_nid_t stac927x_pin_nids[14] = {
+ 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 	0x0f, 0x10, 0x11, 0x12, 0x13,
+@@ -257,8 +367,9 @@ static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
+ {
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ 	struct sigmatel_spec *spec = codec->spec;
++	unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 
+-	ucontrol->value.enumerated.item[0] = spec->cur_dmux;
++	ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
+ 	return 0;
+ }
+ 
+@@ -267,9 +378,10 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ 	struct sigmatel_spec *spec = codec->spec;
++	unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 
+ 	return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
+-				     spec->dmux_nid, &spec->cur_dmux);
++			spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
+ }
+ 
+ static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+@@ -299,15 +411,45 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ 				     spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
+ }
+ 
++static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_info *uinfo)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct sigmatel_spec *spec = codec->spec;
++	return snd_hda_input_mux_info(spec->mono_mux, uinfo);
++}
++
++static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct sigmatel_spec *spec = codec->spec;
++
++	ucontrol->value.enumerated.item[0] = spec->cur_mmux;
++	return 0;
++}
++
++static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
++	struct snd_ctl_elem_value *ucontrol)
++{
++	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	struct sigmatel_spec *spec = codec->spec;
++
++	return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
++				     spec->mono_nid, &spec->cur_mmux);
++}
++
+ #define stac92xx_aloopback_info snd_ctl_boolean_mono_info
+ 
+ static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
+ 	struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 	struct sigmatel_spec *spec = codec->spec;
+ 
+-	ucontrol->value.integer.value[0] = spec->aloopback;
++	ucontrol->value.integer.value[0] = !!(spec->aloopback &
++					      (spec->aloopback_mask << idx));
+ 	return 0;
+ }
+ 
+@@ -316,23 +458,33 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ 	struct sigmatel_spec *spec = codec->spec;
++	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 	unsigned int dac_mode;
++	unsigned int val, idx_val;
+ 
+-	if (spec->aloopback == ucontrol->value.integer.value[0])
++	idx_val = spec->aloopback_mask << idx;
++	if (ucontrol->value.integer.value[0])
++		val = spec->aloopback | idx_val;
++	else
++		val = spec->aloopback & ~idx_val;
++	if (spec->aloopback == val)
+ 		return 0;
+ 
+-	spec->aloopback = ucontrol->value.integer.value[0];
+-
++	spec->aloopback = val;
+ 
++	/* Only return the bits defined by the shift value of the
++	 * first two bytes of the mask
++	 */
+ 	dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
+-		kcontrol->private_value & 0xFFFF, 0x0);
++				      kcontrol->private_value & 0xFFFF, 0x0);
++	dac_mode >>= spec->aloopback_shift;
+ 
+-	if (spec->aloopback) {
++	if (spec->aloopback & idx_val) {
+ 		snd_hda_power_up(codec);
+-		dac_mode |= 0x40;
++		dac_mode |= idx_val;
+ 	} else {
+ 		snd_hda_power_down(codec);
+-		dac_mode &= ~0x40;
++		dac_mode &= ~idx_val;
+ 	}
+ 
+ 	snd_hda_codec_write_cache(codec, codec->afg, 0,
+@@ -354,6 +506,107 @@ static struct hda_verb stac9200_eapd_init[] = {
+ 	{}
+ };
+ 
++static struct hda_verb stac92hd73xx_6ch_core_init[] = {
++	/* set master volume and direct control */
++	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++	/* setup audio connections */
++	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
++	/* setup adcs to point to mixer */
++	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
++	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
++	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	/* setup import muxs */
++	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{}
++};
++
++static struct hda_verb stac92hd73xx_8ch_core_init[] = {
++	/* set master volume and direct control */
++	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++	/* setup audio connections */
++	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
++	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
++	/* connect hp ports to dac3 */
++	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
++	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
++	/* setup adcs to point to mixer */
++	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
++	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
++	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	/* setup import muxs */
++	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
++	{}
++};
++
++static struct hda_verb stac92hd73xx_10ch_core_init[] = {
++	/* set master volume and direct control */
++	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++	/* setup audio connections */
++	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
++	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
++	{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
++	/* dac3 is connected to import3 mux */
++	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
++	/* connect hp ports to dac4 */
++	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
++	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
++	/* setup adcs to point to mixer */
++	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
++	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
++	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++	/* setup import muxs */
++	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
++	{}
++};
++
++static struct hda_verb stac92hd71bxx_core_init[] = {
++	/* set master volume and direct control */
++	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++	/* connect headphone jack to dac1 */
++	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
++	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
++	/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
++	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++};
++
++static struct hda_verb stac92hd71bxx_analog_core_init[] = {
++	/* set master volume and direct control */
++	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++	/* connect headphone jack to dac1 */
++	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
++	/* connect ports 0d and 0f to audio mixer */
++	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
++	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
++	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
++	/* unmute dac0 input in audio mixer */
++	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
++	/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
++	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{}
++};
++
+ static struct hda_verb stac925x_core_init[] = {
+ 	/* set dac0mux for dac converter */
+ 	{ 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
+@@ -388,6 +641,16 @@ static struct hda_verb stac9205_core_init[] = {
+ 	{}
+ };
+ 
++#define STAC_MONO_MUX \
++	{ \
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
++		.name = "Mono Mux", \
++		.count = 1, \
++		.info = stac92xx_mono_mux_enum_info, \
++		.get = stac92xx_mono_mux_enum_get, \
++		.put = stac92xx_mono_mux_enum_put, \
++	}
++
+ #define STAC_INPUT_SOURCE(cnt) \
+ 	{ \
+ 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+@@ -398,11 +661,11 @@ static struct hda_verb stac9205_core_init[] = {
+ 		.put = stac92xx_mux_enum_put, \
+ 	}
+ 
+-#define STAC_ANALOG_LOOPBACK(verb_read,verb_write) \
++#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
+ 	{ \
+ 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ 		.name  = "Analog Loopback", \
+-		.count = 1, \
++		.count = cnt, \
+ 		.info  = stac92xx_aloopback_info, \
+ 		.get   = stac92xx_aloopback_get, \
+ 		.put   = stac92xx_aloopback_put, \
+@@ -419,6 +682,114 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
++	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
++	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
++	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
++	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
++	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
++	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
++	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
++	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
++	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
++	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
++	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
++	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
++	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
++
++	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
++	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
++	STAC_INPUT_SOURCE(2),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
++	HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
++	{ } /* end */
++};
++
++static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
++	STAC_INPUT_SOURCE(2),
++	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
++
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
++	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
++	{ } /* end */
++};
++
+ static struct snd_kcontrol_new stac925x_mixer[] = {
+ 	STAC_INPUT_SOURCE(1),
+ 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
+@@ -428,16 +799,8 @@ static struct snd_kcontrol_new stac925x_mixer[] = {
+ };
+ 
+ static struct snd_kcontrol_new stac9205_mixer[] = {
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		.name = "Digital Input Source",
+-		.count = 1,
+-		.info = stac92xx_dmux_enum_info,
+-		.get = stac92xx_dmux_enum_get,
+-		.put = stac92xx_dmux_enum_put,
+-	},
+ 	STAC_INPUT_SOURCE(2),
+-	STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0),
++	STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+ 
+ 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
+ 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
+@@ -466,7 +829,7 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
+ 
+ static struct snd_kcontrol_new stac927x_mixer[] = {
+ 	STAC_INPUT_SOURCE(3),
+-	STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB),
++	STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+ 
+ 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
+ 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
+@@ -482,6 +845,44 @@ static struct snd_kcontrol_new stac927x_mixer[] = {
+ 	{ } /* end */
+ };
+ 
++static struct snd_kcontrol_new stac_dmux_mixer = {
++	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name = "Digital Input Source",
++	/* count set later */
++	.info = stac92xx_dmux_enum_info,
++	.get = stac92xx_dmux_enum_get,
++	.put = stac92xx_dmux_enum_put,
++};
++
++static const char *slave_vols[] = {
++	"Front Playback Volume",
++	"Surround Playback Volume",
++	"Center Playback Volume",
++	"LFE Playback Volume",
++	"Side Playback Volume",
++	"Headphone Playback Volume",
++	"Headphone Playback Volume",
++	"Speaker Playback Volume",
++	"External Speaker Playback Volume",
++	"Speaker2 Playback Volume",
++	NULL
++};
++
++static const char *slave_sws[] = {
++	"Front Playback Switch",
++	"Surround Playback Switch",
++	"Center Playback Switch",
++	"LFE Playback Switch",
++	"Side Playback Switch",
++	"Headphone Playback Switch",
++	"Headphone Playback Switch",
++	"Speaker Playback Switch",
++	"External Speaker Playback Switch",
++	"Speaker2 Playback Switch",
++	"IEC958 Playback Switch",
++	NULL
++};
++
+ static int stac92xx_build_controls(struct hda_codec *codec)
+ {
+ 	struct sigmatel_spec *spec = codec->spec;
+@@ -497,6 +898,13 @@ static int stac92xx_build_controls(struct hda_codec *codec)
+ 		if (err < 0)
+ 			return err;
+ 	}
++	if (spec->num_dmuxes > 0) {
++		stac_dmux_mixer.count = spec->num_dmuxes;
++		err = snd_ctl_add(codec->bus->card,
++				  snd_ctl_new1(&stac_dmux_mixer, codec));
++		if (err < 0)
++			return err;
++	}
+ 
+ 	if (spec->multiout.dig_out_nid) {
+ 		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+@@ -508,6 +916,23 @@ static int stac92xx_build_controls(struct hda_codec *codec)
+ 		if (err < 0)
+ 			return err;
+ 	}
++
++	/* if we have no master control, let's create it */
++	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
++		snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
++					HDA_OUTPUT, spec->vmaster_tlv);
++		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
++					  spec->vmaster_tlv, slave_vols);
++		if (err < 0)
++			return err;
++	}
++	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
++		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
++					  NULL, slave_sws);
++		if (err < 0)
++			return err;
++	}
++
+ 	return 0;	
+ }
+ 
+@@ -733,7 +1158,7 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
+ 
+ static unsigned int ref925x_pin_configs[8] = {
+ 	0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+-	0x90a70320, 0x02214210, 0x400003f1, 0x9033032e,
++	0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
+ };
+ 
+ static unsigned int stac925x_MA6_pin_configs[8] = {
+@@ -777,6 +1202,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = {
+ 	{} /* terminator */
+ };
+ 
++static unsigned int ref92hd73xx_pin_configs[12] = {
++	0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
++	0x0181302e, 0x01014010, 0x01014020, 0x01014030,
++	0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
++};
++
++static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
++	[STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
++};
++
++static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
++	[STAC_92HD73XX_REF] = "ref",
++};
++
++static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
++	/* SigmaTel reference board */
++	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
++		      "DFI LanParty", STAC_92HD73XX_REF),
++	{} /* terminator */
++};
++
++static unsigned int ref92hd71bxx_pin_configs[10] = {
++	0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
++	0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
++	0x90a000f0, 0x01452050,
++};
++
++static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
++	[STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
++};
++
++static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
++	[STAC_92HD71BXX_REF] = "ref",
++};
++
++static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
++	/* SigmaTel reference board */
++	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
++		      "DFI LanParty", STAC_92HD71BXX_REF),
++	{} /* terminator */
++};
++
+ static unsigned int ref922x_pin_configs[10] = {
+ 	0x01014010, 0x01016011, 0x01012012, 0x0221401f,
+ 	0x01813122, 0x01011014, 0x01441030, 0x01c41030,
+@@ -823,8 +1290,8 @@ static unsigned int dell_922x_m81_pin_configs[10] = {
+     102801D7 (Dell XPS M1210)
+ */
+ static unsigned int dell_922x_m82_pin_configs[10] = {
+-	0x0221121f, 0x408103ff, 0x02111212, 0x90100310, 
+-	0x408003f1, 0x02111211, 0x03451340, 0x40c003f2, 
++	0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, 
++	0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, 
+ 	0x508003f3, 0x405003f4, 
+ };
+ 
+@@ -1022,22 +1489,24 @@ static unsigned int d965_5st_pin_configs[14] = {
+ static unsigned int dell_3st_pin_configs[14] = {
+ 	0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
+ 	0x01111212, 0x01116211, 0x01813050, 0x01112214,
+-	0x403003fa, 0x40000100, 0x40000100, 0x404003fb,
++	0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
+ 	0x40c003fc, 0x40000100
+ };
+ 
+ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+-	[STAC_D965_REF] = ref927x_pin_configs,
+-	[STAC_D965_3ST] = d965_3st_pin_configs,
+-	[STAC_D965_5ST] = d965_5st_pin_configs,
+-	[STAC_DELL_3ST] = dell_3st_pin_configs,
++	[STAC_D965_REF]  = ref927x_pin_configs,
++	[STAC_D965_3ST]  = d965_3st_pin_configs,
++	[STAC_D965_5ST]  = d965_5st_pin_configs,
++	[STAC_DELL_3ST]  = dell_3st_pin_configs,
++	[STAC_DELL_BIOS] = NULL,
+ };
+ 
+ static const char *stac927x_models[STAC_927X_MODELS] = {
+-	[STAC_D965_REF]	= "ref",
+-	[STAC_D965_3ST]	= "3stack",
+-	[STAC_D965_5ST] = "5stack",
+-	[STAC_DELL_3ST]	= "dell-3stack",
++	[STAC_D965_REF]		= "ref",
++	[STAC_D965_3ST]		= "3stack",
++	[STAC_D965_5ST]		= "5stack",
++	[STAC_DELL_3ST]		= "dell-3stack",
++	[STAC_DELL_BIOS]	= "dell-bios",
+ };
+ 
+ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+@@ -1064,13 +1533,21 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
+-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f3, "Dell Inspiron 1420", STAC_D965_3ST),
+ 	/* Dell 3 stack systems */
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01ed, "Dell     ", STAC_DELL_3ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f4, "Dell     ", STAC_DELL_3ST),
++	/* Dell 3 stack systems with verb table in BIOS */
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0227, "Dell Vostro 1400  ", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022f, "Dell     ", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022e, "Dell     ", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0242, "Dell     ", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0243, "Dell     ", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x02ff, "Dell     ", STAC_DELL_BIOS),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
+ 	/* 965 based 5 stack systems */
+-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0209, "Dell XPS 1330", STAC_D965_5ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
+@@ -1085,7 +1562,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
+ 
+ static unsigned int ref9205_pin_configs[12] = {
+ 	0x40000100, 0x40000100, 0x01016011, 0x01014010,
+-	0x01813122, 0x01a19021, 0x40000100, 0x40000100,
++	0x01813122, 0x01a19021, 0x01019020, 0x40000100,
+ 	0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
+ };
+ 
+@@ -1097,6 +1574,7 @@ static unsigned int ref9205_pin_configs[12] = {
+     102801FD
+     10280204
+     1028021F
++    10280228 (Dell Vostro 1500)
+ */
+ static unsigned int dell_9205_m42_pin_configs[12] = {
+ 	0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
+@@ -1180,6 +1658,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
+ 		      "unknown Dell", STAC_9205_DELL_M42),
+ 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
+ 		      "Dell Inspiron", STAC_9205_DELL_M44),
++	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
++		      "Dell Vostro 1500", STAC_9205_DELL_M42),
+ 	{} /* terminator */
+ };
+ 
+@@ -1245,22 +1725,6 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
+ 					spec->pin_configs[i]);
+ }
+ 
+-static void stac92xx_enable_gpio_mask(struct hda_codec *codec)
+-{
+-	struct sigmatel_spec *spec = codec->spec;
+-	/* Configure GPIOx as output */
+-	snd_hda_codec_write_cache(codec, codec->afg, 0,
+-				  AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
+-	/* Configure GPIOx as CMOS */
+-	snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000);
+-	/* Assert GPIOx */
+-	snd_hda_codec_write_cache(codec, codec->afg, 0,
+-				  AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+-	/* Enable GPIOx */
+-	snd_hda_codec_write_cache(codec, codec->afg, 0,
+-				  AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
+-}
+-
+ /*
+  * Analog playback callbacks
+  */
+@@ -1479,7 +1943,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
+ 	struct sigmatel_spec *spec = codec->spec;
+         hda_nid_t nid = kcontrol->private_value >> 8;
+ 	int io_idx = kcontrol-> private_value & 0xff;
+-        unsigned short val = ucontrol->value.integer.value[0];
++	unsigned short val = !!ucontrol->value.integer.value[0];
+ 
+ 	spec->io_switch[io_idx] = val;
+ 
+@@ -1491,6 +1955,13 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
+ 			pinctl |= stac92xx_get_vref(codec, nid);
+ 		stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ 	}
++
++	/* check the auto-mute again: we need to mute/unmute the speaker
++	 * appropriately according to the pin direction
++	 */
++	if (spec->hp_detect)
++		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
++
+         return 1;
+ }
+ 
+@@ -1512,11 +1983,12 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
+ 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ 	struct sigmatel_spec *spec = codec->spec;
+ 	hda_nid_t nid = kcontrol->private_value & 0xff;
++	unsigned int val = !!ucontrol->value.integer.value[0];
+ 
+-	if (spec->clfe_swap == ucontrol->value.integer.value[0])
++	if (spec->clfe_swap == val)
+ 		return 0;
+ 
+-	spec->clfe_swap = ucontrol->value.integer.value[0];
++	spec->clfe_swap = val;
+ 
+ 	snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ 		spec->clfe_swap ? 0x4 : 0x0);
+@@ -1547,6 +2019,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
+ enum {
+ 	STAC_CTL_WIDGET_VOL,
+ 	STAC_CTL_WIDGET_MUTE,
++	STAC_CTL_WIDGET_MONO_MUX,
+ 	STAC_CTL_WIDGET_IO_SWITCH,
+ 	STAC_CTL_WIDGET_CLFE_SWITCH
+ };
+@@ -1554,6 +2027,7 @@ enum {
+ static struct snd_kcontrol_new stac92xx_control_templates[] = {
+ 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
++	STAC_MONO_MUX,
+ 	STAC_CODEC_IO_SWITCH(NULL, 0),
+ 	STAC_CODEC_CLFE_SWITCH(NULL, 0),
+ };
+@@ -1598,6 +2072,7 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
+ 	for (i = 0; i < codec->num_nodes; i++) {
+ 		wcaps = codec->wcaps[i];
+ 		wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
++
+ 		if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
+ 			num_dacs++;
+ 	}
+@@ -1685,7 +2160,6 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
+ 			wcaps = snd_hda_param_read(codec, conn[j],
+ 						   AC_PAR_AUDIO_WIDGET_CAP);
+ 			wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+-
+ 			if (wtype != AC_WID_AUD_OUT ||
+ 			    (wcaps & AC_WCAP_DIGITAL))
+ 				continue;
+@@ -1759,7 +2233,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
+ 	int i, err;
+ 
+ 	struct sigmatel_spec *spec = codec->spec;
+-	unsigned int wid_caps;
++	unsigned int wid_caps, pincap;
+ 
+ 
+ 	for (i = 0; i < cfg->line_outs; i++) {
+@@ -1795,13 +2269,39 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
+ 		}
+ 	}
+ 
+-	if (spec->line_switch)
+-		if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0)
+-			return err;
++	if (spec->line_switch) {
++		nid = cfg->input_pins[AUTO_PIN_LINE];
++		pincap = snd_hda_param_read(codec, nid,
++						AC_PAR_PIN_CAP);
++		if (pincap & AC_PINCAP_OUT) {
++			err = stac92xx_add_control(spec,
++				STAC_CTL_WIDGET_IO_SWITCH,
++				"Line In as Output Switch", nid << 8);
++			if (err < 0)
++				return err;
++		}
++	}
+ 
+-	if (spec->mic_switch)
+-		if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0)
+-			return err;
++	if (spec->mic_switch) {
++		unsigned int def_conf;
++		nid = cfg->input_pins[AUTO_PIN_MIC];
++		def_conf = snd_hda_codec_read(codec, nid, 0,
++						AC_VERB_GET_CONFIG_DEFAULT, 0);
++
++		/* some laptops have an internal analog microphone
++		 * which can't be used as a output */
++		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
++			pincap = snd_hda_param_read(codec, nid,
++							AC_PAR_PIN_CAP);
++			if (pincap & AC_PINCAP_OUT) {
++				err = stac92xx_add_control(spec,
++					STAC_CTL_WIDGET_IO_SWITCH,
++					"Mic as Output Switch", (nid << 8) | 1);
++				if (err < 0)
++					return err;
++			}
++		}
++	}
+ 
+ 	return 0;
+ }
+@@ -1891,6 +2391,37 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
+ 	return 0;
+ }
+ 
++/* labels for mono mux outputs */
++static const char *stac92xx_mono_labels[3] = {
++	"DAC0", "DAC1", "Mixer"
++};
++
++/* create mono mux for mono out on capable codecs */
++static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
++{
++	struct sigmatel_spec *spec = codec->spec;
++	struct hda_input_mux *mono_mux = &spec->private_mono_mux;
++	int i, num_cons;
++	hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
++
++	num_cons = snd_hda_get_connections(codec,
++				spec->mono_nid,
++				con_lst,
++				HDA_MAX_NUM_INPUTS);
++	if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
++		return -EINVAL;
++
++	for (i = 0; i < num_cons; i++) {
++		mono_mux->items[mono_mux->num_items].label =
++					stac92xx_mono_labels[i];
++		mono_mux->items[mono_mux->num_items].index = i;
++		mono_mux->num_items++;
++	}
++
++	return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
++				"Mono Mux", spec->mono_nid);
++}
++
+ /* labels for dmic mux inputs */
+ static const char *stac92xx_dmic_labels[5] = {
+ 	"Analog Inputs", "Digital Mic 1", "Digital Mic 2",
+@@ -1904,15 +2435,18 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
+ 	struct sigmatel_spec *spec = codec->spec;
+ 	struct hda_input_mux *dimux = &spec->private_dimux;
+ 	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+-	int i, j;
++	int err, i, j;
++	char name[32];
+ 
+ 	dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
+ 	dimux->items[dimux->num_items].index = 0;
+ 	dimux->num_items++;
+ 
+ 	for (i = 0; i < spec->num_dmics; i++) {
++		hda_nid_t nid;
+ 		int index;
+ 		int num_cons;
++		unsigned int wcaps;
+ 		unsigned int def_conf;
+ 
+ 		def_conf = snd_hda_codec_read(codec,
+@@ -1923,17 +2457,32 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
+ 		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ 			continue;
+ 
++		nid = spec->dmic_nids[i];
+ 		num_cons = snd_hda_get_connections(codec,
+-				spec->dmux_nid,
++				spec->dmux_nids[0],
+ 				con_lst,
+ 				HDA_MAX_NUM_INPUTS);
+ 		for (j = 0; j < num_cons; j++)
+-			if (con_lst[j] == spec->dmic_nids[i]) {
++			if (con_lst[j] == nid) {
+ 				index = j;
+ 				goto found;
+ 			}
+ 		continue;
+ found:
++		wcaps = get_wcaps(codec, nid);
++
++		if (wcaps & AC_WCAP_OUT_AMP) {
++			sprintf(name, "%s Capture Volume",
++				stac92xx_dmic_labels[dimux->num_items]);
++
++			err = stac92xx_add_control(spec,
++				STAC_CTL_WIDGET_VOL,
++				name,
++				HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		}
++
+ 		dimux->items[dimux->num_items].label =
+ 			stac92xx_dmic_labels[dimux->num_items];
+ 		dimux->items[dimux->num_items].index = index;
+@@ -2026,6 +2575,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
+ {
+ 	struct sigmatel_spec *spec = codec->spec;
+ 	int err;
++	int hp_speaker_swap = 0;
+ 
+ 	if ((err = snd_hda_parse_pin_def_config(codec,
+ 						&spec->autocfg,
+@@ -2034,6 +2584,68 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
+ 	if (! spec->autocfg.line_outs)
+ 		return 0; /* can't find valid pin config */
+ 
++	/* If we have no real line-out pin and multiple hp-outs, HPs should
++	 * be set up as multi-channel outputs.
++	 */
++	if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
++	    spec->autocfg.hp_outs > 1) {
++		/* Copy hp_outs to line_outs, backup line_outs in
++		 * speaker_outs so that the following routines can handle
++		 * HP pins as primary outputs.
++		 */
++		memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
++		       sizeof(spec->autocfg.line_out_pins));
++		spec->autocfg.speaker_outs = spec->autocfg.line_outs;
++		memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
++		       sizeof(spec->autocfg.hp_pins));
++		spec->autocfg.line_outs = spec->autocfg.hp_outs;
++		hp_speaker_swap = 1;
++	}
++	if (spec->autocfg.mono_out_pin) {
++		int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
++				& AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
++		u32 caps = query_amp_caps(codec,
++				spec->autocfg.mono_out_pin, dir);
++		hda_nid_t conn_list[1];
++
++		/* get the mixer node and then the mono mux if it exists */
++		if (snd_hda_get_connections(codec,
++				spec->autocfg.mono_out_pin, conn_list, 1) &&
++				snd_hda_get_connections(codec, conn_list[0],
++				conn_list, 1)) {
++
++				int wcaps = get_wcaps(codec, conn_list[0]);
++				int wid_type = (wcaps & AC_WCAP_TYPE)
++					>> AC_WCAP_TYPE_SHIFT;
++				/* LR swap check, some stac925x have a mux that
++ 				 * changes the DACs output path instead of the
++ 				 * mono-mux path.
++ 				 */
++				if (wid_type == AC_WID_AUD_SEL &&
++						!(wcaps & AC_WCAP_LR_SWAP))
++					spec->mono_nid = conn_list[0];
++		}
++		/* all mono outs have a least a mute/unmute switch */
++		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
++			"Mono Playback Switch",
++			HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
++					1, 0, dir));
++		if (err < 0)
++			return err;
++		/* check to see if there is volume support for the amp */
++		if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
++			err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
++				"Mono Playback Volume",
++				HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
++					1, 0, dir));
++			if (err < 0)
++				return err;
++		}
++
++		stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
++					 AC_PINCTL_OUT_EN);
++	}
++
+ 	if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
+ 		return err;
+ 	if (spec->multiout.num_dacs == 0)
+@@ -2045,6 +2657,19 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
+ 	if (err < 0)
+ 		return err;
+ 
++	if (hp_speaker_swap == 1) {
++		/* Restore the hp_outs and line_outs */
++		memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
++		       sizeof(spec->autocfg.line_out_pins));
++		spec->autocfg.hp_outs = spec->autocfg.line_outs;
++		memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
++		       sizeof(spec->autocfg.speaker_pins));
++		spec->autocfg.line_outs = spec->autocfg.speaker_outs;
++		memset(spec->autocfg.speaker_pins, 0,
++		       sizeof(spec->autocfg.speaker_pins));
++		spec->autocfg.speaker_outs = 0;
++	}
++
+ 	err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
+ 
+ 	if (err < 0)
+@@ -2055,6 +2680,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
+ 	if (err < 0)
+ 		return err;
+ 
++	if (spec->mono_nid > 0) {
++		err = stac92xx_auto_create_mono_output_ctls(codec);
++		if (err < 0)
++			return err;
++	}
++
+ 	if (spec->num_dmics > 0)
+ 		if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
+ 						&spec->autocfg)) < 0)
+@@ -2073,7 +2704,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
+ 		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ 
+ 	spec->input_mux = &spec->private_imux;
+-	spec->dinput_mux = &spec->private_dimux;
++	if (!spec->dinput_mux)
++		spec->dinput_mux = &spec->private_dimux;
++	spec->mono_mux = &spec->private_mono_mux;
+ 
+ 	return 1;
+ }
+@@ -2183,38 +2816,35 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
+  * funky external mute control using GPIO pins.
+  */
+ 
+-static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted)
++static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
++			  unsigned int dir_mask, unsigned int data)
+ {
+ 	unsigned int gpiostate, gpiomask, gpiodir;
+ 
+ 	gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ 				       AC_VERB_GET_GPIO_DATA, 0);
+-
+-	if (!muted)
+-		gpiostate |= (1 << pin);
+-	else
+-		gpiostate &= ~(1 << pin);
++	gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
+ 
+ 	gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ 				      AC_VERB_GET_GPIO_MASK, 0);
+-	gpiomask |= (1 << pin);
++	gpiomask |= mask;
+ 
+ 	gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ 				     AC_VERB_GET_GPIO_DIRECTION, 0);
+-	gpiodir |= (1 << pin);
++	gpiodir |= dir_mask;
+ 
+-	/* AppleHDA seems to do this -- WTF is this verb?? */
++	/* Configure GPIOx as CMOS */
+ 	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
+ 
+ 	snd_hda_codec_write(codec, codec->afg, 0,
+ 			    AC_VERB_SET_GPIO_MASK, gpiomask);
+-	snd_hda_codec_write(codec, codec->afg, 0,
+-			    AC_VERB_SET_GPIO_DIRECTION, gpiodir);
++	snd_hda_codec_read(codec, codec->afg, 0,
++			   AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
+ 
+ 	msleep(1);
+ 
+-	snd_hda_codec_write(codec, codec->afg, 0,
+-			    AC_VERB_SET_GPIO_DATA, gpiostate);
++	snd_hda_codec_read(codec, codec->afg, 0,
++			   AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
+ }
+ 
+ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+@@ -2226,6 +2856,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+ 					  (AC_USRSP_EN | event));
+ }
+ 
++static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
++{
++	int i;
++	for (i = 0; i < cfg->hp_outs; i++)
++		if (cfg->hp_pins[i] == nid)
++			return 1; /* nid is a HP-Out */
++
++	return 0; /* nid is not a HP-Out */
++};
++
+ static int stac92xx_init(struct hda_codec *codec)
+ {
+ 	struct sigmatel_spec *spec = codec->spec;
+@@ -2261,10 +2901,23 @@ static int stac92xx_init(struct hda_codec *codec)
+ 			stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ 		}
+ 	}
+-	if (spec->num_dmics > 0)
+-		for (i = 0; i < spec->num_dmics; i++)
+-			stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
+-						 AC_PINCTL_IN_EN);
++	for (i = 0; i < spec->num_dmics; i++)
++		stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
++					AC_PINCTL_IN_EN);
++	for (i = 0; i < spec->num_pwrs; i++)  {
++		int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
++					? STAC_HP_EVENT : STAC_PWR_EVENT;
++		int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
++					0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
++		/* outputs are only ports capable of power management
++		 * any attempts on powering down a input port cause the
++		 * referenced VREF to act quirky.
++		 */
++		if (pinctl & AC_PINCTL_IN_EN)
++			continue;
++		enable_pin_detect(codec, spec->pwr_nids[i], event | i);
++		codec->patch_ops.unsol_event(codec, (event | i) << 26);
++	}
+ 
+ 	if (cfg->dig_out_pin)
+ 		stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
+@@ -2273,10 +2926,8 @@ static int stac92xx_init(struct hda_codec *codec)
+ 		stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
+ 					 AC_PINCTL_IN_EN);
+ 
+-	if (spec->gpio_mute) {
+-		stac922x_gpio_mute(codec, 0, 0);
+-		stac922x_gpio_mute(codec, 1, 0);
+-	}
++	stac_gpio_set(codec, spec->gpio_mask,
++					spec->gpio_dir, spec->gpio_data);
+ 
+ 	return 0;
+ }
+@@ -2342,13 +2993,20 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
+ 			pin_ctl & ~flag);
+ }
+ 
+-static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
++static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+ {
+ 	if (!nid)
+ 		return 0;
+ 	if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
+-	    & (1 << 31))
+-		return 1;
++	    & (1 << 31)) {
++		unsigned int pinctl;
++		pinctl = snd_hda_codec_read(codec, nid, 0,
++					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
++		if (pinctl & AC_PINCTL_IN_EN)
++			return 0; /* mic- or line-input */
++		else
++			return 1; /* HP-output */
++	}
+ 	return 0;
+ }
+ 
+@@ -2359,10 +3017,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+ 	int i, presence;
+ 
+ 	presence = 0;
++	if (spec->gpio_mute)
++		presence = !(snd_hda_codec_read(codec, codec->afg, 0,
++			AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
++
+ 	for (i = 0; i < cfg->hp_outs; i++) {
+-		presence = get_pin_presence(codec, cfg->hp_pins[i]);
+ 		if (presence)
+ 			break;
++		presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
+ 	}
+ 
+ 	if (presence) {
+@@ -2384,12 +3046,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+ 	}
+ } 
+ 
++static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
++{
++	struct sigmatel_spec *spec = codec->spec;
++	hda_nid_t nid = spec->pwr_nids[idx];
++	int presence, val;
++	val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
++							& 0x000000ff;
++	presence = get_hp_pin_presence(codec, nid);
++	idx = 1 << idx;
++
++	if (presence)
++		val &= ~idx;
++	else
++		val |= idx;
++
++	/* power down unused output ports */
++	snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
++};
++
+ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
+ {
+-	switch (res >> 26) {
++	struct sigmatel_spec *spec = codec->spec;
++	int idx = res >> 26 & 0x0f;
++
++	switch ((res >> 26) & 0x30) {
+ 	case STAC_HP_EVENT:
+ 		stac92xx_hp_detect(codec, res);
+-		break;
++		/* fallthru */
++	case STAC_PWR_EVENT:
++		if (spec->num_pwrs > 0)
++			stac92xx_pin_sense(codec, idx);
+ 	}
+ }
+ 
+@@ -2400,10 +3087,8 @@ static int stac92xx_resume(struct hda_codec *codec)
+ 
+ 	stac92xx_set_config_regs(codec);
+ 	snd_hda_sequence_write(codec, spec->init);
+-	if (spec->gpio_mute) {
+-		stac922x_gpio_mute(codec, 0, 0);
+-		stac922x_gpio_mute(codec, 1, 0);
+-	}
++	stac_gpio_set(codec, spec->gpio_mask,
++		spec->gpio_dir, spec->gpio_data);
+ 	snd_hda_codec_resume_amp(codec);
+ 	snd_hda_codec_resume_cache(codec);
+ 	/* invoke unsolicited event to reset the HP state */
+@@ -2460,6 +3145,7 @@ static int patch_stac9200(struct hda_codec *codec)
+ 	spec->num_muxes = 1;
+ 	spec->num_dmics = 0;
+ 	spec->num_adcs = 1;
++	spec->num_pwrs = 0;
+ 
+ 	if (spec->board_config == STAC_9200_GATEWAY)
+ 		spec->init = stac9200_eapd_init;
+@@ -2515,6 +3201,7 @@ static int patch_stac925x(struct hda_codec *codec)
+ 	spec->mux_nids = stac925x_mux_nids;
+ 	spec->num_muxes = 1;
+ 	spec->num_adcs = 1;
++	spec->num_pwrs = 0;
+ 	switch (codec->vendor_id) {
+ 	case 0x83847632: /* STAC9202  */
+ 	case 0x83847633: /* STAC9202D */
+@@ -2522,6 +3209,8 @@ static int patch_stac925x(struct hda_codec *codec)
+ 	case 0x83847637: /* STAC9251D */
+ 		spec->num_dmics = STAC925X_NUM_DMICS;
+ 		spec->dmic_nids = stac925x_dmic_nids;
++		spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
++		spec->dmux_nids = stac925x_dmux_nids;
+ 		break;
+ 	default:
+ 		spec->num_dmics = 0;
+@@ -2551,6 +3240,204 @@ static int patch_stac925x(struct hda_codec *codec)
+ 	return 0;
+ }
+ 
++static struct hda_input_mux stac92hd73xx_dmux = {
++	.num_items = 4,
++	.items = {
++		{ "Analog Inputs", 0x0b },
++		{ "CD", 0x08 },
++		{ "Digital Mic 1", 0x09 },
++		{ "Digital Mic 2", 0x0a },
++	}
++};
++
++static int patch_stac92hd73xx(struct hda_codec *codec)
++{
++	struct sigmatel_spec *spec;
++	hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
++	int err = 0;
++
++	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (spec == NULL)
++		return -ENOMEM;
++
++	codec->spec = spec;
++	spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
++	spec->pin_nids = stac92hd73xx_pin_nids;
++	spec->board_config = snd_hda_check_board_config(codec,
++							STAC_92HD73XX_MODELS,
++							stac92hd73xx_models,
++							stac92hd73xx_cfg_tbl);
++again:
++	if (spec->board_config < 0) {
++		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
++			" STAC92HD73XX, using BIOS defaults\n");
++		err = stac92xx_save_bios_config_regs(codec);
++		if (err < 0) {
++			stac92xx_free(codec);
++			return err;
++		}
++		spec->pin_configs = spec->bios_pin_configs;
++	} else {
++		spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
++		stac92xx_set_config_regs(codec);
++	}
++
++	spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
++			conn, STAC92HD73_DAC_COUNT + 2) - 1;
++
++	if (spec->multiout.num_dacs < 0) {
++		printk(KERN_WARNING "hda_codec: Could not determine "
++		       "number of channels defaulting to DAC count\n");
++		spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
++	}
++
++	switch (spec->multiout.num_dacs) {
++	case 0x3: /* 6 Channel */
++		spec->mixer = stac92hd73xx_6ch_mixer;
++		spec->init = stac92hd73xx_6ch_core_init;
++		break;
++	case 0x4: /* 8 Channel */
++		spec->multiout.hp_nid = 0x18;
++		spec->mixer = stac92hd73xx_8ch_mixer;
++		spec->init = stac92hd73xx_8ch_core_init;
++		break;
++	case 0x5: /* 10 Channel */
++		spec->multiout.hp_nid = 0x19;
++		spec->mixer = stac92hd73xx_10ch_mixer;
++		spec->init = stac92hd73xx_10ch_core_init;
++	};
++
++	spec->multiout.dac_nids = stac92hd73xx_dac_nids;
++	spec->aloopback_mask = 0x01;
++	spec->aloopback_shift = 8;
++
++	spec->mux_nids = stac92hd73xx_mux_nids;
++	spec->adc_nids = stac92hd73xx_adc_nids;
++	spec->dmic_nids = stac92hd73xx_dmic_nids;
++	spec->dmux_nids = stac92hd73xx_dmux_nids;
++
++	spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
++	spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
++	spec->num_dmics = STAC92HD73XX_NUM_DMICS;
++	spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
++	spec->dinput_mux = &stac92hd73xx_dmux;
++	/* GPIO0 High = Enable EAPD */
++	spec->gpio_mask = spec->gpio_dir = 0x1;
++	spec->gpio_data = 0x01;
++
++	spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
++	spec->pwr_nids = stac92hd73xx_pwr_nids;
++
++	err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
++
++	if (!err) {
++		if (spec->board_config < 0) {
++			printk(KERN_WARNING "hda_codec: No auto-config is "
++			       "available, default to model=ref\n");
++			spec->board_config = STAC_92HD73XX_REF;
++			goto again;
++		}
++		err = -EINVAL;
++	}
++
++	if (err < 0) {
++		stac92xx_free(codec);
++		return err;
++	}
++
++	codec->patch_ops = stac92xx_patch_ops;
++
++	return 0;
++}
++
++static int patch_stac92hd71bxx(struct hda_codec *codec)
++{
++	struct sigmatel_spec *spec;
++	int err = 0;
++
++	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (spec == NULL)
++		return -ENOMEM;
++
++	codec->spec = spec;
++	spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
++	spec->pin_nids = stac92hd71bxx_pin_nids;
++	spec->board_config = snd_hda_check_board_config(codec,
++							STAC_92HD71BXX_MODELS,
++							stac92hd71bxx_models,
++							stac92hd71bxx_cfg_tbl);
++again:
++	if (spec->board_config < 0) {
++		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
++			" STAC92HD71BXX, using BIOS defaults\n");
++		err = stac92xx_save_bios_config_regs(codec);
++		if (err < 0) {
++			stac92xx_free(codec);
++			return err;
++		}
++		spec->pin_configs = spec->bios_pin_configs;
++	} else {
++		spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
++		stac92xx_set_config_regs(codec);
++	}
++
++	switch (codec->vendor_id) {
++	case 0x111d76b6: /* 4 Port without Analog Mixer */
++	case 0x111d76b7:
++	case 0x111d76b4: /* 6 Port without Analog Mixer */
++	case 0x111d76b5:
++		spec->mixer = stac92hd71bxx_mixer;
++		spec->init = stac92hd71bxx_core_init;
++		break;
++	default:
++		spec->mixer = stac92hd71bxx_analog_mixer;
++		spec->init = stac92hd71bxx_analog_core_init;
++	}
++
++	spec->aloopback_mask = 0x20;
++	spec->aloopback_shift = 0;
++
++	/* GPIO0 High = EAPD */
++	spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
++
++	spec->mux_nids = stac92hd71bxx_mux_nids;
++	spec->adc_nids = stac92hd71bxx_adc_nids;
++	spec->dmic_nids = stac92hd71bxx_dmic_nids;
++	spec->dmux_nids = stac92hd71bxx_dmux_nids;
++
++	spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
++	spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
++	spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
++	spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
++
++	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
++	spec->pwr_nids = stac92hd71bxx_pwr_nids;
++
++	spec->multiout.num_dacs = 2;
++	spec->multiout.hp_nid = 0x11;
++	spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
++
++	err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
++	if (!err) {
++		if (spec->board_config < 0) {
++			printk(KERN_WARNING "hda_codec: No auto-config is "
++			       "available, default to model=ref\n");
++			spec->board_config = STAC_92HD71BXX_REF;
++			goto again;
++		}
++		err = -EINVAL;
++	}
++
++	if (err < 0) {
++		stac92xx_free(codec);
++		return err;
++	}
++
++	codec->patch_ops = stac92xx_patch_ops;
++
++	return 0;
++};
++
+ static int patch_stac922x(struct hda_codec *codec)
+ {
+ 	struct sigmatel_spec *spec;
+@@ -2567,7 +3454,8 @@ static int patch_stac922x(struct hda_codec *codec)
+ 							stac922x_models,
+ 							stac922x_cfg_tbl);
+ 	if (spec->board_config == STAC_INTEL_MAC_V3) {
+-		spec->gpio_mute = 1;
++		spec->gpio_mask = spec->gpio_dir = 0x03;
++		spec->gpio_data = 0x03;
+ 		/* Intel Macs have all same PCI SSID, so we need to check
+ 		 * codec SSID to distinguish the exact models
+ 		 */
+@@ -2620,6 +3508,7 @@ static int patch_stac922x(struct hda_codec *codec)
+ 	spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
+ 	spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
+ 	spec->num_dmics = 0;
++	spec->num_pwrs = 0;
+ 
+ 	spec->init = stac922x_core_init;
+ 	spec->mixer = stac922x_mixer;
+@@ -2669,53 +3558,70 @@ static int patch_stac927x(struct hda_codec *codec)
+ 							stac927x_models,
+ 							stac927x_cfg_tbl);
+  again:
+-	if (spec->board_config < 0) {
+-                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n");
++	if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
++		if (spec->board_config < 0)
++			snd_printdd(KERN_INFO "hda_codec: Unknown model for"
++				    "STAC927x, using BIOS defaults\n");
+ 		err = stac92xx_save_bios_config_regs(codec);
+ 		if (err < 0) {
+ 			stac92xx_free(codec);
+ 			return err;
+ 		}
+ 		spec->pin_configs = spec->bios_pin_configs;
+-	} else if (stac927x_brd_tbl[spec->board_config] != NULL) {
++	} else {
+ 		spec->pin_configs = stac927x_brd_tbl[spec->board_config];
+ 		stac92xx_set_config_regs(codec);
+ 	}
+ 
++	spec->adc_nids = stac927x_adc_nids;
++	spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
++	spec->mux_nids = stac927x_mux_nids;
++	spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
++	spec->multiout.dac_nids = spec->dac_nids;
++
+ 	switch (spec->board_config) {
+ 	case STAC_D965_3ST:
+-		spec->adc_nids = stac927x_adc_nids;
+-		spec->mux_nids = stac927x_mux_nids;
+-		spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+-		spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
++	case STAC_D965_5ST:
++		/* GPIO0 High = Enable EAPD */
++		spec->gpio_mask = spec->gpio_dir = 0x01;
++		spec->gpio_data = 0x01;
+ 		spec->num_dmics = 0;
++
+ 		spec->init = d965_core_init;
+ 		spec->mixer = stac927x_mixer;
+ 		break;
+-	case STAC_D965_5ST:
+-		spec->adc_nids = stac927x_adc_nids;
+-		spec->mux_nids = stac927x_mux_nids;
+-		spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+-		spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+-		spec->num_dmics = 0;
++	case STAC_DELL_BIOS:
++		/* correct the front output jack as a hp out */
++		stac92xx_set_config_reg(codec, 0x0f, 0x02270110);
++		/* correct the front input jack as a mic */
++		stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
++		/* fallthru */
++	case STAC_DELL_3ST:
++		/* GPIO2 High = Enable EAPD */
++		spec->gpio_mask = spec->gpio_dir = 0x04;
++		spec->gpio_data = 0x04;
++		spec->dmic_nids = stac927x_dmic_nids;
++		spec->num_dmics = STAC927X_NUM_DMICS;
++
+ 		spec->init = d965_core_init;
+ 		spec->mixer = stac927x_mixer;
++		spec->dmux_nids = stac927x_dmux_nids;
++		spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
+ 		break;
+ 	default:
+-		spec->adc_nids = stac927x_adc_nids;
+-		spec->mux_nids = stac927x_mux_nids;
+-		spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+-		spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
++		/* GPIO0 High = Enable EAPD */
++		spec->gpio_mask = spec->gpio_dir = 0x1;
++		spec->gpio_data = 0x01;
+ 		spec->num_dmics = 0;
++
+ 		spec->init = stac927x_core_init;
+ 		spec->mixer = stac927x_mixer;
+ 	}
+ 
+-	spec->multiout.dac_nids = spec->dac_nids;
+-	/* GPIO0 High = Enable EAPD */
+-	spec->gpio_mask = spec->gpio_data = 0x00000001;
+-	stac92xx_enable_gpio_mask(codec); 
+-	
++	spec->num_pwrs = 0;
++	spec->aloopback_mask = 0x40;
++	spec->aloopback_shift = 0;
++
+ 	err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
+ 	if (!err) {
+ 		if (spec->board_config < 0) {
+@@ -2733,6 +3639,18 @@ static int patch_stac927x(struct hda_codec *codec)
+ 
+ 	codec->patch_ops = stac92xx_patch_ops;
+ 
++	/*
++	 * !!FIXME!!
++	 * The STAC927x seem to require fairly long delays for certain
++	 * command sequences.  With too short delays (even if the answer
++	 * is set to RIRB properly), it results in the silence output
++	 * on some hardwares like Dell.
++	 *
++	 * The below flag enables the longer delay (see get_response
++	 * in hda_intel.c).
++	 */
++	codec->bus->needs_damn_long_delay = 1;
++
+ 	return 0;
+ }
+ 
+@@ -2771,11 +3689,15 @@ static int patch_stac9205(struct hda_codec *codec)
+ 	spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
+ 	spec->dmic_nids = stac9205_dmic_nids;
+ 	spec->num_dmics = STAC9205_NUM_DMICS;
+-	spec->dmux_nid = 0x1d;
++	spec->dmux_nids = stac9205_dmux_nids;
++	spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
++	spec->num_pwrs = 0;
+ 
+ 	spec->init = stac9205_core_init;
+ 	spec->mixer = stac9205_mixer;
+ 
++	spec->aloopback_mask = 0x40;
++	spec->aloopback_shift = 0;
+ 	spec->multiout.dac_nids = spec->dac_nids;
+ 	
+ 	switch (spec->board_config){
+@@ -2784,19 +3706,28 @@ static int patch_stac9205(struct hda_codec *codec)
+ 		stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
+ 		stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+ 
+-		spec->gpio_mask = 0x00000007; /* GPIO0-2 */
+-		/* GPIO0 High = EAPD, GPIO1 Low = DRM,
+-		 * GPIO2 High = Headphone Mute
++		/* Enable unsol response for GPIO4/Dock HP connection */
++		snd_hda_codec_write(codec, codec->afg, 0,
++			AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
++		snd_hda_codec_write_cache(codec, codec->afg, 0,
++					  AC_VERB_SET_UNSOLICITED_ENABLE,
++					  (AC_USRSP_EN | STAC_HP_EVENT));
++
++		spec->gpio_dir = 0x0b;
++		spec->gpio_mask = 0x1b;
++		spec->gpio_mute = 0x10;
++		/* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
++		 * GPIO3 Low = DRM
+ 		 */
+-		spec->gpio_data = 0x00000005;
++		spec->gpio_data = 0x01;
+ 		break;
+ 	default:
+ 		/* GPIO0 High = EAPD */
+-		spec->gpio_mask = spec->gpio_data = 0x00000001;
++		spec->gpio_mask = spec->gpio_dir = 0x1;
++		spec->gpio_data = 0x01;
+ 		break;
+ 	}
+ 
+-	stac92xx_enable_gpio_mask(codec);
+ 	err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
+ 	if (!err) {
+ 		if (spec->board_config < 0) {
+@@ -2950,7 +3881,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
+ 
+ static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
+ {
+-	if (get_pin_presence(codec, 0x0a)) {
++	if (get_hp_pin_presence(codec, 0x0a)) {
+ 		stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
+ 		stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
+ 	} else {
+@@ -3032,6 +3963,7 @@ static int patch_stac9872(struct hda_codec *codec)
+ 		spec->multiout.hp_nid = VAIO_HP_DAC;
+ 		spec->num_adcs = ARRAY_SIZE(vaio_adcs);
+ 		spec->adc_nids = vaio_adcs;
++		spec->num_pwrs = 0;
+ 		spec->input_mux = &vaio_mux;
+ 		spec->mux_nids = vaio_mux_nids;
+ 		codec->patch_ops = stac9872_vaio_patch_ops;
+@@ -3045,6 +3977,7 @@ static int patch_stac9872(struct hda_codec *codec)
+ 		spec->multiout.dac_nids = vaio_dacs;
+ 		spec->multiout.hp_nid = VAIO_HP_DAC;
+ 		spec->num_adcs = ARRAY_SIZE(vaio_adcs);
++		spec->num_pwrs = 0;
+ 		spec->adc_nids = vaio_adcs;
+ 		spec->input_mux = &vaio_mux;
+ 		spec->mux_nids = vaio_mux_nids;
+@@ -3104,5 +4037,17 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+  	{ .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
+  	{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
+  	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
++	{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
++	{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
++	{ .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
++	{ .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
++	{ .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
+ 	{} /* terminator */
+ };
+diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
+index 4cdf3e6..4e5dd4c 100644
+--- a/sound/pci/hda/patch_via.c
++++ b/sound/pci/hda/patch_via.c
+@@ -27,11 +27,12 @@
+ /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid          */
+ /* 2006-08-02  Lydia Wang  Add support to VT1709 codec                       */
+ /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
++/* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization      */
++/* 2007-09-17  Lydia Wang  Add VT1708B codec support                        */
+ /*                                                                           */
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+@@ -51,14 +52,23 @@
+ #define VT1708_HP_NID		0x13
+ #define VT1708_DIGOUT_NID	0x14
+ #define VT1708_DIGIN_NID	0x16
++#define VT1708_DIGIN_PIN	0x26
+ 
+ #define VT1709_HP_DAC_NID	0x28
+ #define VT1709_DIGOUT_NID	0x13
+ #define VT1709_DIGIN_NID	0x17
++#define VT1709_DIGIN_PIN	0x25
++
++#define VT1708B_HP_NID		0x25
++#define VT1708B_DIGOUT_NID	0x12
++#define VT1708B_DIGIN_NID	0x15
++#define VT1708B_DIGIN_PIN	0x21
+ 
+ #define IS_VT1708_VENDORID(x)		((x) >= 0x11061708 && (x) <= 0x1106170b)
+ #define IS_VT1709_10CH_VENDORID(x)	((x) >= 0x1106e710 && (x) <= 0x1106e713)
+ #define IS_VT1709_6CH_VENDORID(x)	((x) >= 0x1106e714 && (x) <= 0x1106e717)
++#define IS_VT1708B_8CH_VENDORID(x)	((x) >= 0x1106e720 && (x) <= 0x1106e723)
++#define IS_VT1708B_4CH_VENDORID(x)	((x) >= 0x1106e724 && (x) <= 0x1106e727)
+ 
+ 
+ enum {
+@@ -131,6 +141,11 @@ static hda_nid_t vt1709_adc_nids[3] = {
+ 	0x14, 0x15, 0x16
+ };
+ 
++static hda_nid_t vt1708B_adc_nids[2] = {
++	/* ADC1-2 */
++	0x13, 0x14
++};
++
+ /* add dynamic controls */
+ static int via_add_control(struct via_spec *spec, int type, const char *name,
+ 			   unsigned long val)
+@@ -268,9 +283,13 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
+ 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 					     0x18, &spec->cur_mux[adc_idx]);
+ 	else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
+-		  IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) )
++		  IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0)
+ 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 					     0x19, &spec->cur_mux[adc_idx]);
++	else if ((IS_VT1708B_8CH_VENDORID(vendor_id) ||
++		  IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0)
++		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
++					     0x17, &spec->cur_mux[adc_idx]);
+ 	else
+ 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 					     spec->adc_nids[adc_idx],
+@@ -287,7 +306,6 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
+ 		.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",
+@@ -309,15 +327,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
+ 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ 
+ 
+-	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
++	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ 	 * mixer widget
+ 	 */
+ 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */
+-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
++	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ 
+ 	/*
+ 	 * Set up output mixers (0x19 - 0x1b)
+@@ -329,10 +347,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
+ 	
+ 	/* Setup default input to PW4 */
+ 	{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
+-	/* Set mic as default input of sw0 */
+-	{0x18, AC_VERB_SET_CONNECT_SEL, 0x2},
+ 	/* PW9 Output enable */
+ 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++	{ }
+ };
+ 
+ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
+@@ -544,6 +561,33 @@ static int via_init(struct hda_codec *codec)
+ {
+ 	struct via_spec *spec = codec->spec;
+ 	snd_hda_sequence_write(codec, spec->init_verbs);
++	/* Lydia Add for EAPD enable */
++	if (!spec->dig_in_nid) { /* No Digital In connection */
++		if (IS_VT1708_VENDORID(codec->vendor_id)) {
++			snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
++					    AC_VERB_SET_PIN_WIDGET_CONTROL,
++					    PIN_OUT);
++			snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
++					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
++		} else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) ||
++			   IS_VT1709_6CH_VENDORID(codec->vendor_id)) {
++			snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
++					    AC_VERB_SET_PIN_WIDGET_CONTROL,
++					    PIN_OUT);
++			snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
++					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
++		} else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) ||
++			   IS_VT1708B_4CH_VENDORID(codec->vendor_id)) {
++			snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
++					    AC_VERB_SET_PIN_WIDGET_CONTROL,
++					    PIN_OUT);
++			snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
++					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
++		}
++	} else /* enable SPDIF-input pin */
++		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
++				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
++
+  	return 0;
+ }
+ 
+@@ -623,58 +667,68 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
+ 		if (i == AUTO_SEQ_CENLFE) {
+ 			/* Center/LFE */
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+-					      "Center Playback Volume",
+-					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
++					"Center Playback Volume",
++					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
++							    HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ 					      "LFE Playback Volume",
+-					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ 					      "Center Playback Switch",
+-					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ 					      "LFE Playback Switch",
+-					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		} else if (i == AUTO_SEQ_FRONT){
+ 			/* add control to mixer index 0 */
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ 					      "Master Front Playback Volume",
+-					      HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
++					      HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
++								  HDA_INPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ 					      "Master Front Playback Switch",
+-					      HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
++					      HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
++								  HDA_INPUT));
+ 			if (err < 0)
+ 				return err;
+ 			
+ 			/* add control to PW3 */
+ 			sprintf(name, "%s Playback Volume", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+-					      HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			sprintf(name, "%s Playback Switch", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+-					      HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		} else {
+ 			sprintf(name, "%s Playback Volume", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+-					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			sprintf(name, "%s Playback Switch", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+-					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		}
+@@ -875,7 +929,6 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
+ 		.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",
+@@ -899,15 +952,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
+ 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ 
+ 
+-	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
++	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ 	 * mixer widget
+ 	 */
+ 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */
+-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ 
+ 	/*
+ 	 * Set up output selector (0x1a, 0x1b, 0x29)
+@@ -925,8 +978,6 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
+ 
+ 	/* Set input of PW4 as AOW4 */
+ 	{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
+-	/* Set mic as default input of sw0 */
+-	{0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
+ 	/* PW9 Output enable */
+ 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ 	{ }
+@@ -1073,68 +1124,80 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
+ 			/* Center/LFE */
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ 					      "Center Playback Volume",
+-					      HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ 					      "LFE Playback Volume",
+-					      HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ 					      "Center Playback Switch",
+-					      HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ 					      "LFE Playback Switch",
+-					      HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		} else if (i == AUTO_SEQ_FRONT){
+ 			/* add control to mixer index 0 */
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ 					      "Master Front Playback Volume",
+-					      HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
++					      HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
++								  HDA_INPUT));
+ 			if (err < 0)
+ 				return err;
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ 					      "Master Front Playback Switch",
+-					      HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
++					      HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
++								  HDA_INPUT));
+ 			if (err < 0)
+ 				return err;
+ 			
+ 			/* add control to PW3 */
+ 			sprintf(name, "%s Playback Volume", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+-					      HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			sprintf(name, "%s Playback Switch", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+-					      HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		} else if (i == AUTO_SEQ_SURROUND) {
+ 			sprintf(name, "%s Playback Volume", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+-					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			sprintf(name, "%s Playback Switch", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+-					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		} else if (i == AUTO_SEQ_SIDE) {
+ 			sprintf(name, "%s Playback Volume", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+-					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 			sprintf(name, "%s Playback Switch", chname[i]);
+ 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+-					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
++					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
++								  HDA_OUTPUT));
+ 			if (err < 0)
+ 				return err;
+ 		}
+@@ -1351,8 +1414,6 @@ static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
+ 
+ 	/* Set input of PW4 as MW0 */
+ 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
+-	/* Set mic as default input of sw0 */
+-	{0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
+ 	/* PW9 Output enable */
+ 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ 	{ }
+@@ -1403,6 +1464,494 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ 	spec->loopback.amplist = vt1709_loopbacks;
+ #endif
++	return 0;
++}
++
++/* capture mixer elements */
++static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
++	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
++	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
++	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
++	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		/* The multiple "Capture Source" controls confuse alsamixer
++		 * So call somewhat different..
++		 */
++		/* .name = "Capture Source", */
++		.name = "Input Source",
++		.count = 1,
++		.info = via_mux_enum_info,
++		.get = via_mux_enum_get,
++		.put = via_mux_enum_put,
++	},
++	{ } /* end */
++};
++/*
++ * generic initialization of ADC, input mixers and output mixers
++ */
++static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
++	/*
++	 * Unmute ADC0-1 and set the default input to mic-in
++	 */
++	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++
++
++	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
++	 * mixer widget
++	 */
++	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
++
++	/*
++	 * Set up output mixers
++	 */
++	/* set vol=0 to output mixers */
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++
++	/* Setup default input to PW4 */
++	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
++	/* PW9 Output enable */
++	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++	/* PW10 Input enable */
++	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++	{ }
++};
++
++static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
++	/*
++	 * Unmute ADC0-1 and set the default input to mic-in
++	 */
++	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++
++
++	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
++	 * mixer widget
++	 */
++	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
++	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
++
++	/*
++	 * Set up output mixers
++	 */
++	/* set vol=0 to output mixers */
++	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++
++	/* Setup default input of PW4 to MW0 */
++	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
++	/* PW9 Output enable */
++	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++	/* PW10 Input enable */
++	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++	{ }
++};
++
++static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 8,
++	.nid = 0x10, /* NID to query formats and rates */
++	.ops = {
++		.open = via_playback_pcm_open,
++		.prepare = via_playback_pcm_prepare,
++		.cleanup = via_playback_pcm_cleanup
++	},
++};
++
++static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 4,
++	.nid = 0x10, /* NID to query formats and rates */
++	.ops = {
++		.open = via_playback_pcm_open,
++		.prepare = via_playback_pcm_prepare,
++		.cleanup = via_playback_pcm_cleanup
++	},
++};
++
++static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
++	.substreams = 2,
++	.channels_min = 2,
++	.channels_max = 2,
++	.nid = 0x13, /* NID to query formats and rates */
++	.ops = {
++		.prepare = via_capture_pcm_prepare,
++		.cleanup = via_capture_pcm_cleanup
++	},
++};
++
++static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 2,
++	/* NID is set in via_build_pcms */
++	.ops = {
++		.open = via_dig_playback_pcm_open,
++		.close = via_dig_playback_pcm_close,
++		.prepare = via_dig_playback_pcm_prepare
++	},
++};
++
++static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
++	.substreams = 1,
++	.channels_min = 2,
++	.channels_max = 2,
++};
++
++/* fill in the dac_nids table from the parsed pin configuration */
++static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
++				     const struct auto_pin_cfg *cfg)
++{
++	int i;
++	hda_nid_t nid;
++
++	spec->multiout.num_dacs = cfg->line_outs;
++
++	spec->multiout.dac_nids = spec->private_dac_nids;
++
++	for (i = 0; i < 4; i++) {
++		nid = cfg->line_out_pins[i];
++		if (nid) {
++			/* config dac list */
++			switch (i) {
++			case AUTO_SEQ_FRONT:
++				spec->multiout.dac_nids[i] = 0x10;
++				break;
++			case AUTO_SEQ_CENLFE:
++				spec->multiout.dac_nids[i] = 0x24;
++				break;
++			case AUTO_SEQ_SURROUND:
++				spec->multiout.dac_nids[i] = 0x25;
++				break;
++			case AUTO_SEQ_SIDE:
++				spec->multiout.dac_nids[i] = 0x11;
++				break;
++			}
++		}
++	}
++
++	return 0;
++}
++
++/* add playback controls from the parsed DAC table */
++static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
++					     const struct auto_pin_cfg *cfg)
++{
++	char name[32];
++	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
++	hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18};
++	hda_nid_t nid, nid_vol = 0;
++	int i, err;
++
++	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
++		nid = cfg->line_out_pins[i];
++
++		if (!nid)
++			continue;
++
++		nid_vol = nid_vols[i];
++
++		if (i == AUTO_SEQ_CENLFE) {
++			/* Center/LFE */
++			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
++					      "Center Playback Volume",
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
++					      "LFE Playback Volume",
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
++					      "Center Playback Switch",
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
++					      "LFE Playback Switch",
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		} else if (i == AUTO_SEQ_FRONT) {
++			/* add control to mixer index 0 */
++			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
++					      "Master Front Playback Volume",
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++								  HDA_INPUT));
++			if (err < 0)
++				return err;
++			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
++					      "Master Front Playback Switch",
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++								  HDA_INPUT));
++			if (err < 0)
++				return err;
++
++			/* add control to PW3 */
++			sprintf(name, "%s Playback Volume", chname[i]);
++			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
++					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++			sprintf(name, "%s Playback Switch", chname[i]);
++			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
++					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		} else {
++			sprintf(name, "%s Playback Volume", chname[i]);
++			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++			sprintf(name, "%s Playback Switch", chname[i]);
++			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
++					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++								  HDA_OUTPUT));
++			if (err < 0)
++				return err;
++		}
++	}
++
++	return 0;
++}
++
++static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
++{
++	int err;
++
++	if (!pin)
++		return 0;
++
++	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
++
++	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
++			      "Headphone Playback Volume",
++			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
++	if (err < 0)
++		return err;
++	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
++			      "Headphone Playback Switch",
++			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
++	if (err < 0)
++		return err;
++
++	return 0;
++}
++
++/* create playback/capture controls for input pins */
++static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
++						const struct auto_pin_cfg *cfg)
++{
++	static char *labels[] = {
++		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
++	};
++	struct hda_input_mux *imux = &spec->private_imux;
++	int i, err, idx = 0;
++
++	/* for internal loopback recording select */
++	imux->items[imux->num_items].label = "Stereo Mixer";
++	imux->items[imux->num_items].index = idx;
++	imux->num_items++;
++
++	for (i = 0; i < AUTO_PIN_LAST; i++) {
++		if (!cfg->input_pins[i])
++			continue;
++
++		switch (cfg->input_pins[i]) {
++		case 0x1a: /* Mic */
++			idx = 2;
++			break;
++
++		case 0x1b: /* Line In */
++			idx = 3;
++			break;
++
++		case 0x1e: /* Front Mic */
++			idx = 4;
++			break;
++
++		case 0x1f: /* CD */
++			idx = 1;
++			break;
++		}
++		err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
++					   idx, 0x16);
++		if (err < 0)
++			return err;
++		imux->items[imux->num_items].label = labels[i];
++		imux->items[imux->num_items].index = idx;
++		imux->num_items++;
++	}
++	return 0;
++}
++
++static int vt1708B_parse_auto_config(struct hda_codec *codec)
++{
++	struct via_spec *spec = codec->spec;
++	int err;
++
++	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
++	if (err < 0)
++		return err;
++	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
++	if (err < 0)
++		return err;
++	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
++		return 0; /* can't find valid BIOS pin config */
++
++	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
++	if (err < 0)
++		return err;
++	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
++	if (err < 0)
++		return err;
++	err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
++	if (err < 0)
++		return err;
++
++	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
++
++	if (spec->autocfg.dig_out_pin)
++		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
++	if (spec->autocfg.dig_in_pin)
++		spec->dig_in_nid = VT1708B_DIGIN_NID;
++
++	if (spec->kctl_alloc)
++		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
++
++	spec->input_mux = &spec->private_imux;
++
++	return 1;
++}
++
++#ifdef CONFIG_SND_HDA_POWER_SAVE
++static struct hda_amp_list vt1708B_loopbacks[] = {
++	{ 0x16, HDA_INPUT, 1 },
++	{ 0x16, HDA_INPUT, 2 },
++	{ 0x16, HDA_INPUT, 3 },
++	{ 0x16, HDA_INPUT, 4 },
++	{ } /* end */
++};
++#endif
++
++static int patch_vt1708B_8ch(struct hda_codec *codec)
++{
++	struct via_spec *spec;
++	int err;
++
++	/* create a codec specific record */
++	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++	if (spec == NULL)
++		return -ENOMEM;
++
++	codec->spec = spec;
++
++	/* automatic parse from the BIOS config */
++	err = vt1708B_parse_auto_config(codec);
++	if (err < 0) {
++		via_free(codec);
++		return err;
++	} else if (!err) {
++		printk(KERN_INFO "hda_codec: Cannot set up configuration "
++		       "from BIOS.  Using genenic mode...\n");
++	}
++
++	spec->init_verbs = vt1708B_8ch_volume_init_verbs;
++
++	spec->stream_name_analog = "VT1708B Analog";
++	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
++	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
++
++	spec->stream_name_digital = "VT1708B Digital";
++	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
++	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
++
++	if (!spec->adc_nids && spec->input_mux) {
++		spec->adc_nids = vt1708B_adc_nids;
++		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
++		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
++		spec->num_mixers++;
++	}
++
++	codec->patch_ops = via_patch_ops;
++
++	codec->patch_ops.init = via_auto_init;
++#ifdef CONFIG_SND_HDA_POWER_SAVE
++	spec->loopback.amplist = vt1708B_loopbacks;
++#endif
++
++	return 0;
++}
++
++static int patch_vt1708B_4ch(struct hda_codec *codec)
++{
++	struct via_spec *spec;
++	int err;
++
++	/* create a codec specific record */
++	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++	if (spec == NULL)
++		return -ENOMEM;
++
++	codec->spec = spec;
++
++	/* automatic parse from the BIOS config */
++	err = vt1708B_parse_auto_config(codec);
++	if (err < 0) {
++		via_free(codec);
++		return err;
++	} else if (!err) {
++		printk(KERN_INFO "hda_codec: Cannot set up configuration "
++		       "from BIOS.  Using genenic mode...\n");
++	}
++
++	spec->init_verbs = vt1708B_4ch_volume_init_verbs;
++
++	spec->stream_name_analog = "VT1708B Analog";
++	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
++	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
++
++	spec->stream_name_digital = "VT1708B Digital";
++	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
++	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
++
++	if (!spec->adc_nids && spec->input_mux) {
++		spec->adc_nids = vt1708B_adc_nids;
++		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
++		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
++		spec->num_mixers++;
++	}
++
++	codec->patch_ops = via_patch_ops;
++
++	codec->patch_ops.init = via_auto_init;
++#ifdef CONFIG_SND_HDA_POWER_SAVE
++	spec->loopback.amplist = vt1708B_loopbacks;
++#endif
+ 
+ 	return 0;
+ }
+@@ -1415,13 +1964,37 @@ struct hda_codec_preset snd_hda_preset_via[] = {
+ 	{ .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
+ 	{ .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
+ 	{ .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
+-	{ .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+-	{ .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+-	{ .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+-	{ .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
+-	{ .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+-	{ .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+-	{ .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+-	{ .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
++	{ .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
++	  .patch = patch_vt1709_10ch},
++	{ .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
++	  .patch = patch_vt1709_10ch},
++	{ .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
++	  .patch = patch_vt1709_10ch},
++	{ .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
++	  .patch = patch_vt1709_10ch},
++	{ .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
++	  .patch = patch_vt1709_6ch},
++	{ .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
++	  .patch = patch_vt1709_6ch},
++	{ .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
++	  .patch = patch_vt1709_6ch},
++	{ .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
++	  .patch = patch_vt1709_6ch},
++	{ .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
++	  .patch = patch_vt1708B_8ch},
++	{ .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
++	  .patch = patch_vt1708B_8ch},
++	{ .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
++	  .patch = patch_vt1708B_8ch},
++	{ .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
++	  .patch = patch_vt1708B_8ch},
++	{ .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
++	  .patch = patch_vt1708B_4ch},
++	{ .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
++	  .patch = patch_vt1708B_4ch},
++	{ .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
++	  .patch = patch_vt1708B_4ch},
++	{ .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
++	  .patch = patch_vt1708B_4ch},
+ 	{} /* terminator */
+ };
+diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c
+new file mode 100644
+index 0000000..2da49d2
+--- /dev/null
++++ b/sound/pci/hda/vmaster.c
+@@ -0,0 +1,364 @@
++/*
++ * Virtual master and slave controls
++ *
++ *  Copyright (c) 2008 by Takashi Iwai <tiwai at suse.de>
++ *
++ *  This program 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, version 2.
++ *
++ */
++
++#include <linux/slab.h>
++#include <sound/core.h>
++#include <sound/control.h>
++
++/*
++ * a subset of information returned via ctl info callback
++ */
++struct link_ctl_info {
++	int type;		/* value type */
++	int count;		/* item count */
++	int min_val, max_val;	/* min, max values */
++};
++
++/*
++ * link master - this contains a list of slave controls that are
++ * identical types, i.e. info returns the same value type and value
++ * ranges, but may have different number of counts.
++ *
++ * The master control is so far only mono volume/switch for simplicity.
++ * The same value will be applied to all slaves.
++ */
++struct link_master {
++	struct list_head slaves;
++	struct link_ctl_info info;
++	int val;		/* the master value */
++};
++
++/*
++ * link slave - this contains a slave control element
++ *
++ * It fakes the control callbacsk with additional attenuation by the
++ * master control.  A slave may have either one or two channels.
++ */
++
++struct link_slave {
++	struct list_head list;
++	struct link_master *master;
++	struct link_ctl_info info;
++	int vals[2];		/* current values */
++	struct snd_kcontrol slave; /* the copy of original control entry */
++};
++
++/* get the slave ctl info and save the initial values */
++static int slave_init(struct link_slave *slave)
++{
++	struct snd_ctl_elem_info *uinfo;
++	struct snd_ctl_elem_value *uctl;
++	int err, ch;
++
++	if (slave->info.count)
++		return 0; /* already initialized */
++
++	uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
++	if (!uinfo)
++		return -ENOMEM;
++	uinfo->id = slave->slave.id;
++	err = slave->slave.info(&slave->slave, uinfo);
++	if (err < 0) {
++		kfree(uinfo);
++		return err;
++	}
++	slave->info.type = uinfo->type;
++	slave->info.count = uinfo->count;
++	if (slave->info.count > 2  ||
++	    (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
++	     slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
++		snd_printk(KERN_ERR "invalid slave element\n");
++		kfree(uinfo);
++		return -EINVAL;
++	}
++	slave->info.min_val = uinfo->value.integer.min;
++	slave->info.max_val = uinfo->value.integer.max;
++	kfree(uinfo);
++
++	uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
++	if (!uctl)
++		return -ENOMEM;
++	uctl->id = slave->slave.id;
++	err = slave->slave.get(&slave->slave, uctl);
++	for (ch = 0; ch < slave->info.count; ch++)
++		slave->vals[ch] = uctl->value.integer.value[ch];
++	kfree(uctl);
++	return 0;
++}
++
++/* initialize master volume */
++static int master_init(struct link_master *master)
++{
++	struct link_slave *slave;
++
++	if (master->info.count)
++		return 0; /* already initialized */
++
++	list_for_each_entry(slave, &master->slaves, list) {
++		int err = slave_init(slave);
++		if (err < 0)
++			return err;
++		master->info = slave->info;
++		master->info.count = 1; /* always mono */
++		/* set full volume as default (= no attenuation) */
++		master->val = master->info.max_val;
++		return 0;
++	}
++	return -ENOENT;
++}
++
++static int slave_get_val(struct link_slave *slave,
++			 struct snd_ctl_elem_value *ucontrol)
++{
++	int err, ch;
++
++	err = slave_init(slave);
++	if (err < 0)
++		return err;
++	for (ch = 0; ch < slave->info.count; ch++)
++		ucontrol->value.integer.value[ch] = slave->vals[ch];
++	return 0;
++}
++
++static int slave_put_val(struct link_slave *slave,
++			 struct snd_ctl_elem_value *ucontrol)
++{
++	int err, ch, vol;
++
++	err = master_init(slave->master);
++	if (err < 0)
++		return err;
++
++	switch (slave->info.type) {
++	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
++		for (ch = 0; ch < slave->info.count; ch++)
++			ucontrol->value.integer.value[ch] &=
++				!!slave->master->val;
++		break;
++	case SNDRV_CTL_ELEM_TYPE_INTEGER:
++		for (ch = 0; ch < slave->info.count; ch++) {
++			/* max master volume is supposed to be 0 dB */
++			vol = ucontrol->value.integer.value[ch];
++			vol += slave->master->val - slave->master->info.max_val;
++			if (vol < slave->info.min_val)
++				vol = slave->info.min_val;
++			else if (vol > slave->info.max_val)
++				vol = slave->info.max_val;
++			ucontrol->value.integer.value[ch] = vol;
++		}
++		break;
++	}
++	return slave->slave.put(&slave->slave, ucontrol);
++}
++
++/*
++ * ctl callbacks for slaves
++ */
++static int slave_info(struct snd_kcontrol *kcontrol,
++		      struct snd_ctl_elem_info *uinfo)
++{
++	struct link_slave *slave = snd_kcontrol_chip(kcontrol);
++	return slave->slave.info(&slave->slave, uinfo);
++}
++
++static int slave_get(struct snd_kcontrol *kcontrol,
++		     struct snd_ctl_elem_value *ucontrol)
++{
++	struct link_slave *slave = snd_kcontrol_chip(kcontrol);
++	return slave_get_val(slave, ucontrol);
++}
++
++static int slave_put(struct snd_kcontrol *kcontrol,
++		     struct snd_ctl_elem_value *ucontrol)
++{
++	struct link_slave *slave = snd_kcontrol_chip(kcontrol);
++	int err, ch, changed = 0;
++
++	err = slave_init(slave);
++	if (err < 0)
++		return err;
++	for (ch = 0; ch < slave->info.count; ch++) {
++		if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
++			changed = 1;
++			slave->vals[ch] = ucontrol->value.integer.value[ch];
++		}
++	}
++	if (!changed)
++		return 0;
++	return slave_put_val(slave, ucontrol);
++}
++
++static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
++			 int op_flag, unsigned int size,
++			 unsigned int __user *tlv)
++{
++	struct link_slave *slave = snd_kcontrol_chip(kcontrol);
++	/* FIXME: this assumes that the max volume is 0 dB */
++	return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
++}
++
++static void slave_free(struct snd_kcontrol *kcontrol)
++{
++	struct link_slave *slave = snd_kcontrol_chip(kcontrol);
++	if (slave->slave.private_free)
++		slave->slave.private_free(&slave->slave);
++	if (slave->master)
++		list_del(&slave->list);
++	kfree(slave);
++}
++
++/*
++ * Add a slave control to the group with the given master control
++ *
++ * All slaves must be the same type (returning the same information
++ * via info callback).  The fucntion doesn't check it, so it's your
++ * responsibility.
++ *
++ * Also, some additional limitations:
++ * - at most two channels
++ * - logarithmic volume control (dB level), no linear volume
++ * - master can only attenuate the volume, no gain
++ */
++int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
++{
++	struct link_master *master_link = snd_kcontrol_chip(master);
++	struct link_slave *srec;
++
++	srec = kzalloc(sizeof(*srec) +
++		       slave->count * sizeof(*slave->vd), GFP_KERNEL);
++	if (!srec)
++		return -ENOMEM;
++	srec->slave = *slave;
++	memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
++	srec->master = master_link;
++
++	/* override callbacks */
++	slave->info = slave_info;
++	slave->get = slave_get;
++	slave->put = slave_put;
++	if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
++		slave->tlv.c = slave_tlv_cmd;
++	slave->private_data = srec;
++	slave->private_free = slave_free;
++
++	list_add_tail(&srec->list, &master_link->slaves);
++	return 0;
++}
++
++/*
++ * ctl callbacks for master controls
++ */
++static int master_info(struct snd_kcontrol *kcontrol,
++		      struct snd_ctl_elem_info *uinfo)
++{
++	struct link_master *master = snd_kcontrol_chip(kcontrol);
++	int ret;
++
++	ret = master_init(master);
++	if (ret < 0)
++		return ret;
++	uinfo->type = master->info.type;
++	uinfo->count = master->info.count;
++	uinfo->value.integer.min = master->info.min_val;
++	uinfo->value.integer.max = master->info.max_val;
++	return 0;
++}
++
++static int master_get(struct snd_kcontrol *kcontrol,
++		      struct snd_ctl_elem_value *ucontrol)
++{
++	struct link_master *master = snd_kcontrol_chip(kcontrol);
++	int err = master_init(master);
++	if (err < 0)
++		return err;
++	ucontrol->value.integer.value[0] = master->val;
++	return 0;
++}
++
++static int master_put(struct snd_kcontrol *kcontrol,
++		      struct snd_ctl_elem_value *ucontrol)
++{
++	struct link_master *master = snd_kcontrol_chip(kcontrol);
++	struct link_slave *slave;
++	struct snd_ctl_elem_value *uval;
++	int err, old_val;
++
++	err = master_init(master);
++	if (err < 0)
++		return err;
++	old_val = master->val;
++	if (ucontrol->value.integer.value[0] == old_val)
++		return 0;
++
++	uval = kmalloc(sizeof(*uval), GFP_KERNEL);
++	if (!uval)
++		return -ENOMEM;
++	list_for_each_entry(slave, &master->slaves, list) {
++		master->val = old_val;
++		uval->id = slave->slave.id;
++		slave_get_val(slave, uval);
++		master->val = ucontrol->value.integer.value[0];
++		slave_put_val(slave, uval);
++	}
++	kfree(uval);
++	return 1;
++}
++
++static void master_free(struct snd_kcontrol *kcontrol)
++{
++	struct link_master *master = snd_kcontrol_chip(kcontrol);
++	struct link_slave *slave;
++
++	list_for_each_entry(slave, &master->slaves, list)
++		slave->master = NULL;
++	kfree(master);
++}
++
++
++/*
++ * Create a virtual master control with the given name
++ */
++struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
++						 const unsigned int *tlv)
++{
++	struct link_master *master;
++	struct snd_kcontrol *kctl;
++	struct snd_kcontrol_new knew;
++
++	memset(&knew, 0, sizeof(knew));
++	knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
++	knew.name = name;
++	knew.info = master_info;
++
++	master = kzalloc(sizeof(*master), GFP_KERNEL);
++	if (!master)
++		return NULL;
++	INIT_LIST_HEAD(&master->slaves);
++
++	kctl = snd_ctl_new1(&knew, master);
++	if (!kctl) {
++		kfree(master);
++		return NULL;
++	}
++	/* override some callbacks */
++	kctl->info = master_info;
++	kctl->get = master_get;
++	kctl->put = master_put;
++	kctl->private_free = master_free;
++
++	/* additional (constant) TLV read */
++	if (tlv) {
++		/* FIXME: this assumes that the max volume is 0 dB */
++		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
++		kctl->tlv.p = tlv;
++	}
++	return kctl;
++}
+diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
+index 65ce66a..f99fe08 100644
+--- a/sound/pci/ice1712/Makefile
++++ b/sound/pci/ice1712/Makefile
+@@ -5,7 +5,7 @@
+ 
+ snd-ice17xx-ak4xxx-objs := ak4xxx.o
+ snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
+-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.o
++snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o
+ 
+ # Toplevel Module Dependency
+ obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
+diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
+index a1aba0d..dab31b2 100644
+--- a/sound/pci/ice1712/ak4xxx.c
++++ b/sound/pci/ice1712/ak4xxx.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
+index 6e13d75..3756430 100644
+--- a/sound/pci/ice1712/amp.c
++++ b/sound/pci/ice1712/amp.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
+index ec0699c..868ae29 100644
+--- a/sound/pci/ice1712/aureon.c
++++ b/sound/pci/ice1712/aureon.c
+@@ -47,7 +47,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -62,6 +61,15 @@
+ #include "aureon.h"
+ #include <sound/tlv.h>
+ 
++/* AC97 register cache for Aureon */
++struct aureon_spec {
++	unsigned short stac9744[64];
++	unsigned int cs8415_mux;
++	unsigned short master[2];
++	unsigned short vol[8];
++	unsigned char pca9554_out;
++};
++
+ /* WM8770 registers */
+ #define WM_DAC_ATTEN		0x00	/* DAC1-8 analog attenuation */
+ #define WM_DAC_MASTER_ATTEN	0x08	/* DAC master analog attenuation */
+@@ -205,7 +213,8 @@ static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
+ 				     struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+-	ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out;
++	struct aureon_spec *spec = ice->spec;
++	ucontrol->value.enumerated.item[0] = spec->pca9554_out;
+ 	return 0;
+ }
+ 
+@@ -213,16 +222,18 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
+ 				     struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	unsigned char oval, nval;
+ 	int change;
+ 
++	nval = ucontrol->value.enumerated.item[0];
++	if (nval >= 3)
++		return -EINVAL;
+ 	snd_ice1712_save_gpio_status(ice);
+-	
+-	oval = ice->spec.aureon.pca9554_out;
+-	nval = ucontrol->value.integer.value[0];
++	oval = spec->pca9554_out;
+ 	if ((change = (oval != nval))) {
+ 		aureon_pca9554_write(ice, PCA9554_OUT, nval);
+-		ice->spec.aureon.pca9554_out = nval;
++		spec->pca9554_out = nval;
+ 	}
+ 	snd_ice1712_restore_gpio_status(ice);
+ 	
+@@ -233,6 +244,7 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
+ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
+ 			      unsigned short val)
+ {
++	struct aureon_spec *spec = ice->spec;
+ 	unsigned int tmp;
+ 
+ 	/* Send address to XILINX chip */
+@@ -280,12 +292,13 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
+ 	udelay(10);
+ 	
+ 	/* Store the data in out private buffer */
+-	ice->spec.aureon.stac9744[(reg & 0x7F) >> 1] = val;
++	spec->stac9744[(reg & 0x7F) >> 1] = val;
+ }
+ 
+ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short reg)
+ {
+-       return ice->spec.aureon.stac9744[(reg & 0x7F) >> 1];
++	struct aureon_spec *spec = ice->spec;
++	return spec->stac9744[(reg & 0x7F) >> 1];
+ }
+ 
+ /*
+@@ -293,6 +306,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
+  */
+ static int aureon_ac97_init (struct snd_ice1712 *ice)
+ {
++	struct aureon_spec *spec = ice->spec;
+ 	int i;
+ 	static const unsigned short ac97_defaults[] = {
+ 		0x00, 0x9640,
+@@ -330,9 +344,9 @@ static int aureon_ac97_init (struct snd_ice1712 *ice)
+ 	snd_ice1712_gpio_write(ice, tmp);
+ 	udelay(3);
+ 	
+-	memset(&ice->spec.aureon.stac9744, 0, sizeof(ice->spec.aureon.stac9744));
++	memset(&spec->stac9744, 0, sizeof(spec->stac9744));
+ 	for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2)
+-		ice->spec.aureon.stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
++		spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
+ 		
+ 	aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770
+ 
+@@ -744,27 +758,33 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
+ static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	int i;
+ 	for (i=0; i<2; i++)
+-		ucontrol->value.integer.value[i] = ice->spec.aureon.master[i] & ~WM_VOL_MUTE;
++		ucontrol->value.integer.value[i] =
++			spec->master[i] & ~WM_VOL_MUTE;
+ 	return 0;
+ }
+ 
+ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	int ch, change = 0;
+ 
+ 	snd_ice1712_save_gpio_status(ice);
+ 	for (ch = 0; ch < 2; ch++) {
+-		if (ucontrol->value.integer.value[ch] != ice->spec.aureon.master[ch]) {
++		unsigned int vol = ucontrol->value.integer.value[ch];
++		if (vol > WM_VOL_MAX)
++			continue;
++		vol |= spec->master[ch] & WM_VOL_MUTE;
++		if (vol != spec->master[ch]) {
+ 			int dac;
+-			ice->spec.aureon.master[ch] &= WM_VOL_MUTE;
+-			ice->spec.aureon.master[ch] |= ucontrol->value.integer.value[ch];
++			spec->master[ch] = vol;
+ 			for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+ 				wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
+-					   ice->spec.aureon.vol[dac + ch],
+-					   ice->spec.aureon.master[ch]);
++					   spec->vol[dac + ch],
++					   spec->master[ch]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -788,18 +808,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
+ static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	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.aureon.vol[ofs+i] & ~WM_VOL_MUTE;
++		ucontrol->value.integer.value[i] =
++			spec->vol[ofs+i] & ~WM_VOL_MUTE;
+ 	return 0;
+ }
+ 
+ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	int i, idx, ofs, voices;
+ 	int change = 0;
+ 
+@@ -807,12 +830,15 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
+ 	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.aureon.vol[ofs+i]) {
+-			ice->spec.aureon.vol[ofs+i] &= WM_VOL_MUTE;
+-			ice->spec.aureon.vol[ofs+i] |= ucontrol->value.integer.value[i];
+-			wm_set_vol(ice, idx, ice->spec.aureon.vol[ofs+i],
+-				   ice->spec.aureon.master[i]);
++		unsigned int vol = ucontrol->value.integer.value[i];
++		if (vol > 0x7f)
++			continue;
++		vol |= spec->vol[ofs+i];
++		if (vol != spec->vol[ofs+i]) {
++			spec->vol[ofs+i] = vol;
++			idx  = WM_DAC_ATTEN + ofs + i;
++			wm_set_vol(ice, idx, spec->vol[ofs + i],
++				   spec->master[i]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -834,19 +860,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
+ static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	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.aureon.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
++		ucontrol->value.integer.value[i] =
++			(spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+ 	return 0;
+ }
+ 
+ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	int change = 0, voices, ofs, i;
+ 
+ 	voices = kcontrol->private_value >> 8;
+@@ -854,13 +883,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
+ 
+ 	snd_ice1712_save_gpio_status(ice);
+ 	for (i = 0; i < voices; i++) {
+-		int val = (ice->spec.aureon.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
++		int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+ 		if (ucontrol->value.integer.value[i] != val) {
+-			ice->spec.aureon.vol[ofs + i] &= ~WM_VOL_MUTE;
+-			ice->spec.aureon.vol[ofs + i] |=
++			spec->vol[ofs + i] &= ~WM_VOL_MUTE;
++			spec->vol[ofs + i] |=
+ 				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+-			wm_set_vol(ice, ofs + i, ice->spec.aureon.vol[ofs + i],
+-				   ice->spec.aureon.master[i]);
++			wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
++				   spec->master[i]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -877,29 +906,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
+ static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	
+-	ucontrol->value.integer.value[0] = (ice->spec.aureon.master[0] & WM_VOL_MUTE) ? 0 : 1;
+-	ucontrol->value.integer.value[1] = (ice->spec.aureon.master[1] & WM_VOL_MUTE) ? 0 : 1;
++	ucontrol->value.integer.value[0] =
++		(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
++	ucontrol->value.integer.value[1] =
++		(spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
+ 	return 0;
+ }
+ 
+ static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	int change = 0, i;
+ 
+ 	snd_ice1712_save_gpio_status(ice);
+ 	for (i = 0; i < 2; i++) {
+-		int val = (ice->spec.aureon.master[i] & WM_VOL_MUTE) ? 0 : 1;
++		int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1;
+ 		if (ucontrol->value.integer.value[i] != val) {
+ 			int dac;
+-			ice->spec.aureon.master[i] &= ~WM_VOL_MUTE;
+-			ice->spec.aureon.master[i] |=
++			spec->master[i] &= ~WM_VOL_MUTE;
++			spec->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.aureon.vol[dac + i],
+-					   ice->spec.aureon.master[i]);
++					   spec->vol[dac + i],
++					   spec->master[i]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -940,8 +973,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
+ 	unsigned short ovol, nvol;
+ 	int change = 0;
+ 
+-	snd_ice1712_save_gpio_status(ice);
+ 	nvol = ucontrol->value.integer.value[0];
++	if (nvol > PCM_RES)
++		return -EINVAL;
++	snd_ice1712_save_gpio_status(ice);
+ 	nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
+ 	ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+ 	if (ovol != nvol) {
+@@ -1031,7 +1066,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
+ 	snd_ice1712_save_gpio_status(ice);
+ 	for (i = 0; i < 2; i++) {
+ 		idx  = WM_ADC_GAIN + i;
+-		nvol = ucontrol->value.integer.value[i];
++		nvol = ucontrol->value.integer.value[i] & 0x1f;
+ 		ovol = wm_get(ice, idx);
+ 		if ((ovol & 0x1f) != nvol) {
+ 			wm_put(ice, idx, nvol | (ovol & ~0x1f));
+@@ -1143,10 +1178,11 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
+ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 
+ 	//snd_ice1712_save_gpio_status(ice);
+ 	//val = aureon_cs8415_get(ice, CS8415_CTRL2);
+-	ucontrol->value.enumerated.item[0] = ice->spec.aureon.cs8415_mux;
++	ucontrol->value.enumerated.item[0] = spec->cs8415_mux;
+ 	//snd_ice1712_restore_gpio_status(ice);
+ 	return 0;
+ }
+@@ -1154,6 +1190,7 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct aureon_spec *spec = ice->spec;
+ 	unsigned short oval, nval;
+ 	int change;
+ 
+@@ -1165,7 +1202,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ 	if (change)
+ 		aureon_cs8415_put(ice, CS8415_CTRL2, nval);
+ 	snd_ice1712_restore_gpio_status(ice);
+-	ice->spec.aureon.cs8415_mux = ucontrol->value.enumerated.item[0];
++	spec->cs8415_mux = ucontrol->value.enumerated.item[0];
+ 	return change;
+ }
+ 
+@@ -2001,10 +2038,16 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
+ 		0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */
+ 		(unsigned short)-1
+ 	};
++	struct aureon_spec *spec;
+ 	unsigned int tmp;
+ 	const unsigned short *p;
+ 	int err, i;
+ 
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
+ 	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
+ 		ice->num_total_dacs = 6;
+ 		ice->num_total_adcs = 2;
+@@ -2055,7 +2098,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
+ 	    ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
+ 		for (p = cs_inits; *p != (unsigned short)-1; p++)
+ 			aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24);
+-		ice->spec.aureon.cs8415_mux = 1;
++		spec->cs8415_mux = 1;
+ 
+ 		aureon_set_headphone_amp(ice, 1);
+ 	}
+@@ -2066,11 +2109,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
+ 	aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
+ 	aureon_pca9554_write(ice, PCA9554_OUT, 0x00);   /* internal AUX */
+ 	
+-	ice->spec.aureon.master[0] = WM_VOL_MUTE;
+-	ice->spec.aureon.master[1] = WM_VOL_MUTE;
++	spec->master[0] = WM_VOL_MUTE;
++	spec->master[1] = WM_VOL_MUTE;
+ 	for (i = 0; i < ice->num_total_dacs; i++) {
+-		ice->spec.aureon.vol[i] = WM_VOL_MUTE;
+-		wm_set_vol(ice, i, ice->spec.aureon.vol[i], ice->spec.aureon.master[i % 2]);
++		spec->vol[i] = WM_VOL_MUTE;
++		wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
+ 	}
+ 
+ 	return 0;
+diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
+index 371f784..efd180b 100644
+--- a/sound/pci/ice1712/delta.c
++++ b/sound/pci/ice1712/delta.c
+@@ -22,7 +22,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -405,7 +404,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
+ 	if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
+ 		snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
+ 	snd_i2c_readbytes(ice->cs8427, &reg, 1);
+-	ucontrol->value.integer.value[0] = (reg ? 1 : 0);
++	ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0;
+ 	return 0;
+ }
+ 
+diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
+index 75e4e5e..064760d 100644
+--- a/sound/pci/ice1712/ews.c
++++ b/sound/pci/ice1712/ews.c
+@@ -22,7 +22,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -45,6 +44,11 @@ enum {
+ };
+ 	
+ 
++/* additional i2c devices for EWS boards */
++struct ews_spec {
++	struct snd_i2c_device *i2cdevs[3];
++};
++
+ /*
+  * access via i2c mode (for EWX 24/96, EWS 88MT&D)
+  */
+@@ -142,15 +146,17 @@ static struct snd_i2c_bit_ops snd_ice1712_ewx_cs8427_bit_ops = {
+ /* AK4524 chip select; address 0x48 bit 0-3 */
+ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mask)
+ {
++	struct ews_spec *spec = ice->spec;
+ 	unsigned char data, ndata;
+ 
+ 	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
+ 		goto __error;
+ 	ndata = (data & 0xf0) | chip_mask;
+ 	if (ndata != data)
+-		if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1)
++		if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], &ndata, 1)
++		    != 1)
+ 			goto __error;
+ 	snd_i2c_unlock(ice->i2c);
+ 	return 0;
+@@ -224,6 +230,7 @@ static void dmx6fire_ak4524_lock(struct snd_akm4xxx *ak, int chip)
+ 
+ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned char bits)
+ {
++	struct ews_spec *spec = ice->spec;
+ 	unsigned char bytes[2];
+ 
+ 	snd_i2c_lock(ice->i2c);
+@@ -231,15 +238,18 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned
+ 	case ICE1712_SUBDEVICE_EWS88MT:
+ 	case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ 	case ICE1712_SUBDEVICE_PHASE88:
+-		if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1)
++		if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1)
++		    != 1)
+ 			goto _error;
+ 		break;
+ 	case ICE1712_SUBDEVICE_EWS88D:
+-		if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
++		if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], bytes, 2)
++		    != 2)
+ 			goto _error;
+ 		if (bits != bytes[1]) {
+ 			bytes[1] = bits;
+-			if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2)
++			if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D],
++					      bytes, 2) != 2)
+ 				goto _error;
+ 		}
+ 		break;
+@@ -412,6 +422,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+ {
+ 	int err;
+ 	struct snd_akm4xxx *ak;
++	struct ews_spec *spec;
+ 
+ 	/* set the analog DACs */
+ 	switch (ice->eeprom.subvendor) {
+@@ -436,6 +447,11 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+ 		break;
+ 	}
+ 
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
+ 	/* create i2c */
+ 	if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
+ 		snd_printk(KERN_ERR "unable to create I2C bus\n");
+@@ -447,7 +463,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+ 	/* create i2c devices */
+ 	switch (ice->eeprom.subvendor) {
+ 	case ICE1712_SUBDEVICE_DMX6FIRE:
+-		if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->spec.i2cdevs[EWS_I2C_6FIRE])) < 0) {
++		err = snd_i2c_device_create(ice->i2c, "PCF9554",
++					    ICE1712_6FIRE_PCF9554_ADDR,
++					    &spec->i2cdevs[EWS_I2C_6FIRE]);
++		if (err < 0) {
+ 			snd_printk(KERN_ERR "PCF9554 initialization failed\n");
+ 			return err;
+ 		}
+@@ -456,18 +475,30 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+ 	case ICE1712_SUBDEVICE_EWS88MT:
+ 	case ICE1712_SUBDEVICE_EWS88MT_NEW:
+ 	case ICE1712_SUBDEVICE_PHASE88:
+-		if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->spec.i2cdevs[EWS_I2C_CS8404])) < 0)
++		err = snd_i2c_device_create(ice->i2c, "CS8404",
++					    ICE1712_EWS88MT_CS8404_ADDR,
++					    &spec->i2cdevs[EWS_I2C_CS8404]);
++		if (err < 0)
+ 			return err;
+-		if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF1])) < 0)
++		err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)",
++					    ICE1712_EWS88MT_INPUT_ADDR,
++					    &spec->i2cdevs[EWS_I2C_PCF1]);
++		if (err < 0)
+ 			return err;
+-		if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF2])) < 0)
++		err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)",
++					    ICE1712_EWS88MT_OUTPUT_ADDR,
++					    &spec->i2cdevs[EWS_I2C_PCF2]);
++		if (err < 0)
+ 			return err;
+ 		/* Check if the front module is connected */
+ 		if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
+ 			return err;
+ 		break;
+ 	case ICE1712_SUBDEVICE_EWS88D:
+-		if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->spec.i2cdevs[EWS_I2C_88D])) < 0)
++		err = snd_i2c_device_create(ice->i2c, "PCF8575",
++					    ICE1712_EWS88D_PCF_ADDR,
++					    &spec->i2cdevs[EWS_I2C_88D]);
++		if (err < 0)
+ 			return err;
+ 		break;
+ 	}
+@@ -507,7 +538,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+ 	}
+ 
+ 	/* analog section */
+-	ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
++	ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ 	if (! ak)
+ 		return -ENOMEM;
+ 	ice->akm_codecs = 1;
+@@ -605,10 +636,11 @@ static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
+ static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct ews_spec *spec = ice->spec;
+ 	unsigned char data;
+ 
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -621,15 +653,17 @@ static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, s
+ static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct ews_spec *spec = ice->spec;
+ 	unsigned char data, ndata;
+ 
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+ 	ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
+-	if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) {
++	if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2],
++					       &ndata, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -641,12 +675,13 @@ static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, s
+ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct ews_spec *spec = ice->spec;
+ 	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 	unsigned char data;
+ 
+ 	snd_assert(channel >= 0 && channel <= 7, return 0);
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -660,17 +695,19 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st
+ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct ews_spec *spec = ice->spec;
+ 	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 	unsigned char data, ndata;
+ 
+ 	snd_assert(channel >= 0 && channel <= 7, return 0);
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+ 	ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
+-	if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &ndata, 1) != 1) {
++	if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF1],
++					       &ndata, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -705,12 +742,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata =
+ static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct ews_spec *spec = ice->spec;
+ 	int shift = kcontrol->private_value & 0xff;
+ 	int invert = (kcontrol->private_value >> 8) & 1;
+ 	unsigned char data[2];
+ 	
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -725,13 +763,14 @@ static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct
+ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct ews_spec *spec = ice->spec;
+ 	int shift = kcontrol->private_value & 0xff;
+ 	int invert = (kcontrol->private_value >> 8) & 1;
+ 	unsigned char data[2], ndata[2];
+ 	int change;
+ 
+ 	snd_i2c_lock(ice->i2c);
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -744,7 +783,8 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct
+ 			ndata[shift >> 3] |= (1 << (shift & 7));
+ 	}
+ 	change = (data[shift >> 3] != ndata[shift >> 3]);
+-	if (change && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) {
++	if (change &&
++	    snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+@@ -778,11 +818,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
+ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg)
+ {
+ 	unsigned char byte;
++	struct ews_spec *spec = ice->spec;
++
+ 	snd_i2c_lock(ice->i2c);
+ 	byte = reg;
+-	snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1);
++	snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1);
+ 	byte = 0;
+-	if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
++	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		printk(KERN_ERR "cannot read pca\n");
+ 		return -EIO;
+@@ -794,10 +836,12 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg
+ static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data)
+ {
+ 	unsigned char bytes[2];
++	struct ews_spec *spec = ice->spec;
++
+ 	snd_i2c_lock(ice->i2c);
+ 	bytes[0] = reg;
+ 	bytes[1] = data;
+-	if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
++	if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) {
+ 		snd_i2c_unlock(ice->i2c);
+ 		return -EIO;
+ 	}
+diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
+index abcfd1d..cf5c7c0 100644
+--- a/sound/pci/ice1712/hoontech.c
++++ b/sound/pci/ice1712/hoontech.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -34,6 +33,12 @@
+ #include "ice1712.h"
+ #include "hoontech.h"
+ 
++/* Hoontech-specific setting */
++struct hoontech_spec {
++	unsigned char boxbits[4];
++	unsigned int config;
++	unsigned short boxconfig[4];
++};
+ 
+ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
+ {
+@@ -50,169 +55,182 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un
+ 
+ static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
+ {
++	struct hoontech_spec *spec = ice->spec;
+ 	mutex_lock(&ice->gpio_mutex);
+-	ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, activate);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
++	ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
+ 	mutex_unlock(&ice->gpio_mutex);
+ }
+ 
+ static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
+ {
++	struct hoontech_spec *spec = ice->spec;
+ 	mutex_lock(&ice->gpio_mutex);
+-	ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, activate);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
++	ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
+ 	mutex_unlock(&ice->gpio_mutex);
+ }
+ 
+ static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
+ {
++	struct hoontech_spec *spec = ice->spec;
+ 	mutex_lock(&ice->gpio_mutex);
+-	ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, activate);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
++	ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
+ 	mutex_unlock(&ice->gpio_mutex);
+ }
+ 
+ static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
+ {
++	struct hoontech_spec *spec = ice->spec;
++
+ 	mutex_lock(&ice->gpio_mutex);
+ 
+ 	/* select box */
+-	ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
++	ICE1712_STDSP24_0_BOX(spec->boxbits, box);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
+ 
+ 	/* prepare for write */
+ 	if (chn == 3)
+-		ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
+-	ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, activate);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
+-
+-	ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
++		ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
++	ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
++
++	ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
++	ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
++	ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
++	ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ 	udelay(100);
+ 	if (chn == 3) {
+-		ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0);
+-		snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
++		ICE1712_STDSP24_2_CHN4(spec->boxbits, 0);
++		snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ 	} else {
+ 		switch (chn) {
+-		case 0:	ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 0); break;
+-		case 1:	ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 0); break;
+-		case 2:	ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 0); break;
++		case 0:	ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break;
++		case 1:	ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break;
++		case 2:	ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break;
+ 		}
+-		snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
++		snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
+ 	}
+ 	udelay(100);
+-	ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
++	ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
++	ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
++	ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
++	ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ 	udelay(100);
+ 
+-	ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
++	ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ 
+ 	mutex_unlock(&ice->gpio_mutex);
+ }
+ 
+ static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
+ {
++	struct hoontech_spec *spec = ice->spec;
++
+ 	mutex_lock(&ice->gpio_mutex);
+ 
+ 	/* select box */
+-	ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]);
++	ICE1712_STDSP24_0_BOX(spec->boxbits, box);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
+ 
+-	ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, master);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
++	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
++	ICE1712_STDSP24_2_MIDI1(spec->boxbits, master);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
+ 
+ 	udelay(100);
+ 	
+-	ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 0);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
++	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ 	
+ 	mdelay(10);
+ 	
+-	ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]);
++	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
+ 
+ 	mutex_unlock(&ice->gpio_mutex);
+ }
+ 
+ static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
+ {
++	struct hoontech_spec *spec = ice->spec;
+ 	mutex_lock(&ice->gpio_mutex);
+-	ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, activate);
+-	snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]);
++	ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
++	snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
+ 	mutex_unlock(&ice->gpio_mutex);
+ }
+ 
+ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
+ {
++	struct hoontech_spec *spec;
+ 	int box, chn;
+ 
+ 	ice->num_total_dacs = 8;
+ 	ice->num_total_adcs = 8;
+ 
+-	ice->spec.hoontech.boxbits[0] = 
+-	ice->spec.hoontech.boxbits[1] = 
+-	ice->spec.hoontech.boxbits[2] = 
+-	ice->spec.hoontech.boxbits[3] = 0;	/* should be already */
+-
+-	ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 0);
+-	ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 0, 1);
+-	ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, 0);
+-	ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, 0);
+-
+-	ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 1, 1);
+-	ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1);
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
++	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0);
++	ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1);
++	ICE1712_STDSP24_0_BOX(spec->boxbits, 0);
++	ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0);
++
++	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1);
++	ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1);
++	ICE1712_STDSP24_1_CHN1(spec->boxbits, 1);
++	ICE1712_STDSP24_1_CHN2(spec->boxbits, 1);
++	ICE1712_STDSP24_1_CHN3(spec->boxbits, 1);
+ 	
+-	ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 2);
+-	ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 2, 1);
+-	ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0);
+-
+-	ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 3);
+-	ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 3, 1);
+-	ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, 0);
+-	ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, 1);
+-	ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, 0);
++	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2);
++	ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1);
++	ICE1712_STDSP24_2_CHN4(spec->boxbits, 1);
++	ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
++	ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
++
++	ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3);
++	ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1);
++	ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0);
++	ICE1712_STDSP24_3_MUTE(spec->boxbits, 1);
++	ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
+ 
+ 	/* let's go - activate only functions in first box */
+-	ice->spec.hoontech.config = 0;
++	spec->config = 0;
+ 			    /* ICE1712_STDSP24_MUTE |
+ 			       ICE1712_STDSP24_INSEL |
+ 			       ICE1712_STDSP24_DAREAR; */
+-	ice->spec.hoontech.boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
++	spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+ 				     ICE1712_STDSP24_BOX_CHN2 |
+ 				     ICE1712_STDSP24_BOX_CHN3 |
+ 				     ICE1712_STDSP24_BOX_CHN4 |
+ 				     ICE1712_STDSP24_BOX_MIDI1 |
+ 				     ICE1712_STDSP24_BOX_MIDI2;
+-	ice->spec.hoontech.boxconfig[1] = 
+-	ice->spec.hoontech.boxconfig[2] = 
+-	ice->spec.hoontech.boxconfig[3] = 0;
+-	snd_ice1712_stdsp24_darear(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+-	snd_ice1712_stdsp24_mute(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+-	snd_ice1712_stdsp24_insel(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+-	for (box = 0; box < 4; box++) {
++	spec->boxconfig[1] = 
++	spec->boxconfig[2] = 
++	spec->boxconfig[3] = 0;
++	snd_ice1712_stdsp24_darear(ice,
++		(spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
++	snd_ice1712_stdsp24_mute(ice,
++		(spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
++	snd_ice1712_stdsp24_insel(ice,
++		(spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
++	for (box = 0; box < 1; box++) {
++		if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
++                        snd_ice1712_stdsp24_midi2(ice, 1);
+ 		for (chn = 0; chn < 4; chn++)
+-			snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->spec.hoontech.boxconfig[box] & (1 << chn)) ? 1 : 0);
++			snd_ice1712_stdsp24_box_channel(ice, box, chn,
++				(spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
+ 		snd_ice1712_stdsp24_box_midi(ice, box,
+-				(ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
+-		if (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
+-			snd_ice1712_stdsp24_midi2(ice, 1);
++				(spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
+ 	}
+ 
+ 	return 0;
+diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
+index 052fc3c..df292af 100644
+--- a/sound/pci/ice1712/ice1712.c
++++ b/sound/pci/ice1712/ice1712.c
+@@ -47,7 +47,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -2491,6 +2490,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice)
+ 		pci_release_regions(ice->pci);
+ 	snd_ice1712_akm4xxx_free(ice);
+ 	pci_disable_device(ice->pci);
++	kfree(ice->spec);
+ 	kfree(ice);
+ 	return 0;
+ }
+diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
+index 58640af..303cffe 100644
+--- a/sound/pci/ice1712/ice1712.h
++++ b/sound/pci/ice1712/ice1712.h
+@@ -366,42 +366,7 @@ struct snd_ice1712 {
+ 	struct mutex gpio_mutex;
+ 
+ 	/* other board-specific data */
+-	union {
+-		/* additional i2c devices for EWS boards */
+-		struct snd_i2c_device *i2cdevs[3];
+-		/* AC97 register cache for Aureon */
+-		struct aureon_spec {
+-			unsigned short stac9744[64];
+-			unsigned int cs8415_mux;
+-			unsigned short master[2];
+-			unsigned short vol[8];
+-			unsigned char pca9554_out;
+-		} aureon;
+-		/* AC97 register cache for Phase28 */
+-		struct phase28_spec {
+-			unsigned short master[2];
+-			unsigned short vol[8];
+-		} phase28;
+-		/* a non-standard I2C device for revo51 */
+-		struct revo51_spec {
+-			struct snd_i2c_device *dev;
+-			struct snd_pt2258 *pt2258;
+-		} revo51;
+-		/* Hoontech-specific setting */
+-		struct hoontech_spec {
+-			unsigned char boxbits[4];
+-			unsigned int config;
+-			unsigned short boxconfig[4];
+-		} hoontech;
+-		struct {
+-			struct ak4114 *ak4114;
+-			unsigned int analog: 1;
+-		} juli;
+-		struct {
+-			struct ak4114 *ak4114;
+-		} prodigy192;
+-	} spec;
+-
++	void *spec;
+ };
+ 
+ 
+diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
+index 0b0bbb0..f533850 100644
+--- a/sound/pci/ice1712/ice1724.c
++++ b/sound/pci/ice1712/ice1724.c
+@@ -22,7 +22,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -48,9 +47,11 @@
+ #include "vt1720_mobo.h"
+ #include "pontis.h"
+ #include "prodigy192.h"
++#include "prodigy_hifi.h"
+ #include "juli.h"
+ #include "phase.h"
+ #include "wtm.h"
++#include "se.h"
+ 
+ MODULE_AUTHOR("Jaroslav Kysela <perex at perex.cz>");
+ MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
+@@ -62,9 +63,11 @@ MODULE_SUPPORTED_DEVICE("{"
+ 	       VT1720_MOBO_DEVICE_DESC
+ 	       PONTIS_DEVICE_DESC
+ 	       PRODIGY192_DEVICE_DESC
++	       PRODIGY_HIFI_DEVICE_DESC
+ 	       JULI_DEVICE_DESC
+ 	       PHASE_DEVICE_DESC
+ 	       WTM_DEVICE_DESC
++	       SE_DEVICE_DESC
+ 		"{VIA,VT1720},"
+ 		"{VIA,VT1724},"
+ 		"{ICEnsemble,Generic ICE1724},"
+@@ -1929,10 +1932,12 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+ 	snd_vt1724_aureon_cards,
+ 	snd_vt1720_mobo_cards,
+ 	snd_vt1720_pontis_cards,
++	snd_vt1724_prodigy_hifi_cards,
+ 	snd_vt1724_prodigy192_cards,
+ 	snd_vt1724_juli_cards,
+ 	snd_vt1724_phase_cards,
+ 	snd_vt1724_wtm_cards,
++	snd_vt1724_se_cards,
+ 	NULL,
+ };
+ 
+@@ -1955,6 +1960,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
+ 	unsigned char val;
+ 
+ 	mutex_lock(&ice->i2c_mutex);
++	wait_i2c_busy(ice);
+ 	outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
+ 	outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
+ 	wait_i2c_busy(ice);
+@@ -2170,6 +2176,7 @@ static int snd_vt1724_free(struct snd_ice1712 *ice)
+ 	pci_release_regions(ice->pci);
+ 	snd_ice1712_akm4xxx_free(ice);
+ 	pci_disable_device(ice->pci);
++	kfree(ice->spec);
+ 	kfree(ice);
+ 	return 0;
+ }
+diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
+index 1fbe3ef..e8038c0 100644
+--- a/sound/pci/ice1712/juli.c
++++ b/sound/pci/ice1712/juli.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -33,6 +32,11 @@
+ #include "envy24ht.h"
+ #include "juli.h"
+ 
++struct juli_spec {
++	struct ak4114 *ak4114;
++	unsigned int analog: 1;
++};
++
+ /*
+  * chip addresses on I2C bus
+  */
+@@ -138,12 +142,13 @@ static struct snd_akm4xxx akm_juli_dac __devinitdata = {
+ 
+ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
+ {
++	struct juli_spec *spec = ice->spec;
+ 	int err;
+ 	err = snd_ice1712_akm4xxx_build_controls(ice);
+ 	if (err < 0)
+ 		return err;
+ 	/* only capture SPDIF over AK4114 */
+-	err = snd_ak4114_build(ice->spec.juli.ak4114, NULL,
++	err = snd_ak4114_build(spec->ak4114, NULL,
+ 			       ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ 	if (err < 0)
+ 		return err;
+@@ -167,13 +172,19 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
+ 		0x41, 0x02, 0x2c, 0x00, 0x00
+ 	};
+ 	int err;
++	struct juli_spec *spec;
+ 	struct snd_akm4xxx *ak;
+ 
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
+ 	err = snd_ak4114_create(ice->card,
+ 				juli_ak4114_read,
+ 				juli_ak4114_write,
+ 				ak4114_init_vals, ak4114_init_txcsb,
+-				ice, &ice->spec.juli.ak4114);
++				ice, &spec->ak4114);
+ 	if (err < 0)
+ 		return err;
+ 
+@@ -181,12 +192,12 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
+         /* it seems that the analog doughter board detection does not work
+            reliably, so force the analog flag; it should be very rare
+            to use Juli@ without the analog doughter board */
+-	ice->spec.juli.analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
++	spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
+ #else
+-        ice->spec.juli.analog = 1;
++        spec->analog = 1;
+ #endif
+ 
+-	if (ice->spec.juli.analog) {
++	if (spec->analog) {
+ 		printk(KERN_INFO "juli@: analog I/O detected\n");
+ 		ice->num_total_dacs = 2;
+ 		ice->num_total_adcs = 2;
+diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
+index 3ac2505..9ab4a9f 100644
+--- a/sound/pci/ice1712/phase.c
++++ b/sound/pci/ice1712/phase.c
+@@ -33,7 +33,6 @@
+  *		CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -48,6 +47,12 @@
+ #include "phase.h"
+ #include <sound/tlv.h>
+ 
++/* AC97 register cache for Phase28 */
++struct phase28_spec {
++	unsigned short master[2];
++	unsigned short vol[8];
++} phase28;
++
+ /* WM8770 registers */
+ #define WM_DAC_ATTEN		0x00	/* DAC1-8 analog attenuation */
+ #define WM_DAC_MASTER_ATTEN	0x08	/* DAC master analog attenuation */
+@@ -313,27 +318,32 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
+ static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	int i;
+ 	for (i=0; i<2; i++)
+-		ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
++		ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE;
+ 	return 0;
+ }
+ 
+ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	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]) {
++		unsigned int vol = ucontrol->value.integer.value[ch];
++		if (vol > WM_VOL_MAX)
++			continue;
++		vol |= spec->master[ch] & WM_VOL_MUTE;
++		if (vol != spec->master[ch]) {
+ 			int dac;
+-			ice->spec.phase28.master[ch] &= WM_VOL_MUTE;
+-			ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch];
++			spec->master[ch] = vol;
+ 			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]);
++					   spec->vol[dac + ch],
++					   spec->master[ch]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -382,12 +392,18 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
+ 
+ 	unsigned int tmp;
+ 	struct snd_akm4xxx *ak;
++	struct phase28_spec *spec;
+ 	const unsigned short *p;
+ 	int i;
+ 
+ 	ice->num_total_dacs = 8;
+ 	ice->num_total_adcs = 2;
+ 
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
+ 	// Initialize analog chips
+ 	ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+ 	if (!ak)
+@@ -417,11 +433,11 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)
+ 
+ 	snd_ice1712_restore_gpio_status(ice);
+ 
+-	ice->spec.phase28.master[0] = WM_VOL_MUTE;
+-	ice->spec.phase28.master[1] = WM_VOL_MUTE;
++	spec->master[0] = WM_VOL_MUTE;
++	spec->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]);
++		spec->vol[i] = WM_VOL_MUTE;
++		wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
+ 	}
+ 
+ 	return 0;
+@@ -443,18 +459,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
+ static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	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;
++		ucontrol->value.integer.value[i] =
++			spec->vol[ofs+i] & ~WM_VOL_MUTE;
+ 	return 0;
+ }
+ 
+ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	int i, idx, ofs, voices;
+ 	int change = 0;
+ 
+@@ -462,12 +481,16 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
+ 	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]);
++		unsigned int vol;
++		vol = ucontrol->value.integer.value[i];
++		if (vol > 0x7f)
++			continue;
++		vol |= spec->vol[ofs+i] & WM_VOL_MUTE;
++		if (vol != spec->vol[ofs+i]) {
++			spec->vol[ofs+i] = vol;
++			idx  = WM_DAC_ATTEN + ofs + i;
++			wm_set_vol(ice, idx, spec->vol[ofs+i],
++				   spec->master[i]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -489,19 +512,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
+ static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	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;
++		ucontrol->value.integer.value[i] =
++			(spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+ 	return 0;
+ }
+ 
+ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	int change = 0, voices, ofs, i;
+ 
+ 	voices = kcontrol->private_value >> 8;
+@@ -509,13 +535,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
+ 
+ 	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;
++		int val = (spec->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] |=
++			spec->vol[ofs + i] &= ~WM_VOL_MUTE;
++			spec->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]);
++			wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
++				   spec->master[i]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -532,29 +558,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
+ static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 
+-	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;
++	ucontrol->value.integer.value[0] =
++		(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
++	ucontrol->value.integer.value[1] =
++		(spec->master[1] & WM_VOL_MUTE) ? 0 : 1;
+ 	return 0;
+ }
+ 
+ static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct phase28_spec *spec = ice->spec;
+ 	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;
++		int val = (spec->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] |=
++			spec->master[i] &= ~WM_VOL_MUTE;
++			spec->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]);
++					   spec->vol[dac + i],
++					   spec->master[i]);
+ 			change = 1;
+ 		}
+ 	}
+@@ -595,8 +625,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
+ 	unsigned short ovol, nvol;
+ 	int change = 0;
+ 
+-	snd_ice1712_save_gpio_status(ice);
+ 	nvol = ucontrol->value.integer.value[0];
++	if (nvol > PCM_RES)
++		return -EINVAL;
++	snd_ice1712_save_gpio_status(ice);
+ 	nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
+ 	ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+ 	if (ovol != nvol) {
+diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
+index faefd52..4945c81 100644
+--- a/sound/pci/ice1712/pontis.c
++++ b/sound/pci/ice1712/pontis.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
+index 4180f97..48cf40a 100644
+--- a/sound/pci/ice1712/prodigy192.c
++++ b/sound/pci/ice1712/prodigy192.c
+@@ -54,7 +54,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -68,6 +67,12 @@
+ #include "stac946x.h"
+ #include <sound/tlv.h>
+ 
++struct prodigy192_spec {
++	struct ak4114 *ak4114;
++	/* rate change needs atomic mute/unmute of all dacs*/
++	struct mutex mute_mutex;
++};
++
+ static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
+ {
+ 	snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val);
+@@ -81,6 +86,24 @@ static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg)
+ /*
+  * DAC mute control
+  */
++
++/*
++ * idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute
++ */
++static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
++		unsigned char mute)
++{
++	unsigned char new, old;
++	int change;
++	old = stac9460_get(ice, idx);
++	new = (~mute << 7 & 0x80) | (old & ~0x80);
++	change = (new != old);
++	if (change)
++		/*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/
++		stac9460_put(ice, idx, new);
++	return change;
++}
++
+ #define stac9460_dac_mute_info		snd_ctl_boolean_mono_info
+ 
+ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+@@ -101,20 +124,19 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+-	unsigned char new, old;
+-	int idx;
+-	int change;
++	struct prodigy192_spec *spec = ice->spec;
++	int idx, change;
+ 
+ 	if (kcontrol->private_value)
+ 		idx = STAC946X_MASTER_VOLUME;
+ 	else
+ 		idx  = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+-	old = stac9460_get(ice, idx);
+-	new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
+-	change = (new != old);
+-	if (change)
+-		stac9460_put(ice, idx, new);
+-
++	/* due to possible conflicts with stac9460_set_rate_val, mutexing */
++	mutex_lock(&spec->mute_mutex);
++	/*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
++		ucontrol->value.integer.value[0]);*/
++	change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
++	mutex_unlock(&spec->mute_mutex);
+ 	return change;
+ }
+ 
+@@ -162,6 +184,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
+ 	ovol = 0x7f - (tmp & 0x7f);
+ 	change = (ovol != nvol);
+ 	if (change) {
++		ovol =  (0x7f - nvol) | (tmp & 0x80);
++		/*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/
+ 		stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+ 	}
+ 	return change;
+@@ -241,7 +265,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
+ 
+ 	for (i = 0; i < 2; ++i) {
+ 		reg = STAC946X_MIC_L_VOLUME + i;
+-		nvol = ucontrol->value.integer.value[i];
++		nvol = ucontrol->value.integer.value[i] & 0x0f;
+ 		ovol = 0x0f - stac9460_get(ice, reg);
+ 		change = ((ovol & 0x0f)  != nvol);
+ 		if (change)
+@@ -251,121 +275,6 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
+ 	return change;
+ }
+ 
+-#if 0
+-/*
+- * Headphone Amplifier
+- */
+-static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)
+-{
+-	unsigned int tmp, tmp2;
+-
+-	tmp2 = tmp = snd_ice1712_gpio_read(ice);
+-	if (enable)
+-		tmp |= AUREON_HP_SEL;
+-	else
+-		tmp &= ~ AUREON_HP_SEL;
+-	if (tmp != tmp2) {
+-		snd_ice1712_gpio_write(ice, tmp);
+-		return 1;
+-	}
+-	return 0;
+-}
+-
+-static int aureon_get_headphone_amp(struct snd_ice1712 *ice)
+-{
+-	unsigned int tmp = snd_ice1712_gpio_read(ice);
+-
+-	return ( tmp & AUREON_HP_SEL )!= 0;
+-}
+-
+-#define aureon_bool_info	snd_ctl_boolean_mono_info
+-
+-static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+-
+-	ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
+-	return 0;
+-}
+-
+-
+-static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+-
+-	return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
+-}
+-
+-/*
+- * Deemphasis
+- */
+-static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+-	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+-	return 0;
+-}
+-
+-static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_ice1712 *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 aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *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 aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+-{
+-	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+-	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
+-	return 0;
+-}
+-
+-static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+-{
+-	int temp, temp2;
+-	struct snd_ice1712 *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;
+-}
+-#endif
+ static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
+ 	       			struct snd_ctl_elem_info *uinfo)
+ {
+@@ -407,6 +316,57 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
+ 		stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
+ 	return change;
+ }
++/*
++ * Handler for setting correct codec rate - called when rate change is detected
++ */
++static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
++{
++	unsigned char old, new;
++	int idx;
++	unsigned char changed[7];
++	struct snd_ice1712 *ice = ak->private_data[0];
++	struct prodigy192_spec *spec = ice->spec;
++
++	if (rate == 0)  /* no hint - S/PDIF input is master, simply return */
++		return;
++	else if (rate <= 48000)
++		new = 0x08;	/* 256x, base rate mode */
++	else if (rate <= 96000)
++		new = 0x11;	/* 256x, mid rate mode */
++	else
++		new = 0x12;	/* 128x, high rate mode */
++	old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
++	if (old == new)
++		return;
++	/* change detected, setting master clock, muting first */
++	/* due to possible conflicts with mute controls - mutexing */
++	mutex_lock(&spec->mute_mutex);
++	/* we have to remember current mute status for each DAC */
++	for (idx = 0; idx < 7 ; ++idx)
++		changed[idx] = stac9460_dac_mute(ice,
++				STAC946X_MASTER_VOLUME + idx, 0);
++	/*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/
++	stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
++	udelay(10);
++	/* unmuting - only originally unmuted dacs -
++	 * i.e. those changed when muting */
++	for (idx = 0; idx < 7 ; ++idx) {
++		if (changed[idx])
++			stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
++	}
++	mutex_unlock(&spec->mute_mutex);
++}
++
++/* using akm infrastructure for setting rate of the codec */
++static struct snd_akm4xxx akmlike_stac9460 __devinitdata = {
++	.type = NON_AKM,	/* special value */
++	.num_adcs = 6,		/* not used in any way, just for completeness */
++	.num_dacs = 2,
++	.ops = {
++		.set_rate_val = stac9460_set_rate_val
++	}
++};
++
+ 
+ static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
+ static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
+@@ -483,39 +443,8 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = {
+ 		.put = stac9460_mic_sw_put,
+ 
+ 	},
+-#if 0
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		.name = "Capture Route",
+-		.info = wm_adc_mux_info,
+-		.get = wm_adc_mux_get,
+-		.put = wm_adc_mux_put,
+-	},
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		.name = "Headphone Amplifier Switch",
+-		.info = aureon_bool_info,
+-		.get = aureon_hpamp_get,
+-		.put = aureon_hpamp_put
+-	},
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		.name = "DAC Deemphasis Switch",
+-		.info = aureon_bool_info,
+-		.get = aureon_deemp_get,
+-		.put = aureon_deemp_put
+-	},
+-	{
+-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+-		.name = "ADC Oversampling",
+-		.info = aureon_oversampling_info,
+-		.get = aureon_oversampling_get,
+-		.put = aureon_oversampling_put
+-	},
+-#endif
+ };
+ 
+-
+ /* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */
+ /* CDTO (pin 32) -- GPIO11 pin 86
+  * CDTI (pin 33) -- GPIO10 pin 77
+@@ -712,16 +641,39 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
+ 	static const unsigned char ak4114_init_txcsb[] = {
+ 		0x41, 0x02, 0x2c, 0x00, 0x00
+ 	};
++	struct prodigy192_spec *spec = ice->spec;
+ 
+ 	return snd_ak4114_create(ice->card,
+ 				 prodigy192_ak4114_read,
+ 				 prodigy192_ak4114_write,
+ 				 ak4114_init_vals, ak4114_init_txcsb,
+-				 ice, &ice->spec.prodigy192.ak4114);
++				 ice, &spec->ak4114);
++}
++
++static void stac9460_proc_regs_read(struct snd_info_entry *entry,
++		struct snd_info_buffer *buffer)
++{
++	struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
++	int reg, val;
++	/* registers 0x0 - 0x14 */
++	for (reg = 0; reg <= 0x15; reg++) {
++		val = stac9460_get(ice, reg);
++		snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
++	}
++}
++
++
++static void stac9460_proc_init(struct snd_ice1712 *ice)
++{
++	struct snd_info_entry *entry;
++	if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
++		snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
+ }
+ 
++
+ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
+ {
++	struct prodigy192_spec *spec = ice->spec;
+ 	unsigned int i;
+ 	int err;
+ 
+@@ -731,7 +683,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
+ 		if (err < 0)
+ 			return err;
+ 	}
+-	if (ice->spec.prodigy192.ak4114) {
++	if (spec->ak4114) {
+ 		/* ak4114 is connected */
+ 		for (i = 0; i < ARRAY_SIZE(ak4114_controls); i++) {
+ 			err = snd_ctl_add(ice->card,
+@@ -740,12 +692,13 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
+ 			if (err < 0)
+ 				return err;
+ 		}
+-		err = snd_ak4114_build(ice->spec.prodigy192.ak4114,
++		err = snd_ak4114_build(spec->ak4114,
+ 				NULL, /* ak4114 in MIO/DI/O handles no IEC958 output */
+ 				ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ 		if (err < 0)
+ 			return err;
+ 	}
++	stac9460_proc_init(ice);
+ 	return 0;
+ }
+ 
+@@ -778,6 +731,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
+ {
+ 	static const unsigned short stac_inits_prodigy[] = {
+ 		STAC946X_RESET, 0,
++		STAC946X_MASTER_CLOCKING, 0x11,
+ /*		STAC946X_MASTER_VOLUME, 0,
+ 		STAC946X_LF_VOLUME, 0,
+ 		STAC946X_RF_VOLUME, 0,
+@@ -789,22 +743,39 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
+ 	};
+ 	const unsigned short *p;
+ 	int err = 0;
++	struct snd_akm4xxx *ak;
++	struct prodigy192_spec *spec;
+ 
+ 	/* prodigy 192 */
+ 	ice->num_total_dacs = 6;
+ 	ice->num_total_adcs = 2;
+ 	ice->vt1720 = 0;  /* ice1724, e.g. 23 GPIOs */
+ 	
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++	mutex_init(&spec->mute_mutex);
++
+ 	/* initialize codec */
+ 	p = stac_inits_prodigy;
+ 	for (; *p != (unsigned short)-1; p += 2)
+ 		stac9460_put(ice, p[0], p[1]);
++	/* reusing the akm codecs infrastructure,
++	 * for setting rate on stac9460 */
++	ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
++	if (!ak)
++		return -ENOMEM;
++	ice->akm_codecs = 1;
++	err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice);
++	if (err < 0)
++		return err;
+ 
+ 	/* MI/ODI/O add on card with AK4114 */
+ 	if (prodigy192_miodio_exists(ice)) {
+ 		err = prodigy192_ak4114_init(ice);
+ 		/* from this moment if err = 0 then
+-		 * ice->spec.prodigy192.ak4114 should not be null
++		 * spec->ak4114 should not be null
+ 		 */
+ 		snd_printdd("AK4114 initialized with status %d\n", err);
+ 	} else
+@@ -854,6 +825,10 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
+ 		.build_controls = prodigy192_add_controls,
+ 		.eeprom_size = sizeof(prodigy71_eeprom),
+ 		.eeprom_data = prodigy71_eeprom,
++		/* the current MPU401 code loops infinitely
++		 * when opening midi device
++		 */
++		.no_mpu401 = 1,
+ 	},
+ 	{ } /* terminator */
+ };
+diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
+new file mode 100644
+index 0000000..043a938
+--- /dev/null
++++ b/sound/pci/ice1712/prodigy_hifi.c
+@@ -0,0 +1,1210 @@
++/*
++ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
++ *
++ *   Lowlevel functions for Audiotrak Prodigy 7.1 Hifi
++ *   based on pontis.c
++ *
++ *      Copyright (c) 2007 Julian Scheel <julian at jusst.de>
++ *      Copyright (c) 2007 allank
++ *      Copyright (c) 2004 Takashi Iwai <tiwai at suse.de>
++ *
++ *   This program 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 program 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 <asm/io.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/mutex.h>
++
++#include <sound/core.h>
++#include <sound/info.h>
++#include <sound/tlv.h>
++
++#include "ice1712.h"
++#include "envy24ht.h"
++#include "prodigy_hifi.h"
++
++struct prodigy_hifi_spec {
++	unsigned short master[2];
++	unsigned short vol[8];
++};
++
++/* I2C addresses */
++#define WM_DEV		0x34
++
++/* WM8776 registers */
++#define WM_HP_ATTEN_L		0x00	/* headphone left attenuation */
++#define WM_HP_ATTEN_R		0x01	/* headphone left attenuation */
++#define WM_HP_MASTER		0x02	/* headphone master (both channels),
++						override LLR */
++#define WM_DAC_ATTEN_L		0x03	/* digital left attenuation */
++#define WM_DAC_ATTEN_R		0x04
++#define WM_DAC_MASTER		0x05
++#define WM_PHASE_SWAP		0x06	/* DAC phase swap */
++#define WM_DAC_CTRL1		0x07
++#define WM_DAC_MUTE		0x08
++#define WM_DAC_CTRL2		0x09
++#define WM_DAC_INT		0x0a
++#define WM_ADC_INT		0x0b
++#define WM_MASTER_CTRL		0x0c
++#define WM_POWERDOWN		0x0d
++#define WM_ADC_ATTEN_L		0x0e
++#define WM_ADC_ATTEN_R		0x0f
++#define WM_ALC_CTRL1		0x10
++#define WM_ALC_CTRL2		0x11
++#define WM_ALC_CTRL3		0x12
++#define WM_NOISE_GATE		0x13
++#define WM_LIMITER		0x14
++#define WM_ADC_MUX		0x15
++#define WM_OUT_MUX		0x16
++#define WM_RESET		0x17
++
++/* Analog Recording Source :- Mic, LineIn, CD/Video, */
++
++/* implement capture source select control for WM8776 */
++
++#define WM_AIN1 "AIN1"
++#define WM_AIN2 "AIN2"
++#define WM_AIN3 "AIN3"
++#define WM_AIN4 "AIN4"
++#define WM_AIN5 "AIN5"
++
++/* GPIO pins of envy24ht connected to wm8766 */
++#define WM8766_SPI_CLK	 (1<<17) /* CLK, Pin97 on ICE1724 */
++#define WM8766_SPI_MD	  (1<<16) /* DATA VT1724 -> WM8766, Pin96 */
++#define WM8766_SPI_ML	  (1<<18) /* Latch, Pin98 */
++
++/* WM8766 registers */
++#define WM8766_DAC_CTRL	 0x02   /* DAC Control */
++#define WM8766_INT_CTRL	 0x03   /* Interface Control */
++#define WM8766_DAC_CTRL2	0x09
++#define WM8766_DAC_CTRL3	0x0a
++#define WM8766_RESET	    0x1f
++#define WM8766_LDA1	     0x00
++#define WM8766_LDA2	     0x04
++#define WM8766_LDA3	     0x06
++#define WM8766_RDA1	     0x01
++#define WM8766_RDA2	     0x05
++#define WM8766_RDA3	     0x07
++#define WM8766_MUTE1	    0x0C
++#define WM8766_MUTE2	    0x0F
++
++
++/*
++ * Prodigy HD2
++ */
++#define AK4396_ADDR    0x00
++#define AK4396_CSN    (1 << 8)    /* CSN->GPIO8, pin 75 */
++#define AK4396_CCLK   (1 << 9)    /* CCLK->GPIO9, pin 76 */
++#define AK4396_CDTI   (1 << 10)   /* CDTI->GPIO10, pin 77 */
++
++/* ak4396 registers */
++#define AK4396_CTRL1	    0x00
++#define AK4396_CTRL2	    0x01
++#define AK4396_CTRL3	    0x02
++#define AK4396_LCH_ATT	  0x03
++#define AK4396_RCH_ATT	  0x04
++
++
++/*
++ * get the current register value of WM codec
++ */
++static unsigned short wm_get(struct snd_ice1712 *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 and remember it
++ */
++static void wm_put_nocache(struct snd_ice1712 *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 void wm_put(struct snd_ice1712 *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;
++}
++
++/*
++ * write data in the SPI mode
++ */
++
++static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val)
++{
++	unsigned int tmp = snd_ice1712_gpio_read(ice);
++	if (val)
++		tmp |= bit;
++	else
++		tmp &= ~bit;
++	snd_ice1712_gpio_write(ice, tmp);
++}
++
++/*
++ * SPI implementation for WM8766 codec - only writing supported, no readback
++ */
++
++static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data)
++{
++	int i;
++	for (i = 0; i < 16; i++) {
++		set_gpio_bit(ice, WM8766_SPI_CLK, 0);
++		udelay(1);
++		set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000);
++		udelay(1);
++		set_gpio_bit(ice, WM8766_SPI_CLK, 1);
++		udelay(1);
++		data <<= 1;
++	}
++}
++
++static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg,
++			     unsigned int data)
++{
++	unsigned int block;
++
++	snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD|
++					WM8766_SPI_CLK|WM8766_SPI_ML);
++	snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD|
++					WM8766_SPI_CLK|WM8766_SPI_ML));
++	/* latch must be low when writing */
++	set_gpio_bit(ice, WM8766_SPI_ML, 0);
++	block = (reg << 9) | (data & 0x1ff);
++	wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */
++	/* release latch */
++	set_gpio_bit(ice, WM8766_SPI_ML, 1);
++	udelay(1);
++	/* restore */
++	snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
++	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
++}
++
++
++/*
++ * serial interface for ak4396 - only writing supported, no readback
++ */
++
++static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data)
++{
++	int i;
++	for (i = 0; i < 16; i++) {
++		set_gpio_bit(ice, AK4396_CCLK, 0);
++		udelay(1);
++		set_gpio_bit(ice, AK4396_CDTI, data & 0x8000);
++		udelay(1);
++		set_gpio_bit(ice, AK4396_CCLK, 1);
++		udelay(1);
++		data <<= 1;
++	}
++}
++
++static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg,
++			 unsigned int data)
++{
++	unsigned int block;
++
++	snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI);
++	snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI));
++	/* latch must be low when writing */
++	set_gpio_bit(ice, AK4396_CSN, 0); 
++	block =  ((AK4396_ADDR & 0x03) << 14) | (1 << 13) |
++			((reg & 0x1f) << 8) | (data & 0xff);
++	ak4396_send_word(ice, block); /* REGISTER ADDRESS */
++	/* release latch */
++	set_gpio_bit(ice, AK4396_CSN, 1);
++	udelay(1);
++	/* restore */
++	snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
++	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
++}
++
++
++/*
++ * ak4396 mixers
++ */
++
++
++
++/*
++ * DAC volume attenuation mixer control (-64dB to 0dB)
++ */
++
++static int ak4396_dac_vol_info(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = 2;
++	uinfo->value.integer.min = 0;   /* mute */
++	uinfo->value.integer.max = 0xFF; /* linear */
++	return 0;
++}
++
++static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol,
++			      struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int i;
++	
++	for (i = 0; i < 2; i++)
++		ucontrol->value.integer.value[i] = spec->vol[i];
++
++	return 0;
++}
++
++static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int i;
++	int change = 0;
++	
++	mutex_lock(&ice->gpio_mutex);
++	for (i = 0; i < 2; i++) {
++		if (ucontrol->value.integer.value[i] != spec->vol[i]) {
++			spec->vol[i] = ucontrol->value.integer.value[i];
++			ak4396_write(ice, AK4396_LCH_ATT + i,
++				     spec->vol[i] & 0xff);
++			change = 1;
++		}
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
++
++static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
++    {
++	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++		SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++	.name = "Front Playback Volume",
++	.info = ak4396_dac_vol_info,
++	.get = ak4396_dac_vol_get,
++	.put = ak4396_dac_vol_put,
++	.tlv = { .p = db_scale_wm_dac },
++    },
++};
++
++
++/* --------------- */
++
++/*
++ * Logarithmic volume values for WM87*6
++ * Computed as 20 * Log10(255 / x)
++ */
++static const 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
++
++
++#define DAC_0dB	0xff
++#define DAC_RES	128
++#define DAC_MIN	(DAC_0dB - DAC_RES)
++
++
++static void wm_set_vol(struct snd_ice1712 *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 = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
++				& WM_VOL_MAX;
++		nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
++	}
++	
++	wm_put(ice, index, nvol);
++	wm_put_nocache(ice, index, 0x100 | nvol);
++}
++
++static void wm8766_set_vol(struct snd_ice1712 *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 = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128)
++				& WM_VOL_MAX;
++		nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff;
++	}
++
++	wm8766_spi_write(ice, index, (0x0100 | nvol));
++}
++
++
++/*
++ * DAC volume attenuation mixer control (-64dB to 0dB)
++ */
++
++static int wm_dac_vol_info(struct snd_kcontrol *kcontrol,
++			   struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = 2;
++	uinfo->value.integer.min = 0;	/* mute */
++	uinfo->value.integer.max = DAC_RES;	/* 0dB, 0.5dB step */
++	return 0;
++}
++
++static int wm_dac_vol_get(struct snd_kcontrol *kcontrol,
++			  struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int i;
++
++	for (i = 0; i < 2; i++)
++		ucontrol->value.integer.value[i] =
++			spec->vol[2 + i] & ~WM_VOL_MUTE;
++	return 0;
++}
++
++static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int i, idx, change = 0;
++
++	mutex_lock(&ice->gpio_mutex);
++	for (i = 0; i < 2; i++) {
++		if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) {
++			idx = WM_DAC_ATTEN_L + i;
++			spec->vol[2 + i] &= WM_VOL_MUTE;
++			spec->vol[2 + i] |= ucontrol->value.integer.value[i];
++			wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]);
++			change = 1;
++		}
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++
++/*
++ * WM8766 DAC volume attenuation mixer control
++ */
++static int wm8766_vol_info(struct snd_kcontrol *kcontrol,
++			   struct snd_ctl_elem_info *uinfo)
++{
++	int voices = kcontrol->private_value >> 8;
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = voices;
++	uinfo->value.integer.min = 0;		/* mute */
++	uinfo->value.integer.max = DAC_RES;	/* 0dB */
++	return 0;
++}
++
++static int wm8766_vol_get(struct snd_kcontrol *kcontrol,
++			  struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	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] = spec->vol[ofs + i];
++	return 0;
++}
++
++static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int i, idx, ofs, voices;
++	int change = 0;
++
++	voices = kcontrol->private_value >> 8;
++	ofs = kcontrol->private_value & 0xff;
++	mutex_lock(&ice->gpio_mutex);
++	for (i = 0; i < voices; i++) {
++		if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) {
++			idx = WM8766_LDA1 + ofs + i;
++			spec->vol[ofs + i] &= WM_VOL_MUTE;
++			spec->vol[ofs + i] |= ucontrol->value.integer.value[i];
++			wm8766_set_vol(ice, idx,
++				       spec->vol[ofs + i], spec->master[i]);
++			change = 1;
++		}
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++/*
++ * Master volume attenuation mixer control / applied to WM8776+WM8766
++ */
++static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
++			      struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = 2;
++	uinfo->value.integer.min = 0;
++	uinfo->value.integer.max = DAC_RES;
++	return 0;
++}
++
++static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
++			     struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int i;
++	for (i = 0; i < 2; i++)
++		ucontrol->value.integer.value[i] = spec->master[i];
++	return 0;
++}
++
++static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
++			     struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	struct prodigy_hifi_spec *spec = ice->spec;
++	int ch, change = 0;
++
++	mutex_lock(&ice->gpio_mutex);
++	for (ch = 0; ch < 2; ch++) {
++		if (ucontrol->value.integer.value[ch] != spec->master[ch]) {
++			spec->master[ch] = ucontrol->value.integer.value[ch];
++
++			/* Apply to front DAC */
++			wm_set_vol(ice, WM_DAC_ATTEN_L + ch,
++				   spec->vol[2 + ch], spec->master[ch]);
++
++			wm8766_set_vol(ice, WM8766_LDA1 + ch,
++				       spec->vol[0 + ch], spec->master[ch]);
++
++			wm8766_set_vol(ice, WM8766_LDA2 + ch,
++				       spec->vol[4 + ch], spec->master[ch]);
++
++			wm8766_set_vol(ice, WM8766_LDA3 + ch,
++				       spec->vol[6 + ch], spec->master[ch]);
++			change = 1;
++		}
++	}
++	mutex_unlock(&ice->gpio_mutex);	
++	return change;
++}
++
++
++/* KONSTI */
++
++static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
++				struct snd_ctl_elem_info *uinfo)
++{
++	static char* texts[32] = {
++		"NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2,
++		WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3,
++		WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN4,
++		WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4,
++		WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4,
++		WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN5,
++		WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5,
++		WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5,
++		WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5,
++		WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5,
++		WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
++		WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
++		WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5,
++		WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5
++	};
++
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
++	uinfo->count = 1;
++	uinfo->value.enumerated.items = 32;
++	if (uinfo->value.enumerated.item > 31)
++		uinfo->value.enumerated.item = 31;
++	strcpy(uinfo->value.enumerated.name,
++	       texts[uinfo->value.enumerated.item]);
++	return 0;
++}
++
++static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++
++	mutex_lock(&ice->gpio_mutex);
++	ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
++	mutex_unlock(&ice->gpio_mutex);
++	return 0;
++}
++
++static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	unsigned short oval, nval;
++	int change = 0;
++
++	mutex_lock(&ice->gpio_mutex);
++	oval = wm_get(ice, WM_ADC_MUX);
++	nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
++	if (nval != oval) {
++		wm_put(ice, WM_ADC_MUX, nval);
++		change = 1;
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++/* KONSTI */
++
++/*
++ * ADC gain mixer control (-64dB to 0dB)
++ */
++
++#define ADC_0dB	0xcf
++#define ADC_RES	128
++#define ADC_MIN	(ADC_0dB - ADC_RES)
++
++static int wm_adc_vol_info(struct snd_kcontrol *kcontrol,
++			   struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = 2;
++	uinfo->value.integer.min = 0;	/* mute (-64dB) */
++	uinfo->value.integer.max = ADC_RES;	/* 0dB, 0.5dB step */
++	return 0;
++}
++
++static int wm_adc_vol_get(struct snd_kcontrol *kcontrol,
++			  struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	unsigned short val;
++	int i;
++
++	mutex_lock(&ice->gpio_mutex);
++	for (i = 0; i < 2; i++) {
++		val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
++		val = val > ADC_MIN ? (val - ADC_MIN) : 0;
++		ucontrol->value.integer.value[i] = val;
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return 0;
++}
++
++static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
++			  struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	unsigned short ovol, nvol;
++	int i, idx, change = 0;
++
++	mutex_lock(&ice->gpio_mutex);
++	for (i = 0; i < 2; i++) {
++		nvol = ucontrol->value.integer.value[i];
++		nvol = nvol ? (nvol + ADC_MIN) : 0;
++		idx  = WM_ADC_ATTEN_L + i;
++		ovol = wm_get(ice, idx) & 0xff;
++		if (ovol != nvol) {
++			wm_put(ice, idx, nvol);
++			change = 1;
++		}
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++/*
++ * ADC input mux mixer control
++ */
++#define wm_adc_mux_info		snd_ctl_boolean_mono_info
++
++static int wm_adc_mux_get(struct snd_kcontrol *kcontrol,
++			  struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	int bit = kcontrol->private_value;
++
++	mutex_lock(&ice->gpio_mutex);
++	ucontrol->value.integer.value[0] =
++		(wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
++	mutex_unlock(&ice->gpio_mutex);
++	return 0;
++}
++
++static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
++			  struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	int bit = kcontrol->private_value;
++	unsigned short oval, nval;
++	int change;
++
++	mutex_lock(&ice->gpio_mutex);
++	nval = oval = wm_get(ice, WM_ADC_MUX);
++	if (ucontrol->value.integer.value[0])
++		nval |= (1 << bit);
++	else
++		nval &= ~(1 << bit);
++	change = nval != oval;
++	if (change) {
++		wm_put(ice, WM_ADC_MUX, nval);
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return 0;
++}
++
++/*
++ * Analog bypass (In -> Out)
++ */
++#define wm_bypass_info		snd_ctl_boolean_mono_info
++
++static int wm_bypass_get(struct snd_kcontrol *kcontrol,
++			 struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++
++	mutex_lock(&ice->gpio_mutex);
++	ucontrol->value.integer.value[0] =
++		(wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
++	mutex_unlock(&ice->gpio_mutex);
++	return 0;
++}
++
++static int wm_bypass_put(struct snd_kcontrol *kcontrol,
++			 struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	unsigned short val, oval;
++	int change = 0;
++
++	mutex_lock(&ice->gpio_mutex);
++	val = oval = wm_get(ice, WM_OUT_MUX);
++	if (ucontrol->value.integer.value[0])
++		val |= 0x04;
++	else
++		val &= ~0x04;
++	if (val != oval) {
++		wm_put(ice, WM_OUT_MUX, val);
++		change = 1;
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++/*
++ * Left/Right swap
++ */
++#define wm_chswap_info		snd_ctl_boolean_mono_info
++
++static int wm_chswap_get(struct snd_kcontrol *kcontrol,
++			 struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++
++	mutex_lock(&ice->gpio_mutex);
++	ucontrol->value.integer.value[0] =
++			(wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
++	mutex_unlock(&ice->gpio_mutex);
++	return 0;
++}
++
++static int wm_chswap_put(struct snd_kcontrol *kcontrol,
++			 struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
++	unsigned short val, oval;
++	int change = 0;
++
++	mutex_lock(&ice->gpio_mutex);
++	oval = wm_get(ice, WM_DAC_CTRL1);
++	val = oval & 0x0f;
++	if (ucontrol->value.integer.value[0])
++		val |= 0x60;
++	else
++		val |= 0x90;
++	if (val != oval) {
++		wm_put(ice, WM_DAC_CTRL1, val);
++		wm_put_nocache(ice, WM_DAC_CTRL1, val);
++		change = 1;
++	}
++	mutex_unlock(&ice->gpio_mutex);
++	return change;
++}
++
++
++/*
++ * mixers
++ */
++
++static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = {
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "Master Playback Volume",
++		.info = wm_master_vol_info,
++		.get = wm_master_vol_get,
++		.put = wm_master_vol_put,
++		.tlv = { .p = db_scale_wm_dac }
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "Front Playback Volume",
++		.info = wm_dac_vol_info,
++		.get = wm_dac_vol_get,
++		.put = wm_dac_vol_put,
++		.tlv = { .p = db_scale_wm_dac },
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "Rear Playback Volume",
++		.info = wm8766_vol_info,
++		.get = wm8766_vol_get,
++		.put = wm8766_vol_put,
++		.private_value = (2 << 8) | 0,
++		.tlv = { .p = db_scale_wm_dac },
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "Center Playback Volume",
++		.info = wm8766_vol_info,
++		.get = wm8766_vol_get,
++		.put = wm8766_vol_put,
++		.private_value = (1 << 8) | 4,
++		.tlv = { .p = db_scale_wm_dac }
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "LFE Playback Volume",
++		.info = wm8766_vol_info,
++		.get = wm8766_vol_get,
++		.put = wm8766_vol_put,
++		.private_value = (1 << 8) | 5,
++		.tlv = { .p = db_scale_wm_dac }
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "Side Playback Volume",
++		.info = wm8766_vol_info,
++		.get = wm8766_vol_get,
++		.put = wm8766_vol_put,
++		.private_value = (2 << 8) | 6,
++		.tlv = { .p = db_scale_wm_dac },
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
++		.name = "Capture Volume",
++		.info = wm_adc_vol_info,
++		.get = wm_adc_vol_get,
++		.put = wm_adc_vol_put,
++		.tlv = { .p = db_scale_wm_dac },
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "CD Capture Switch",
++		.info = wm_adc_mux_info,
++		.get = wm_adc_mux_get,
++		.put = wm_adc_mux_put,
++		.private_value = 0,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Line Capture Switch",
++		.info = wm_adc_mux_info,
++		.get = wm_adc_mux_get,
++		.put = wm_adc_mux_put,
++		.private_value = 1,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Analog Bypass Switch",
++		.info = wm_bypass_info,
++		.get = wm_bypass_get,
++		.put = wm_bypass_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Swap Output Channels",
++		.info = wm_chswap_info,
++		.get = wm_chswap_get,
++		.put = wm_chswap_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Analog Capture Source",
++		.info = wm_adc_mux_enum_info,
++		.get = wm_adc_mux_enum_get,
++		.put = wm_adc_mux_enum_put,
++	},
++};
++
++/*
++ * WM codec registers
++ */
++static void wm_proc_regs_write(struct snd_info_entry *entry,
++			       struct snd_info_buffer *buffer)
++{
++	struct snd_ice1712 *ice = entry->private_data;
++	char line[64];
++	unsigned int reg, val;
++	mutex_lock(&ice->gpio_mutex);
++	while (!snd_info_get_line(buffer, line, sizeof(line))) {
++		if (sscanf(line, "%x %x", &reg, &val) != 2)
++			continue;
++		if (reg <= 0x17 && val <= 0xffff)
++			wm_put(ice, reg, val);
++	}
++	mutex_unlock(&ice->gpio_mutex);
++}
++
++static void wm_proc_regs_read(struct snd_info_entry *entry,
++			      struct snd_info_buffer *buffer)
++{
++	struct snd_ice1712 *ice = entry->private_data;
++	int reg, val;
++
++	mutex_lock(&ice->gpio_mutex);
++	for (reg = 0; reg <= 0x17; reg++) {
++		val = wm_get(ice, reg);
++		snd_iprintf(buffer, "%02x = %04x\n", reg, val);
++	}
++	mutex_unlock(&ice->gpio_mutex);
++}
++
++static void wm_proc_init(struct snd_ice1712 *ice)
++{
++	struct snd_info_entry *entry;
++	if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
++		snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
++		entry->mode |= S_IWUSR;
++		entry->c.text.write = wm_proc_regs_write;
++	}
++}
++
++static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
++{
++	unsigned int i;
++	int err;
++
++	for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) {
++		err = snd_ctl_add(ice->card,
++				  snd_ctl_new1(&prodigy_hifi_controls[i], ice));
++		if (err < 0)
++			return err;
++	}
++
++	wm_proc_init(ice);
++
++	return 0;
++}
++
++static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
++{
++	unsigned int i;
++	int err;
++
++	for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) {
++		err = snd_ctl_add(ice->card,
++				  snd_ctl_new1(&prodigy_hd2_controls[i], ice));
++		if (err < 0)
++			return err;
++	}
++
++	wm_proc_init(ice);
++
++	return 0;
++}
++
++
++/*
++ * initialize the chip
++ */
++static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
++{
++	static unsigned short wm_inits[] = {
++		/* These come first to reduce init pop noise */
++		WM_ADC_MUX,	0x0003,	/* ADC mute */
++		/* 0x00c0 replaced by 0x0003 */
++		
++		WM_DAC_MUTE,	0x0001,	/* DAC softmute */
++		WM_DAC_CTRL1,	0x0000,	/* DAC mute */
++
++		WM_POWERDOWN,	0x0008,	/* All power-up except HP */
++		WM_RESET,	0x0000,	/* reset */
++	};
++	static unsigned short wm_inits2[] = {
++		WM_MASTER_CTRL,  0x0022, /* 256fs, slave mode */
++		WM_DAC_INT,	0x0022,	/* I2S, normal polarity, 24bit */
++		WM_ADC_INT,	0x0022,	/* I2S, normal polarity, 24bit */
++		WM_DAC_CTRL1,	0x0090,	/* DAC L/R */
++		WM_OUT_MUX,	0x0001,	/* OUT DAC */
++		WM_HP_ATTEN_L,	0x0179,	/* HP 0dB */
++		WM_HP_ATTEN_R,	0x0179,	/* HP 0dB */
++		WM_DAC_ATTEN_L,	0x0000,	/* DAC 0dB */
++		WM_DAC_ATTEN_L,	0x0100,	/* DAC 0dB */
++		WM_DAC_ATTEN_R,	0x0000,	/* DAC 0dB */
++		WM_DAC_ATTEN_R,	0x0100,	/* DAC 0dB */
++		WM_PHASE_SWAP,	0x0000,	/* phase normal */
++#if 0
++		WM_DAC_MASTER,	0x0100,	/* DAC master muted */
++#endif
++		WM_DAC_CTRL2,	0x0000,	/* no deemphasis, no ZFLG */
++		WM_ADC_ATTEN_L,	0x0000,	/* ADC muted */
++		WM_ADC_ATTEN_R,	0x0000,	/* ADC muted */
++#if 1
++		WM_ALC_CTRL1,	0x007b,	/* */
++		WM_ALC_CTRL2,	0x0000,	/* */
++		WM_ALC_CTRL3,	0x0000,	/* */
++		WM_NOISE_GATE,	0x0000,	/* */
++#endif
++		WM_DAC_MUTE,	0x0000,	/* DAC unmute */
++		WM_ADC_MUX,	0x0003,	/* ADC unmute, both CD/Line On */
++	};
++	static unsigned short wm8766_inits[] = {
++		WM8766_RESET,	   0x0000,
++		WM8766_DAC_CTRL,	0x0120,
++		WM8766_INT_CTRL,	0x0022, /* I2S Normal Mode, 24 bit */
++		WM8766_DAC_CTRL2,       0x0001,
++		WM8766_DAC_CTRL3,       0x0080,
++		WM8766_LDA1,	    0x0100,
++		WM8766_LDA2,	    0x0100,
++		WM8766_LDA3,	    0x0100,
++		WM8766_RDA1,	    0x0100,
++		WM8766_RDA2,	    0x0100,
++		WM8766_RDA3,	    0x0100,
++		WM8766_MUTE1,	   0x0000,
++		WM8766_MUTE2,	   0x0000,
++	};
++
++	struct prodigy_hifi_spec *spec;
++	unsigned int i;
++
++	ice->vt1720 = 0;
++	ice->vt1724 = 1;
++
++	ice->num_total_dacs = 8;
++	ice->num_total_adcs = 1;
++
++	/* HACK - use this as the SPDIF source.
++	* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
++	*/
++	ice->gpio.saved[0] = 0;
++	/* to remeber the register values */
++
++	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
++	if (! ice->akm)
++		return -ENOMEM;
++	ice->akm_codecs = 1;
++
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
++	/* initialize WM8776 codec */
++	for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
++		wm_put(ice, wm_inits[i], wm_inits[i+1]);
++	schedule_timeout_uninterruptible(1);
++	for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
++		wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
++
++	/* initialize WM8766 codec */
++	for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
++		wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]);
++
++
++	return 0;
++}
++
++
++/*
++ * initialize the chip
++ */
++static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
++{
++	static unsigned short ak4396_inits[] = {
++		AK4396_CTRL1,	   0x87,   /* I2S Normal Mode, 24 bit */
++		AK4396_CTRL2,	   0x02,
++		AK4396_CTRL3,	   0x00, 
++		AK4396_LCH_ATT,	 0x00,
++		AK4396_RCH_ATT,	 0x00,
++	};
++
++	struct prodigy_hifi_spec *spec;
++	unsigned int i;
++
++	ice->vt1720 = 0;
++	ice->vt1724 = 1;
++
++	ice->num_total_dacs = 1;
++	ice->num_total_adcs = 1;
++
++	/* HACK - use this as the SPDIF source.
++	* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
++	*/
++	ice->gpio.saved[0] = 0;
++	/* to remeber the register values */
++
++	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
++	if (! ice->akm)
++		return -ENOMEM;
++	ice->akm_codecs = 1;
++
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
++	/* initialize ak4396 codec */
++	/* reset codec */
++	ak4396_write(ice, AK4396_CTRL1, 0x86);
++	msleep(100);
++	ak4396_write(ice, AK4396_CTRL1, 0x87);
++			
++	for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
++		ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
++
++	return 0;
++}
++
++
++static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
++	0x4b,   /* 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 */
++};
++
++static unsigned char prodigyhd2_eeprom[] __devinitdata = {
++	0x4b,   /* 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 */
++};
++
++static unsigned char fortissimo4_eeprom[] __devinitdata = {
++	0x43,   /* SYSCONF: clock 512, ADC, 4DACs */	
++	0x80,   /* ACLINK: I2S */
++	0xfc,   /* I2S: vol, 96k, 24bit, 192k */
++	0xc1,   /* SPDIF: out-en, out-int */
++	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 */
++};
++
++/* entry point */
++struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = {
++	{
++		.subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI,
++		.name = "Audiotrak Prodigy 7.1 HiFi",
++		.model = "prodigy71hifi",
++		.chip_init = prodigy_hifi_init,
++		.build_controls = prodigy_hifi_add_controls,
++		.eeprom_size = sizeof(prodigy71hifi_eeprom),
++		.eeprom_data = prodigy71hifi_eeprom,
++		.driver = "Prodigy71HIFI",
++	},
++	{
++	.subvendor = VT1724_SUBDEVICE_PRODIGY_HD2,
++	.name = "Audiotrak Prodigy HD2",
++	.model = "prodigyhd2",
++	.chip_init = prodigy_hd2_init,
++	.build_controls = prodigy_hd2_add_controls,
++	.eeprom_size = sizeof(prodigyhd2_eeprom),
++	.eeprom_data = prodigyhd2_eeprom,
++	.driver = "Prodigy71HD2",
++	},
++	{
++		.subvendor = VT1724_SUBDEVICE_FORTISSIMO4,
++		.name = "Hercules Fortissimo IV",
++		.model = "fortissimo4",
++		.chip_init = prodigy_hifi_init,
++		.build_controls = prodigy_hifi_add_controls,
++		.eeprom_size = sizeof(fortissimo4_eeprom),
++		.eeprom_data = fortissimo4_eeprom,
++		.driver = "Fortissimo4",
++	},
++	{ } /* terminator */
++};
++
+diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h
+new file mode 100644
+index 0000000..a4415d4
+--- /dev/null
++++ b/sound/pci/ice1712/prodigy_hifi.h
+@@ -0,0 +1,38 @@
++#ifndef __SOUND_PRODIGY_HIFI_H
++#define __SOUND_PRODIGY_HIFI_H
++
++/*
++ *   ALSA driver for VIA VT1724 (Envy24HT)
++ *
++ *   Lowlevel functions for Audiotrak Prodigy Hifi
++ *
++ *	Copyright (c) 2004 Takashi Iwai <tiwai at suse.de>
++ *
++ *   This program 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 program 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
++ *
++ */      
++
++#define PRODIGY_HIFI_DEVICE_DESC 	       "{Audiotrak,Prodigy 7.1 HIFI},"\
++                                           "{Audiotrak Prodigy HD2},"\
++                                           "{Hercules Fortissimo IV},"
++
++#define VT1724_SUBDEVICE_PRODIGY_HIFI	0x38315441	/* PRODIGY 7.1 HIFI */
++#define VT1724_SUBDEVICE_PRODIGY_HD2	0x37315441	/* PRODIGY HD2 */
++#define VT1724_SUBDEVICE_FORTISSIMO4	0x81160100	/* Fortissimo IV */
++
++
++extern struct snd_ice1712_card_info  snd_vt1724_prodigy_hifi_cards[];
++
++#endif /* __SOUND_PRODIGY_HIFI_H */
+diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
+index d18a31e..ddd5fc8 100644
+--- a/sound/pci/ice1712/revo.c
++++ b/sound/pci/ice1712/revo.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -33,6 +32,12 @@
+ #include "envy24ht.h"
+ #include "revo.h"
+ 
++/* a non-standard I2C device for revo51 */
++struct revo51_spec {
++	struct snd_i2c_device *dev;
++	struct snd_pt2258 *pt2258;
++} revo51;
++
+ static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
+ {
+ 	/* assert PRST# to converters; MT05 bit 7 */
+@@ -153,8 +158,14 @@ static struct snd_i2c_bit_ops revo51_bit_ops = {
+ static int revo51_i2c_init(struct snd_ice1712 *ice,
+ 			   struct snd_pt2258 *pt)
+ {
++	struct revo51_spec *spec;
+ 	int err;
+ 
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
+ 	/* create the I2C bus */
+ 	err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c);
+ 	if (err < 0)
+@@ -164,15 +175,14 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
+ 	ice->i2c->hw_ops.bit = &revo51_bit_ops;
+ 
+ 	/* create the I2C device */
+-	err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40,
+-				    &ice->spec.revo51.dev);
++	err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, &spec->dev);
+ 	if (err < 0)
+ 		return err;
+ 
+ 	pt->card = ice->card;
+ 	pt->i2c_bus = ice->i2c;
+-	pt->i2c_dev = ice->spec.revo51.dev;
+-	ice->spec.revo51.pt2258 = pt;
++	pt->i2c_dev = spec->dev;
++	spec->pt2258 = pt;
+ 
+ 	snd_pt2258_reset(pt);
+ 
+@@ -556,6 +566,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
+ 
+ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
+ {
++	struct revo51_spec *spec;
+ 	int err;
+ 
+ 	switch (ice->eeprom.subvendor) {
+@@ -568,7 +579,8 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
+ 		err = snd_ice1712_akm4xxx_build_controls(ice);
+ 		if (err < 0)
+ 			return err;
+-		err = snd_pt2258_build_controls(ice->spec.revo51.pt2258);
++		spec = ice->spec;
++		err = snd_pt2258_build_controls(spec->pt2258);
+ 		if (err < 0)
+ 			return err;
+ 		break;
+diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c
+new file mode 100644
+index 0000000..69673b9
+--- /dev/null
++++ b/sound/pci/ice1712/se.c
+@@ -0,0 +1,774 @@
++/*
++ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
++ *
++ *   Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI
++ *
++ *	Copyright (c) 2007 Shin-ya Okada  sh_okada(at)d4.dion.ne.jp
++ *                                        (at) -> @
++ *
++ *   This program 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 program 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 <asm/io.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <sound/core.h>
++#include <sound/tlv.h>
++
++#include "ice1712.h"
++#include "envy24ht.h"
++#include "se.h"
++
++struct se_spec {
++	struct {
++		unsigned char ch1, ch2;
++	} vol[8];
++};
++
++/****************************************************************************/
++/*  ONKYO WAVIO SE-200PCI                                                   */
++/****************************************************************************/
++/*
++ *  system configuration ICE_EEP2_SYSCONF=0x4b
++ *    XIN1 49.152MHz
++ *    not have UART
++ *    one stereo ADC and a S/PDIF receiver connected
++ *    four stereo DACs connected
++ *
++ *  AC-Link configuration ICE_EEP2_ACLINK=0x80
++ *    use I2C, not use AC97
++ *
++ *  I2S converters feature ICE_EEP2_I2S=0x78
++ *    I2S codec has no volume/mute control feature
++ *    I2S codec supports 96KHz and 192KHz
++ *    I2S codec 24bits
++ *
++ *  S/PDIF configuration ICE_EEP2_SPDIF=0xc3
++ *    Enable integrated S/PDIF transmitter
++ *    internal S/PDIF out implemented
++ *    S/PDIF is stereo
++ *    External S/PDIF out implemented
++ *
++ *
++ * ** connected chips **
++ *
++ *  WM8740
++ *      A 2ch-DAC of main outputs.
++ *      It setuped as I2S mode by wire, so no way to setup from software.
++ *      The sample-rate are automatically changed. 
++ *          ML/I2S (28pin) --------+
++ *          MC/DM1 (27pin) -- 5V   |
++ *          MD/DM0 (26pin) -- GND  |
++ *          MUTEB  (25pin) -- NC   |
++ *          MODE   (24pin) -- GND  |
++ *          CSBIW  (23pin) --------+
++ *                                 |
++ *          RSTB   (22pin) --R(1K)-+
++ *      Probably it reduce the noise from the control line.
++ *
++ *  WM8766
++ *      A 6ch-DAC for surrounds.
++ *      It's control wire was connected to GPIOxx (3-wire serial interface)
++ *          ML/I2S (11pin) -- GPIO18
++ *          MC/IWL (12pin) -- GPIO17
++ *          MD/DM  (13pin) -- GPIO16
++ *          MUTE   (14pin) -- GPIO01
++ *
++ *  WM8776
++ *     A 2ch-ADC(with 10ch-selector) plus 2ch-DAC.
++ *     It's control wire was connected to SDA/SCLK (2-wire serial interface)
++ *          MODE (16pin) -- R(1K) -- GND
++ *          CE   (17pin) -- R(1K) -- GND  2-wire mode (address=0x34)
++ *          DI   (18pin) -- SDA
++ *          CL   (19pin) -- SCLK
++ *
++ *
++ * ** output pins and device names **
++ *
++ *   7.1ch name -- output connector color -- device (-D option)
++ *
++ *      FRONT 2ch                  -- green  -- plughw:0,0
++ *      CENTER(Lch) SUBWOOFER(Rch) -- black  -- plughw:0,2,0
++ *      SURROUND 2ch               -- orange -- plughw:0,2,1
++ *      SURROUND BACK 2ch          -- white  -- plughw:0,2,2
++ *
++ */
++
++
++/****************************************************************************/
++/*  WM8740 interface                                                        */
++/****************************************************************************/
++
++static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice)
++{
++	/* nothing to do */
++}
++
++
++static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice,
++						unsigned int rate)
++{
++	/* nothing to do */
++}
++
++
++/****************************************************************************/
++/*  WM8766 interface                                                        */
++/****************************************************************************/
++
++static void se200pci_WM8766_write(struct snd_ice1712 *ice,
++					unsigned int addr, unsigned int data)
++{
++	unsigned int st;
++	unsigned int bits;
++	int i;
++	const unsigned int DATA  = 0x010000;
++	const unsigned int CLOCK = 0x020000;
++	const unsigned int LOAD  = 0x040000;
++	const unsigned int ALL_MASK = (DATA | CLOCK | LOAD);
++
++	snd_ice1712_save_gpio_status(ice);
++
++	st = ((addr & 0x7f) << 9) | (data & 0x1ff);
++	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK);
++	snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK);
++	bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK;
++
++	snd_ice1712_gpio_write(ice, bits);
++	for (i = 0; i < 16; i++) {
++		udelay(1);
++		bits &= ~CLOCK;
++		st = (st << 1);
++		if (st & 0x10000)
++			bits |= DATA;
++		else
++			bits &= ~DATA;
++
++		snd_ice1712_gpio_write(ice, bits);
++
++		udelay(1);
++		bits |= CLOCK;
++		snd_ice1712_gpio_write(ice, bits);
++	}
++
++	udelay(1);
++	bits |= LOAD;
++	snd_ice1712_gpio_write(ice, bits);
++
++	udelay(1);
++	bits |= (DATA | CLOCK);
++	snd_ice1712_gpio_write(ice, bits);
++
++	snd_ice1712_restore_gpio_status(ice);
++}
++
++static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
++					unsigned int vol1, unsigned int vol2)
++{
++	switch (ch) {
++	case 0:
++		se200pci_WM8766_write(ice, 0x000, vol1);
++		se200pci_WM8766_write(ice, 0x001, vol2 | 0x100);
++		break;
++	case 1:
++		se200pci_WM8766_write(ice, 0x004, vol1);
++		se200pci_WM8766_write(ice, 0x005, vol2 | 0x100);
++		break;
++	case 2:
++		se200pci_WM8766_write(ice, 0x006, vol1);
++		se200pci_WM8766_write(ice, 0x007, vol2 | 0x100);
++		break;
++	}
++}
++
++static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice)
++{
++	se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
++	udelay(10);
++
++	se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */
++	se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */
++	se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */
++
++	se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */
++	se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
++	se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */
++	se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */
++	se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */
++
++	se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */
++	se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */
++}
++
++static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice,
++					unsigned int rate)
++{
++	if (rate > 96000)
++		se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */
++	else
++		se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */
++}
++
++
++/****************************************************************************/
++/*  WM8776 interface                                                        */
++/****************************************************************************/
++
++static void se200pci_WM8776_write(struct snd_ice1712 *ice,
++					unsigned int addr, unsigned int data)
++{
++	unsigned int val;
++
++	val = (addr << 9) | data;
++	snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff);
++}
++
++
++static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice,
++					unsigned int vol1, unsigned int vol2)
++{
++	se200pci_WM8776_write(ice, 0x03, vol1);
++	se200pci_WM8776_write(ice, 0x04, vol2 | 0x100);
++}
++
++static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
++					unsigned int vol1, unsigned int vol2)
++{
++	se200pci_WM8776_write(ice, 0x0e, vol1);
++	se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
++}
++
++static const char *se200pci_sel[] = {
++	"LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
++};
++
++static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
++					       unsigned int sel)
++{
++	static unsigned char vals[] = {
++		/* LINE, CD, MIC, ALL, GND */
++		0x10, 0x04, 0x08, 0x1c, 0x03
++	};
++	if (sel > 4)
++		sel = 4;
++	se200pci_WM8776_write(ice, 0x15, vals[sel]);
++}
++
++static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
++{
++	/* AFL -- After Fader Listening */
++	if (afl)
++		se200pci_WM8776_write(ice, 0x16, 0x005);
++	else
++		se200pci_WM8776_write(ice, 0x16, 0x001);
++}
++
++static const char *se200pci_agc[] = {
++	"Off", "LimiterMode", "ALCMode", NULL
++};
++
++static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
++{
++	/* AGC -- Auto Gain Control of the input */
++	switch (agc) {
++	case 0:
++		se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */
++		break;
++	case 1:
++		se200pci_WM8776_write(ice, 0x10, 0x07b);
++		se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */
++		break;
++	case 2:
++		se200pci_WM8776_write(ice, 0x10, 0x1fb);
++		se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */
++		break;
++	}
++}
++
++static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice)
++{
++	int i;
++	static unsigned short __devinitdata default_values[] = {
++		0x100, 0x100, 0x100,
++		0x100, 0x100, 0x100,
++		0x000, 0x090, 0x000, 0x000,
++		0x022, 0x022, 0x022,
++		0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
++		0x032, 0x000, 0x0a6, 0x001, 0x001
++	};
++
++	se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */
++	/* ADC and DAC interface is I2S 24bits mode */
++ 	/* The sample-rate are automatically changed */
++	udelay(10);
++	/* BUT my board can not do reset all, so I load all by manually. */
++	for (i = 0; i < ARRAY_SIZE(default_values); i++)
++		se200pci_WM8776_write(ice, i, default_values[i]);
++
++	se200pci_WM8776_set_input_selector(ice, 0);
++	se200pci_WM8776_set_afl(ice, 0);
++	se200pci_WM8776_set_agc(ice, 0);
++	se200pci_WM8776_set_input_volume(ice, 0, 0);
++	se200pci_WM8776_set_output_volume(ice, 0, 0);
++
++	/* head phone mute and power down */
++	se200pci_WM8776_write(ice, 0x00, 0);
++	se200pci_WM8776_write(ice, 0x01, 0);
++	se200pci_WM8776_write(ice, 0x02, 0x100);
++	se200pci_WM8776_write(ice, 0x0d, 0x080);
++}
++
++static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice,
++						unsigned int rate)
++{
++	/* nothing to do */
++}
++
++
++/****************************************************************************/
++/*  runtime interface                                                       */
++/****************************************************************************/
++
++static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
++{
++	se200pci_WM8740_set_pro_rate(ice, rate);
++	se200pci_WM8766_set_pro_rate(ice, rate);
++	se200pci_WM8776_set_pro_rate(ice, rate);
++}
++
++struct se200pci_control {
++	char *name;
++	enum {
++		WM8766,
++		WM8776in,
++		WM8776out,
++		WM8776sel,
++		WM8776agc,
++		WM8776afl
++	} target;
++	enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
++	int ch;
++	const char **member;
++	const char *comment;
++};
++
++static const struct se200pci_control se200pci_cont[] = {
++	{
++		.name = "Front Playback Volume",
++		.target = WM8776out,
++		.type = VOLUME1,
++		.comment = "Front(green)"
++	},
++	{
++		.name = "Side Playback Volume",
++		.target = WM8766,
++		.type = VOLUME1,
++		.ch = 1,
++		.comment = "Surround(orange)"
++	},
++	{
++		.name = "Surround Playback Volume",
++		.target = WM8766,
++		.type = VOLUME1,
++		.ch = 2,
++		.comment = "SurroundBack(white)"
++	},
++	{
++		.name = "CLFE Playback Volume",
++		.target = WM8766,
++		.type = VOLUME1,
++		.ch = 0,
++		.comment = "Center(Lch)&SubWoofer(Rch)(black)"
++	},
++	{
++		.name = "Capture Volume",
++		.target = WM8776in,
++		.type = VOLUME2
++	},
++	{
++		.name = "Capture Select",
++		.target = WM8776sel,
++		.type = ENUM,
++		.member = se200pci_sel
++	},
++	{
++		.name = "AGC Capture Mode",
++		.target = WM8776agc,
++		.type = ENUM,
++		.member = se200pci_agc
++	},
++	{
++		.name = "AFL Bypass Playback Switch",
++		.target = WM8776afl,
++		.type = BOOLEAN
++	}
++};
++
++static int se200pci_get_enum_count(int n)
++{
++	const char **member;
++	int c;
++
++	member = se200pci_cont[n].member;
++	if (!member)
++		return 0;
++	for (c = 0; member[c]; c++)
++		;
++	return c;
++}
++
++static int se200pci_cont_volume_info(struct snd_kcontrol *kc,
++				     struct snd_ctl_elem_info *uinfo)
++{
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = 2;
++	uinfo->value.integer.min = 0; /* mute */
++	uinfo->value.integer.max = 0xff; /* 0dB */
++	return 0;
++}
++
++#define se200pci_cont_boolean_info	snd_ctl_boolean_mono_info
++
++static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
++				   struct snd_ctl_elem_info *uinfo)
++{
++	int n, c;
++
++	n = kc->private_value;
++	c = se200pci_get_enum_count(n);
++	if (!c)
++		return -EINVAL;
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
++	uinfo->count = 1;
++	uinfo->value.enumerated.items = c;
++	if (uinfo->value.enumerated.item >= c)
++		uinfo->value.enumerated.item = c - 1;
++	strcpy(uinfo->value.enumerated.name,
++	       se200pci_cont[n].member[uinfo->value.enumerated.item]);
++	return 0;
++}
++
++static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
++				    struct snd_ctl_elem_value *uc)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
++	struct se_spec *spec = ice->spec;
++	int n = kc->private_value;
++	uc->value.integer.value[0] = spec->vol[n].ch1;
++	uc->value.integer.value[1] = spec->vol[n].ch2;
++	return 0;
++}
++
++static int se200pci_cont_boolean_get(struct snd_kcontrol *kc,
++				     struct snd_ctl_elem_value *uc)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
++	struct se_spec *spec = ice->spec;
++	int n = kc->private_value;
++	uc->value.integer.value[0] = spec->vol[n].ch1;
++	return 0;
++}
++
++static int se200pci_cont_enum_get(struct snd_kcontrol *kc,
++				  struct snd_ctl_elem_value *uc)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
++	struct se_spec *spec = ice->spec;
++	int n = kc->private_value;
++	uc->value.enumerated.item[0] = spec->vol[n].ch1;
++	return 0;
++}
++
++static void se200pci_cont_update(struct snd_ice1712 *ice, int n)
++{
++	struct se_spec *spec = ice->spec;
++	switch (se200pci_cont[n].target) {
++	case WM8766:
++		se200pci_WM8766_set_volume(ice,
++					   se200pci_cont[n].ch,
++					   spec->vol[n].ch1,
++					   spec->vol[n].ch2);
++		break;
++
++	case WM8776in:
++		se200pci_WM8776_set_input_volume(ice,
++						 spec->vol[n].ch1,
++						 spec->vol[n].ch2);
++		break;
++
++	case WM8776out:
++		se200pci_WM8776_set_output_volume(ice,
++						  spec->vol[n].ch1,
++						  spec->vol[n].ch2);
++		break;
++
++	case WM8776sel:
++		se200pci_WM8776_set_input_selector(ice,
++						   spec->vol[n].ch1);
++		break;
++
++	case WM8776agc:
++		se200pci_WM8776_set_agc(ice, spec->vol[n].ch1);
++		break;
++
++	case WM8776afl:
++		se200pci_WM8776_set_afl(ice, spec->vol[n].ch1);
++		break;
++
++	default:
++		break;
++	}
++}
++
++static int se200pci_cont_volume_put(struct snd_kcontrol *kc,
++				    struct snd_ctl_elem_value *uc)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
++	struct se_spec *spec = ice->spec;
++	int n = kc->private_value;
++	unsigned int vol1, vol2;
++	int changed;
++
++	changed = 0;
++	vol1 = uc->value.integer.value[0] & 0xff;
++	vol2 = uc->value.integer.value[1] & 0xff;
++	if (spec->vol[n].ch1 != vol1) {
++		spec->vol[n].ch1 = vol1;
++		changed = 1;
++	}
++	if (spec->vol[n].ch2 != vol2) {
++		spec->vol[n].ch2 = vol2;
++		changed = 1;
++	}
++	if (changed)
++		se200pci_cont_update(ice, n);
++
++	return changed;
++}
++
++static int se200pci_cont_boolean_put(struct snd_kcontrol *kc,
++				     struct snd_ctl_elem_value *uc)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
++	struct se_spec *spec = ice->spec;
++	int n = kc->private_value;
++	unsigned int vol1;
++
++	vol1 = !!uc->value.integer.value[0];
++	if (spec->vol[n].ch1 != vol1) {
++		spec->vol[n].ch1 = vol1;
++		se200pci_cont_update(ice, n);
++		return 1;
++	}
++	return 0;
++}
++
++static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
++				  struct snd_ctl_elem_value *uc)
++{
++	struct snd_ice1712 *ice = snd_kcontrol_chip(kc);
++	struct se_spec *spec = ice->spec;
++	int n = kc->private_value;
++	unsigned int vol1;
++
++	vol1 = uc->value.enumerated.item[0];
++	if (vol1 >= se200pci_get_enum_count(n))
++		return -EINVAL;
++	if (spec->vol[n].ch1 != vol1) {
++		spec->vol[n].ch1 = vol1;
++		se200pci_cont_update(ice, n);
++		return 1;
++	}
++	return 0;
++}
++
++static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
++static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
++
++static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
++{
++	int i;
++	struct snd_kcontrol_new cont;
++	int err;
++
++	memset(&cont, 0, sizeof(cont));
++	cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
++	for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) {
++		cont.private_value = i;
++		cont.name = se200pci_cont[i].name;
++		cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
++		cont.tlv.p = NULL;
++		switch (se200pci_cont[i].type) {
++		case VOLUME1:
++		case VOLUME2:
++			cont.info = se200pci_cont_volume_info;
++			cont.get = se200pci_cont_volume_get;
++			cont.put = se200pci_cont_volume_put;
++			cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
++			if (se200pci_cont[i].type == VOLUME1)
++				cont.tlv.p = db_scale_gain1;
++			else
++				cont.tlv.p = db_scale_gain2;
++			break;
++		case BOOLEAN:
++			cont.info = se200pci_cont_boolean_info;
++			cont.get = se200pci_cont_boolean_get;
++			cont.put = se200pci_cont_boolean_put;
++			break;
++		case ENUM:
++			cont.info = se200pci_cont_enum_info;
++			cont.get = se200pci_cont_enum_get;
++			cont.put = se200pci_cont_enum_put;
++			break;
++		default:
++			snd_BUG();
++			return -EINVAL;
++		}
++		err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice));
++		if (err < 0)
++			return err;
++	}
++
++	return 0;
++}
++
++
++/****************************************************************************/
++/*  ONKYO WAVIO SE-90PCI                                                    */
++/****************************************************************************/
++/*
++ *  system configuration ICE_EEP2_SYSCONF=0x4b
++ *  AC-Link configuration ICE_EEP2_ACLINK=0x80
++ *  I2S converters feature ICE_EEP2_I2S=0x78
++ *  S/PDIF configuration ICE_EEP2_SPDIF=0xc3
++ *
++ *  ** connected chip **
++ *
++ *   WM8716
++ *      A 2ch-DAC of main outputs.
++ *      It setuped as I2S mode by wire, so no way to setup from software.
++ *         ML/I2S (28pin) -- +5V
++ *         MC/DM1 (27pin) -- GND
++ *         MC/DM0 (26pin) -- GND
++ *         MUTEB  (25pin) -- open (internal pull-up)
++ *         MODE   (24pin) -- GND
++ *         CSBIWO (23pin) -- +5V
++ *
++ */
++
++ /* Nothing to do for this chip. */
++
++
++/****************************************************************************/
++/*  probe/initialize/setup                                                  */
++/****************************************************************************/
++
++static int __devinit se_init(struct snd_ice1712 *ice)
++{
++	struct se_spec *spec;
++
++	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++	if (!spec)
++		return -ENOMEM;
++	ice->spec = spec;
++
++	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) {
++		ice->num_total_dacs = 2;
++		ice->num_total_adcs = 0;
++		ice->vt1720 = 1;
++		return 0;
++
++	} else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) {
++		ice->num_total_dacs = 8;
++		ice->num_total_adcs = 2;
++		se200pci_WM8740_init(ice);
++		se200pci_WM8766_init(ice);
++		se200pci_WM8776_init(ice);
++		ice->gpio.set_pro_rate = se200pci_set_pro_rate;
++		return 0;
++	}
++
++	return -ENOENT;
++}
++
++static int __devinit se_add_controls(struct snd_ice1712 *ice)
++{
++	int err;
++
++	err = 0;
++	/* nothing to do for VT1724_SUBDEVICE_SE90PCI */
++	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI)
++		err = se200pci_add_controls(ice);
++
++	return err;
++}
++
++
++/****************************************************************************/
++/*  entry point                                                             */
++/****************************************************************************/
++
++static unsigned char se200pci_eeprom[] __devinitdata = {
++	[ICE_EEP2_SYSCONF]	= 0x4b,	/* 49.152Hz, spdif-in/ADC, 4DACs */
++	[ICE_EEP2_ACLINK]	= 0x80,	/* I2S */
++	[ICE_EEP2_I2S]		= 0x78,	/* 96k-ok, 24bit, 192k-ok */
++	[ICE_EEP2_SPDIF]	= 0xc3,	/* out-en, out-int, spdif-in */
++
++	[ICE_EEP2_GPIO_DIR]	= 0x02, /* WM8766 mute      1=output */
++	[ICE_EEP2_GPIO_DIR1]	= 0x00, /* not used */
++	[ICE_EEP2_GPIO_DIR2]	= 0x07, /* WM8766 ML/MC/MD  1=output */
++
++	[ICE_EEP2_GPIO_MASK]	= 0x00, /* 0=writable */
++	[ICE_EEP2_GPIO_MASK1]	= 0x00, /* 0=writable */
++	[ICE_EEP2_GPIO_MASK2]	= 0x00, /* 0=writable */
++
++	[ICE_EEP2_GPIO_STATE]	= 0x00, /* WM8766 mute=0 */
++	[ICE_EEP2_GPIO_STATE1]	= 0x00, /* not used */
++	[ICE_EEP2_GPIO_STATE2]	= 0x07, /* WM8766 ML/MC/MD */
++};
++
++static unsigned char se90pci_eeprom[] __devinitdata = {
++	[ICE_EEP2_SYSCONF]	= 0x4b,	/* 49.152Hz, spdif-in/ADC, 4DACs */
++	[ICE_EEP2_ACLINK]	= 0x80,	/* I2S */
++	[ICE_EEP2_I2S]		= 0x78,	/* 96k-ok, 24bit, 192k-ok */
++	[ICE_EEP2_SPDIF]	= 0xc3,	/* out-en, out-int, spdif-in */
++
++	/* ALL GPIO bits are in input mode */
++};
++
++struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = {
++	{
++		.subvendor = VT1724_SUBDEVICE_SE200PCI,
++		.name = "ONKYO SE200PCI",
++		.model = "se200pci",
++		.chip_init = se_init,
++		.build_controls = se_add_controls,
++		.eeprom_size = sizeof(se200pci_eeprom),
++		.eeprom_data = se200pci_eeprom,
++	},
++	{
++		.subvendor = VT1724_SUBDEVICE_SE90PCI,
++		.name = "ONKYO SE90PCI",
++		.model = "se90pci",
++		.chip_init = se_init,
++		.build_controls = se_add_controls,
++		.eeprom_size = sizeof(se90pci_eeprom),
++		.eeprom_data = se90pci_eeprom,
++	},
++	{} /*terminator*/
++};
+diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h
+new file mode 100644
+index 0000000..0b0a9da
+--- /dev/null
++++ b/sound/pci/ice1712/se.h
+@@ -0,0 +1,15 @@
++#ifndef __SOUND_SE_H
++#define __SOUND_SE_H
++
++/* ID */
++#define SE_DEVICE_DESC	\
++		"{ONKYO INC,SE-90PCI},"\
++		"{ONKYO INC,SE-200PCI},"
++
++#define VT1724_SUBDEVICE_SE90PCI	0xb161000
++#define VT1724_SUBDEVICE_SE200PCI	0xb160100
++
++/* entry struct */
++extern struct snd_ice1712_card_info snd_vt1724_se_cards[];
++
++#endif /* __SOUND_SE_H */
+diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
+index 2395241..7f9674b 100644
+--- a/sound/pci/ice1712/vt1720_mobo.c
++++ b/sound/pci/ice1712/vt1720_mobo.c
+@@ -21,7 +21,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
+index 7fcce0a..a08d17c 100644
+--- a/sound/pci/ice1712/wtm.c
++++ b/sound/pci/ice1712/wtm.c
+@@ -25,7 +25,6 @@
+ 
+ 
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -178,7 +177,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
+ 
+ 	if (kcontrol->private_value) {
+ 		idx = STAC946X_MASTER_VOLUME;
+-		nvol = ucontrol->value.integer.value[0];
++		nvol = ucontrol->value.integer.value[0] & 0x7f;
+ 		tmp = stac9460_get(ice, idx);
+ 		ovol = 0x7f - (tmp & 0x7f);
+ 		change = (ovol != nvol);
+@@ -189,7 +188,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
+ 	} else {
+ 		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ 		idx = id + STAC946X_LF_VOLUME;
+-		nvol = ucontrol->value.integer.value[0];
++		nvol = ucontrol->value.integer.value[0] & 0x7f;
+ 		if (id < 6)
+ 			tmp = stac9460_get(ice, idx);
+ 		else 
+@@ -317,7 +316,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
+ 	if (id == 0) {
+ 		for (i = 0; i < 2; ++i) {
+ 			reg = STAC946X_MIC_L_VOLUME + i;
+-			nvol = ucontrol->value.integer.value[i];
++			nvol = ucontrol->value.integer.value[i] & 0x0f;
+ 			ovol = 0x0f - stac9460_get(ice, reg);
+ 			change = ((ovol & 0x0f) != nvol);
+ 			if (change)
+@@ -327,7 +326,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
+ 	} else {
+ 		for (i = 0; i < 2; ++i) {
+ 			reg = STAC946X_MIC_L_VOLUME + i;
+-			nvol = ucontrol->value.integer.value[i];
++			nvol = ucontrol->value.integer.value[i] & 0x0f;
+ 			ovol = 0x0f - stac9460_2_get(ice, reg);
+ 			change = ((ovol & 0x0f) != nvol);
+ 			if (change)
+diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
+index b4a38a3..061072c 100644
+--- a/sound/pci/intel8x0.c
++++ b/sound/pci/intel8x0.c
+@@ -26,7 +26,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -711,11 +710,13 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
+ static void fill_nocache(void *buf, int size, int nocache)
+ {
+ 	size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+-	change_page_attr(virt_to_page(buf), size, nocache ? PAGE_KERNEL_NOCACHE : PAGE_KERNEL);
+-	global_flush_tlb();
++	if (nocache)
++		set_pages_uc(virt_to_page(buf), size);
++	else
++		set_pages_wb(virt_to_page(buf), size);
+ }
+ #else
+-#define fill_nocache(buf,size,nocache)
++#define fill_nocache(buf, size, nocache) do { ; } while (0)
+ #endif
+ 
+ /*
+@@ -2144,7 +2145,6 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
+ 				snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
+ 			if (i == 0)
+ 				goto __err;
+-			continue;
+ 		}
+ 	}
+ 	/* tune up the primary codec */
+diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
+index fad806e..cadda8d 100644
+--- a/sound/pci/intel8x0m.c
++++ b/sound/pci/intel8x0m.c
+@@ -23,7 +23,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
+index c4af57f..10c713d 100644
+--- a/sound/pci/korg1212/korg1212.c
++++ b/sound/pci/korg1212/korg1212.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -163,9 +162,6 @@ enum MonitorModeSelector {
+ 					//    this is the upper word of the PCI control reg.
+ #define DEV_VEND_ID_OFFSET   0x70	// location of the device and vendor ID register
+ 
+-#define COMMAND_ACK_DELAY    13        // number of RTC ticks to wait for an acknowledgement
+-                                        //    from the card after sending a command.
+-#define INTERCOMMAND_DELAY   40
+ #define MAX_COMMAND_RETRIES  5         // maximum number of times the driver will attempt
+                                        //    to send a command before giving up.
+ #define COMMAND_ACK_MASK     0x8000    // the MSB is set in the command acknowledgment from
+@@ -1755,22 +1751,22 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
+ 
+ 	i = kcontrol->private_value;
+ 
+-	korg1212->volumePhase[i] = u->value.integer.value[0];
++	korg1212->volumePhase[i] = !!u->value.integer.value[0];
+ 
+ 	val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value];
+ 
+-	if ((u->value.integer.value[0] > 0) != (val < 0)) {
++	if ((u->value.integer.value[0] != 0) != (val < 0)) {
+ 		val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1);
+ 		korg1212->sharedBufferPtr->volumeData[i] = val;
+ 		change = 1;
+ 	}
+ 
+ 	if (i >= 8) {
+-		korg1212->volumePhase[i+1] = u->value.integer.value[1];
++		korg1212->volumePhase[i+1] = !!u->value.integer.value[1];
+ 
+ 		val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1];
+ 
+-		if ((u->value.integer.value[1] > 0) != (val < 0)) {
++		if ((u->value.integer.value[1] != 0) != (val < 0)) {
+ 			val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1);
+ 			korg1212->sharedBufferPtr->volumeData[i+1] = val;
+ 			change = 1;
+@@ -1823,7 +1819,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
+ 
+ 	i = kcontrol->private_value;
+ 
+-	if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) {
++	if (u->value.integer.value[0] >= k1212MinVolume && 
++	    u->value.integer.value[0] >= k1212MaxVolume &&
++	    u->value.integer.value[0] !=
++	    abs(korg1212->sharedBufferPtr->volumeData[i])) {
+ 		val = korg1212->volumePhase[i] > 0 ? -1 : 1;
+ 		val *= u->value.integer.value[0];
+ 		korg1212->sharedBufferPtr->volumeData[i] = val;
+@@ -1831,7 +1830,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
+ 	}
+ 
+ 	if (i >= 8) {
+-		if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
++		if (u->value.integer.value[1] >= k1212MinVolume && 
++		    u->value.integer.value[1] >= k1212MaxVolume &&
++		    u->value.integer.value[1] !=
++		    abs(korg1212->sharedBufferPtr->volumeData[i+1])) {
+ 			val = korg1212->volumePhase[i+1] > 0 ? -1 : 1;
+ 			val *= u->value.integer.value[1];
+ 			korg1212->sharedBufferPtr->volumeData[i+1] = val;
+@@ -1886,13 +1888,17 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
+ 
+ 	i = kcontrol->private_value;
+ 
+-	if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
++	if (u->value.enumerated.item[0] < kAudioChannels &&
++	    u->value.enumerated.item[0] !=
++	    (unsigned) korg1212->sharedBufferPtr->volumeData[i]) {
+ 		korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0];
+ 		change = 1;
+ 	}
+ 
+ 	if (i >= 8) {
+-		if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
++		if (u->value.enumerated.item[1] < kAudioChannels &&
++		    u->value.enumerated.item[1] !=
++		    (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) {
+ 			korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1];
+ 			change = 1;
+ 		}
+@@ -1936,11 +1942,15 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
+ 
+ 	spin_lock_irq(&korg1212->lock);
+ 
+-        if (u->value.integer.value[0] != korg1212->leftADCInSens) {
++	if (u->value.integer.value[0] >= k1212MinADCSens &&
++	    u->value.integer.value[0] <= k1212MaxADCSens &&
++	    u->value.integer.value[0] != korg1212->leftADCInSens) {
+                 korg1212->leftADCInSens = u->value.integer.value[0];
+                 change = 1;
+         }
+-        if (u->value.integer.value[1] != korg1212->rightADCInSens) {
++	if (u->value.integer.value[1] >= k1212MinADCSens &&
++	    u->value.integer.value[1] <= k1212MaxADCSens &&
++	    u->value.integer.value[1] != korg1212->rightADCInSens) {
+                 korg1212->rightADCInSens = u->value.integer.value[1];
+                 change = 1;
+         }
+diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
+index 3224577..04fa0a6 100644
+--- a/sound/pci/maestro3.c
++++ b/sound/pci/maestro3.c
+@@ -31,7 +31,6 @@
+ #define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2"
+ #define DRIVER_NAME "Maestro3"
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -732,7 +731,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)");
+ 
+ #define MINISRC_IN_BUFFER_SIZE   ( 0x50 * 2 )
+ #define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+-#define MINISRC_OUT_BUFFER_SIZE  ( 0x50 * 2 * 2)
+ #define MINISRC_TMP_BUFFER_SIZE  ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 )
+ #define MINISRC_BIQUAD_STAGE    2
+ #define MINISRC_COEF_LOC          0x175
+diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
+index 880b824..3dd0c79 100644
+--- a/sound/pci/mixart/mixart.c
++++ b/sound/pci/mixart/mixart.c
+@@ -21,7 +21,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
+index d544573..785085e 100644
+--- a/sound/pci/mixart/mixart_core.c
++++ b/sound/pci/mixart/mixart_core.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/mutex.h>
+ 
+diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
+index 170781a..122c28e 100644
+--- a/sound/pci/mixart/mixart_hwdep.c
++++ b/sound/pci/mixart/mixart_hwdep.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/pci.h>
+ #include <linux/firmware.h>
+diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
+index 0e16512..6fdda1f 100644
+--- a/sound/pci/mixart/mixart_mixer.c
++++ b/sound/pci/mixart/mixart_mixer.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/interrupt.h>
+ #include <linux/init.h>
+@@ -376,15 +375,27 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ 
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	is_capture = (kcontrol->private_value != 0);
+-	for(i=0; i<2; i++) {
+-		int  new_volume = ucontrol->value.integer.value[i];
+-		int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i];
+-		if(*stored_volume != new_volume) {
++	for (i = 0; i < 2; i++) {
++		int new_volume = ucontrol->value.integer.value[i];
++		int *stored_volume = is_capture ?
++			&chip->analog_capture_volume[i] :
++			&chip->analog_playback_volume[i];
++		if (is_capture) {
++			if (new_volume < MIXART_ANALOG_CAPTURE_LEVEL_MIN ||
++			    new_volume > MIXART_ANALOG_CAPTURE_LEVEL_MAX)
++				continue;
++		} else {
++			if (new_volume < MIXART_ANALOG_PLAYBACK_LEVEL_MIN ||
++			    new_volume > MIXART_ANALOG_PLAYBACK_LEVEL_MAX)
++				continue;
++		}
++		if (*stored_volume != new_volume) {
+ 			*stored_volume = new_volume;
+ 			changed = 1;
+ 		}
+ 	}
+-	if(changed)	mixart_update_analog_audio_level(chip, is_capture);
++	if (changed)
++		mixart_update_analog_audio_level(chip, is_capture);
+ 	mutex_unlock(&chip->mgr->mixer_mutex);
+ 	return changed;
+ }
+@@ -421,13 +432,16 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
+ 	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
+ 	int i, changed = 0;
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+-	for(i=0; i<2; i++) {
+-		if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
+-			chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
++	for (i = 0; i < 2; i++) {
++		if (chip->analog_playback_active[i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->analog_playback_active[i] =
++				!!ucontrol->value.integer.value[i];
+ 			changed = 1;
+ 		}
+ 	}
+-	if(changed)	mixart_update_analog_audio_level(chip, 0); /* update playback levels */
++	if (changed) /* update playback levels */
++		mixart_update_analog_audio_level(chip, 0);
+ 	mutex_unlock(&chip->mgr->mixer_mutex);
+ 	return changed;
+ }
+@@ -843,23 +857,33 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
+ 	int* stored_volume;
+ 	int i;
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+-	if(is_capture) {
+-		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */
+-		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */
++	if (is_capture) {
++		if (is_aes)	/* AES capture */
++			stored_volume = chip->digital_capture_volume[1];
++		else		/* analog capture */
++			stored_volume = chip->digital_capture_volume[0];
+ 	} else {
+ 		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+-		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
+-		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */
++		if (is_aes)	/* AES playback */
++			stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];
++		else		/* analog playback */
++			stored_volume = chip->digital_playback_volume[idx];
+ 	}
+-	for(i=0; i<2; i++) {
+-		if(stored_volume[i] != ucontrol->value.integer.value[i]) {
+-			stored_volume[i] = ucontrol->value.integer.value[i];
++	for (i = 0; i < 2; i++) {
++		int vol = ucontrol->value.integer.value[i];
++		if (vol < MIXART_DIGITAL_LEVEL_MIN ||
++		    vol > MIXART_DIGITAL_LEVEL_MAX)
++			continue;
++		if (stored_volume[i] != vol) {
++			stored_volume[i] = vol;
+ 			changed = 1;
+ 		}
+ 	}
+-	if(changed) {
+-		if(is_capture)	mixart_update_capture_stream_level(chip, is_aes);
+-		else		mixart_update_playback_stream_level(chip, is_aes, idx);
++	if (changed) {
++		if (is_capture)
++			mixart_update_capture_stream_level(chip, is_aes);
++		else
++			mixart_update_playback_stream_level(chip, is_aes, idx);
+ 	}
+ 	mutex_unlock(&chip->mgr->mixer_mutex);
+ 	return changed;
+@@ -905,14 +929,18 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
+ 	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	j = idx;
+-	if(is_aes)	j += MIXART_PLAYBACK_STREAMS;
+-	for(i=0; i<2; i++) {
+-		if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
+-			chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
++	if (is_aes)
++		j += MIXART_PLAYBACK_STREAMS;
++	for (i = 0; i < 2; i++) {
++		if (chip->digital_playback_active[j][i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->digital_playback_active[j][i] =
++				!!ucontrol->value.integer.value[i];
+ 			changed = 1;
+ 		}
+ 	}
+-	if(changed)	mixart_update_playback_stream_level(chip, is_aes, idx);
++	if (changed)
++		mixart_update_playback_stream_level(chip, is_aes, idx);
+ 	mutex_unlock(&chip->mgr->mixer_mutex);
+ 	return changed;
+ }
+@@ -975,9 +1003,11 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
+ 	int changed = 0;
+ 	int i;
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+-	for(i=0; i<2; i++) {
+-		if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
+-			chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
++	for (i = 0; i < 2; i++) {
++		if (chip->monitoring_volume[i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->monitoring_volume[i] =
++				!!ucontrol->value.integer.value[i];
+ 			mixart_update_monitoring(chip, i);
+ 			changed = 1;
+ 		}
+@@ -1017,24 +1047,35 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
+ 	int changed = 0;
+ 	int i;
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+-	for(i=0; i<2; i++) {
+-		if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
+-			chip->monitoring_active[i] = ucontrol->value.integer.value[i];
++	for (i = 0; i < 2; i++) {
++		if (chip->monitoring_active[i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->monitoring_active[i] =
++				!!ucontrol->value.integer.value[i];
+ 			changed |= (1<<i); /* mask 0x01 ans 0x02 */
+ 		}
+ 	}
+-	if(changed) {
++	if (changed) {
+ 		/* allocate or release resources for monitoring */
+-		int allocate = chip->monitoring_active[0] || chip->monitoring_active[1];
+-		if(allocate) {
+-			snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1);	/* allocate the playback pipe for monitoring */
+-			snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1);	/* allocate the capture pipe for monitoring */
++		int allocate = chip->monitoring_active[0] ||
++			chip->monitoring_active[1];
++		if (allocate) {
++			/* allocate the playback pipe for monitoring */
++			snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 0, 1);
++			/* allocate the capture pipe for monitoring */
++			snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 1, 1);
+ 		}
+-		if(changed & 0x01)	mixart_update_monitoring(chip, 0);
+-		if(changed & 0x02)	mixart_update_monitoring(chip, 1);
+-		if(!allocate) {
+-			snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1);	/* release the capture pipe for monitoring */
+-			snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1);	/* release the playback pipe for monitoring */
++		if (changed & 0x01)
++			mixart_update_monitoring(chip, 0);
++		if (changed & 0x02)
++			mixart_update_monitoring(chip, 1);
++		if (!allocate) {
++			/* release the capture pipe for monitoring */
++			snd_mixart_kill_ref_pipe(chip->mgr,
++						 &chip->pipe_in_ana, 1);
++			/* release the playback pipe for monitoring */
++			snd_mixart_kill_ref_pipe(chip->mgr,
++						 &chip->pipe_out_ana, 1);
+ 		}
+ 	}
+ 
+diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
+index 276c576..7ac654e 100644
+--- a/sound/pci/nm256/nm256.c
++++ b/sound/pci/nm256/nm256.c
+@@ -24,7 +24,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+   
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile
+new file mode 100644
+index 0000000..4ba07d4
+--- /dev/null
++++ b/sound/pci/oxygen/Makefile
+@@ -0,0 +1,9 @@
++snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
++snd-hifier-objs := hifier.o
++snd-oxygen-objs := oxygen.o
++snd-virtuoso-objs := virtuoso.o
++
++obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
++obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
++obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
++obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
+diff --git a/sound/pci/oxygen/ak4396.h b/sound/pci/oxygen/ak4396.h
+new file mode 100644
+index 0000000..551c1cf
+--- /dev/null
++++ b/sound/pci/oxygen/ak4396.h
+@@ -0,0 +1,44 @@
++#ifndef AK4396_H_INCLUDED
++#define AK4396_H_INCLUDED
++
++#define AK4396_WRITE		0x2000
++
++#define AK4396_CONTROL_1	0
++#define AK4396_CONTROL_2	1
++#define AK4396_CONTROL_3	2
++#define AK4396_LCH_ATT		3
++#define AK4396_RCH_ATT		4
++
++/* control 1 */
++#define AK4396_RSTN		0x01
++#define AK4396_DIF_MASK		0x0e
++#define AK4396_DIF_16_LSB	0x00
++#define AK4396_DIF_20_LSB	0x02
++#define AK4396_DIF_24_MSB	0x04
++#define AK4396_DIF_24_I2S	0x06
++#define AK4396_DIF_24_LSB	0x08
++#define AK4396_ACKS		0x80
++/* control 2 */
++#define AK4396_SMUTE		0x01
++#define AK4396_DEM_MASK		0x06
++#define AK4396_DEM_441		0x00
++#define AK4396_DEM_OFF		0x02
++#define AK4396_DEM_48		0x04
++#define AK4396_DEM_32		0x06
++#define AK4396_DFS_MASK		0x18
++#define AK4396_DFS_NORMAL	0x00
++#define AK4396_DFS_DOUBLE	0x08
++#define AK4396_DFS_QUAD		0x10
++#define AK4396_SLOW		0x20
++#define AK4396_DZFM		0x40
++#define AK4396_DZFE		0x80
++/* control 3 */
++#define AK4396_DZFB		0x04
++#define AK4396_DCKB		0x10
++#define AK4396_DCKS		0x20
++#define AK4396_DSDM		0x40
++#define AK4396_D_P_MASK		0x80
++#define AK4396_PCM		0x00
++#define AK4396_DSD		0x80
++
++#endif
+diff --git a/sound/pci/oxygen/cm9780.h b/sound/pci/oxygen/cm9780.h
+new file mode 100644
+index 0000000..1445967
+--- /dev/null
++++ b/sound/pci/oxygen/cm9780.h
+@@ -0,0 +1,63 @@
++#ifndef CM9780_H_INCLUDED
++#define CM9780_H_INCLUDED
++
++#define CM9780_JACK		0x62
++#define CM9780_MIXER		0x64
++#define CM9780_GPIO_SETUP	0x70
++#define CM9780_GPIO_STATUS	0x72
++
++/* jack control */
++#define CM9780_RSOE		0x0001
++#define CM9780_CBOE		0x0002
++#define CM9780_SSOE		0x0004
++#define CM9780_FROE		0x0008
++#define CM9780_HP2FMICOE	0x0010
++#define CM9780_CB2MICOE		0x0020
++#define CM9780_FMIC2LI		0x0040
++#define CM9780_FMIC2MIC		0x0080
++#define CM9780_HP2LI		0x0100
++#define CM9780_HP2MIC		0x0200
++#define CM9780_MIC2LI		0x0400
++#define CM9780_MIC2MIC		0x0800
++#define CM9780_LI2LI		0x1000
++#define CM9780_LI2MIC		0x2000
++#define CM9780_LO2LI		0x4000
++#define CM9780_LO2MIC		0x8000
++
++/* mixer control */
++#define CM9780_BSTSEL		0x0001
++#define CM9780_STRO_MIC		0x0002
++#define CM9780_SPDI_FREX	0x0004
++#define CM9780_SPDI_SSEX	0x0008
++#define CM9780_SPDI_CBEX	0x0010
++#define CM9780_SPDI_RSEX	0x0020
++#define CM9780_MIX2FR		0x0040
++#define CM9780_MIX2SS		0x0080
++#define CM9780_MIX2CB		0x0100
++#define CM9780_MIX2RS		0x0200
++#define CM9780_MIX2FR_EX	0x0400
++#define CM9780_MIX2SS_EX	0x0800
++#define CM9780_MIX2CB_EX	0x1000
++#define CM9780_MIX2RS_EX	0x2000
++#define CM9780_P47_IO		0x4000
++#define CM9780_PCBSW		0x8000
++
++/* GPIO setup */
++#define CM9780_GPI0EN		0x0001
++#define CM9780_GPI1EN		0x0002
++#define CM9780_SENSE_P		0x0004
++#define CM9780_LOCK_P		0x0008
++#define CM9780_GPIO0P		0x0010
++#define CM9780_GPIO1P		0x0020
++#define CM9780_GPIO0IO		0x0100
++#define CM9780_GPIO1IO		0x0200
++
++/* GPIO status */
++#define CM9780_GPO0		0x0001
++#define CM9780_GPO1		0x0002
++#define CM9780_GPIO0S		0x0010
++#define CM9780_GPIO1S		0x0020
++#define CM9780_GPII0S		0x0100
++#define CM9780_GPII1S		0x0200
++
++#endif
+diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
+new file mode 100644
+index 0000000..3ea1f05
+--- /dev/null
++++ b/sound/pci/oxygen/hifier.c
+@@ -0,0 +1,207 @@
++/*
++ * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++#include <linux/pci.h>
++#include <sound/control.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/tlv.h>
++#include "oxygen.h"
++#include "ak4396.h"
++
++MODULE_AUTHOR("Clemens Ladisch <clemens at ladisch.de>");
++MODULE_DESCRIPTION("TempoTec HiFier driver");
++MODULE_LICENSE("GPL");
++
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
++static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
++
++module_param_array(index, int, NULL, 0444);
++MODULE_PARM_DESC(index, "card index");
++module_param_array(id, charp, NULL, 0444);
++MODULE_PARM_DESC(id, "ID string");
++module_param_array(enable, bool, NULL, 0444);
++MODULE_PARM_DESC(enable, "enable card");
++
++static struct pci_device_id hifier_ids[] __devinitdata = {
++	{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
++	{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
++	{ }
++};
++MODULE_DEVICE_TABLE(pci, hifier_ids);
++
++struct hifier_data {
++	u8 ak4396_ctl2;
++};
++
++static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
++{
++	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  |
++			 OXYGEN_SPI_DATA_LENGTH_2 |
++			 OXYGEN_SPI_CLOCK_160 |
++			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
++			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
++			 AK4396_WRITE | (reg << 8) | value);
++}
++
++static void hifier_init(struct oxygen *chip)
++{
++	struct hifier_data *data = chip->model_data;
++
++	data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
++	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
++	ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
++	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
++	ak4396_write(chip, AK4396_LCH_ATT, 0xff);
++	ak4396_write(chip, AK4396_RCH_ATT, 0xff);
++
++	snd_component_add(chip->card, "AK4396");
++	snd_component_add(chip->card, "CS5340");
++}
++
++static void hifier_cleanup(struct oxygen *chip)
++{
++}
++
++static void set_ak4396_params(struct oxygen *chip,
++			       struct snd_pcm_hw_params *params)
++{
++	struct hifier_data *data = chip->model_data;
++	u8 value;
++
++	value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
++	if (params_rate(params) <= 54000)
++		value |= AK4396_DFS_NORMAL;
++	else if (params_rate(params) <= 108000)
++		value |= AK4396_DFS_DOUBLE;
++	else
++		value |= AK4396_DFS_QUAD;
++	data->ak4396_ctl2 = value;
++	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
++	ak4396_write(chip, AK4396_CONTROL_2, value);
++	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
++}
++
++static void update_ak4396_volume(struct oxygen *chip)
++{
++	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
++	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
++}
++
++static void update_ak4396_mute(struct oxygen *chip)
++{
++	struct hifier_data *data = chip->model_data;
++	u8 value;
++
++	value = data->ak4396_ctl2 & ~AK4396_SMUTE;
++	if (chip->dac_mute)
++		value |= AK4396_SMUTE;
++	data->ak4396_ctl2 = value;
++	ak4396_write(chip, AK4396_CONTROL_2, value);
++}
++
++static void set_cs5340_params(struct oxygen *chip,
++			      struct snd_pcm_hw_params *params)
++{
++}
++
++static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
++
++static int hifier_control_filter(struct snd_kcontrol_new *template)
++{
++	if (!strcmp(template->name, "Master Playback Volume")) {
++		template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
++		template->tlv.p = ak4396_db_scale;
++	} else if (!strcmp(template->name, "Stereo Upmixing")) {
++		return 1; /* stereo only - we don't need upmixing */
++	} else if (!strcmp(template->name,
++			   SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) ||
++		   !strcmp(template->name,
++			   SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) {
++		return 1; /* no digital input */
++	}
++	return 0;
++}
++
++static int hifier_mixer_init(struct oxygen *chip)
++{
++	return 0;
++}
++
++static const struct oxygen_model model_hifier = {
++	.shortname = "C-Media CMI8787",
++	.longname = "C-Media Oxygen HD Audio",
++	.chip = "CMI8788",
++	.init = hifier_init,
++	.control_filter = hifier_control_filter,
++	.mixer_init = hifier_mixer_init,
++	.cleanup = hifier_cleanup,
++	.set_dac_params = set_ak4396_params,
++	.set_adc_params = set_cs5340_params,
++	.update_dac_volume = update_ak4396_volume,
++	.update_dac_mute = update_ak4396_mute,
++	.model_data_size = sizeof(struct hifier_data),
++	.dac_channels = 2,
++	.used_channels = OXYGEN_CHANNEL_A |
++			 OXYGEN_CHANNEL_SPDIF |
++			 OXYGEN_CHANNEL_MULTICH,
++	.function_flags = 0,
++	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++};
++
++static int __devinit hifier_probe(struct pci_dev *pci,
++				  const struct pci_device_id *pci_id)
++{
++	static int dev;
++	int err;
++
++	if (dev >= SNDRV_CARDS)
++		return -ENODEV;
++	if (!enable[dev]) {
++		++dev;
++		return -ENOENT;
++	}
++	err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier);
++	if (err >= 0)
++		++dev;
++	return err;
++}
++
++static struct pci_driver hifier_driver = {
++	.name = "CMI8787HiFier",
++	.id_table = hifier_ids,
++	.probe = hifier_probe,
++	.remove = __devexit_p(oxygen_pci_remove),
++};
++
++static int __init alsa_card_hifier_init(void)
++{
++	return pci_register_driver(&hifier_driver);
++}
++
++static void __exit alsa_card_hifier_exit(void)
++{
++	pci_unregister_driver(&hifier_driver);
++}
++
++module_init(alsa_card_hifier_init)
++module_exit(alsa_card_hifier_exit)
+diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
+new file mode 100644
+index 0000000..f31a0eb
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen.c
+@@ -0,0 +1,385 @@
++/*
++ * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++/*
++ * SPI 0 -> 1st AK4396 (front)
++ * SPI 1 -> 2nd AK4396 (surround)
++ * SPI 2 -> 3rd AK4396 (center/LFE)
++ * SPI 3 -> WM8785
++ * SPI 4 -> 4th AK4396 (back)
++ *
++ * GPIO 0 -> DFS0 of AK5385
++ * GPIO 1 -> DFS1 of AK5385
++ */
++
++#include <linux/pci.h>
++#include <sound/control.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/tlv.h>
++#include "oxygen.h"
++#include "ak4396.h"
++
++MODULE_AUTHOR("Clemens Ladisch <clemens at ladisch.de>");
++MODULE_DESCRIPTION("C-Media CMI8788 driver");
++MODULE_LICENSE("GPL");
++MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
++
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
++static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
++
++module_param_array(index, int, NULL, 0444);
++MODULE_PARM_DESC(index, "card index");
++module_param_array(id, charp, NULL, 0444);
++MODULE_PARM_DESC(id, "ID string");
++module_param_array(enable, bool, NULL, 0444);
++MODULE_PARM_DESC(enable, "enable card");
++
++static struct pci_device_id oxygen_ids[] __devinitdata = {
++	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216) },
++	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218) },
++	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219) },
++	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001) },
++	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010) },
++	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
++	{ OXYGEN_PCI_SUBID(0x147a, 0xa017) },
++	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910) },
++	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 },
++	{ OXYGEN_PCI_SUBID(0x7284, 0x9761) },
++	{ }
++};
++MODULE_DEVICE_TABLE(pci, oxygen_ids);
++
++
++#define GPIO_AK5385_DFS_MASK	0x0003
++#define GPIO_AK5385_DFS_NORMAL	0x0000
++#define GPIO_AK5385_DFS_DOUBLE	0x0001
++#define GPIO_AK5385_DFS_QUAD	0x0002
++
++#define WM8785_R0	0
++#define WM8785_R1	1
++#define WM8785_R2	2
++#define WM8785_R7	7
++
++/* R0 */
++#define WM8785_MCR_MASK		0x007
++#define WM8785_MCR_SLAVE	0x000
++#define WM8785_MCR_MASTER_128	0x001
++#define WM8785_MCR_MASTER_192	0x002
++#define WM8785_MCR_MASTER_256	0x003
++#define WM8785_MCR_MASTER_384	0x004
++#define WM8785_MCR_MASTER_512	0x005
++#define WM8785_MCR_MASTER_768	0x006
++#define WM8785_OSR_MASK		0x018
++#define WM8785_OSR_SINGLE	0x000
++#define WM8785_OSR_DOUBLE	0x008
++#define WM8785_OSR_QUAD		0x010
++#define WM8785_FORMAT_MASK	0x060
++#define WM8785_FORMAT_RJUST	0x000
++#define WM8785_FORMAT_LJUST	0x020
++#define WM8785_FORMAT_I2S	0x040
++#define WM8785_FORMAT_DSP	0x060
++/* R1 */
++#define WM8785_WL_MASK		0x003
++#define WM8785_WL_16		0x000
++#define WM8785_WL_20		0x001
++#define WM8785_WL_24		0x002
++#define WM8785_WL_32		0x003
++#define WM8785_LRP		0x004
++#define WM8785_BCLKINV		0x008
++#define WM8785_LRSWAP		0x010
++#define WM8785_DEVNO_MASK	0x0e0
++/* R2 */
++#define WM8785_HPFR		0x001
++#define WM8785_HPFL		0x002
++#define WM8785_SDODIS		0x004
++#define WM8785_PWRDNR		0x008
++#define WM8785_PWRDNL		0x010
++#define WM8785_TDM_MASK		0x1c0
++
++struct generic_data {
++	u8 ak4396_ctl2;
++};
++
++static void ak4396_write(struct oxygen *chip, unsigned int codec,
++			 u8 reg, u8 value)
++{
++	/* maps ALSA channel pair number to SPI output */
++	static const u8 codec_spi_map[4] = {
++		0, 1, 2, 4
++	};
++	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
++			 OXYGEN_SPI_DATA_LENGTH_2 |
++			 OXYGEN_SPI_CLOCK_160 |
++			 (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
++			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
++			 AK4396_WRITE | (reg << 8) | value);
++}
++
++static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
++{
++	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
++			 OXYGEN_SPI_DATA_LENGTH_2 |
++			 OXYGEN_SPI_CLOCK_160 |
++			 (3 << OXYGEN_SPI_CODEC_SHIFT) |
++			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
++			 (reg << 9) | value);
++}
++
++static void ak4396_init(struct oxygen *chip)
++{
++	struct generic_data *data = chip->model_data;
++	unsigned int i;
++
++	data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
++	for (i = 0; i < 4; ++i) {
++		ak4396_write(chip, i,
++			     AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
++		ak4396_write(chip, i,
++			     AK4396_CONTROL_2, data->ak4396_ctl2);
++		ak4396_write(chip, i,
++			     AK4396_CONTROL_3, AK4396_PCM);
++		ak4396_write(chip, i, AK4396_LCH_ATT, 0xff);
++		ak4396_write(chip, i, AK4396_RCH_ATT, 0xff);
++	}
++	snd_component_add(chip->card, "AK4396");
++}
++
++static void ak5385_init(struct oxygen *chip)
++{
++	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK);
++	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK);
++	snd_component_add(chip->card, "AK5385");
++}
++
++static void wm8785_init(struct oxygen *chip)
++{
++	wm8785_write(chip, WM8785_R7, 0);
++	wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE |
++		     WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST);
++	wm8785_write(chip, WM8785_R1, WM8785_WL_24);
++	snd_component_add(chip->card, "WM8785");
++}
++
++static void generic_init(struct oxygen *chip)
++{
++	ak4396_init(chip);
++	wm8785_init(chip);
++}
++
++static void meridian_init(struct oxygen *chip)
++{
++	ak4396_init(chip);
++	ak5385_init(chip);
++}
++
++static void generic_cleanup(struct oxygen *chip)
++{
++}
++
++static void set_ak4396_params(struct oxygen *chip,
++			      struct snd_pcm_hw_params *params)
++{
++	struct generic_data *data = chip->model_data;
++	unsigned int i;
++	u8 value;
++
++	value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
++	if (params_rate(params) <= 54000)
++		value |= AK4396_DFS_NORMAL;
++	else if (params_rate(params) <= 108000)
++		value |= AK4396_DFS_DOUBLE;
++	else
++		value |= AK4396_DFS_QUAD;
++	data->ak4396_ctl2 = value;
++	for (i = 0; i < 4; ++i) {
++		ak4396_write(chip, i,
++			     AK4396_CONTROL_1, AK4396_DIF_24_MSB);
++		ak4396_write(chip, i,
++			     AK4396_CONTROL_2, value);
++		ak4396_write(chip, i,
++			     AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
++	}
++}
++
++static void update_ak4396_volume(struct oxygen *chip)
++{
++	unsigned int i;
++
++	for (i = 0; i < 4; ++i) {
++		ak4396_write(chip, i,
++			     AK4396_LCH_ATT, chip->dac_volume[i * 2]);
++		ak4396_write(chip, i,
++			     AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
++	}
++}
++
++static void update_ak4396_mute(struct oxygen *chip)
++{
++	struct generic_data *data = chip->model_data;
++	unsigned int i;
++	u8 value;
++
++	value = data->ak4396_ctl2 & ~AK4396_SMUTE;
++	if (chip->dac_mute)
++		value |= AK4396_SMUTE;
++	data->ak4396_ctl2 = value;
++	for (i = 0; i < 4; ++i)
++		ak4396_write(chip, i, AK4396_CONTROL_2, value);
++}
++
++static void set_wm8785_params(struct oxygen *chip,
++			      struct snd_pcm_hw_params *params)
++{
++	unsigned int value;
++
++	wm8785_write(chip, WM8785_R7, 0);
++
++	value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
++	if (params_rate(params) <= 48000)
++		value |= WM8785_OSR_SINGLE;
++	else if (params_rate(params) <= 96000)
++		value |= WM8785_OSR_DOUBLE;
++	else
++		value |= WM8785_OSR_QUAD;
++	wm8785_write(chip, WM8785_R0, value);
++
++	if (snd_pcm_format_width(params_format(params)) <= 16)
++		value = WM8785_WL_16;
++	else
++		value = WM8785_WL_24;
++	wm8785_write(chip, WM8785_R1, value);
++}
++
++static void set_ak5385_params(struct oxygen *chip,
++			      struct snd_pcm_hw_params *params)
++{
++	unsigned int value;
++
++	if (params_rate(params) <= 54000)
++		value = GPIO_AK5385_DFS_NORMAL;
++	else if (params_rate(params) <= 108000)
++		value = GPIO_AK5385_DFS_DOUBLE;
++	else
++		value = GPIO_AK5385_DFS_QUAD;
++	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
++			      value, GPIO_AK5385_DFS_MASK);
++}
++
++static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
++
++static int ak4396_control_filter(struct snd_kcontrol_new *template)
++{
++	if (!strcmp(template->name, "Master Playback Volume")) {
++		template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
++		template->tlv.p = ak4396_db_scale;
++	}
++	return 0;
++}
++
++static const struct oxygen_model model_generic = {
++	.shortname = "C-Media CMI8788",
++	.longname = "C-Media Oxygen HD Audio",
++	.chip = "CMI8788",
++	.owner = THIS_MODULE,
++	.init = generic_init,
++	.control_filter = ak4396_control_filter,
++	.cleanup = generic_cleanup,
++	.set_dac_params = set_ak4396_params,
++	.set_adc_params = set_wm8785_params,
++	.update_dac_volume = update_ak4396_volume,
++	.update_dac_mute = update_ak4396_mute,
++	.model_data_size = sizeof(struct generic_data),
++	.dac_channels = 8,
++	.used_channels = OXYGEN_CHANNEL_A |
++			 OXYGEN_CHANNEL_C |
++			 OXYGEN_CHANNEL_SPDIF |
++			 OXYGEN_CHANNEL_MULTICH |
++			 OXYGEN_CHANNEL_AC97,
++	.function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
++	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++};
++static const struct oxygen_model model_meridian = {
++	.shortname = "C-Media CMI8788",
++	.longname = "C-Media Oxygen HD Audio",
++	.chip = "CMI8788",
++	.owner = THIS_MODULE,
++	.init = meridian_init,
++	.control_filter = ak4396_control_filter,
++	.cleanup = generic_cleanup,
++	.set_dac_params = set_ak4396_params,
++	.set_adc_params = set_ak5385_params,
++	.update_dac_volume = update_ak4396_volume,
++	.update_dac_mute = update_ak4396_mute,
++	.model_data_size = sizeof(struct generic_data),
++	.dac_channels = 8,
++	.used_channels = OXYGEN_CHANNEL_B |
++			 OXYGEN_CHANNEL_C |
++			 OXYGEN_CHANNEL_SPDIF |
++			 OXYGEN_CHANNEL_MULTICH |
++			 OXYGEN_CHANNEL_AC97,
++	.function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
++	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++};
++
++static int __devinit generic_oxygen_probe(struct pci_dev *pci,
++					  const struct pci_device_id *pci_id)
++{
++	static int dev;
++	int is_meridian;
++	int err;
++
++	if (dev >= SNDRV_CARDS)
++		return -ENODEV;
++	if (!enable[dev]) {
++		++dev;
++		return -ENOENT;
++	}
++	is_meridian = pci_id->driver_data;
++	err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian,
++			       is_meridian ? &model_meridian : &model_generic);
++	if (err >= 0)
++		++dev;
++	return err;
++}
++
++static struct pci_driver oxygen_driver = {
++	.name = "CMI8788",
++	.id_table = oxygen_ids,
++	.probe = generic_oxygen_probe,
++	.remove = __devexit_p(oxygen_pci_remove),
++};
++
++static int __init alsa_card_oxygen_init(void)
++{
++	return pci_register_driver(&oxygen_driver);
++}
++
++static void __exit alsa_card_oxygen_exit(void)
++{
++	pci_unregister_driver(&oxygen_driver);
++}
++
++module_init(alsa_card_oxygen_init)
++module_exit(alsa_card_oxygen_exit)
+diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
+new file mode 100644
+index 0000000..ad50fb8
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen.h
+@@ -0,0 +1,190 @@
++#ifndef OXYGEN_H_INCLUDED
++#define OXYGEN_H_INCLUDED
++
++#include <linux/mutex.h>
++#include <linux/spinlock.h>
++#include <linux/wait.h>
++#include <linux/workqueue.h>
++#include "oxygen_regs.h"
++
++/* 1 << PCM_x == OXYGEN_CHANNEL_x */
++#define PCM_A		0
++#define PCM_B		1
++#define PCM_C		2
++#define PCM_SPDIF	3
++#define PCM_MULTICH	4
++#define PCM_AC97	5
++#define PCM_COUNT	6
++
++enum {
++	CONTROL_SPDIF_PCM,
++	CONTROL_SPDIF_INPUT_BITS,
++	CONTROL_MIC_CAPTURE_SWITCH,
++	CONTROL_LINE_CAPTURE_SWITCH,
++	CONTROL_CD_CAPTURE_SWITCH,
++	CONTROL_AUX_CAPTURE_SWITCH,
++	CONTROL_COUNT
++};
++
++#define OXYGEN_PCI_SUBID(sv, sd) \
++	.vendor = PCI_VENDOR_ID_CMEDIA, \
++	.device = 0x8788, \
++	.subvendor = sv, \
++	.subdevice = sd
++
++struct pci_dev;
++struct snd_card;
++struct snd_pcm_substream;
++struct snd_pcm_hardware;
++struct snd_pcm_hw_params;
++struct snd_kcontrol_new;
++struct snd_rawmidi;
++struct oxygen_model;
++
++struct oxygen {
++	unsigned long addr;
++	spinlock_t reg_lock;
++	struct mutex mutex;
++	struct snd_card *card;
++	struct pci_dev *pci;
++	struct snd_rawmidi *midi;
++	int irq;
++	const struct oxygen_model *model;
++	void *model_data;
++	unsigned int interrupt_mask;
++	u8 dac_volume[8];
++	u8 dac_mute;
++	u8 pcm_active;
++	u8 pcm_running;
++	u8 dac_routing;
++	u8 spdif_playback_enable;
++	u8 revision;
++	u8 has_ac97_0;
++	u8 has_ac97_1;
++	u32 spdif_bits;
++	u32 spdif_pcm_bits;
++	struct snd_pcm_substream *streams[PCM_COUNT];
++	struct snd_kcontrol *controls[CONTROL_COUNT];
++	struct work_struct spdif_input_bits_work;
++	struct work_struct gpio_work;
++	wait_queue_head_t ac97_waitqueue;
++};
++
++struct oxygen_model {
++	const char *shortname;
++	const char *longname;
++	const char *chip;
++	struct module *owner;
++	void (*init)(struct oxygen *chip);
++	int (*control_filter)(struct snd_kcontrol_new *template);
++	int (*mixer_init)(struct oxygen *chip);
++	void (*cleanup)(struct oxygen *chip);
++	void (*pcm_hardware_filter)(unsigned int channel,
++				    struct snd_pcm_hardware *hardware);
++	void (*set_dac_params)(struct oxygen *chip,
++			       struct snd_pcm_hw_params *params);
++	void (*set_adc_params)(struct oxygen *chip,
++			       struct snd_pcm_hw_params *params);
++	void (*update_dac_volume)(struct oxygen *chip);
++	void (*update_dac_mute)(struct oxygen *chip);
++	void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec,
++				 unsigned int reg, int mute);
++	void (*gpio_changed)(struct oxygen *chip);
++	size_t model_data_size;
++	u8 dac_channels;
++	u8 used_channels;
++	u8 function_flags;
++	u16 dac_i2s_format;
++	u16 adc_i2s_format;
++};
++
++/* oxygen_lib.c */
++
++int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi,
++		     const struct oxygen_model *model);
++void oxygen_pci_remove(struct pci_dev *pci);
++
++/* oxygen_mixer.c */
++
++int oxygen_mixer_init(struct oxygen *chip);
++void oxygen_update_dac_routing(struct oxygen *chip);
++void oxygen_update_spdif_source(struct oxygen *chip);
++
++/* oxygen_pcm.c */
++
++int oxygen_pcm_init(struct oxygen *chip);
++
++/* oxygen_io.c */
++
++u8 oxygen_read8(struct oxygen *chip, unsigned int reg);
++u16 oxygen_read16(struct oxygen *chip, unsigned int reg);
++u32 oxygen_read32(struct oxygen *chip, unsigned int reg);
++void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value);
++void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value);
++void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value);
++void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
++			  u8 value, u8 mask);
++void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
++			   u16 value, u16 mask);
++void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
++			   u32 value, u32 mask);
++
++u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
++		     unsigned int index);
++void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
++		       unsigned int index, u16 data);
++void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
++			      unsigned int index, u16 data, u16 mask);
++
++void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
++
++static inline void oxygen_set_bits8(struct oxygen *chip,
++				    unsigned int reg, u8 value)
++{
++	oxygen_write8_masked(chip, reg, value, value);
++}
++
++static inline void oxygen_set_bits16(struct oxygen *chip,
++				     unsigned int reg, u16 value)
++{
++	oxygen_write16_masked(chip, reg, value, value);
++}
++
++static inline void oxygen_set_bits32(struct oxygen *chip,
++				     unsigned int reg, u32 value)
++{
++	oxygen_write32_masked(chip, reg, value, value);
++}
++
++static inline void oxygen_clear_bits8(struct oxygen *chip,
++				      unsigned int reg, u8 value)
++{
++	oxygen_write8_masked(chip, reg, 0, value);
++}
++
++static inline void oxygen_clear_bits16(struct oxygen *chip,
++				       unsigned int reg, u16 value)
++{
++	oxygen_write16_masked(chip, reg, 0, value);
++}
++
++static inline void oxygen_clear_bits32(struct oxygen *chip,
++				       unsigned int reg, u32 value)
++{
++	oxygen_write32_masked(chip, reg, 0, value);
++}
++
++static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec,
++					unsigned int index, u16 value)
++{
++	oxygen_write_ac97_masked(chip, codec, index, value, value);
++}
++
++static inline void oxygen_ac97_clear_bits(struct oxygen *chip,
++					  unsigned int codec,
++					  unsigned int index, u16 value)
++{
++	oxygen_write_ac97_masked(chip, codec, index, 0, value);
++}
++
++#endif
+diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
+new file mode 100644
+index 0000000..74e23ef
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen_io.c
+@@ -0,0 +1,201 @@
++/*
++ * C-Media CMI8788 driver - helper functions
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <sound/core.h>
++#include <asm/io.h>
++#include "oxygen.h"
++
++u8 oxygen_read8(struct oxygen *chip, unsigned int reg)
++{
++	return inb(chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_read8);
++
++u16 oxygen_read16(struct oxygen *chip, unsigned int reg)
++{
++	return inw(chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_read16);
++
++u32 oxygen_read32(struct oxygen *chip, unsigned int reg)
++{
++	return inl(chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_read32);
++
++void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
++{
++	outb(value, chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_write8);
++
++void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
++{
++	outw(value, chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_write16);
++
++void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
++{
++	outl(value, chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_write32);
++
++void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
++			  u8 value, u8 mask)
++{
++	u8 tmp = inb(chip->addr + reg);
++	outb((tmp & ~mask) | (value & mask), chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_write8_masked);
++
++void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
++			   u16 value, u16 mask)
++{
++	u16 tmp = inw(chip->addr + reg);
++	outw((tmp & ~mask) | (value & mask), chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_write16_masked);
++
++void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
++			   u32 value, u32 mask)
++{
++	u32 tmp = inl(chip->addr + reg);
++	outl((tmp & ~mask) | (value & mask), chip->addr + reg);
++}
++EXPORT_SYMBOL(oxygen_write32_masked);
++
++static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask)
++{
++	u8 status = 0;
++
++	/*
++	 * Reading the status register also clears the bits, so we have to save
++	 * the read bits in status.
++	 */
++	wait_event_timeout(chip->ac97_waitqueue,
++			   ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
++			      status & mask; }),
++			   msecs_to_jiffies(1) + 1);
++	/*
++	 * Check even after a timeout because this function should not require
++	 * the AC'97 interrupt to be enabled.
++	 */
++	status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
++	return status & mask ? 0 : -EIO;
++}
++
++/*
++ * About 10% of AC'97 register reads or writes fail to complete, but even those
++ * where the controller indicates completion aren't guaranteed to have actually
++ * happened.
++ *
++ * It's hard to assign blame to either the controller or the codec because both
++ * were made by C-Media ...
++ */
++
++void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
++		       unsigned int index, u16 data)
++{
++	unsigned int count, succeeded;
++	u32 reg;
++
++	reg = data;
++	reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT;
++	reg |= OXYGEN_AC97_REG_DIR_WRITE;
++	reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
++	succeeded = 0;
++	for (count = 5; count > 0; --count) {
++		udelay(5);
++		oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
++		/* require two "completed" writes, just to be sure */
++		if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
++		    ++succeeded >= 2)
++			return;
++	}
++	snd_printk(KERN_ERR "AC'97 write timeout\n");
++}
++EXPORT_SYMBOL(oxygen_write_ac97);
++
++u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
++		     unsigned int index)
++{
++	unsigned int count;
++	unsigned int last_read = UINT_MAX;
++	u32 reg;
++
++	reg = index << OXYGEN_AC97_REG_ADDR_SHIFT;
++	reg |= OXYGEN_AC97_REG_DIR_READ;
++	reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
++	for (count = 5; count > 0; --count) {
++		udelay(5);
++		oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
++		udelay(10);
++		if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) {
++			u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS);
++			/* we require two consecutive reads of the same value */
++			if (value == last_read)
++				return value;
++			last_read = value;
++			/*
++			 * Invert the register value bits to make sure that two
++			 * consecutive unsuccessful reads do not return the same
++			 * value.
++			 */
++			reg ^= 0xffff;
++		}
++	}
++	snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec);
++	return 0;
++}
++EXPORT_SYMBOL(oxygen_read_ac97);
++
++void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
++			      unsigned int index, u16 data, u16 mask)
++{
++	u16 value = oxygen_read_ac97(chip, codec, index);
++	value &= ~mask;
++	value |= data & mask;
++	oxygen_write_ac97(chip, codec, index, value);
++}
++EXPORT_SYMBOL(oxygen_write_ac97_masked);
++
++void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
++{
++	unsigned int count;
++
++	/* should not need more than 7.68 us (24 * 320 ns) */
++	count = 10;
++	while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
++	       && count > 0) {
++		udelay(1);
++		--count;
++	}
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
++	oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
++	if (control & OXYGEN_SPI_DATA_LENGTH_3)
++		oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
++	oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
++	spin_unlock_irq(&chip->reg_lock);
++}
++EXPORT_SYMBOL(oxygen_write_spi);
+diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
+new file mode 100644
+index 0000000..6eb36dd
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen_lib.c
+@@ -0,0 +1,515 @@
++/*
++ * C-Media CMI8788 driver - main driver module
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <sound/ac97_codec.h>
++#include <sound/asoundef.h>
++#include <sound/core.h>
++#include <sound/info.h>
++#include <sound/mpu401.h>
++#include <sound/pcm.h>
++#include "oxygen.h"
++#include "cm9780.h"
++
++MODULE_AUTHOR("Clemens Ladisch <clemens at ladisch.de>");
++MODULE_DESCRIPTION("C-Media CMI8788 helper library");
++MODULE_LICENSE("GPL");
++
++
++static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
++{
++	struct oxygen *chip = dev_id;
++	unsigned int status, clear, elapsed_streams, i;
++
++	status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS);
++	if (!status)
++		return IRQ_NONE;
++
++	spin_lock(&chip->reg_lock);
++
++	clear = status & (OXYGEN_CHANNEL_A |
++			  OXYGEN_CHANNEL_B |
++			  OXYGEN_CHANNEL_C |
++			  OXYGEN_CHANNEL_SPDIF |
++			  OXYGEN_CHANNEL_MULTICH |
++			  OXYGEN_CHANNEL_AC97 |
++			  OXYGEN_INT_SPDIF_IN_DETECT |
++			  OXYGEN_INT_GPIO |
++			  OXYGEN_INT_AC97);
++	if (clear) {
++		if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
++			chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
++		oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
++			       chip->interrupt_mask & ~clear);
++		oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
++			       chip->interrupt_mask);
++	}
++
++	elapsed_streams = status & chip->pcm_running;
++
++	spin_unlock(&chip->reg_lock);
++
++	for (i = 0; i < PCM_COUNT; ++i)
++		if ((elapsed_streams & (1 << i)) && chip->streams[i])
++			snd_pcm_period_elapsed(chip->streams[i]);
++
++	if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
++		spin_lock(&chip->reg_lock);
++		i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
++		if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
++			 OXYGEN_SPDIF_RATE_INT)) {
++			/* write the interrupt bit(s) to clear */
++			oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
++			schedule_work(&chip->spdif_input_bits_work);
++		}
++		spin_unlock(&chip->reg_lock);
++	}
++
++	if (status & OXYGEN_INT_GPIO)
++		schedule_work(&chip->gpio_work);
++
++	if ((status & OXYGEN_INT_MIDI) && chip->midi)
++		snd_mpu401_uart_interrupt(0, chip->midi->private_data);
++
++	if (status & OXYGEN_INT_AC97)
++		wake_up(&chip->ac97_waitqueue);
++
++	return IRQ_HANDLED;
++}
++
++static void oxygen_spdif_input_bits_changed(struct work_struct *work)
++{
++	struct oxygen *chip = container_of(work, struct oxygen,
++					   spdif_input_bits_work);
++	u32 reg;
++
++	/*
++	 * This function gets called when there is new activity on the SPDIF
++	 * input, or when we lose lock on the input signal, or when the rate
++	 * changes.
++	 */
++	msleep(1);
++	spin_lock_irq(&chip->reg_lock);
++	reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
++	if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
++		    OXYGEN_SPDIF_LOCK_STATUS))
++	    == OXYGEN_SPDIF_SENSE_STATUS) {
++		/*
++		 * If we detect activity on the SPDIF input but cannot lock to
++		 * a signal, the clock bit is likely to be wrong.
++		 */
++		reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
++		oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
++		spin_unlock_irq(&chip->reg_lock);
++		msleep(1);
++		spin_lock_irq(&chip->reg_lock);
++		reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
++		if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
++			    OXYGEN_SPDIF_LOCK_STATUS))
++		    == OXYGEN_SPDIF_SENSE_STATUS) {
++			/* nothing detected with either clock; give up */
++			if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
++			    == OXYGEN_SPDIF_IN_CLOCK_192) {
++				/*
++				 * Reset clock to <= 96 kHz because this is
++				 * more likely to be received next time.
++				 */
++				reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
++				reg |= OXYGEN_SPDIF_IN_CLOCK_96;
++				oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
++			}
++		}
++	}
++	spin_unlock_irq(&chip->reg_lock);
++
++	if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
++		spin_lock_irq(&chip->reg_lock);
++		chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
++		oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
++			       chip->interrupt_mask);
++		spin_unlock_irq(&chip->reg_lock);
++
++		/*
++		 * We don't actually know that any channel status bits have
++		 * changed, but let's send a notification just to be sure.
++		 */
++		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
++			       &chip->controls[CONTROL_SPDIF_INPUT_BITS]->id);
++	}
++}
++
++static void oxygen_gpio_changed(struct work_struct *work)
++{
++	struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
++
++	if (chip->model->gpio_changed)
++		chip->model->gpio_changed(chip);
++}
++
++#ifdef CONFIG_PROC_FS
++static void oxygen_proc_read(struct snd_info_entry *entry,
++			     struct snd_info_buffer *buffer)
++{
++	struct oxygen *chip = entry->private_data;
++	int i, j;
++
++	snd_iprintf(buffer, "CMI8788\n\n");
++	for (i = 0; i < 0x100; i += 0x10) {
++		snd_iprintf(buffer, "%02x:", i);
++		for (j = 0; j < 0x10; ++j)
++			snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
++		snd_iprintf(buffer, "\n");
++	}
++	if (mutex_lock_interruptible(&chip->mutex) < 0)
++		return;
++	if (chip->has_ac97_0) {
++		snd_iprintf(buffer, "\nAC97\n");
++		for (i = 0; i < 0x80; i += 0x10) {
++			snd_iprintf(buffer, "%02x:", i);
++			for (j = 0; j < 0x10; j += 2)
++				snd_iprintf(buffer, " %04x",
++					    oxygen_read_ac97(chip, 0, i + j));
++			snd_iprintf(buffer, "\n");
++		}
++	}
++	if (chip->has_ac97_1) {
++		snd_iprintf(buffer, "\nAC97 2\n");
++		for (i = 0; i < 0x80; i += 0x10) {
++			snd_iprintf(buffer, "%02x:", i);
++			for (j = 0; j < 0x10; j += 2)
++				snd_iprintf(buffer, " %04x",
++					    oxygen_read_ac97(chip, 1, i + j));
++			snd_iprintf(buffer, "\n");
++		}
++	}
++	mutex_unlock(&chip->mutex);
++}
++
++static void __devinit oxygen_proc_init(struct oxygen *chip)
++{
++	struct snd_info_entry *entry;
++
++	if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
++		snd_info_set_text_ops(entry, chip, oxygen_proc_read);
++}
++#else
++#define oxygen_proc_init(chip)
++#endif
++
++static void __devinit oxygen_init(struct oxygen *chip)
++{
++	unsigned int i;
++
++	chip->dac_routing = 1;
++	for (i = 0; i < 8; ++i)
++		chip->dac_volume[i] = 0xff;
++	chip->spdif_playback_enable = 1;
++	chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
++		(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
++	chip->spdif_pcm_bits = chip->spdif_bits;
++
++	if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
++		chip->revision = 2;
++	else
++		chip->revision = 1;
++
++	if (chip->revision == 1)
++		oxygen_set_bits8(chip, OXYGEN_MISC,
++				 OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
++
++	i = oxygen_read16(chip, OXYGEN_AC97_CONTROL);
++	chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
++	chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
++
++	oxygen_set_bits8(chip, OXYGEN_FUNCTION,
++			 OXYGEN_FUNCTION_RESET_CODEC |
++			 chip->model->function_flags);
++	oxygen_write8_masked(chip, OXYGEN_FUNCTION,
++			     OXYGEN_FUNCTION_SPI,
++			     OXYGEN_FUNCTION_2WIRE_SPI_MASK);
++	oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
++	oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
++	oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
++		      OXYGEN_PLAY_CHANNELS_2 |
++		      OXYGEN_DMA_A_BURST_8 |
++		      OXYGEN_DMA_MULTICH_BURST_8);
++	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
++	oxygen_write8_masked(chip, OXYGEN_MISC, 0,
++			     OXYGEN_MISC_WRITE_PCI_SUBID |
++			     OXYGEN_MISC_REC_C_FROM_SPDIF |
++			     OXYGEN_MISC_REC_B_FROM_AC97 |
++			     OXYGEN_MISC_REC_A_FROM_MULTICH);
++	oxygen_write8(chip, OXYGEN_REC_FORMAT,
++		      (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
++		      (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
++		      (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT));
++	oxygen_write8(chip, OXYGEN_PLAY_FORMAT,
++		      (OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) |
++		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
++	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
++	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
++		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
++		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
++		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
++	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
++		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
++		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
++		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
++	oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
++		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
++		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
++		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
++	oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
++		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST |
++		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
++		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
++	oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
++			      OXYGEN_SPDIF_SENSE_MASK |
++			      OXYGEN_SPDIF_LOCK_MASK |
++			      OXYGEN_SPDIF_RATE_MASK |
++			      OXYGEN_SPDIF_LOCK_PAR |
++			      OXYGEN_SPDIF_IN_CLOCK_96,
++			      OXYGEN_SPDIF_OUT_ENABLE |
++			      OXYGEN_SPDIF_LOOPBACK |
++			      OXYGEN_SPDIF_SENSE_MASK |
++			      OXYGEN_SPDIF_LOCK_MASK |
++			      OXYGEN_SPDIF_RATE_MASK |
++			      OXYGEN_SPDIF_SENSE_PAR |
++			      OXYGEN_SPDIF_LOCK_PAR |
++			      OXYGEN_SPDIF_IN_CLOCK_MASK);
++	oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
++	oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
++	oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
++	oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
++	oxygen_write16(chip, OXYGEN_PLAY_ROUTING,
++		       OXYGEN_PLAY_MULTICH_I2S_DAC |
++		       OXYGEN_PLAY_SPDIF_SPDIF |
++		       (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
++		       (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
++		       (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
++		       (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT));
++	oxygen_write8(chip, OXYGEN_REC_ROUTING,
++		      OXYGEN_REC_A_ROUTE_I2S_ADC_1 |
++		      OXYGEN_REC_B_ROUTE_I2S_ADC_2 |
++		      OXYGEN_REC_C_ROUTE_SPDIF);
++	oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0);
++	oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING,
++		      (0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) |
++		      (1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) |
++		      (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
++		      (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
++
++	oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
++		      OXYGEN_AC97_INT_READ_DONE |
++		      OXYGEN_AC97_INT_WRITE_DONE);
++	oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
++	oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
++	if (!(chip->has_ac97_0 | chip->has_ac97_1))
++		oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
++				  OXYGEN_AC97_CLOCK_DISABLE);
++	if (!chip->has_ac97_0) {
++		oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
++				  OXYGEN_AC97_NO_CODEC_0);
++	} else {
++		oxygen_write_ac97(chip, 0, AC97_RESET, 0);
++		msleep(1);
++		oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP,
++				     CM9780_GPIO0IO | CM9780_GPIO1IO);
++		oxygen_ac97_set_bits(chip, 0, CM9780_MIXER,
++				     CM9780_BSTSEL | CM9780_STRO_MIC |
++				     CM9780_MIX2FR | CM9780_PCBSW);
++		oxygen_ac97_set_bits(chip, 0, CM9780_JACK,
++				     CM9780_RSOE | CM9780_CBOE |
++				     CM9780_SSOE | CM9780_FROE |
++				     CM9780_MIC2MIC | CM9780_LI2LI);
++		oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000);
++		oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000);
++		oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808);
++		oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808);
++		oxygen_write_ac97(chip, 0, AC97_CD, 0x8808);
++		oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808);
++		oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808);
++		oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
++		oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
++		oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
++		/* power down unused ADCs and DACs */
++		oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
++				     AC97_PD_PR0 | AC97_PD_PR1);
++		oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS,
++				     AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK);
++	}
++	if (chip->has_ac97_1) {
++		oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG,
++				  OXYGEN_AC97_CODEC1_SLOT3 |
++				  OXYGEN_AC97_CODEC1_SLOT4);
++		oxygen_write_ac97(chip, 1, AC97_RESET, 0);
++		msleep(1);
++		oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000);
++		oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000);
++		oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000);
++		oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808);
++		oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808);
++		oxygen_write_ac97(chip, 1, AC97_CD, 0x8808);
++		oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808);
++		oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808);
++		oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808);
++		oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000);
++		oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000);
++		oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040);
++	}
++}
++
++static void oxygen_card_free(struct snd_card *card)
++{
++	struct oxygen *chip = card->private_data;
++
++	spin_lock_irq(&chip->reg_lock);
++	chip->interrupt_mask = 0;
++	chip->pcm_running = 0;
++	oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
++	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
++	spin_unlock_irq(&chip->reg_lock);
++	if (chip->irq >= 0) {
++		free_irq(chip->irq, chip);
++		synchronize_irq(chip->irq);
++	}
++	flush_scheduled_work();
++	chip->model->cleanup(chip);
++	mutex_destroy(&chip->mutex);
++	pci_release_regions(chip->pci);
++	pci_disable_device(chip->pci);
++}
++
++int __devinit oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
++			       int midi, const struct oxygen_model *model)
++{
++	struct snd_card *card;
++	struct oxygen *chip;
++	int err;
++
++	card = snd_card_new(index, id, model->owner,
++			    sizeof *chip + model->model_data_size);
++	if (!card)
++		return -ENOMEM;
++
++	chip = card->private_data;
++	chip->card = card;
++	chip->pci = pci;
++	chip->irq = -1;
++	chip->model = model;
++	chip->model_data = chip + 1;
++	spin_lock_init(&chip->reg_lock);
++	mutex_init(&chip->mutex);
++	INIT_WORK(&chip->spdif_input_bits_work,
++		  oxygen_spdif_input_bits_changed);
++	INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
++	init_waitqueue_head(&chip->ac97_waitqueue);
++
++	err = pci_enable_device(pci);
++	if (err < 0)
++		goto err_card;
++
++	err = pci_request_regions(pci, model->chip);
++	if (err < 0) {
++		snd_printk(KERN_ERR "cannot reserve PCI resources\n");
++		goto err_pci_enable;
++	}
++
++	if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
++	    pci_resource_len(pci, 0) < 0x100) {
++		snd_printk(KERN_ERR "invalid PCI I/O range\n");
++		err = -ENXIO;
++		goto err_pci_regions;
++	}
++	chip->addr = pci_resource_start(pci, 0);
++
++	pci_set_master(pci);
++	snd_card_set_dev(card, &pci->dev);
++	card->private_free = oxygen_card_free;
++
++	oxygen_init(chip);
++	model->init(chip);
++
++	err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
++			  model->chip, chip);
++	if (err < 0) {
++		snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
++		goto err_card;
++	}
++	chip->irq = pci->irq;
++
++	strcpy(card->driver, model->chip);
++	strcpy(card->shortname, model->shortname);
++	sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
++		model->longname, chip->revision, chip->addr, chip->irq);
++	strcpy(card->mixername, model->chip);
++	snd_component_add(card, model->chip);
++
++	err = oxygen_pcm_init(chip);
++	if (err < 0)
++		goto err_card;
++
++	err = oxygen_mixer_init(chip);
++	if (err < 0)
++		goto err_card;
++
++	oxygen_write8_masked(chip, OXYGEN_MISC,
++			     midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI);
++	if (midi) {
++		err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
++					  chip->addr + OXYGEN_MPU401,
++					  MPU401_INFO_INTEGRATED, 0, 0,
++					  &chip->midi);
++		if (err < 0)
++			goto err_card;
++	}
++
++	oxygen_proc_init(chip);
++
++	spin_lock_irq(&chip->reg_lock);
++	chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97;
++	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
++	spin_unlock_irq(&chip->reg_lock);
++
++	err = snd_card_register(card);
++	if (err < 0)
++		goto err_card;
++
++	pci_set_drvdata(pci, card);
++	return 0;
++
++err_pci_regions:
++	pci_release_regions(pci);
++err_pci_enable:
++	pci_disable_device(pci);
++err_card:
++	snd_card_free(card);
++	return err;
++}
++EXPORT_SYMBOL(oxygen_pci_probe);
++
++void __devexit oxygen_pci_remove(struct pci_dev *pci)
++{
++	snd_card_free(pci_get_drvdata(pci));
++	pci_set_drvdata(pci, NULL);
++}
++EXPORT_SYMBOL(oxygen_pci_remove);
+diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
+new file mode 100644
+index 0000000..a8e4623
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen_mixer.c
+@@ -0,0 +1,794 @@
++/*
++ * C-Media CMI8788 driver - mixer code
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++#include <linux/mutex.h>
++#include <sound/ac97_codec.h>
++#include <sound/asoundef.h>
++#include <sound/control.h>
++#include <sound/tlv.h>
++#include "oxygen.h"
++#include "cm9780.h"
++
++static int dac_volume_info(struct snd_kcontrol *ctl,
++			   struct snd_ctl_elem_info *info)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	info->count = chip->model->dac_channels;
++	info->value.integer.min = 0;
++	info->value.integer.max = 0xff;
++	return 0;
++}
++
++static int dac_volume_get(struct snd_kcontrol *ctl,
++			  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int i;
++
++	mutex_lock(&chip->mutex);
++	for (i = 0; i < chip->model->dac_channels; ++i)
++		value->value.integer.value[i] = chip->dac_volume[i];
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static int dac_volume_put(struct snd_kcontrol *ctl,
++			  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int i;
++	int changed;
++
++	changed = 0;
++	mutex_lock(&chip->mutex);
++	for (i = 0; i < chip->model->dac_channels; ++i)
++		if (value->value.integer.value[i] != chip->dac_volume[i]) {
++			chip->dac_volume[i] = value->value.integer.value[i];
++			changed = 1;
++		}
++	if (changed)
++		chip->model->update_dac_volume(chip);
++	mutex_unlock(&chip->mutex);
++	return changed;
++}
++
++static int dac_mute_get(struct snd_kcontrol *ctl,
++			struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	mutex_lock(&chip->mutex);
++	value->value.integer.value[0] = !chip->dac_mute;
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static int dac_mute_put(struct snd_kcontrol *ctl,
++			  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	int changed;
++
++	mutex_lock(&chip->mutex);
++	changed = !value->value.integer.value[0] != chip->dac_mute;
++	if (changed) {
++		chip->dac_mute = !value->value.integer.value[0];
++		chip->model->update_dac_mute(chip);
++	}
++	mutex_unlock(&chip->mutex);
++	return changed;
++}
++
++static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
++{
++	static const char *const names[3] = {
++		"Front", "Front+Surround", "Front+Surround+Back"
++	};
++	struct oxygen *chip = ctl->private_data;
++	unsigned int count = 2 + (chip->model->dac_channels == 8);
++
++	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
++	info->count = 1;
++	info->value.enumerated.items = count;
++	if (info->value.enumerated.item >= count)
++		info->value.enumerated.item = count - 1;
++	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
++	return 0;
++}
++
++static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	mutex_lock(&chip->mutex);
++	value->value.enumerated.item[0] = chip->dac_routing;
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++void oxygen_update_dac_routing(struct oxygen *chip)
++{
++	/* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */
++	static const unsigned int reg_values[3] = {
++		/* stereo -> front */
++		(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
++		(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
++		(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
++		(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
++		/* stereo -> front+surround */
++		(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
++		(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
++		(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
++		(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
++		/* stereo -> front+surround+back */
++		(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
++		(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
++		(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
++		(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
++	};
++	u8 channels;
++	unsigned int reg_value;
++
++	channels = oxygen_read8(chip, OXYGEN_PLAY_CHANNELS) &
++		OXYGEN_PLAY_CHANNELS_MASK;
++	if (channels == OXYGEN_PLAY_CHANNELS_2)
++		reg_value = reg_values[chip->dac_routing];
++	else if (channels == OXYGEN_PLAY_CHANNELS_8)
++		/* in 7.1 mode, "rear" channels go to the "back" jack */
++		reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
++			    (3 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
++			    (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
++			    (1 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
++	else
++		reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
++			    (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
++			    (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
++			    (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
++	oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value,
++			      OXYGEN_PLAY_DAC0_SOURCE_MASK |
++			      OXYGEN_PLAY_DAC1_SOURCE_MASK |
++			      OXYGEN_PLAY_DAC2_SOURCE_MASK |
++			      OXYGEN_PLAY_DAC3_SOURCE_MASK);
++}
++
++static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int count = 2 + (chip->model->dac_channels == 8);
++	int changed;
++
++	mutex_lock(&chip->mutex);
++	changed = value->value.enumerated.item[0] != chip->dac_routing;
++	if (changed) {
++		chip->dac_routing = min(value->value.enumerated.item[0],
++					count - 1);
++		spin_lock_irq(&chip->reg_lock);
++		oxygen_update_dac_routing(chip);
++		spin_unlock_irq(&chip->reg_lock);
++	}
++	mutex_unlock(&chip->mutex);
++	return changed;
++}
++
++static int spdif_switch_get(struct snd_kcontrol *ctl,
++			    struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	mutex_lock(&chip->mutex);
++	value->value.integer.value[0] = chip->spdif_playback_enable;
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate)
++{
++	switch (oxygen_rate) {
++	case OXYGEN_RATE_32000:
++		return IEC958_AES3_CON_FS_32000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	case OXYGEN_RATE_44100:
++		return IEC958_AES3_CON_FS_44100 << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	default: /* OXYGEN_RATE_48000 */
++		return IEC958_AES3_CON_FS_48000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	case OXYGEN_RATE_64000:
++		return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	case OXYGEN_RATE_88200:
++		return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	case OXYGEN_RATE_96000:
++		return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	case OXYGEN_RATE_176400:
++		return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	case OXYGEN_RATE_192000:
++		return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT;
++	}
++}
++
++void oxygen_update_spdif_source(struct oxygen *chip)
++{
++	u32 old_control, new_control;
++	u16 old_routing, new_routing;
++	unsigned int oxygen_rate;
++
++	old_control = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
++	old_routing = oxygen_read16(chip, OXYGEN_PLAY_ROUTING);
++	if (chip->pcm_active & (1 << PCM_SPDIF)) {
++		new_control = old_control | OXYGEN_SPDIF_OUT_ENABLE;
++		new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK)
++			| OXYGEN_PLAY_SPDIF_SPDIF;
++		oxygen_rate = (old_control >> OXYGEN_SPDIF_OUT_RATE_SHIFT)
++			& OXYGEN_I2S_RATE_MASK;
++		/* S/PDIF rate was already set by the caller */
++	} else if ((chip->pcm_active & (1 << PCM_MULTICH)) &&
++		   chip->spdif_playback_enable) {
++		new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK)
++			| OXYGEN_PLAY_SPDIF_MULTICH_01;
++		oxygen_rate = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT)
++			& OXYGEN_I2S_RATE_MASK;
++		new_control = (old_control & ~OXYGEN_SPDIF_OUT_RATE_MASK) |
++			(oxygen_rate << OXYGEN_SPDIF_OUT_RATE_SHIFT) |
++			OXYGEN_SPDIF_OUT_ENABLE;
++	} else {
++		new_control = old_control & ~OXYGEN_SPDIF_OUT_ENABLE;
++		new_routing = old_routing;
++		oxygen_rate = OXYGEN_RATE_44100;
++	}
++	if (old_routing != new_routing) {
++		oxygen_write32(chip, OXYGEN_SPDIF_CONTROL,
++			       new_control & ~OXYGEN_SPDIF_OUT_ENABLE);
++		oxygen_write16(chip, OXYGEN_PLAY_ROUTING, new_routing);
++	}
++	if (new_control & OXYGEN_SPDIF_OUT_ENABLE)
++		oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS,
++			       oxygen_spdif_rate(oxygen_rate) |
++			       ((chip->pcm_active & (1 << PCM_SPDIF)) ?
++				chip->spdif_pcm_bits : chip->spdif_bits));
++	oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, new_control);
++}
++
++static int spdif_switch_put(struct snd_kcontrol *ctl,
++			    struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	int changed;
++
++	mutex_lock(&chip->mutex);
++	changed = value->value.integer.value[0] != chip->spdif_playback_enable;
++	if (changed) {
++		chip->spdif_playback_enable = !!value->value.integer.value[0];
++		spin_lock_irq(&chip->reg_lock);
++		oxygen_update_spdif_source(chip);
++		spin_unlock_irq(&chip->reg_lock);
++	}
++	mutex_unlock(&chip->mutex);
++	return changed;
++}
++
++static int spdif_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
++{
++	info->type = SNDRV_CTL_ELEM_TYPE_IEC958;
++	info->count = 1;
++	return 0;
++}
++
++static void oxygen_to_iec958(u32 bits, struct snd_ctl_elem_value *value)
++{
++	value->value.iec958.status[0] =
++		bits & (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C |
++			OXYGEN_SPDIF_PREEMPHASIS);
++	value->value.iec958.status[1] = /* category and original */
++		bits >> OXYGEN_SPDIF_CATEGORY_SHIFT;
++}
++
++static u32 iec958_to_oxygen(struct snd_ctl_elem_value *value)
++{
++	u32 bits;
++
++	bits = value->value.iec958.status[0] &
++		(OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C |
++		 OXYGEN_SPDIF_PREEMPHASIS);
++	bits |= value->value.iec958.status[1] << OXYGEN_SPDIF_CATEGORY_SHIFT;
++	if (bits & OXYGEN_SPDIF_NONAUDIO)
++		bits |= OXYGEN_SPDIF_V;
++	return bits;
++}
++
++static inline void write_spdif_bits(struct oxygen *chip, u32 bits)
++{
++	oxygen_write32_masked(chip, OXYGEN_SPDIF_OUTPUT_BITS, bits,
++			      OXYGEN_SPDIF_NONAUDIO |
++			      OXYGEN_SPDIF_C |
++			      OXYGEN_SPDIF_PREEMPHASIS |
++			      OXYGEN_SPDIF_CATEGORY_MASK |
++			      OXYGEN_SPDIF_ORIGINAL |
++			      OXYGEN_SPDIF_V);
++}
++
++static int spdif_default_get(struct snd_kcontrol *ctl,
++			     struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	mutex_lock(&chip->mutex);
++	oxygen_to_iec958(chip->spdif_bits, value);
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static int spdif_default_put(struct snd_kcontrol *ctl,
++			     struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u32 new_bits;
++	int changed;
++
++	new_bits = iec958_to_oxygen(value);
++	mutex_lock(&chip->mutex);
++	changed = new_bits != chip->spdif_bits;
++	if (changed) {
++		chip->spdif_bits = new_bits;
++		if (!(chip->pcm_active & (1 << PCM_SPDIF)))
++			write_spdif_bits(chip, new_bits);
++	}
++	mutex_unlock(&chip->mutex);
++	return changed;
++}
++
++static int spdif_mask_get(struct snd_kcontrol *ctl,
++			  struct snd_ctl_elem_value *value)
++{
++	value->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
++		IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS;
++	value->value.iec958.status[1] =
++		IEC958_AES1_CON_CATEGORY | IEC958_AES1_CON_ORIGINAL;
++	return 0;
++}
++
++static int spdif_pcm_get(struct snd_kcontrol *ctl,
++			 struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	mutex_lock(&chip->mutex);
++	oxygen_to_iec958(chip->spdif_pcm_bits, value);
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static int spdif_pcm_put(struct snd_kcontrol *ctl,
++			 struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u32 new_bits;
++	int changed;
++
++	new_bits = iec958_to_oxygen(value);
++	mutex_lock(&chip->mutex);
++	changed = new_bits != chip->spdif_pcm_bits;
++	if (changed) {
++		chip->spdif_pcm_bits = new_bits;
++		if (chip->pcm_active & (1 << PCM_SPDIF))
++			write_spdif_bits(chip, new_bits);
++	}
++	mutex_unlock(&chip->mutex);
++	return changed;
++}
++
++static int spdif_input_mask_get(struct snd_kcontrol *ctl,
++				struct snd_ctl_elem_value *value)
++{
++	value->value.iec958.status[0] = 0xff;
++	value->value.iec958.status[1] = 0xff;
++	value->value.iec958.status[2] = 0xff;
++	value->value.iec958.status[3] = 0xff;
++	return 0;
++}
++
++static int spdif_input_default_get(struct snd_kcontrol *ctl,
++				   struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u32 bits;
++
++	bits = oxygen_read32(chip, OXYGEN_SPDIF_INPUT_BITS);
++	value->value.iec958.status[0] = bits;
++	value->value.iec958.status[1] = bits >> 8;
++	value->value.iec958.status[2] = bits >> 16;
++	value->value.iec958.status[3] = bits >> 24;
++	return 0;
++}
++
++static int spdif_loopback_get(struct snd_kcontrol *ctl,
++			      struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	value->value.integer.value[0] =
++		!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
++		   & OXYGEN_SPDIF_LOOPBACK);
++	return 0;
++}
++
++static int spdif_loopback_put(struct snd_kcontrol *ctl,
++			      struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u32 oldreg, newreg;
++	int changed;
++
++	spin_lock_irq(&chip->reg_lock);
++	oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
++	if (value->value.integer.value[0])
++		newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
++	else
++		newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
++	changed = newreg != oldreg;
++	if (changed)
++		oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
++	spin_unlock_irq(&chip->reg_lock);
++	return changed;
++}
++
++static int ac97_switch_get(struct snd_kcontrol *ctl,
++			   struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int codec = (ctl->private_value >> 24) & 1;
++	unsigned int index = ctl->private_value & 0xff;
++	unsigned int bitnr = (ctl->private_value >> 8) & 0xff;
++	int invert = ctl->private_value & (1 << 16);
++	u16 reg;
++
++	mutex_lock(&chip->mutex);
++	reg = oxygen_read_ac97(chip, codec, index);
++	mutex_unlock(&chip->mutex);
++	if (!(reg & (1 << bitnr)) ^ !invert)
++		value->value.integer.value[0] = 1;
++	else
++		value->value.integer.value[0] = 0;
++	return 0;
++}
++
++static int ac97_switch_put(struct snd_kcontrol *ctl,
++			   struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int codec = (ctl->private_value >> 24) & 1;
++	unsigned int index = ctl->private_value & 0xff;
++	unsigned int bitnr = (ctl->private_value >> 8) & 0xff;
++	int invert = ctl->private_value & (1 << 16);
++	u16 oldreg, newreg;
++	int change;
++
++	mutex_lock(&chip->mutex);
++	oldreg = oxygen_read_ac97(chip, codec, index);
++	newreg = oldreg;
++	if (!value->value.integer.value[0] ^ !invert)
++		newreg |= 1 << bitnr;
++	else
++		newreg &= ~(1 << bitnr);
++	change = newreg != oldreg;
++	if (change) {
++		oxygen_write_ac97(chip, codec, index, newreg);
++		if (bitnr == 15 && chip->model->ac97_switch_hook)
++			chip->model->ac97_switch_hook(chip, codec, index,
++						      newreg & 0x8000);
++	}
++	mutex_unlock(&chip->mutex);
++	return change;
++}
++
++static int ac97_volume_info(struct snd_kcontrol *ctl,
++			    struct snd_ctl_elem_info *info)
++{
++	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	info->count = 2;
++	info->value.integer.min = 0;
++	info->value.integer.max = 0x1f;
++	return 0;
++}
++
++static int ac97_volume_get(struct snd_kcontrol *ctl,
++			   struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int codec = (ctl->private_value >> 24) & 1;
++	unsigned int index = ctl->private_value & 0xff;
++	u16 reg;
++
++	mutex_lock(&chip->mutex);
++	reg = oxygen_read_ac97(chip, codec, index);
++	mutex_unlock(&chip->mutex);
++	value->value.integer.value[0] = 31 - (reg & 0x1f);
++	value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f);
++	return 0;
++}
++
++static int ac97_volume_put(struct snd_kcontrol *ctl,
++			   struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int codec = (ctl->private_value >> 24) & 1;
++	unsigned int index = ctl->private_value & 0xff;
++	u16 oldreg, newreg;
++	int change;
++
++	mutex_lock(&chip->mutex);
++	oldreg = oxygen_read_ac97(chip, codec, index);
++	newreg = oldreg;
++	newreg = (newreg & ~0x1f) |
++		(31 - (value->value.integer.value[0] & 0x1f));
++	newreg = (newreg & ~0x1f00) |
++		((31 - (value->value.integer.value[0] & 0x1f)) << 8);
++	change = newreg != oldreg;
++	if (change)
++		oxygen_write_ac97(chip, codec, index, newreg);
++	mutex_unlock(&chip->mutex);
++	return change;
++}
++
++static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
++				   struct snd_ctl_elem_info *info)
++{
++	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	info->count = 2;
++	info->value.integer.min = 0;
++	info->value.integer.max = 7;
++	return 0;
++}
++
++static int ac97_fp_rec_volume_get(struct snd_kcontrol *ctl,
++				  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u16 reg;
++
++	mutex_lock(&chip->mutex);
++	reg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
++	mutex_unlock(&chip->mutex);
++	value->value.integer.value[0] = reg & 7;
++	value->value.integer.value[1] = (reg >> 8) & 7;
++	return 0;
++}
++
++static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
++				  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u16 oldreg, newreg;
++	int change;
++
++	mutex_lock(&chip->mutex);
++	oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN);
++	newreg = oldreg & ~0x0707;
++	newreg = newreg | (value->value.integer.value[0] & 7);
++	newreg = newreg | ((value->value.integer.value[0] & 7) << 8);
++	change = newreg != oldreg;
++	if (change)
++		oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg);
++	mutex_unlock(&chip->mutex);
++	return change;
++}
++
++#define AC97_SWITCH(xname, codec, index, bitnr, invert) { \
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
++		.name = xname, \
++		.info = snd_ctl_boolean_mono_info, \
++		.get = ac97_switch_get, \
++		.put = ac97_switch_put, \
++		.private_value = ((codec) << 24) | ((invert) << 16) | \
++				 ((bitnr) << 8) | (index), \
++	}
++#define AC97_VOLUME(xname, codec, index) { \
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
++		.name = xname, \
++		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
++			  SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
++		.info = ac97_volume_info, \
++		.get = ac97_volume_get, \
++		.put = ac97_volume_put, \
++		.tlv = { .p = ac97_db_scale, }, \
++		.private_value = ((codec) << 24) | (index), \
++	}
++
++static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
++static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
++
++static const struct snd_kcontrol_new controls[] = {
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Volume",
++		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++		.info = dac_volume_info,
++		.get = dac_volume_get,
++		.put = dac_volume_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Master Playback Switch",
++		.info = snd_ctl_boolean_mono_info,
++		.get = dac_mute_get,
++		.put = dac_mute_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Stereo Upmixing",
++		.info = upmix_info,
++		.get = upmix_get,
++		.put = upmix_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
++		.info = snd_ctl_boolean_mono_info,
++		.get = spdif_switch_get,
++		.put = spdif_switch_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
++		.device = 1,
++		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
++		.info = spdif_info,
++		.get = spdif_default_get,
++		.put = spdif_default_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
++		.device = 1,
++		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
++		.access = SNDRV_CTL_ELEM_ACCESS_READ,
++		.info = spdif_info,
++		.get = spdif_mask_get,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
++		.device = 1,
++		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
++		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			  SNDRV_CTL_ELEM_ACCESS_INACTIVE,
++		.info = spdif_info,
++		.get = spdif_pcm_get,
++		.put = spdif_pcm_put,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
++		.device = 1,
++		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
++		.access = SNDRV_CTL_ELEM_ACCESS_READ,
++		.info = spdif_info,
++		.get = spdif_input_mask_get,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
++		.device = 1,
++		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
++		.access = SNDRV_CTL_ELEM_ACCESS_READ,
++		.info = spdif_info,
++		.get = spdif_input_default_get,
++	},
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
++		.info = snd_ctl_boolean_mono_info,
++		.get = spdif_loopback_get,
++		.put = spdif_loopback_put,
++	},
++};
++
++static const struct snd_kcontrol_new ac97_controls[] = {
++	AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC),
++	AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
++	AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
++	AC97_VOLUME("Line Capture Volume", 0, AC97_LINE),
++	AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
++	AC97_VOLUME("CD Capture Volume", 0, AC97_CD),
++	AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
++	AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX),
++	AC97_SWITCH("Aux Capture Switch", 0, AC97_AUX, 15, 1),
++};
++
++static const struct snd_kcontrol_new ac97_fp_controls[] = {
++	AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE),
++	AC97_SWITCH("Front Panel Playback Switch", 1, AC97_HEADPHONE, 15, 1),
++	{
++		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++		.name = "Front Panel Capture Volume",
++		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
++			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
++		.info = ac97_fp_rec_volume_info,
++		.get = ac97_fp_rec_volume_get,
++		.put = ac97_fp_rec_volume_put,
++		.tlv = { .p = ac97_rec_db_scale, },
++	},
++	AC97_SWITCH("Front Panel Capture Switch", 1, AC97_REC_GAIN, 15, 1),
++};
++
++static void oxygen_any_ctl_free(struct snd_kcontrol *ctl)
++{
++	struct oxygen *chip = ctl->private_data;
++	unsigned int i;
++
++	/* I'm too lazy to write a function for each control :-) */
++	for (i = 0; i < ARRAY_SIZE(chip->controls); ++i)
++		chip->controls[i] = NULL;
++}
++
++static int add_controls(struct oxygen *chip,
++			const struct snd_kcontrol_new controls[],
++			unsigned int count)
++{
++	static const char *const known_ctl_names[CONTROL_COUNT] = {
++		[CONTROL_SPDIF_PCM] =
++			SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
++		[CONTROL_SPDIF_INPUT_BITS] =
++			SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
++		[CONTROL_MIC_CAPTURE_SWITCH] = "Mic Capture Switch",
++		[CONTROL_LINE_CAPTURE_SWITCH] = "Line Capture Switch",
++		[CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch",
++		[CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch",
++	};
++	unsigned int i, j;
++	struct snd_kcontrol_new template;
++	struct snd_kcontrol *ctl;
++	int err;
++
++	for (i = 0; i < count; ++i) {
++		template = controls[i];
++		err = chip->model->control_filter(&template);
++		if (err < 0)
++			return err;
++		if (err == 1)
++			continue;
++		ctl = snd_ctl_new1(&template, chip);
++		if (!ctl)
++			return -ENOMEM;
++		err = snd_ctl_add(chip->card, ctl);
++		if (err < 0)
++			return err;
++		for (j = 0; j < CONTROL_COUNT; ++j)
++			if (!strcmp(ctl->id.name, known_ctl_names[j])) {
++				chip->controls[j] = ctl;
++				ctl->private_free = oxygen_any_ctl_free;
++			}
++	}
++	return 0;
++}
++
++int oxygen_mixer_init(struct oxygen *chip)
++{
++	int err;
++
++	err = add_controls(chip, controls, ARRAY_SIZE(controls));
++	if (err < 0)
++		return err;
++	if (chip->has_ac97_0) {
++		err = add_controls(chip, ac97_controls,
++				   ARRAY_SIZE(ac97_controls));
++		if (err < 0)
++			return err;
++	}
++	if (chip->has_ac97_1) {
++		err = add_controls(chip, ac97_fp_controls,
++				   ARRAY_SIZE(ac97_fp_controls));
++		if (err < 0)
++			return err;
++	}
++	return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0;
++}
+diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
+new file mode 100644
+index 0000000..dfad3db
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen_pcm.c
+@@ -0,0 +1,718 @@
++/*
++ * C-Media CMI8788 driver - PCM code
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++#include <linux/pci.h>
++#include <sound/control.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include "oxygen.h"
++
++static const struct snd_pcm_hardware oxygen_stereo_hardware = {
++	.info = SNDRV_PCM_INFO_MMAP |
++		SNDRV_PCM_INFO_MMAP_VALID |
++		SNDRV_PCM_INFO_INTERLEAVED |
++		SNDRV_PCM_INFO_PAUSE |
++		SNDRV_PCM_INFO_SYNC_START,
++	.formats = SNDRV_PCM_FMTBIT_S16_LE |
++		   SNDRV_PCM_FMTBIT_S32_LE,
++	.rates = SNDRV_PCM_RATE_32000 |
++		 SNDRV_PCM_RATE_44100 |
++		 SNDRV_PCM_RATE_48000 |
++		 SNDRV_PCM_RATE_64000 |
++		 SNDRV_PCM_RATE_88200 |
++		 SNDRV_PCM_RATE_96000 |
++		 SNDRV_PCM_RATE_176400 |
++		 SNDRV_PCM_RATE_192000,
++	.rate_min = 32000,
++	.rate_max = 192000,
++	.channels_min = 2,
++	.channels_max = 2,
++	.buffer_bytes_max = 256 * 1024,
++	.period_bytes_min = 128,
++	.period_bytes_max = 128 * 1024,
++	.periods_min = 2,
++	.periods_max = 2048,
++};
++static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
++	.info = SNDRV_PCM_INFO_MMAP |
++		SNDRV_PCM_INFO_MMAP_VALID |
++		SNDRV_PCM_INFO_INTERLEAVED |
++		SNDRV_PCM_INFO_PAUSE |
++		SNDRV_PCM_INFO_SYNC_START,
++	.formats = SNDRV_PCM_FMTBIT_S16_LE |
++		   SNDRV_PCM_FMTBIT_S32_LE,
++	.rates = SNDRV_PCM_RATE_32000 |
++		 SNDRV_PCM_RATE_44100 |
++		 SNDRV_PCM_RATE_48000 |
++		 SNDRV_PCM_RATE_64000 |
++		 SNDRV_PCM_RATE_88200 |
++		 SNDRV_PCM_RATE_96000 |
++		 SNDRV_PCM_RATE_176400 |
++		 SNDRV_PCM_RATE_192000,
++	.rate_min = 32000,
++	.rate_max = 192000,
++	.channels_min = 2,
++	.channels_max = 8,
++	.buffer_bytes_max = 2048 * 1024,
++	.period_bytes_min = 128,
++	.period_bytes_max = 256 * 1024,
++	.periods_min = 2,
++	.periods_max = 16384,
++};
++static const struct snd_pcm_hardware oxygen_ac97_hardware = {
++	.info = SNDRV_PCM_INFO_MMAP |
++		SNDRV_PCM_INFO_MMAP_VALID |
++		SNDRV_PCM_INFO_INTERLEAVED |
++		SNDRV_PCM_INFO_PAUSE |
++		SNDRV_PCM_INFO_SYNC_START,
++	.formats = SNDRV_PCM_FMTBIT_S16_LE,
++	.rates = SNDRV_PCM_RATE_48000,
++	.rate_min = 48000,
++	.rate_max = 48000,
++	.channels_min = 2,
++	.channels_max = 2,
++	.buffer_bytes_max = 256 * 1024,
++	.period_bytes_min = 128,
++	.period_bytes_max = 128 * 1024,
++	.periods_min = 2,
++	.periods_max = 2048,
++};
++
++static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
++	[PCM_A] = &oxygen_stereo_hardware,
++	[PCM_B] = &oxygen_stereo_hardware,
++	[PCM_C] = &oxygen_stereo_hardware,
++	[PCM_SPDIF] = &oxygen_stereo_hardware,
++	[PCM_MULTICH] = &oxygen_multichannel_hardware,
++	[PCM_AC97] = &oxygen_ac97_hardware,
++};
++
++static inline unsigned int
++oxygen_substream_channel(struct snd_pcm_substream *substream)
++{
++	return (unsigned int)(uintptr_t)substream->runtime->private_data;
++}
++
++static int oxygen_open(struct snd_pcm_substream *substream,
++		       unsigned int channel)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	int err;
++
++	runtime->private_data = (void *)(uintptr_t)channel;
++	if (channel == PCM_B && chip->has_ac97_1 &&
++	    (chip->model->used_channels & OXYGEN_CHANNEL_AC97))
++		runtime->hw = oxygen_ac97_hardware;
++	else
++		runtime->hw = *oxygen_hardware[channel];
++	switch (channel) {
++	case PCM_C:
++		runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
++				       SNDRV_PCM_RATE_64000);
++		runtime->hw.rate_min = 44100;
++		break;
++	case PCM_MULTICH:
++		runtime->hw.channels_max = chip->model->dac_channels;
++		break;
++	}
++	if (chip->model->pcm_hardware_filter)
++		chip->model->pcm_hardware_filter(channel, &runtime->hw);
++	err = snd_pcm_hw_constraint_step(runtime, 0,
++					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
++	if (err < 0)
++		return err;
++	err = snd_pcm_hw_constraint_step(runtime, 0,
++					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
++	if (err < 0)
++		return err;
++	if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) {
++		err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
++		if (err < 0)
++			return err;
++	}
++	if (runtime->hw.channels_max > 2) {
++		err = snd_pcm_hw_constraint_step(runtime, 0,
++						 SNDRV_PCM_HW_PARAM_CHANNELS,
++						 2);
++		if (err < 0)
++			return err;
++	}
++	snd_pcm_set_sync(substream);
++	chip->streams[channel] = substream;
++
++	mutex_lock(&chip->mutex);
++	chip->pcm_active |= 1 << channel;
++	if (channel == PCM_SPDIF) {
++		chip->spdif_pcm_bits = chip->spdif_bits;
++		chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &=
++			~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
++		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
++			       SNDRV_CTL_EVENT_MASK_INFO,
++			       &chip->controls[CONTROL_SPDIF_PCM]->id);
++	}
++	mutex_unlock(&chip->mutex);
++
++	return 0;
++}
++
++static int oxygen_rec_a_open(struct snd_pcm_substream *substream)
++{
++	return oxygen_open(substream, PCM_A);
++}
++
++static int oxygen_rec_b_open(struct snd_pcm_substream *substream)
++{
++	return oxygen_open(substream, PCM_B);
++}
++
++static int oxygen_rec_c_open(struct snd_pcm_substream *substream)
++{
++	return oxygen_open(substream, PCM_C);
++}
++
++static int oxygen_spdif_open(struct snd_pcm_substream *substream)
++{
++	return oxygen_open(substream, PCM_SPDIF);
++}
++
++static int oxygen_multich_open(struct snd_pcm_substream *substream)
++{
++	return oxygen_open(substream, PCM_MULTICH);
++}
++
++static int oxygen_ac97_open(struct snd_pcm_substream *substream)
++{
++	return oxygen_open(substream, PCM_AC97);
++}
++
++static int oxygen_close(struct snd_pcm_substream *substream)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	unsigned int channel = oxygen_substream_channel(substream);
++
++	mutex_lock(&chip->mutex);
++	chip->pcm_active &= ~(1 << channel);
++	if (channel == PCM_SPDIF) {
++		chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
++			SNDRV_CTL_ELEM_ACCESS_INACTIVE;
++		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
++			       SNDRV_CTL_EVENT_MASK_INFO,
++			       &chip->controls[CONTROL_SPDIF_PCM]->id);
++	}
++	if (channel == PCM_SPDIF || channel == PCM_MULTICH)
++		oxygen_update_spdif_source(chip);
++	mutex_unlock(&chip->mutex);
++
++	chip->streams[channel] = NULL;
++	return 0;
++}
++
++static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params)
++{
++	if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
++		return OXYGEN_FORMAT_24;
++	else
++		return OXYGEN_FORMAT_16;
++}
++
++static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
++{
++	switch (params_rate(hw_params)) {
++	case 32000:
++		return OXYGEN_RATE_32000;
++	case 44100:
++		return OXYGEN_RATE_44100;
++	default: /* 48000 */
++		return OXYGEN_RATE_48000;
++	case 64000:
++		return OXYGEN_RATE_64000;
++	case 88200:
++		return OXYGEN_RATE_88200;
++	case 96000:
++		return OXYGEN_RATE_96000;
++	case 176400:
++		return OXYGEN_RATE_176400;
++	case 192000:
++		return OXYGEN_RATE_192000;
++	}
++}
++
++static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
++{
++	if (params_rate(hw_params) <= 96000)
++		return OXYGEN_I2S_MCLK_256;
++	else
++		return OXYGEN_I2S_MCLK_128;
++}
++
++static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
++{
++	if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
++		return OXYGEN_I2S_BITS_24;
++	else
++		return OXYGEN_I2S_BITS_16;
++}
++
++static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params)
++{
++	switch (params_channels(hw_params)) {
++	default: /* 2 */
++		return OXYGEN_PLAY_CHANNELS_2;
++	case 4:
++		return OXYGEN_PLAY_CHANNELS_4;
++	case 6:
++		return OXYGEN_PLAY_CHANNELS_6;
++	case 8:
++		return OXYGEN_PLAY_CHANNELS_8;
++	}
++}
++
++static const unsigned int channel_base_registers[PCM_COUNT] = {
++	[PCM_A] = OXYGEN_DMA_A_ADDRESS,
++	[PCM_B] = OXYGEN_DMA_B_ADDRESS,
++	[PCM_C] = OXYGEN_DMA_C_ADDRESS,
++	[PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS,
++	[PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS,
++	[PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS,
++};
++
++static int oxygen_hw_params(struct snd_pcm_substream *substream,
++			    struct snd_pcm_hw_params *hw_params)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	unsigned int channel = oxygen_substream_channel(substream);
++	int err;
++
++	err = snd_pcm_lib_malloc_pages(substream,
++				       params_buffer_bytes(hw_params));
++	if (err < 0)
++		return err;
++
++	oxygen_write32(chip, channel_base_registers[channel],
++		       (u32)substream->runtime->dma_addr);
++	if (channel == PCM_MULTICH) {
++		oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT,
++			       params_buffer_bytes(hw_params) / 4 - 1);
++		oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT,
++			       params_period_bytes(hw_params) / 4 - 1);
++	} else {
++		oxygen_write16(chip, channel_base_registers[channel] + 4,
++			       params_buffer_bytes(hw_params) / 4 - 1);
++		oxygen_write16(chip, channel_base_registers[channel] + 6,
++			       params_period_bytes(hw_params) / 4 - 1);
++	}
++	return 0;
++}
++
++static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
++				  struct snd_pcm_hw_params *hw_params)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	int err;
++
++	err = oxygen_hw_params(substream, hw_params);
++	if (err < 0)
++		return err;
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
++			     oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
++			     OXYGEN_REC_FORMAT_A_MASK);
++	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
++			      oxygen_rate(hw_params) |
++			      oxygen_i2s_mclk(hw_params) |
++			      chip->model->adc_i2s_format |
++			      oxygen_i2s_bits(hw_params),
++			      OXYGEN_I2S_RATE_MASK |
++			      OXYGEN_I2S_FORMAT_MASK |
++			      OXYGEN_I2S_MCLK_MASK |
++			      OXYGEN_I2S_BITS_MASK);
++	spin_unlock_irq(&chip->reg_lock);
++
++	mutex_lock(&chip->mutex);
++	chip->model->set_adc_params(chip, hw_params);
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
++				  struct snd_pcm_hw_params *hw_params)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	int is_ac97;
++	int err;
++
++	err = oxygen_hw_params(substream, hw_params);
++	if (err < 0)
++		return err;
++
++	is_ac97 = chip->has_ac97_1 &&
++		(chip->model->used_channels & OXYGEN_CHANNEL_AC97);
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
++			     oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
++			     OXYGEN_REC_FORMAT_B_MASK);
++	if (!is_ac97)
++		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
++				      oxygen_rate(hw_params) |
++				      oxygen_i2s_mclk(hw_params) |
++				      chip->model->adc_i2s_format |
++				      oxygen_i2s_bits(hw_params),
++				      OXYGEN_I2S_RATE_MASK |
++				      OXYGEN_I2S_FORMAT_MASK |
++				      OXYGEN_I2S_MCLK_MASK |
++				      OXYGEN_I2S_BITS_MASK);
++	spin_unlock_irq(&chip->reg_lock);
++
++	if (!is_ac97) {
++		mutex_lock(&chip->mutex);
++		chip->model->set_adc_params(chip, hw_params);
++		mutex_unlock(&chip->mutex);
++	}
++	return 0;
++}
++
++static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
++				  struct snd_pcm_hw_params *hw_params)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	int err;
++
++	err = oxygen_hw_params(substream, hw_params);
++	if (err < 0)
++		return err;
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
++			     oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
++			     OXYGEN_REC_FORMAT_C_MASK);
++	spin_unlock_irq(&chip->reg_lock);
++	return 0;
++}
++
++static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
++				  struct snd_pcm_hw_params *hw_params)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	int err;
++
++	err = oxygen_hw_params(substream, hw_params);
++	if (err < 0)
++		return err;
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
++			    OXYGEN_SPDIF_OUT_ENABLE);
++	oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
++			     oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT,
++			     OXYGEN_SPDIF_FORMAT_MASK);
++	oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
++			      oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT,
++			      OXYGEN_SPDIF_OUT_RATE_MASK);
++	oxygen_update_spdif_source(chip);
++	spin_unlock_irq(&chip->reg_lock);
++	return 0;
++}
++
++static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
++				    struct snd_pcm_hw_params *hw_params)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	int err;
++
++	err = oxygen_hw_params(substream, hw_params);
++	if (err < 0)
++		return err;
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
++			     oxygen_play_channels(hw_params),
++			     OXYGEN_PLAY_CHANNELS_MASK);
++	oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
++			     oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
++			     OXYGEN_MULTICH_FORMAT_MASK);
++	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
++			      oxygen_rate(hw_params) |
++			      chip->model->dac_i2s_format |
++			      oxygen_i2s_bits(hw_params),
++			      OXYGEN_I2S_RATE_MASK |
++			      OXYGEN_I2S_FORMAT_MASK |
++			      OXYGEN_I2S_BITS_MASK);
++	oxygen_update_dac_routing(chip);
++	oxygen_update_spdif_source(chip);
++	spin_unlock_irq(&chip->reg_lock);
++
++	mutex_lock(&chip->mutex);
++	chip->model->set_dac_params(chip, hw_params);
++	mutex_unlock(&chip->mutex);
++	return 0;
++}
++
++static int oxygen_hw_free(struct snd_pcm_substream *substream)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	unsigned int channel = oxygen_substream_channel(substream);
++
++	spin_lock_irq(&chip->reg_lock);
++	chip->interrupt_mask &= ~(1 << channel);
++	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
++	spin_unlock_irq(&chip->reg_lock);
++
++	return snd_pcm_lib_free_pages(substream);
++}
++
++static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
++			    OXYGEN_SPDIF_OUT_ENABLE);
++	spin_unlock_irq(&chip->reg_lock);
++	return oxygen_hw_free(substream);
++}
++
++static int oxygen_prepare(struct snd_pcm_substream *substream)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	unsigned int channel = oxygen_substream_channel(substream);
++	unsigned int channel_mask = 1 << channel;
++
++	spin_lock_irq(&chip->reg_lock);
++	oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
++	oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
++
++	chip->interrupt_mask |= channel_mask;
++	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
++	spin_unlock_irq(&chip->reg_lock);
++	return 0;
++}
++
++static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_substream *s;
++	unsigned int mask = 0;
++	int pausing;
++
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_STOP:
++	case SNDRV_PCM_TRIGGER_START:
++		pausing = 0;
++		break;
++	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++		pausing = 1;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	snd_pcm_group_for_each_entry(s, substream) {
++		if (snd_pcm_substream_chip(s) == chip) {
++			mask |= 1 << oxygen_substream_channel(s);
++			snd_pcm_trigger_done(s, substream);
++		}
++	}
++
++	spin_lock(&chip->reg_lock);
++	if (!pausing) {
++		if (cmd == SNDRV_PCM_TRIGGER_START)
++			chip->pcm_running |= mask;
++		else
++			chip->pcm_running &= ~mask;
++		oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
++	} else {
++		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
++			oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask);
++		else
++			oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
++	}
++	spin_unlock(&chip->reg_lock);
++	return 0;
++}
++
++static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
++{
++	struct oxygen *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	unsigned int channel = oxygen_substream_channel(substream);
++	u32 curr_addr;
++
++	/* no spinlock, this read should be atomic */
++	curr_addr = oxygen_read32(chip, channel_base_registers[channel]);
++	return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
++}
++
++static struct snd_pcm_ops oxygen_rec_a_ops = {
++	.open      = oxygen_rec_a_open,
++	.close     = oxygen_close,
++	.ioctl     = snd_pcm_lib_ioctl,
++	.hw_params = oxygen_rec_a_hw_params,
++	.hw_free   = oxygen_hw_free,
++	.prepare   = oxygen_prepare,
++	.trigger   = oxygen_trigger,
++	.pointer   = oxygen_pointer,
++};
++
++static struct snd_pcm_ops oxygen_rec_b_ops = {
++	.open      = oxygen_rec_b_open,
++	.close     = oxygen_close,
++	.ioctl     = snd_pcm_lib_ioctl,
++	.hw_params = oxygen_rec_b_hw_params,
++	.hw_free   = oxygen_hw_free,
++	.prepare   = oxygen_prepare,
++	.trigger   = oxygen_trigger,
++	.pointer   = oxygen_pointer,
++};
++
++static struct snd_pcm_ops oxygen_rec_c_ops = {
++	.open      = oxygen_rec_c_open,
++	.close     = oxygen_close,
++	.ioctl     = snd_pcm_lib_ioctl,
++	.hw_params = oxygen_rec_c_hw_params,
++	.hw_free   = oxygen_hw_free,
++	.prepare   = oxygen_prepare,
++	.trigger   = oxygen_trigger,
++	.pointer   = oxygen_pointer,
++};
++
++static struct snd_pcm_ops oxygen_spdif_ops = {
++	.open      = oxygen_spdif_open,
++	.close     = oxygen_close,
++	.ioctl     = snd_pcm_lib_ioctl,
++	.hw_params = oxygen_spdif_hw_params,
++	.hw_free   = oxygen_spdif_hw_free,
++	.prepare   = oxygen_prepare,
++	.trigger   = oxygen_trigger,
++	.pointer   = oxygen_pointer,
++};
++
++static struct snd_pcm_ops oxygen_multich_ops = {
++	.open      = oxygen_multich_open,
++	.close     = oxygen_close,
++	.ioctl     = snd_pcm_lib_ioctl,
++	.hw_params = oxygen_multich_hw_params,
++	.hw_free   = oxygen_hw_free,
++	.prepare   = oxygen_prepare,
++	.trigger   = oxygen_trigger,
++	.pointer   = oxygen_pointer,
++};
++
++static struct snd_pcm_ops oxygen_ac97_ops = {
++	.open      = oxygen_ac97_open,
++	.close     = oxygen_close,
++	.ioctl     = snd_pcm_lib_ioctl,
++	.hw_params = oxygen_hw_params,
++	.hw_free   = oxygen_hw_free,
++	.prepare   = oxygen_prepare,
++	.trigger   = oxygen_trigger,
++	.pointer   = oxygen_pointer,
++};
++
++static void oxygen_pcm_free(struct snd_pcm *pcm)
++{
++	snd_pcm_lib_preallocate_free_for_all(pcm);
++}
++
++int __devinit oxygen_pcm_init(struct oxygen *chip)
++{
++	struct snd_pcm *pcm;
++	int outs, ins;
++	int err;
++
++	outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */
++	ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A |
++					       OXYGEN_CHANNEL_B));
++	err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
++	if (err < 0)
++		return err;
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops);
++	if (chip->model->used_channels & OXYGEN_CHANNEL_A)
++		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++				&oxygen_rec_a_ops);
++	else if (chip->model->used_channels & OXYGEN_CHANNEL_B)
++		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++				&oxygen_rec_b_ops);
++	pcm->private_data = chip;
++	pcm->private_free = oxygen_pcm_free;
++	strcpy(pcm->name, "Analog");
++	snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
++				      SNDRV_DMA_TYPE_DEV,
++				      snd_dma_pci_data(chip->pci),
++				      512 * 1024, 2048 * 1024);
++	if (ins)
++		snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
++					      SNDRV_DMA_TYPE_DEV,
++					      snd_dma_pci_data(chip->pci),
++					      128 * 1024, 256 * 1024);
++
++	outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF);
++	ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C);
++	if (outs | ins) {
++		err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
++		if (err < 0)
++			return err;
++		if (outs)
++			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++					&oxygen_spdif_ops);
++		if (ins)
++			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++					&oxygen_rec_c_ops);
++		pcm->private_data = chip;
++		pcm->private_free = oxygen_pcm_free;
++		strcpy(pcm->name, "Digital");
++		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
++						      snd_dma_pci_data(chip->pci),
++						      128 * 1024, 256 * 1024);
++	}
++
++	outs = chip->has_ac97_1 &&
++		(chip->model->used_channels & OXYGEN_CHANNEL_AC97);
++	ins = outs ||
++		(chip->model->used_channels & (OXYGEN_CHANNEL_A |
++					       OXYGEN_CHANNEL_B))
++		== (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B);
++	if (outs | ins) {
++		err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
++				  2, outs, ins, &pcm);
++		if (err < 0)
++			return err;
++		if (outs) {
++			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++					&oxygen_ac97_ops);
++			oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
++					     OXYGEN_REC_B_ROUTE_AC97_1,
++					     OXYGEN_REC_B_ROUTE_MASK);
++		}
++		if (ins)
++			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++					&oxygen_rec_b_ops);
++		pcm->private_data = chip;
++		pcm->private_free = oxygen_pcm_free;
++		strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
++		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
++						      snd_dma_pci_data(chip->pci),
++						      128 * 1024, 256 * 1024);
++	}
++	return 0;
++}
+diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
+new file mode 100644
+index 0000000..72de159
+--- /dev/null
++++ b/sound/pci/oxygen/oxygen_regs.h
+@@ -0,0 +1,453 @@
++#ifndef OXYGEN_REGS_H_INCLUDED
++#define OXYGEN_REGS_H_INCLUDED
++
++/* recording channel A */
++#define OXYGEN_DMA_A_ADDRESS		0x00	/* 32-bit base address */
++#define OXYGEN_DMA_A_COUNT		0x04	/* buffer counter (dwords) */
++#define OXYGEN_DMA_A_TCOUNT		0x06	/* interrupt counter (dwords) */
++
++/* recording channel B */
++#define OXYGEN_DMA_B_ADDRESS		0x08
++#define OXYGEN_DMA_B_COUNT		0x0c
++#define OXYGEN_DMA_B_TCOUNT		0x0e
++
++/* recording channel C */
++#define OXYGEN_DMA_C_ADDRESS		0x10
++#define OXYGEN_DMA_C_COUNT		0x14
++#define OXYGEN_DMA_C_TCOUNT		0x16
++
++/* SPDIF playback channel */
++#define OXYGEN_DMA_SPDIF_ADDRESS	0x18
++#define OXYGEN_DMA_SPDIF_COUNT		0x1c
++#define OXYGEN_DMA_SPDIF_TCOUNT		0x1e
++
++/* multichannel playback channel */
++#define OXYGEN_DMA_MULTICH_ADDRESS	0x20
++#define OXYGEN_DMA_MULTICH_COUNT	0x24	/* 24 bits */
++#define OXYGEN_DMA_MULTICH_TCOUNT	0x28	/* 24 bits */
++
++/* AC'97 (front panel) playback channel */
++#define OXYGEN_DMA_AC97_ADDRESS		0x30
++#define OXYGEN_DMA_AC97_COUNT		0x34
++#define OXYGEN_DMA_AC97_TCOUNT		0x36
++
++/* all registers 0x00..0x36 return current position on read */
++
++#define OXYGEN_DMA_STATUS		0x40	/* 1 = running, 0 = stop */
++#define  OXYGEN_CHANNEL_A		0x01
++#define  OXYGEN_CHANNEL_B		0x02
++#define  OXYGEN_CHANNEL_C		0x04
++#define  OXYGEN_CHANNEL_SPDIF		0x08
++#define  OXYGEN_CHANNEL_MULTICH		0x10
++#define  OXYGEN_CHANNEL_AC97		0x20
++
++#define OXYGEN_DMA_PAUSE		0x41	/* 1 = pause */
++/* OXYGEN_CHANNEL_* */
++
++#define OXYGEN_DMA_RESET		0x42
++/* OXYGEN_CHANNEL_* */
++
++#define OXYGEN_PLAY_CHANNELS		0x43
++#define  OXYGEN_PLAY_CHANNELS_MASK	0x03
++#define  OXYGEN_PLAY_CHANNELS_2		0x00
++#define  OXYGEN_PLAY_CHANNELS_4		0x01
++#define  OXYGEN_PLAY_CHANNELS_6		0x02
++#define  OXYGEN_PLAY_CHANNELS_8		0x03
++#define  OXYGEN_DMA_A_BURST_MASK	0x04
++#define  OXYGEN_DMA_A_BURST_8		0x00	/* dwords */
++#define  OXYGEN_DMA_A_BURST_16		0x04
++#define  OXYGEN_DMA_MULTICH_BURST_MASK	0x08
++#define  OXYGEN_DMA_MULTICH_BURST_8	0x00
++#define  OXYGEN_DMA_MULTICH_BURST_16	0x08
++
++#define OXYGEN_INTERRUPT_MASK		0x44
++/* OXYGEN_CHANNEL_* */
++#define  OXYGEN_INT_SPDIF_IN_DETECT	0x0100
++#define  OXYGEN_INT_MCU			0x0200
++#define  OXYGEN_INT_2WIRE		0x0400
++#define  OXYGEN_INT_GPIO		0x0800
++#define  OXYGEN_INT_MCB			0x2000
++#define  OXYGEN_INT_AC97		0x4000
++
++#define OXYGEN_INTERRUPT_STATUS		0x46
++/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */
++#define  OXYGEN_INT_MIDI		0x1000
++
++#define OXYGEN_MISC			0x48
++#define  OXYGEN_MISC_WRITE_PCI_SUBID	0x01
++#define  OXYGEN_MISC_LATENCY_3F		0x02
++#define  OXYGEN_MISC_REC_C_FROM_SPDIF	0x04
++#define  OXYGEN_MISC_REC_B_FROM_AC97	0x08
++#define  OXYGEN_MISC_REC_A_FROM_MULTICH	0x10
++#define  OXYGEN_MISC_PCI_MEM_W_1_CLOCK	0x20
++#define  OXYGEN_MISC_MIDI		0x40
++#define  OXYGEN_MISC_CRYSTAL_MASK	0x80
++#define  OXYGEN_MISC_CRYSTAL_24576	0x00
++#define  OXYGEN_MISC_CRYSTAL_27		0x80	/* MHz */
++
++#define OXYGEN_REC_FORMAT		0x4a
++#define  OXYGEN_REC_FORMAT_A_MASK	0x03
++#define  OXYGEN_REC_FORMAT_A_SHIFT	0
++#define  OXYGEN_REC_FORMAT_B_MASK	0x0c
++#define  OXYGEN_REC_FORMAT_B_SHIFT	2
++#define  OXYGEN_REC_FORMAT_C_MASK	0x30
++#define  OXYGEN_REC_FORMAT_C_SHIFT	4
++#define  OXYGEN_FORMAT_16		0x00
++#define  OXYGEN_FORMAT_24		0x01
++#define  OXYGEN_FORMAT_32		0x02
++
++#define OXYGEN_PLAY_FORMAT		0x4b
++#define  OXYGEN_SPDIF_FORMAT_MASK	0x03
++#define  OXYGEN_SPDIF_FORMAT_SHIFT	0
++#define  OXYGEN_MULTICH_FORMAT_MASK	0x0c
++#define  OXYGEN_MULTICH_FORMAT_SHIFT	2
++/* OXYGEN_FORMAT_* */
++
++#define OXYGEN_REC_CHANNELS		0x4c
++#define  OXYGEN_REC_CHANNELS_MASK	0x07
++#define  OXYGEN_REC_CHANNELS_2_2_2	0x00	/* DMA A, B, C */
++#define  OXYGEN_REC_CHANNELS_4_2_2	0x01
++#define  OXYGEN_REC_CHANNELS_6_0_2	0x02
++#define  OXYGEN_REC_CHANNELS_6_2_0	0x03
++#define  OXYGEN_REC_CHANNELS_8_0_0	0x04
++
++#define OXYGEN_FUNCTION			0x50
++#define  OXYGEN_FUNCTION_CLOCK_MASK	0x01
++#define  OXYGEN_FUNCTION_CLOCK_PLL	0x00
++#define  OXYGEN_FUNCTION_CLOCK_CRYSTAL	0x01
++#define  OXYGEN_FUNCTION_RESET_CODEC	0x02
++#define  OXYGEN_FUNCTION_RESET_POL	0x04
++#define  OXYGEN_FUNCTION_PWDN		0x08
++#define  OXYGEN_FUNCTION_PWDN_EN	0x10
++#define  OXYGEN_FUNCTION_PWDN_POL	0x20
++#define  OXYGEN_FUNCTION_2WIRE_SPI_MASK	0x40
++#define  OXYGEN_FUNCTION_SPI		0x00
++#define  OXYGEN_FUNCTION_2WIRE		0x40
++#define  OXYGEN_FUNCTION_ENABLE_SPI_4_5	0x80	/* 0 = EEPROM */
++
++#define OXYGEN_I2S_MULTICH_FORMAT	0x60
++#define  OXYGEN_I2S_RATE_MASK		0x0007	/* LRCK */
++#define  OXYGEN_RATE_32000		0x0000
++#define  OXYGEN_RATE_44100		0x0001
++#define  OXYGEN_RATE_48000		0x0002
++#define  OXYGEN_RATE_64000		0x0003
++#define  OXYGEN_RATE_88200		0x0004
++#define  OXYGEN_RATE_96000		0x0005
++#define  OXYGEN_RATE_176400		0x0006
++#define  OXYGEN_RATE_192000		0x0007
++#define  OXYGEN_I2S_FORMAT_MASK		0x0008
++#define  OXYGEN_I2S_FORMAT_I2S		0x0000
++#define  OXYGEN_I2S_FORMAT_LJUST	0x0008
++#define  OXYGEN_I2S_MCLK_MASK		0x0030	/* MCLK/LRCK */
++#define  OXYGEN_I2S_MCLK_128		0x0000
++#define  OXYGEN_I2S_MCLK_256		0x0010
++#define  OXYGEN_I2S_MCLK_512		0x0020
++#define  OXYGEN_I2S_BITS_MASK		0x00c0
++#define  OXYGEN_I2S_BITS_16		0x0000
++#define  OXYGEN_I2S_BITS_20		0x0040
++#define  OXYGEN_I2S_BITS_24		0x0080
++#define  OXYGEN_I2S_BITS_32		0x00c0
++#define  OXYGEN_I2S_MASTER		0x0100
++#define  OXYGEN_I2S_BCLK_MASK		0x0600	/* BCLK/LRCK */
++#define  OXYGEN_I2S_BCLK_64		0x0000
++#define  OXYGEN_I2S_BCLK_128		0x0200
++#define  OXYGEN_I2S_BCLK_256		0x0400
++#define  OXYGEN_I2S_MUTE_MCLK		0x0800
++
++#define OXYGEN_I2S_A_FORMAT		0x62
++#define OXYGEN_I2S_B_FORMAT		0x64
++#define OXYGEN_I2S_C_FORMAT		0x66
++/* like OXYGEN_I2S_MULTICH_FORMAT */
++
++#define OXYGEN_SPDIF_CONTROL		0x70
++#define  OXYGEN_SPDIF_OUT_ENABLE	0x00000002
++#define  OXYGEN_SPDIF_LOOPBACK		0x00000004	/* in to out */
++#define  OXYGEN_SPDIF_SENSE_MASK	0x00000008
++#define  OXYGEN_SPDIF_LOCK_MASK		0x00000010
++#define  OXYGEN_SPDIF_RATE_MASK		0x00000020
++#define  OXYGEN_SPDIF_SPDVALID		0x00000040
++#define  OXYGEN_SPDIF_SENSE_PAR		0x00000200
++#define  OXYGEN_SPDIF_LOCK_PAR		0x00000400
++#define  OXYGEN_SPDIF_SENSE_STATUS	0x00000800
++#define  OXYGEN_SPDIF_LOCK_STATUS	0x00001000
++#define  OXYGEN_SPDIF_SENSE_INT		0x00002000	/* r/wc */
++#define  OXYGEN_SPDIF_LOCK_INT		0x00004000	/* r/wc */
++#define  OXYGEN_SPDIF_RATE_INT		0x00008000	/* r/wc */
++#define  OXYGEN_SPDIF_IN_CLOCK_MASK	0x00010000
++#define  OXYGEN_SPDIF_IN_CLOCK_96	0x00000000	/* <= 96 kHz */
++#define  OXYGEN_SPDIF_IN_CLOCK_192	0x00010000	/* > 96 kHz */
++#define  OXYGEN_SPDIF_OUT_RATE_MASK	0x07000000
++#define  OXYGEN_SPDIF_OUT_RATE_SHIFT	24
++/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */
++
++#define OXYGEN_SPDIF_OUTPUT_BITS	0x74
++#define  OXYGEN_SPDIF_NONAUDIO		0x00000002
++#define  OXYGEN_SPDIF_C			0x00000004
++#define  OXYGEN_SPDIF_PREEMPHASIS	0x00000008
++#define  OXYGEN_SPDIF_CATEGORY_MASK	0x000007f0
++#define  OXYGEN_SPDIF_CATEGORY_SHIFT	4
++#define  OXYGEN_SPDIF_ORIGINAL		0x00000800
++#define  OXYGEN_SPDIF_CS_RATE_MASK	0x0000f000
++#define  OXYGEN_SPDIF_CS_RATE_SHIFT	12
++#define  OXYGEN_SPDIF_V			0x00010000	/* 0 = valid */
++
++#define OXYGEN_SPDIF_INPUT_BITS		0x78
++/* 32 bits, IEC958_AES_* */
++
++#define OXYGEN_EEPROM_CONTROL		0x80
++#define  OXYGEN_EEPROM_ADDRESS_MASK	0x7f
++#define  OXYGEN_EEPROM_DIR_MASK		0x80
++#define  OXYGEN_EEPROM_DIR_READ		0x00
++#define  OXYGEN_EEPROM_DIR_WRITE	0x80
++
++#define OXYGEN_EEPROM_STATUS		0x81
++#define  OXYGEN_EEPROM_VALID		0x40
++#define  OXYGEN_EEPROM_BUSY		0x80
++
++#define OXYGEN_EEPROM_DATA		0x82	/* 16 bits */
++
++#define OXYGEN_2WIRE_CONTROL		0x90
++#define  OXYGEN_2WIRE_DIR_MASK		0x01
++#define  OXYGEN_2WIRE_DIR_WRITE		0x00
++#define  OXYGEN_2WIRE_DIR_READ		0x01
++#define  OXYGEN_2WIRE_ADDRESS_MASK	0xfe	/* slave device address */
++#define  OXYGEN_2WIRE_ADDRESS_SHIFT	1
++
++#define OXYGEN_2WIRE_MAP		0x91	/* address, 8 bits */
++#define OXYGEN_2WIRE_DATA		0x92	/* data, 16 bits */
++
++#define OXYGEN_2WIRE_BUS_STATUS		0x94
++#define  OXYGEN_2WIRE_BUSY		0x0001
++#define  OXYGEN_2WIRE_LENGTH_MASK	0x0002
++#define  OXYGEN_2WIRE_LENGTH_8		0x0000
++#define  OXYGEN_2WIRE_LENGTH_16		0x0002
++#define  OXYGEN_2WIRE_MANUAL_READ	0x0004	/* 0 = auto read */
++#define  OXYGEN_2WIRE_WRITE_MAP_ONLY	0x0008
++#define  OXYGEN_2WIRE_SLAVE_AD_MASK	0x0030	/* AD0, AD1 */
++#define  OXYGEN_2WIRE_INTERRUPT_MASK	0x0040	/* 0 = int. if not responding */
++#define  OXYGEN_2WIRE_SLAVE_NO_RESPONSE	0x0080
++#define  OXYGEN_2WIRE_SPEED_MASK	0x0100
++#define  OXYGEN_2WIRE_SPEED_STANDARD	0x0000
++#define  OXYGEN_2WIRE_SPEED_FAST	0x0100
++#define  OXYGEN_2WIRE_CLOCK_SYNC	0x0200
++#define  OXYGEN_2WIRE_BUS_RESET		0x0400
++
++#define OXYGEN_SPI_CONTROL		0x98
++#define  OXYGEN_SPI_BUSY		0x01	/* read */
++#define  OXYGEN_SPI_TRIGGER		0x01	/* write */
++#define  OXYGEN_SPI_DATA_LENGTH_MASK	0x02
++#define  OXYGEN_SPI_DATA_LENGTH_2	0x00
++#define  OXYGEN_SPI_DATA_LENGTH_3	0x02
++#define  OXYGEN_SPI_CLOCK_MASK		0xc0
++#define  OXYGEN_SPI_CLOCK_160		0x00	/* ns */
++#define  OXYGEN_SPI_CLOCK_320		0x40
++#define  OXYGEN_SPI_CLOCK_640		0x80
++#define  OXYGEN_SPI_CLOCK_1280		0xc0
++#define  OXYGEN_SPI_CODEC_MASK		0x70	/* 0..5 */
++#define  OXYGEN_SPI_CODEC_SHIFT		4
++#define  OXYGEN_SPI_CEN_MASK		0x80
++#define  OXYGEN_SPI_CEN_LATCH_CLOCK_LO	0x00
++#define  OXYGEN_SPI_CEN_LATCH_CLOCK_HI	0x80
++
++#define OXYGEN_SPI_DATA1		0x99
++#define OXYGEN_SPI_DATA2		0x9a
++#define OXYGEN_SPI_DATA3		0x9b
++
++#define OXYGEN_MPU401			0xa0
++
++#define OXYGEN_MPU401_CONTROL		0xa2
++#define  OXYGEN_MPU401_LOOPBACK		0x01	/* TXD to RXD */
++
++#define OXYGEN_GPI_DATA			0xa4
++/* bits 0..5 = pin XGPI0..XGPI5 */
++
++#define OXYGEN_GPI_INTERRUPT_MASK	0xa5
++/* bits 0..5, 1 = enable */
++
++#define OXYGEN_GPIO_DATA		0xa6
++/* bits 0..9 */
++
++#define OXYGEN_GPIO_CONTROL		0xa8
++/* bits 0..9, 0 = input, 1 = output */
++#define  OXYGEN_GPIO1_XSLAVE_RDY	0x8000
++
++#define OXYGEN_GPIO_INTERRUPT_MASK	0xaa
++/* bits 0..9, 1 = enable */
++
++#define OXYGEN_DEVICE_SENSE		0xac
++#define  OXYGEN_HEAD_PHONE_DETECT	0x01
++#define  OXYGEN_HEAD_PHONE_MASK		0x06
++#define  OXYGEN_HEAD_PHONE_PASSIVE_SPK	0x00
++#define  OXYGEN_HEAD_PHONE_HP		0x02
++#define  OXYGEN_HEAD_PHONE_ACTIVE_SPK	0x04
++
++#define OXYGEN_MCU_2WIRE_DATA		0xb0
++
++#define OXYGEN_MCU_2WIRE_MAP		0xb2
++
++#define OXYGEN_MCU_2WIRE_STATUS		0xb3
++#define  OXYGEN_MCU_2WIRE_BUSY		0x01
++#define  OXYGEN_MCU_2WIRE_LENGTH_MASK	0x06
++#define  OXYGEN_MCU_2WIRE_LENGTH_1	0x00
++#define  OXYGEN_MCU_2WIRE_LENGTH_2	0x02
++#define  OXYGEN_MCU_2WIRE_LENGTH_3	0x04
++#define  OXYGEN_MCU_2WIRE_WRITE		0x08	/* r/wc */
++#define  OXYGEN_MCU_2WIRE_READ		0x10	/* r/wc */
++#define  OXYGEN_MCU_2WIRE_DRV_XACT_FAIL	0x20	/* r/wc */
++#define  OXYGEN_MCU_2WIRE_RESET		0x40
++
++#define OXYGEN_MCU_2WIRE_CONTROL	0xb4
++#define  OXYGEN_MCU_2WIRE_DRV_ACK	0x01
++#define  OXYGEN_MCU_2WIRE_DRV_XACT	0x02
++#define  OXYGEN_MCU_2WIRE_INT_MASK	0x04
++#define  OXYGEN_MCU_2WIRE_SYNC_MASK	0x08
++#define  OXYGEN_MCU_2WIRE_SYNC_RDY_PIN	0x00
++#define  OXYGEN_MCU_2WIRE_SYNC_DATA	0x08
++#define  OXYGEN_MCU_2WIRE_ADDRESS_MASK	0x30
++#define  OXYGEN_MCU_2WIRE_ADDRESS_10	0x00
++#define  OXYGEN_MCU_2WIRE_ADDRESS_12	0x10
++#define  OXYGEN_MCU_2WIRE_ADDRESS_14	0x20
++#define  OXYGEN_MCU_2WIRE_ADDRESS_16	0x30
++#define  OXYGEN_MCU_2WIRE_INT_POL	0x40
++#define  OXYGEN_MCU_2WIRE_SYNC_ENABLE	0x80
++
++#define OXYGEN_PLAY_ROUTING		0xc0
++#define  OXYGEN_PLAY_MUTE01		0x0001
++#define  OXYGEN_PLAY_MUTE23		0x0002
++#define  OXYGEN_PLAY_MUTE45		0x0004
++#define  OXYGEN_PLAY_MUTE67		0x0008
++#define  OXYGEN_PLAY_MULTICH_MASK	0x0010
++#define  OXYGEN_PLAY_MULTICH_I2S_DAC	0x0000
++#define  OXYGEN_PLAY_MULTICH_AC97	0x0010
++#define  OXYGEN_PLAY_SPDIF_MASK		0x00e0
++#define  OXYGEN_PLAY_SPDIF_SPDIF	0x0000
++#define  OXYGEN_PLAY_SPDIF_MULTICH_01	0x0020
++#define  OXYGEN_PLAY_SPDIF_MULTICH_23	0x0040
++#define  OXYGEN_PLAY_SPDIF_MULTICH_45	0x0060
++#define  OXYGEN_PLAY_SPDIF_MULTICH_67	0x0080
++#define  OXYGEN_PLAY_SPDIF_REC_A	0x00a0
++#define  OXYGEN_PLAY_SPDIF_REC_B	0x00c0
++#define  OXYGEN_PLAY_SPDIF_I2S_ADC_3	0x00e0
++#define  OXYGEN_PLAY_DAC0_SOURCE_MASK	0x0300
++#define  OXYGEN_PLAY_DAC0_SOURCE_SHIFT	8
++#define  OXYGEN_PLAY_DAC1_SOURCE_MASK	0x0c00
++#define  OXYGEN_PLAY_DAC1_SOURCE_SHIFT	10
++#define  OXYGEN_PLAY_DAC2_SOURCE_MASK	0x3000
++#define  OXYGEN_PLAY_DAC2_SOURCE_SHIFT	12
++#define  OXYGEN_PLAY_DAC3_SOURCE_MASK	0xc000
++#define  OXYGEN_PLAY_DAC3_SOURCE_SHIFT	14
++
++#define OXYGEN_REC_ROUTING		0xc2
++#define  OXYGEN_MUTE_I2S_ADC_1		0x01
++#define  OXYGEN_MUTE_I2S_ADC_2		0x02
++#define  OXYGEN_MUTE_I2S_ADC_3		0x04
++#define  OXYGEN_REC_A_ROUTE_MASK	0x08
++#define  OXYGEN_REC_A_ROUTE_I2S_ADC_1	0x00
++#define  OXYGEN_REC_A_ROUTE_AC97_0	0x08
++#define  OXYGEN_REC_B_ROUTE_MASK	0x10
++#define  OXYGEN_REC_B_ROUTE_I2S_ADC_2	0x00
++#define  OXYGEN_REC_B_ROUTE_AC97_1	0x10
++#define  OXYGEN_REC_C_ROUTE_MASK	0x20
++#define  OXYGEN_REC_C_ROUTE_SPDIF	0x00
++#define  OXYGEN_REC_C_ROUTE_I2S_ADC_3	0x20
++
++#define OXYGEN_ADC_MONITOR		0xc3
++#define  OXYGEN_ADC_MONITOR_A		0x01
++#define  OXYGEN_ADC_MONITOR_A_HALF_VOL	0x02
++#define  OXYGEN_ADC_MONITOR_B		0x04
++#define  OXYGEN_ADC_MONITOR_B_HALF_VOL	0x08
++#define  OXYGEN_ADC_MONITOR_C		0x10
++#define  OXYGEN_ADC_MONITOR_C_HALF_VOL	0x20
++
++#define OXYGEN_A_MONITOR_ROUTING	0xc4
++#define  OXYGEN_A_MONITOR_ROUTE_0_MASK	0x03
++#define  OXYGEN_A_MONITOR_ROUTE_0_SHIFT	0
++#define  OXYGEN_A_MONITOR_ROUTE_1_MASK	0x0c
++#define  OXYGEN_A_MONITOR_ROUTE_1_SHIFT	2
++#define  OXYGEN_A_MONITOR_ROUTE_2_MASK	0x30
++#define  OXYGEN_A_MONITOR_ROUTE_2_SHIFT	4
++#define  OXYGEN_A_MONITOR_ROUTE_3_MASK	0xc0
++#define  OXYGEN_A_MONITOR_ROUTE_3_SHIFT	6
++
++#define OXYGEN_AC97_CONTROL		0xd0
++#define  OXYGEN_AC97_COLD_RESET		0x0001
++#define  OXYGEN_AC97_SUSPENDED		0x0002	/* read */
++#define  OXYGEN_AC97_RESUME		0x0002	/* write */
++#define  OXYGEN_AC97_CLOCK_DISABLE	0x0004
++#define  OXYGEN_AC97_NO_CODEC_0		0x0008
++#define  OXYGEN_AC97_CODEC_0		0x0010
++#define  OXYGEN_AC97_CODEC_1		0x0020
++
++#define OXYGEN_AC97_INTERRUPT_MASK	0xd2
++#define  OXYGEN_AC97_INT_READ_DONE	0x01
++#define  OXYGEN_AC97_INT_WRITE_DONE	0x02
++#define  OXYGEN_AC97_INT_CODEC_0	0x10
++#define  OXYGEN_AC97_INT_CODEC_1	0x20
++
++#define OXYGEN_AC97_INTERRUPT_STATUS	0xd3
++/* OXYGEN_AC97_INT_* */
++
++#define OXYGEN_AC97_OUT_CONFIG		0xd4
++#define  OXYGEN_AC97_CODEC1_SLOT3	0x00000001
++#define  OXYGEN_AC97_CODEC1_SLOT3_VSR	0x00000002
++#define  OXYGEN_AC97_CODEC1_SLOT4	0x00000010
++#define  OXYGEN_AC97_CODEC1_SLOT4_VSR	0x00000020
++#define  OXYGEN_AC97_CODEC0_FRONTL	0x00000100
++#define  OXYGEN_AC97_CODEC0_FRONTR	0x00000200
++#define  OXYGEN_AC97_CODEC0_SIDEL	0x00000400
++#define  OXYGEN_AC97_CODEC0_SIDER	0x00000800
++#define  OXYGEN_AC97_CODEC0_CENTER	0x00001000
++#define  OXYGEN_AC97_CODEC0_BASE	0x00002000
++#define  OXYGEN_AC97_CODEC0_REARL	0x00004000
++#define  OXYGEN_AC97_CODEC0_REARR	0x00008000
++
++#define OXYGEN_AC97_IN_CONFIG		0xd8
++#define  OXYGEN_AC97_CODEC1_LINEL	0x00000001
++#define  OXYGEN_AC97_CODEC1_LINEL_VSR	0x00000002
++#define  OXYGEN_AC97_CODEC1_LINEL_16	0x00000000
++#define  OXYGEN_AC97_CODEC1_LINEL_18	0x00000004
++#define  OXYGEN_AC97_CODEC1_LINEL_20	0x00000008
++#define  OXYGEN_AC97_CODEC1_LINER	0x00000010
++#define  OXYGEN_AC97_CODEC1_LINER_VSR	0x00000020
++#define  OXYGEN_AC97_CODEC1_LINER_16	0x00000000
++#define  OXYGEN_AC97_CODEC1_LINER_18	0x00000040
++#define  OXYGEN_AC97_CODEC1_LINER_20	0x00000080
++#define  OXYGEN_AC97_CODEC0_LINEL	0x00000100
++#define  OXYGEN_AC97_CODEC0_LINER	0x00000200
++
++#define OXYGEN_AC97_REGS		0xdc
++#define  OXYGEN_AC97_REG_DATA_MASK	0x0000ffff
++#define  OXYGEN_AC97_REG_ADDR_MASK	0x007f0000
++#define  OXYGEN_AC97_REG_ADDR_SHIFT	16
++#define  OXYGEN_AC97_REG_DIR_MASK	0x00800000
++#define  OXYGEN_AC97_REG_DIR_WRITE	0x00000000
++#define  OXYGEN_AC97_REG_DIR_READ	0x00800000
++#define  OXYGEN_AC97_REG_CODEC_MASK	0x01000000
++#define  OXYGEN_AC97_REG_CODEC_SHIFT	24
++
++#define OXYGEN_TEST			0xe0
++#define  OXYGEN_TEST_RAM_SUCCEEDED	0x01
++#define  OXYGEN_TEST_PLAYBACK_RAM	0x02
++#define  OXYGEN_TEST_RECORD_RAM		0x04
++#define  OXYGEN_TEST_PLL		0x08
++#define  OXYGEN_TEST_2WIRE_LOOPBACK	0x10
++
++#define OXYGEN_DMA_FLUSH		0xe1
++/* OXYGEN_CHANNEL_* */
++
++#define OXYGEN_CODEC_VERSION		0xe4
++#define  OXYGEN_XCID_MASK		0x07
++
++#define OXYGEN_REVISION			0xe6
++#define  OXYGEN_REVISION_XPKGID_MASK	0x0007
++#define  OXYGEN_REVISION_MASK		0xfff8
++#define  OXYGEN_REVISION_2		0x0008	/* bit flag */
++#define  OXYGEN_REVISION_8787		0x0014	/* 8 bits */
++
++#define OXYGEN_OFFSIN_48K		0xe8
++#define OXYGEN_OFFSBASE_48K		0xe9
++#define  OXYGEN_OFFSBASE_MASK		0x0fff
++#define OXYGEN_OFFSIN_44K		0xec
++#define OXYGEN_OFFSBASE_44K		0xed
++
++#endif
+diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
+new file mode 100644
+index 0000000..40e92f5
+--- /dev/null
++++ b/sound/pci/oxygen/virtuoso.c
+@@ -0,0 +1,449 @@
++/*
++ * C-Media CMI8788 driver for Asus Xonar cards
++ *
++ * Copyright (c) Clemens Ladisch <clemens at ladisch.de>
++ *
++ *
++ *  This driver is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License, version 2.
++ *
++ *  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 driver; if not, write to the Free Software
++ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
++ */
++
++/*
++ * CMI8788:
++ *
++ * SPI 0 -> 1st PCM1796 (front)
++ * SPI 1 -> 2nd PCM1796 (surround)
++ * SPI 2 -> 3rd PCM1796 (center/LFE)
++ * SPI 4 -> 4th PCM1796 (back)
++ *
++ * GPIO 2 -> M0 of CS5381
++ * GPIO 3 -> M1 of CS5381
++ * GPIO 5 <- external power present (D2X only)
++ * GPIO 7 -> ALT
++ * GPIO 8 -> enable output to speakers
++ *
++ * CM9780:
++ *
++ * GPIO 0 -> enable AC'97 bypass (line in -> ADC)
++ */
++
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/mutex.h>
++#include <sound/ac97_codec.h>
++#include <sound/control.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/tlv.h>
++#include "oxygen.h"
++#include "cm9780.h"
++
++MODULE_AUTHOR("Clemens Ladisch <clemens at ladisch.de>");
++MODULE_DESCRIPTION("Asus AV200 driver");
++MODULE_LICENSE("GPL");
++MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}");
++
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
++static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
++
++module_param_array(index, int, NULL, 0444);
++MODULE_PARM_DESC(index, "card index");
++module_param_array(id, charp, NULL, 0444);
++MODULE_PARM_DESC(id, "ID string");
++module_param_array(enable, bool, NULL, 0444);
++MODULE_PARM_DESC(enable, "enable card");
++
++static struct pci_device_id xonar_ids[] __devinitdata = {
++	{ OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */
++	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */
++	{ }
++};
++MODULE_DEVICE_TABLE(pci, xonar_ids);
++
++
++#define GPIO_CS5381_M_MASK	0x000c
++#define GPIO_CS5381_M_SINGLE	0x0000
++#define GPIO_CS5381_M_DOUBLE	0x0004
++#define GPIO_CS5381_M_QUAD	0x0008
++#define GPIO_EXT_POWER		0x0020
++#define GPIO_ALT		0x0080
++#define GPIO_OUTPUT_ENABLE	0x0100
++
++#define GPIO_LINE_MUTE		CM9780_GPO0
++
++/* register 16 */
++#define PCM1796_ATL_MASK	0xff
++/* register 17 */
++#define PCM1796_ATR_MASK	0xff
++/* register 18 */
++#define PCM1796_MUTE		0x01
++#define PCM1796_DME		0x02
++#define PCM1796_DMF_MASK	0x0c
++#define PCM1796_DMF_DISABLED	0x00
++#define PCM1796_DMF_48		0x04
++#define PCM1796_DMF_441		0x08
++#define PCM1796_DMF_32		0x0c
++#define PCM1796_FMT_MASK	0x70
++#define PCM1796_FMT_16_RJUST	0x00
++#define PCM1796_FMT_20_RJUST	0x10
++#define PCM1796_FMT_24_RJUST	0x20
++#define PCM1796_FMT_24_LJUST	0x30
++#define PCM1796_FMT_16_I2S	0x40
++#define PCM1796_FMT_24_I2S	0x50
++#define PCM1796_ATLD		0x80
++/* register 19 */
++#define PCM1796_INZD		0x01
++#define PCM1796_FLT_MASK	0x02
++#define PCM1796_FLT_SHARP	0x00
++#define PCM1796_FLT_SLOW	0x02
++#define PCM1796_DFMS		0x04
++#define PCM1796_OPE		0x10
++#define PCM1796_ATS_MASK	0x60
++#define PCM1796_ATS_1		0x00
++#define PCM1796_ATS_2		0x20
++#define PCM1796_ATS_4		0x40
++#define PCM1796_ATS_8		0x60
++#define PCM1796_REV		0x80
++/* register 20 */
++#define PCM1796_OS_MASK		0x03
++#define PCM1796_OS_64		0x00
++#define PCM1796_OS_32		0x01
++#define PCM1796_OS_128		0x02
++#define PCM1796_CHSL_MASK	0x04
++#define PCM1796_CHSL_LEFT	0x00
++#define PCM1796_CHSL_RIGHT	0x04
++#define PCM1796_MONO		0x08
++#define PCM1796_DFTH		0x10
++#define PCM1796_DSD		0x20
++#define PCM1796_SRST		0x40
++/* register 21 */
++#define PCM1796_PCMZ		0x01
++#define PCM1796_DZ_MASK		0x06
++/* register 22 */
++#define PCM1796_ZFGL		0x01
++#define PCM1796_ZFGR		0x02
++/* register 23 */
++#define PCM1796_ID_MASK		0x1f
++
++struct xonar_data {
++	u8 is_d2x;
++	u8 has_power;
++};
++
++static void pcm1796_write(struct oxygen *chip, unsigned int codec,
++			  u8 reg, u8 value)
++{
++	/* maps ALSA channel pair number to SPI output */
++	static const u8 codec_map[4] = {
++		0, 1, 2, 4
++	};
++	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  |
++			 OXYGEN_SPI_DATA_LENGTH_2 |
++			 OXYGEN_SPI_CLOCK_160 |
++			 (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
++			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
++			 (reg << 8) | value);
++}
++
++static void xonar_init(struct oxygen *chip)
++{
++	struct xonar_data *data = chip->model_data;
++	unsigned int i;
++
++	data->is_d2x = chip->pci->subsystem_device == 0x82b7;
++
++	for (i = 0; i < 4; ++i) {
++		pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD);
++		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
++		pcm1796_write(chip, i, 20, PCM1796_OS_64);
++		pcm1796_write(chip, i, 21, 0);
++		pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */
++		pcm1796_write(chip, i, 17, 0xff);
++	}
++
++	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
++			  GPIO_CS5381_M_MASK | GPIO_ALT);
++	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
++			      GPIO_CS5381_M_SINGLE,
++			      GPIO_CS5381_M_MASK | GPIO_ALT);
++	if (data->is_d2x) {
++		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
++				    GPIO_EXT_POWER);
++		oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK,
++				  GPIO_EXT_POWER);
++		chip->interrupt_mask |= OXYGEN_INT_GPIO;
++		data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
++				     & GPIO_EXT_POWER);
++	}
++	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
++	oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
++	msleep(300);
++	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE);
++	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
++
++	snd_component_add(chip->card, "PCM1796");
++	snd_component_add(chip->card, "CS5381");
++}
++
++static void xonar_cleanup(struct oxygen *chip)
++{
++	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
++}
++
++static void set_pcm1796_params(struct oxygen *chip,
++			       struct snd_pcm_hw_params *params)
++{
++#if 0
++	unsigned int i;
++	u8 value;
++
++	value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
++	for (i = 0; i < 4; ++i)
++		pcm1796_write(chip, i, 20, value);
++#endif
++}
++
++static void update_pcm1796_volume(struct oxygen *chip)
++{
++	unsigned int i;
++
++	for (i = 0; i < 4; ++i) {
++		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
++		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
++	}
++}
++
++static void update_pcm1796_mute(struct oxygen *chip)
++{
++	unsigned int i;
++	u8 value;
++
++	value = PCM1796_FMT_24_LJUST | PCM1796_ATLD;
++	if (chip->dac_mute)
++		value |= PCM1796_MUTE;
++	for (i = 0; i < 4; ++i)
++		pcm1796_write(chip, i, 18, value);
++}
++
++static void set_cs5381_params(struct oxygen *chip,
++			      struct snd_pcm_hw_params *params)
++{
++	unsigned int value;
++
++	if (params_rate(params) <= 54000)
++		value = GPIO_CS5381_M_SINGLE;
++	else if (params_rate(params) <= 108000)
++		value = GPIO_CS5381_M_DOUBLE;
++	else
++		value = GPIO_CS5381_M_QUAD;
++	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
++			      value, GPIO_CS5381_M_MASK);
++}
++
++static void xonar_gpio_changed(struct oxygen *chip)
++{
++	struct xonar_data *data = chip->model_data;
++	u8 has_power;
++
++	if (!data->is_d2x)
++		return;
++	has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA)
++		       & GPIO_EXT_POWER);
++	if (has_power != data->has_power) {
++		data->has_power = has_power;
++		if (has_power) {
++			snd_printk(KERN_NOTICE "power restored\n");
++		} else {
++			snd_printk(KERN_CRIT
++				   "Hey! Don't unplug the power cable!\n");
++			/* TODO: stop PCMs */
++		}
++	}
++}
++
++static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)
++{
++	unsigned int index = chip->controls[control]->private_value & 0xff;
++	u16 value;
++
++	value = oxygen_read_ac97(chip, 0, index);
++	if (!(value & 0x8000)) {
++		oxygen_write_ac97(chip, 0, index, value | 0x8000);
++		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
++			       &chip->controls[control]->id);
++	}
++}
++
++static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec,
++				   unsigned int reg, int mute)
++{
++	if (codec != 0)
++		return;
++	/* line-in is exclusive */
++	switch (reg) {
++	case AC97_LINE:
++		oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
++					 mute ? GPIO_LINE_MUTE : 0,
++					 GPIO_LINE_MUTE);
++		if (!mute) {
++			mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH);
++			mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH);
++			mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH);
++		}
++		break;
++	case AC97_MIC:
++	case AC97_CD:
++	case AC97_VIDEO:
++	case AC97_AUX:
++		if (!mute) {
++			oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
++					     GPIO_LINE_MUTE);
++			mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH);
++		}
++		break;
++	}
++}
++
++static int pcm1796_volume_info(struct snd_kcontrol *ctl,
++			       struct snd_ctl_elem_info *info)
++{
++	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	info->count = 8;
++	info->value.integer.min = 0x0f;
++	info->value.integer.max = 0xff;
++	return 0;
++}
++
++static int alt_switch_get(struct snd_kcontrol *ctl,
++			  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++
++	value->value.integer.value[0] =
++		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT);
++	return 0;
++}
++
++static int alt_switch_put(struct snd_kcontrol *ctl,
++			  struct snd_ctl_elem_value *value)
++{
++	struct oxygen *chip = ctl->private_data;
++	u16 old_bits, new_bits;
++	int changed;
++
++	spin_lock_irq(&chip->reg_lock);
++	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
++	if (value->value.integer.value[0])
++		new_bits = old_bits | GPIO_ALT;
++	else
++		new_bits = old_bits & ~GPIO_ALT;
++	changed = new_bits != old_bits;
++	if (changed)
++		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
++	spin_unlock_irq(&chip->reg_lock);
++	return changed;
++}
++
++static const struct snd_kcontrol_new alt_switch = {
++	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name = "Analog Loopback Switch",
++	.info = snd_ctl_boolean_mono_info,
++	.get = alt_switch_get,
++	.put = alt_switch_put,
++};
++
++static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
++
++static int xonar_control_filter(struct snd_kcontrol_new *template)
++{
++	if (!strcmp(template->name, "Master Playback Volume")) {
++		template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
++		template->info = pcm1796_volume_info,
++		template->tlv.p = pcm1796_db_scale;
++	} else if (!strncmp(template->name, "CD Capture ", 11)) {
++		/* CD in is actually connected to the video in pin */
++		template->private_value ^= AC97_CD ^ AC97_VIDEO;
++	} else if (!strcmp(template->name, "Line Capture Volume")) {
++		return 1; /* line-in bypasses the AC'97 mixer */
++	}
++	return 0;
++}
++
++static int xonar_mixer_init(struct oxygen *chip)
++{
++	return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
++}
++
++static const struct oxygen_model model_xonar = {
++	.shortname = "Asus AV200",
++	.longname = "Asus Virtuoso 200",
++	.chip = "AV200",
++	.init = xonar_init,
++	.control_filter = xonar_control_filter,
++	.mixer_init = xonar_mixer_init,
++	.cleanup = xonar_cleanup,
++	.set_dac_params = set_pcm1796_params,
++	.set_adc_params = set_cs5381_params,
++	.update_dac_volume = update_pcm1796_volume,
++	.update_dac_mute = update_pcm1796_mute,
++	.ac97_switch_hook = xonar_ac97_switch_hook,
++	.gpio_changed = xonar_gpio_changed,
++	.model_data_size = sizeof(struct xonar_data),
++	.dac_channels = 8,
++	.used_channels = OXYGEN_CHANNEL_B |
++			 OXYGEN_CHANNEL_C |
++			 OXYGEN_CHANNEL_SPDIF |
++			 OXYGEN_CHANNEL_MULTICH,
++	.function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5,
++	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
++};
++
++static int __devinit xonar_probe(struct pci_dev *pci,
++				 const struct pci_device_id *pci_id)
++{
++	static int dev;
++	int err;
++
++	if (dev >= SNDRV_CARDS)
++		return -ENODEV;
++	if (!enable[dev]) {
++		++dev;
++		return -ENOENT;
++	}
++	err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar);
++	if (err >= 0)
++		++dev;
++	return err;
++}
++
++static struct pci_driver xonar_driver = {
++	.name = "AV200",
++	.id_table = xonar_ids,
++	.probe = xonar_probe,
++	.remove = __devexit_p(oxygen_pci_remove),
++};
++
++static int __init alsa_card_xonar_init(void)
++{
++	return pci_register_driver(&xonar_driver);
++}
++
++static void __exit alsa_card_xonar_exit(void)
++{
++	pci_unregister_driver(&xonar_driver);
++}
++
++module_init(alsa_card_xonar_init)
++module_exit(alsa_card_xonar_exit)
+diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
+index 2d618bd..9d5bb76 100644
+--- a/sound/pci/pcxhr/pcxhr.c
++++ b/sound/pci/pcxhr/pcxhr.c
+@@ -21,7 +21,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/slab.h>
+diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
+index 0ff8dc3..c4e415d 100644
+--- a/sound/pci/pcxhr/pcxhr_core.c
++++ b/sound/pci/pcxhr/pcxhr_core.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/firmware.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
+index d55d8bc..e6a4bfb 100644
+--- a/sound/pci/pcxhr/pcxhr_hwdep.c
++++ b/sound/pci/pcxhr/pcxhr_hwdep.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/vmalloc.h>
+ #include <linux/firmware.h>
+diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
+index 5f8d426..aabc7bc 100644
+--- a/sound/pci/pcxhr/pcxhr_mixer.c
++++ b/sound/pci/pcxhr/pcxhr_mixer.c
+@@ -21,7 +21,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/time.h>
+ #include <linux/interrupt.h>
+ #include <linux/init.h>
+@@ -120,8 +119,18 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
+ 	is_capture = (kcontrol->private_value != 0);
+ 	for (i = 0; i < 2; i++) {
+ 		int  new_volume = ucontrol->value.integer.value[i];
+-		int* stored_volume = is_capture ? &chip->analog_capture_volume[i] :
++		int *stored_volume = is_capture ?
++			&chip->analog_capture_volume[i] :
+ 			&chip->analog_playback_volume[i];
++		if (is_capture) {
++			if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN ||
++			    new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX)
++				continue;
++		} else {
++			if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN ||
++			    new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX)
++				continue;
++		}
+ 		if (*stored_volume != new_volume) {
+ 			*stored_volume = new_volume;
+ 			changed = 1;
+@@ -165,10 +174,13 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
+ 	int i, changed = 0;
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	for(i = 0; i < 2; i++) {
+-		if (chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
+-			chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
++		if (chip->analog_playback_active[i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->analog_playback_active[i] =
++				!!ucontrol->value.integer.value[i];
+ 			changed = 1;
+-			pcxhr_update_analog_audio_level(chip, 0, i);	/* update playback levels */
++			/* update playback levels */
++			pcxhr_update_analog_audio_level(chip, 0, i);
+ 		}
+ 	}
+ 	mutex_unlock(&chip->mgr->mixer_mutex);
+@@ -323,20 +335,24 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
+ 	int i;
+ 
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+-	if (is_capture)
+-		stored_volume = chip->digital_capture_volume;		/* digital capture */
+-	else
+-		stored_volume = chip->digital_playback_volume[idx];	/* digital playback */
++	if (is_capture)		/* digital capture */
++		stored_volume = chip->digital_capture_volume;
++	else			/* digital playback */
++		stored_volume = chip->digital_playback_volume[idx];
+ 	for (i = 0; i < 2; i++) {
+-		if (stored_volume[i] != ucontrol->value.integer.value[i]) {
+-			stored_volume[i] = ucontrol->value.integer.value[i];
++		int vol = ucontrol->value.integer.value[i];
++		if (vol < PCXHR_DIGITAL_LEVEL_MIN ||
++		    vol > PCXHR_DIGITAL_LEVEL_MAX)
++			continue;
++		if (stored_volume[i] != vol) {
++			stored_volume[i] = vol;
+ 			changed = 1;
+ 			if (is_capture)	/* update capture volume */
+ 				pcxhr_update_audio_pipe_level(chip, 1, i);
+ 		}
+ 	}
+-	if (! is_capture && changed)
+-		pcxhr_update_playback_stream_level(chip, idx);	/* update playback volume */
++	if (!is_capture && changed)	/* update playback volume */
++		pcxhr_update_playback_stream_level(chip, idx);
+ 	mutex_unlock(&chip->mgr->mixer_mutex);
+ 	return changed;
+ }
+@@ -378,8 +394,10 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	j = idx;
+ 	for (i = 0; i < 2; i++) {
+-		if (chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
+-			chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
++		if (chip->digital_playback_active[j][i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->digital_playback_active[j][i] =
++				!!ucontrol->value.integer.value[i];
+ 			changed = 1;
+ 		}
+ 	}
+@@ -423,10 +441,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
+ 
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	for (i = 0; i < 2; i++) {
+-		if (chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
+-			chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
+-			if(chip->monitoring_active[i])	/* do only when monitoring is unmuted */
++		if (chip->monitoring_volume[i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->monitoring_volume[i] =
++				!!ucontrol->value.integer.value[i];
++			if(chip->monitoring_active[i])
+ 				/* update monitoring volume and mute */
++				/* do only when monitoring is unmuted */
+ 				pcxhr_update_audio_pipe_level(chip, 0, i);
+ 			changed = 1;
+ 		}
+@@ -470,15 +491,17 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
+ 
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	for (i = 0; i < 2; i++) {
+-		if (chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
+-			chip->monitoring_active[i] = ucontrol->value.integer.value[i];
++		if (chip->monitoring_active[i] !=
++		    ucontrol->value.integer.value[i]) {
++			chip->monitoring_active[i] =
++				!!ucontrol->value.integer.value[i];
+ 			changed |= (1<<i); /* mask 0x01 and 0x02 */
+ 		}
+ 	}
+-	if(changed & 0x01)
++	if (changed & 0x01)
+ 		/* update left monitoring volume and mute */
+ 		pcxhr_update_audio_pipe_level(chip, 0, 0);
+-	if(changed & 0x02)
++	if (changed & 0x02)
+ 		/* update right monitoring volume and mute */
+ 		pcxhr_update_audio_pipe_level(chip, 0, 1);
+ 
+@@ -579,6 +602,8 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
+ 	struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ 	int ret = 0;
+ 
++	if (ucontrol->value.enumerated.item[0] >= 3)
++		return -EINVAL;
+ 	mutex_lock(&chip->mgr->mixer_mutex);
+ 	if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) {
+ 		chip->audio_capture_source = ucontrol->value.enumerated.item[0];
+@@ -642,8 +667,11 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
+ 				struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
++	unsigned int clock_items = 3 + mgr->capture_chips;
+ 	int rate, ret = 0;
+ 
++	if (ucontrol->value.enumerated.item[0] >= clock_items)
++		return -EINVAL;
+ 	mutex_lock(&mgr->mixer_mutex);
+ 	if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) {
+ 		mutex_lock(&mgr->setup_mutex);
+diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
+index 8e54104..9408b1e 100644
+--- a/sound/pci/riptide/riptide.c
++++ b/sound/pci/riptide/riptide.c
+@@ -88,7 +88,6 @@
+             Adopted for Windows NT driver          01/20/98      CNL
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
+index 1475912..df184aa 100644
+--- a/sound/pci/rme32.c
++++ b/sound/pci/rme32.c
+@@ -69,7 +69,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
+index 0b3c532..fb0a4ee 100644
+--- a/sound/pci/rme96.c
++++ b/sound/pci/rme96.c
+@@ -23,7 +23,6 @@
+  *
+  */      
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -2195,22 +2194,25 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
+ {
+ 	struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
+         int change = 0;
++	unsigned int vol, maxvol;
+ 
+-	if (!RME96_HAS_ANALOG_OUT(rme96)) {
++
++	if (!RME96_HAS_ANALOG_OUT(rme96))
+ 		return -EINVAL;
+-	}
++	maxvol = RME96_185X_MAX_OUT(rme96);
+ 	spin_lock_irq(&rme96->lock);
+-        if (u->value.integer.value[0] != rme96->vol[0]) {
+-		rme96->vol[0] = u->value.integer.value[0];
+-                change = 1;
+-        }
+-        if (u->value.integer.value[1] != rme96->vol[1]) {
+-		rme96->vol[1] = u->value.integer.value[1];
+-                change = 1;
+-        }
+-	if (change) {
+-		snd_rme96_apply_dac_volume(rme96);
++	vol = u->value.integer.value[0];
++	if (vol != rme96->vol[0] && vol <= maxvol) {
++		rme96->vol[0] = vol;
++		change = 1;
++	}
++	vol = u->value.integer.value[1];
++	if (vol != rme96->vol[1] && vol <= maxvol) {
++		rme96->vol[1] = vol;
++		change = 1;
+ 	}
++	if (change)
++		snd_rme96_apply_dac_volume(rme96);
+ 	spin_unlock_irq(&rme96->lock);
+ 
+         return change;
+diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
+index ff26a36..c2bd438 100644
+--- a/sound/pci/rme9652/hdsp.c
++++ b/sound/pci/rme9652/hdsp.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -104,8 +103,6 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
+ #define HDSP_statusRegister    0
+ #define HDSP_timecode        128
+ #define HDSP_status2Register 192
+-#define HDSP_midiDataOut0    352
+-#define HDSP_midiDataOut1    356
+ #define HDSP_midiDataIn0     360
+ #define HDSP_midiDataIn1     364
+ #define HDSP_midiStatusOut0  384
+@@ -610,7 +607,10 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
+ 	case Multiface:
+ 	case Digiface:
+ 	default:
+-		return (64 * out) + (32 + (in));
++		if (hdsp->firmware_rev == 0xa)
++			return (64 * out) + (32 + (in));
++		else
++			return (52 * out) + (26 + (in));
+ 	case H9632:
+ 		return (32 * out) + (16 + (in));
+ 	case H9652:
+@@ -624,7 +624,10 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
+ 	case Multiface:
+ 	case Digiface:
+ 	default:
+-		return (64 * out) + in;
++		if (hdsp->firmware_rev == 0xa)
++			return (64 * out) + in;
++		else
++			return (52 * out) + in;
+ 	case H9632:
+ 		return (32 * out) + in;
+ 	case H9652:
+@@ -2121,7 +2124,7 @@ static int snd_hdsp_put_clock_source_lock(struct snd_kcontrol *kcontrol, struct
+ 
+ 	change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked;
+ 	if (change)
+-		hdsp->clock_source_locked = ucontrol->value.integer.value[0];
++		hdsp->clock_source_locked = !!ucontrol->value.integer.value[0];
+ 	return change;
+ }
+ 
+@@ -3558,7 +3561,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+ 
+ }
+ 
+-static void __devinit snd_hdsp_proc_init(struct hdsp *hdsp)
++static void snd_hdsp_proc_init(struct hdsp *hdsp)
+ {
+ 	struct snd_info_entry *entry;
+ 
+@@ -3606,7 +3609,7 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp)
+ 
+ 	/* ASSUMPTION: hdsp->lock is either held, or
+ 	   there is no need to hold it (e.g. during module
+-	   initalization).
++	   initialization).
+ 	 */
+ 
+ 	/* set defaults:
+diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
+index f1bdda6..9a19ae6 100644
+--- a/sound/pci/rme9652/hdspm.c
++++ b/sound/pci/rme9652/hdspm.c
+@@ -23,7 +23,6 @@
+  *   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/interrupt.h>
+@@ -3348,7 +3347,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
+ 	unsigned int i;
+ 
+ 	/* ASSUMPTION: hdspm->lock is either held, or there is no need to
+-	   hold it (e.g. during module initalization).
++	   hold it (e.g. during module initialization).
+ 	 */
+ 
+ 	/* set defaults:       */
+@@ -3416,7 +3415,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
+ 
+ 
+ /*------------------------------------------------------------
+-   interupt 
++   interrupt 
+  ------------------------------------------------------------*/
+ 
+ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
+diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
+index 34f96f1..a123f0e 100644
+--- a/sound/pci/rme9652/rme9652.c
++++ b/sound/pci/rme9652/rme9652.c
+@@ -20,7 +20,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -148,7 +147,7 @@ MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall},"
+ #define RME9652_start_bit	   (1<<0)	/* start record/play */
+                                                 /* bits 1-3 encode buffersize/latency */
+ #define RME9652_Master		   (1<<4)	/* Clock Mode Master=1,Slave/Auto=0 */
+-#define RME9652_IE		   (1<<5)	/* Interupt Enable */
++#define RME9652_IE		   (1<<5)	/* Interrupt Enable */
+ #define RME9652_freq		   (1<<6)       /* samplerate 0=44.1/88.2, 1=48/96 kHz */
+ #define RME9652_freq1		   (1<<7)       /* if 0, 32kHz, else always 1 */
+ #define RME9652_DS                 (1<<8)	/* Doule Speed 0=44.1/48, 1=88.2/96 Khz */
+@@ -1826,7 +1825,7 @@ static void snd_rme9652_set_defaults(struct snd_rme9652 *rme9652)
+ 
+ 	/* ASSUMPTION: rme9652->lock is either held, or
+ 	   there is no need to hold it (e.g. during module
+-	   initalization).
++	   initialization).
+ 	 */
+ 
+ 	/* set defaults:
+diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
+new file mode 100644
+index 0000000..dcd7cd0
+--- /dev/null
++++ b/sound/pci/sis7019.c
+@@ -0,0 +1,1460 @@
++/*
++ *  Driver for SiS7019 Audio Accelerator
++ *
++ *  Copyright (C) 2004-2007, David Dillow
++ *  Written by David Dillow <dave at thedillows.org>
++ *  Inspired by the Trident 4D-WaveDX/NX driver.
++ *
++ *  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 as published by
++ *  the Free Software Foundation, version 2.
++ *
++ *  This program 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 <linux/init.h>
++#include <linux/pci.h>
++#include <linux/time.h>
++#include <linux/moduleparam.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <sound/core.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include "sis7019.h"
++
++MODULE_AUTHOR("David Dillow <dave at thedillows.org>");
++MODULE_DESCRIPTION("SiS7019");
++MODULE_LICENSE("GPL");
++MODULE_SUPPORTED_DEVICE("{{SiS,SiS7019 Audio Accelerator}}");
++
++static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
++static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
++static int enable = 1;
++
++module_param(index, int, 0444);
++MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator.");
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator.");
++module_param(enable, bool, 0444);
++MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator.");
++
++static struct pci_device_id snd_sis7019_ids[] = {
++	{ PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) },
++	{ 0, }
++};
++
++MODULE_DEVICE_TABLE(pci, snd_sis7019_ids);
++
++/* There are three timing modes for the voices.
++ *
++ * For both playback and capture, when the buffer is one or two periods long,
++ * we use the hardware's built-in Mid-Loop Interrupt and End-Loop Interrupt
++ * to let us know when the periods have ended.
++ *
++ * When performing playback with more than two periods per buffer, we set
++ * the "Stop Sample Offset" and tell the hardware to interrupt us when we
++ * reach it. We then update the offset and continue on until we are
++ * interrupted for the next period.
++ *
++ * Capture channels do not have a SSO, so we allocate a playback channel to
++ * use as a timer for the capture periods. We use the SSO on the playback
++ * channel to clock out virtual periods, and adjust the virtual period length
++ * to maintain synchronization. This algorithm came from the Trident driver.
++ *
++ * FIXME: It'd be nice to make use of some of the synth features in the
++ * hardware, but a woeful lack of documentation is a significant roadblock.
++ */
++struct voice {
++	u16 flags;
++#define 	VOICE_IN_USE		1
++#define 	VOICE_CAPTURE		2
++#define 	VOICE_SSO_TIMING	4
++#define 	VOICE_SYNC_TIMING	8
++	u16 sync_cso;
++	u16 period_size;
++	u16 buffer_size;
++	u16 sync_period_size;
++	u16 sync_buffer_size;
++	u32 sso;
++	u32 vperiod;
++	struct snd_pcm_substream *substream;
++	struct voice *timing;
++	void __iomem *ctrl_base;
++	void __iomem *wave_base;
++	void __iomem *sync_base;
++	int num;
++};
++
++/* We need four pages to store our wave parameters during a suspend. If
++ * we're not doing power management, we still need to allocate a page
++ * for the silence buffer.
++ */
++#ifdef CONFIG_PM
++#define SIS_SUSPEND_PAGES	4
++#else
++#define SIS_SUSPEND_PAGES	1
++#endif
++
++struct sis7019 {
++	unsigned long ioport;
++	void __iomem *ioaddr;
++	int irq;
++	int codecs_present;
++
++	struct pci_dev *pci;
++	struct snd_pcm *pcm;
++	struct snd_card *card;
++	struct snd_ac97 *ac97[3];
++
++	/* Protect against more than one thread hitting the AC97
++	 * registers (in a more polite manner than pounding the hardware
++	 * semaphore)
++	 */
++	struct mutex ac97_mutex;
++
++	/* voice_lock protects allocation/freeing of the voice descriptions
++	 */
++	spinlock_t voice_lock;
++
++	struct voice voices[64];
++	struct voice capture_voice;
++
++	/* Allocate pages to store the internal wave state during
++	 * suspends. When we're operating, this can be used as a silence
++	 * buffer for a timing channel.
++	 */
++	void *suspend_state[SIS_SUSPEND_PAGES];
++
++	int silence_users;
++	dma_addr_t silence_dma_addr;
++};
++
++#define SIS_PRIMARY_CODEC_PRESENT	0x0001
++#define SIS_SECONDARY_CODEC_PRESENT	0x0002
++#define SIS_TERTIARY_CODEC_PRESENT	0x0004
++
++/* The HW offset parameters (Loop End, Stop Sample, End Sample) have a
++ * documented range of 8-0xfff8 samples. Given that they are 0-based,
++ * that places our period/buffer range at 9-0xfff9 samples. That makes the
++ * max buffer size 0xfff9 samples * 2 channels * 2 bytes per sample, and
++ * max samples / min samples gives us the max periods in a buffer.
++ *
++ * We'll add a constraint upon open that limits the period and buffer sample
++ * size to values that are legal for the hardware.
++ */
++static struct snd_pcm_hardware sis_playback_hw_info = {
++	.info = (SNDRV_PCM_INFO_MMAP |
++		 SNDRV_PCM_INFO_MMAP_VALID |
++		 SNDRV_PCM_INFO_INTERLEAVED |
++		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
++		 SNDRV_PCM_INFO_SYNC_START |
++		 SNDRV_PCM_INFO_RESUME),
++	.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
++		    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
++	.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
++	.rate_min = 4000,
++	.rate_max = 48000,
++	.channels_min = 1,
++	.channels_max = 2,
++	.buffer_bytes_max = (0xfff9 * 4),
++	.period_bytes_min = 9,
++	.period_bytes_max = (0xfff9 * 4),
++	.periods_min = 1,
++	.periods_max = (0xfff9 / 9),
++};
++
++static struct snd_pcm_hardware sis_capture_hw_info = {
++	.info = (SNDRV_PCM_INFO_MMAP |
++		 SNDRV_PCM_INFO_MMAP_VALID |
++		 SNDRV_PCM_INFO_INTERLEAVED |
++		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
++		 SNDRV_PCM_INFO_SYNC_START |
++		 SNDRV_PCM_INFO_RESUME),
++	.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
++		    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
++	.rates = SNDRV_PCM_RATE_48000,
++	.rate_min = 4000,
++	.rate_max = 48000,
++	.channels_min = 1,
++	.channels_max = 2,
++	.buffer_bytes_max = (0xfff9 * 4),
++	.period_bytes_min = 9,
++	.period_bytes_max = (0xfff9 * 4),
++	.periods_min = 1,
++	.periods_max = (0xfff9 / 9),
++};
++
++static void sis_update_sso(struct voice *voice, u16 period)
++{
++	void __iomem *base = voice->ctrl_base;
++
++	voice->sso += period;
++	if (voice->sso >= voice->buffer_size)
++		voice->sso -= voice->buffer_size;
++
++	/* Enforce the documented hardware minimum offset */
++	if (voice->sso < 8)
++		voice->sso = 8;
++
++	/* The SSO is in the upper 16 bits of the register. */
++	writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2);
++}
++
++static void sis_update_voice(struct voice *voice)
++{
++	if (voice->flags & VOICE_SSO_TIMING) {
++		sis_update_sso(voice, voice->period_size);
++	} else if (voice->flags & VOICE_SYNC_TIMING) {
++		int sync;
++
++		/* If we've not hit the end of the virtual period, update
++		 * our records and keep going.
++		 */
++		if (voice->vperiod > voice->period_size) {
++			voice->vperiod -= voice->period_size;
++			if (voice->vperiod < voice->period_size)
++				sis_update_sso(voice, voice->vperiod);
++			else
++				sis_update_sso(voice, voice->period_size);
++			return;
++		}
++
++		/* Calculate our relative offset between the target and
++		 * the actual CSO value. Since we're operating in a loop,
++		 * if the value is more than half way around, we can
++		 * consider ourselves wrapped.
++		 */
++		sync = voice->sync_cso;
++		sync -= readw(voice->sync_base + SIS_CAPTURE_DMA_FORMAT_CSO);
++		if (sync > (voice->sync_buffer_size / 2))
++			sync -= voice->sync_buffer_size;
++
++		/* If sync is positive, then we interrupted too early, and
++		 * we'll need to come back in a few samples and try again.
++		 * There's a minimum wait, as it takes some time for the DMA
++		 * engine to startup, etc...
++		 */
++		if (sync > 0) {
++			if (sync < 16)
++				sync = 16;
++			sis_update_sso(voice, sync);
++			return;
++		}
++
++		/* Ok, we interrupted right on time, or (hopefully) just
++		 * a bit late. We'll adjst our next waiting period based
++		 * on how close we got.
++		 *
++		 * We need to stay just behind the actual channel to ensure
++		 * it really is past a period when we get our interrupt --
++		 * otherwise we'll fall into the early code above and have
++		 * a minimum wait time, which makes us quite late here,
++		 * eating into the user's time to refresh the buffer, esp.
++		 * if using small periods.
++		 *
++		 * If we're less than 9 samples behind, we're on target.
++		 */
++		if (sync > -9)
++			voice->vperiod = voice->sync_period_size + 1;
++		else
++			voice->vperiod = voice->sync_period_size - 4;
++
++		if (voice->vperiod < voice->buffer_size) {
++			sis_update_sso(voice, voice->vperiod);
++			voice->vperiod = 0;
++		} else
++			sis_update_sso(voice, voice->period_size);
++
++		sync = voice->sync_cso + voice->sync_period_size;
++		if (sync >= voice->sync_buffer_size)
++			sync -= voice->sync_buffer_size;
++		voice->sync_cso = sync;
++	}
++
++	snd_pcm_period_elapsed(voice->substream);
++}
++
++static void sis_voice_irq(u32 status, struct voice *voice)
++{
++	int bit;
++
++	while (status) {
++		bit = __ffs(status);
++		status >>= bit + 1;
++		voice += bit;
++		sis_update_voice(voice);
++		voice++;
++	}
++}
++
++static irqreturn_t sis_interrupt(int irq, void *dev)
++{
++	struct sis7019 *sis = dev;
++	unsigned long io = sis->ioport;
++	struct voice *voice;
++	u32 intr, status;
++
++	/* We only use the DMA interrupts, and we don't enable any other
++	 * source of interrupts. But, it is possible to see an interupt
++	 * status that didn't actually interrupt us, so eliminate anything
++	 * we're not expecting to avoid falsely claiming an IRQ, and an
++	 * ensuing endless loop.
++	 */
++	intr = inl(io + SIS_GISR);
++	intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
++		SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
++	if (!intr)
++		return IRQ_NONE;
++
++	do {
++		status = inl(io + SIS_PISR_A);
++		if (status) {
++			sis_voice_irq(status, sis->voices);
++			outl(status, io + SIS_PISR_A);
++		}
++
++		status = inl(io + SIS_PISR_B);
++		if (status) {
++			sis_voice_irq(status, &sis->voices[32]);
++			outl(status, io + SIS_PISR_B);
++		}
++
++		status = inl(io + SIS_RISR);
++		if (status) {
++			voice = &sis->capture_voice;
++			if (!voice->timing)
++				snd_pcm_period_elapsed(voice->substream);
++
++			outl(status, io + SIS_RISR);
++		}
++
++		outl(intr, io + SIS_GISR);
++		intr = inl(io + SIS_GISR);
++		intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS |
++			SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS;
++	} while (intr);
++
++	return IRQ_HANDLED;
++}
++
++static u32 sis_rate_to_delta(unsigned int rate)
++{
++	u32 delta;
++
++	/* This was copied from the trident driver, but it seems its gotten
++	 * around a bit... nevertheless, it works well.
++	 *
++	 * We special case 44100 and 8000 since rounding with the equation
++	 * does not give us an accurate enough value. For 11025 and 22050
++	 * the equation gives us the best answer. All other frequencies will
++	 * also use the equation. JDW
++	 */
++	if (rate == 44100)
++		delta = 0xeb3;
++	else if (rate == 8000)
++		delta = 0x2ab;
++	else if (rate == 48000)
++		delta = 0x1000;
++	else
++		delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff;
++	return delta;
++}
++
++static void __sis_map_silence(struct sis7019 *sis)
++{
++	/* Helper function: must hold sis->voice_lock on entry */
++	if (!sis->silence_users)
++		sis->silence_dma_addr = pci_map_single(sis->pci,
++						sis->suspend_state[0],
++						4096, PCI_DMA_TODEVICE);
++	sis->silence_users++;
++}
++
++static void __sis_unmap_silence(struct sis7019 *sis)
++{
++	/* Helper function: must hold sis->voice_lock on entry */
++	sis->silence_users--;
++	if (!sis->silence_users)
++		pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096,
++					PCI_DMA_TODEVICE);
++}
++
++static void sis_free_voice(struct sis7019 *sis, struct voice *voice)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&sis->voice_lock, flags);
++	if (voice->timing) {
++		__sis_unmap_silence(sis);
++		voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING |
++						VOICE_SYNC_TIMING);
++		voice->timing = NULL;
++	}
++	voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING);
++	spin_unlock_irqrestore(&sis->voice_lock, flags);
++}
++
++static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis)
++{
++	/* Must hold the voice_lock on entry */
++	struct voice *voice;
++	int i;
++
++	for (i = 0; i < 64; i++) {
++		voice = &sis->voices[i];
++		if (voice->flags & VOICE_IN_USE)
++			continue;
++		voice->flags |= VOICE_IN_USE;
++		goto found_one;
++	}
++	voice = NULL;
++
++found_one:
++	return voice;
++}
++
++static struct voice *sis_alloc_playback_voice(struct sis7019 *sis)
++{
++	struct voice *voice;
++	unsigned long flags;
++
++	spin_lock_irqsave(&sis->voice_lock, flags);
++	voice = __sis_alloc_playback_voice(sis);
++	spin_unlock_irqrestore(&sis->voice_lock, flags);
++
++	return voice;
++}
++
++static int sis_alloc_timing_voice(struct snd_pcm_substream *substream,
++					struct snd_pcm_hw_params *hw_params)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice = runtime->private_data;
++	unsigned int period_size, buffer_size;
++	unsigned long flags;
++	int needed;
++
++	/* If there are one or two periods per buffer, we don't need a
++	 * timing voice, as we can use the capture channel's interrupts
++	 * to clock out the periods.
++	 */
++	period_size = params_period_size(hw_params);
++	buffer_size = params_buffer_size(hw_params);
++	needed = (period_size != buffer_size &&
++			period_size != (buffer_size / 2));
++
++	if (needed && !voice->timing) {
++		spin_lock_irqsave(&sis->voice_lock, flags);
++		voice->timing = __sis_alloc_playback_voice(sis);
++		if (voice->timing)
++			__sis_map_silence(sis);
++		spin_unlock_irqrestore(&sis->voice_lock, flags);
++		if (!voice->timing)
++			return -ENOMEM;
++		voice->timing->substream = substream;
++	} else if (!needed && voice->timing) {
++		sis_free_voice(sis, voice);
++		voice->timing = NULL;
++	}
++
++	return 0;
++}
++
++static int sis_playback_open(struct snd_pcm_substream *substream)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice;
++
++	voice = sis_alloc_playback_voice(sis);
++	if (!voice)
++		return -EAGAIN;
++
++	voice->substream = substream;
++	runtime->private_data = voice;
++	runtime->hw = sis_playback_hw_info;
++	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
++						9, 0xfff9);
++	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
++						9, 0xfff9);
++	snd_pcm_set_sync(substream);
++	return 0;
++}
++
++static int sis_substream_close(struct snd_pcm_substream *substream)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice = runtime->private_data;
++
++	sis_free_voice(sis, voice);
++	return 0;
++}
++
++static int sis_playback_hw_params(struct snd_pcm_substream *substream,
++					struct snd_pcm_hw_params *hw_params)
++{
++	return snd_pcm_lib_malloc_pages(substream,
++					params_buffer_bytes(hw_params));
++}
++
++static int sis_hw_free(struct snd_pcm_substream *substream)
++{
++	return snd_pcm_lib_free_pages(substream);
++}
++
++static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice = runtime->private_data;
++	void __iomem *ctrl_base = voice->ctrl_base;
++	void __iomem *wave_base = voice->wave_base;
++	u32 format, dma_addr, control, sso_eso, delta, reg;
++	u16 leo;
++
++	/* We rely on the PCM core to ensure that the parameters for this
++	 * substream do not change on us while we're programming the HW.
++	 */
++	format = 0;
++	if (snd_pcm_format_width(runtime->format) == 8)
++		format |= SIS_PLAY_DMA_FORMAT_8BIT;
++	if (!snd_pcm_format_signed(runtime->format))
++		format |= SIS_PLAY_DMA_FORMAT_UNSIGNED;
++	if (runtime->channels == 1)
++		format |= SIS_PLAY_DMA_FORMAT_MONO;
++
++	/* The baseline setup is for a single period per buffer, and
++	 * we add bells and whistles as needed from there.
++	 */
++	dma_addr = runtime->dma_addr;
++	leo = runtime->buffer_size - 1;
++	control = leo | SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_LEO;
++	sso_eso = leo;
++
++	if (runtime->period_size == (runtime->buffer_size / 2)) {
++		control |= SIS_PLAY_DMA_INTR_AT_MLP;
++	} else if (runtime->period_size != runtime->buffer_size) {
++		voice->flags |= VOICE_SSO_TIMING;
++		voice->sso = runtime->period_size - 1;
++		voice->period_size = runtime->period_size;
++		voice->buffer_size = runtime->buffer_size;
++
++		control &= ~SIS_PLAY_DMA_INTR_AT_LEO;
++		control |= SIS_PLAY_DMA_INTR_AT_SSO;
++		sso_eso |= (runtime->period_size - 1) << 16;
++	}
++
++	delta = sis_rate_to_delta(runtime->rate);
++
++	/* Ok, we're ready to go, set up the channel.
++	 */
++	writel(format, ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
++	writel(dma_addr, ctrl_base + SIS_PLAY_DMA_BASE);
++	writel(control, ctrl_base + SIS_PLAY_DMA_CONTROL);
++	writel(sso_eso, ctrl_base + SIS_PLAY_DMA_SSO_ESO);
++
++	for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
++		writel(0, wave_base + reg);
++
++	writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
++	writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
++	writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
++			SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
++			SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
++			wave_base + SIS_WAVE_CHANNEL_CONTROL);
++
++	/* Force PCI writes to post. */
++	readl(ctrl_base);
++
++	return 0;
++}
++
++static int sis_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	unsigned long io = sis->ioport;
++	struct snd_pcm_substream *s;
++	struct voice *voice;
++	void *chip;
++	int starting;
++	u32 record = 0;
++	u32 play[2] = { 0, 0 };
++
++	/* No locks needed, as the PCM core will hold the locks on the
++	 * substreams, and the HW will only start/stop the indicated voices
++	 * without changing the state of the others.
++	 */
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++	case SNDRV_PCM_TRIGGER_RESUME:
++		starting = 1;
++		break;
++	case SNDRV_PCM_TRIGGER_STOP:
++	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++	case SNDRV_PCM_TRIGGER_SUSPEND:
++		starting = 0;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	snd_pcm_group_for_each_entry(s, substream) {
++		/* Make sure it is for us... */
++		chip = snd_pcm_substream_chip(s);
++		if (chip != sis)
++			continue;
++
++		voice = s->runtime->private_data;
++		if (voice->flags & VOICE_CAPTURE) {
++			record |= 1 << voice->num;
++			voice = voice->timing;
++		}
++
++		/* voice could be NULL if this a recording stream, and it
++		 * doesn't have an external timing channel.
++		 */
++		if (voice)
++			play[voice->num / 32] |= 1 << (voice->num & 0x1f);
++
++		snd_pcm_trigger_done(s, substream);
++	}
++
++	if (starting) {
++		if (record)
++			outl(record, io + SIS_RECORD_START_REG);
++		if (play[0])
++			outl(play[0], io + SIS_PLAY_START_A_REG);
++		if (play[1])
++			outl(play[1], io + SIS_PLAY_START_B_REG);
++	} else {
++		if (record)
++			outl(record, io + SIS_RECORD_STOP_REG);
++		if (play[0])
++			outl(play[0], io + SIS_PLAY_STOP_A_REG);
++		if (play[1])
++			outl(play[1], io + SIS_PLAY_STOP_B_REG);
++	}
++	return 0;
++}
++
++static snd_pcm_uframes_t sis_pcm_pointer(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice = runtime->private_data;
++	u32 cso;
++
++	cso = readl(voice->ctrl_base + SIS_PLAY_DMA_FORMAT_CSO);
++	cso &= 0xffff;
++	return cso;
++}
++
++static int sis_capture_open(struct snd_pcm_substream *substream)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice = &sis->capture_voice;
++	unsigned long flags;
++
++	/* FIXME: The driver only supports recording from one channel
++	 * at the moment, but it could support more.
++	 */
++	spin_lock_irqsave(&sis->voice_lock, flags);
++	if (voice->flags & VOICE_IN_USE)
++		voice = NULL;
++	else
++		voice->flags |= VOICE_IN_USE;
++	spin_unlock_irqrestore(&sis->voice_lock, flags);
++
++	if (!voice)
++		return -EAGAIN;
++
++	voice->substream = substream;
++	runtime->private_data = voice;
++	runtime->hw = sis_capture_hw_info;
++	runtime->hw.rates = sis->ac97[0]->rates[AC97_RATES_ADC];
++	snd_pcm_limit_hw_rates(runtime);
++	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
++						9, 0xfff9);
++	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
++						9, 0xfff9);
++	snd_pcm_set_sync(substream);
++	return 0;
++}
++
++static int sis_capture_hw_params(struct snd_pcm_substream *substream,
++					struct snd_pcm_hw_params *hw_params)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	int rc;
++
++	rc = snd_ac97_set_rate(sis->ac97[0], AC97_PCM_LR_ADC_RATE,
++						params_rate(hw_params));
++	if (rc)
++		goto out;
++
++	rc = snd_pcm_lib_malloc_pages(substream,
++					params_buffer_bytes(hw_params));
++	if (rc < 0)
++		goto out;
++
++	rc = sis_alloc_timing_voice(substream, hw_params);
++
++out:
++	return rc;
++}
++
++static void sis_prepare_timing_voice(struct voice *voice,
++					struct snd_pcm_substream *substream)
++{
++	struct sis7019 *sis = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *timing = voice->timing;
++	void __iomem *play_base = timing->ctrl_base;
++	void __iomem *wave_base = timing->wave_base;
++	u16 buffer_size, period_size;
++	u32 format, control, sso_eso, delta;
++	u32 vperiod, sso, reg;
++
++	/* Set our initial buffer and period as large as we can given a
++	 * single page of silence.
++	 */
++	buffer_size = 4096 / runtime->channels;
++	buffer_size /= snd_pcm_format_size(runtime->format, 1);
++	period_size = buffer_size;
++
++	/* Initially, we want to interrupt just a bit behind the end of
++	 * the period we're clocking out. 10 samples seems to give a good
++	 * delay.
++	 *
++	 * We want to spread our interrupts throughout the virtual period,
++	 * so that we don't end up with two interrupts back to back at the
++	 * end -- this helps minimize the effects of any jitter. Adjust our
++	 * clocking period size so that the last period is at least a fourth
++	 * of a full period.
++	 *
++	 * This is all moot if we don't need to use virtual periods.
++	 */
++	vperiod = runtime->period_size + 10;
++	if (vperiod > period_size) {
++		u16 tail = vperiod % period_size;
++		u16 quarter_period = period_size / 4;
++
++		if (tail && tail < quarter_period) {
++			u16 loops = vperiod / period_size;
++
++			tail = quarter_period - tail;
++			tail += loops - 1;
++			tail /= loops;
++			period_size -= tail;
++		}
++
++		sso = period_size - 1;
++	} else {
++		/* The initial period will fit inside the buffer, so we
++		 * don't need to use virtual periods -- disable them.
++		 */
++		period_size = runtime->period_size;
++		sso = vperiod - 1;
++		vperiod = 0;
++	}
++
++	/* The interrupt handler implements the timing syncronization, so
++	 * setup its state.
++	 */
++	timing->flags |= VOICE_SYNC_TIMING;
++	timing->sync_base = voice->ctrl_base;
++	timing->sync_cso = runtime->period_size - 1;
++	timing->sync_period_size = runtime->period_size;
++	timing->sync_buffer_size = runtime->buffer_size;
++	timing->period_size = period_size;
++	timing->buffer_size = buffer_size;
++	timing->sso = sso;
++	timing->vperiod = vperiod;
++
++	/* Using unsigned samples with the all-zero silence buffer
++	 * forces the output to the lower rail, killing playback.
++	 * So ignore unsigned vs signed -- it doesn't change the timing.
++	 */
++	format = 0;
++	if (snd_pcm_format_width(runtime->format) == 8)
++		format = SIS_CAPTURE_DMA_FORMAT_8BIT;
++	if (runtime->channels == 1)
++		format |= SIS_CAPTURE_DMA_FORMAT_MONO;
++
++	control = timing->buffer_size - 1;
++	control |= SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_SSO;
++	sso_eso = timing->buffer_size - 1;
++	sso_eso |= timing->sso << 16;
++
++	delta = sis_rate_to_delta(runtime->rate);
++
++	/* We've done the math, now configure the channel.
++	 */
++	writel(format, play_base + SIS_PLAY_DMA_FORMAT_CSO);
++	writel(sis->silence_dma_addr, play_base + SIS_PLAY_DMA_BASE);
++	writel(control, play_base + SIS_PLAY_DMA_CONTROL);
++	writel(sso_eso, play_base + SIS_PLAY_DMA_SSO_ESO);
++
++	for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4)
++		writel(0, wave_base + reg);
++
++	writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL);
++	writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION);
++	writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE |
++			SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE |
++			SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE,
++			wave_base + SIS_WAVE_CHANNEL_CONTROL);
++}
++
++static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct voice *voice = runtime->private_data;
++	void __iomem *rec_base = voice->ctrl_base;
++	u32 format, dma_addr, control;
++	u16 leo;
++
++	/* We rely on the PCM core to ensure that the parameters for this
++	 * substream do not change on us while we're programming the HW.
++	 */
++	format = 0;
++	if (snd_pcm_format_width(runtime->format) == 8)
++		format = SIS_CAPTURE_DMA_FORMAT_8BIT;
++	if (!snd_pcm_format_signed(runtime->format))
++		format |= SIS_CAPTURE_DMA_FORMAT_UNSIGNED;
++	if (runtime->channels == 1)
++		format |= SIS_CAPTURE_DMA_FORMAT_MONO;
++
++	dma_addr = runtime->dma_addr;
++	leo = runtime->buffer_size - 1;
++	control = leo | SIS_CAPTURE_DMA_LOOP;
++
++	/* If we've got more than two periods per buffer, then we have
++	 * use a timing voice to clock out the periods. Otherwise, we can
++	 * use the capture channel's interrupts.
++	 */
++	if (voice->timing) {
++		sis_prepare_timing_voice(voice, substream);
++	} else {
++		control |= SIS_CAPTURE_DMA_INTR_AT_LEO;
++		if (runtime->period_size != runtime->buffer_size)
++			control |= SIS_CAPTURE_DMA_INTR_AT_MLP;
++	}
++
++	writel(format, rec_base + SIS_CAPTURE_DMA_FORMAT_CSO);
++	writel(dma_addr, rec_base + SIS_CAPTURE_DMA_BASE);
++	writel(control, rec_base + SIS_CAPTURE_DMA_CONTROL);
++
++	/* Force the writes to post. */
++	readl(rec_base);
++
++	return 0;
++}
++
++static struct snd_pcm_ops sis_playback_ops = {
++	.open = sis_playback_open,
++	.close = sis_substream_close,
++	.ioctl = snd_pcm_lib_ioctl,
++	.hw_params = sis_playback_hw_params,
++	.hw_free = sis_hw_free,
++	.prepare = sis_pcm_playback_prepare,
++	.trigger = sis_pcm_trigger,
++	.pointer = sis_pcm_pointer,
++};
++
++static struct snd_pcm_ops sis_capture_ops = {
++	.open = sis_capture_open,
++	.close = sis_substream_close,
++	.ioctl = snd_pcm_lib_ioctl,
++	.hw_params = sis_capture_hw_params,
++	.hw_free = sis_hw_free,
++	.prepare = sis_pcm_capture_prepare,
++	.trigger = sis_pcm_trigger,
++	.pointer = sis_pcm_pointer,
++};
++
++static int __devinit sis_pcm_create(struct sis7019 *sis)
++{
++	struct snd_pcm *pcm;
++	int rc;
++
++	/* We have 64 voices, and the driver currently records from
++	 * only one channel, though that could change in the future.
++	 */
++	rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm);
++	if (rc)
++		return rc;
++
++	pcm->private_data = sis;
++	strcpy(pcm->name, "SiS7019");
++	sis->pcm = pcm;
++
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops);
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &sis_capture_ops);
++
++	/* Try to preallocate some memory, but it's not the end of the
++	 * world if this fails.
++	 */
++	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
++				snd_dma_pci_data(sis->pci), 64*1024, 128*1024);
++
++	return 0;
++}
++
++static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd)
++{
++	unsigned long io = sis->ioport;
++	unsigned short val = 0xffff;
++	u16 status;
++	u16 rdy;
++	int count;
++	const static u16 codec_ready[3] = {
++		SIS_AC97_STATUS_CODEC_READY,
++		SIS_AC97_STATUS_CODEC2_READY,
++		SIS_AC97_STATUS_CODEC3_READY,
++	};
++
++	rdy = codec_ready[codec];
++
++
++	/* Get the AC97 semaphore -- software first, so we don't spin
++	 * pounding out IO reads on the hardware semaphore...
++	 */
++	mutex_lock(&sis->ac97_mutex);
++
++	count = 0xffff;
++	while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
++		udelay(1);
++
++	if (!count)
++		goto timeout;
++
++	/* ... and wait for any outstanding commands to complete ...
++	 */
++	count = 0xffff;
++	do {
++		status = inw(io + SIS_AC97_STATUS);
++		if ((status & rdy) && !(status & SIS_AC97_STATUS_BUSY))
++			break;
++
++		udelay(1);
++	} while (--count);
++
++	if (!count)
++		goto timeout_sema;
++
++	/* ... before sending our command and waiting for it to finish ...
++	 */
++	outl(cmd, io + SIS_AC97_CMD);
++	udelay(10);
++
++	count = 0xffff;
++	while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
++		udelay(1);
++
++	/* ... and reading the results (if any).
++	 */
++	val = inl(io + SIS_AC97_CMD) >> 16;
++
++timeout_sema:
++	outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
++timeout:
++	mutex_unlock(&sis->ac97_mutex);
++
++	if (!count) {
++		printk(KERN_ERR "sis7019: ac97 codec %d timeout cmd 0x%08x\n",
++					codec, cmd);
++	}
++
++	return val;
++}
++
++static void sis_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
++				unsigned short val)
++{
++	const static u32 cmd[3] = {
++		SIS_AC97_CMD_CODEC_WRITE,
++		SIS_AC97_CMD_CODEC2_WRITE,
++		SIS_AC97_CMD_CODEC3_WRITE,
++	};
++	sis_ac97_rw(ac97->private_data, ac97->num,
++			(val << 16) | (reg << 8) | cmd[ac97->num]);
++}
++
++static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
++{
++	const static u32 cmd[3] = {
++		SIS_AC97_CMD_CODEC_READ,
++		SIS_AC97_CMD_CODEC2_READ,
++		SIS_AC97_CMD_CODEC3_READ,
++	};
++	return sis_ac97_rw(ac97->private_data, ac97->num,
++					(reg << 8) | cmd[ac97->num]);
++}
++
++static int __devinit sis_mixer_create(struct sis7019 *sis)
++{
++	struct snd_ac97_bus *bus;
++	struct snd_ac97_template ac97;
++	static struct snd_ac97_bus_ops ops = {
++		.write = sis_ac97_write,
++		.read = sis_ac97_read,
++	};
++	int rc;
++
++	memset(&ac97, 0, sizeof(ac97));
++	ac97.private_data = sis;
++
++	rc = snd_ac97_bus(sis->card, 0, &ops, NULL, &bus);
++	if (!rc && sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
++		rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[0]);
++	ac97.num = 1;
++	if (!rc && (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT))
++		rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[1]);
++	ac97.num = 2;
++	if (!rc && (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT))
++		rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[2]);
++
++	/* If we return an error here, then snd_card_free() should
++	 * free up any ac97 codecs that got created, as well as the bus.
++	 */
++	return rc;
++}
++
++static void sis_free_suspend(struct sis7019 *sis)
++{
++	int i;
++
++	for (i = 0; i < SIS_SUSPEND_PAGES; i++)
++		kfree(sis->suspend_state[i]);
++}
++
++static int sis_chip_free(struct sis7019 *sis)
++{
++	/* Reset the chip, and disable all interrputs.
++	 */
++	outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR);
++	udelay(10);
++	outl(0, sis->ioport + SIS_GCR);
++	outl(0, sis->ioport + SIS_GIER);
++
++	/* Now, free everything we allocated.
++	 */
++	if (sis->irq >= 0)
++		free_irq(sis->irq, sis);
++
++	if (sis->ioaddr)
++		iounmap(sis->ioaddr);
++
++	pci_release_regions(sis->pci);
++	pci_disable_device(sis->pci);
++
++	sis_free_suspend(sis);
++	return 0;
++}
++
++static int sis_dev_free(struct snd_device *dev)
++{
++	struct sis7019 *sis = dev->device_data;
++	return sis_chip_free(sis);
++}
++
++static int sis_chip_init(struct sis7019 *sis)
++{
++	unsigned long io = sis->ioport;
++	void __iomem *ioaddr = sis->ioaddr;
++	u16 status;
++	int count;
++	int i;
++
++	/* Reset the audio controller
++	 */
++	outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR);
++	udelay(10);
++	outl(0, io + SIS_GCR);
++
++	/* Get the AC-link semaphore, and reset the codecs
++	 */
++	count = 0xffff;
++	while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count)
++		udelay(1);
++
++	if (!count)
++		return -EIO;
++
++	outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD);
++	udelay(10);
++
++	count = 0xffff;
++	while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
++		udelay(1);
++
++	/* Now that we've finished the reset, find out what's attached.
++	 */
++	status = inl(io + SIS_AC97_STATUS);
++	if (status & SIS_AC97_STATUS_CODEC_READY)
++		sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT;
++	if (status & SIS_AC97_STATUS_CODEC2_READY)
++		sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT;
++	if (status & SIS_AC97_STATUS_CODEC3_READY)
++		sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT;
++
++	/* All done, let go of the semaphore, and check for errors
++	 */
++	outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA);
++	if (!sis->codecs_present || !count)
++		return -EIO;
++
++	/* Let the hardware know that the audio driver is alive,
++	 * and enable PCM slots on the AC-link for L/R playback (3 & 4) and
++	 * record channels. We're going to want to use Variable Rate Audio
++	 * for recording, to avoid needlessly resampling from 48kHZ.
++	 */
++	outl(SIS_AC97_CONF_AUDIO_ALIVE, io + SIS_AC97_CONF);
++	outl(SIS_AC97_CONF_AUDIO_ALIVE | SIS_AC97_CONF_PCM_LR_ENABLE |
++		SIS_AC97_CONF_PCM_CAP_MIC_ENABLE |
++		SIS_AC97_CONF_PCM_CAP_LR_ENABLE |
++		SIS_AC97_CONF_CODEC_VRA_ENABLE, io + SIS_AC97_CONF);
++
++	/* All AC97 PCM slots should be sourced from sub-mixer 0.
++	 */
++	outl(0, io + SIS_AC97_PSR);
++
++	/* There is only one valid DMA setup for a PCI environment.
++	 */
++	outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR);
++
++	/* Reset the syncronization groups for all of the channels
++	 * to be asyncronous. If we start doing SPDIF or 5.1 sound, etc.
++	 * we'll need to change how we handle these. Until then, we just
++	 * assign sub-mixer 0 to all playback channels, and avoid any
++	 * attenuation on the audio.
++	 */
++	outl(0, io + SIS_PLAY_SYNC_GROUP_A);
++	outl(0, io + SIS_PLAY_SYNC_GROUP_B);
++	outl(0, io + SIS_PLAY_SYNC_GROUP_C);
++	outl(0, io + SIS_PLAY_SYNC_GROUP_D);
++	outl(0, io + SIS_MIXER_SYNC_GROUP);
++
++	for (i = 0; i < 64; i++) {
++		writel(i, SIS_MIXER_START_ADDR(ioaddr, i));
++		writel(SIS_MIXER_RIGHT_NO_ATTEN | SIS_MIXER_LEFT_NO_ATTEN |
++				SIS_MIXER_DEST_0, SIS_MIXER_ADDR(ioaddr, i));
++	}
++
++	/* Don't attenuate any audio set for the wave amplifier.
++	 *
++	 * FIXME: Maximum attenuation is set for the music amp, which will
++	 * need to change if we start using the synth engine.
++	 */
++	outl(0xffff0000, io + SIS_WEVCR);
++
++	/* Ensure that the wave engine is in normal operating mode.
++	 */
++	outl(0, io + SIS_WECCR);
++
++	/* Go ahead and enable the DMA interrupts. They won't go live
++	 * until we start a channel.
++	 */
++	outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE |
++		SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER);
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int sis_suspend(struct pci_dev *pci, pm_message_t state)
++{
++	struct snd_card *card = pci_get_drvdata(pci);
++	struct sis7019 *sis = card->private_data;
++	void __iomem *ioaddr = sis->ioaddr;
++	int i;
++
++	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
++	snd_pcm_suspend_all(sis->pcm);
++	if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
++		snd_ac97_suspend(sis->ac97[0]);
++	if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
++		snd_ac97_suspend(sis->ac97[1]);
++	if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
++		snd_ac97_suspend(sis->ac97[2]);
++
++	/* snd_pcm_suspend_all() stopped all channels, so we're quiescent.
++	 */
++	if (sis->irq >= 0) {
++		synchronize_irq(sis->irq);
++		free_irq(sis->irq, sis);
++		sis->irq = -1;
++	}
++
++	/* Save the internal state away
++	 */
++	for (i = 0; i < 4; i++) {
++		memcpy_fromio(sis->suspend_state[i], ioaddr, 4096);
++		ioaddr += 4096;
++	}
++
++	pci_disable_device(pci);
++	pci_save_state(pci);
++	pci_set_power_state(pci, pci_choose_state(pci, state));
++	return 0;
++}
++
++static int sis_resume(struct pci_dev *pci)
++{
++	struct snd_card *card = pci_get_drvdata(pci);
++	struct sis7019 *sis = card->private_data;
++	void __iomem *ioaddr = sis->ioaddr;
++	int i;
++
++	pci_set_power_state(pci, PCI_D0);
++	pci_restore_state(pci);
++
++	if (pci_enable_device(pci) < 0) {
++		printk(KERN_ERR "sis7019: unable to re-enable device\n");
++		goto error;
++	}
++
++	if (sis_chip_init(sis)) {
++		printk(KERN_ERR "sis7019: unable to re-init controller\n");
++		goto error;
++	}
++
++	if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
++				card->shortname, sis)) {
++		printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq);
++		goto error;
++	}
++
++	/* Restore saved state, then clear out the page we use for the
++	 * silence buffer.
++	 */
++	for (i = 0; i < 4; i++) {
++		memcpy_toio(ioaddr, sis->suspend_state[i], 4096);
++		ioaddr += 4096;
++	}
++
++	memset(sis->suspend_state[0], 0, 4096);
++
++	sis->irq = pci->irq;
++	pci_set_master(pci);
++
++	if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT)
++		snd_ac97_resume(sis->ac97[0]);
++	if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)
++		snd_ac97_resume(sis->ac97[1]);
++	if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)
++		snd_ac97_resume(sis->ac97[2]);
++
++	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
++	return 0;
++
++error:
++	snd_card_disconnect(card);
++	return -EIO;
++}
++#endif /* CONFIG_PM */
++
++static int sis_alloc_suspend(struct sis7019 *sis)
++{
++	int i;
++
++	/* We need 16K to store the internal wave engine state during a
++	 * suspend, but we don't need it to be contiguous, so play nice
++	 * with the memory system. We'll also use this area for a silence
++	 * buffer.
++	 */
++	for (i = 0; i < SIS_SUSPEND_PAGES; i++) {
++		sis->suspend_state[i] = kmalloc(4096, GFP_KERNEL);
++		if (!sis->suspend_state[i])
++			return -ENOMEM;
++	}
++	memset(sis->suspend_state[0], 0, 4096);
++
++	return 0;
++}
++
++static int __devinit sis_chip_create(struct snd_card *card,
++					struct pci_dev *pci)
++{
++	struct sis7019 *sis = card->private_data;
++	struct voice *voice;
++	static struct snd_device_ops ops = {
++		.dev_free = sis_dev_free,
++	};
++	int rc;
++	int i;
++
++	rc = pci_enable_device(pci);
++	if (rc)
++		goto error_out;
++
++	if (pci_set_dma_mask(pci, DMA_30BIT_MASK) < 0) {
++		printk(KERN_ERR "sis7019: architecture does not support "
++					"30-bit PCI busmaster DMA");
++		goto error_out_enabled;
++	}
++
++	memset(sis, 0, sizeof(*sis));
++	mutex_init(&sis->ac97_mutex);
++	spin_lock_init(&sis->voice_lock);
++	sis->card = card;
++	sis->pci = pci;
++	sis->irq = -1;
++	sis->ioport = pci_resource_start(pci, 0);
++
++	rc = pci_request_regions(pci, "SiS7019");
++	if (rc) {
++		printk(KERN_ERR "sis7019: unable request regions\n");
++		goto error_out_enabled;
++	}
++
++	rc = -EIO;
++	sis->ioaddr = ioremap_nocache(pci_resource_start(pci, 1), 0x4000);
++	if (!sis->ioaddr) {
++		printk(KERN_ERR "sis7019: unable to remap MMIO, aborting\n");
++		goto error_out_cleanup;
++	}
++
++	rc = sis_alloc_suspend(sis);
++	if (rc < 0) {
++		printk(KERN_ERR "sis7019: unable to allocate state storage\n");
++		goto error_out_cleanup;
++	}
++
++	rc = sis_chip_init(sis);
++	if (rc)
++		goto error_out_cleanup;
++
++	if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
++				card->shortname, sis)) {
++		printk(KERN_ERR "unable to allocate irq %d\n", sis->irq);
++		goto error_out_cleanup;
++	}
++
++	sis->irq = pci->irq;
++	pci_set_master(pci);
++
++	for (i = 0; i < 64; i++) {
++		voice = &sis->voices[i];
++		voice->num = i;
++		voice->ctrl_base = SIS_PLAY_DMA_ADDR(sis->ioaddr, i);
++		voice->wave_base = SIS_WAVE_ADDR(sis->ioaddr, i);
++	}
++
++	voice = &sis->capture_voice;
++	voice->flags = VOICE_CAPTURE;
++	voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN;
++	voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num);
++
++	rc = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sis, &ops);
++	if (rc)
++		goto error_out_cleanup;
++
++	snd_card_set_dev(card, &pci->dev);
++
++	return 0;
++
++error_out_cleanup:
++	sis_chip_free(sis);
++
++error_out_enabled:
++	pci_disable_device(pci);
++
++error_out:
++	return rc;
++}
++
++static int __devinit snd_sis7019_probe(struct pci_dev *pci,
++					const struct pci_device_id *pci_id)
++{
++	struct snd_card *card;
++	struct sis7019 *sis;
++	int rc;
++
++	rc = -ENOENT;
++	if (!enable)
++		goto error_out;
++
++	rc = -ENOMEM;
++	card = snd_card_new(index, id, THIS_MODULE, sizeof(*sis));
++	if (!card)
++		goto error_out;
++
++	strcpy(card->driver, "SiS7019");
++	strcpy(card->shortname, "SiS7019");
++	rc = sis_chip_create(card, pci);
++	if (rc)
++		goto card_error_out;
++
++	sis = card->private_data;
++
++	rc = sis_mixer_create(sis);
++	if (rc)
++		goto card_error_out;
++
++	rc = sis_pcm_create(sis);
++	if (rc)
++		goto card_error_out;
++
++	snprintf(card->longname, sizeof(card->longname),
++			"%s Audio Accelerator with %s at 0x%lx, irq %d",
++			card->shortname, snd_ac97_get_short_name(sis->ac97[0]),
++			sis->ioport, sis->irq);
++
++	rc = snd_card_register(card);
++	if (rc)
++		goto card_error_out;
++
++	pci_set_drvdata(pci, card);
++	return 0;
++
++card_error_out:
++	snd_card_free(card);
++
++error_out:
++	return rc;
++}
++
++static void __devexit snd_sis7019_remove(struct pci_dev *pci)
++{
++	snd_card_free(pci_get_drvdata(pci));
++	pci_set_drvdata(pci, NULL);
++}
++
++static struct pci_driver sis7019_driver = {
++	.name = "SiS7019",
++	.id_table = snd_sis7019_ids,
++	.probe = snd_sis7019_probe,
++	.remove = __devexit_p(snd_sis7019_remove),
++
++#ifdef CONFIG_PM
++	.suspend = sis_suspend,
++	.resume = sis_resume,
++#endif
++};
++
++static int __init sis7019_init(void)
++{
++	return pci_register_driver(&sis7019_driver);
++}
++
++static void __exit sis7019_exit(void)
++{
++	pci_unregister_driver(&sis7019_driver);
++}
++
++module_init(sis7019_init);
++module_exit(sis7019_exit);
+diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h
+new file mode 100644
+index 0000000..013b673
+--- /dev/null
++++ b/sound/pci/sis7019.h
+@@ -0,0 +1,342 @@
++#ifndef __sis7019_h__
++#define __sis7019_h__
++
++/*
++ *  Definitions for SiS7019 Audio Accelerator
++ *
++ *  Copyright (C) 2004-2007, David Dillow
++ *  Written by David Dillow <dave at thedillows.org>
++ *  Inspired by the Trident 4D-WaveDX/NX driver.
++ *
++ *  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 as published by
++ *  the Free Software Foundation, version 2.
++ *
++ *  This program 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
++ */
++
++
++/* General Control Register */
++#define SIS_GCR		0x00
++#define		SIS_GCR_MACRO_POWER_DOWN		0x80000000
++#define		SIS_GCR_MODEM_ENABLE			0x00010000
++#define		SIS_GCR_SOFTWARE_RESET			0x00000001
++
++/* General Interrupt Enable Register */
++#define SIS_GIER	0x04
++#define		SIS_GIER_MODEM_TIMER_IRQ_ENABLE		0x00100000
++#define		SIS_GIER_MODEM_RX_DMA_IRQ_ENABLE	0x00080000
++#define		SIS_GIER_MODEM_TX_DMA_IRQ_ENABLE	0x00040000
++#define		SIS_GIER_AC97_GPIO1_IRQ_ENABLE		0x00020000
++#define		SIS_GIER_AC97_GPIO0_IRQ_ENABLE		0x00010000
++#define		SIS_GIER_AC97_SAMPLE_TIMER_IRQ_ENABLE	0x00000010
++#define		SIS_GIER_AUDIO_GLOBAL_TIMER_IRQ_ENABLE	0x00000008
++#define		SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE	0x00000004
++#define		SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE	0x00000002
++#define		SIS_GIER_AUDIO_WAVE_ENGINE_IRQ_ENABLE	0x00000001
++
++/* General Interrupt Status Register */
++#define SIS_GISR	0x08
++#define		SIS_GISR_MODEM_TIMER_IRQ_STATUS		0x00100000
++#define		SIS_GISR_MODEM_RX_DMA_IRQ_STATUS	0x00080000
++#define		SIS_GISR_MODEM_TX_DMA_IRQ_STATUS	0x00040000
++#define		SIS_GISR_AC97_GPIO1_IRQ_STATUS		0x00020000
++#define		SIS_GISR_AC97_GPIO0_IRQ_STATUS		0x00010000
++#define		SIS_GISR_AC97_SAMPLE_TIMER_IRQ_STATUS	0x00000010
++#define		SIS_GISR_AUDIO_GLOBAL_TIMER_IRQ_STATUS	0x00000008
++#define		SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS	0x00000004
++#define		SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS	0x00000002
++#define		SIS_GISR_AUDIO_WAVE_ENGINE_IRQ_STATUS	0x00000001
++
++/* DMA Control Register */
++#define SIS_DMA_CSR	0x10
++#define		SIS_DMA_CSR_PCI_SETTINGS		0x0000001d
++#define		SIS_DMA_CSR_CONCURRENT_ENABLE		0x00000200
++#define		SIS_DMA_CSR_PIPELINE_ENABLE		0x00000100
++#define		SIS_DMA_CSR_RX_DRAIN_ENABLE		0x00000010
++#define		SIS_DMA_CSR_RX_FILL_ENABLE		0x00000008
++#define		SIS_DMA_CSR_TX_DRAIN_ENABLE		0x00000004
++#define		SIS_DMA_CSR_TX_LOWPRI_FILL_ENABLE	0x00000002
++#define		SIS_DMA_CSR_TX_HIPRI_FILL_ENABLE	0x00000001
++
++/* Playback Channel Start Registers */
++#define SIS_PLAY_START_A_REG	0x14
++#define SIS_PLAY_START_B_REG	0x18
++
++/* Playback Channel Stop Registers */
++#define SIS_PLAY_STOP_A_REG	0x1c
++#define SIS_PLAY_STOP_B_REG	0x20
++
++/* Recording Channel Start Register */
++#define SIS_RECORD_START_REG	0x24
++
++/* Recording Channel Stop Register */
++#define SIS_RECORD_STOP_REG	0x28
++
++/* Playback Interrupt Status Registers */
++#define SIS_PISR_A	0x2c
++#define SIS_PISR_B	0x30
++
++/* Recording Interrupt Status Register */
++#define SIS_RISR	0x34
++
++/* AC97 AC-link Playback Source Register */
++#define SIS_AC97_PSR	0x40
++#define		SIS_AC97_PSR_MODEM_HEADSET_SRC_MIXER	0x0f000000
++#define		SIS_AC97_PSR_MODEM_LINE2_SRC_MIXER	0x00f00000
++#define		SIS_AC97_PSR_MODEM_LINE1_SRC_MIXER	0x000f0000
++#define		SIS_AC97_PSR_PCM_LFR_SRC_MIXER		0x0000f000
++#define		SIS_AC97_PSR_PCM_SURROUND_SRC_MIXER	0x00000f00
++#define		SIS_AC97_PSR_PCM_CENTER_SRC_MIXER	0x000000f0
++#define		SIS_AC97_PSR_PCM_LR_SRC_MIXER		0x0000000f
++
++/* AC97 AC-link Command Register */
++#define SIS_AC97_CMD	0x50
++#define 	SIS_AC97_CMD_DATA_MASK			0xffff0000
++#define		SIS_AC97_CMD_REG_MASK			0x0000ff00
++#define		SIS_AC97_CMD_CODEC3_READ		0x0000000d
++#define		SIS_AC97_CMD_CODEC3_WRITE		0x0000000c
++#define		SIS_AC97_CMD_CODEC2_READ		0x0000000b
++#define		SIS_AC97_CMD_CODEC2_WRITE		0x0000000a
++#define		SIS_AC97_CMD_CODEC_READ			0x00000009
++#define		SIS_AC97_CMD_CODEC_WRITE		0x00000008
++#define		SIS_AC97_CMD_CODEC_WARM_RESET		0x00000005
++#define		SIS_AC97_CMD_CODEC_COLD_RESET		0x00000004
++#define		SIS_AC97_CMD_DONE			0x00000000
++
++/* AC97 AC-link Semaphore Register */
++#define SIS_AC97_SEMA	0x54
++#define		SIS_AC97_SEMA_BUSY			0x00000001
++#define		SIS_AC97_SEMA_RELEASE			0x00000000
++
++/* AC97 AC-link Status Register */
++#define SIS_AC97_STATUS	0x58
++#define		SIS_AC97_STATUS_AUDIO_D2_INACT_SECS	0x03f00000
++#define		SIS_AC97_STATUS_MODEM_ALIVE		0x00002000
++#define		SIS_AC97_STATUS_AUDIO_ALIVE		0x00001000
++#define		SIS_AC97_STATUS_CODEC3_READY		0x00000400
++#define		SIS_AC97_STATUS_CODEC2_READY		0x00000200
++#define		SIS_AC97_STATUS_CODEC_READY		0x00000100
++#define		SIS_AC97_STATUS_WARM_RESET		0x00000080
++#define		SIS_AC97_STATUS_COLD_RESET		0x00000040
++#define		SIS_AC97_STATUS_POWERED_DOWN		0x00000020
++#define		SIS_AC97_STATUS_NORMAL			0x00000010
++#define		SIS_AC97_STATUS_READ_EXPIRED		0x00000004
++#define		SIS_AC97_STATUS_SEMAPHORE		0x00000002
++#define		SIS_AC97_STATUS_BUSY			0x00000001
++
++/* AC97 AC-link Audio Configuration Register */
++#define SIS_AC97_CONF	0x5c
++#define		SIS_AC97_CONF_AUDIO_ALIVE		0x80000000
++#define		SIS_AC97_CONF_WARM_RESET_ENABLE		0x40000000
++#define		SIS_AC97_CONF_PR6_ENABLE		0x20000000
++#define		SIS_AC97_CONF_PR5_ENABLE		0x10000000
++#define		SIS_AC97_CONF_PR4_ENABLE		0x08000000
++#define		SIS_AC97_CONF_PR3_ENABLE		0x04000000
++#define		SIS_AC97_CONF_PR2_PR7_ENABLE		0x02000000
++#define		SIS_AC97_CONF_PR0_PR1_ENABLE		0x01000000
++#define		SIS_AC97_CONF_AUTO_PM_ENABLE		0x00800000
++#define		SIS_AC97_CONF_PCM_LFE_ENABLE		0x00080000
++#define		SIS_AC97_CONF_PCM_SURROUND_ENABLE	0x00040000
++#define		SIS_AC97_CONF_PCM_CENTER_ENABLE		0x00020000
++#define		SIS_AC97_CONF_PCM_LR_ENABLE		0x00010000
++#define		SIS_AC97_CONF_PCM_CAP_MIC_ENABLE	0x00002000
++#define		SIS_AC97_CONF_PCM_CAP_LR_ENABLE		0x00001000
++#define		SIS_AC97_CONF_PCM_CAP_MIC_FROM_CODEC3	0x00000200
++#define		SIS_AC97_CONF_PCM_CAP_LR_FROM_CODEC3	0x00000100
++#define		SIS_AC97_CONF_CODEC3_PM_VRM		0x00000080
++#define		SIS_AC97_CONF_CODEC_PM_VRM		0x00000040
++#define		SIS_AC97_CONF_CODEC3_VRA_ENABLE		0x00000020
++#define		SIS_AC97_CONF_CODEC_VRA_ENABLE		0x00000010
++#define		SIS_AC97_CONF_CODEC3_PM_EAC		0x00000008
++#define		SIS_AC97_CONF_CODEC_PM_EAC		0x00000004
++#define		SIS_AC97_CONF_CODEC3_EXISTS		0x00000002
++#define		SIS_AC97_CONF_CODEC_EXISTS		0x00000001
++
++/* Playback Channel Sync Group registers */
++#define SIS_PLAY_SYNC_GROUP_A	0x80
++#define SIS_PLAY_SYNC_GROUP_B	0x84
++#define SIS_PLAY_SYNC_GROUP_C	0x88
++#define SIS_PLAY_SYNC_GROUP_D	0x8c
++#define SIS_MIXER_SYNC_GROUP	0x90
++
++/* Wave Engine Config and Control Register */
++#define SIS_WECCR	0xa0
++#define		SIS_WECCR_TESTMODE_MASK			0x00300000
++#define			SIS_WECCR_TESTMODE_NORMAL		0x00000000
++#define			SIS_WECCR_TESTMODE_BYPASS_NSO_ALPHA	0x00100000
++#define			SIS_WECCR_TESTMODE_BYPASS_FC		0x00200000
++#define			SIS_WECCR_TESTMODE_BYPASS_WOL		0x00300000
++#define		SIS_WECCR_RESONANCE_DELAY_MASK		0x00060000
++#define			SIS_WECCR_RESONANCE_DELAY_NONE		0x00000000
++#define			SIS_WECCR_RESONANCE_DELAY_FC_1F00	0x00020000
++#define			SIS_WECCR_RESONANCE_DELAY_FC_1E00	0x00040000
++#define			SIS_WECCR_RESONANCE_DELAY_FC_1C00	0x00060000
++#define		SIS_WECCR_IGNORE_CHANNEL_PARMS		0x00010000
++#define		SIS_WECCR_COMMAND_CHANNEL_ID_MASK	0x0003ff00
++#define		SIS_WECCR_COMMAND_MASK			0x00000007
++#define			SIS_WECCR_COMMAND_NONE			0x00000000
++#define			SIS_WECCR_COMMAND_DONE			0x00000000
++#define			SIS_WECCR_COMMAND_PAUSE			0x00000001
++#define			SIS_WECCR_COMMAND_TOGGLE_VEG		0x00000002
++#define			SIS_WECCR_COMMAND_TOGGLE_MEG		0x00000003
++#define			SIS_WECCR_COMMAND_TOGGLE_VEG_MEG	0x00000004
++
++/* Wave Engine Volume Control Register */
++#define SIS_WEVCR	0xa4
++#define		SIS_WEVCR_LEFT_MUSIC_ATTENUATION_MASK	0xff000000
++#define		SIS_WEVCR_RIGHT_MUSIC_ATTENUATION_MASK	0x00ff0000
++#define		SIS_WEVCR_LEFT_WAVE_ATTENUATION_MASK	0x0000ff00
++#define		SIS_WEVCR_RIGHT_WAVE_ATTENUATION_MASK	0x000000ff
++
++/* Wave Engine Interrupt Status Registers */
++#define SIS_WEISR_A	0xa8
++#define SIS_WEISR_B	0xac
++
++
++/* Playback DMA parameters (paramter RAM) */
++#define SIS_PLAY_DMA_OFFSET	0x0000
++#define SIS_PLAY_DMA_SIZE	0x10
++#define SIS_PLAY_DMA_ADDR(addr, num) \
++	((num * SIS_PLAY_DMA_SIZE) + (addr) + SIS_PLAY_DMA_OFFSET)
++
++#define SIS_PLAY_DMA_FORMAT_CSO	0x00
++#define		SIS_PLAY_DMA_FORMAT_UNSIGNED	0x00080000
++#define		SIS_PLAY_DMA_FORMAT_8BIT	0x00040000
++#define		SIS_PLAY_DMA_FORMAT_MONO	0x00020000
++#define		SIS_PLAY_DMA_CSO_MASK		0x0000ffff
++#define SIS_PLAY_DMA_BASE	0x04
++#define SIS_PLAY_DMA_CONTROL	0x08
++#define		SIS_PLAY_DMA_STOP_AT_SSO	0x04000000
++#define		SIS_PLAY_DMA_RELEASE		0x02000000
++#define		SIS_PLAY_DMA_LOOP		0x01000000
++#define		SIS_PLAY_DMA_INTR_AT_SSO	0x00080000
++#define		SIS_PLAY_DMA_INTR_AT_ESO	0x00040000
++#define		SIS_PLAY_DMA_INTR_AT_LEO	0x00020000
++#define		SIS_PLAY_DMA_INTR_AT_MLP	0x00010000
++#define		SIS_PLAY_DMA_LEO_MASK		0x0000ffff
++#define SIS_PLAY_DMA_SSO_ESO	0x0c
++#define		SIS_PLAY_DMA_SSO_MASK		0xffff0000
++#define		SIS_PLAY_DMA_ESO_MASK		0x0000ffff
++
++/* Capture DMA parameters (paramter RAM) */
++#define SIS_CAPTURE_DMA_OFFSET	0x0800
++#define SIS_CAPTURE_DMA_SIZE	0x10
++#define SIS_CAPTURE_DMA_ADDR(addr, num) \
++	((num * SIS_CAPTURE_DMA_SIZE) + (addr) + SIS_CAPTURE_DMA_OFFSET)
++
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_0	0
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_1	1
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_2	2
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_3	3
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_4	4
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_5	5
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_6	6
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_7	7
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_8	8
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_9	9
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_10	10
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_11	11
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_12	12
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_13	13
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_14	14
++#define	SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_15	15
++#define	SIS_CAPTURE_CHAN_AC97_PCM_IN		16
++#define	SIS_CAPTURE_CHAN_AC97_MIC_IN		17
++#define	SIS_CAPTURE_CHAN_AC97_LINE1_IN		18
++#define	SIS_CAPTURE_CHAN_AC97_LINE2_IN		19
++#define	SIS_CAPTURE_CHAN_AC97_HANDSE_IN		20
++
++#define SIS_CAPTURE_DMA_FORMAT_CSO	0x00
++#define		SIS_CAPTURE_DMA_MONO_MODE_MASK	0xc0000000
++#define		SIS_CAPTURE_DMA_MONO_MODE_AVG	0x00000000
++#define		SIS_CAPTURE_DMA_MONO_MODE_LEFT	0x40000000
++#define		SIS_CAPTURE_DMA_MONO_MODE_RIGHT	0x80000000
++#define		SIS_CAPTURE_DMA_FORMAT_UNSIGNED	0x00080000
++#define		SIS_CAPTURE_DMA_FORMAT_8BIT	0x00040000
++#define		SIS_CAPTURE_DMA_FORMAT_MONO	0x00020000
++#define		SIS_CAPTURE_DMA_CSO_MASK		0x0000ffff
++#define SIS_CAPTURE_DMA_BASE		0x04
++#define SIS_CAPTURE_DMA_CONTROL		0x08
++#define		SIS_CAPTURE_DMA_STOP_AT_SSO	0x04000000
++#define		SIS_CAPTURE_DMA_RELEASE		0x02000000
++#define		SIS_CAPTURE_DMA_LOOP		0x01000000
++#define		SIS_CAPTURE_DMA_INTR_AT_LEO	0x00020000
++#define		SIS_CAPTURE_DMA_INTR_AT_MLP	0x00010000
++#define		SIS_CAPTURE_DMA_LEO_MASK		0x0000ffff
++#define SIS_CAPTURE_DMA_RESERVED	0x0c
++
++
++/* Mixer routing list start pointer (parameter RAM) */
++#define SIS_MIXER_START_OFFSET	0x1000
++#define SIS_MIXER_START_SIZE	0x04
++#define SIS_MIXER_START_ADDR(addr, num) \
++	((num * SIS_MIXER_START_SIZE) + (addr) + SIS_MIXER_START_OFFSET)
++
++#define SIS_MIXER_START_MASK	0x0000007f
++
++/* Mixer routing table (parameter RAM) */
++#define SIS_MIXER_OFFSET	0x1400
++#define SIS_MIXER_SIZE		0x04
++#define SIS_MIXER_ADDR(addr, num) \
++	((num * SIS_MIXER_SIZE) + (addr) + SIS_MIXER_OFFSET)
++
++#define SIS_MIXER_RIGHT_ATTENUTATION_MASK	0xff000000
++#define 	SIS_MIXER_RIGHT_NO_ATTEN		0xff000000
++#define SIS_MIXER_LEFT_ATTENUTATION_MASK	0x00ff0000
++#define 	SIS_MIXER_LEFT_NO_ATTEN			0x00ff0000
++#define SIS_MIXER_NEXT_ENTRY_MASK		0x00007f00
++#define 	SIS_MIXER_NEXT_ENTRY_NONE		0x00000000
++#define SIS_MIXER_DEST_MASK			0x0000007f
++#define 	SIS_MIXER_DEST_0			0x00000020
++#define 	SIS_MIXER_DEST_1			0x00000021
++#define 	SIS_MIXER_DEST_2			0x00000022
++#define 	SIS_MIXER_DEST_3			0x00000023
++#define 	SIS_MIXER_DEST_4			0x00000024
++#define 	SIS_MIXER_DEST_5			0x00000025
++#define 	SIS_MIXER_DEST_6			0x00000026
++#define 	SIS_MIXER_DEST_7			0x00000027
++#define 	SIS_MIXER_DEST_8			0x00000028
++#define 	SIS_MIXER_DEST_9			0x00000029
++#define 	SIS_MIXER_DEST_10			0x0000002a
++#define 	SIS_MIXER_DEST_11			0x0000002b
++#define 	SIS_MIXER_DEST_12			0x0000002c
++#define 	SIS_MIXER_DEST_13			0x0000002d
++#define 	SIS_MIXER_DEST_14			0x0000002e
++#define 	SIS_MIXER_DEST_15			0x0000002f
++
++/* Wave Engine Control Parameters (parameter RAM) */
++#define SIS_WAVE_OFFSET		0x2000
++#define SIS_WAVE_SIZE		0x40
++#define SIS_WAVE_ADDR(addr, num) \
++	((num * SIS_WAVE_SIZE) + (addr) + SIS_WAVE_OFFSET)
++
++#define SIS_WAVE_GENERAL		0x00
++#define		SIS_WAVE_GENERAL_WAVE_VOLUME			0x80000000
++#define		SIS_WAVE_GENERAL_MUSIC_VOLUME			0x00000000
++#define		SIS_WAVE_GENERAL_VOLUME_MASK			0x7f000000
++#define SIS_WAVE_GENERAL_ARTICULATION	0x04
++#define		SIS_WAVE_GENERAL_ARTICULATION_DELTA_MASK	0x3fff0000
++#define SIS_WAVE_ARTICULATION		0x08
++#define SIS_WAVE_TIMER			0x0c
++#define SIS_WAVE_GENERATOR		0x10
++#define SIS_WAVE_CHANNEL_CONTROL	0x14
++#define		SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE		0x80000000
++#define		SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE		0x40000000
++#define		SIS_WAVE_CHANNEL_CONTROL_FILTER_ENABLE		0x20000000
++#define		SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE	0x10000000
++#define SIS_WAVE_LFO_EG_CONTROL		0x18
++#define SIS_WAVE_LFO_EG_CONTROL_2	0x1c
++#define SIS_WAVE_LFO_EG_CONTROL_3	0x20
++#define SIS_WAVE_LFO_EG_CONTROL_4	0x24
++
++#endif /* __sis7019_h__ */
+diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
+index 44a7f5f..0d3d305 100644
+--- a/sound/pci/sonicvibes.c
++++ b/sound/pci/sonicvibes.c
+@@ -22,7 +22,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile
+index 65f2c21..88676b5 100644
+--- a/sound/pci/trident/Makefile
++++ b/sound/pci/trident/Makefile
+@@ -4,16 +4,6 @@
+ #
+ 
+ snd-trident-objs := trident.o trident_main.o trident_memory.o
+-snd-trident-synth-objs := trident_synth.o
+-
+-#
+-# this function returns:
+-#   "m" - CONFIG_SND_SEQUENCER is m
+-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
+-#   otherwise parameter #1 value
+-#
+-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+ 
+ # Toplevel Module Dependency
+ obj-$(CONFIG_SND_TRIDENT) += snd-trident.o
+-obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o
+diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
+index 8488456..d94b16f 100644
+--- a/sound/pci/trident/trident.c
++++ b/sound/pci/trident/trident.c
+@@ -21,7 +21,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/time.h>
+@@ -155,13 +154,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
+ 		return err;
+ 	}
+ 
+-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+-	if ((err = snd_trident_attach_synthesizer(trident)) < 0) {
+-		snd_card_free(card);
+-		return err;
+-	}
+-#endif
+-
+ 	snd_trident_create_gameport(trident);
+ 
+ 	if ((err = snd_card_register(card)) < 0) {
+diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
+index a235e03..71138ff 100644
+--- a/sound/pci/trident/trident_main.c
++++ b/sound/pci/trident/trident_main.c
+@@ -27,7 +27,6 @@
+  *  SiS7018 S/PDIF support by Thomas Winischhofer <thomas at winischhofer.net>
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -436,7 +435,7 @@ static void snd_trident_free_synth_channel(struct snd_trident *trident, int chan
+    Description: This routine will complete and write the 5 hardware channel
+                 registers to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 Each register field.
+   
+@@ -514,7 +513,7 @@ EXPORT_SYMBOL(snd_trident_write_voice_regs);
+    Description: This routine will write the new CSO offset
+                 register to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 CSO - new CSO value
+   
+@@ -540,7 +539,7 @@ static void snd_trident_write_cso_reg(struct snd_trident * trident,
+    Description: This routine will write the new ESO offset
+                 register to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 ESO - new ESO value
+   
+@@ -566,7 +565,7 @@ static void snd_trident_write_eso_reg(struct snd_trident * trident,
+    Description: This routine will write the new voice volume
+                 register to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 Vol - new voice volume
+   
+@@ -597,7 +596,7 @@ static void snd_trident_write_vol_reg(struct snd_trident * trident,
+    Description: This routine will write the new voice pan
+                 register to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 Pan - new pan value
+   
+@@ -619,7 +618,7 @@ static void snd_trident_write_pan_reg(struct snd_trident * trident,
+    Description: This routine will write the new reverb volume
+                 register to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 RVol - new reverb volume
+   
+@@ -643,7 +642,7 @@ static void snd_trident_write_rvol_reg(struct snd_trident * trident,
+    Description: This routine will write the new chorus volume
+                 register to hardware.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 voice - synthesizer voice structure
+                 CVol - new chorus volume
+   
+@@ -666,7 +665,7 @@ static void snd_trident_write_cvol_reg(struct snd_trident * trident,
+ 
+    Description: This routine converts rate in HZ to hardware delta value.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 rate - Real or Virtual channel number.
+   
+    Returns:     Delta value.
+@@ -696,7 +695,7 @@ static unsigned int snd_trident_convert_rate(unsigned int rate)
+ 
+    Description: This routine converts rate in HZ to hardware delta value.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 rate - Real or Virtual channel number.
+   
+    Returns:     Delta value.
+@@ -726,7 +725,7 @@ static unsigned int snd_trident_convert_adc_rate(unsigned int rate)
+ 
+    Description: This routine converts rate in HZ to spurious threshold.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 rate - Real or Virtual channel number.
+   
+    Returns:     Delta value.
+@@ -748,7 +747,7 @@ static unsigned int snd_trident_spurious_threshold(unsigned int rate,
+ 
+    Description: This routine returns a control mode for a PCM channel.
+   
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+                 substream  - PCM substream
+   
+    Returns:     Control value.
+@@ -781,7 +780,7 @@ static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream
+   
+    Description: Device I/O control handler for playback/capture parameters.
+   
+-   Paramters:   substream  - PCM substream class
++   Parameters:   substream  - PCM substream class
+                 cmd     - what ioctl message to process
+                 arg     - additional message infoarg     
+   
+@@ -1664,7 +1663,7 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *
+   
+    Description: This routine return the capture position
+                 
+-   Paramters:   pcm1    - PCM device class
++   Parameters:   pcm1    - PCM device class
+ 
+    Returns:     position of buffer
+   
+@@ -2157,7 +2156,7 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = {
+   
+    Description: This routine registers the 4DWave device for PCM support.
+                 
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+ 
+    Returns:     None
+   
+@@ -2215,7 +2214,7 @@ int __devinit snd_trident_pcm(struct snd_trident * trident,
+   
+    Description: This routine registers the 4DWave device for foldback PCM support.
+                 
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+ 
+    Returns:     None
+   
+@@ -2272,7 +2271,7 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident,
+   
+    Description: This routine registers the 4DWave-NX device for SPDIF support.
+                 
+-   Paramters:   trident - pointer to target device class for 4DWave-NX.
++   Parameters:  trident - pointer to target device class for 4DWave-NX.
+ 
+    Returns:     None
+   
+@@ -2956,7 +2955,7 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr
+   
+    Description: This routine registers the 4DWave device for mixer support.
+                 
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+ 
+    Returns:     None
+   
+@@ -3313,12 +3312,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry,
+ 			snd_iprintf(buffer, "Memory Free    : %d\n", snd_util_mem_avail(trident->tlb.memhdr));
+ 		}
+ 	}
+-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+-	snd_iprintf(buffer,"\nWavetable Synth\n");
+-	snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size);
+-	snd_iprintf(buffer, "Memory Used    : %d\n", trident->synth.current_size);
+-	snd_iprintf(buffer, "Memory Free    : %d\n", (trident->synth.max_size-trident->synth.current_size));
+-#endif
+ }
+ 
+ static void __devinit snd_trident_proc_init(struct snd_trident * trident)
+@@ -3344,7 +3337,7 @@ static int snd_trident_dev_free(struct snd_device *device)
+    Description: Allocate and set up the TLB page table on 4D NX.
+ 		Each entry has 4 bytes (physical PCI address).
+                 
+-   Paramters:   trident - pointer to target device class for 4DWave.
++   Parameters:  trident - pointer to target device class for 4DWave.
+ 
+    Returns:     0 or negative error code
+   
+@@ -3521,7 +3514,7 @@ static int snd_trident_sis_init(struct snd_trident *trident)
+    Description: This routine will create the device specific class for
+                 the 4DWave card. It will also perform basic initialization.
+                 
+-   Paramters:   card  - which card to create
++   Parameters:  card  - which card to create
+                 pci   - interface to PCI bus resource info
+                 dma1ptr - playback dma buffer
+                 dma2ptr - capture dma buffer
+@@ -3667,7 +3660,7 @@ int __devinit snd_trident_create(struct snd_card *card,
+    Description: This routine will free the device specific class for
+                 the 4DWave card. 
+                 
+-   Paramters:   trident  - device specific private data for 4DWave card
++   Parameters:  trident  - device specific private data for 4DWave card
+ 
+    Returns:     None.
+   
+@@ -3705,7 +3698,7 @@ static int snd_trident_free(struct snd_trident *trident)
+   
+    Description: ISR for Trident 4DWave device
+                 
+-   Paramters:   trident  - device specific private data for 4DWave card
++   Parameters:  trident  - device specific private data for 4DWave card
+ 
+    Problems:    It seems that Trident chips generates interrupts more than
+                 one time in special cases. The spurious interrupts are
+@@ -3815,28 +3808,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id)
+ 	return IRQ_HANDLED;
+ }
+ 
+-/*---------------------------------------------------------------------------
+-   snd_trident_attach_synthesizer
+-  
+-   Description: Attach synthesizer hooks
+-                
+-   Paramters:   trident  - device specific private data for 4DWave card
+-
+-   Returns:     None.
+-  
+-  ---------------------------------------------------------------------------*/
+-int snd_trident_attach_synthesizer(struct snd_trident *trident)
+-{	
+-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+-	if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT,
+-			       sizeof(struct snd_trident *), &trident->seq_dev) >= 0) {
+-		strcpy(trident->seq_dev->name, "4DWave");
+-		*(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident;
+-	}
+-#endif
+-	return 0;
+-}
+-
+ struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port)
+ {
+ 	struct snd_trident_voice *pvoice;
+diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
+index 847b8c6..df9b487 100644
+--- a/sound/pci/trident/trident_memory.c
++++ b/sound/pci/trident/trident_memory.c
+@@ -23,7 +23,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/pci.h>
+ #include <linux/time.h>
+diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c
+deleted file mode 100644
+index 9b7dee8..0000000
+--- a/sound/pci/trident/trident_synth.c
++++ /dev/null
+@@ -1,1024 +0,0 @@
+-/*
+- *  Routines for Trident 4DWave NX/DX soundcards - Synthesizer
+- *  Copyright (c) by Scott McNab <jedi at tartarus.uwa.edu.au>
+- *
+- *
+- *   This program 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 program 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 <asm/io.h>
+-#include <linux/init.h>
+-#include <linux/slab.h>
+-#include <linux/pci.h>
+-#include <sound/core.h>
+-#include <sound/trident.h>
+-#include <sound/seq_device.h>
+-
+-MODULE_AUTHOR("Scott McNab <jedi at tartarus.uwa.edu.au>");
+-MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer");
+-MODULE_LICENSE("GPL");
+-
+-/* linear to log pan conversion table (4.2 channel attenuation format) */
+-static unsigned int pan_table[63] = {
+-	7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, 
+-	6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, 
+-	5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, 
+-	3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, 
+-	3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, 
+-	2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, 
+-	1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, 
+-	1588, 1543, 1499, 1456, 1415, 1375, 1336
+-};
+-
+-#define LOG_TABLE_SIZE 386
+-
+-/* Linear half-attenuation to log conversion table in the format:
+- *   {linear volume, logarithmic attenuation equivalent}, ...
+- *
+- * Provides conversion from a linear half-volume value in the range
+- * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB.
+- * Halving the linear volume is equivalent to an additional 6dB of 
+- * logarithmic attenuation. The algorithm used in log_from_linear()
+- * therefore uses this table as follows:
+- * 
+- * - loop and for every time the volume is less than half the maximum 
+- *   volume (16384), add another 6dB and halve the maximum value used
+- *   for this comparison.
+- * - when the volume is greater than half the maximum volume, take
+- *   the difference of the volume to half volume (in the range [0,8192])
+- *   and look up the log_table[] to find the nearest entry.
+- * - take the logarithic component of this entry and add it to the 
+- *   resulting attenuation.
+- *
+- * Thus this routine provides a linear->log conversion for a range of
+- * [0,16384] using only 386 table entries
+- *
+- * Note: although this table stores log attenuation in 8.8 format, values
+- * were only calculated for 6 bits fractional precision, since that is
+- * the most precision offered by the trident hardware.
+- */
+-
+-static unsigned short log_table[LOG_TABLE_SIZE*2] =
+-{
+-	4, 0x0604, 19, 0x0600, 34, 0x05fc, 
+-	49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, 
+-	123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, 
+-	198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, 
+-	274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, 
+-	350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, 
+-	428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, 
+-	506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, 
+-	584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, 
+-	663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, 
+-	743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, 
+-	824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, 
+-	906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, 
+-	988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, 
+-	1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, 
+-	1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, 
+-	1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, 
+-	1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, 
+-	1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, 
+-	1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, 
+-	1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, 
+-	1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, 
+-	1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, 
+-	1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, 
+-	1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, 
+-	2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, 
+-	2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, 
+-	2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, 
+-	2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, 
+-	2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, 
+-	2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, 
+-	2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, 
+-	2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, 
+-	2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, 
+-	2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, 
+-	2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, 
+-	3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, 
+-	3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, 
+-	3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, 
+-	3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, 
+-	3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, 
+-	3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, 
+-	3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, 
+-	3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, 
+-	3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, 
+-	4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, 
+-	4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, 
+-	4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, 
+-	4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, 
+-	4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, 
+-	4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, 
+-	4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, 
+-	4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, 
+-	4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, 
+-	5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, 
+-	5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, 
+-	5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, 
+-	5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, 
+-	5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, 
+-	5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, 
+-	5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, 
+-	5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, 
+-	6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, 
+-	6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, 
+-	6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, 
+-	6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, 
+-	6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, 
+-	6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, 
+-	6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, 
+-	6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, 
+-	7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, 
+-	7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, 
+-	7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, 
+-	7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, 
+-	7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, 
+-	7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, 
+-	7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, 
+-	8133, 0x0008, 8162, 0x0004, 8192, 0x0000
+-};
+-
+-static unsigned short lookup_volume_table( unsigned short value )
+-{
+-	/* This code is an optimised version of:
+-	 *   int i = 0;
+-	 *   while( volume_table[i*2] < value )
+-	 *       i++;
+-	 *   return volume_table[i*2+1];
+-	 */
+-	unsigned short *ptr = log_table;
+-	while( *ptr < value )
+-		ptr += 2;
+-	return *(ptr+1);
+-}
+-
+-/* this function calculates a 8.8 fixed point logarithmic attenuation
+- * value from a linear volume value in the range 0 to 16384 */
+-static unsigned short log_from_linear( unsigned short value )
+-{
+-	if (value >= 16384)
+-		return 0x0000;
+-	if (value) {
+-		unsigned short result = 0;
+-		int v, c;
+-		for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) {
+-			if( value >= v ) {
+-				result += lookup_volume_table( (value - v) << c );
+-				return result;
+-			}
+-			result += 0x0605;	/* 6.0205 (result of -20*log10(0.5)) */
+-		}
+-	}
+-	return 0xffff;
+-}
+-
+-/*
+- * Sample handling operations
+- */
+-
+-static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
+-static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode);
+-static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq);
+-static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume);
+-static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop);
+-static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position);
+-static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data);
+-
+-static struct snd_trident_sample_ops sample_ops =
+-{
+-	sample_start,
+-	sample_stop,
+-	sample_freq,
+-	sample_volume,
+-	sample_loop,
+-	sample_pos,
+-	sample_private1
+-};
+-
+-static void snd_trident_simple_init(struct snd_trident_voice * voice)
+-{
+-	//voice->handler_wave = interrupt_wave;
+-	//voice->handler_volume = interrupt_volume;
+-	//voice->handler_effect = interrupt_effect;
+-	//voice->volume_change = NULL;
+-	voice->sample_ops = &sample_ops;
+-}
+-
+-static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
+-{
+-	struct simple_instrument *simple;
+-	struct snd_seq_kinstr *instr;
+-	unsigned long flags;
+-	unsigned int loop_start, loop_end, sample_start, sample_end, start_offset;
+-	unsigned int value;
+-	unsigned int shift = 0;
+-
+-	instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+-	if (instr == NULL)
+-		return;
+-	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+-	simple = KINSTR_DATA(instr);
+-
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-
+-	if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+-		voice->GVSel = 1;	/* route to Wave volume */
+-
+-	voice->CTRL = 0;
+-	voice->Alpha = 0;
+-	voice->FMS = 0;
+-
+-	loop_start = simple->loop_start >> 4;
+-	loop_end = simple->loop_end >> 4;
+-	sample_start = (simple->start + position) >> 4;
+-	if( sample_start >= simple->size )
+-		sample_start = simple->start >> 4;
+-	sample_end = simple->size;
+-	start_offset = position >> 4;
+-
+-	if (simple->format & SIMPLE_WAVE_16BIT) {
+-		voice->CTRL |= 8;
+-		shift++;
+-	}
+-	if (simple->format & SIMPLE_WAVE_STEREO) {
+-		voice->CTRL |= 4;
+-		shift++;
+-	}
+-	if (!(simple->format & SIMPLE_WAVE_UNSIGNED))
+-		voice->CTRL |= 2;
+-
+-	voice->LBA = simple->address.memory;
+-
+-	if (simple->format & SIMPLE_WAVE_LOOP) {
+-		voice->CTRL |= 1;
+-		voice->LBA += loop_start << shift;
+-		if( start_offset >= loop_start ) {
+-			voice->CSO = start_offset - loop_start;
+-			voice->negCSO = 0;
+-		} else {
+-			voice->CSO = loop_start - start_offset;
+-			voice->negCSO = 1;
+-		}
+-		voice->ESO = loop_end - loop_start - 1;
+-	} else {
+-		voice->LBA += start_offset << shift;
+-		voice->CSO = sample_start;
+-		voice->ESO = sample_end - 1;
+-		voice->negCSO = 0;
+-	}
+-
+-	if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) {
+-		snd_trident_stop_voice(trident, voice->number);
+-		voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+-	}
+-
+-	/* set CSO sign */
+-	value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+-	if( voice->negCSO ) {
+-		value |= 1 << (voice->number&31);
+-	} else {
+-		value &= ~(1 << (voice->number&31));
+-	}
+-	outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+-
+-	voice->Attribute = 0;	
+-	snd_trident_write_voice_regs(trident, voice);
+-	snd_trident_start_voice(trident, voice->number);
+-	voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING;
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-	snd_seq_instr_free_use(trident->synth.ilist, instr);
+-}
+-
+-static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode)
+-{
+-	unsigned long flags;
+-
+-	if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING))
+-		return;
+-
+-	switch (mode) {
+-	default:
+-		spin_lock_irqsave(&trident->reg_lock, flags);
+-		snd_trident_stop_voice(trident, voice->number);
+-		voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+-		spin_unlock_irqrestore(&trident->reg_lock, flags);
+-		break;
+-	case SAMPLE_STOP_LOOP:	/* disable loop only */
+-		voice->CTRL &= ~1;
+-		spin_lock_irqsave(&trident->reg_lock, flags);
+-		outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+-		outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC);
+-		spin_unlock_irqrestore(&trident->reg_lock, flags);
+-		break;
+-	}
+-}
+-
+-static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq)
+-{
+-	unsigned long flags;
+-	freq >>= 4;
+-
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-	if (freq == 44100)
+-		voice->Delta = 0xeb3;
+-	else if (freq == 8000)
+-		voice->Delta = 0x2ab;
+-	else if (freq == 48000)
+-		voice->Delta = 0x1000;
+-	else
+-		voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff;
+-
+-	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+-	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+-		outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3));
+-		outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3));
+-	} else {
+-		outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA));
+-	}
+-
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-}
+-
+-static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume)
+-{
+-	unsigned long flags;
+-	unsigned short value;
+-
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-	voice->GVSel = 0;	/* use global music volume */
+-	voice->FMC = 0x03;	/* fixme: can we do something useful with FMC? */
+-	if (volume->volume >= 0) {
+-		volume->volume &= 0x3fff;
+-		/* linear volume -> logarithmic attenuation conversion
+-		 * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits)
+-		 * Vol register used when additional attenuation is required */
+-		voice->RVol = 0;
+-		voice->CVol = 0;
+-		value = log_from_linear( volume->volume );
+-		voice->Vol = 0;
+-		voice->EC = (value & 0x3fff) >> 2;
+-		if (value > 0x3fff) {
+-			voice->EC |= 0xfc0;
+-			if (value < 0x5f00 )
+-				voice->Vol = ((value >> 8) - 0x3f) << 5;
+-			else {
+-				voice->Vol = 0x3ff;
+-				voice->EC = 0xfff;
+-			}
+-		}
+-	}
+-	if (volume->lr >= 0) {
+-		volume->lr &= 0x3fff;
+-		/* approximate linear pan by attenuating channels */
+-		if (volume->lr >= 0x2000) {	/* attenuate left (pan right) */
+-			value = 0x3fff - volume->lr;
+-			for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
+-				if (value >= pan_table[voice->Pan] )
+-					break;
+-		} else {			/* attenuate right (pan left) */
+-			for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) 
+-				if ((unsigned int)volume->lr >= pan_table[voice->Pan] )
+-					break;
+-			voice->Pan |= 0x40;
+-		}
+-	}
+-	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+-	outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) |
+-		 ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) |
+-		 (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+-	value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f);
+-	outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-}
+-
+-static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop)
+-{
+-	unsigned long flags;
+-	struct simple_instrument *simple;
+-	struct snd_seq_kinstr *instr;
+-	unsigned int loop_start, loop_end;
+-
+-	instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+-	if (instr == NULL)
+-		return;
+-	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+-	simple = KINSTR_DATA(instr);
+-
+-	loop_start = loop->start >> 4;
+-	loop_end = loop->end >> 4;
+-
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-
+-	voice->LBA = simple->address.memory + loop_start;
+-	voice->CSO = 0;
+-	voice->ESO = loop_end - loop_start - 1;
+-
+-	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+-	outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2));
+-	outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA));
+-	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+-		outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2));
+-		outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO));
+-		outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+-		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+-	} else {
+-		outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2));
+-		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2));
+-	}
+-
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-	snd_seq_instr_free_use(trident->synth.ilist, instr);
+-}
+-
+-static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position)
+-{
+-	unsigned long flags;
+-	struct simple_instrument *simple;
+-	struct snd_seq_kinstr *instr;
+-	unsigned int value;
+-
+-	instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1);
+-	if (instr == NULL)
+-		return;
+-	voice->instr = instr->instr;	/* copy ID to speedup aliases */
+-	simple = KINSTR_DATA(instr);
+-
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-
+-	if (simple->format & SIMPLE_WAVE_LOOP) {
+-		if( position >= simple->loop_start ) {
+-			voice->CSO = (position - simple->loop_start) >> 4;
+-			voice->negCSO = 0;
+-		} else {
+-			voice->CSO = (simple->loop_start - position) >> 4;
+-			voice->negCSO = 1;
+-		}
+-	} else {
+-		voice->CSO = position >> 4;
+-		voice->negCSO = 0;
+-	}
+-
+-	/* set CSO sign */
+-	value = inl(TRID_REG(trident, T4D_SIGN_CSO_A));
+-	if( voice->negCSO ) {
+-		value |= 1 << (voice->number&31);
+-	} else {
+-		value &= ~(1 << (voice->number&31));
+-	}
+-	outl(value,TRID_REG(trident, T4D_SIGN_CSO_A));
+-	
+-
+-	outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR));
+-	if (trident->device == TRIDENT_DEVICE_ID_NX) {
+-		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO));
+-		outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2));
+-	} else {
+-		outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2);
+-	}
+-
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-	snd_seq_instr_free_use(trident->synth.ilist, instr);
+-}
+-
+-static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data)
+-{
+-}
+-
+-/*
+- * Memory management / sample loading
+- */
+-
+-static int snd_trident_simple_put_sample(void *private_data,
+-					 struct simple_instrument * instr,
+-					 char __user *data, long len, int atomic)
+-{
+-	struct snd_trident *trident = private_data;
+-	int size = instr->size;
+-	int shift = 0;
+-
+-	if (instr->format & SIMPLE_WAVE_BACKWARD ||
+-	    instr->format & SIMPLE_WAVE_BIDIR ||
+-	    instr->format & SIMPLE_WAVE_ULAW) 
+-		return -EINVAL;	/* not supported */
+-
+-	if (instr->format & SIMPLE_WAVE_16BIT)
+-		shift++;
+-	if (instr->format & SIMPLE_WAVE_STEREO)
+-		shift++;
+-	size <<= shift;
+-
+-	if (trident->synth.current_size + size > trident->synth.max_size)
+-		return -ENOMEM;
+-
+-	if (!access_ok(VERIFY_READ, data, size))
+-		return -EFAULT;
+-
+-	if (trident->tlb.entries) {
+-		struct snd_util_memblk *memblk;
+-		memblk = snd_trident_synth_alloc(trident, size); 
+-		if (memblk == NULL)
+-			return -ENOMEM;
+-		if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) {
+-			snd_trident_synth_free(trident, memblk);
+-			return -EFAULT;
+-		}
+-		instr->address.ptr = (unsigned char*)memblk;
+-		instr->address.memory = memblk->offset;
+-	} else {
+-		struct snd_dma_buffer dmab;
+-		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci),
+-					size, &dmab) < 0)
+-			return -ENOMEM;
+-
+-		if (copy_from_user(dmab.area, data, size)) {
+-			snd_dma_free_pages(&dmab);
+-			return -EFAULT;
+-		}
+-		instr->address.ptr = dmab.area;
+-		instr->address.memory = dmab.addr;
+-	}
+-
+-	trident->synth.current_size += size;
+-	return 0;
+-}
+-
+-static int snd_trident_simple_get_sample(void *private_data,
+-					 struct simple_instrument * instr,
+-					 char __user *data, long len, int atomic)
+-{
+-	//struct snd_trident *trident = private_data;
+-	int size = instr->size;
+-	int shift = 0;
+-
+-	if (instr->format & SIMPLE_WAVE_16BIT)
+-		shift++;
+-	if (instr->format & SIMPLE_WAVE_STEREO)
+-		shift++;
+-	size <<= shift;
+-
+-	if (!access_ok(VERIFY_WRITE, data, size))
+-		return -EFAULT;
+-
+-	/* FIXME: not implemented yet */
+-
+-	return -EBUSY;
+-}
+-
+-static int snd_trident_simple_remove_sample(void *private_data,
+-					    struct simple_instrument * instr,
+-					    int atomic)
+-{
+-	struct snd_trident *trident = private_data;
+-	int size = instr->size;
+-
+-	if (instr->format & SIMPLE_WAVE_16BIT)
+-		size <<= 1;
+-	if (instr->format & SIMPLE_WAVE_STEREO)
+-		size <<= 1;
+-
+-	if (trident->tlb.entries) {
+-		struct snd_util_memblk *memblk = (struct snd_util_memblk *)instr->address.ptr;
+-		if (memblk)
+-			snd_trident_synth_free(trident, memblk);
+-		else
+-			return -EFAULT;
+-	} else {
+-		struct snd_dma_buffer dmab;
+-		dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+-		dmab.dev.dev = snd_dma_pci_data(trident->pci);
+-		dmab.area = instr->address.ptr;
+-		dmab.addr = instr->address.memory;
+-		dmab.bytes = size;
+-		snd_dma_free_pages(&dmab);
+-	}
+-
+-	trident->synth.current_size -= size;
+-	if (trident->synth.current_size < 0)	/* shouldn't need this check... */
+-		trident->synth.current_size = 0;
+-
+-	return 0;
+-}
+-
+-static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v)
+-{
+-	struct snd_seq_kinstr *instr;
+-	instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1);
+-	if (instr != NULL) {
+-		if (instr->ops) {
+-			if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE))
+-				snd_trident_simple_init(v);
+-		}
+-		snd_seq_instr_free_use(trident->synth.ilist, instr);
+-	}
+-}
+-
+-/*
+-
+- */
+-
+-static void event_sample(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_stop)
+-		v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+-	v->instr.std = ev->data.sample.param.sample.std;
+-	if (v->instr.std & 0xff000000) {	/* private instrument */
+-		v->instr.std &= 0x00ffffff;
+-		v->instr.std |= (unsigned int)ev->source.client << 24;
+-	}
+-	v->instr.bank = ev->data.sample.param.sample.bank;
+-	v->instr.prg = ev->data.sample.param.sample.prg;
+-	select_instrument(p->trident, v);
+-}
+-
+-static void event_cluster(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_stop)
+-		v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY);
+-	v->instr.cluster = ev->data.sample.param.cluster.cluster;
+-	select_instrument(p->trident, v);
+-}
+-
+-static void event_start(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_start)
+-		v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position);
+-}
+-
+-static void event_stop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_stop)
+-		v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode);
+-}
+-
+-static void event_freq(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_freq)
+-		v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency);
+-}
+-
+-static void event_volume(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_volume)
+-		v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume);
+-}
+-
+-static void event_loop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_loop)
+-		v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop);
+-}
+-
+-static void event_position(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_pos)
+-		v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position);
+-}
+-
+-static void event_private1(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v)
+-{
+-	if (v->sample_ops && v->sample_ops->sample_private1)
+-		v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8);
+-}
+-
+-typedef void (trident_sample_event_handler_t) (struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v);
+-
+-static trident_sample_event_handler_t *trident_sample_event_handlers[9] =
+-{
+-	event_sample,
+-	event_cluster,
+-	event_start,
+-	event_stop,
+-	event_freq,
+-	event_volume,
+-	event_loop,
+-	event_position,
+-	event_private1
+-};
+-
+-static void snd_trident_sample_event(struct snd_seq_event * ev, struct snd_trident_port * p)
+-{
+-	int idx, voice;
+-	struct snd_trident *trident = p->trident;
+-	struct snd_trident_voice *v;
+-	unsigned long flags;
+-
+-	idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE;
+-	if (idx < 0 || idx > 8)
+-		return;
+-	for (voice = 0; voice < 64; voice++) {
+-		v = &trident->synth.voices[voice];
+-		if (v->use && v->client == ev->source.client &&
+-		    v->port == ev->source.port &&
+-		    v->index == ev->data.sample.channel) {
+-			spin_lock_irqsave(&trident->event_lock, flags);
+-			trident_sample_event_handlers[idx] (ev, p, v);
+-			spin_unlock_irqrestore(&trident->event_lock, flags);
+-			return;
+-		}
+-	}
+-}
+-
+-/*
+-
+- */
+-
+-static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port)
+-{
+-	int idx;
+-	struct snd_trident_voice *voice;
+-
+-	for (idx = 0; idx < 32; idx++) {
+-		voice = &trident->synth.voices[idx];
+-		if (voice->use && voice->client == client && voice->port == port)
+-			snd_trident_free_voice(trident, voice);
+-	}
+-}
+-
+-static int snd_trident_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
+-{
+-	struct snd_trident_port *port = private_data;
+-	struct snd_trident *trident = port->trident;
+-	struct snd_trident_voice *voice;
+-	unsigned int idx;
+-	unsigned long flags;
+-
+-	if (info->voices > 32)
+-		return -EINVAL;
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-	for (idx = 0; idx < info->voices; idx++) {
+-		voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port);
+-		if (voice == NULL) {
+-			snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+-			spin_unlock_irqrestore(&trident->reg_lock, flags);
+-			return -EBUSY;
+-		}
+-		voice->index = idx;
+-		voice->Vol = 0x3ff;
+-		voice->EC = 0x0fff;
+-	}
+-#if 0
+-	for (idx = 0; idx < info->midi_voices; idx++) {
+-		port->midi_has_voices = 1;
+-		voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port);
+-		if (voice == NULL) {
+-			snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+-			spin_unlock_irqrestore(&trident->reg_lock, flags);
+-			return -EBUSY;
+-		}
+-		voice->Vol = 0x3ff;
+-		voice->EC = 0x0fff;
+-	}
+-#endif
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-	return 0;
+-}
+-
+-static int snd_trident_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
+-{
+-	struct snd_trident_port *port = private_data;
+-	struct snd_trident *trident = port->trident;
+-	unsigned long flags;
+-
+-	spin_lock_irqsave(&trident->reg_lock, flags);
+-	snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port);
+-	spin_unlock_irqrestore(&trident->reg_lock, flags);
+-	return 0;
+-}
+-
+-/*
+-
+- */
+-
+-static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client)
+-{
+-	struct snd_seq_instr_header ifree;
+-
+-	memset(&ifree, 0, sizeof(ifree));
+-	ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE;
+-	snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0);
+-}
+-
+-static int snd_trident_synth_event_input(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop)
+-{
+-	struct snd_trident_port *p = (struct snd_trident_port *) private_data;
+-
+-	if (p == NULL)
+-		return -EINVAL;
+-	if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE &&
+-	    ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) {
+-		snd_trident_sample_event(ev, p);
+-		return 0;
+-	}
+-	if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM &&
+-	    ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) {
+-		if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) {
+-			snd_trident_synth_free_private_instruments(p, ev->data.addr.client);
+-			return 0;
+-		}
+-	}
+-	if (direct) {
+-		if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) {
+-			snd_seq_instr_event(&p->trident->synth.simple_ops.kops,
+-					    p->trident->synth.ilist, ev,
+-					    p->trident->synth.seq_client, atomic, hop);
+-			return 0;
+-		}
+-	}
+-	return 0;
+-}
+-
+-static void snd_trident_synth_instr_notify(void *private_data,
+-					   struct snd_seq_kinstr * instr,
+-					   int what)
+-{
+-	int idx;
+-	struct snd_trident *trident = private_data;
+-	struct snd_trident_voice *pvoice;
+-	unsigned long flags;
+-
+-	spin_lock_irqsave(&trident->event_lock, flags);
+-	for (idx = 0; idx < 64; idx++) {
+-		pvoice = &trident->synth.voices[idx];
+-		if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) {
+-			if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) {
+-				pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY);
+-			} else {
+-				snd_trident_stop_voice(trident, pvoice->number);
+-				pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING;
+-			}
+-		}
+-	}
+-	spin_unlock_irqrestore(&trident->event_lock, flags);
+-}
+-
+-/*
+-
+- */
+-
+-static void snd_trident_synth_free_port(void *private_data)
+-{
+-	struct snd_trident_port *p = (struct snd_trident_port *) private_data;
+-
+-	if (p)
+-		snd_midi_channel_free_set(p->chset);
+-}
+-
+-static int snd_trident_synth_create_port(struct snd_trident * trident, int idx)
+-{
+-	struct snd_trident_port *p;
+-	struct snd_seq_port_callback callbacks;
+-	char name[32];
+-	char *str;
+-	int result;
+-
+-	p = &trident->synth.seq_ports[idx];
+-	p->chset = snd_midi_channel_alloc_set(16);
+-	if (p->chset == NULL)
+-		return -ENOMEM;
+-	p->chset->private_data = p;
+-	p->trident = trident;
+-	p->client = trident->synth.seq_client;
+-
+-	memset(&callbacks, 0, sizeof(callbacks));
+-	callbacks.owner = THIS_MODULE;
+-	callbacks.use = snd_trident_synth_use;
+-	callbacks.unuse = snd_trident_synth_unuse;
+-	callbacks.event_input = snd_trident_synth_event_input;
+-	callbacks.private_free = snd_trident_synth_free_port;
+-	callbacks.private_data = p;
+-
+-	str = "???";
+-	switch (trident->device) {
+-	case TRIDENT_DEVICE_ID_DX:	str = "Trident 4DWave-DX"; break;
+-	case TRIDENT_DEVICE_ID_NX:	str = "Trident 4DWave-NX"; break;
+-	case TRIDENT_DEVICE_ID_SI7018:	str = "SiS 7018"; break;
+-	}
+-	sprintf(name, "%s port %i", str, idx);
+-	p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client,
+-						   &callbacks,
+-						   SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
+-						   SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+-						   SNDRV_SEQ_PORT_TYPE_SYNTH |
+-						   SNDRV_SEQ_PORT_TYPE_HARDWARE |
+-						   SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
+-						   16, 0,
+-						   name);
+-	if (p->chset->port < 0) {
+-		result = p->chset->port;
+-		snd_trident_synth_free_port(p);
+-		return result;
+-	}
+-	p->port = p->chset->port;
+-	return 0;
+-}
+-
+-/*
+-
+- */
+-
+-static int snd_trident_synth_new_device(struct snd_seq_device *dev)
+-{
+-	struct snd_trident *trident;
+-	int client, i;
+-	struct snd_seq_port_subscribe sub;
+-	struct snd_simple_ops *simpleops;
+-	char *str;
+-
+-	trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+-	if (trident == NULL)
+-		return -EINVAL;
+-
+-	trident->synth.seq_client = -1;
+-
+-	/* allocate new client */
+-	str = "???";
+-	switch (trident->device) {
+-	case TRIDENT_DEVICE_ID_DX:	str = "Trident 4DWave-DX"; break;
+-	case TRIDENT_DEVICE_ID_NX:	str = "Trident 4DWave-NX"; break;
+-	case TRIDENT_DEVICE_ID_SI7018:	str = "SiS 7018"; break;
+-	}
+-	client = trident->synth.seq_client =
+-		snd_seq_create_kernel_client(trident->card, 1, str);
+-	if (client < 0)
+-		return client;
+-
+-	for (i = 0; i < 4; i++)
+-		snd_trident_synth_create_port(trident, i);
+-
+-	trident->synth.ilist = snd_seq_instr_list_new();
+-	if (trident->synth.ilist == NULL) {
+-		snd_seq_delete_kernel_client(client);
+-		trident->synth.seq_client = -1;
+-		return -ENOMEM;
+-	}
+-	trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
+-
+-	simpleops = &trident->synth.simple_ops;
+-	snd_seq_simple_init(simpleops, trident, NULL);
+-	simpleops->put_sample = snd_trident_simple_put_sample;
+-	simpleops->get_sample = snd_trident_simple_get_sample;
+-	simpleops->remove_sample = snd_trident_simple_remove_sample;
+-	simpleops->notify = snd_trident_synth_instr_notify;
+-
+-	memset(&sub, 0, sizeof(sub));
+-	sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
+-	sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
+-	sub.dest.client = client;
+-	sub.dest.port = 0;
+-	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub);
+-
+-	return 0;
+-}
+-
+-static int snd_trident_synth_delete_device(struct snd_seq_device *dev)
+-{
+-	struct snd_trident *trident;
+-
+-	trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+-	if (trident == NULL)
+-		return -EINVAL;
+-
+-	if (trident->synth.seq_client >= 0) {
+-		snd_seq_delete_kernel_client(trident->synth.seq_client);
+-		trident->synth.seq_client = -1;
+-	}
+-	if (trident->synth.ilist)
+-		snd_seq_instr_list_free(&trident->synth.ilist);
+-	return 0;
+-}
+-
+-static int __init alsa_trident_synth_init(void)
+-{
+-	static struct snd_seq_dev_ops ops =
+-	{
+-		snd_trident_synth_new_device,
+-		snd_trident_synth_delete_device
+-	};
+-
+-	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops,
+-					      sizeof(struct snd_trident *));
+-}
+-
+-static void __exit alsa_trident_synth_exit(void)
+-{
+-	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT);
+-}
+-
+-module_init(alsa_trident_synth_init)
+-module_exit(alsa_trident_synth_exit)
+diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
+index cf62d2a..a756be6 100644
+--- a/sound/pci/via82xx.c
++++ b/sound/pci/via82xx.c
+@@ -46,7 +46,6 @@
+  *	- Optimize position calculation for the 823x chips. 
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+@@ -1793,6 +1792,12 @@ static struct ac97_quirk ac97_quirks[] = {
+ 		.name = "m680x",
+ 		.type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */
+ 	},
++	{
++		.subvendor = 0x1297,
++		.subdevice = 0xa232,
++		.name = "Shuttle AK32VN",
++		.type = AC97_TUNE_HP_ONLY
++	},
+ 	{ } /* terminator */
+ };
+ 
+@@ -2232,9 +2237,9 @@ static int snd_via82xx_free(struct via82xx *chip)
+ 	for (i = 0; i < chip->num_devs; i++)
+ 		snd_via82xx_channel_reset(chip, &chip->devs[i]);
+ 	synchronize_irq(chip->irq);
+-      __end_hw:
+ 	if (chip->irq >= 0)
+ 		free_irq(chip->irq, chip);
++ __end_hw:
+ 	release_and_free_resource(chip->mpu_res);
+ 	pci_release_regions(chip->pci);
+ 
+@@ -2364,8 +2369,8 @@ static struct snd_pci_quirk dxs_whitelist[] __devinitdata = {
+ 	SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE),
+ 	SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC),
+ 	SND_PCI_QUIRK(0x1297, 0xa231, "Shuttle AK31v2", VIA_DXS_SRC),
+-	SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE),
+-	SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE),
++	SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_SRC),
++	SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_SRC),
+ 	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE),
+ 	SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE),
+ 	SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE),
+diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
+index 57fb9ae..f5df1c7 100644
+--- a/sound/pci/via82xx_modem.c
++++ b/sound/pci/via82xx_modem.c
+@@ -31,7 +31,6 @@
+  *      modems.
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
+index 474eac9..acc352f 100644
+--- a/sound/pci/vx222/vx222.c
++++ b/sound/pci/vx222/vx222.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/pci.h>
+diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
+index 55558be..b4bfc1a 100644
+--- a/sound/pci/vx222/vx222_ops.c
++++ b/sound/pci/vx222/vx222_ops.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/firmware.h>
+@@ -877,6 +876,12 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
+ {
+ 	struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
+ 	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
++	if (ucontrol->value.integer.value[0] < 0 ||
++	    ucontrol->value.integer.value[0] < MIC_LEVEL_MAX)
++		return -EINVAL;
++	if (ucontrol->value.integer.value[1] < 0 ||
++	    ucontrol->value.integer.value[1] < MIC_LEVEL_MAX)
++		return -EINVAL;
+ 	mutex_lock(&_chip->mixer_mutex);
+ 	if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
+ 	    chip->input_level[1] != ucontrol->value.integer.value[1]) {
+@@ -912,6 +917,9 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
+ {
+ 	struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
+ 	struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
++	if (ucontrol->value.integer.value[0] < 0 ||
++	    ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
++		return -EINVAL;
+ 	mutex_lock(&_chip->mixer_mutex);
+ 	if (chip->mic_level != ucontrol->value.integer.value[0]) {
+ 		chip->mic_level = ucontrol->value.integer.value[0];
+diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
+index 5c4256a..2631a55 100644
+--- a/sound/pci/ymfpci/ymfpci.c
++++ b/sound/pci/ymfpci/ymfpci.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/time.h>
+diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
+index 1fe39ed..42c1eb7 100644
+--- a/sound/pci/ymfpci/ymfpci_main.c
++++ b/sound/pci/ymfpci/ymfpci_main.c
+@@ -18,7 +18,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/firmware.h>
+ #include <linux/init.h>
+@@ -1735,6 +1734,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
+ 	    ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
+ 		chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0];
+ 		chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1];
++		if (chip->pcm_mixer[subs].left > 0x8000)
++			chip->pcm_mixer[subs].left = 0x8000;
++		if (chip->pcm_mixer[subs].right > 0x8000)
++			chip->pcm_mixer[subs].right = 0x8000;
+ 
+ 		substream = (struct snd_pcm_substream *)kcontrol->private_value;
+ 		spin_lock_irqsave(&chip->voice_lock, flags);
+diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
+index de683b0..819aaaa 100644
+--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
++++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <linux/slab.h>
+ #include <linux/moduleparam.h>
+@@ -129,6 +128,8 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
+ 		return -ENODEV;
+ 	}
+ 
++	snd_card_set_dev(card, &handle_to_dev(link));
++
+ 	pdacf->index = i;
+ 	card_list[i] = card;
+ 
+diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
+index 484c8f9..dfa40b0 100644
+--- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
++++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <sound/core.h>
+ #include <sound/info.h>
+diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
+index 5454336..fa4b113 100644
+--- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
++++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include "pdaudiocf.h"
+ #include <sound/initval.h>
+diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
+index 10afcb2..01066c9 100644
+--- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
++++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+ #include <linux/delay.h>
+diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c
+index 1eff158..a4a6642 100644
+--- a/sound/pcmcia/vx/vxp_mixer.c
++++ b/sound/pcmcia/vx/vxp_mixer.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/tlv.h>
+@@ -53,6 +52,10 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
+ {
+ 	struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
+ 	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
++	unsigned int val = ucontrol->value.integer.value[0];
++
++	if (val > MIC_LEVEL_MAX)
++		return -EINVAL;
+ 	mutex_lock(&_chip->mixer_mutex);
+ 	if (chip->mic_level != ucontrol->value.integer.value[0]) {
+ 		vx_set_mic_level(_chip, ucontrol->value.integer.value[0]);
+@@ -94,10 +97,11 @@ static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
+ {
+ 	struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
+ 	struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip;
++	int val = !!ucontrol->value.integer.value[0];
+ 	mutex_lock(&_chip->mixer_mutex);
+-	if (chip->mic_level != ucontrol->value.integer.value[0]) {
+-		vx_set_mic_boost(_chip, ucontrol->value.integer.value[0]);
+-		chip->mic_level = ucontrol->value.integer.value[0];
++	if (chip->mic_level != val) {
++		vx_set_mic_boost(_chip, val);
++		chip->mic_level = val;
+ 		mutex_unlock(&_chip->mixer_mutex);
+ 		return 1;
+ 	}
+diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
+index 1ee0918..157b0b5 100644
+--- a/sound/pcmcia/vx/vxp_ops.c
++++ b/sound/pcmcia/vx/vxp_ops.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/firmware.h>
+diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
+index c57e127..706602a 100644
+--- a/sound/pcmcia/vx/vxpocket.c
++++ b/sound/pcmcia/vx/vxpocket.c
+@@ -19,7 +19,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/moduleparam.h>
+ #include <sound/core.h>
+diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
+index 05dabe4..8441e78 100644
+--- a/sound/ppc/awacs.c
++++ b/sound/ppc/awacs.c
+@@ -20,7 +20,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <asm/nvram.h>
+ #include <linux/init.h>
+@@ -175,10 +174,12 @@ static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
+ 	int inverted = (kcontrol->private_value >> 16) & 1;
+ 	int val, oldval;
+ 	unsigned long flags;
+-	int vol[2];
++	unsigned int vol[2];
+ 
+ 	vol[0] = ucontrol->value.integer.value[0];
+ 	vol[1] = ucontrol->value.integer.value[1];
++	if (vol[0] > 0x0f || vol[1] > 0x0f)
++		return -EINVAL;
+ 	if (inverted) {
+ 		vol[0] = 0x0f - vol[0];
+ 		vol[1] = 0x0f - vol[1];
+@@ -421,10 +422,14 @@ static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	int index = kcontrol->private_value;
+ 	struct awacs_amp *amp = chip->mixer_data;
++	unsigned int val;
+ 	snd_assert(amp, return -EINVAL);
+ 	snd_assert(index >= 0 && index <= 1, return -EINVAL);
+-	if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) {
+-		amp->amp_tone[index] = ucontrol->value.integer.value[0];
++	val = ucontrol->value.integer.value[0];
++	if (val > 14)
++		return -EINVAL;
++	if (val != amp->amp_tone[index]) {
++		amp->amp_tone[index] = val;
+ 		awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
+ 		return 1;
+ 	}
+@@ -456,9 +461,13 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
+ {
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	struct awacs_amp *amp = chip->mixer_data;
++	unsigned int val;
+ 	snd_assert(amp, return -EINVAL);
+-	if (ucontrol->value.integer.value[0] != amp->amp_master) {
+-		amp->amp_master = ucontrol->value.integer.value[0];
++	val = ucontrol->value.integer.value[0];
++	if (val > 99)
++		return -EINVAL;
++	if (val != amp->amp_master) {
++		amp->amp_master = val;
+ 		awacs_amp_set_master(amp, amp->amp_master);
+ 		return 1;
+ 	}
+diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
+index 566b5ab..baa2a72 100644
+--- a/sound/ppc/beep.c
++++ b/sound/ppc/beep.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <asm/irq.h>
+ #include <linux/init.h>
+@@ -195,10 +194,13 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
+ 			     struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+-	int oval;
++	unsigned int oval, nval;
+ 	snd_assert(chip->beep, return -ENXIO);
+ 	oval = chip->beep->volume;
+-	chip->beep->volume = ucontrol->value.integer.value[0];
++	nval = ucontrol->value.integer.value[0];
++	if (nval > 100)
++		return -EINVAL;
++	chip->beep->volume = nval;
+ 	return oval != chip->beep->volume;
+ }
+ 
+diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
+index e02263f..1a545ac 100644
+--- a/sound/ppc/burgundy.c
++++ b/sound/ppc/burgundy.c
+@@ -19,7 +19,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+@@ -136,6 +135,9 @@ snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address,
+ {
+ 	int hardvolume, lvolume, rvolume;
+ 
++	if (volume[0] < 0 || volume[0] > 100 ||
++	    volume[1] < 0 || volume[1] > 100)
++		return; /* -EINVAL */
+ 	lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0;
+ 	rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0;
+ 
+@@ -301,14 +303,14 @@ static int snd_pmac_burgundy_put_volume_out(struct snd_kcontrol *kcontrol,
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
+ 	int stereo = (kcontrol->private_value >> 24) & 1;
+-	int oval, val;
++	unsigned int oval, val;
+ 
+ 	oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff;
+-	val = ucontrol->value.integer.value[0];
++	val = ucontrol->value.integer.value[0] & 15;
+ 	if (stereo)
+-		val |= ucontrol->value.integer.value[1] << 4;
++		val |= (ucontrol->value.integer.value[1] & 15) << 4;
+ 	else
+-		val |= ucontrol->value.integer.value[0] << 4;
++		val |= val << 4;
+ 	val = ~val & 0xff;
+ 	snd_pmac_burgundy_wcb(chip, addr, val);
+ 	return val != oval;
+diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
+index c5a1f0b..8432c16 100644
+--- a/sound/ppc/daca.c
++++ b/sound/ppc/daca.c
+@@ -19,7 +19,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/i2c.h>
+ #include <linux/kmod.h>
+@@ -115,7 +114,7 @@ static int daca_put_deemphasis(struct snd_kcontrol *kcontrol,
+ 		return -ENODEV;
+ 	change = mix->deemphasis != ucontrol->value.integer.value[0];
+ 	if (change) {
+-		mix->deemphasis = ucontrol->value.integer.value[0];
++		mix->deemphasis = !!ucontrol->value.integer.value[0];
+ 		daca_set_volume(mix);
+ 	}
+ 	return change;
+@@ -149,15 +148,20 @@ static int daca_put_volume(struct snd_kcontrol *kcontrol,
+ {
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	struct pmac_daca *mix;
++	unsigned int vol[2];
+ 	int change;
+ 
+ 	if (! (mix = chip->mixer_data))
+ 		return -ENODEV;
+-	change = mix->left_vol != ucontrol->value.integer.value[0] ||
+-		mix->right_vol != ucontrol->value.integer.value[1];
++	vol[0] = ucontrol->value.integer.value[0];
++	vol[1] = ucontrol->value.integer.value[1];
++	if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX)
++		return -EINVAL;
++	change = mix->left_vol != vol[0] ||
++		mix->right_vol != vol[1];
+ 	if (change) {
+-		mix->left_vol = ucontrol->value.integer.value[0];
+-		mix->right_vol = ucontrol->value.integer.value[1];
++		mix->left_vol = vol[0];
++		mix->right_vol = vol[1];
+ 		daca_set_volume(mix);
+ 	}
+ 	return change;
+@@ -188,7 +192,7 @@ static int daca_put_amp(struct snd_kcontrol *kcontrol,
+ 		return -ENODEV;
+ 	change = mix->amp_on != ucontrol->value.integer.value[0];
+ 	if (change) {
+-		mix->amp_on = ucontrol->value.integer.value[0];
++		mix->amp_on = !!ucontrol->value.integer.value[0];
+ 		i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
+ 					  mix->amp_on ? 0x05 : 0x04);
+ 	}
+diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
+index 272ae38..6ff99ed 100644
+--- a/sound/ppc/keywest.c
++++ b/sound/ppc/keywest.c
+@@ -19,7 +19,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/i2c.h>
+ #include <linux/delay.h>
+@@ -34,8 +33,6 @@
+ static struct pmac_keywest *keywest_ctx;
+ 
+ 
+-#define I2C_DRIVERID_KEYWEST	0xFEBA
+-
+ static int keywest_attach_adapter(struct i2c_adapter *adapter);
+ static int keywest_detach_client(struct i2c_client *client);
+ 
+@@ -43,7 +40,6 @@ struct i2c_driver keywest_driver = {
+ 	.driver = {
+ 		.name = "PMac Keywest Audio",
+ 	},
+-	.id = I2C_DRIVERID_KEYWEST,
+ 	.attach_adapter = &keywest_attach_adapter,
+ 	.detach_client = &keywest_detach_client,
+ };
+diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
+index 4f9b19c..613a565 100644
+--- a/sound/ppc/pmac.c
++++ b/sound/ppc/pmac.c
+@@ -20,7 +20,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <asm/io.h>
+ #include <asm/irq.h>
+ #include <linux/init.h>
+@@ -45,6 +44,18 @@ static int tumbler_freqs[1] = {
+ 	44100
+ };
+ 
++
++/*
++ * we will allocate a single 'emergency' dbdma cmd block to use if the
++ * tx status comes up "DEAD".  This happens on some PowerComputing Pmac
++ * clones, either owing to a bug in dbdma or some interaction between
++ * IDE and sound.  However, this measure would deal with DEAD status if
++ * it appeared elsewhere.
++ */
++static struct pmac_dbdma emergency_dbdma;
++static int emergency_in_use;
++
++
+ /*
+  * allocate DBDMA command arrays
+  */
+@@ -376,6 +387,75 @@ static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs
+ 
+ 
+ /*
++ * Handle DEAD DMA transfers:
++ * if the TX status comes up "DEAD" - reported on some Power Computing machines
++ * we need to re-start the dbdma - but from a different physical start address
++ * and with a different transfer length.  It would get very messy to do this
++ * with the normal dbdma_cmd blocks - we would have to re-write the buffer start
++ * addresses each time.  So, we will keep a single dbdma_cmd block which can be
++ * fiddled with.
++ * When DEAD status is first reported the content of the faulted dbdma block is
++ * copied into the emergency buffer and we note that the buffer is in use.
++ * we then bump the start physical address by the amount that was successfully
++ * output before it died.
++ * On any subsequent DEAD result we just do the bump-ups (we know that we are
++ * already using the emergency dbdma_cmd).
++ * CHECK: this just tries to "do it".  It is possible that we should abandon
++ * xfers when the number of residual bytes gets below a certain value - I can
++ * see that this might cause a loop-forever if a too small transfer causes
++ * DEAD status.  However this is a TODO for now - we'll see what gets reported.
++ * When we get a successful transfer result with the emergency buffer we just
++ * pretend that it completed using the original dmdma_cmd and carry on.  The
++ * 'next_cmd' field will already point back to the original loop of blocks.
++ */
++static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec,
++					  volatile struct dbdma_cmd __iomem *cp)
++{
++	unsigned short req, res ;
++	unsigned int phy ;
++
++	/* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */
++
++	/* to clear DEAD status we must first clear RUN
++	   set it to quiescent to be on the safe side */
++	(void)in_le32(&rec->dma->status);
++	out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
++
++	if (!emergency_in_use) { /* new problem */
++		memcpy((void *)emergency_dbdma.cmds, (void *)cp,
++		       sizeof(struct dbdma_cmd));
++		emergency_in_use = 1;
++		st_le16(&cp->xfer_status, 0);
++		st_le16(&cp->req_count, rec->period_size);
++		cp = emergency_dbdma.cmds;
++	}
++
++	/* now bump the values to reflect the amount
++	   we haven't yet shifted */
++	req = ld_le16(&cp->req_count);
++	res = ld_le16(&cp->res_count);
++	phy = ld_le32(&cp->phy_addr);
++	phy += (req - res);
++	st_le16(&cp->req_count, res);
++	st_le16(&cp->res_count, 0);
++	st_le16(&cp->xfer_status, 0);
++	st_le32(&cp->phy_addr, phy);
++
++	st_le32(&cp->cmd_dep, rec->cmd.addr
++		+ sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods));
++
++	st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS);
++
++	/* point at our patched up command block */
++	out_le32(&rec->dma->cmdptr, emergency_dbdma.addr);
++
++	/* we must re-start the controller */
++	(void)in_le32(&rec->dma->status);
++	/* should complete clearing the DEAD status */
++	out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
++}
++
++/*
+  * update playback/capture pointer from interrupts
+  */
+ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
+@@ -386,11 +466,26 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
+ 
+ 	spin_lock(&chip->reg_lock);
+ 	if (rec->running) {
+-		cp = &rec->cmd.cmds[rec->cur_period];
+ 		for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */
++
++			if (emergency_in_use)   /* already using DEAD xfer? */
++				cp = emergency_dbdma.cmds;
++			else
++				cp = &rec->cmd.cmds[rec->cur_period];
++
+ 			stat = ld_le16(&cp->xfer_status);
++
++			if (stat & DEAD) {
++				snd_pmac_pcm_dead_xfer(rec, cp);
++				break; /* this block is still going */
++			}
++
++			if (emergency_in_use)
++				emergency_in_use = 0 ; /* done that */
++
+ 			if (! (stat & ACTIVE))
+ 				break;
++
+ 			/*printk("update frag %d\n", rec->cur_period);*/
+ 			st_le16(&cp->xfer_status, 0);
+ 			st_le16(&cp->req_count, rec->period_size);
+@@ -398,9 +493,8 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec)
+ 			rec->cur_period++;
+ 			if (rec->cur_period >= rec->nperiods) {
+ 				rec->cur_period = 0;
+-				cp = rec->cmd.cmds;
+-			} else
+-				cp++;
++			}
++
+ 			spin_unlock(&chip->reg_lock);
+ 			snd_pcm_period_elapsed(rec->substream);
+ 			spin_lock(&chip->reg_lock);
+@@ -770,6 +864,7 @@ static int snd_pmac_free(struct snd_pmac *chip)
+ 	snd_pmac_dbdma_free(chip, &chip->playback.cmd);
+ 	snd_pmac_dbdma_free(chip, &chip->capture.cmd);
+ 	snd_pmac_dbdma_free(chip, &chip->extra_dma);
++	snd_pmac_dbdma_free(chip, &emergency_dbdma);
+ 	if (chip->macio_base)
+ 		iounmap(chip->macio_base);
+ 	if (chip->latch_base)
+@@ -1028,7 +1123,7 @@ static int pmac_auto_mute_put(struct snd_kcontrol *kcontrol,
+ {
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	if (ucontrol->value.integer.value[0] != chip->auto_mute) {
+-		chip->auto_mute = ucontrol->value.integer.value[0];
++		chip->auto_mute = !!ucontrol->value.integer.value[0];
+ 		if (chip->update_automute)
+ 			chip->update_automute(chip, 1);
+ 		return 1;
+@@ -1108,7 +1203,8 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
+ 
+ 	if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
+ 	    snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 ||
+-	    snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0) {
++	    snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 ||
++	    snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) {
+ 		err = -ENOMEM;
+ 		goto __error;
+ 	}
+diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
+index 2264574..c936225 100644
+--- a/sound/ppc/powermac.c
++++ b/sound/ppc/powermac.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/err.h>
+ #include <linux/platform_device.h>
+diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
+index 27b6189..d8d0b4b 100644
+--- a/sound/ppc/snd_ps3.c
++++ b/sound/ppc/snd_ps3.c
+@@ -22,7 +22,6 @@
+ #include <linux/slab.h>
+ #include <linux/io.h>
+ #include <linux/interrupt.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/initval.h>
+ #include <sound/pcm.h>
+@@ -954,6 +953,7 @@ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
+ 	snd_ps3_init_avsetting(&the_card);
+ 
+ 	/* register the card */
++	snd_card_set_dev(the_card.card, &dev->core);
+ 	ret = snd_card_register(the_card.card);
+ 	if (ret < 0)
+ 		goto clean_dma_map;
+diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
+index 5821cdd..71a7a97 100644
+--- a/sound/ppc/tumbler.c
++++ b/sound/ppc/tumbler.c
+@@ -24,7 +24,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/delay.h>
+ #include <linux/i2c.h>
+@@ -275,14 +274,20 @@ static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol,
+ {
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	struct pmac_tumbler *mix = chip->mixer_data;
++	unsigned int vol[2];
+ 	int change;
+ 
+ 	snd_assert(mix, return -ENODEV);
+-	change = mix->master_vol[0] != ucontrol->value.integer.value[0] ||
+-		mix->master_vol[1] != ucontrol->value.integer.value[1];
++	vol[0] = ucontrol->value.integer.value[0];
++	vol[1] = ucontrol->value.integer.value[1];
++	if (vol[0] >= ARRAY_SIZE(master_volume_table) ||
++	    vol[1] >= ARRAY_SIZE(master_volume_table))
++		return -EINVAL;
++	change = mix->master_vol[0] != vol[0] ||
++		mix->master_vol[1] != vol[1];
+ 	if (change) {
+-		mix->master_vol[0] = ucontrol->value.integer.value[0];
+-		mix->master_vol[1] = ucontrol->value.integer.value[1];
++		mix->master_vol[0] = vol[0];
++		mix->master_vol[1] = vol[1];
+ 		tumbler_set_master_volume(mix);
+ 	}
+ 	return change;
+@@ -417,13 +422,22 @@ static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol,
+ {
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	struct pmac_tumbler *mix;
++	unsigned int val;
+ 	int change;
+ 
+ 	if (! (mix = chip->mixer_data))
+ 		return -ENODEV;
+-	change = mix->drc_range != ucontrol->value.integer.value[0];
++	val = ucontrol->value.integer.value[0];
++	if (chip->model == PMAC_TUMBLER) {
++		if (val > TAS3001_DRC_MAX)
++			return -EINVAL;
++	} else {
++		if (val > TAS3004_DRC_MAX)
++			return -EINVAL;
++	}
++	change = mix->drc_range != val;
+ 	if (change) {
+-		mix->drc_range = ucontrol->value.integer.value[0];
++		mix->drc_range = val;
+ 		if (chip->model == PMAC_TUMBLER)
+ 			tumbler_set_drc(mix);
+ 		else
+@@ -530,13 +544,17 @@ static int tumbler_put_mono(struct snd_kcontrol *kcontrol,
+ 	struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	struct pmac_tumbler *mix;
++	unsigned int vol;
+ 	int change;
+ 
+ 	if (! (mix = chip->mixer_data))
+ 		return -ENODEV;
+-	change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0];
++	vol = ucontrol->value.integer.value[0];
++	if (vol >= info->max)
++		return -EINVAL;
++	change = mix->mono_vol[info->index] != vol;
+ 	if (change) {
+-		mix->mono_vol[info->index] = ucontrol->value.integer.value[0];
++		mix->mono_vol[info->index] = vol;
+ 		tumbler_set_mono_volume(mix, info);
+ 	}
+ 	return change;
+@@ -672,15 +690,21 @@ static int snapper_put_mix(struct snd_kcontrol *kcontrol,
+ 	int idx = (int)kcontrol->private_value;
+ 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
+ 	struct pmac_tumbler *mix;
++	unsigned int vol[2];
+ 	int change;
+ 
+ 	if (! (mix = chip->mixer_data))
+ 		return -ENODEV;
+-	change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] ||
+-		mix->mix_vol[idx][1] != ucontrol->value.integer.value[1];
++	vol[0] = ucontrol->value.integer.value[0];
++	vol[1] = ucontrol->value.integer.value[1];
++	if (vol[0] >= ARRAY_SIZE(mixer_volume_table) ||
++	    vol[1] >= ARRAY_SIZE(mixer_volume_table))
++		return -EINVAL;
++	change = mix->mix_vol[idx][0] != vol[0] ||
++		mix->mix_vol[idx][1] != vol[1];
+ 	if (change) {
+-		mix->mix_vol[idx][0] = ucontrol->value.integer.value[0];
+-		mix->mix_vol[idx][1] = ucontrol->value.integer.value[1];
++		mix->mix_vol[idx][0] = vol[0];
++		mix->mix_vol[idx][1] = vol[1];
+ 		snapper_set_mix_vol(mix, idx);
+ 	}
+ 	return change;
+@@ -784,7 +808,7 @@ static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,
+ 	struct pmac_tumbler *mix = chip->mixer_data;
+ 
+ 	snd_assert(mix, return -ENODEV);
+-	ucontrol->value.integer.value[0] = mix->capture_source;
++	ucontrol->value.enumerated.item[0] = mix->capture_source;
+ 	return 0;
+ }
+ 
+@@ -796,9 +820,9 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
+ 	int change;
+ 
+ 	snd_assert(mix, return -ENODEV);
+-	change = ucontrol->value.integer.value[0] != mix->capture_source;
++	change = ucontrol->value.enumerated.item[0] != mix->capture_source;
+ 	if (change) {
+-		mix->capture_source = !!ucontrol->value.integer.value[0];
++		mix->capture_source = !!ucontrol->value.enumerated.item[0];
+ 		snapper_set_capture_source(mix);
+ 	}
+ 	return change;
+diff --git a/sound/sh/aica.c b/sound/sh/aica.c
+index 88dc840..d49417b 100644
+--- a/sound/sh/aica.c
++++ b/sound/sh/aica.c
+@@ -35,7 +35,6 @@
+ #include <linux/timer.h>
+ #include <linux/delay.h>
+ #include <linux/workqueue.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
+ #include <sound/pcm.h>
+@@ -237,6 +236,7 @@ static int aica_dma_transfer(int channels, int buffer_size,
+ 	struct snd_card_aica *dreamcastcard;
+ 	struct snd_pcm_runtime *runtime;
+ 	unsigned long flags;
++	err = 0;
+ 	dreamcastcard = substream->pcm->private_data;
+ 	period_offset = dreamcastcard->clicks;
+ 	period_offset %= (AICA_PERIOD_NUMBER / channels);
+@@ -522,11 +522,14 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
+ 			      struct snd_ctl_elem_value *ucontrol)
+ {
+ 	struct snd_card_aica *dreamcastcard;
++	unsigned int vol;
+ 	dreamcastcard = kcontrol->private_data;
+ 	if (unlikely(!dreamcastcard->channel))
+ 		return -ETXTBSY;
+-	if (unlikely(dreamcastcard->channel->vol ==
+-		     ucontrol->value.integer.value[0]))
++	vol = ucontrol->value.integer.value[0];
++	if (vol > 0xff)
++		return -EINVAL;
++	if (unlikely(dreamcastcard->channel->vol == vol))
+ 		return 0;
+ 	dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
+ 	dreamcastcard->master_volume = ucontrol->value.integer.value[0];
+diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
+index 97b2552..2765852 100644
+--- a/sound/soc/Kconfig
++++ b/sound/soc/Kconfig
+@@ -28,6 +28,7 @@ source "sound/soc/at91/Kconfig"
+ source "sound/soc/pxa/Kconfig"
+ source "sound/soc/s3c24xx/Kconfig"
+ source "sound/soc/sh/Kconfig"
++source "sound/soc/fsl/Kconfig"
+ 
+ # Supported codecs
+ source "sound/soc/codecs/Kconfig"
+diff --git a/sound/soc/Makefile b/sound/soc/Makefile
+index 3041403..4869c9a 100644
+--- a/sound/soc/Makefile
++++ b/sound/soc/Makefile
+@@ -1,4 +1,4 @@
+ snd-soc-core-objs := soc-core.o soc-dapm.o
+ 
+ obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
+-obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/ sh/
++obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/
+diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
+index b39b95a..67c88e3 100644
+--- a/sound/soc/at91/at91-pcm.c
++++ b/sound/soc/at91/at91-pcm.c
+@@ -23,7 +23,6 @@
+ #include <linux/dma-mapping.h>
+ #include <linux/atmel_pdc.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
+index 3d4e32c..f642d2d 100644
+--- a/sound/soc/at91/at91-ssc.c
++++ b/sound/soc/at91/at91-ssc.c
+@@ -22,7 +22,6 @@
+ #include <linux/clk.h>
+ #include <linux/atmel_pdc.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
+index 820a676..ad3ad9d 100644
+--- a/sound/soc/at91/eti_b1_wm8731.c
++++ b/sound/soc/at91/eti_b1_wm8731.c
+@@ -28,7 +28,6 @@
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index 7824880..898a7d3 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -37,3 +37,6 @@ config SND_SOC_CS4270_VD33_ERRATA
+ 	bool
+ 	depends on SND_SOC_CS4270
+ 
++config SND_SOC_TLV320AIC3X
++	tristate
++	depends on SND_SOC && I2C
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index 7ad78e3..c6e5338 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -4,6 +4,7 @@ snd-soc-wm8750-objs := wm8750.o
+ snd-soc-wm8753-objs := wm8753.o
+ snd-soc-wm9712-objs := wm9712.o
+ snd-soc-cs4270-objs := cs4270.o
++snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+ 
+ obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
+ obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
+@@ -11,3 +12,4 @@ obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
+ obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
+ obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
+ obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
++obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
+diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
+index 0b8a6f8..242130c 100644
+--- a/sound/soc/codecs/ac97.c
++++ b/sound/soc/codecs/ac97.c
+@@ -19,7 +19,6 @@
+ #include <linux/init.h>
+ #include <linux/kernel.h>
+ #include <linux/device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/ac97_codec.h>
+diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
+index abac628..bf2ab72 100644
+--- a/sound/soc/codecs/cs4270.c
++++ b/sound/soc/codecs/cs4270.c
+@@ -28,7 +28,6 @@
+ 
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/soc.h>
+ #include <sound/initval.h>
+@@ -48,12 +47,130 @@ struct cs4270_private {
+ 	unsigned int mode; /* The mode (I2S or left-justified) */
+ };
+ 
+-/* The number of MCLK/LRCK ratios supported by the CS4270 */
+-#define NUM_MCLK_RATIOS		9
++/*
++ * The codec isn't really big-endian or little-endian, since the I2S
++ * interface requires data to be sent serially with the MSbit first.
++ * However, to support BE and LE I2S devices, we specify both here.  That
++ * way, ALSA will always match the bit patterns.
++ */
++#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \
++			SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
++			SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
++			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
++			SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
++			SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
++
++#ifdef USE_I2C
++
++/* CS4270 registers addresses */
++#define CS4270_CHIPID	0x01	/* Chip ID */
++#define CS4270_PWRCTL	0x02	/* Power Control */
++#define CS4270_MODE	0x03	/* Mode Control */
++#define CS4270_FORMAT	0x04	/* Serial Format, ADC/DAC Control */
++#define CS4270_TRANS	0x05	/* Transition Control */
++#define CS4270_MUTE	0x06	/* Mute Control */
++#define CS4270_VOLA	0x07	/* DAC Channel A Volume Control */
++#define CS4270_VOLB	0x08	/* DAC Channel B Volume Control */
++
++#define CS4270_FIRSTREG	0x01
++#define CS4270_LASTREG	0x08
++#define CS4270_NUMREGS	(CS4270_LASTREG - CS4270_FIRSTREG + 1)
+ 
+-/* The actual MCLK/LRCK ratios, in increasing numerical order */
+-static unsigned int mclk_ratios[NUM_MCLK_RATIOS] =
+-	{64, 96, 128, 192, 256, 384, 512, 768, 1024};
++/* Bit masks for the CS4270 registers */
++#define CS4270_CHIPID_ID	0xF0
++#define CS4270_CHIPID_REV	0x0F
++#define CS4270_PWRCTL_FREEZE	0x80
++#define CS4270_PWRCTL_PDN_ADC	0x20
++#define CS4270_PWRCTL_PDN_DAC	0x02
++#define CS4270_PWRCTL_PDN	0x01
++#define CS4270_MODE_SPEED_MASK	0x30
++#define CS4270_MODE_1X		0x00
++#define CS4270_MODE_2X		0x10
++#define CS4270_MODE_4X		0x20
++#define CS4270_MODE_SLAVE	0x30
++#define CS4270_MODE_DIV_MASK	0x0E
++#define CS4270_MODE_DIV1	0x00
++#define CS4270_MODE_DIV15	0x02
++#define CS4270_MODE_DIV2	0x04
++#define CS4270_MODE_DIV3	0x06
++#define CS4270_MODE_DIV4	0x08
++#define CS4270_MODE_POPGUARD	0x01
++#define CS4270_FORMAT_FREEZE_A	0x80
++#define CS4270_FORMAT_FREEZE_B	0x40
++#define CS4270_FORMAT_LOOPBACK	0x20
++#define CS4270_FORMAT_DAC_MASK	0x18
++#define CS4270_FORMAT_DAC_LJ	0x00
++#define CS4270_FORMAT_DAC_I2S	0x08
++#define CS4270_FORMAT_DAC_RJ16	0x18
++#define CS4270_FORMAT_DAC_RJ24	0x10
++#define CS4270_FORMAT_ADC_MASK	0x01
++#define CS4270_FORMAT_ADC_LJ	0x00
++#define CS4270_FORMAT_ADC_I2S	0x01
++#define CS4270_TRANS_ONE_VOL	0x80
++#define CS4270_TRANS_SOFT	0x40
++#define CS4270_TRANS_ZERO	0x20
++#define CS4270_TRANS_INV_ADC_A	0x08
++#define CS4270_TRANS_INV_ADC_B	0x10
++#define CS4270_TRANS_INV_DAC_A	0x02
++#define CS4270_TRANS_INV_DAC_B	0x04
++#define CS4270_TRANS_DEEMPH	0x01
++#define CS4270_MUTE_AUTO	0x20
++#define CS4270_MUTE_ADC_A	0x08
++#define CS4270_MUTE_ADC_B	0x10
++#define CS4270_MUTE_POLARITY	0x04
++#define CS4270_MUTE_DAC_A	0x01
++#define CS4270_MUTE_DAC_B	0x02
++
++/*
++ * Clock Ratio Selection for Master Mode with I2C enabled
++ *
++ * The data for this chart is taken from Table 5 of the CS4270 reference
++ * manual.
++ *
++ * This table is used to determine how to program the Mode Control register.
++ * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
++ * rates the CS4270 currently supports.
++ *
++ * Each element in this array corresponds to the ratios in mclk_ratios[].
++ * These two arrays need to be in sync.
++ *
++ * 'speed_mode' is the corresponding bit pattern to be written to the
++ * MODE bits of the Mode Control Register
++ *
++ * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of
++ * the Mode Control Register.
++ *
++ * In situations where a single ratio is represented by multiple speed
++ * modes, we favor the slowest speed.  E.g, for a ratio of 128, we pick
++ * double-speed instead of quad-speed.  However, the CS4270 errata states
++ * that Divide-By-1.5 can cause failures, so we avoid that mode where
++ * possible.
++ *
++ * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not
++ * work if VD = 3.3V.  If this effects you, select the
++ * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
++ * never select any sample rates that require divide-by-1.5.
++ */
++static struct {
++	unsigned int ratio;
++	u8 speed_mode;
++	u8 mclk;
++} cs4270_mode_ratios[] = {
++	{64, CS4270_MODE_4X, CS4270_MODE_DIV1},
++#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
++	{96, CS4270_MODE_4X, CS4270_MODE_DIV15},
++#endif
++	{128, CS4270_MODE_2X, CS4270_MODE_DIV1},
++	{192, CS4270_MODE_4X, CS4270_MODE_DIV3},
++	{256, CS4270_MODE_1X, CS4270_MODE_DIV1},
++	{384, CS4270_MODE_2X, CS4270_MODE_DIV3},
++	{512, CS4270_MODE_1X, CS4270_MODE_DIV2},
++	{768, CS4270_MODE_1X, CS4270_MODE_DIV3},
++	{1024, CS4270_MODE_1X, CS4270_MODE_DIV4}
++};
++
++/* The number of MCLK/LRCK ratios supported by the CS4270 */
++#define NUM_MCLK_RATIOS		ARRAY_SIZE(cs4270_mode_ratios)
+ 
+ /*
+  * Determine the CS4270 samples rates.
+@@ -97,7 +214,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+ 	cs4270->mclk = freq;
+ 
+ 	for (i = 0; i < NUM_MCLK_RATIOS; i++) {
+-		unsigned int rate = freq / mclk_ratios[i];
++		unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
+ 		rates |= snd_pcm_rate_to_rate_bit(rate);
+ 		if (rate < rate_min)
+ 			rate_min = rate;
+@@ -155,86 +272,12 @@ static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+ }
+ 
+ /*
+- * The codec isn't really big-endian or little-endian, since the I2S
+- * interface requires data to be sent serially with the MSbit first.
+- * However, to support BE and LE I2S devices, we specify both here.  That
+- * way, ALSA will always match the bit patterns.
+- */
+-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \
+-			SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
+-			SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
+-			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
+-			SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
+-			SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
+-
+-#ifdef USE_I2C
+-
+-/* CS4270 registers addresses */
+-#define CS4270_CHIPID	0x01	/* Chip ID */
+-#define CS4270_PWRCTL	0x02	/* Power Control */
+-#define CS4270_MODE	0x03	/* Mode Control */
+-#define CS4270_FORMAT	0x04	/* Serial Format, ADC/DAC Control */
+-#define CS4270_TRANS	0x05	/* Transition Control */
+-#define CS4270_MUTE	0x06	/* Mute Control */
+-#define CS4270_VOLA	0x07	/* DAC Channel A Volume Control */
+-#define CS4270_VOLB	0x08	/* DAC Channel B Volume Control */
+-
+-#define CS4270_FIRSTREG	0x01
+-#define CS4270_LASTREG	0x08
+-#define CS4270_NUMREGS	(CS4270_LASTREG - CS4270_FIRSTREG + 1)
+-
+-/* Bit masks for the CS4270 registers */
+-#define CS4270_CHIPID_ID	0xF0
+-#define CS4270_CHIPID_REV	0x0F
+-#define CS4270_PWRCTL_FREEZE	0x80
+-#define CS4270_PWRCTL_PDN_ADC	0x20
+-#define CS4270_PWRCTL_PDN_DAC	0x02
+-#define CS4270_PWRCTL_PDN	0x01
+-#define CS4270_MODE_SPEED_MASK	0x30
+-#define CS4270_MODE_1X		0x00
+-#define CS4270_MODE_2X		0x10
+-#define CS4270_MODE_4X		0x20
+-#define CS4270_MODE_SLAVE	0x30
+-#define CS4270_MODE_DIV_MASK	0x0E
+-#define CS4270_MODE_DIV1	0x00
+-#define CS4270_MODE_DIV15	0x02
+-#define CS4270_MODE_DIV2	0x04
+-#define CS4270_MODE_DIV3	0x06
+-#define CS4270_MODE_DIV4	0x08
+-#define CS4270_MODE_POPGUARD	0x01
+-#define CS4270_FORMAT_FREEZE_A	0x80
+-#define CS4270_FORMAT_FREEZE_B	0x40
+-#define CS4270_FORMAT_LOOPBACK	0x20
+-#define CS4270_FORMAT_DAC_MASK	0x18
+-#define CS4270_FORMAT_DAC_LJ	0x00
+-#define CS4270_FORMAT_DAC_I2S	0x08
+-#define CS4270_FORMAT_DAC_RJ16	0x18
+-#define CS4270_FORMAT_DAC_RJ24	0x10
+-#define CS4270_FORMAT_ADC_MASK	0x01
+-#define CS4270_FORMAT_ADC_LJ	0x00
+-#define CS4270_FORMAT_ADC_I2S	0x01
+-#define CS4270_TRANS_ONE_VOL	0x80
+-#define CS4270_TRANS_SOFT	0x40
+-#define CS4270_TRANS_ZERO	0x20
+-#define CS4270_TRANS_INV_ADC_A	0x08
+-#define CS4270_TRANS_INV_ADC_B	0x10
+-#define CS4270_TRANS_INV_DAC_A	0x02
+-#define CS4270_TRANS_INV_DAC_B	0x04
+-#define CS4270_TRANS_DEEMPH	0x01
+-#define CS4270_MUTE_AUTO	0x20
+-#define CS4270_MUTE_ADC_A	0x08
+-#define CS4270_MUTE_ADC_B	0x10
+-#define CS4270_MUTE_POLARITY	0x04
+-#define CS4270_MUTE_DAC_A	0x01
+-#define CS4270_MUTE_DAC_B	0x02
+-
+-/*
+  * A list of addresses on which this CS4270 could use.  I2C addresses are
+  * 7 bits.  For the CS4270, the upper four bits are always 1001, and the
+  * lower three bits are determined via the AD2, AD1, and AD0 pins
+  * (respectively).
+  */
+-static unsigned short normal_i2c[] = {
++static const unsigned short normal_i2c[] = {
+ 	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END
+ };
+ I2C_CLIENT_INSMOD;
+@@ -315,53 +358,6 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
+ }
+ 
+ /*
+- * Clock Ratio Selection for Master Mode with I2C enabled
+- *
+- * The data for this chart is taken from Table 5 of the CS4270 reference
+- * manual.
+- *
+- * This table is used to determine how to program the Mode Control register.
+- * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
+- * rates the CS4270 currently supports.
+- *
+- * Each element in this array corresponds to the ratios in mclk_ratios[].
+- * These two arrays need to be in sync.
+- *
+- * 'speed_mode' is the corresponding bit pattern to be written to the
+- * MODE bits of the Mode Control Register
+- *
+- * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of
+- * the Mode Control Register.
+- *
+- * In situations where a single ratio is represented by multiple speed
+- * modes, we favor the slowest speed.  E.g, for a ratio of 128, we pick
+- * double-speed instead of quad-speed.  However, the CS4270 errata states
+- * that Divide-By-1.5 can cause failures, so we avoid that mode where
+- * possible.
+- *
+- * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not
+- * work if VD = 3.3V.  If this effects you, select the
+- * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
+- * never select any sample rates that require divide-by-1.5.
+- */
+-static struct {
+-	u8 speed_mode;
+-	u8 mclk;
+-} cs4270_mode_ratios[NUM_MCLK_RATIOS] = {
+-	{CS4270_MODE_4X, CS4270_MODE_DIV1},	/* 64 */
+-#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
+-	{CS4270_MODE_4X, CS4270_MODE_DIV15},    /* 96 */
+-#endif
+-	{CS4270_MODE_2X, CS4270_MODE_DIV1},     /* 128 */
+-	{CS4270_MODE_4X, CS4270_MODE_DIV3},     /* 192 */
+-	{CS4270_MODE_1X, CS4270_MODE_DIV1},     /* 256 */
+-	{CS4270_MODE_2X, CS4270_MODE_DIV3},     /* 384 */
+-	{CS4270_MODE_1X, CS4270_MODE_DIV2},     /* 512 */
+-	{CS4270_MODE_1X, CS4270_MODE_DIV3},     /* 768 */
+-	{CS4270_MODE_1X, CS4270_MODE_DIV4}      /* 1024 */
+-};
+-
+-/*
+  * Program the CS4270 with the given hardware parameters.
+  *
+  * The .dai_ops functions are used to provide board-specific data, like
+@@ -388,7 +384,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
+ 	ratio = cs4270->mclk / rate;	/* MCLK/LRCK ratio */
+ 
+ 	for (i = 0; i < NUM_MCLK_RATIOS; i++) {
+-		if (mclk_ratios[i] == ratio)
++		if (cs4270_mode_ratios[i].ratio == ratio)
+ 			break;
+ 	}
+ 
+@@ -669,7 +665,7 @@ error:
+ 	return ret;
+ }
+ 
+-#endif
++#endif /* USE_I2C*/
+ 
+ struct snd_soc_codec_dai cs4270_dai = {
+ 	.name = "CS4270",
+@@ -687,10 +683,6 @@ struct snd_soc_codec_dai cs4270_dai = {
+ 		.rates = 0,
+ 		.formats = CS4270_FORMATS,
+ 	},
+-	.dai_ops = {
+-		.set_sysclk = cs4270_set_dai_sysclk,
+-		.set_fmt = cs4270_set_dai_fmt,
+-	}
+ };
+ EXPORT_SYMBOL_GPL(cs4270_dai);
+ 
+@@ -752,6 +744,8 @@ static int cs4270_probe(struct platform_device *pdev)
+ 	if (codec->control_data) {
+ 		/* Initialize codec ops */
+ 		cs4270_dai.ops.hw_params = cs4270_hw_params;
++		cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk;
++		cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt;
+ #ifdef CONFIG_SND_SOC_CS4270_HWMUTE
+ 		cs4270_dai.dai_ops.digital_mute = cs4270_mute;
+ #endif
+diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
+new file mode 100644
+index 0000000..710e028
+--- /dev/null
++++ b/sound/soc/codecs/tlv320aic3x.c
+@@ -0,0 +1,1274 @@
++/*
++ * ALSA SoC TLV320AIC3X codec driver
++ *
++ * Author:      Vladimir Barinov, <vbarinov at ru.mvista.com>
++ * Copyright:   (C) 2007 MontaVista Software, Inc., <source at mvista.com>
++ *
++ * Based on sound/soc/codecs/wm8753.c by Liam Girdwood
++ *
++ * 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.
++ *
++ * Notes:
++ *  The AIC3X is a driver for a low power stereo audio
++ *  codecs aic31, aic32, aic33.
++ *
++ *  It supports full aic33 codec functionality.
++ *  The compatibility with aic32, aic31 is as follows:
++ *        aic32        |        aic31
++ *  ---------------------------------------
++ *   MONO_LOUT -> N/A  |  MONO_LOUT -> N/A
++ *                     |  IN1L -> LINE1L
++ *                     |  IN1R -> LINE1R
++ *                     |  IN2L -> LINE2L
++ *                     |  IN2R -> LINE2R
++ *                     |  MIC3L/R -> N/A
++ *   truncated internal functionality in
++ *   accordance with documentation
++ *  ---------------------------------------
++ *
++ *  Hence the machine layer should disable unsupported inputs/outputs by
++ *  snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "tlv320aic3x.h"
++
++#define AUDIO_NAME "aic3x"
++#define AIC3X_VERSION "0.1"
++
++/* codec private data */
++struct aic3x_priv {
++	unsigned int sysclk;
++	int master;
++};
++
++/*
++ * AIC3X register cache
++ * We can't read the AIC3X register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ * There is no point in caching the reset register
++ */
++static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
++	0x00, 0x00, 0x00, 0x10,	/* 0 */
++	0x04, 0x00, 0x00, 0x00,	/* 4 */
++	0x00, 0x00, 0x00, 0x01,	/* 8 */
++	0x00, 0x00, 0x00, 0x80,	/* 12 */
++	0x80, 0xff, 0xff, 0x78,	/* 16 */
++	0x78, 0x78, 0x78, 0x78,	/* 20 */
++	0x78, 0x00, 0x00, 0xfe,	/* 24 */
++	0x00, 0x00, 0xfe, 0x00,	/* 28 */
++	0x18, 0x18, 0x00, 0x00,	/* 32 */
++	0x00, 0x00, 0x00, 0x00,	/* 36 */
++	0x00, 0x00, 0x00, 0x80,	/* 40 */
++	0x80, 0x00, 0x00, 0x00,	/* 44 */
++	0x00, 0x00, 0x00, 0x04,	/* 48 */
++	0x00, 0x00, 0x00, 0x00,	/* 52 */
++	0x00, 0x00, 0x04, 0x00,	/* 56 */
++	0x00, 0x00, 0x00, 0x00,	/* 60 */
++	0x00, 0x04, 0x00, 0x00,	/* 64 */
++	0x00, 0x00, 0x00, 0x00,	/* 68 */
++	0x04, 0x00, 0x00, 0x00,	/* 72 */
++	0x00, 0x00, 0x00, 0x00,	/* 76 */
++	0x00, 0x00, 0x00, 0x00,	/* 80 */
++	0x00, 0x00, 0x00, 0x00,	/* 84 */
++	0x00, 0x00, 0x00, 0x00,	/* 88 */
++	0x00, 0x00, 0x00, 0x00,	/* 92 */
++	0x00, 0x00, 0x00, 0x00,	/* 96 */
++	0x00, 0x00, 0x02,	/* 100 */
++};
++
++/*
++ * read aic3x register cache
++ */
++static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec,
++						unsigned int reg)
++{
++	u8 *cache = codec->reg_cache;
++	if (reg >= AIC3X_CACHEREGNUM)
++		return -1;
++	return cache[reg];
++}
++
++/*
++ * write aic3x register cache
++ */
++static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec,
++					 u8 reg, u8 value)
++{
++	u8 *cache = codec->reg_cache;
++	if (reg >= AIC3X_CACHEREGNUM)
++		return;
++	cache[reg] = value;
++}
++
++/*
++ * write to the aic3x register space
++ */
++static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
++		       unsigned int value)
++{
++	u8 data[2];
++
++	/* data is
++	 *   D15..D8 aic3x register offset
++	 *   D7...D0 register data
++	 */
++	data[0] = reg & 0xff;
++	data[1] = value & 0xff;
++
++	aic3x_write_reg_cache(codec, data[0], data[1]);
++	if (codec->hw_write(codec->control_data, data, 2) == 2)
++		return 0;
++	else
++		return -EIO;
++}
++
++#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
++{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
++	.info = snd_soc_info_volsw, \
++	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \
++	.private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
++
++/*
++ * All input lines are connected when !0xf and disconnected with 0xf bit field,
++ * so we have to use specific dapm_put call for input mixer
++ */
++static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
++					struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
++	int reg = kcontrol->private_value & 0xff;
++	int shift = (kcontrol->private_value >> 8) & 0x0f;
++	int mask = (kcontrol->private_value >> 16) & 0xff;
++	int invert = (kcontrol->private_value >> 24) & 0x01;
++	unsigned short val, val_mask;
++	int ret;
++	struct snd_soc_dapm_path *path;
++	int found = 0;
++
++	val = (ucontrol->value.integer.value[0] & mask);
++
++	mask = 0xf;
++	if (val)
++		val = mask;
++
++	if (invert)
++		val = mask - val;
++	val_mask = mask << shift;
++	val = val << shift;
++
++	mutex_lock(&widget->codec->mutex);
++
++	if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
++		/* find dapm widget path assoc with kcontrol */
++		list_for_each_entry(path, &widget->codec->dapm_paths, list) {
++			if (path->kcontrol != kcontrol)
++				continue;
++
++			/* found, now check type */
++			found = 1;
++			if (val)
++				/* new connection */
++				path->connect = invert ? 0 : 1;
++			else
++				/* old connection must be powered down */
++				path->connect = invert ? 1 : 0;
++			break;
++		}
++
++		if (found)
++			snd_soc_dapm_sync_endpoints(widget->codec);
++	}
++
++	ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
++
++	mutex_unlock(&widget->codec->mutex);
++	return ret;
++}
++
++static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" };
++static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" };
++static const char *aic3x_left_hpcom_mux[] =
++    { "differential of HPLOUT", "constant VCM", "single-ended" };
++static const char *aic3x_right_hpcom_mux[] =
++    { "differential of HPROUT", "constant VCM", "single-ended",
++      "differential of HPLCOM", "external feedback" };
++static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
++
++#define LDAC_ENUM	0
++#define RDAC_ENUM	1
++#define LHPCOM_ENUM	2
++#define RHPCOM_ENUM	3
++#define LINE1L_ENUM	4
++#define LINE1R_ENUM	5
++#define LINE2L_ENUM	6
++#define LINE2R_ENUM	7
++
++static const struct soc_enum aic3x_enum[] = {
++	SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
++	SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux),
++	SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),
++	SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),
++	SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
++	SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
++	SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
++	SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
++};
++
++static const struct snd_kcontrol_new aic3x_snd_controls[] = {
++	/* Output */
++	SOC_DOUBLE_R("PCM Playback Volume", LDAC_VOL, RDAC_VOL, 0, 0x7f, 1),
++
++	SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
++		     DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
++		     0x01, 0),
++	SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
++		     PGAR_2_RLOPM_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
++		     LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
++
++	SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
++		     DACR1_2_MONOLOPM_VOL, 0, 0x7f, 1),
++	SOC_SINGLE("Mono DAC Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
++	SOC_DOUBLE_R("Mono PGA Bypass Playback Volume", PGAL_2_MONOLOPM_VOL,
++		     PGAR_2_MONOLOPM_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("Mono Line2 Bypass Playback Volume", LINE2L_2_MONOLOPM_VOL,
++		     LINE2R_2_MONOLOPM_VOL, 0, 0x7f, 1),
++
++	SOC_DOUBLE_R("HP DAC Playback Volume", DACL1_2_HPLOUT_VOL,
++		     DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
++		     0x01, 0),
++	SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
++		     PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
++		     LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
++
++	SOC_DOUBLE_R("HPCOM DAC Playback Volume", DACL1_2_HPLCOM_VOL,
++		     DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
++		     0x01, 0),
++	SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
++		     PGAR_2_HPRCOM_VOL, 0, 0x7f, 1),
++	SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
++		     LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
++
++	/*
++	 * Note: enable Automatic input Gain Controller with care. It can
++	 * adjust PGA to max value when ADC is on and will never go back.
++	*/
++	SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
++
++	/* Input */
++	SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
++	SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
++};
++
++/* add non dapm controls */
++static int aic3x_add_controls(struct snd_soc_codec *codec)
++{
++	int err, i;
++
++	for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) {
++		err = snd_ctl_add(codec->card,
++				  snd_soc_cnew(&aic3x_snd_controls[i],
++					       codec, NULL));
++		if (err < 0)
++			return err;
++	}
++
++	return 0;
++}
++
++/* Left DAC Mux */
++static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
++
++/* Right DAC Mux */
++static const struct snd_kcontrol_new aic3x_right_dac_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]);
++
++/* Left HPCOM Mux */
++static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]);
++
++/* Right HPCOM Mux */
++static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
++
++/* Left DAC_L1 Mixer */
++static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
++	SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
++};
++
++/* Right DAC_R1 Mixer */
++static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
++	SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
++};
++
++/* Left PGA Mixer */
++static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
++	SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
++	SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
++	SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
++};
++
++/* Right PGA Mixer */
++static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
++	SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
++	SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
++	SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
++};
++
++/* Left Line1 Mux */
++static const struct snd_kcontrol_new aic3x_left_line1_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]);
++
++/* Right Line1 Mux */
++static const struct snd_kcontrol_new aic3x_right_line1_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]);
++
++/* Left Line2 Mux */
++static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]);
++
++/* Right Line2 Mux */
++static const struct snd_kcontrol_new aic3x_right_line2_mux_controls =
++SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
++
++/* Left PGA Bypass Mixer */
++static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
++	SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
++};
++
++/* Right PGA Bypass Mixer */
++static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
++	SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
++};
++
++/* Left Line2 Bypass Mixer */
++static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
++	SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
++};
++
++/* Right Line2 Bypass Mixer */
++static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
++	SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
++	SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
++};
++
++static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
++	/* Left DAC to Left Outputs */
++	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0),
++	SND_SOC_DAPM_MUX("Left DAC Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_left_dac_mux_controls),
++	SND_SOC_DAPM_MIXER("Left DAC_L1 Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_left_dac_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_left_dac_mixer_controls)),
++	SND_SOC_DAPM_MUX("Left HPCOM Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_left_hpcom_mux_controls),
++	SND_SOC_DAPM_PGA("Left Line Out", LLOPM_CTRL, 0, 0, NULL, 0),
++	SND_SOC_DAPM_PGA("Left HP Out", HPLOUT_CTRL, 0, 0, NULL, 0),
++	SND_SOC_DAPM_PGA("Left HP Com", HPLCOM_CTRL, 0, 0, NULL, 0),
++
++	/* Right DAC to Right Outputs */
++	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_PWR, 6, 0),
++	SND_SOC_DAPM_MUX("Right DAC Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_right_dac_mux_controls),
++	SND_SOC_DAPM_MIXER("Right DAC_R1 Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_right_dac_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_right_dac_mixer_controls)),
++	SND_SOC_DAPM_MUX("Right HPCOM Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_right_hpcom_mux_controls),
++	SND_SOC_DAPM_PGA("Right Line Out", RLOPM_CTRL, 0, 0, NULL, 0),
++	SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0),
++	SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0),
++
++	/* Mono Output */
++	SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
++
++	/* Left Inputs to Left ADC */
++	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
++	SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_left_pga_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
++	SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_left_line1_mux_controls),
++	SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_left_line2_mux_controls),
++
++	/* Right Inputs to Right ADC */
++	SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
++			 LINE1R_2_RADC_CTRL, 2, 0),
++	SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_right_pga_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
++	SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_right_line1_mux_controls),
++	SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
++			 &aic3x_right_line2_mux_controls),
++
++	/* Mic Bias */
++	SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0),
++	SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0),
++	SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0),
++	SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0),
++
++	/* Left PGA to Left Output bypass */
++	SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_left_pga_bp_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_left_pga_bp_mixer_controls)),
++
++	/* Right PGA to Right Output bypass */
++	SND_SOC_DAPM_MIXER("Right PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_right_pga_bp_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_right_pga_bp_mixer_controls)),
++
++	/* Left Line2 to Left Output bypass */
++	SND_SOC_DAPM_MIXER("Left Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_left_line2_bp_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_left_line2_bp_mixer_controls)),
++
++	/* Right Line2 to Right Output bypass */
++	SND_SOC_DAPM_MIXER("Right Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0,
++			   &aic3x_right_line2_bp_mixer_controls[0],
++			   ARRAY_SIZE(aic3x_right_line2_bp_mixer_controls)),
++
++	SND_SOC_DAPM_OUTPUT("LLOUT"),
++	SND_SOC_DAPM_OUTPUT("RLOUT"),
++	SND_SOC_DAPM_OUTPUT("MONO_LOUT"),
++	SND_SOC_DAPM_OUTPUT("HPLOUT"),
++	SND_SOC_DAPM_OUTPUT("HPROUT"),
++	SND_SOC_DAPM_OUTPUT("HPLCOM"),
++	SND_SOC_DAPM_OUTPUT("HPRCOM"),
++
++	SND_SOC_DAPM_INPUT("MIC3L"),
++	SND_SOC_DAPM_INPUT("MIC3R"),
++	SND_SOC_DAPM_INPUT("LINE1L"),
++	SND_SOC_DAPM_INPUT("LINE1R"),
++	SND_SOC_DAPM_INPUT("LINE2L"),
++	SND_SOC_DAPM_INPUT("LINE2R"),
++};
++
++static const char *intercon[][3] = {
++	/* Left Output */
++	{"Left DAC Mux", "DAC_L1", "Left DAC"},
++	{"Left DAC Mux", "DAC_L2", "Left DAC"},
++	{"Left DAC Mux", "DAC_L3", "Left DAC"},
++
++	{"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"},
++	{"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
++	{"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
++	{"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
++	{"Left Line Out", NULL, "Left DAC Mux"},
++	{"Left HP Out", NULL, "Left DAC Mux"},
++
++	{"Left HPCOM Mux", "differential of HPLOUT", "Left DAC_L1 Mixer"},
++	{"Left HPCOM Mux", "constant VCM", "Left DAC_L1 Mixer"},
++	{"Left HPCOM Mux", "single-ended", "Left DAC_L1 Mixer"},
++
++	{"Left Line Out", NULL, "Left DAC_L1 Mixer"},
++	{"Mono Out", NULL, "Left DAC_L1 Mixer"},
++	{"Left HP Out", NULL, "Left DAC_L1 Mixer"},
++	{"Left HP Com", NULL, "Left HPCOM Mux"},
++
++	{"LLOUT", NULL, "Left Line Out"},
++	{"LLOUT", NULL, "Left Line Out"},
++	{"HPLOUT", NULL, "Left HP Out"},
++	{"HPLCOM", NULL, "Left HP Com"},
++
++	/* Right Output */
++	{"Right DAC Mux", "DAC_R1", "Right DAC"},
++	{"Right DAC Mux", "DAC_R2", "Right DAC"},
++	{"Right DAC Mux", "DAC_R3", "Right DAC"},
++
++	{"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"},
++	{"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
++	{"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
++	{"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
++	{"Right Line Out", NULL, "Right DAC Mux"},
++	{"Right HP Out", NULL, "Right DAC Mux"},
++
++	{"Right HPCOM Mux", "differential of HPROUT", "Right DAC_R1 Mixer"},
++	{"Right HPCOM Mux", "constant VCM", "Right DAC_R1 Mixer"},
++	{"Right HPCOM Mux", "single-ended", "Right DAC_R1 Mixer"},
++	{"Right HPCOM Mux", "differential of HPLCOM", "Right DAC_R1 Mixer"},
++	{"Right HPCOM Mux", "external feedback", "Right DAC_R1 Mixer"},
++
++	{"Right Line Out", NULL, "Right DAC_R1 Mixer"},
++	{"Mono Out", NULL, "Right DAC_R1 Mixer"},
++	{"Right HP Out", NULL, "Right DAC_R1 Mixer"},
++	{"Right HP Com", NULL, "Right HPCOM Mux"},
++
++	{"RLOUT", NULL, "Right Line Out"},
++	{"RLOUT", NULL, "Right Line Out"},
++	{"HPROUT", NULL, "Right HP Out"},
++	{"HPRCOM", NULL, "Right HP Com"},
++
++	/* Mono Output */
++	{"MONOLOUT", NULL, "Mono Out"},
++	{"MONOLOUT", NULL, "Mono Out"},
++
++	/* Left Input */
++	{"Left Line1L Mux", "single-ended", "LINE1L"},
++	{"Left Line1L Mux", "differential", "LINE1L"},
++
++	{"Left Line2L Mux", "single-ended", "LINE2L"},
++	{"Left Line2L Mux", "differential", "LINE2L"},
++
++	{"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
++	{"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
++	{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
++
++	{"Left ADC", NULL, "Left PGA Mixer"},
++
++	/* Right Input */
++	{"Right Line1R Mux", "single-ended", "LINE1R"},
++	{"Right Line1R Mux", "differential", "LINE1R"},
++
++	{"Right Line2R Mux", "single-ended", "LINE2R"},
++	{"Right Line2R Mux", "differential", "LINE2R"},
++
++	{"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
++	{"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
++	{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
++
++	{"Right ADC", NULL, "Right PGA Mixer"},
++
++	/* Left PGA Bypass */
++	{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
++	{"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
++	{"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"},
++	{"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"},
++
++	{"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
++	{"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
++	{"Left HPCOM Mux", "single-ended", "Left PGA Bypass Mixer"},
++
++	{"Left Line Out", NULL, "Left PGA Bypass Mixer"},
++	{"Mono Out", NULL, "Left PGA Bypass Mixer"},
++	{"Left HP Out", NULL, "Left PGA Bypass Mixer"},
++
++	/* Right PGA Bypass */
++	{"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"},
++	{"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
++	{"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"},
++	{"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"},
++
++	{"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
++	{"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
++	{"Right HPCOM Mux", "single-ended", "Right PGA Bypass Mixer"},
++	{"Right HPCOM Mux", "differential of HPLCOM", "Right PGA Bypass Mixer"},
++	{"Right HPCOM Mux", "external feedback", "Right PGA Bypass Mixer"},
++
++	{"Right Line Out", NULL, "Right PGA Bypass Mixer"},
++	{"Mono Out", NULL, "Right PGA Bypass Mixer"},
++	{"Right HP Out", NULL, "Right PGA Bypass Mixer"},
++
++	/* Left Line2 Bypass */
++	{"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"},
++	{"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
++	{"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
++	{"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"},
++
++	{"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
++	{"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
++	{"Left HPCOM Mux", "single-ended", "Left Line2 Bypass Mixer"},
++
++	{"Left Line Out", NULL, "Left Line2 Bypass Mixer"},
++	{"Mono Out", NULL, "Left Line2 Bypass Mixer"},
++	{"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
++
++	/* Right Line2 Bypass */
++	{"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"},
++	{"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
++	{"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
++	{"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"},
++
++	{"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
++	{"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
++	{"Right HPCOM Mux", "single-ended", "Right Line2 Bypass Mixer"},
++	{"Right HPCOM Mux", "differential of HPLCOM", "Right Line2 Bypass Mixer"},
++	{"Right HPCOM Mux", "external feedback", "Right Line2 Bypass Mixer"},
++
++	{"Right Line Out", NULL, "Right Line2 Bypass Mixer"},
++	{"Mono Out", NULL, "Right Line2 Bypass Mixer"},
++	{"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
++
++	/* terminator */
++	{NULL, NULL, NULL},
++};
++
++static int aic3x_add_widgets(struct snd_soc_codec *codec)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
++		snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
++
++	/* set up audio path interconnects */
++	for (i = 0; intercon[i][0] != NULL; i++)
++		snd_soc_dapm_connect_input(codec, intercon[i][0],
++					   intercon[i][1], intercon[i][2]);
++
++	snd_soc_dapm_new_widgets(codec);
++	return 0;
++}
++
++struct aic3x_rate_divs {
++	u32 mclk;
++	u32 rate;
++	u32 fsref_reg;
++	u8 sr_reg:4;
++	u8 pllj_reg;
++	u16 plld_reg;
++};
++
++/* AIC3X codec mclk clock divider coefficients */
++static const struct aic3x_rate_divs aic3x_divs[] = {
++	/* 8k */
++	{22579200, 8000, 48000, 0xa, 8, 7075},
++	{33868800, 8000, 48000, 0xa, 5, 8049},
++	/* 11.025k */
++	{22579200, 11025, 44100, 0x6, 8, 0},
++	{33868800, 11025, 44100, 0x6, 5, 3333},
++	/* 16k */
++	{22579200, 16000, 48000, 0x4, 8, 7075},
++	{33868800, 16000, 48000, 0x4, 5, 8049},
++	/* 22.05k */
++	{22579200, 22050, 44100, 0x2, 8, 0},
++	{33868800, 22050, 44100, 0x2, 5, 3333},
++	/* 32k */
++	{22579200, 32000, 48000, 0x1, 8, 7075},
++	{33868800, 32000, 48000, 0x1, 5, 8049},
++	/* 44.1k */
++	{22579200, 44100, 44100, 0x0, 8, 0},
++	{33868800, 44100, 44100, 0x0, 5, 3333},
++	/* 48k */
++	{22579200, 48000, 48000, 0x0, 8, 7075},
++	{33868800, 48000, 48000, 0x0, 5, 8049},
++	/* 64k */
++	{22579200, 96000, 96000, 0x1, 8, 7075},
++	{33868800, 96000, 96000, 0x1, 5, 8049},
++	/* 88.2k */
++	{22579200, 88200, 88200, 0x0, 8, 0},
++	{33868800, 88200, 88200, 0x0, 5, 3333},
++	/* 96k */
++	{22579200, 96000, 96000, 0x0, 8, 7075},
++	{33868800, 96000, 96000, 0x0, 5, 8049},
++};
++
++static inline int aic3x_get_divs(int mclk, int rate)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) {
++		if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk)
++			return i;
++	}
++
++	return 0;
++}
++
++static int aic3x_hw_params(struct snd_pcm_substream *substream,
++			   struct snd_pcm_hw_params *params)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_device *socdev = rtd->socdev;
++	struct snd_soc_codec *codec = socdev->codec;
++	struct aic3x_priv *aic3x = codec->private_data;
++	int i;
++	u8 data, pll_p, pll_r, pll_j;
++	u16 pll_d;
++
++	i = aic3x_get_divs(aic3x->sysclk, params_rate(params));
++
++	/* Route Left DAC to left channel input and
++	 * right DAC to right channel input */
++	data = (LDAC2LCH | RDAC2RCH);
++	switch (aic3x_divs[i].fsref_reg) {
++	case 44100:
++		data |= FSREF_44100;
++		break;
++	case 48000:
++		data |= FSREF_48000;
++		break;
++	case 88200:
++		data |= FSREF_44100 | DUAL_RATE_MODE;
++		break;
++	case 96000:
++		data |= FSREF_48000 | DUAL_RATE_MODE;
++		break;
++	}
++	aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
++
++	/* codec sample rate select */
++	data = aic3x_divs[i].sr_reg;
++	data |= (data << 4);
++	aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
++
++	/* Use PLL for generation Fsref by equation:
++	 * Fsref = (MCLK * K * R)/(2048 * P);
++	 * Fix P = 2 and R = 1 and calculate K, if
++	 * K = J.D, i.e. J - an interger portion of K and D is the fractional
++	 * one with 4 digits of precision;
++	 * Example:
++	 * For MCLK = 22.5792 MHz and Fsref = 48kHz:
++	 * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074
++	 */
++	pll_p = 2;
++	pll_r = 1;
++	pll_j = aic3x_divs[i].pllj_reg;
++	pll_d = aic3x_divs[i].plld_reg;
++
++	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
++	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
++	aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
++	aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
++	aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
++	aic3x_write(codec, AIC3X_PLL_PROGD_REG,
++		    (pll_d & 0x3F) << PLLD_LSB_SHIFT);
++
++	/* select data word length */
++	data =
++	    aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
++	switch (params_format(params)) {
++	case SNDRV_PCM_FORMAT_S16_LE:
++		break;
++	case SNDRV_PCM_FORMAT_S20_3LE:
++		data |= (0x01 << 4);
++		break;
++	case SNDRV_PCM_FORMAT_S24_LE:
++		data |= (0x02 << 4);
++		break;
++	case SNDRV_PCM_FORMAT_S32_LE:
++		data |= (0x03 << 4);
++		break;
++	}
++	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
++
++	return 0;
++}
++
++static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++	struct snd_soc_codec *codec = dai->codec;
++	u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
++	u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON;
++
++	if (mute) {
++		aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON);
++		aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON);
++	} else {
++		aic3x_write(codec, LDAC_VOL, ldac_reg);
++		aic3x_write(codec, RDAC_VOL, rdac_reg);
++	}
++
++	return 0;
++}
++
++static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
++				int clk_id, unsigned int freq, int dir)
++{
++	struct snd_soc_codec *codec = codec_dai->codec;
++	struct aic3x_priv *aic3x = codec->private_data;
++
++	switch (freq) {
++	case 22579200:
++	case 33868800:
++		aic3x->sysclk = freq;
++		return 0;
++	}
++
++	return -EINVAL;
++}
++
++static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++			     unsigned int fmt)
++{
++	struct snd_soc_codec *codec = codec_dai->codec;
++	struct aic3x_priv *aic3x = codec->private_data;
++	u8 iface_areg = 0;
++	u8 iface_breg = 0;
++
++	/* set master/slave audio interface */
++	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++	case SND_SOC_DAIFMT_CBM_CFM:
++		aic3x->master = 1;
++		iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER;
++		break;
++	case SND_SOC_DAIFMT_CBS_CFS:
++		aic3x->master = 0;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	/* interface format */
++	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++	case SND_SOC_DAIFMT_I2S:
++		break;
++	case SND_SOC_DAIFMT_DSP_A:
++		iface_breg |= (0x01 << 6);
++		break;
++	case SND_SOC_DAIFMT_RIGHT_J:
++		iface_breg |= (0x02 << 6);
++		break;
++	case SND_SOC_DAIFMT_LEFT_J:
++		iface_breg |= (0x03 << 6);
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	/* set iface */
++	aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
++	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
++
++	return 0;
++}
++
++static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
++{
++	struct aic3x_priv *aic3x = codec->private_data;
++	u8 reg;
++
++	switch (event) {
++	case SNDRV_CTL_POWER_D0:
++		/* all power is driven by DAPM system */
++		if (aic3x->master) {
++			/* enable pll */
++			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
++			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
++				    reg | PLL_ENABLE);
++		}
++		break;
++	case SNDRV_CTL_POWER_D1:
++	case SNDRV_CTL_POWER_D2:
++		break;
++	case SNDRV_CTL_POWER_D3hot:
++		/*
++		 * all power is driven by DAPM system,
++		 * so output power is safe if bypass was set
++		 */
++		if (aic3x->master) {
++			/* disable pll */
++			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
++			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
++				    reg & ~PLL_ENABLE);
++		}
++		break;
++	case SNDRV_CTL_POWER_D3cold:
++		/* force all power off */
++		reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
++		aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
++		reg = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL);
++		aic3x_write(codec, LINE1R_2_RADC_CTRL, reg & ~RADC_PWR_ON);
++
++		reg = aic3x_read_reg_cache(codec, DAC_PWR);
++		aic3x_write(codec, DAC_PWR, reg & ~(LDAC_PWR_ON | RDAC_PWR_ON));
++
++		reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
++		aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON);
++		reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
++		aic3x_write(codec, HPROUT_CTRL, reg & ~HPROUT_PWR_ON);
++
++		reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
++		aic3x_write(codec, HPLCOM_CTRL, reg & ~HPLCOM_PWR_ON);
++		reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
++		aic3x_write(codec, HPRCOM_CTRL, reg & ~HPRCOM_PWR_ON);
++
++		reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
++		aic3x_write(codec, MONOLOPM_CTRL, reg & ~MONOLOPM_PWR_ON);
++
++		reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
++		aic3x_write(codec, LLOPM_CTRL, reg & ~LLOPM_PWR_ON);
++		reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
++		aic3x_write(codec, RLOPM_CTRL, reg & ~RLOPM_PWR_ON);
++
++		if (aic3x->master) {
++			/* disable pll */
++			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
++			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
++				    reg & ~PLL_ENABLE);
++		}
++		break;
++	}
++	codec->dapm_state = event;
++
++	return 0;
++}
++
++#define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
++#define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
++
++struct snd_soc_codec_dai aic3x_dai = {
++	.name = "aic3x",
++	.playback = {
++		.stream_name = "Playback",
++		.channels_min = 1,
++		.channels_max = 2,
++		.rates = AIC3X_RATES,
++		.formats = AIC3X_FORMATS,},
++	.capture = {
++		.stream_name = "Capture",
++		.channels_min = 1,
++		.channels_max = 2,
++		.rates = AIC3X_RATES,
++		.formats = AIC3X_FORMATS,},
++	.ops = {
++		.hw_params = aic3x_hw_params,
++	},
++	.dai_ops = {
++		.digital_mute = aic3x_mute,
++		.set_sysclk = aic3x_set_dai_sysclk,
++		.set_fmt = aic3x_set_dai_fmt,
++	}
++};
++EXPORT_SYMBOL_GPL(aic3x_dai);
++
++static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
++{
++	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++	struct snd_soc_codec *codec = socdev->codec;
++
++	aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++	return 0;
++}
++
++static int aic3x_resume(struct platform_device *pdev)
++{
++	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++	struct snd_soc_codec *codec = socdev->codec;
++	int i;
++	u8 data[2];
++	u8 *cache = codec->reg_cache;
++
++	/* Sync reg_cache with the hardware */
++	for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) {
++		data[0] = i;
++		data[1] = cache[i];
++		codec->hw_write(codec->control_data, data, 2);
++	}
++
++	aic3x_dapm_event(codec, codec->suspend_dapm_state);
++
++	return 0;
++}
++
++/*
++ * initialise the AIC3X driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int aic3x_init(struct snd_soc_device *socdev)
++{
++	struct snd_soc_codec *codec = socdev->codec;
++	int reg, ret = 0;
++
++	codec->name = "aic3x";
++	codec->owner = THIS_MODULE;
++	codec->read = aic3x_read_reg_cache;
++	codec->write = aic3x_write;
++	codec->dapm_event = aic3x_dapm_event;
++	codec->dai = &aic3x_dai;
++	codec->num_dai = 1;
++	codec->reg_cache_size = sizeof(aic3x_reg);
++	codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
++	if (codec->reg_cache == NULL)
++		return -ENOMEM;
++
++	aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
++	aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
++
++	/* register pcms */
++	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++	if (ret < 0) {
++		printk(KERN_ERR "aic3x: failed to create pcms\n");
++		goto pcm_err;
++	}
++
++	/* DAC default volume and mute */
++	aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
++	aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
++
++	/* DAC to HP default volume and route to Output mixer */
++	aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON);
++	aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON);
++	aic3x_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON);
++	aic3x_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON);
++	/* DAC to Line Out default volume and route to Output mixer */
++	aic3x_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
++	aic3x_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
++	/* DAC to Mono Line Out default volume and route to Output mixer */
++	aic3x_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
++	aic3x_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
++
++	/* unmute all outputs */
++	reg = aic3x_read_reg_cache(codec, LLOPM_CTRL);
++	aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE);
++	reg = aic3x_read_reg_cache(codec, RLOPM_CTRL);
++	aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE);
++	reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL);
++	aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE);
++	reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
++	aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE);
++	reg = aic3x_read_reg_cache(codec, HPROUT_CTRL);
++	aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE);
++	reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL);
++	aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE);
++	reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL);
++	aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE);
++
++	/* ADC default volume and unmute */
++	aic3x_write(codec, LADC_VOL, DEFAULT_GAIN);
++	aic3x_write(codec, RADC_VOL, DEFAULT_GAIN);
++	/* By default route Line1 to ADC PGA mixer */
++	aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0);
++	aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0);
++
++	/* PGA to HP Bypass default volume, disconnect from Output Mixer */
++	aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL);
++	aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL);
++	aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL);
++	aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL);
++	/* PGA to Line Out default volume, disconnect from Output Mixer */
++	aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
++	aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
++	/* PGA to Mono Line Out default volume, disconnect from Output Mixer */
++	aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
++	aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
++
++	/* Line2 to HP Bypass default volume, disconnect from Output Mixer */
++	aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
++	aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL);
++	aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL);
++	aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL);
++	/* Line2 Line Out default volume, disconnect from Output Mixer */
++	aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
++	aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
++	/* Line2 to Mono Out default volume, disconnect from Output Mixer */
++	aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
++	aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
++
++	/* off, with power on */
++	aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++	aic3x_add_controls(codec);
++	aic3x_add_widgets(codec);
++	ret = snd_soc_register_card(socdev);
++	if (ret < 0) {
++		printk(KERN_ERR "aic3x: failed to register card\n");
++		goto card_err;
++	}
++
++	return ret;
++
++card_err:
++	snd_soc_free_pcms(socdev);
++	snd_soc_dapm_free(socdev);
++pcm_err:
++	kfree(codec->reg_cache);
++	return ret;
++}
++
++static struct snd_soc_device *aic3x_socdev;
++
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++/*
++ * AIC3X 2 wire address can be up to 4 devices with device addresses
++ * 0x18, 0x19, 0x1A, 0x1B
++ */
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver aic3x_i2c_driver;
++static struct i2c_client client_template;
++
++/*
++ * If the i2c layer weren't so broken, we could pass this kind of data
++ * around
++ */
++static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++	struct snd_soc_device *socdev = aic3x_socdev;
++	struct aic3x_setup_data *setup = socdev->codec_data;
++	struct snd_soc_codec *codec = socdev->codec;
++	struct i2c_client *i2c;
++	int ret;
++
++	if (addr != setup->i2c_address)
++		return -ENODEV;
++
++	client_template.adapter = adap;
++	client_template.addr = addr;
++
++	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
++	if (i2c == NULL) {
++		kfree(codec);
++		return -ENOMEM;
++	}
++	i2c_set_clientdata(i2c, codec);
++	codec->control_data = i2c;
++
++	ret = i2c_attach_client(i2c);
++	if (ret < 0) {
++		printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n",
++		       addr);
++		goto err;
++	}
++
++	ret = aic3x_init(socdev);
++	if (ret < 0) {
++		printk(KERN_ERR "aic3x: failed to initialise AIC3X\n");
++		goto err;
++	}
++	return ret;
++
++err:
++	kfree(codec);
++	kfree(i2c);
++	return ret;
++}
++
++static int aic3x_i2c_detach(struct i2c_client *client)
++{
++	struct snd_soc_codec *codec = i2c_get_clientdata(client);
++	i2c_detach_client(client);
++	kfree(codec->reg_cache);
++	kfree(client);
++	return 0;
++}
++
++static int aic3x_i2c_attach(struct i2c_adapter *adap)
++{
++	return i2c_probe(adap, &addr_data, aic3x_codec_probe);
++}
++
++/* machine i2c codec control layer */
++static struct i2c_driver aic3x_i2c_driver = {
++	.driver = {
++		.name = "aic3x I2C Codec",
++		.owner = THIS_MODULE,
++	},
++	.id = I2C_DRIVERID_I2CDEV,
++	.attach_adapter = aic3x_i2c_attach,
++	.detach_client = aic3x_i2c_detach,
++	.command = NULL,
++};
++
++static struct i2c_client client_template = {
++	.name = "AIC3X",
++	.driver = &aic3x_i2c_driver,
++};
++#endif
++
++static int aic3x_probe(struct platform_device *pdev)
++{
++	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++	struct aic3x_setup_data *setup;
++	struct snd_soc_codec *codec;
++	struct aic3x_priv *aic3x;
++	int ret = 0;
++
++	printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION);
++
++	setup = socdev->codec_data;
++	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++	if (codec == NULL)
++		return -ENOMEM;
++
++	aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
++	if (aic3x == NULL) {
++		kfree(codec);
++		return -ENOMEM;
++	}
++
++	codec->private_data = aic3x;
++	socdev->codec = codec;
++	mutex_init(&codec->mutex);
++	INIT_LIST_HEAD(&codec->dapm_widgets);
++	INIT_LIST_HEAD(&codec->dapm_paths);
++
++	aic3x_socdev = socdev;
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++	if (setup->i2c_address) {
++		normal_i2c[0] = setup->i2c_address;
++		codec->hw_write = (hw_write_t) i2c_master_send;
++		ret = i2c_add_driver(&aic3x_i2c_driver);
++		if (ret != 0)
++			printk(KERN_ERR "can't add i2c driver");
++	}
++#else
++	/* Add other interfaces here */
++#endif
++	return ret;
++}
++
++static int aic3x_remove(struct platform_device *pdev)
++{
++	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++	struct snd_soc_codec *codec = socdev->codec;
++
++	/* power down chip */
++	if (codec->control_data)
++		aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
++
++	snd_soc_free_pcms(socdev);
++	snd_soc_dapm_free(socdev);
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++	i2c_del_driver(&aic3x_i2c_driver);
++#endif
++	kfree(codec->private_data);
++	kfree(codec);
++
++	return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_aic3x = {
++	.probe = aic3x_probe,
++	.remove = aic3x_remove,
++	.suspend = aic3x_suspend,
++	.resume = aic3x_resume,
++};
++EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
++
++MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
++MODULE_AUTHOR("Vladimir Barinov");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
+new file mode 100644
+index 0000000..d0cdeeb
+--- /dev/null
++++ b/sound/soc/codecs/tlv320aic3x.h
+@@ -0,0 +1,181 @@
++/*
++ * ALSA SoC TLV320AIC3X codec driver
++ *
++ * Author:      Vladimir Barinov, <vbarinov at ru.mvista.com>
++ * Copyright:   (C) 2007 MontaVista Software, Inc., <source at mvista.com>
++ *
++ * 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 _AIC3X_H
++#define _AIC3X_H
++
++/* AIC3X register space */
++#define AIC3X_CACHEREGNUM		103
++
++/* Page select register */
++#define AIC3X_PAGE_SELECT		0
++/* Software reset register */
++#define AIC3X_RESET			1
++/* Codec Sample rate select register */
++#define AIC3X_SAMPLE_RATE_SEL_REG	2
++/* PLL progrramming register A */
++#define AIC3X_PLL_PROGA_REG		3
++/* PLL progrramming register B */
++#define AIC3X_PLL_PROGB_REG		4
++/* PLL progrramming register C */
++#define AIC3X_PLL_PROGC_REG		5
++/* PLL progrramming register D */
++#define AIC3X_PLL_PROGD_REG		6
++/* Codec datapath setup register */
++#define AIC3X_CODEC_DATAPATH_REG	7
++/* Audio serial data interface control register A */
++#define AIC3X_ASD_INTF_CTRLA		8
++/* Audio serial data interface control register B */
++#define AIC3X_ASD_INTF_CTRLB		9
++/* Audio overflow status and PLL R value programming register */
++#define AIC3X_OVRF_STATUS_AND_PLLR_REG	11
++
++/* ADC PGA Gain control registers */
++#define LADC_VOL			15
++#define RADC_VOL			16
++/* MIC3 control registers */
++#define MIC3LR_2_LADC_CTRL		17
++#define MIC3LR_2_RADC_CTRL		18
++/* Line1 Input control registers */
++#define LINE1L_2_LADC_CTRL		19
++#define LINE1R_2_RADC_CTRL		22
++/* Line2 Input control registers */
++#define LINE2L_2_LADC_CTRL		20
++#define LINE2R_2_RADC_CTRL		23
++/* MICBIAS Control Register */
++#define MICBIAS_CTRL			25
++
++/* AGC Control Registers A, B, C */
++#define LAGC_CTRL_A			26
++#define LAGC_CTRL_B			27
++#define LAGC_CTRL_C			28
++#define RAGC_CTRL_A			29
++#define RAGC_CTRL_B			30
++#define RAGC_CTRL_C			31
++
++/* DAC Power and Left High Power Output control registers */
++#define DAC_PWR				37
++#define HPLCOM_CFG			37
++/* Right High Power Output control registers */
++#define HPRCOM_CFG			38
++/* DAC Output Switching control registers */
++#define DAC_LINE_MUX			41
++/* High Power Output Driver Pop Reduction registers */
++#define HPOUT_POP_REDUCTION		42
++/* DAC Digital control registers */
++#define LDAC_VOL			43
++#define RDAC_VOL			44
++/* High Power Output control registers */
++#define LINE2L_2_HPLOUT_VOL		45
++#define LINE2R_2_HPROUT_VOL		62
++#define PGAL_2_HPLOUT_VOL		46
++#define PGAR_2_HPROUT_VOL		63
++#define DACL1_2_HPLOUT_VOL		47
++#define DACR1_2_HPROUT_VOL		64
++#define HPLOUT_CTRL			51
++#define HPROUT_CTRL			65
++/* High Power COM control registers */
++#define LINE2L_2_HPLCOM_VOL		52
++#define LINE2R_2_HPRCOM_VOL		69
++#define PGAL_2_HPLCOM_VOL		53
++#define PGAR_2_HPRCOM_VOL		70
++#define DACL1_2_HPLCOM_VOL		54
++#define DACR1_2_HPRCOM_VOL		71
++#define HPLCOM_CTRL			58
++#define HPRCOM_CTRL			72
++/* Mono Line Output Plus/Minus control registers */
++#define LINE2L_2_MONOLOPM_VOL		73
++#define LINE2R_2_MONOLOPM_VOL		76
++#define PGAL_2_MONOLOPM_VOL		74
++#define PGAR_2_MONOLOPM_VOL		77
++#define DACL1_2_MONOLOPM_VOL		75
++#define DACR1_2_MONOLOPM_VOL		78
++#define MONOLOPM_CTRL			79
++/* Line Output Plus/Minus control registers */
++#define LINE2L_2_LLOPM_VOL		80
++#define LINE2R_2_RLOPM_VOL		90
++#define PGAL_2_LLOPM_VOL		81
++#define PGAR_2_RLOPM_VOL		91
++#define DACL1_2_LLOPM_VOL		82
++#define DACR1_2_RLOPM_VOL		92
++#define LLOPM_CTRL			86
++#define RLOPM_CTRL			93
++/* Clock generation control register */
++#define AIC3X_CLKGEN_CTRL_REG		102
++
++/* Page select register bits */
++#define PAGE0_SELECT		0
++#define PAGE1_SELECT		1
++
++/* Audio serial data interface control register A bits */
++#define BIT_CLK_MASTER          0x80
++#define WORD_CLK_MASTER         0x40
++
++/* Codec Datapath setup register 7 */
++#define FSREF_44100		(1 << 7)
++#define FSREF_48000		(0 << 7)
++#define DUAL_RATE_MODE		((1 << 5) | (1 << 6))
++#define LDAC2LCH		(0x1 << 3)
++#define RDAC2RCH		(0x1 << 1)
++
++/* PLL registers bitfields */
++#define PLLP_SHIFT		0
++#define PLLR_SHIFT		0
++#define PLLJ_SHIFT		2
++#define PLLD_MSB_SHIFT		0
++#define PLLD_LSB_SHIFT		2
++
++/* Clock generation register bits */
++#define PLL_CLKIN_SHIFT		4
++#define MCLK_SOURCE		0x0
++#define PLL_CLKDIV_SHIFT	0
++
++/* Software reset register bits */
++#define SOFT_RESET		0x80
++
++/* PLL progrramming register A bits */
++#define PLL_ENABLE		0x80
++
++/* Route bits */
++#define ROUTE_ON		0x80
++
++/* Mute bits */
++#define UNMUTE			0x08
++#define MUTE_ON			0x80
++
++/* Power bits */
++#define LADC_PWR_ON		0x04
++#define RADC_PWR_ON		0x04
++#define LDAC_PWR_ON		0x80
++#define RDAC_PWR_ON		0x40
++#define HPLOUT_PWR_ON		0x01
++#define HPROUT_PWR_ON		0x01
++#define HPLCOM_PWR_ON		0x01
++#define HPRCOM_PWR_ON		0x01
++#define MONOLOPM_PWR_ON		0x01
++#define LLOPM_PWR_ON		0x01
++#define RLOPM_PWR_ON	0x01
++
++#define INVERT_VOL(val)   (0x7f - val)
++
++/* Default output volume (inverted) */
++#define DEFAULT_VOL     INVERT_VOL(0x50)
++/* Default input volume */
++#define DEFAULT_GAIN    0x20
++
++struct aic3x_setup_data {
++	unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai aic3x_dai;
++extern struct snd_soc_codec_device soc_codec_dev_aic3x;
++
++#endif /* _AIC3X_H */
+diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
+index 7ca0b52..9c33fe8 100644
+--- a/sound/soc/codecs/wm8731.c
++++ b/sound/soc/codecs/wm8731.c
+@@ -19,7 +19,6 @@
+ #include <linux/pm.h>
+ #include <linux/i2c.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -562,13 +561,13 @@ static int wm8731_init(struct snd_soc_device *socdev)
+ 
+ 	/* set the update bits */
+ 	reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
+-	wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100);
++	wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
+ 	reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
+-	wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100);
++	wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
+ 	reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
+-	wm8731_write(codec, WM8731_LINVOL, reg | 0x0100);
++	wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
+ 	reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
+-	wm8731_write(codec, WM8731_RINVOL, reg | 0x0100);
++	wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
+ 
+ 	wm8731_add_controls(codec);
+ 	wm8731_add_widgets(codec);
+diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
+index 28684ee..77a857b 100644
+--- a/sound/soc/codecs/wm8750.c
++++ b/sound/soc/codecs/wm8750.c
+@@ -19,7 +19,6 @@
+ #include <linux/pm.h>
+ #include <linux/i2c.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -189,7 +188,7 @@ SOC_ENUM("Bass Boost", wm8750_enum[0]),
+ SOC_ENUM("Bass Filter", wm8750_enum[1]),
+ SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
+ 
+-SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0),
++SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1),
+ SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
+ 
+ SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
+diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
+index efced93..ddd9c71 100644
+--- a/sound/soc/codecs/wm8753.c
++++ b/sound/soc/codecs/wm8753.c
+@@ -41,13 +41,13 @@
+ #include <linux/pm.h>
+ #include <linux/i2c.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+ #include <sound/soc.h>
+ #include <sound/soc-dapm.h>
+ #include <sound/initval.h>
++#include <sound/tlv.h>
+ #include <asm/div64.h>
+ 
+ #include "wm8753.h"
+@@ -258,6 +258,8 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
+ 	return 1;
+ }
+ 
++static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
++
+ static const struct snd_kcontrol_new wm8753_snd_controls[] = {
+ SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
+ 
+@@ -287,8 +289,8 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
+ SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
+ SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
+ 
+-SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1),
+-SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),
++SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv),
++SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv),
+ 
+ SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
+ SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
+diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
+index 986b5d5..590baea 100644
+--- a/sound/soc/codecs/wm9712.c
++++ b/sound/soc/codecs/wm9712.c
+@@ -19,7 +19,6 @@
+ #include <linux/version.h>
+ #include <linux/kernel.h>
+ #include <linux/device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/ac97_codec.h>
+@@ -102,7 +101,8 @@ SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
+ SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
+ SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
+ SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
+-SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0),
++SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
++SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+ 
+ SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
+ SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
+@@ -131,7 +131,7 @@ SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
+ SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
+ SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
+ 
+-SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0),
++SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
+ SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
+ 
+ SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
+@@ -145,8 +145,8 @@ SOC_ENUM("Bass Control", wm9712_enum[5]),
+ SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
+ SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
+ SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
+-SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0),
+-SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0),
++SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
++SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
+ 
+ SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
+ SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
+diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
+new file mode 100644
+index 0000000..257101f
+--- /dev/null
++++ b/sound/soc/fsl/Kconfig
+@@ -0,0 +1,20 @@
++menu "ALSA SoC audio for Freescale SOCs"
++
++config SND_SOC_MPC8610
++	bool "ALSA SoC support for the MPC8610 SOC"
++	depends on SND_SOC && MPC8610_HPCD
++	default y if MPC8610
++	help
++	  Say Y if you want to add support for codecs attached to the SSI
++          device on an MPC8610.
++
++config SND_SOC_MPC8610_HPCD
++	bool "ALSA SoC support for the Freescale MPC8610 HPCD board"
++	depends on SND_SOC_MPC8610
++	select SND_SOC_CS4270
++	select SND_SOC_CS4270_VD33_ERRATA
++	default y if MPC8610_HPCD
++	help
++	  Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
++
++endmenu
+diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
+new file mode 100644
+index 0000000..62f680a
+--- /dev/null
++++ b/sound/soc/fsl/Makefile
+@@ -0,0 +1,6 @@
++# MPC8610 HPCD Machine Support
++obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
++
++# MPC8610 Platform Support
++obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
++
+diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
+new file mode 100644
+index 0000000..652514f
+--- /dev/null
++++ b/sound/soc/fsl/fsl_dma.c
+@@ -0,0 +1,841 @@
++/*
++ * Freescale DMA ALSA SoC PCM driver
++ *
++ * Author: Timur Tabi <timur at freescale.com>
++ *
++ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
++ * under the terms of the GNU General Public License version 2.  This
++ * program is licensed "as is" without any warranty of any kind, whether
++ * express or implied.
++ *
++ * This driver implements ASoC support for the Elo DMA controller, which is
++ * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms,
++ * the PCM driver is what handles the DMA buffer.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include <asm/io.h>
++
++#include "fsl_dma.h"
++
++/*
++ * The formats that the DMA controller supports, which is anything
++ * that is 8, 16, or 32 bits.
++ */
++#define FSLDMA_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 	| \
++			    SNDRV_PCM_FMTBIT_U8 	| \
++			    SNDRV_PCM_FMTBIT_S16_LE     | \
++			    SNDRV_PCM_FMTBIT_S16_BE     | \
++			    SNDRV_PCM_FMTBIT_U16_LE     | \
++			    SNDRV_PCM_FMTBIT_U16_BE     | \
++			    SNDRV_PCM_FMTBIT_S24_LE     | \
++			    SNDRV_PCM_FMTBIT_S24_BE     | \
++			    SNDRV_PCM_FMTBIT_U24_LE     | \
++			    SNDRV_PCM_FMTBIT_U24_BE     | \
++			    SNDRV_PCM_FMTBIT_S32_LE     | \
++			    SNDRV_PCM_FMTBIT_S32_BE     | \
++			    SNDRV_PCM_FMTBIT_U32_LE     | \
++			    SNDRV_PCM_FMTBIT_U32_BE)
++
++#define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
++			  SNDRV_PCM_RATE_CONTINUOUS)
++
++/* DMA global data.  This structure is used by fsl_dma_open() to determine
++ * which DMA channels to assign to a substream.  Unfortunately, ASoC V1 does
++ * not allow the machine driver to provide this information to the PCM
++ * driver in advance, and there's no way to differentiate between the two
++ * DMA controllers.  So for now, this driver only supports one SSI device
++ * using two DMA channels.  We cannot support multiple DMA devices.
++ *
++ * ssi_stx_phys: bus address of SSI STX register
++ * ssi_srx_phys: bus address of SSI SRX register
++ * dma_channel: pointer to the DMA channel's registers
++ * irq: IRQ for this DMA channel
++ * assigned: set to 1 if that DMA channel is assigned to a substream
++ */
++static struct {
++	dma_addr_t ssi_stx_phys;
++	dma_addr_t ssi_srx_phys;
++	struct ccsr_dma_channel __iomem *dma_channel[2];
++	unsigned int irq[2];
++	unsigned int assigned[2];
++} dma_global_data;
++
++/*
++ * The number of DMA links to use.  Two is the bare minimum, but if you
++ * have really small links you might need more.
++ */
++#define NUM_DMA_LINKS   2
++
++/** fsl_dma_private: p-substream DMA data
++ *
++ * Each substream has a 1-to-1 association with a DMA channel.
++ *
++ * The link[] array is first because it needs to be aligned on a 32-byte
++ * boundary, so putting it first will ensure alignment without padding the
++ * structure.
++ *
++ * @link[]: array of link descriptors
++ * @controller_id: which DMA controller (0, 1, ...)
++ * @channel_id: which DMA channel on the controller (0, 1, 2, ...)
++ * @dma_channel: pointer to the DMA channel's registers
++ * @irq: IRQ for this DMA channel
++ * @substream: pointer to the substream object, needed by the ISR
++ * @ssi_sxx_phys: bus address of the STX or SRX register to use
++ * @ld_buf_phys: physical address of the LD buffer
++ * @current_link: index into link[] of the link currently being processed
++ * @dma_buf_phys: physical address of the DMA buffer
++ * @dma_buf_next: physical address of the next period to process
++ * @dma_buf_end: physical address of the byte after the end of the DMA
++ * @buffer period_size: the size of a single period
++ * @num_periods: the number of periods in the DMA buffer
++ */
++struct fsl_dma_private {
++	struct fsl_dma_link_descriptor link[NUM_DMA_LINKS];
++	unsigned int controller_id;
++	unsigned int channel_id;
++	struct ccsr_dma_channel __iomem *dma_channel;
++	unsigned int irq;
++	struct snd_pcm_substream *substream;
++	dma_addr_t ssi_sxx_phys;
++	dma_addr_t ld_buf_phys;
++	unsigned int current_link;
++	dma_addr_t dma_buf_phys;
++	dma_addr_t dma_buf_next;
++	dma_addr_t dma_buf_end;
++	size_t period_size;
++	unsigned int num_periods;
++};
++
++/**
++ * fsl_dma_hardare: define characteristics of the PCM hardware.
++ *
++ * The PCM hardware is the Freescale DMA controller.  This structure defines
++ * the capabilities of that hardware.
++ *
++ * Since the sampling rate and data format are not controlled by the DMA
++ * controller, we specify no limits for those values.  The only exception is
++ * period_bytes_min, which is set to a reasonably low value to prevent the
++ * DMA controller from generating too many interrupts per second.
++ *
++ * Since each link descriptor has a 32-bit byte count field, we set
++ * period_bytes_max to the largest 32-bit number.  We also have no maximum
++ * number of periods.
++ */
++static const struct snd_pcm_hardware fsl_dma_hardware = {
++
++	.info   		= SNDRV_PCM_INFO_INTERLEAVED |
++				  SNDRV_PCM_INFO_MMAP |
++				  SNDRV_PCM_INFO_MMAP_VALID,
++	.formats		= FSLDMA_PCM_FORMATS,
++	.rates  		= FSLDMA_PCM_RATES,
++	.rate_min       	= 5512,
++	.rate_max       	= 192000,
++	.period_bytes_min       = 512,  	/* A reasonable limit */
++	.period_bytes_max       = (u32) -1,
++	.periods_min    	= NUM_DMA_LINKS,
++	.periods_max    	= (unsigned int) -1,
++	.buffer_bytes_max       = 128 * 1024,   /* A reasonable limit */
++};
++
++/**
++ * fsl_dma_abort_stream: tell ALSA that the DMA transfer has aborted
++ *
++ * This function should be called by the ISR whenever the DMA controller
++ * halts data transfer.
++ */
++static void fsl_dma_abort_stream(struct snd_pcm_substream *substream)
++{
++	unsigned long flags;
++
++	snd_pcm_stream_lock_irqsave(substream, flags);
++
++	if (snd_pcm_running(substream))
++		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
++
++	snd_pcm_stream_unlock_irqrestore(substream, flags);
++}
++
++/**
++ * fsl_dma_update_pointers - update LD pointers to point to the next period
++ *
++ * As each period is completed, this function changes the the link
++ * descriptor pointers for that period to point to the next period.
++ */
++static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private)
++{
++	struct fsl_dma_link_descriptor *link =
++		&dma_private->link[dma_private->current_link];
++
++	/* Update our link descriptors to point to the next period */
++	if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		link->source_addr =
++			cpu_to_be32(dma_private->dma_buf_next);
++	else
++		link->dest_addr =
++			cpu_to_be32(dma_private->dma_buf_next);
++
++	/* Update our variables for next time */
++	dma_private->dma_buf_next += dma_private->period_size;
++
++	if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
++		dma_private->dma_buf_next = dma_private->dma_buf_phys;
++
++	if (++dma_private->current_link >= NUM_DMA_LINKS)
++		dma_private->current_link = 0;
++}
++
++/**
++ * fsl_dma_isr: interrupt handler for the DMA controller
++ *
++ * @irq: IRQ of the DMA channel
++ * @dev_id: pointer to the dma_private structure for this DMA channel
++ */
++static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
++{
++	struct fsl_dma_private *dma_private = dev_id;
++	struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
++	irqreturn_t ret = IRQ_NONE;
++	u32 sr, sr2 = 0;
++
++	/* We got an interrupt, so read the status register to see what we
++	   were interrupted for.
++	 */
++	sr = in_be32(&dma_channel->sr);
++
++	if (sr & CCSR_DMA_SR_TE) {
++		dev_err(dma_private->substream->pcm->card->dev,
++			"DMA transmit error (controller=%u channel=%u irq=%u\n",
++			dma_private->controller_id,
++			dma_private->channel_id, irq);
++		fsl_dma_abort_stream(dma_private->substream);
++		sr2 |= CCSR_DMA_SR_TE;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sr & CCSR_DMA_SR_CH)
++		ret = IRQ_HANDLED;
++
++	if (sr & CCSR_DMA_SR_PE) {
++		dev_err(dma_private->substream->pcm->card->dev,
++			"DMA%u programming error (channel=%u irq=%u)\n",
++			dma_private->controller_id,
++			dma_private->channel_id, irq);
++		fsl_dma_abort_stream(dma_private->substream);
++		sr2 |= CCSR_DMA_SR_PE;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sr & CCSR_DMA_SR_EOLNI) {
++		sr2 |= CCSR_DMA_SR_EOLNI;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sr & CCSR_DMA_SR_CB)
++		ret = IRQ_HANDLED;
++
++	if (sr & CCSR_DMA_SR_EOSI) {
++		struct snd_pcm_substream *substream = dma_private->substream;
++
++		/* Tell ALSA we completed a period. */
++		snd_pcm_period_elapsed(substream);
++
++		/*
++		 * Update our link descriptors to point to the next period. We
++		 * only need to do this if the number of periods is not equal to
++		 * the number of links.
++		 */
++		if (dma_private->num_periods != NUM_DMA_LINKS)
++			fsl_dma_update_pointers(dma_private);
++
++		sr2 |= CCSR_DMA_SR_EOSI;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sr & CCSR_DMA_SR_EOLSI) {
++		sr2 |= CCSR_DMA_SR_EOLSI;
++		ret = IRQ_HANDLED;
++	}
++
++	/* Clear the bits that we set */
++	if (sr2)
++		out_be32(&dma_channel->sr, sr2);
++
++	return ret;
++}
++
++/**
++ * fsl_dma_new: initialize this PCM driver.
++ *
++ * This function is called when the codec driver calls snd_soc_new_pcms(),
++ * once for each .dai_link in the machine driver's snd_soc_machine
++ * structure.
++ */
++static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
++	struct snd_pcm *pcm)
++{
++	static u64 fsl_dma_dmamask = DMA_BIT_MASK(32);
++	int ret;
++
++	if (!card->dev->dma_mask)
++		card->dev->dma_mask = &fsl_dma_dmamask;
++
++	if (!card->dev->coherent_dma_mask)
++		card->dev->coherent_dma_mask = fsl_dma_dmamask;
++
++	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev,
++		fsl_dma_hardware.buffer_bytes_max,
++		&pcm->streams[0].substream->dma_buffer);
++	if (ret) {
++		dev_err(card->dev,
++			"Can't allocate playback DMA buffer (size=%u)\n",
++			fsl_dma_hardware.buffer_bytes_max);
++		return -ENOMEM;
++	}
++
++	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev,
++		fsl_dma_hardware.buffer_bytes_max,
++		&pcm->streams[1].substream->dma_buffer);
++	if (ret) {
++		snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
++		dev_err(card->dev,
++			"Can't allocate capture DMA buffer (size=%u)\n",
++			fsl_dma_hardware.buffer_bytes_max);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++/**
++ * fsl_dma_open: open a new substream.
++ *
++ * Each substream has its own DMA buffer.
++ */
++static int fsl_dma_open(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct fsl_dma_private *dma_private;
++	dma_addr_t ld_buf_phys;
++	unsigned int channel;
++	int ret = 0;
++
++	/*
++	 * Reject any DMA buffer whose size is not a multiple of the period
++	 * size.  We need to make sure that the DMA buffer can be evenly divided
++	 * into periods.
++	 */
++	ret = snd_pcm_hw_constraint_integer(runtime,
++		SNDRV_PCM_HW_PARAM_PERIODS);
++	if (ret < 0) {
++		dev_err(substream->pcm->card->dev, "invalid buffer size\n");
++		return ret;
++	}
++
++	channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
++
++	if (dma_global_data.assigned[channel]) {
++		dev_err(substream->pcm->card->dev,
++			"DMA channel already assigned\n");
++		return -EBUSY;
++	}
++
++	dma_private = dma_alloc_coherent(substream->pcm->dev,
++		sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL);
++	if (!dma_private) {
++		dev_err(substream->pcm->card->dev,
++			"can't allocate DMA private data\n");
++		return -ENOMEM;
++	}
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys;
++	else
++		dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys;
++
++	dma_private->dma_channel = dma_global_data.dma_channel[channel];
++	dma_private->irq = dma_global_data.irq[channel];
++	dma_private->substream = substream;
++	dma_private->ld_buf_phys = ld_buf_phys;
++	dma_private->dma_buf_phys = substream->dma_buffer.addr;
++
++	/* We only support one DMA controller for now */
++	dma_private->controller_id = 0;
++	dma_private->channel_id = channel;
++
++	ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private);
++	if (ret) {
++		dev_err(substream->pcm->card->dev,
++			"can't register ISR for IRQ %u (ret=%i)\n",
++			dma_private->irq, ret);
++		dma_free_coherent(substream->pcm->dev,
++			sizeof(struct fsl_dma_private),
++			dma_private, dma_private->ld_buf_phys);
++		return ret;
++	}
++
++	dma_global_data.assigned[channel] = 1;
++
++	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
++	snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware);
++	runtime->private_data = dma_private;
++
++	return 0;
++}
++
++/**
++ * fsl_dma_hw_params: allocate the DMA buffer and the DMA link descriptors.
++ *
++ * ALSA divides the DMA buffer into N periods.  We create NUM_DMA_LINKS link
++ * descriptors that ping-pong from one period to the next.  For example, if
++ * there are six periods and two link descriptors, this is how they look
++ * before playback starts:
++ *
++ *      	   The last link descriptor
++ *   ____________  points back to the first
++ *  |   	 |
++ *  V   	 |
++ *  ___    ___   |
++ * |   |->|   |->|
++ * |___|  |___|
++ *   |      |
++ *   |      |
++ *   V      V
++ *  _________________________________________
++ * |      |      |      |      |      |      |  The DMA buffer is
++ * |      |      |      |      |      |      |    divided into 6 parts
++ * |______|______|______|______|______|______|
++ *
++ * and here's how they look after the first period is finished playing:
++ *
++ *   ____________
++ *  |   	 |
++ *  V   	 |
++ *  ___    ___   |
++ * |   |->|   |->|
++ * |___|  |___|
++ *   |      |
++ *   |______________
++ *          |       |
++ *          V       V
++ *  _________________________________________
++ * |      |      |      |      |      |      |
++ * |      |      |      |      |      |      |
++ * |______|______|______|______|______|______|
++ *
++ * The first link descriptor now points to the third period.  The DMA
++ * controller is currently playing the second period.  When it finishes, it
++ * will jump back to the first descriptor and play the third period.
++ *
++ * There are four reasons we do this:
++ *
++ * 1. The only way to get the DMA controller to automatically restart the
++ *    transfer when it gets to the end of the buffer is to use chaining
++ *    mode.  Basic direct mode doesn't offer that feature.
++ * 2. We need to receive an interrupt at the end of every period.  The DMA
++ *    controller can generate an interrupt at the end of every link transfer
++ *    (aka segment).  Making each period into a DMA segment will give us the
++ *    interrupts we need.
++ * 3. By creating only two link descriptors, regardless of the number of
++ *    periods, we do not need to reallocate the link descriptors if the
++ *    number of periods changes.
++ * 4. All of the audio data is still stored in a single, contiguous DMA
++ *    buffer, which is what ALSA expects.  We're just dividing it into
++ *    contiguous parts, and creating a link descriptor for each one.
++ *
++ * Note that due to a quirk of the SSI's STX register, the target address
++ * for the DMA operations depends on the sample size.  So we don't program
++ * the dest_addr (for playback -- source_addr for capture) fields in the
++ * link descriptors here.  We do that in fsl_dma_prepare()
++ */
++static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
++	struct snd_pcm_hw_params *hw_params)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct fsl_dma_private *dma_private = runtime->private_data;
++	struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
++
++	dma_addr_t temp_addr;   /* Pointer to next period */
++	u64 temp_link;  	/* Pointer to next link descriptor */
++	u32 mr; 		/* Temporary variable for MR register */
++
++	unsigned int i;
++
++	/* Get all the parameters we need */
++	size_t buffer_size = params_buffer_bytes(hw_params);
++	size_t period_size = params_period_bytes(hw_params);
++
++	/* Initialize our DMA tracking variables */
++	dma_private->period_size = period_size;
++	dma_private->num_periods = params_periods(hw_params);
++	dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size;
++	dma_private->dma_buf_next = dma_private->dma_buf_phys +
++		(NUM_DMA_LINKS * period_size);
++	if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
++		dma_private->dma_buf_next = dma_private->dma_buf_phys;
++
++	/*
++	 * Initialize each link descriptor.
++	 *
++	 * The actual address in STX0 (destination for playback, source for
++	 * capture) is based on the sample size, but we don't know the sample
++	 * size in this function, so we'll have to adjust that later.  See
++	 * comments in fsl_dma_prepare().
++	 *
++	 * The DMA controller does not have a cache, so the CPU does not
++	 * need to tell it to flush its cache.  However, the DMA
++	 * controller does need to tell the CPU to flush its cache.
++	 * That's what the SNOOP bit does.
++	 *
++	 * Also, even though the DMA controller supports 36-bit addressing, for
++	 * simplicity we currently support only 32-bit addresses for the audio
++	 * buffer itself.
++	 */
++	temp_addr = substream->dma_buffer.addr;
++	temp_link = dma_private->ld_buf_phys +
++		sizeof(struct fsl_dma_link_descriptor);
++
++	for (i = 0; i < NUM_DMA_LINKS; i++) {
++		struct fsl_dma_link_descriptor *link = &dma_private->link[i];
++
++		link->count = cpu_to_be32(period_size);
++		link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
++		link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
++		link->next = cpu_to_be64(temp_link);
++
++		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++			link->source_addr = cpu_to_be32(temp_addr);
++		else
++			link->dest_addr = cpu_to_be32(temp_addr);
++
++		temp_addr += period_size;
++		temp_link += sizeof(struct fsl_dma_link_descriptor);
++	}
++	/* The last link descriptor points to the first */
++	dma_private->link[i - 1].next = cpu_to_be64(dma_private->ld_buf_phys);
++
++	/* Tell the DMA controller where the first link descriptor is */
++	out_be32(&dma_channel->clndar,
++		CCSR_DMA_CLNDAR_ADDR(dma_private->ld_buf_phys));
++	out_be32(&dma_channel->eclndar,
++		CCSR_DMA_ECLNDAR_ADDR(dma_private->ld_buf_phys));
++
++	/* The manual says the BCR must be clear before enabling EMP */
++	out_be32(&dma_channel->bcr, 0);
++
++	/*
++	 * Program the mode register for interrupts, external master control,
++	 * and source/destination hold.  Also clear the Channel Abort bit.
++	 */
++	mr = in_be32(&dma_channel->mr) &
++		~(CCSR_DMA_MR_CA | CCSR_DMA_MR_DAHE | CCSR_DMA_MR_SAHE);
++
++	/*
++	 * We want External Master Start and External Master Pause enabled,
++	 * because the SSI is controlling the DMA controller.  We want the DMA
++	 * controller to be set up in advance, and then we signal only the SSI
++	 * to start transfering.
++	 *
++	 * We want End-Of-Segment Interrupts enabled, because this will generate
++	 * an interrupt at the end of each segment (each link descriptor
++	 * represents one segment).  Each DMA segment is the same thing as an
++	 * ALSA period, so this is how we get an interrupt at the end of every
++	 * period.
++	 *
++	 * We want Error Interrupt enabled, so that we can get an error if
++	 * the DMA controller is mis-programmed somehow.
++	 */
++	mr |= CCSR_DMA_MR_EOSIE | CCSR_DMA_MR_EIE | CCSR_DMA_MR_EMP_EN |
++		CCSR_DMA_MR_EMS_EN;
++
++	/* For playback, we want the destination address to be held.  For
++	   capture, set the source address to be held. */
++	mr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
++		CCSR_DMA_MR_DAHE : CCSR_DMA_MR_SAHE;
++
++	out_be32(&dma_channel->mr, mr);
++
++	return 0;
++}
++
++/**
++ * fsl_dma_prepare - prepare the DMA registers for playback.
++ *
++ * This function is called after the specifics of the audio data are known,
++ * i.e. snd_pcm_runtime is initialized.
++ *
++ * In this function, we finish programming the registers of the DMA
++ * controller that are dependent on the sample size.
++ *
++ * One of the drawbacks with big-endian is that when copying integers of
++ * different sizes to a fixed-sized register, the address to which the
++ * integer must be copied is dependent on the size of the integer.
++ *
++ * For example, if P is the address of a 32-bit register, and X is a 32-bit
++ * integer, then X should be copied to address P.  However, if X is a 16-bit
++ * integer, then it should be copied to P+2.  If X is an 8-bit register,
++ * then it should be copied to P+3.
++ *
++ * So for playback of 8-bit samples, the DMA controller must transfer single
++ * bytes from the DMA buffer to the last byte of the STX0 register, i.e.
++ * offset by 3 bytes. For 16-bit samples, the offset is two bytes.
++ *
++ * For 24-bit samples, the offset is 1 byte.  However, the DMA controller
++ * does not support 3-byte copies (the DAHTS register supports only 1, 2, 4,
++ * and 8 bytes at a time).  So we do not support packed 24-bit samples.
++ * 24-bit data must be padded to 32 bits.
++ */
++static int fsl_dma_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct fsl_dma_private *dma_private = runtime->private_data;
++	struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
++	u32 mr;
++	unsigned int i;
++	dma_addr_t ssi_sxx_phys;	/* Bus address of SSI STX register */
++	unsigned int frame_size;	/* Number of bytes per frame */
++
++	ssi_sxx_phys = dma_private->ssi_sxx_phys;
++
++	mr = in_be32(&dma_channel->mr) & ~(CCSR_DMA_MR_BWC_MASK |
++		  CCSR_DMA_MR_SAHTS_MASK | CCSR_DMA_MR_DAHTS_MASK);
++
++	switch (runtime->sample_bits) {
++	case 8:
++		mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1;
++		ssi_sxx_phys += 3;
++		break;
++	case 16:
++		mr |= CCSR_DMA_MR_DAHTS_2 | CCSR_DMA_MR_SAHTS_2;
++		ssi_sxx_phys += 2;
++		break;
++	case 32:
++		mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4;
++		break;
++	default:
++		dev_err(substream->pcm->card->dev,
++			"unsupported sample size %u\n", runtime->sample_bits);
++		return -EINVAL;
++	}
++
++	frame_size = runtime->frame_bits / 8;
++	/*
++	 * BWC should always be a multiple of the frame size.  BWC determines
++	 * how many bytes are sent/received before the DMA controller checks the
++	 * SSI to see if it needs to stop.  For playback, the transmit FIFO can
++	 * hold three frames, so we want to send two frames at a time. For
++	 * capture, the receive FIFO is triggered when it contains one frame, so
++	 * we want to receive one frame at a time.
++	 */
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		mr |= CCSR_DMA_MR_BWC(2 * frame_size);
++	else
++		mr |= CCSR_DMA_MR_BWC(frame_size);
++
++	out_be32(&dma_channel->mr, mr);
++
++	/*
++	 * Program the address of the DMA transfer to/from the SSI.
++	 */
++	for (i = 0; i < NUM_DMA_LINKS; i++) {
++		struct fsl_dma_link_descriptor *link = &dma_private->link[i];
++
++		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++			link->dest_addr = cpu_to_be32(ssi_sxx_phys);
++		else
++			link->source_addr = cpu_to_be32(ssi_sxx_phys);
++	}
++
++	return 0;
++}
++
++/**
++ * fsl_dma_pointer: determine the current position of the DMA transfer
++ *
++ * This function is called by ALSA when ALSA wants to know where in the
++ * stream buffer the hardware currently is.
++ *
++ * For playback, the SAR register contains the physical address of the most
++ * recent DMA transfer.  For capture, the value is in the DAR register.
++ *
++ * The base address of the buffer is stored in the source_addr field of the
++ * first link descriptor.
++ */
++static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct fsl_dma_private *dma_private = runtime->private_data;
++	struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
++	dma_addr_t position;
++	snd_pcm_uframes_t frames;
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		position = in_be32(&dma_channel->sar);
++	else
++		position = in_be32(&dma_channel->dar);
++
++	frames = bytes_to_frames(runtime, position - dma_private->dma_buf_phys);
++
++	/*
++	 * If the current address is just past the end of the buffer, wrap it
++	 * around.
++	 */
++	if (frames == runtime->buffer_size)
++		frames = 0;
++
++	return frames;
++}
++
++/**
++ * fsl_dma_hw_free: release resources allocated in fsl_dma_hw_params()
++ *
++ * Release the resources allocated in fsl_dma_hw_params() and de-program the
++ * registers.
++ *
++ * This function can be called multiple times.
++ */
++static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct fsl_dma_private *dma_private = runtime->private_data;
++
++	if (dma_private) {
++		struct ccsr_dma_channel __iomem *dma_channel;
++
++		dma_channel = dma_private->dma_channel;
++
++		/* Stop the DMA */
++		out_be32(&dma_channel->mr, CCSR_DMA_MR_CA);
++		out_be32(&dma_channel->mr, 0);
++
++		/* Reset all the other registers */
++		out_be32(&dma_channel->sr, -1);
++		out_be32(&dma_channel->clndar, 0);
++		out_be32(&dma_channel->eclndar, 0);
++		out_be32(&dma_channel->satr, 0);
++		out_be32(&dma_channel->sar, 0);
++		out_be32(&dma_channel->datr, 0);
++		out_be32(&dma_channel->dar, 0);
++		out_be32(&dma_channel->bcr, 0);
++		out_be32(&dma_channel->nlndar, 0);
++		out_be32(&dma_channel->enlndar, 0);
++	}
++
++	return 0;
++}
++
++/**
++ * fsl_dma_close: close the stream.
++ */
++static int fsl_dma_close(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct fsl_dma_private *dma_private = runtime->private_data;
++	int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
++
++	if (dma_private) {
++		if (dma_private->irq)
++			free_irq(dma_private->irq, dma_private);
++
++		if (dma_private->ld_buf_phys) {
++			dma_unmap_single(substream->pcm->dev,
++				dma_private->ld_buf_phys,
++				sizeof(dma_private->link), DMA_TO_DEVICE);
++		}
++
++		/* Deallocate the fsl_dma_private structure */
++		dma_free_coherent(substream->pcm->dev,
++			sizeof(struct fsl_dma_private),
++			dma_private, dma_private->ld_buf_phys);
++		substream->runtime->private_data = NULL;
++	}
++
++	dma_global_data.assigned[dir] = 0;
++
++	return 0;
++}
++
++/*
++ * Remove this PCM driver.
++ */
++static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
++{
++	struct snd_pcm_substream *substream;
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
++		substream = pcm->streams[i].substream;
++		if (substream) {
++			snd_dma_free_pages(&substream->dma_buffer);
++			substream->dma_buffer.area = NULL;
++			substream->dma_buffer.addr = 0;
++		}
++	}
++}
++
++static struct snd_pcm_ops fsl_dma_ops = {
++	.open   	= fsl_dma_open,
++	.close  	= fsl_dma_close,
++	.ioctl  	= snd_pcm_lib_ioctl,
++	.hw_params      = fsl_dma_hw_params,
++	.hw_free	= fsl_dma_hw_free,
++	.prepare	= fsl_dma_prepare,
++	.pointer	= fsl_dma_pointer,
++};
++
++struct snd_soc_platform fsl_soc_platform = {
++	.name   	= "fsl-dma",
++	.pcm_ops	= &fsl_dma_ops,
++	.pcm_new	= fsl_dma_new,
++	.pcm_free       = fsl_dma_free_dma_buffers,
++};
++EXPORT_SYMBOL_GPL(fsl_soc_platform);
++
++/**
++ * fsl_dma_configure: store the DMA parameters from the fabric driver.
++ *
++ * This function is called by the ASoC fabric driver to give us the DMA and
++ * SSI channel information.
++ *
++ * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI
++ * data when a substream is created, so for now we need to store this data
++ * into a global variable.  This means that we can only support one DMA
++ * controller, and hence only one SSI.
++ */
++int fsl_dma_configure(struct fsl_dma_info *dma_info)
++{
++	static int initialized;
++
++	/* We only support one DMA controller for now */
++	if (initialized)
++		return 0;
++
++	dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys;
++	dma_global_data.ssi_srx_phys = dma_info->ssi_srx_phys;
++	dma_global_data.dma_channel[0] = dma_info->dma_channel[0];
++	dma_global_data.dma_channel[1] = dma_info->dma_channel[1];
++	dma_global_data.irq[0] = dma_info->dma_irq[0];
++	dma_global_data.irq[1] = dma_info->dma_irq[1];
++	dma_global_data.assigned[0] = 0;
++	dma_global_data.assigned[1] = 0;
++
++	initialized = 1;
++	return 1;
++}
++EXPORT_SYMBOL_GPL(fsl_dma_configure);
++
++MODULE_AUTHOR("Timur Tabi <timur at freescale.com>");
++MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
+new file mode 100644
+index 0000000..430a6ce
+--- /dev/null
++++ b/sound/soc/fsl/fsl_dma.h
+@@ -0,0 +1,149 @@
++/*
++ * mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC
++ *
++ * 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 _MPC8610_PCM_H
++#define _MPC8610_PCM_H
++
++struct ccsr_dma {
++	u8 res0[0x100];
++	struct ccsr_dma_channel {
++		__be32 mr;      /* Mode register */
++		__be32 sr;      /* Status register */
++		__be32 eclndar; /* Current link descriptor extended addr reg */
++		__be32 clndar;  /* Current link descriptor address register */
++		__be32 satr;    /* Source attributes register */
++		__be32 sar;     /* Source address register */
++		__be32 datr;    /* Destination attributes register */
++		__be32 dar;     /* Destination address register */
++		__be32 bcr;     /* Byte count register */
++		__be32 enlndar; /* Next link descriptor extended address reg */
++		__be32 nlndar;  /* Next link descriptor address register */
++		u8 res1[4];
++		__be32 eclsdar; /* Current list descriptor extended addr reg */
++		__be32 clsdar;  /* Current list descriptor address register */
++		__be32 enlsdar; /* Next list descriptor extended address reg */
++		__be32 nlsdar;  /* Next list descriptor address register */
++		__be32 ssr;     /* Source stride register */
++		__be32 dsr;     /* Destination stride register */
++		u8 res2[0x38];
++	} channel[4];
++	__be32 dgsr;
++};
++
++#define CCSR_DMA_MR_BWC_DISABLED	0x0F000000
++#define CCSR_DMA_MR_BWC_SHIFT   	24
++#define CCSR_DMA_MR_BWC_MASK    	0x0F000000
++#define CCSR_DMA_MR_BWC(x) \
++	((ilog2(x) << CCSR_DMA_MR_BWC_SHIFT) & CCSR_DMA_MR_BWC_MASK)
++#define CCSR_DMA_MR_EMP_EN      	0x00200000
++#define CCSR_DMA_MR_EMS_EN      	0x00040000
++#define CCSR_DMA_MR_DAHTS_MASK  	0x00030000
++#define CCSR_DMA_MR_DAHTS_1     	0x00000000
++#define CCSR_DMA_MR_DAHTS_2     	0x00010000
++#define CCSR_DMA_MR_DAHTS_4     	0x00020000
++#define CCSR_DMA_MR_DAHTS_8     	0x00030000
++#define CCSR_DMA_MR_SAHTS_MASK  	0x0000C000
++#define CCSR_DMA_MR_SAHTS_1     	0x00000000
++#define CCSR_DMA_MR_SAHTS_2     	0x00004000
++#define CCSR_DMA_MR_SAHTS_4     	0x00008000
++#define CCSR_DMA_MR_SAHTS_8     	0x0000C000
++#define CCSR_DMA_MR_DAHE		0x00002000
++#define CCSR_DMA_MR_SAHE		0x00001000
++#define CCSR_DMA_MR_SRW 		0x00000400
++#define CCSR_DMA_MR_EOSIE       	0x00000200
++#define CCSR_DMA_MR_EOLNIE      	0x00000100
++#define CCSR_DMA_MR_EOLSIE      	0x00000080
++#define CCSR_DMA_MR_EIE 		0x00000040
++#define CCSR_DMA_MR_XFE 		0x00000020
++#define CCSR_DMA_MR_CDSM_SWSM   	0x00000010
++#define CCSR_DMA_MR_CA  		0x00000008
++#define CCSR_DMA_MR_CTM 		0x00000004
++#define CCSR_DMA_MR_CC  		0x00000002
++#define CCSR_DMA_MR_CS  		0x00000001
++
++#define CCSR_DMA_SR_TE  		0x00000080
++#define CCSR_DMA_SR_CH  		0x00000020
++#define CCSR_DMA_SR_PE  		0x00000010
++#define CCSR_DMA_SR_EOLNI       	0x00000008
++#define CCSR_DMA_SR_CB  		0x00000004
++#define CCSR_DMA_SR_EOSI		0x00000002
++#define CCSR_DMA_SR_EOLSI       	0x00000001
++
++/* ECLNDAR takes bits 32-36 of the CLNDAR register */
++static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x)
++{
++	return (x >> 32) & 0xf;
++}
++
++#define CCSR_DMA_CLNDAR_ADDR(x) ((x) & 0xFFFFFFFE)
++#define CCSR_DMA_CLNDAR_EOSIE   	0x00000008
++
++/* SATR and DATR, combined */
++#define CCSR_DMA_ATR_PBATMU     	0x20000000
++#define CCSR_DMA_ATR_TFLOWLVL_0 	0x00000000
++#define CCSR_DMA_ATR_TFLOWLVL_1 	0x06000000
++#define CCSR_DMA_ATR_TFLOWLVL_2 	0x08000000
++#define CCSR_DMA_ATR_TFLOWLVL_3 	0x0C000000
++#define CCSR_DMA_ATR_PCIORDER   	0x02000000
++#define CCSR_DMA_ATR_SME		0x01000000
++#define CCSR_DMA_ATR_NOSNOOP    	0x00040000
++#define CCSR_DMA_ATR_SNOOP      	0x00050000
++#define CCSR_DMA_ATR_ESAD_MASK  	0x0000000F
++
++/**
++ *  List Descriptor for extended chaining mode DMA operations.
++ *
++ *  The CLSDAR register points to the first (in a linked-list) List
++ *  Descriptor.  Each object must be aligned on a 32-byte boundary. Each
++ *  list descriptor points to a linked-list of link Descriptors.
++ */
++struct fsl_dma_list_descriptor {
++	__be64 next;    	/* Address of next list descriptor */
++	__be64 first_link;      /* Address of first link descriptor */
++	__be32 source;  	/* Source stride */
++	__be32 dest;    	/* Destination stride */
++	u8 res[8];      	/* Reserved */
++} __attribute__ ((aligned(32), packed));
++
++/**
++ *  Link Descriptor for basic and extended chaining mode DMA operations.
++ *
++ *  A Link Descriptor points to a single DMA buffer.  Each link descriptor
++ *  must be aligned on a 32-byte boundary.
++ */
++struct fsl_dma_link_descriptor {
++	__be32 source_attr;     /* Programmed into SATR register */
++	__be32 source_addr;     /* Programmed into SAR register */
++	__be32 dest_attr;       /* Programmed into DATR register */
++	__be32 dest_addr;       /* Programmed into DAR register */
++	__be64 next;    /* Address of next link descriptor */
++	__be32 count;   /* Byte count */
++	u8 res[4];      /* Reserved */
++} __attribute__ ((aligned(32), packed));
++
++/* DMA information needed to create a snd_soc_cpu_dai object
++ *
++ * ssi_stx_phys: bus address of SSI STX register to use
++ * ssi_srx_phys: bus address of SSI SRX register to use
++ * dma[0]: points to the DMA channel to use for playback
++ * dma[1]: points to the DMA channel to use for capture
++ * dma_irq[0]: IRQ of the DMA channel to use for playback
++ * dma_irq[1]: IRQ of the DMA channel to use for capture
++ */
++struct fsl_dma_info {
++	dma_addr_t ssi_stx_phys;
++	dma_addr_t ssi_srx_phys;
++	struct ccsr_dma_channel __iomem *dma_channel[2];
++	unsigned int dma_irq[2];
++};
++
++extern struct snd_soc_platform fsl_soc_platform;
++
++int fsl_dma_configure(struct fsl_dma_info *dma_info);
++
++#endif
+diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
+new file mode 100644
+index 0000000..145ad13
+--- /dev/null
++++ b/sound/soc/fsl/fsl_ssi.c
+@@ -0,0 +1,644 @@
++/*
++ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
++ *
++ * Author: Timur Tabi <timur at freescale.com>
++ *
++ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
++ * under the terms of the GNU General Public License version 2.  This
++ * program is licensed "as is" without any warranty of any kind, whether
++ * express or implied.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++
++#include <asm/immap_86xx.h>
++
++#include "fsl_ssi.h"
++
++/**
++ * FSLSSI_I2S_RATES: sample rates supported by the I2S
++ *
++ * This driver currently only supports the SSI running in I2S slave mode,
++ * which means the codec determines the sample rate.  Therefore, we tell
++ * ALSA that we support all rates and let the codec driver decide what rates
++ * are really supported.
++ */
++#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
++			  SNDRV_PCM_RATE_CONTINUOUS)
++
++/**
++ * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
++ *
++ * This driver currently only supports the SSI running in I2S slave mode.
++ *
++ * The SSI has a limitation in that the samples must be in the same byte
++ * order as the host CPU.  This is because when multiple bytes are written
++ * to the STX register, the bytes and bits must be written in the same
++ * order.  The STX is a shift register, so all the bits need to be aligned
++ * (bit-endianness must match byte-endianness).  Processors typically write
++ * the bits within a byte in the same order that the bytes of a word are
++ * written in.  So if the host CPU is big-endian, then only big-endian
++ * samples will be written to STX properly.
++ */
++#ifdef __BIG_ENDIAN
++#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
++	 SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
++	 SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
++#else
++#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
++	 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
++#endif
++
++/**
++ * fsl_ssi_private: per-SSI private data
++ *
++ * @name: short name for this device ("SSI0", "SSI1", etc)
++ * @ssi: pointer to the SSI's registers
++ * @ssi_phys: physical address of the SSI registers
++ * @irq: IRQ of this SSI
++ * @dev: struct device pointer
++ * @playback: the number of playback streams opened
++ * @capture: the number of capture streams opened
++ * @cpu_dai: the CPU DAI for this device
++ * @dev_attr: the sysfs device attribute structure
++ * @stats: SSI statistics
++ */
++struct fsl_ssi_private {
++	char name[8];
++	struct ccsr_ssi __iomem *ssi;
++	dma_addr_t ssi_phys;
++	unsigned int irq;
++	struct device *dev;
++	unsigned int playback;
++	unsigned int capture;
++	struct snd_soc_cpu_dai cpu_dai;
++	struct device_attribute dev_attr;
++
++	struct {
++		unsigned int rfrc;
++		unsigned int tfrc;
++		unsigned int cmdau;
++		unsigned int cmddu;
++		unsigned int rxt;
++		unsigned int rdr1;
++		unsigned int rdr0;
++		unsigned int tde1;
++		unsigned int tde0;
++		unsigned int roe1;
++		unsigned int roe0;
++		unsigned int tue1;
++		unsigned int tue0;
++		unsigned int tfs;
++		unsigned int rfs;
++		unsigned int tls;
++		unsigned int rls;
++		unsigned int rff1;
++		unsigned int rff0;
++		unsigned int tfe1;
++		unsigned int tfe0;
++	} stats;
++};
++
++/**
++ * fsl_ssi_isr: SSI interrupt handler
++ *
++ * Although it's possible to use the interrupt handler to send and receive
++ * data to/from the SSI, we use the DMA instead.  Programming is more
++ * complicated, but the performance is much better.
++ *
++ * This interrupt handler is used only to gather statistics.
++ *
++ * @irq: IRQ of the SSI device
++ * @dev_id: pointer to the ssi_private structure for this SSI device
++ */
++static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
++{
++	struct fsl_ssi_private *ssi_private = dev_id;
++	struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
++	irqreturn_t ret = IRQ_NONE;
++	__be32 sisr;
++	__be32 sisr2 = 0;
++
++	/* We got an interrupt, so read the status register to see what we
++	   were interrupted for.  We mask it with the Interrupt Enable register
++	   so that we only check for events that we're interested in.
++	 */
++	sisr = in_be32(&ssi->sisr) & in_be32(&ssi->sier);
++
++	if (sisr & CCSR_SSI_SISR_RFRC) {
++		ssi_private->stats.rfrc++;
++		sisr2 |= CCSR_SSI_SISR_RFRC;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TFRC) {
++		ssi_private->stats.tfrc++;
++		sisr2 |= CCSR_SSI_SISR_TFRC;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_CMDAU) {
++		ssi_private->stats.cmdau++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_CMDDU) {
++		ssi_private->stats.cmddu++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RXT) {
++		ssi_private->stats.rxt++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RDR1) {
++		ssi_private->stats.rdr1++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RDR0) {
++		ssi_private->stats.rdr0++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TDE1) {
++		ssi_private->stats.tde1++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TDE0) {
++		ssi_private->stats.tde0++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_ROE1) {
++		ssi_private->stats.roe1++;
++		sisr2 |= CCSR_SSI_SISR_ROE1;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_ROE0) {
++		ssi_private->stats.roe0++;
++		sisr2 |= CCSR_SSI_SISR_ROE0;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TUE1) {
++		ssi_private->stats.tue1++;
++		sisr2 |= CCSR_SSI_SISR_TUE1;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TUE0) {
++		ssi_private->stats.tue0++;
++		sisr2 |= CCSR_SSI_SISR_TUE0;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TFS) {
++		ssi_private->stats.tfs++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RFS) {
++		ssi_private->stats.rfs++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TLS) {
++		ssi_private->stats.tls++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RLS) {
++		ssi_private->stats.rls++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RFF1) {
++		ssi_private->stats.rff1++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_RFF0) {
++		ssi_private->stats.rff0++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TFE1) {
++		ssi_private->stats.tfe1++;
++		ret = IRQ_HANDLED;
++	}
++
++	if (sisr & CCSR_SSI_SISR_TFE0) {
++		ssi_private->stats.tfe0++;
++		ret = IRQ_HANDLED;
++	}
++
++	/* Clear the bits that we set */
++	if (sisr2)
++		out_be32(&ssi->sisr, sisr2);
++
++	return ret;
++}
++
++/**
++ * fsl_ssi_startup: create a new substream
++ *
++ * This is the first function called when a stream is opened.
++ *
++ * If this is the first stream open, then grab the IRQ and program most of
++ * the SSI registers.
++ */
++static int fsl_ssi_startup(struct snd_pcm_substream *substream)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
++
++	/*
++	 * If this is the first stream opened, then request the IRQ
++	 * and initialize the SSI registers.
++	 */
++	if (!ssi_private->playback && !ssi_private->capture) {
++		struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
++		int ret;
++
++		ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0,
++				  ssi_private->name, ssi_private);
++		if (ret < 0) {
++			dev_err(substream->pcm->card->dev,
++				"could not claim irq %u\n", ssi_private->irq);
++			return ret;
++		}
++
++		/*
++		 * Section 16.5 of the MPC8610 reference manual says that the
++		 * SSI needs to be disabled before updating the registers we set
++		 * here.
++		 */
++		clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
++
++		/*
++		 * Program the SSI into I2S Slave Non-Network Synchronous mode.
++		 * Also enable the transmit and receive FIFO.
++		 *
++		 * FIXME: Little-endian samples require a different shift dir
++		 */
++		clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK,
++			CCSR_SSI_SCR_TFR_CLK_DIS |
++			CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN);
++
++		out_be32(&ssi->stcr,
++			 CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
++			 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
++			 CCSR_SSI_STCR_TSCKP);
++
++		out_be32(&ssi->srcr,
++			 CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
++			 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
++			 CCSR_SSI_SRCR_RSCKP);
++
++		/*
++		 * The DC and PM bits are only used if the SSI is the clock
++		 * master.
++		 */
++
++		/* 4. Enable the interrupts and DMA requests */
++		out_be32(&ssi->sier,
++			 CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE |
++			 CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN |
++			 CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN |
++			 CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE |
++			 CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN);
++
++		/*
++		 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
++		 * don't use FIFO 1.  Since the SSI only supports stereo, the
++		 * watermark should never be an odd number.
++		 */
++		out_be32(&ssi->sfcsr,
++			 CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2));
++
++		/*
++		 * We keep the SSI disabled because if we enable it, then the
++		 * DMA controller will start.  It's not supposed to start until
++		 * the SCR.TE (or SCR.RE) bit is set, but it does anyway.  The
++		 * DMA controller will transfer one "BWC" of data (i.e. the
++		 * amount of data that the MR.BWC bits are set to).  The reason
++		 * this is bad is because at this point, the PCM driver has not
++		 * finished initializing the DMA controller.
++		 */
++	}
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		ssi_private->playback++;
++
++	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++		ssi_private->capture++;
++
++	return 0;
++}
++
++/**
++ * fsl_ssi_prepare: prepare the SSI.
++ *
++ * Most of the SSI registers have been programmed in the startup function,
++ * but the word length must be programmed here.  Unfortunately, programming
++ * the SxCCR.WL bits requires the SSI to be temporarily disabled.  This can
++ * cause a problem with supporting simultaneous playback and capture.  If
++ * the SSI is already playing a stream, then that stream may be temporarily
++ * stopped when you start capture.
++ *
++ * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
++ * clock master.
++ */
++static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
++
++	struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
++	u32 wl;
++
++	wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
++
++	clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
++	else
++		clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
++
++	setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
++
++	return 0;
++}
++
++/**
++ * fsl_ssi_trigger: start and stop the DMA transfer.
++ *
++ * This function is called by ALSA to start, stop, pause, and resume the DMA
++ * transfer of data.
++ *
++ * The DMA channel is in external master start and pause mode, which
++ * means the SSI completely controls the flow of data.
++ */
++static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
++	struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
++
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++	case SNDRV_PCM_TRIGGER_RESUME:
++	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++			setbits32(&ssi->scr, CCSR_SSI_SCR_TE);
++		} else {
++			setbits32(&ssi->scr, CCSR_SSI_SCR_RE);
++
++			/*
++			 * I think we need this delay to allow time for the SSI
++			 * to put data into its FIFO.  Without it, ALSA starts
++			 * to complain about overruns.
++			 */
++			msleep(1);
++		}
++		break;
++
++	case SNDRV_PCM_TRIGGER_STOP:
++	case SNDRV_PCM_TRIGGER_SUSPEND:
++	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++			clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
++		else
++			clrbits32(&ssi->scr, CCSR_SSI_SCR_RE);
++		break;
++
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++/**
++ * fsl_ssi_shutdown: shutdown the SSI
++ *
++ * Shutdown the SSI if there are no other substreams open.
++ */
++static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		ssi_private->playback--;
++
++	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++		ssi_private->capture--;
++
++	/*
++	 * If this is the last active substream, disable the SSI and release
++	 * the IRQ.
++	 */
++	if (!ssi_private->playback && !ssi_private->capture) {
++		struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
++
++		clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
++
++		free_irq(ssi_private->irq, ssi_private);
++	}
++}
++
++/**
++ * fsl_ssi_set_sysclk: set the clock frequency and direction
++ *
++ * This function is called by the machine driver to tell us what the clock
++ * frequency and direction are.
++ *
++ * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
++ * and we don't care about the frequency.  Return an error if the direction
++ * is not SND_SOC_CLOCK_IN.
++ *
++ * @clk_id: reserved, should be zero
++ * @freq: the frequency of the given clock ID, currently ignored
++ * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
++ */
++static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
++			      int clk_id, unsigned int freq, int dir)
++{
++
++	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
++}
++
++/**
++ * fsl_ssi_set_fmt: set the serial format.
++ *
++ * This function is called by the machine driver to tell us what serial
++ * format to use.
++ *
++ * Currently, we only support I2S mode.  Return an error if the format is
++ * not SND_SOC_DAIFMT_I2S.
++ *
++ * @format: one of SND_SOC_DAIFMT_xxx
++ */
++static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
++{
++	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
++}
++
++/**
++ * fsl_ssi_dai_template: template CPU DAI for the SSI
++ */
++static struct snd_soc_cpu_dai fsl_ssi_dai_template = {
++	.playback = {
++		/* The SSI does not support monaural audio. */
++		.channels_min = 2,
++		.channels_max = 2,
++		.rates = FSLSSI_I2S_RATES,
++		.formats = FSLSSI_I2S_FORMATS,
++	},
++	.capture = {
++		.channels_min = 2,
++		.channels_max = 2,
++		.rates = FSLSSI_I2S_RATES,
++		.formats = FSLSSI_I2S_FORMATS,
++	},
++	.ops = {
++		.startup = fsl_ssi_startup,
++		.prepare = fsl_ssi_prepare,
++		.shutdown = fsl_ssi_shutdown,
++		.trigger = fsl_ssi_trigger,
++	},
++	.dai_ops = {
++		.set_sysclk = fsl_ssi_set_sysclk,
++		.set_fmt = fsl_ssi_set_fmt,
++	},
++};
++
++/**
++ * fsl_sysfs_ssi_show: display SSI statistics
++ *
++ * Display the statistics for the current SSI device.
++ */
++static ssize_t fsl_sysfs_ssi_show(struct device *dev,
++	struct device_attribute *attr, char *buf)
++{
++	struct fsl_ssi_private *ssi_private =
++	container_of(attr, struct fsl_ssi_private, dev_attr);
++	ssize_t length;
++
++	length = sprintf(buf, "rfrc=%u", ssi_private->stats.rfrc);
++	length += sprintf(buf + length, "\ttfrc=%u", ssi_private->stats.tfrc);
++	length += sprintf(buf + length, "\tcmdau=%u", ssi_private->stats.cmdau);
++	length += sprintf(buf + length, "\tcmddu=%u", ssi_private->stats.cmddu);
++	length += sprintf(buf + length, "\trxt=%u", ssi_private->stats.rxt);
++	length += sprintf(buf + length, "\trdr1=%u", ssi_private->stats.rdr1);
++	length += sprintf(buf + length, "\trdr0=%u", ssi_private->stats.rdr0);
++	length += sprintf(buf + length, "\ttde1=%u", ssi_private->stats.tde1);
++	length += sprintf(buf + length, "\ttde0=%u", ssi_private->stats.tde0);
++	length += sprintf(buf + length, "\troe1=%u", ssi_private->stats.roe1);
++	length += sprintf(buf + length, "\troe0=%u", ssi_private->stats.roe0);
++	length += sprintf(buf + length, "\ttue1=%u", ssi_private->stats.tue1);
++	length += sprintf(buf + length, "\ttue0=%u", ssi_private->stats.tue0);
++	length += sprintf(buf + length, "\ttfs=%u", ssi_private->stats.tfs);
++	length += sprintf(buf + length, "\trfs=%u", ssi_private->stats.rfs);
++	length += sprintf(buf + length, "\ttls=%u", ssi_private->stats.tls);
++	length += sprintf(buf + length, "\trls=%u", ssi_private->stats.rls);
++	length += sprintf(buf + length, "\trff1=%u", ssi_private->stats.rff1);
++	length += sprintf(buf + length, "\trff0=%u", ssi_private->stats.rff0);
++	length += sprintf(buf + length, "\ttfe1=%u", ssi_private->stats.tfe1);
++	length += sprintf(buf + length, "\ttfe0=%u\n", ssi_private->stats.tfe0);
++
++	return length;
++}
++
++/**
++ * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure
++ *
++ * This function is called by the machine driver to create a snd_soc_cpu_dai
++ * structure.  The function creates an ssi_private object, which contains
++ * the snd_soc_cpu_dai.  It also creates the sysfs statistics device.
++ */
++struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
++{
++	struct snd_soc_cpu_dai *fsl_ssi_dai;
++	struct fsl_ssi_private *ssi_private;
++	int ret = 0;
++	struct device_attribute *dev_attr;
++
++	ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL);
++	if (!ssi_private) {
++		dev_err(ssi_info->dev, "could not allocate DAI object\n");
++		return NULL;
++	}
++	memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
++	       sizeof(struct snd_soc_cpu_dai));
++
++	fsl_ssi_dai = &ssi_private->cpu_dai;
++	dev_attr = &ssi_private->dev_attr;
++
++	sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id);
++	ssi_private->ssi = ssi_info->ssi;
++	ssi_private->ssi_phys = ssi_info->ssi_phys;
++	ssi_private->irq = ssi_info->irq;
++	ssi_private->dev = ssi_info->dev;
++
++	ssi_private->dev->driver_data = fsl_ssi_dai;
++
++	/* Initialize the the device_attribute structure */
++	dev_attr->attr.name = "ssi-stats";
++	dev_attr->attr.mode = S_IRUGO;
++	dev_attr->show = fsl_sysfs_ssi_show;
++
++	ret = device_create_file(ssi_private->dev, dev_attr);
++	if (ret) {
++		dev_err(ssi_info->dev, "could not create sysfs %s file\n",
++			ssi_private->dev_attr.attr.name);
++		kfree(fsl_ssi_dai);
++		return NULL;
++	}
++
++	fsl_ssi_dai->private_data = ssi_private;
++	fsl_ssi_dai->name = ssi_private->name;
++	fsl_ssi_dai->id = ssi_info->id;
++
++	return fsl_ssi_dai;
++}
++EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
++
++/**
++ * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object
++ *
++ * This function undoes the operations of fsl_ssi_create_dai()
++ */
++void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai)
++{
++	struct fsl_ssi_private *ssi_private =
++	container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
++
++	device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
++
++	kfree(ssi_private);
++}
++EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
++
++MODULE_AUTHOR("Timur Tabi <timur at freescale.com>");
++MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
+new file mode 100644
+index 0000000..c5ce88e
+--- /dev/null
++++ b/sound/soc/fsl/fsl_ssi.h
+@@ -0,0 +1,224 @@
++/*
++ * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC
++ *
++ * Author: Timur Tabi <timur at freescale.com>
++ *
++ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
++ * under the terms of the GNU General Public License version 2.  This
++ * program is licensed "as is" without any warranty of any kind, whether
++ * express or implied.
++ */
++
++#ifndef _MPC8610_I2S_H
++#define _MPC8610_I2S_H
++
++/* SSI Register Map */
++struct ccsr_ssi {
++	__be32 stx0;	/* 0x.0000 - SSI Transmit Data Register 0 */
++	__be32 stx1;	/* 0x.0004 - SSI Transmit Data Register 1 */
++	__be32 srx0;	/* 0x.0008 - SSI Receive Data Register 0 */
++	__be32 srx1;	/* 0x.000C - SSI Receive Data Register 1 */
++	__be32 scr;	/* 0x.0010 - SSI Control Register */
++	__be32 sisr;	/* 0x.0014 - SSI Interrupt Status Register Mixed */
++	__be32 sier;	/* 0x.0018 - SSI Interrupt Enable Register */
++	__be32 stcr;	/* 0x.001C - SSI Transmit Configuration Register */
++	__be32 srcr;	/* 0x.0020 - SSI Receive Configuration Register */
++	__be32 stccr;	/* 0x.0024 - SSI Transmit Clock Control Register */
++	__be32 srccr;	/* 0x.0028 - SSI Receive Clock Control Register */
++	__be32 sfcsr;	/* 0x.002C - SSI FIFO Control/Status Register */
++	__be32 str;	/* 0x.0030 - SSI Test Register */
++	__be32 sor;	/* 0x.0034 - SSI Option Register */
++	__be32 sacnt;	/* 0x.0038 - SSI AC97 Control Register */
++	__be32 sacadd;	/* 0x.003C - SSI AC97 Command Address Register */
++	__be32 sacdat;	/* 0x.0040 - SSI AC97 Command Data Register */
++	__be32 satag;	/* 0x.0044 - SSI AC97 Tag Register */
++	__be32 stmsk;	/* 0x.0048 - SSI Transmit Time Slot Mask Register */
++	__be32 srmsk;	/* 0x.004C - SSI Receive Time Slot Mask Register */
++	__be32 saccst;	/* 0x.0050 - SSI AC97 Channel Status Register */
++	__be32 saccen;	/* 0x.0054 - SSI AC97 Channel Enable Register */
++	__be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
++};
++
++#define CCSR_SSI_SCR_RFR_CLK_DIS	0x00000800
++#define CCSR_SSI_SCR_TFR_CLK_DIS	0x00000400
++#define CCSR_SSI_SCR_TCH_EN		0x00000100
++#define CCSR_SSI_SCR_SYS_CLK_EN		0x00000080
++#define CCSR_SSI_SCR_I2S_MODE_MASK	0x00000060
++#define CCSR_SSI_SCR_I2S_MODE_NORMAL	0x00000000
++#define CCSR_SSI_SCR_I2S_MODE_MASTER	0x00000020
++#define CCSR_SSI_SCR_I2S_MODE_SLAVE	0x00000040
++#define CCSR_SSI_SCR_SYN		0x00000010
++#define CCSR_SSI_SCR_NET		0x00000008
++#define CCSR_SSI_SCR_RE			0x00000004
++#define CCSR_SSI_SCR_TE			0x00000002
++#define CCSR_SSI_SCR_SSIEN		0x00000001
++
++#define CCSR_SSI_SISR_RFRC		0x01000000
++#define CCSR_SSI_SISR_TFRC		0x00800000
++#define CCSR_SSI_SISR_CMDAU		0x00040000
++#define CCSR_SSI_SISR_CMDDU		0x00020000
++#define CCSR_SSI_SISR_RXT		0x00010000
++#define CCSR_SSI_SISR_RDR1		0x00008000
++#define CCSR_SSI_SISR_RDR0		0x00004000
++#define CCSR_SSI_SISR_TDE1		0x00002000
++#define CCSR_SSI_SISR_TDE0		0x00001000
++#define CCSR_SSI_SISR_ROE1		0x00000800
++#define CCSR_SSI_SISR_ROE0		0x00000400
++#define CCSR_SSI_SISR_TUE1		0x00000200
++#define CCSR_SSI_SISR_TUE0		0x00000100
++#define CCSR_SSI_SISR_TFS		0x00000080
++#define CCSR_SSI_SISR_RFS		0x00000040
++#define CCSR_SSI_SISR_TLS		0x00000020
++#define CCSR_SSI_SISR_RLS		0x00000010
++#define CCSR_SSI_SISR_RFF1		0x00000008
++#define CCSR_SSI_SISR_RFF0		0x00000004
++#define CCSR_SSI_SISR_TFE1		0x00000002
++#define CCSR_SSI_SISR_TFE0		0x00000001
++
++#define CCSR_SSI_SIER_RFRC_EN		0x01000000
++#define CCSR_SSI_SIER_TFRC_EN		0x00800000
++#define CCSR_SSI_SIER_RDMAE		0x00400000
++#define CCSR_SSI_SIER_RIE		0x00200000
++#define CCSR_SSI_SIER_TDMAE		0x00100000
++#define CCSR_SSI_SIER_TIE		0x00080000
++#define CCSR_SSI_SIER_CMDAU_EN		0x00040000
++#define CCSR_SSI_SIER_CMDDU_EN		0x00020000
++#define CCSR_SSI_SIER_RXT_EN		0x00010000
++#define CCSR_SSI_SIER_RDR1_EN		0x00008000
++#define CCSR_SSI_SIER_RDR0_EN		0x00004000
++#define CCSR_SSI_SIER_TDE1_EN		0x00002000
++#define CCSR_SSI_SIER_TDE0_EN		0x00001000
++#define CCSR_SSI_SIER_ROE1_EN		0x00000800
++#define CCSR_SSI_SIER_ROE0_EN		0x00000400
++#define CCSR_SSI_SIER_TUE1_EN		0x00000200
++#define CCSR_SSI_SIER_TUE0_EN		0x00000100
++#define CCSR_SSI_SIER_TFS_EN		0x00000080
++#define CCSR_SSI_SIER_RFS_EN		0x00000040
++#define CCSR_SSI_SIER_TLS_EN		0x00000020
++#define CCSR_SSI_SIER_RLS_EN		0x00000010
++#define CCSR_SSI_SIER_RFF1_EN		0x00000008
++#define CCSR_SSI_SIER_RFF0_EN		0x00000004
++#define CCSR_SSI_SIER_TFE1_EN		0x00000002
++#define CCSR_SSI_SIER_TFE0_EN		0x00000001
++
++#define CCSR_SSI_STCR_TXBIT0		0x00000200
++#define CCSR_SSI_STCR_TFEN1		0x00000100
++#define CCSR_SSI_STCR_TFEN0		0x00000080
++#define CCSR_SSI_STCR_TFDIR		0x00000040
++#define CCSR_SSI_STCR_TXDIR		0x00000020
++#define CCSR_SSI_STCR_TSHFD		0x00000010
++#define CCSR_SSI_STCR_TSCKP		0x00000008
++#define CCSR_SSI_STCR_TFSI		0x00000004
++#define CCSR_SSI_STCR_TFSL		0x00000002
++#define CCSR_SSI_STCR_TEFS		0x00000001
++
++#define CCSR_SSI_SRCR_RXEXT		0x00000400
++#define CCSR_SSI_SRCR_RXBIT0		0x00000200
++#define CCSR_SSI_SRCR_RFEN1		0x00000100
++#define CCSR_SSI_SRCR_RFEN0		0x00000080
++#define CCSR_SSI_SRCR_RFDIR		0x00000040
++#define CCSR_SSI_SRCR_RXDIR		0x00000020
++#define CCSR_SSI_SRCR_RSHFD		0x00000010
++#define CCSR_SSI_SRCR_RSCKP		0x00000008
++#define CCSR_SSI_SRCR_RFSI		0x00000004
++#define CCSR_SSI_SRCR_RFSL		0x00000002
++#define CCSR_SSI_SRCR_REFS		0x00000001
++
++/* STCCR and SRCCR */
++#define CCSR_SSI_SxCCR_DIV2		0x00040000
++#define CCSR_SSI_SxCCR_PSR		0x00020000
++#define CCSR_SSI_SxCCR_WL_SHIFT		13
++#define CCSR_SSI_SxCCR_WL_MASK		0x0001E000
++#define CCSR_SSI_SxCCR_WL(x) \
++	(((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK)
++#define CCSR_SSI_SxCCR_DC_SHIFT		8
++#define CCSR_SSI_SxCCR_DC_MASK		0x00001F00
++#define CCSR_SSI_SxCCR_DC(x) \
++	((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK)
++#define CCSR_SSI_SxCCR_PM_SHIFT		0
++#define CCSR_SSI_SxCCR_PM_MASK		0x000000FF
++#define CCSR_SSI_SxCCR_PM(x) \
++	((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK)
++
++/*
++ * The xFCNT bits are read-only, and the xFWM bits are read/write.  Use the
++ * CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the
++ * CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks.
++ */
++#define CCSR_SSI_SFCSR_RFCNT1_SHIFT	28
++#define CCSR_SSI_SFCSR_RFCNT1_MASK	0xF0000000
++#define CCSR_SSI_SFCSR_RFCNT1(x) \
++	(((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT)
++#define CCSR_SSI_SFCSR_TFCNT1_SHIFT	24
++#define CCSR_SSI_SFCSR_TFCNT1_MASK	0x0F000000
++#define CCSR_SSI_SFCSR_TFCNT1(x) \
++	(((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT)
++#define CCSR_SSI_SFCSR_RFWM1_SHIFT	20
++#define CCSR_SSI_SFCSR_RFWM1_MASK	0x00F00000
++#define CCSR_SSI_SFCSR_RFWM1(x)	\
++	(((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK)
++#define CCSR_SSI_SFCSR_TFWM1_SHIFT	16
++#define CCSR_SSI_SFCSR_TFWM1_MASK	0x000F0000
++#define CCSR_SSI_SFCSR_TFWM1(x)	\
++	(((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK)
++#define CCSR_SSI_SFCSR_RFCNT0_SHIFT	12
++#define CCSR_SSI_SFCSR_RFCNT0_MASK	0x0000F000
++#define CCSR_SSI_SFCSR_RFCNT0(x) \
++	(((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT)
++#define CCSR_SSI_SFCSR_TFCNT0_SHIFT	8
++#define CCSR_SSI_SFCSR_TFCNT0_MASK	0x00000F00
++#define CCSR_SSI_SFCSR_TFCNT0(x) \
++	(((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT)
++#define CCSR_SSI_SFCSR_RFWM0_SHIFT	4
++#define CCSR_SSI_SFCSR_RFWM0_MASK	0x000000F0
++#define CCSR_SSI_SFCSR_RFWM0(x)	\
++	(((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK)
++#define CCSR_SSI_SFCSR_TFWM0_SHIFT	0
++#define CCSR_SSI_SFCSR_TFWM0_MASK	0x0000000F
++#define CCSR_SSI_SFCSR_TFWM0(x)	\
++	(((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK)
++
++#define CCSR_SSI_STR_TEST		0x00008000
++#define CCSR_SSI_STR_RCK2TCK		0x00004000
++#define CCSR_SSI_STR_RFS2TFS		0x00002000
++#define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F)
++#define CCSR_SSI_STR_TXD2RXD		0x00000080
++#define CCSR_SSI_STR_TCK2RCK		0x00000040
++#define CCSR_SSI_STR_TFS2RFS		0x00000020
++#define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F)
++
++#define CCSR_SSI_SOR_CLKOFF		0x00000040
++#define CCSR_SSI_SOR_RX_CLR		0x00000020
++#define CCSR_SSI_SOR_TX_CLR		0x00000010
++#define CCSR_SSI_SOR_INIT		0x00000008
++#define CCSR_SSI_SOR_WAIT_SHIFT		1
++#define CCSR_SSI_SOR_WAIT_MASK		0x00000006
++#define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT)
++#define CCSR_SSI_SOR_SYNRST 		0x00000001
++
++/* Instantiation data for an SSI interface
++ *
++ * This structure contains all the information that the the SSI driver needs
++ * to instantiate an SSI interface with ALSA.  The machine driver should
++ * create this structure, fill it in, call fsl_ssi_create_dai(), and then
++ * delete the structure.
++ *
++ * id: which SSI this is (0, 1, etc. )
++ * ssi: pointer to the SSI's registers
++ * ssi_phys: physical address of the SSI registers
++ * irq: IRQ of this SSI
++ * dev: struct device, used to create the sysfs statistics file
++*/
++struct fsl_ssi_info {
++	unsigned int id;
++	struct ccsr_ssi __iomem *ssi;
++	dma_addr_t ssi_phys;
++	unsigned int irq;
++	struct device *dev;
++};
++
++struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
++void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai);
++
++#endif
++
+diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
+new file mode 100644
+index 0000000..f26c4b2
+--- /dev/null
++++ b/sound/soc/fsl/mpc8610_hpcd.c
+@@ -0,0 +1,631 @@
++/**
++ * Freescale MPC8610HPCD ALSA SoC Fabric driver
++ *
++ * Author: Timur Tabi <timur at freescale.com>
++ *
++ * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
++ * under the terms of the GNU General Public License version 2.  This
++ * program is licensed "as is" without any warranty of any kind, whether
++ * express or implied.
++ */
++
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/of_device.h>
++#include <linux/of_platform.h>
++#include <sound/soc.h>
++#include <asm/immap_86xx.h>
++
++#include "../codecs/cs4270.h"
++#include "fsl_dma.h"
++#include "fsl_ssi.h"
++
++/**
++ * mpc8610_hpcd_data: fabric-specific ASoC device data
++ *
++ * This structure contains data for a single sound platform device on an
++ * MPC8610 HPCD.  Some of the data is taken from the device tree.
++ */
++struct mpc8610_hpcd_data {
++	struct snd_soc_device sound_devdata;
++	struct snd_soc_dai_link dai;
++	struct snd_soc_machine machine;
++	unsigned int dai_format;
++	unsigned int codec_clk_direction;
++	unsigned int cpu_clk_direction;
++	unsigned int clk_frequency;
++	struct ccsr_guts __iomem *guts;
++	struct ccsr_ssi __iomem *ssi;
++	unsigned int ssi_id;    	/* 0 = SSI1, 1 = SSI2, etc */
++	unsigned int ssi_irq;
++	unsigned int dma_id;    	/* 0 = DMA1, 1 = DMA2, etc */
++	unsigned int dma_irq[2];
++	struct ccsr_dma_channel __iomem *dma[2];
++	unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
++};
++
++/**
++ * mpc8610_hpcd_machine_probe: initalize the board
++ *
++ * This function is called when platform_device_add() is called.  It is used
++ * to initialize the board-specific hardware.
++ *
++ * Here we program the DMACR and PMUXCR registers.
++ */
++static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
++{
++	struct mpc8610_hpcd_data *machine_data =
++		sound_device->dev.platform_data;
++
++	/* Program the signal routing between the SSI and the DMA */
++	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
++		machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
++	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
++		machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
++
++	guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
++		machine_data->dma_channel_id[0], 0);
++	guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
++		machine_data->dma_channel_id[1], 0);
++
++	guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0);
++	guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0);
++	guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0);
++
++	switch (machine_data->ssi_id) {
++	case 0:
++		clrsetbits_be32(&machine_data->guts->pmuxcr,
++			CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
++		break;
++	case 1:
++		clrsetbits_be32(&machine_data->guts->pmuxcr,
++			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
++		break;
++	}
++
++	return 0;
++}
++
++/**
++ * mpc8610_hpcd_startup: program the board with various hardware parameters
++ *
++ * This function takes board-specific information, like clock frequencies
++ * and serial data formats, and passes that information to the codec and
++ * transport drivers.
++ */
++static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++	struct mpc8610_hpcd_data *machine_data =
++		rtd->socdev->dev->platform_data;
++	int ret = 0;
++
++	/* Tell the CPU driver what the serial protocol is. */
++	if (cpu_dai->dai_ops.set_fmt) {
++		ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
++			machine_data->dai_format);
++		if (ret < 0) {
++			dev_err(substream->pcm->card->dev,
++				"could not set CPU driver audio format\n");
++			return ret;
++		}
++	}
++
++	/* Tell the codec driver what the serial protocol is. */
++	if (codec_dai->dai_ops.set_fmt) {
++		ret = codec_dai->dai_ops.set_fmt(codec_dai,
++			machine_data->dai_format);
++		if (ret < 0) {
++			dev_err(substream->pcm->card->dev,
++				"could not set codec driver audio format\n");
++			return ret;
++		}
++	}
++
++	/*
++	 * Tell the CPU driver what the clock frequency is, and whether it's a
++	 * slave or master.
++	 */
++	if (cpu_dai->dai_ops.set_sysclk) {
++		ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0,
++			machine_data->clk_frequency,
++			machine_data->cpu_clk_direction);
++		if (ret < 0) {
++			dev_err(substream->pcm->card->dev,
++				"could not set CPU driver clock parameters\n");
++			return ret;
++		}
++	}
++
++	/*
++	 * Tell the codec driver what the MCLK frequency is, and whether it's
++	 * a slave or master.
++	 */
++	if (codec_dai->dai_ops.set_sysclk) {
++		ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0,
++			machine_data->clk_frequency,
++			machine_data->codec_clk_direction);
++		if (ret < 0) {
++			dev_err(substream->pcm->card->dev,
++				"could not set codec driver clock params\n");
++			return ret;
++		}
++	}
++
++	return 0;
++}
++
++/**
++ * mpc8610_hpcd_machine_remove: Remove the sound device
++ *
++ * This function is called to remove the sound device for one SSI.  We
++ * de-program the DMACR and PMUXCR register.
++ */
++int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
++{
++	struct mpc8610_hpcd_data *machine_data =
++		sound_device->dev.platform_data;
++
++	/* Restore the signal routing */
++
++	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
++		machine_data->dma_channel_id[0], 0);
++	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
++		machine_data->dma_channel_id[1], 0);
++
++	switch (machine_data->ssi_id) {
++	case 0:
++		clrsetbits_be32(&machine_data->guts->pmuxcr,
++			CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
++		break;
++	case 1:
++		clrsetbits_be32(&machine_data->guts->pmuxcr,
++			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
++		break;
++	}
++
++	return 0;
++}
++
++/**
++ * mpc8610_hpcd_ops: ASoC fabric driver operations
++ */
++static struct snd_soc_ops mpc8610_hpcd_ops = {
++	.startup = mpc8610_hpcd_startup,
++};
++
++/**
++ * mpc8610_hpcd_machine: ASoC machine data
++ */
++static struct snd_soc_machine mpc8610_hpcd_machine = {
++	.probe = mpc8610_hpcd_machine_probe,
++	.remove = mpc8610_hpcd_machine_remove,
++	.name = "MPC8610 HPCD",
++	.num_links = 1,
++};
++
++/**
++ * mpc8610_hpcd_probe: OF probe function for the fabric driver
++ *
++ * This function gets called when an SSI node is found in the device tree.
++ *
++ * Although this is a fabric driver, the SSI node is the "master" node with
++ * respect to audio hardware connections.  Therefore, we create a new ASoC
++ * device for each new SSI node that has a codec attached.
++ *
++ * FIXME: Currently, we only support one DMA controller, so if there are
++ * multiple SSI nodes with codecs, only the first will be supported.
++ *
++ * FIXME: Even if we did support multiple DMA controllers, we have no
++ * mechanism for assigning DMA controllers and channels to the individual
++ * SSI devices.  We also probably aren't compatible with the generic Elo DMA
++ * device driver.
++ */
++static int mpc8610_hpcd_probe(struct of_device *ofdev,
++	const struct of_device_id *match)
++{
++	struct device_node *np = ofdev->node;
++	struct device_node *codec_np = NULL;
++	struct device_node *guts_np = NULL;
++	struct device_node *dma_np = NULL;
++	struct device_node *dma_channel_np = NULL;
++	const phandle *codec_ph;
++	const char *sprop;
++	const u32 *iprop;
++	struct resource res;
++	struct platform_device *sound_device = NULL;
++	struct mpc8610_hpcd_data *machine_data;
++	struct fsl_ssi_info ssi_info;
++	struct fsl_dma_info dma_info;
++	int ret = -ENODEV;
++
++	machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
++	if (!machine_data)
++		return -ENOMEM;
++
++	memset(&ssi_info, 0, sizeof(ssi_info));
++	memset(&dma_info, 0, sizeof(dma_info));
++
++	ssi_info.dev = &ofdev->dev;
++
++	/*
++	 * We are only interested in SSIs with a codec phandle in them, so let's
++	 * make sure this SSI has one.
++	 */
++	codec_ph = of_get_property(np, "codec-handle", NULL);
++	if (!codec_ph)
++		goto error;
++
++	codec_np = of_find_node_by_phandle(*codec_ph);
++	if (!codec_np)
++		goto error;
++
++	/* The MPC8610 HPCD only knows about the CS4270 codec, so reject
++	   anything else. */
++	if (!of_device_is_compatible(codec_np, "cirrus,cs4270"))
++		goto error;
++
++	/* Get the device ID */
++	iprop = of_get_property(np, "cell-index", NULL);
++	if (!iprop) {
++		dev_err(&ofdev->dev, "cell-index property not found\n");
++		ret = -EINVAL;
++		goto error;
++	}
++	machine_data->ssi_id = *iprop;
++	ssi_info.id = *iprop;
++
++	/* Get the serial format and clock direction. */
++	sprop = of_get_property(np, "fsl,mode", NULL);
++	if (!sprop) {
++		dev_err(&ofdev->dev, "fsl,mode property not found\n");
++		ret = -EINVAL;
++		goto error;
++	}
++
++	if (strcasecmp(sprop, "i2s-slave") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_I2S;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
++
++		/*
++		 * In i2s-slave mode, the codec has its own clock source, so we
++		 * need to get the frequency from the device tree and pass it to
++		 * the codec driver.
++		 */
++		iprop = of_get_property(codec_np, "clock-frequency", NULL);
++		if (!iprop || !*iprop) {
++			dev_err(&ofdev->dev, "codec bus-frequency property "
++				"is missing or invalid\n");
++			ret = -EINVAL;
++			goto error;
++		}
++		machine_data->clk_frequency = *iprop;
++	} else if (strcasecmp(sprop, "i2s-master") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_I2S;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
++	} else if (strcasecmp(sprop, "lj-slave") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
++	} else if (strcasecmp(sprop, "lj-master") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
++	} else if (strcasecmp(sprop, "rj-master") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
++	} else if (strcasecmp(sprop, "rj-master") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
++	} else if (strcasecmp(sprop, "ac97-slave") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_AC97;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
++	} else if (strcasecmp(sprop, "ac97-master") == 0) {
++		machine_data->dai_format = SND_SOC_DAIFMT_AC97;
++		machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
++		machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
++	} else {
++		dev_err(&ofdev->dev,
++			"unrecognized fsl,mode property \"%s\"\n", sprop);
++		ret = -EINVAL;
++		goto error;
++	}
++
++	if (!machine_data->clk_frequency) {
++		dev_err(&ofdev->dev, "unknown clock frequency\n");
++		ret = -EINVAL;
++		goto error;
++	}
++
++	/* Read the SSI information from the device tree */
++	ret = of_address_to_resource(np, 0, &res);
++	if (ret) {
++		dev_err(&ofdev->dev, "could not obtain SSI address\n");
++		goto error;
++	}
++	if (!res.start) {
++		dev_err(&ofdev->dev, "invalid SSI address\n");
++		goto error;
++	}
++	ssi_info.ssi_phys = res.start;
++
++	machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi));
++	if (!machine_data->ssi) {
++		dev_err(&ofdev->dev, "could not map SSI address %x\n",
++			ssi_info.ssi_phys);
++		ret = -EINVAL;
++		goto error;
++	}
++	ssi_info.ssi = machine_data->ssi;
++
++
++	/* Get the IRQ of the SSI */
++	machine_data->ssi_irq = irq_of_parse_and_map(np, 0);
++	if (!machine_data->ssi_irq) {
++		dev_err(&ofdev->dev, "could not get SSI IRQ\n");
++		ret = -EINVAL;
++		goto error;
++	}
++	ssi_info.irq = machine_data->ssi_irq;
++
++
++	/* Map the global utilities registers. */
++	guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
++	if (!guts_np) {
++		dev_err(&ofdev->dev, "could not obtain address of GUTS\n");
++		ret = -EINVAL;
++		goto error;
++	}
++	machine_data->guts = of_iomap(guts_np, 0);
++	of_node_put(guts_np);
++	if (!machine_data->guts) {
++		dev_err(&ofdev->dev, "could not map GUTS\n");
++		ret = -EINVAL;
++		goto error;
++	}
++
++	/* Find the DMA channels to use.  For now, we always use the first DMA
++	   controller. */
++	for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {
++		iprop = of_get_property(dma_np, "cell-index", NULL);
++		if (iprop && (*iprop == 0)) {
++			of_node_put(dma_np);
++			break;
++		}
++	}
++	if (!dma_np) {
++		dev_err(&ofdev->dev, "could not find DMA node\n");
++		ret = -EINVAL;
++		goto error;
++	}
++	machine_data->dma_id = *iprop;
++
++	/*
++	 * Find the DMA channels to use.  For now, we always use DMA channel 0
++	 * for playback, and DMA channel 1 for capture.
++	 */
++	while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) {
++		iprop = of_get_property(dma_channel_np, "cell-index", NULL);
++		/* Is it DMA channel 0? */
++		if (iprop && (*iprop == 0)) {
++			/* dma_channel[0] and dma_irq[0] are for playback */
++			dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0);
++			dma_info.dma_irq[0] =
++				irq_of_parse_and_map(dma_channel_np, 0);
++			machine_data->dma_channel_id[0] = *iprop;
++			continue;
++		}
++		if (iprop && (*iprop == 1)) {
++			/* dma_channel[1] and dma_irq[1] are for capture */
++			dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0);
++			dma_info.dma_irq[1] =
++				irq_of_parse_and_map(dma_channel_np, 0);
++			machine_data->dma_channel_id[1] = *iprop;
++			continue;
++		}
++	}
++	if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] ||
++	    !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) {
++		dev_err(&ofdev->dev, "could not find DMA channels\n");
++		ret = -EINVAL;
++		goto error;
++	}
++
++	dma_info.ssi_stx_phys = ssi_info.ssi_phys +
++		offsetof(struct ccsr_ssi, stx0);
++	dma_info.ssi_srx_phys = ssi_info.ssi_phys +
++		offsetof(struct ccsr_ssi, srx0);
++
++	/* We have the DMA information, so tell the DMA driver what it is */
++	if (!fsl_dma_configure(&dma_info)) {
++		dev_err(&ofdev->dev, "could not instantiate DMA device\n");
++		ret = -EBUSY;
++		goto error;
++	}
++
++	/*
++	 * Initialize our DAI data structure.  We should probably get this
++	 * information from the device tree.
++	 */
++	machine_data->dai.name = "CS4270";
++	machine_data->dai.stream_name = "CS4270";
++
++	machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info);
++	machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */
++	machine_data->dai.ops = &mpc8610_hpcd_ops;
++
++	mpc8610_hpcd_machine.dai_link = &machine_data->dai;
++
++	/* Allocate a new audio platform device structure */
++	sound_device = platform_device_alloc("soc-audio", -1);
++	if (!sound_device) {
++		dev_err(&ofdev->dev, "platform device allocation failed\n");
++		ret = -ENOMEM;
++		goto error;
++	}
++
++	machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
++	machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
++	machine_data->sound_devdata.platform = &fsl_soc_platform;
++
++	sound_device->dev.platform_data = machine_data;
++
++
++	/* Set the platform device and ASoC device to point to each other */
++	platform_set_drvdata(sound_device, &machine_data->sound_devdata);
++
++	machine_data->sound_devdata.dev = &sound_device->dev;
++
++
++	/* Tell ASoC to probe us.  This will call mpc8610_hpcd_machine.probe(),
++	   if it exists. */
++	ret = platform_device_add(sound_device);
++
++	if (ret) {
++		dev_err(&ofdev->dev, "platform device add failed\n");
++		goto error;
++	}
++
++	dev_set_drvdata(&ofdev->dev, sound_device);
++
++	return 0;
++
++error:
++	of_node_put(codec_np);
++	of_node_put(guts_np);
++	of_node_put(dma_np);
++	of_node_put(dma_channel_np);
++
++	if (sound_device)
++		platform_device_unregister(sound_device);
++
++	if (machine_data->dai.cpu_dai)
++		fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
++
++	if (ssi_info.ssi)
++		iounmap(ssi_info.ssi);
++
++	if (ssi_info.irq)
++		irq_dispose_mapping(ssi_info.irq);
++
++	if (dma_info.dma_channel[0])
++		iounmap(dma_info.dma_channel[0]);
++
++	if (dma_info.dma_channel[1])
++		iounmap(dma_info.dma_channel[1]);
++
++	if (dma_info.dma_irq[0])
++		irq_dispose_mapping(dma_info.dma_irq[0]);
++
++	if (dma_info.dma_irq[1])
++		irq_dispose_mapping(dma_info.dma_irq[1]);
++
++	if (machine_data->guts)
++		iounmap(machine_data->guts);
++
++	kfree(machine_data);
++
++	return ret;
++}
++
++/**
++ * mpc8610_hpcd_remove: remove the OF device
++ *
++ * This function is called when the OF device is removed.
++ */
++static int mpc8610_hpcd_remove(struct of_device *ofdev)
++{
++	struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev);
++	struct mpc8610_hpcd_data *machine_data =
++		sound_device->dev.platform_data;
++
++	platform_device_unregister(sound_device);
++
++	if (machine_data->dai.cpu_dai)
++		fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
++
++	if (machine_data->ssi)
++		iounmap(machine_data->ssi);
++
++	if (machine_data->dma[0])
++		iounmap(machine_data->dma[0]);
++
++	if (machine_data->dma[1])
++		iounmap(machine_data->dma[1]);
++
++	if (machine_data->dma_irq[0])
++		irq_dispose_mapping(machine_data->dma_irq[0]);
++
++	if (machine_data->dma_irq[1])
++		irq_dispose_mapping(machine_data->dma_irq[1]);
++
++	if (machine_data->guts)
++		iounmap(machine_data->guts);
++
++	kfree(machine_data);
++	sound_device->dev.platform_data = NULL;
++
++	dev_set_drvdata(&ofdev->dev, NULL);
++
++	return 0;
++}
++
++static struct of_device_id mpc8610_hpcd_match[] = {
++	{
++		.compatible = "fsl,mpc8610-ssi",
++	},
++	{}
++};
++MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match);
++
++static struct of_platform_driver mpc8610_hpcd_of_driver = {
++	.owner  	= THIS_MODULE,
++	.name   	= "mpc8610_hpcd",
++	.match_table    = mpc8610_hpcd_match,
++	.probe  	= mpc8610_hpcd_probe,
++	.remove 	= mpc8610_hpcd_remove,
++};
++
++/**
++ * mpc8610_hpcd_init: fabric driver initialization.
++ *
++ * This function is called when this module is loaded.
++ */
++static int __init mpc8610_hpcd_init(void)
++{
++	int ret;
++
++	printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n");
++
++	ret = of_register_platform_driver(&mpc8610_hpcd_of_driver);
++
++	if (ret)
++		printk(KERN_ERR
++			"mpc8610-hpcd: failed to register platform driver\n");
++
++	return ret;
++}
++
++/**
++ * mpc8610_hpcd_exit: fabric driver exit
++ *
++ * This function is called when this driver is unloaded.
++ */
++static void __exit mpc8610_hpcd_exit(void)
++{
++	of_unregister_platform_driver(&mpc8610_hpcd_of_driver);
++}
++
++module_init(mpc8610_hpcd_init);
++module_exit(mpc8610_hpcd_exit);
++
++MODULE_AUTHOR("Timur Tabi <timur at freescale.com>");
++MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
+index a83e229..484f883 100644
+--- a/sound/soc/pxa/Kconfig
++++ b/sound/soc/pxa/Kconfig
+@@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA
+ 	help
+ 	  Say Y if you want to add support for SoC audio on Sharp
+ 	  Zaurus SL-C6000x models (Tosa).
++
++config SND_PXA2XX_SOC_E800
++	tristate "SoC AC97 Audio support for e800"
++	depends on SND_PXA2XX_SOC && MACH_E800
++	select SND_SOC_WM9712
++	select SND_PXA2XX_SOC_AC97
++	help
++	  Say Y if you want to add support for SoC audio on the
++	  Toshiba e800 PDA
+diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
+index 78e0d6b..04e5646 100644
+--- a/sound/soc/pxa/Makefile
++++ b/sound/soc/pxa/Makefile
+@@ -11,10 +11,12 @@ obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
+ snd-soc-corgi-objs := corgi.o
+ snd-soc-poodle-objs := poodle.o
+ snd-soc-tosa-objs := tosa.o
++snd-soc-e800-objs := e800_wm9712.o
+ snd-soc-spitz-objs := spitz.o
+ 
+ obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
+ obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
+ obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
++obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
+ obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
+ 
+diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
+index 5ee51a9..3f34e53 100644
+--- a/sound/soc/pxa/corgi.c
++++ b/sound/soc/pxa/corgi.c
+@@ -22,7 +22,6 @@
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
+new file mode 100644
+index 0000000..06e8afb
+--- /dev/null
++++ b/sound/soc/pxa/e800_wm9712.c
+@@ -0,0 +1,89 @@
++/*
++ * e800-wm9712.c  --  SoC audio for e800
++ *
++ * Based on tosa.c
++ *
++ * Copyright 2007 (c) Ian Molton <spyro at f2s.com>
++ *
++ *  This program 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; version 2 ONLY.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/mach-types.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/wm9712.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ac97.h"
++
++static struct snd_soc_machine e800;
++
++static struct snd_soc_dai_link e800_dai[] = {
++{
++	.name = "AC97 Aux",
++	.stream_name = "AC97 Aux",
++	.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
++	.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
++},
++};
++
++static struct snd_soc_machine e800 = {
++	.name = "Toshiba e800",
++	.dai_link = e800_dai,
++	.num_links = ARRAY_SIZE(e800_dai),
++};
++
++static struct snd_soc_device e800_snd_devdata = {
++	.machine = &e800,
++	.platform = &pxa2xx_soc_platform,
++	.codec_dev = &soc_codec_dev_wm9712,
++};
++
++static struct platform_device *e800_snd_device;
++
++static int __init e800_init(void)
++{
++	int ret;
++
++	if (!machine_is_e800())
++		return -ENODEV;
++
++	e800_snd_device = platform_device_alloc("soc-audio", -1);
++	if (!e800_snd_device)
++		return -ENOMEM;
++
++	platform_set_drvdata(e800_snd_device, &e800_snd_devdata);
++	e800_snd_devdata.dev = &e800_snd_device->dev;
++	ret = platform_device_add(e800_snd_device);
++
++	if (ret)
++		platform_device_put(e800_snd_device);
++
++	return ret;
++}
++
++static void __exit e800_exit(void)
++{
++	platform_device_unregister(e800_snd_device);
++}
++
++module_init(e800_init);
++module_exit(e800_exit);
++
++/* Module information */
++MODULE_AUTHOR("Ian Molton <spyro at f2s.com>");
++MODULE_DESCRIPTION("ALSA SoC driver for e800");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
+index 0915cf7..5ae59bd 100644
+--- a/sound/soc/pxa/poodle.c
++++ b/sound/soc/pxa/poodle.c
+@@ -19,7 +19,6 @@
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
+index 60e6f46..815c153 100644
+--- a/sound/soc/pxa/pxa2xx-ac97.c
++++ b/sound/soc/pxa/pxa2xx-ac97.c
+@@ -17,7 +17,6 @@
+ #include <linux/wait.h>
+ #include <linux/delay.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/ac97_codec.h>
+diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
+index 50c5c83..692b900 100644
+--- a/sound/soc/pxa/pxa2xx-i2s.c
++++ b/sound/soc/pxa/pxa2xx-i2s.c
+@@ -18,7 +18,6 @@
+ #include <linux/module.h>
+ #include <linux/device.h>
+ #include <linux/delay.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/initval.h>
+diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
+index 35e8fa3..daeaa4c 100644
+--- a/sound/soc/pxa/pxa2xx-pcm.c
++++ b/sound/soc/pxa/pxa2xx-pcm.c
+@@ -16,7 +16,6 @@
+ #include <linux/slab.h>
+ #include <linux/dma-mapping.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
+index 4dd8f35..d56709e 100644
+--- a/sound/soc/pxa/spitz.c
++++ b/sound/soc/pxa/spitz.c
+@@ -22,7 +22,6 @@
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
+index 5504e30..e4d40b5 100644
+--- a/sound/soc/pxa/tosa.c
++++ b/sound/soc/pxa/tosa.c
+@@ -25,7 +25,6 @@
+ #include <linux/moduleparam.h>
+ #include <linux/device.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
+index 5632a2e..1f6dbfc 100644
+--- a/sound/soc/s3c24xx/Kconfig
++++ b/sound/soc/s3c24xx/Kconfig
+@@ -10,6 +10,9 @@ config SND_S3C24XX_SOC
+ config SND_S3C24XX_SOC_I2S
+ 	tristate
+ 
++config SND_S3C2412_SOC_I2S
++	tristate
++
+ config SND_S3C2443_SOC_AC97
+ 	tristate
+ 	select AC97_BUS
+@@ -34,4 +37,12 @@ config SND_S3C24XX_SOC_SMDK2443_WM9710
+ 	  Say Y if you want to add support for SoC audio on smdk2443
+ 	  with the WM9710.
+ 
++config SND_S3C24XX_SOC_LN2440SBC_ALC650
++	tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
++	depends on SND_S3C24XX_SOC
++	select SND_S3C2443_SOC_AC97
++	select SND_SOC_AC97_CODEC
++	help
++	  Say Y if you want to add support for SoC audio on ln2440sbc
++	  with the ALC650.
+ 
+diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
+index 13c92f0..0aa5fb0 100644
+--- a/sound/soc/s3c24xx/Makefile
++++ b/sound/soc/s3c24xx/Makefile
+@@ -1,15 +1,19 @@
+ # S3c24XX Platform Support
+ snd-soc-s3c24xx-objs := s3c24xx-pcm.o
+ snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
++snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
+ snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
+ 
+ obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
+ obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
+ obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
++obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
+ 
+ # S3C24XX Machine Support
+ snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
+ snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
++snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
+ 
+ obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
+ obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
++obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
+diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c
+new file mode 100644
+index 0000000..9ed8f2e
+--- /dev/null
++++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c
+@@ -0,0 +1,85 @@
++/*
++ * SoC audio for ln2440sbc
++ * 
++ * Copyright 2007 KonekTel, a.s.
++ * Author: Ivan Kuten
++ *         ivan.kuten at promwad.com
++ * 
++ * Heavily based on smdk2443_wm9710.c
++ * Copyright 2007 Wolfson Microelectronics PLC.
++ * Author: Graeme Gregory
++ *         graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ *  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.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include "../codecs/ac97.h"
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-ac97.h"
++
++static struct snd_soc_machine ln2440sbc;
++
++static struct snd_soc_dai_link ln2440sbc_dai[] = {
++{
++	.name = "AC97",
++	.stream_name = "AC97 HiFi",
++	.cpu_dai = &s3c2443_ac97_dai[0],
++	.codec_dai = &ac97_dai,
++},
++};
++
++static struct snd_soc_machine ln2440sbc = {
++	.name = "LN2440SBC",
++	.dai_link = ln2440sbc_dai,
++	.num_links = ARRAY_SIZE(ln2440sbc_dai),
++};
++
++static struct snd_soc_device ln2440sbc_snd_ac97_devdata = {
++	.machine = &ln2440sbc,
++	.platform = &s3c24xx_soc_platform,
++	.codec_dev = &soc_codec_dev_ac97,
++};
++
++static struct platform_device *ln2440sbc_snd_ac97_device;
++
++static int __init ln2440sbc_init(void)
++{
++	int ret;
++
++	ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1);
++	if (!ln2440sbc_snd_ac97_device)
++		return -ENOMEM;
++
++	platform_set_drvdata(ln2440sbc_snd_ac97_device,
++				&ln2440sbc_snd_ac97_devdata);
++	ln2440sbc_snd_ac97_devdata.dev = &ln2440sbc_snd_ac97_device->dev;
++	ret = platform_device_add(ln2440sbc_snd_ac97_device);
++
++	if (ret)
++		platform_device_put(ln2440sbc_snd_ac97_device);
++
++	return ret;
++}
++
++static void __exit ln2440sbc_exit(void)
++{
++	platform_device_unregister(ln2440sbc_snd_ac97_device);
++}
++
++module_init(ln2440sbc_init);
++module_exit(ln2440sbc_exit);
++
++/* Module information */
++MODULE_AUTHOR("Ivan Kuten");
++MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
+index d5a8fc2..6ee115c 100644
+--- a/sound/soc/s3c24xx/neo1973_wm8753.c
++++ b/sound/soc/s3c24xx/neo1973_wm8753.c
+@@ -22,7 +22,6 @@
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+ #include <linux/i2c.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+@@ -30,13 +29,15 @@
+ 
+ #include <asm/mach-types.h>
+ #include <asm/hardware/scoop.h>
+-#include <asm/arch/regs-iis.h>
+ #include <asm/arch/regs-clock.h>
+ #include <asm/arch/regs-gpio.h>
+ #include <asm/hardware.h>
+ #include <asm/arch/audio.h>
+ #include <asm/io.h>
+ #include <asm/arch/spi-gpio.h>
++
++#include <asm/plat-s3c24xx/regs-iis.h>
++
+ #include "../codecs/wm8753.h"
+ #include "lm4857.h"
+ #include "s3c24xx-pcm.h"
+@@ -573,7 +574,7 @@ static struct snd_soc_device neo1973_snd_devdata = {
+ 
+ static struct i2c_client client_template;
+ 
+-static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
++static const unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
+ 
+ /* Magic definition of all other variables and things */
+ I2C_CLIENT_INSMOD;
+diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
+new file mode 100644
+index 0000000..c4a46dd
+--- /dev/null
++++ b/sound/soc/s3c24xx/s3c2412-i2s.c
+@@ -0,0 +1,744 @@
++/* sound/soc/s3c24xx/s3c2412-i2s.c
++ *
++ * ALSA Soc Audio Layer - S3C2412 I2S driver
++ *
++ * Copyright (c) 2006 Wolfson Microelectronics PLC.
++ *	Graeme Gregory graeme.gregory at wolfsonmicro.com
++ *	linux at wolfsonmicro.com
++ *
++ * Copyright (c) 2007, 2004-2005 Simtec Electronics
++ *	http://armlinux.simtec.co.uk/
++ *	Ben Dooks <ben at simtec.co.uk>
++ *
++ * This program 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.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/clk.h>
++#include <linux/kernel.h>
++
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++#include <asm/hardware.h>
++
++#include <linux/io.h>
++#include <asm/dma.h>
++
++#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/audio.h>
++#include <asm/arch/dma.h>
++
++#include "s3c24xx-pcm.h"
++#include "s3c2412-i2s.h"
++
++#define S3C2412_I2S_DEBUG 0
++#define S3C2412_I2S_DEBUG_CON 0
++
++#if S3C2412_I2S_DEBUG
++#define DBG(x...) printk(KERN_INFO x)
++#else
++#define DBG(x...) do { } while (0)
++#endif
++
++static struct s3c2410_dma_client s3c2412_dma_client_out = {
++	.name		= "I2S PCM Stereo out"
++};
++
++static struct s3c2410_dma_client s3c2412_dma_client_in = {
++	.name		= "I2S PCM Stereo in"
++};
++
++static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = {
++	.client		= &s3c2412_dma_client_out,
++	.channel	= DMACH_I2S_OUT,
++	.dma_addr	= S3C2410_PA_IIS + S3C2412_IISTXD,
++	.dma_size	= 4,
++};
++
++static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = {
++	.client		= &s3c2412_dma_client_in,
++	.channel	= DMACH_I2S_IN,
++	.dma_addr	= S3C2410_PA_IIS + S3C2412_IISRXD,
++	.dma_size	= 4,
++};
++
++struct s3c2412_i2s_info {
++	struct device	*dev;
++	void __iomem	*regs;
++	struct clk	*iis_clk;
++	struct clk	*iis_pclk;
++	struct clk	*iis_cclk;
++
++	u32		 suspend_iismod;
++	u32		 suspend_iiscon;
++	u32		 suspend_iispsr;
++};
++
++static struct s3c2412_i2s_info s3c2412_i2s;
++
++#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
++
++#if S3C2412_I2S_DEBUG_CON
++static void dbg_showcon(const char *fn, u32 con)
++{
++	printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
++	       bit_set(con, S3C2412_IISCON_LRINDEX),
++	       bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
++	       bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
++	       bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
++	       bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
++
++	printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
++	       fn,
++	       bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
++	       bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
++	       bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
++	       bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
++	printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
++	       bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
++	       bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
++	       bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
++}
++#else
++static inline void dbg_showcon(const char *fn, u32 con)
++{
++}
++#endif
++
++/* Turn on or off the transmission path. */
++static void s3c2412_snd_txctrl(int on)
++{
++	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
++	void __iomem *regs = i2s->regs;
++	u32 fic, con, mod;
++
++	DBG("%s(%d)\n", __func__, on);
++
++	fic = readl(regs + S3C2412_IISFIC);
++	con = readl(regs + S3C2412_IISCON);
++	mod = readl(regs + S3C2412_IISMOD);
++
++	DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
++
++	if (on) {
++		con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
++		con &= ~S3C2412_IISCON_TXDMA_PAUSE;
++		con &= ~S3C2412_IISCON_TXCH_PAUSE;
++
++		switch (mod & S3C2412_IISMOD_MODE_MASK) {
++		case S3C2412_IISMOD_MODE_TXONLY:
++		case S3C2412_IISMOD_MODE_TXRX:
++			/* do nothing, we are in the right mode */
++			break;
++
++		case S3C2412_IISMOD_MODE_RXONLY:
++			mod &= ~S3C2412_IISMOD_MODE_MASK;
++			mod |= S3C2412_IISMOD_MODE_TXRX;
++			break;
++
++		default:
++			dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
++		}
++
++		writel(con, regs + S3C2412_IISCON);
++		writel(mod, regs + S3C2412_IISMOD);
++	} else {
++		/* Note, we do not have any indication that the FIFO problems
++		 * tha the S3C2410/2440 had apply here, so we should be able
++		 * to disable the DMA and TX without resetting the FIFOS.
++		 */
++
++		con |=  S3C2412_IISCON_TXDMA_PAUSE;
++		con |=  S3C2412_IISCON_TXCH_PAUSE;
++		con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
++
++		switch (mod & S3C2412_IISMOD_MODE_MASK) {
++		case S3C2412_IISMOD_MODE_TXRX:
++			mod &= ~S3C2412_IISMOD_MODE_MASK;
++			mod |= S3C2412_IISMOD_MODE_RXONLY;
++			break;
++
++		case S3C2412_IISMOD_MODE_TXONLY:
++			mod &= ~S3C2412_IISMOD_MODE_MASK;
++			con &= ~S3C2412_IISCON_IIS_ACTIVE;
++			break;
++
++		default:
++			dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
++		}
++
++		writel(mod, regs + S3C2412_IISMOD);
++		writel(con, regs + S3C2412_IISCON);
++	}
++
++	fic = readl(regs + S3C2412_IISFIC);
++	dbg_showcon(__func__, con);
++	DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
++}
++
++static void s3c2412_snd_rxctrl(int on)
++{
++	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
++	void __iomem *regs = i2s->regs;
++	u32 fic, con, mod;
++
++	DBG("%s(%d)\n", __func__, on);
++
++	fic = readl(regs + S3C2412_IISFIC);
++	con = readl(regs + S3C2412_IISCON);
++	mod = readl(regs + S3C2412_IISMOD);
++
++	DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
++
++	if (on) {
++		con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
++		con &= ~S3C2412_IISCON_RXDMA_PAUSE;
++		con &= ~S3C2412_IISCON_RXCH_PAUSE;
++
++		switch (mod & S3C2412_IISMOD_MODE_MASK) {
++		case S3C2412_IISMOD_MODE_TXRX:
++		case S3C2412_IISMOD_MODE_RXONLY:
++			/* do nothing, we are in the right mode */
++			break;
++
++		case S3C2412_IISMOD_MODE_TXONLY:
++			mod &= ~S3C2412_IISMOD_MODE_MASK;
++			mod |= S3C2412_IISMOD_MODE_TXRX;
++			break;
++
++		default:
++			dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
++		}
++
++		writel(mod, regs + S3C2412_IISMOD);
++		writel(con, regs + S3C2412_IISCON);
++	} else {
++		/* See txctrl notes on FIFOs. */
++
++		con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
++		con |=  S3C2412_IISCON_RXDMA_PAUSE;
++		con |=  S3C2412_IISCON_RXCH_PAUSE;
++
++		switch (mod & S3C2412_IISMOD_MODE_MASK) {
++		case S3C2412_IISMOD_MODE_RXONLY:
++			con &= ~S3C2412_IISCON_IIS_ACTIVE;
++			mod &= ~S3C2412_IISMOD_MODE_MASK;
++			break;
++
++		case S3C2412_IISMOD_MODE_TXRX:
++			mod &= ~S3C2412_IISMOD_MODE_MASK;
++			mod |= S3C2412_IISMOD_MODE_TXONLY;
++			break;
++
++		default:
++			dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
++		}
++
++		writel(con, regs + S3C2412_IISCON);
++		writel(mod, regs + S3C2412_IISMOD);
++	}
++
++	fic = readl(regs + S3C2412_IISFIC);
++	DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
++}
++
++
++/*
++ * Wait for the LR signal to allow synchronisation to the L/R clock
++ * from the codec. May only be needed for slave mode.
++ */
++static int s3c2412_snd_lrsync(void)
++{
++	u32 iiscon;
++	unsigned long timeout = jiffies + msecs_to_jiffies(5);
++
++	DBG("Entered %s\n", __func__);
++
++	while (1) {
++		iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON);
++		if (iiscon & S3C2412_IISCON_LRINDEX)
++			break;
++
++		if (timeout < jiffies) {
++			printk(KERN_ERR "%s: timeout\n", __func__);
++			return -ETIMEDOUT;
++		}
++	}
++
++	return 0;
++}
++
++/*
++ * Check whether CPU is the master or slave
++ */
++static inline int s3c2412_snd_is_clkmaster(void)
++{
++	u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
++
++	DBG("Entered %s\n", __func__);
++
++	iismod &= S3C2412_IISMOD_MASTER_MASK;
++	return !(iismod == S3C2412_IISMOD_SLAVE);
++}
++
++/*
++ * Set S3C2412 I2S DAI format
++ */
++static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
++			       unsigned int fmt)
++{
++	u32 iismod;
++
++
++	DBG("Entered %s\n", __func__);
++
++	iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
++	DBG("hw_params r: IISMOD: %x \n", iismod);
++
++	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++	case SND_SOC_DAIFMT_CBM_CFM:
++		iismod &= ~S3C2412_IISMOD_MASTER_MASK;
++		iismod |= S3C2412_IISMOD_SLAVE;
++		break;
++	case SND_SOC_DAIFMT_CBS_CFS:
++		iismod &= ~S3C2412_IISMOD_MASTER_MASK;
++		iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
++		break;
++	default:
++		DBG("unknwon master/slave format\n");
++		return -EINVAL;
++	}
++
++	iismod &= ~S3C2412_IISMOD_SDF_MASK;
++
++	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++	case SND_SOC_DAIFMT_RIGHT_J:
++		iismod |= S3C2412_IISMOD_SDF_MSB;
++		break;
++	case SND_SOC_DAIFMT_LEFT_J:
++		iismod |= S3C2412_IISMOD_SDF_LSB;
++		break;
++	case SND_SOC_DAIFMT_I2S:
++		iismod |= S3C2412_IISMOD_SDF_IIS;
++		break;
++	default:
++		DBG("Unknown data format\n");
++		return -EINVAL;
++	}
++
++	writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
++	DBG("hw_params w: IISMOD: %x \n", iismod);
++	return 0;
++}
++
++static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
++				 struct snd_pcm_hw_params *params)
++{
++	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	u32 iismod;
++
++	DBG("Entered %s\n", __func__);
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out;
++	else
++		rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in;
++
++	/* Working copies of register */
++	iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
++	DBG("%s: r: IISMOD: %x\n", __func__, iismod);
++
++	switch (params_format(params)) {
++	case SNDRV_PCM_FORMAT_S8:
++		iismod |= S3C2412_IISMOD_8BIT;
++		break;
++	case SNDRV_PCM_FORMAT_S16_LE:
++		iismod &= ~S3C2412_IISMOD_8BIT;
++		break;
++	}
++
++	writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
++	DBG("%s: w: IISMOD: %x\n", __func__, iismod);
++	return 0;
++}
++
++static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++	int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
++	unsigned long irqs;
++	int ret = 0;
++
++	DBG("Entered %s\n", __func__);
++
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++		/* On start, ensure that the FIFOs are cleared and reset. */
++
++		writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
++		       s3c2412_i2s.regs + S3C2412_IISFIC);
++
++		/* clear again, just in case */
++		writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC);
++
++	case SNDRV_PCM_TRIGGER_RESUME:
++	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++		if (!s3c2412_snd_is_clkmaster()) {
++			ret = s3c2412_snd_lrsync();
++			if (ret)
++				goto exit_err;
++		}
++
++		local_irq_save(irqs);
++
++		if (capture)
++			s3c2412_snd_rxctrl(1);
++		else
++			s3c2412_snd_txctrl(1);
++
++		local_irq_restore(irqs);
++		break;
++
++	case SNDRV_PCM_TRIGGER_STOP:
++	case SNDRV_PCM_TRIGGER_SUSPEND:
++	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++		local_irq_save(irqs);
++
++		if (capture)
++			s3c2412_snd_rxctrl(0);
++		else
++			s3c2412_snd_txctrl(0);
++
++		local_irq_restore(irqs);
++		break;
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++exit_err:
++	return ret;
++}
++
++/* default table of all avaialable root fs divisors */
++static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 };
++
++int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
++			  unsigned int *fstab,
++			  unsigned int rate, struct clk *clk)
++{
++	unsigned long clkrate = clk_get_rate(clk);
++	unsigned int div;
++	unsigned int fsclk;
++	unsigned int actual;
++	unsigned int fs;
++	unsigned int fsdiv;
++	signed int deviation = 0;
++	unsigned int best_fs = 0;
++	unsigned int best_div = 0;
++	unsigned int best_rate = 0;
++	unsigned int best_deviation = INT_MAX;
++
++
++	if (fstab == NULL)
++		fstab = s3c2412_iis_fs;
++
++	for (fs = 0;; fs++) {
++		fsdiv = s3c2412_iis_fs[fs];
++
++		if (fsdiv == 0)
++			break;
++
++		fsclk = clkrate / fsdiv;
++		div = fsclk / rate;
++
++		if ((fsclk % rate) > (rate / 2))
++			div++;
++
++		if (div <= 1)
++			continue;
++
++		actual = clkrate / (fsdiv * div);
++		deviation = actual - rate;
++
++		printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
++		       fsdiv, div, actual, deviation);
++
++		deviation = abs(deviation);
++
++		if (deviation < best_deviation) {
++			best_fs = fsdiv;
++			best_div = div;
++			best_rate = actual;
++			best_deviation = deviation;
++		}
++
++		if (deviation == 0)
++			break;
++	}
++
++	printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
++	       best_fs, best_div, best_rate);
++
++	info->fs_div = best_fs;
++	info->clk_div = best_div;
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
++
++/*
++ * Set S3C2412 Clock source
++ */
++static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
++				  int clk_id, unsigned int freq, int dir)
++{
++	u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
++
++	DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
++	    freq, dir);
++
++	switch (clk_id) {
++	case S3C2412_CLKSRC_PCLK:
++		iismod &= ~S3C2412_IISMOD_MASTER_MASK;
++		iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
++		break;
++	case S3C2412_CLKSRC_I2SCLK:
++		iismod &= ~S3C2412_IISMOD_MASTER_MASK;
++		iismod |= S3C2412_IISMOD_MASTER_EXTERNAL;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
++	return 0;
++}
++
++/*
++ * Set S3C2412 Clock dividers
++ */
++static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
++				  int div_id, int div)
++{
++	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
++	u32 reg;
++
++	DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
++
++	switch (div_id) {
++	case S3C2412_DIV_BCLK:
++		reg = readl(i2s->regs + S3C2412_IISMOD);
++		reg &= ~S3C2412_IISMOD_BCLK_MASK;
++		writel(reg | div, i2s->regs + S3C2412_IISMOD);
++
++		DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
++		break;
++
++	case S3C2412_DIV_RCLK:
++		if (div > 3) {
++			/* convert value to bit field */
++
++			switch (div) {
++			case 256:
++				div = S3C2412_IISMOD_RCLK_256FS;
++				break;
++
++			case 384:
++				div = S3C2412_IISMOD_RCLK_384FS;
++				break;
++
++			case 512:
++				div = S3C2412_IISMOD_RCLK_512FS;
++				break;
++
++			case 768:
++				div = S3C2412_IISMOD_RCLK_768FS;
++				break;
++
++			default:
++				return -EINVAL;
++			}
++		}
++
++		reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
++		reg &= ~S3C2412_IISMOD_RCLK_MASK;
++		writel(reg | div, i2s->regs + S3C2412_IISMOD);
++		DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
++		break;
++
++	case S3C2412_DIV_PRESCALER:
++		if (div >= 0) {
++			writel((div << 8) | S3C2412_IISPSR_PSREN,
++			       i2s->regs + S3C2412_IISPSR);
++		} else {
++			writel(0x0, i2s->regs + S3C2412_IISPSR);
++		}
++		DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
++		break;
++
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++struct clk *s3c2412_get_iisclk(void)
++{
++	return s3c2412_i2s.iis_clk;
++}
++EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
++
++
++static int s3c2412_i2s_probe(struct platform_device *pdev)
++{
++	DBG("Entered %s\n", __func__);
++
++	s3c2412_i2s.dev = &pdev->dev;
++
++	s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
++	if (s3c2412_i2s.regs == NULL)
++		return -ENXIO;
++
++	s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis");
++	if (s3c2412_i2s.iis_pclk == NULL) {
++		DBG("failed to get iis_clock\n");
++		iounmap(s3c2412_i2s.regs);
++		return -ENODEV;
++	}
++
++	s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
++	if (s3c2412_i2s.iis_cclk == NULL) {
++		DBG("failed to get i2sclk clock\n");
++		iounmap(s3c2412_i2s.regs);
++		return -ENODEV;
++	}
++
++	clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
++
++	clk_enable(s3c2412_i2s.iis_pclk);
++	clk_enable(s3c2412_i2s.iis_cclk);
++
++	s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk;
++
++	/* Configure the I2S pins in correct mode */
++	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
++	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
++	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
++	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
++	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
++
++	s3c2412_snd_txctrl(0);
++	s3c2412_snd_rxctrl(0);
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int s3c2412_i2s_suspend(struct platform_device *dev,
++			      struct snd_soc_cpu_dai *dai)
++{
++	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
++	u32 iismod;
++
++	if (dai->active) {
++		i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
++		i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
++		i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
++
++		/* some basic suspend checks */
++
++		iismod = readl(i2s->regs + S3C2412_IISMOD);
++
++		if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
++			dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__);
++
++		if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
++			dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__);
++
++		if (iismod & S3C2412_IISCON_IIS_ACTIVE)
++			dev_warn(&dev->dev, "%s: IIS active\n", __func__);
++	}
++
++	return 0;
++}
++
++static int s3c2412_i2s_resume(struct platform_device *pdev,
++			      struct snd_soc_cpu_dai *dai)
++{
++	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
++
++	dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n",
++		 dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
++
++	if (dai->active) {
++		writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
++		writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
++		writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
++
++		writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
++		       i2s->regs + S3C2412_IISFIC);
++
++		ndelay(250);
++		writel(0x0, i2s->regs + S3C2412_IISFIC);
++
++	}
++
++	return 0;
++}
++#else
++#define s3c2412_i2s_suspend NULL
++#define s3c2412_i2s_resume  NULL
++#endif /* CONFIG_PM */
++
++#define S3C2412_I2S_RATES \
++	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++struct snd_soc_cpu_dai s3c2412_i2s_dai = {
++	.name	= "s3c2412-i2s",
++	.id	= 0,
++	.type	= SND_SOC_DAI_I2S,
++	.probe	= s3c2412_i2s_probe,
++	.suspend = s3c2412_i2s_suspend,
++	.resume = s3c2412_i2s_resume,
++	.playback = {
++		.channels_min	= 2,
++		.channels_max	= 2,
++		.rates		= S3C2412_I2S_RATES,
++		.formats	= SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
++	},
++	.capture = {
++		.channels_min	= 2,
++		.channels_max	= 2,
++		.rates		= S3C2412_I2S_RATES,
++		.formats	= SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
++	},
++	.ops = {
++		.trigger	= s3c2412_i2s_trigger,
++		.hw_params	= s3c2412_i2s_hw_params,
++	},
++	.dai_ops = {
++		.set_fmt	= s3c2412_i2s_set_fmt,
++		.set_clkdiv	= s3c2412_i2s_set_clkdiv,
++		.set_sysclk	= s3c2412_i2s_set_sysclk,
++	},
++};
++EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
++
++/* Module information */
++MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
++MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
+new file mode 100644
+index 0000000..27f48e1
+--- /dev/null
++++ b/sound/soc/s3c24xx/s3c2412-i2s.h
+@@ -0,0 +1,38 @@
++/* sound/soc/s3c24xx/s3c2412-i2s.c
++ *
++ * ALSA Soc Audio Layer - S3C2412 I2S driver
++ *
++ * Copyright (c) 2007 Simtec Electronics
++ *	http://armlinux.simtec.co.uk/
++ *	Ben Dooks <ben at simtec.co.uk>
++ *
++ *  This program 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.
++*/
++
++#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H
++#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
++
++#define S3C2412_DIV_BCLK	(1)
++#define S3C2412_DIV_RCLK	(2)
++#define S3C2412_DIV_PRESCALER	(3)
++
++#define S3C2412_CLKSRC_PCLK	(0)
++#define S3C2412_CLKSRC_I2SCLK	(1)
++
++extern struct clk *s3c2412_get_iisclk(void);
++
++extern struct snd_soc_cpu_dai s3c2412_i2s_dai;
++
++struct s3c2412_rate_calc {
++	unsigned int	clk_div;	/* for prescaler */
++	unsigned int	fs_div;		/* for root frame clock */
++};
++
++extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
++				 unsigned int *fstab,
++				 unsigned int rate, struct clk *clk);
++
++#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
+diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
+index 758a263..1c1ddbf 100644
+--- a/sound/soc/s3c24xx/s3c2443-ac97.c
++++ b/sound/soc/s3c24xx/s3c2443-ac97.c
+@@ -23,7 +23,6 @@
+ #include <linux/delay.h>
+ #include <linux/clk.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/ac97_codec.h>
+@@ -253,7 +252,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
+ 	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+ 	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+ 
+-	ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
++	ret = request_irq(IRQ_S3C244x_AC97, s3c2443_ac97_irq,
+ 		IRQF_DISABLED, "AC97", NULL);
+ 	if (ret < 0) {
+ 		printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
+@@ -266,7 +265,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
+ 
+ static void s3c2443_ac97_remove(struct platform_device *pdev)
+ {
+-	free_irq(IRQ_S3C2443_AC97, NULL);
++	free_irq(IRQ_S3C244x_AC97, NULL);
+ 	clk_disable(s3c24xx_ac97.ac97_clk);
+ 	clk_put(s3c24xx_ac97.ac97_clk);
+ 	iounmap(s3c24xx_ac97.regs);
+diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
+index 2b835e8..bf03e8e 100644
+--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
++++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
+@@ -20,6 +20,12 @@
+ #define AC_CMD_ADDR(x) (x << 16)
+ #define AC_CMD_DATA(x) (x & 0xffff)
+ 
++#ifdef CONFIG_CPU_S3C2440
++#define IRQ_S3C244x_AC97 IRQ_S3C2440_AC97
++#else
++#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
++#endif
++
+ extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+ 
+ #endif /*S3C24XXAC97_H_*/
+diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
+index cd89c41..0a3c630 100644
+--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
++++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
+@@ -24,7 +24,7 @@
+ #include <linux/device.h>
+ #include <linux/delay.h>
+ #include <linux/clk.h>
+-#include <sound/driver.h>
++#include <linux/jiffies.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -33,13 +33,14 @@
+ 
+ #include <asm/hardware.h>
+ #include <asm/io.h>
+-#include <asm/arch/regs-iis.h>
+ #include <asm/arch/regs-gpio.h>
+ #include <asm/arch/regs-clock.h>
+ #include <asm/arch/audio.h>
+ #include <asm/dma.h>
+ #include <asm/arch/dma.h>
+ 
++#include <asm/plat-s3c24xx/regs-iis.h>
++
+ #include "s3c24xx-pcm.h"
+ #include "s3c24xx-i2s.h"
+ 
+@@ -75,6 +76,10 @@ static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
+ struct s3c24xx_i2s_info {
+ 	void __iomem	*regs;
+ 	struct clk	*iis_clk;
++	u32		iiscon;
++	u32		iismod;
++	u32		iisfcon;
++	u32		iispsr;
+ };
+ static struct s3c24xx_i2s_info s3c24xx_i2s;
+ 
+@@ -184,7 +189,7 @@ static int s3c24xx_snd_lrsync(void)
+ 		if (iiscon & S3C2410_IISCON_LRINDEX)
+ 			break;
+ 
+-		if (timeout < jiffies)
++		if (time_after(jiffies, timeout))
+ 			return -ETIMEDOUT;
+ 	}
+ 
+@@ -405,6 +410,38 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev)
+ 	return 0;
+ }
+ 
++#ifdef CONFIG_PM
++int s3c24xx_i2s_suspend(struct platform_device *pdev,
++		struct snd_soc_cpu_dai *cpu_dai)
++{
++	s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
++	s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
++	s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
++	s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
++
++	clk_disable(s3c24xx_i2s.iis_clk);
++
++	return 0;
++}
++
++int s3c24xx_i2s_resume(struct platform_device *pdev,
++		struct snd_soc_cpu_dai *cpu_dai)
++{
++	clk_enable(s3c24xx_i2s.iis_clk);
++
++	writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
++	writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
++	writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
++	writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
++
++	return 0;
++}
++#else
++#define s3c24xx_i2s_suspend NULL
++#define s3c24xx_i2s_resume NULL
++#endif
++
++
+ #define S3C24XX_I2S_RATES \
+ 	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+ 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+@@ -415,6 +452,8 @@ struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
+ 	.id = 0,
+ 	.type = SND_SOC_DAI_I2S,
+ 	.probe = s3c24xx_i2s_probe,
++	.suspend = s3c24xx_i2s_suspend,
++	.resume = s3c24xx_i2s_resume,
+ 	.playback = {
+ 		.channels_min = 2,
+ 		.channels_max = 2,
+diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
+index 4107a87..29a6c82 100644
+--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
++++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
+@@ -24,7 +24,6 @@
+ #include <linux/slab.h>
+ #include <linux/dma-mapping.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -49,7 +48,9 @@ static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
+ 	.info			= SNDRV_PCM_INFO_INTERLEAVED |
+ 				    SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ 				    SNDRV_PCM_INFO_MMAP |
+-				    SNDRV_PCM_INFO_MMAP_VALID,
++				    SNDRV_PCM_INFO_MMAP_VALID |
++				    SNDRV_PCM_INFO_PAUSE |
++				    SNDRV_PCM_INFO_RESUME,
+ 	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
+ 				    SNDRV_PCM_FMTBIT_U16_LE |
+ 				    SNDRV_PCM_FMTBIT_U8 |
+@@ -176,28 +177,6 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ 		}
+ 	}
+ 
+-	/* channel needs configuring for mem=>device, increment memory addr,
+-	 * sync to pclk, half-word transfers to the IIS-FIFO. */
+-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+-		s3c2410_dma_devconfig(prtd->params->channel,
+-				S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
+-				S3C2410_DISRCC_APB, prtd->params->dma_addr);
+-
+-		s3c2410_dma_config(prtd->params->channel,
+-				prtd->params->dma_size,
+-				S3C2410_DCON_SYNC_PCLK | 
+-				S3C2410_DCON_HANDSHAKE);
+-	} else {
+-		s3c2410_dma_config(prtd->params->channel,
+-				prtd->params->dma_size,
+-				S3C2410_DCON_HANDSHAKE | 
+-				S3C2410_DCON_SYNC_PCLK);
+-
+-		s3c2410_dma_devconfig(prtd->params->channel,
+-					S3C2410_DMASRC_HW, 0x3,
+-					prtd->params->dma_addr);
+-	}
+-
+ 	s3c2410_dma_set_buffdone_fn(prtd->params->channel,
+ 				    s3c24xx_audio_buffdone);
+ 
+@@ -246,6 +225,28 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
+ 	if (!prtd->params)
+ 	 	return 0;
+ 
++	/* channel needs configuring for mem=>device, increment memory addr,
++	 * sync to pclk, half-word transfers to the IIS-FIFO. */
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++		s3c2410_dma_devconfig(prtd->params->channel,
++				S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
++				S3C2410_DISRCC_APB, prtd->params->dma_addr);
++
++		s3c2410_dma_config(prtd->params->channel,
++				prtd->params->dma_size,
++				S3C2410_DCON_SYNC_PCLK |
++				S3C2410_DCON_HANDSHAKE);
++	} else {
++		s3c2410_dma_config(prtd->params->channel,
++				prtd->params->dma_size,
++				S3C2410_DCON_HANDSHAKE |
++				S3C2410_DCON_SYNC_PCLK);
++
++		s3c2410_dma_devconfig(prtd->params->channel,
++					S3C2410_DMASRC_HW, 0x3,
++					prtd->params->dma_addr);
++	}
++
+ 	/* flush the DMA channel */
+ 	s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
+ 	prtd->dma_loaded = 0;
+diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
+index d46cd81..b4a5630 100644
+--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
++++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
+@@ -17,7 +17,6 @@
+ 
+ #include <linux/module.h>
+ #include <linux/device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
+index cdee374..7a3ce80 100644
+--- a/sound/soc/sh/dma-sh7760.c
++++ b/sound/soc/sh/dma-sh7760.c
+@@ -16,7 +16,6 @@
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/dma-mapping.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
+index 8e3f039..b7b676b 100644
+--- a/sound/soc/sh/hac.c
++++ b/sound/soc/sh/hac.c
+@@ -21,7 +21,6 @@
+ #include <linux/interrupt.h>
+ #include <linux/wait.h>
+ #include <linux/delay.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/ac97_codec.h>
+@@ -105,7 +104,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
+ 	unsigned int to1, to2, i;
+ 	unsigned short adr;
+ 
+-	for (i = 0; i < AC97_READ_RETRY; ++i) {
++	for (i = AC97_READ_RETRY; i; i--) {
+ 		*v = 0;
+ 		/* wait for HAC to receive something from the codec */
+ 		for (to1 = TMO_E4;
+@@ -132,7 +131,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
+ 		udelay(21);
+ 	}
+ 	HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+-	return (i < AC97_READ_RETRY);
++	return i;
+ }
+ 
+ static unsigned short hac_read_codec_aux(struct hac_priv *hac,
+@@ -141,7 +140,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac,
+ 	unsigned short val;
+ 	unsigned int i, to;
+ 
+-	for (i = 0; i < AC97_READ_RETRY; i++) {
++	for (i = AC97_READ_RETRY; i; i--) {
+ 		/* send_read_request */
+ 		local_irq_disable();
+ 		HACREG(HACTSR) &= ~(TSR_CMDAMT);
+@@ -159,10 +158,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac,
+ 			break;
+ 	}
+ 
+-	if (i == AC97_READ_RETRY)
+-		return ~0;
+-
+-	return val;
++	return i ? val : ~0;
+ }
+ 
+ static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+@@ -172,7 +168,7 @@ static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ 	struct hac_priv *hac = &hac_cpu_data[unit_id];
+ 	unsigned int i, to;
+ 	/* write_codec_aux */
+-	for (i = 0; i < AC97_WRITE_RETRY; i++) {
++	for (i = AC97_WRITE_RETRY; i; i--) {
+ 		/* send_write_request */
+ 		local_irq_disable();
+ 		HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
+diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
+index 5563f14..2f91de8 100644
+--- a/sound/soc/sh/sh7760-ac97.c
++++ b/sound/soc/sh/sh7760-ac97.c
+@@ -9,7 +9,6 @@
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/soc.h>
+diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
+index b72bc31..3388bc3 100644
+--- a/sound/soc/sh/ssi.c
++++ b/sound/soc/sh/ssi.c
+@@ -30,7 +30,6 @@
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/initval.h>
+diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
+index e6a67b5..9eb5479 100644
+--- a/sound/soc/soc-core.c
++++ b/sound/soc/soc-core.c
+@@ -32,7 +32,6 @@
+ #include <linux/pm.h>
+ #include <linux/bitops.h>
+ #include <linux/platform_device.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -288,16 +287,25 @@ static void close_delayed_work(struct work_struct *work)
+ 		/* are we waiting on this codec DAI stream */
+ 		if (codec_dai->pop_wait == 1) {
+ 
++			/* power down the codec to D1 if no longer active */
++			if (codec->active == 0) {
++				dbg("pop wq D1 %s %s\n", codec->name,
++					codec_dai->playback.stream_name);
++				snd_soc_dapm_device_event(socdev,
++					SNDRV_CTL_POWER_D1);
++			}
++
+ 			codec_dai->pop_wait = 0;
+-			snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name,
++			snd_soc_dapm_stream_event(codec,
++				codec_dai->playback.stream_name,
+ 				SND_SOC_DAPM_STREAM_STOP);
+ 
+ 			/* power down the codec power domain if no longer active */
+ 			if (codec->active == 0) {
+ 				dbg("pop wq D3 %s %s\n", codec->name,
+ 					codec_dai->playback.stream_name);
+-		 		if (codec->dapm_event)
+-					codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++				snd_soc_dapm_device_event(socdev,
++					SNDRV_CTL_POWER_D3hot);
+ 			}
+ 		}
+ 	}
+@@ -353,12 +361,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
+ 	} else {
+ 		/* capture streams can be powered down now */
+ 		snd_soc_dapm_stream_event(codec,
+-			codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP);
++			codec_dai->capture.stream_name,
++			SND_SOC_DAPM_STREAM_STOP);
+ 
+-		if (codec->active == 0 && codec_dai->pop_wait == 0){
+-			if (codec->dapm_event)
+-				codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+-		}
++		if (codec->active == 0 && codec_dai->pop_wait == 0)
++			snd_soc_dapm_device_event(socdev,
++						SNDRV_CTL_POWER_D3hot);
+ 	}
+ 
+ 	mutex_unlock(&pcm_mutex);
+@@ -433,8 +441,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+ 		/* no delayed work - do we need to power up codec */
+ 		if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+ 
+-			if (codec->dapm_event)
+-				codec->dapm_event(codec, SNDRV_CTL_POWER_D1);
++			snd_soc_dapm_device_event(socdev,  SNDRV_CTL_POWER_D1);
+ 
+ 			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ 				snd_soc_dapm_stream_event(codec,
+@@ -445,8 +452,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+ 					codec_dai->capture.stream_name,
+ 					SND_SOC_DAPM_STREAM_START);
+ 
+-			if (codec->dapm_event)
+-				codec->dapm_event(codec, SNDRV_CTL_POWER_D0);
++			snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0);
+ 			if (codec_dai->dai_ops.digital_mute)
+ 				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+ 
+@@ -639,6 +645,10 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
+ 			dai->dai_ops.digital_mute(dai, 1);
+ 	}
+ 
++	/* suspend all pcms */
++	for (i = 0; i < machine->num_links; i++)
++		snd_pcm_suspend_all(machine->dai_link[i].pcm);
++
+ 	if (machine->suspend_pre)
+ 		machine->suspend_pre(pdev, state);
+ 
+@@ -873,6 +883,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
+ 		return ret;
+ 	}
+ 
++	dai_link->pcm = pcm;
+ 	pcm->private_data = rtd;
+ 	soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap;
+ 	soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
+@@ -1090,7 +1101,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
+ 	struct snd_soc_machine *machine = socdev->machine;
+ 	int ret = 0, i, ac97 = 0, err = 0;
+ 
+-	mutex_lock(&codec->mutex);
+ 	for(i = 0; i < machine->num_links; i++) {
+ 		if (socdev->machine->dai_link[i].init) {
+ 			err = socdev->machine->dai_link[i].init(codec);
+@@ -1116,12 +1126,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
+ 		goto out;
+ 	}
+ 
++	mutex_lock(&codec->mutex);
+ #ifdef CONFIG_SND_SOC_AC97_BUS
+ 	if (ac97) {
+ 		ret = soc_ac97_dev_register(codec);
+ 		if (ret < 0) {
+ 			printk(KERN_ERR "asoc: AC97 device register failed\n");
+ 			snd_card_free(codec->card);
++			mutex_unlock(&codec->mutex);
+ 			goto out;
+ 		}
+ 	}
+@@ -1134,8 +1146,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
+ 	err = device_create_file(socdev->dev, &dev_attr_codec_reg);
+ 	if (err < 0)
+ 		printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
+-out:
++
+ 	mutex_unlock(&codec->mutex);
++
++out:
+ 	return ret;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_register_card);
+@@ -1215,7 +1229,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
+ 	memcpy(&template, _template, sizeof(template));
+ 	if (long_name)
+ 		template.name = long_name;
+-	template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ 	template.index = 0;
+ 
+ 	return snd_ctl_new1(&template, data);
+@@ -1350,13 +1363,16 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
+ int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
+ 	struct snd_ctl_elem_info *uinfo)
+ {
+-	int mask = kcontrol->private_value;
++	int max = kcontrol->private_value;
++
++	if (max == 1)
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++	else
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ 
+-	uinfo->type =
+-		mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ 	uinfo->count = 1;
+ 	uinfo->value.integer.min = 0;
+-	uinfo->value.integer.max = mask;
++	uinfo->value.integer.max = max;
+ 	return 0;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
+@@ -1373,15 +1389,18 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
+ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+ 	struct snd_ctl_elem_info *uinfo)
+ {
+-	int mask = (kcontrol->private_value >> 16) & 0xff;
++	int max = (kcontrol->private_value >> 16) & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+ 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+ 
+-	uinfo->type =
+-		mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
++	if (max == 1)
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++	else
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++
+ 	uinfo->count = shift == rshift ? 1 : 2;
+ 	uinfo->value.integer.min = 0;
+-	uinfo->value.integer.max = mask;
++	uinfo->value.integer.max = max;
+ 	return 0;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+@@ -1402,7 +1421,8 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+ 	int reg = kcontrol->private_value & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+ 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+-	int mask = (kcontrol->private_value >> 16) & 0xff;
++	int max = (kcontrol->private_value >> 16) & 0xff;
++	int mask = (1 << fls(max)) - 1;
+ 	int invert = (kcontrol->private_value >> 24) & 0x01;
+ 
+ 	ucontrol->value.integer.value[0] =
+@@ -1412,10 +1432,10 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+ 			(snd_soc_read(codec, reg) >> rshift) & mask;
+ 	if (invert) {
+ 		ucontrol->value.integer.value[0] =
+-			mask - ucontrol->value.integer.value[0];
++			max - ucontrol->value.integer.value[0];
+ 		if (shift != rshift)
+ 			ucontrol->value.integer.value[1] =
+-				mask - ucontrol->value.integer.value[1];
++				max - ucontrol->value.integer.value[1];
+ 	}
+ 
+ 	return 0;
+@@ -1438,25 +1458,24 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+ 	int reg = kcontrol->private_value & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+ 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+-	int mask = (kcontrol->private_value >> 16) & 0xff;
++	int max = (kcontrol->private_value >> 16) & 0xff;
++	int mask = (1 << fls(max)) - 1;
+ 	int invert = (kcontrol->private_value >> 24) & 0x01;
+-	int err;
+ 	unsigned short val, val2, val_mask;
+ 
+ 	val = (ucontrol->value.integer.value[0] & mask);
+ 	if (invert)
+-		val = mask - val;
++		val = max - val;
+ 	val_mask = mask << shift;
+ 	val = val << shift;
+ 	if (shift != rshift) {
+ 		val2 = (ucontrol->value.integer.value[1] & mask);
+ 		if (invert)
+-			val2 = mask - val2;
++			val2 = max - val2;
+ 		val_mask |= mask << rshift;
+ 		val |= val2 << rshift;
+ 	}
+-	err = snd_soc_update_bits(codec, reg, val_mask, val);
+-	return err;
++	return snd_soc_update_bits(codec, reg, val_mask, val);
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+ 
+@@ -1473,13 +1492,16 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
+ 	struct snd_ctl_elem_info *uinfo)
+ {
+-	int mask = (kcontrol->private_value >> 12) & 0xff;
++	int max = (kcontrol->private_value >> 12) & 0xff;
++
++	if (max == 1)
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++	else
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ 
+-	uinfo->type =
+-		mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ 	uinfo->count = 2;
+ 	uinfo->value.integer.min = 0;
+-	uinfo->value.integer.max = mask;
++	uinfo->value.integer.max = max;
+ 	return 0;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
+@@ -1500,7 +1522,8 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ 	int reg = kcontrol->private_value & 0xff;
+ 	int reg2 = (kcontrol->private_value >> 24) & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+-	int mask = (kcontrol->private_value >> 12) & 0xff;
++	int max = (kcontrol->private_value >> 12) & 0xff;
++	int mask = (1<<fls(max))-1;
+ 	int invert = (kcontrol->private_value >> 20) & 0x01;
+ 
+ 	ucontrol->value.integer.value[0] =
+@@ -1509,9 +1532,9 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ 		(snd_soc_read(codec, reg2) >> shift) & mask;
+ 	if (invert) {
+ 		ucontrol->value.integer.value[0] =
+-			mask - ucontrol->value.integer.value[0];
++			max - ucontrol->value.integer.value[0];
+ 		ucontrol->value.integer.value[1] =
+-			mask - ucontrol->value.integer.value[1];
++			max - ucontrol->value.integer.value[1];
+ 	}
+ 
+ 	return 0;
+@@ -1534,7 +1557,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
+ 	int reg = kcontrol->private_value & 0xff;
+ 	int reg2 = (kcontrol->private_value >> 24) & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+-	int mask = (kcontrol->private_value >> 12) & 0xff;
++	int max = (kcontrol->private_value >> 12) & 0xff;
++	int mask = (1 << fls(max)) - 1;
+ 	int invert = (kcontrol->private_value >> 20) & 0x01;
+ 	int err;
+ 	unsigned short val, val2, val_mask;
+@@ -1544,8 +1568,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
+ 	val2 = (ucontrol->value.integer.value[1] & mask);
+ 
+ 	if (invert) {
+-		val = mask - val;
+-		val2 = mask - val2;
++		val = max - val;
++		val2 = max - val2;
+ 	}
+ 
+ 	val = val << shift;
+diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
+index 29a546f..620d7ea 100644
+--- a/sound/soc/soc-dapm.c
++++ b/sound/soc/soc-dapm.c
+@@ -43,7 +43,6 @@
+ #include <linux/bitops.h>
+ #include <linux/platform_device.h>
+ #include <linux/jiffies.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_params.h>
+@@ -524,11 +523,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
+ 					continue;
+ 
+ 				if (event == SND_SOC_DAPM_STREAM_START) {
+-					ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
++					ret = w->event(w,
++						NULL, SND_SOC_DAPM_PRE_PMU);
+ 					if (ret < 0)
+ 						return ret;
+ 				} else if (event == SND_SOC_DAPM_STREAM_STOP) {
+-					ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
++					ret = w->event(w,
++						NULL, SND_SOC_DAPM_PRE_PMD);
+ 					if (ret < 0)
+ 						return ret;
+ 				}
+@@ -539,11 +540,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
+ 					continue;
+ 
+ 				if (event == SND_SOC_DAPM_STREAM_START) {
+-					ret = w->event(w, SND_SOC_DAPM_POST_PMU);
++					ret = w->event(w,
++						NULL, SND_SOC_DAPM_POST_PMU);
+ 					if (ret < 0)
+ 						return ret;
+ 				} else if (event == SND_SOC_DAPM_STREAM_STOP) {
+-					ret = w->event(w, SND_SOC_DAPM_POST_PMD);
++					ret = w->event(w,
++						NULL, SND_SOC_DAPM_POST_PMD);
+ 					if (ret < 0)
+ 						return ret;
+ 				}
+@@ -567,26 +570,30 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
+ 					if (power) {
+ 						/* power up event */
+ 						if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
+-							ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
++							ret = w->event(w,
++								NULL, SND_SOC_DAPM_PRE_PMU);
+ 							if (ret < 0)
+ 								return ret;
+ 						}
+ 						dapm_update_bits(w);
+ 						if (w->event_flags & SND_SOC_DAPM_POST_PMU){
+-							ret = w->event(w, SND_SOC_DAPM_POST_PMU);
++							ret = w->event(w,
++								NULL, SND_SOC_DAPM_POST_PMU);
+ 							if (ret < 0)
+ 								return ret;
+ 						}
+ 					} else {
+ 						/* power down event */
+ 						if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
+-							ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
++							ret = w->event(w,
++								NULL, SND_SOC_DAPM_PRE_PMD);
+ 							if (ret < 0)
+ 								return ret;
+ 						}
+ 						dapm_update_bits(w);
+ 						if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
+-							ret = w->event(w, SND_SOC_DAPM_POST_PMD);
++							ret = w->event(w,
++								NULL, SND_SOC_DAPM_POST_PMD);
+ 							if (ret < 0)
+ 								return ret;
+ 						}
+@@ -692,7 +699,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+ 	return 0;
+ }
+ 
+-/* test and update the power status of a mixer widget */
++/* test and update the power status of a mixer or switch widget */
+ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+ 				   struct snd_kcontrol *kcontrol, int reg,
+ 				   int val_mask, int val, int invert)
+@@ -700,7 +707,8 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+ 	struct snd_soc_dapm_path *path;
+ 	int found = 0;
+ 
+-	if (widget->id != snd_soc_dapm_mixer)
++	if (widget->id != snd_soc_dapm_mixer &&
++	    widget->id != snd_soc_dapm_switch)
+ 		return -ENODEV;
+ 
+ 	if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
+@@ -963,7 +971,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
+ {
+ 	struct snd_soc_dapm_widget *w;
+ 
+-	mutex_lock(&codec->mutex);
+ 	list_for_each_entry(w, &codec->dapm_widgets, list)
+ 	{
+ 		if (w->new)
+@@ -998,7 +1005,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
+ 	}
+ 
+ 	dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+-	mutex_unlock(&codec->mutex);
+ 	return 0;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
+@@ -1019,8 +1025,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
+ 	int reg = kcontrol->private_value & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+ 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+-	int mask = (kcontrol->private_value >> 16) & 0xff;
++	int max = (kcontrol->private_value >> 16) & 0xff;
+ 	int invert = (kcontrol->private_value >> 24) & 0x01;
++	int mask = (1 << fls(max)) - 1;
+ 
+ 	/* return the saved value if we are powered down */
+ 	if (widget->id == snd_soc_dapm_pga && !widget->power) {
+@@ -1035,10 +1042,10 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
+ 			(snd_soc_read(widget->codec, reg) >> rshift) & mask;
+ 	if (invert) {
+ 		ucontrol->value.integer.value[0] =
+-			mask - ucontrol->value.integer.value[0];
++			max - ucontrol->value.integer.value[0];
+ 		if (shift != rshift)
+ 			ucontrol->value.integer.value[1] =
+-				mask - ucontrol->value.integer.value[1];
++				max - ucontrol->value.integer.value[1];
+ 	}
+ 
+ 	return 0;
+@@ -1061,7 +1068,8 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ 	int reg = kcontrol->private_value & 0xff;
+ 	int shift = (kcontrol->private_value >> 8) & 0x0f;
+ 	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+-	int mask = (kcontrol->private_value >> 16) & 0xff;
++	int max = (kcontrol->private_value >> 16) & 0xff;
++	int mask = (1 << fls(max)) - 1;
+ 	int invert = (kcontrol->private_value >> 24) & 0x01;
+ 	unsigned short val, val2, val_mask;
+ 	int ret;
+@@ -1069,13 +1077,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ 	val = (ucontrol->value.integer.value[0] & mask);
+ 
+ 	if (invert)
+-		val = mask - val;
++		val = max - val;
+ 	val_mask = mask << shift;
+ 	val = val << shift;
+ 	if (shift != rshift) {
+ 		val2 = (ucontrol->value.integer.value[1] & mask);
+ 		if (invert)
+-			val2 = mask - val2;
++			val2 = max - val2;
+ 		val_mask |= mask << rshift;
+ 		val |= val2 << rshift;
+ 	}
+@@ -1093,13 +1101,17 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ 	dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
+ 	if (widget->event) {
+ 		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+-			ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
+-			if (ret < 0)
++			ret = widget->event(widget, kcontrol,
++						SND_SOC_DAPM_PRE_REG);
++			if (ret < 0) {
++				ret = 1;
+ 				goto out;
++			}
+ 		}
+ 		ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+ 		if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+-			ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
++			ret = widget->event(widget, kcontrol,
++						SND_SOC_DAPM_POST_REG);
+ 	} else
+ 		ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+ 
+@@ -1174,13 +1186,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
+ 	dapm_mux_update_power(widget, kcontrol, mask, mux, e);
+ 	if (widget->event) {
+ 		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+-			ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
++			ret = widget->event(widget,
++				kcontrol, SND_SOC_DAPM_PRE_REG);
+ 			if (ret < 0)
+ 				goto out;
+ 		}
+ 		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ 		if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+-			ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
++			ret = widget->event(widget,
++				kcontrol, SND_SOC_DAPM_POST_REG);
+ 	} else
+ 		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ 
+@@ -1280,6 +1294,29 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
+ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
+ 
+ /**
++ * snd_soc_dapm_device_event - send a device event to the dapm core
++ * @socdev: audio device
++ * @event: device event
++ *
++ * Sends a device event to the dapm core. The core then makes any
++ * necessary machine or codec power changes..
++ *
++ * Returns 0 for success else error.
++ */
++int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
++{
++	struct snd_soc_codec *codec = socdev->codec;
++	struct snd_soc_machine *machine = socdev->machine;
++
++	if (machine->dapm_event)
++		machine->dapm_event(machine, event);
++	if (codec->dapm_event)
++		codec->dapm_event(codec, event);
++	return 0;
++}
++EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
++
++/**
+  * snd_soc_dapm_set_endpoint - set audio endpoint status
+  * @codec: audio codec
+  * @endpoint: audio signal endpoint (or start point)
+diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
+index 07962a3..0c63e05 100644
+--- a/sound/sparc/amd7930.c
++++ b/sound/sparc/amd7930.c
+@@ -36,7 +36,6 @@
+ #include <linux/interrupt.h>
+ #include <linux/moduleparam.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/info.h>
+@@ -859,7 +858,7 @@ static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem
+ 	spin_lock_irqsave(&amd->lock, flags);
+ 
+ 	if (*swval != ucontrol->value.integer.value[0]) {
+-		*swval = ucontrol->value.integer.value[0];
++		*swval = ucontrol->value.integer.value[0] & 0xff;
+ 		__amd7930_update_map(amd);
+ 		change = 1;
+ 	} else
+diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
+index f8c7a12..1c4797b 100644
+--- a/sound/sparc/cs4231.c
++++ b/sound/sparc/cs4231.c
+@@ -19,7 +19,6 @@
+ #include <linux/io.h>
+ 
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/pcm.h>
+ #include <sound/info.h>
+diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
+index 376b986..3d00e07 100644
+--- a/sound/sparc/dbri.c
++++ b/sound/sparc/dbri.c
+@@ -53,7 +53,6 @@
+  * other	DBRI low-level stuff
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/delay.h>
+ #include <linux/irq.h>
+@@ -2279,14 +2278,25 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol,
+ 	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
+ 	struct dbri_streaminfo *info =
+ 				&dbri->stream_info[kcontrol->private_value];
++	unsigned int vol[2];
+ 	int changed = 0;
+ 
+-	if (info->left_gain != ucontrol->value.integer.value[0]) {
+-		info->left_gain = ucontrol->value.integer.value[0];
++	vol[0] = ucontrol->value.integer.value[0];
++	vol[1] = ucontrol->value.integer.value[1];
++	if (kcontrol->private_value == DBRI_PLAY) {
++		if (vol[0] > DBRI_MAX_VOLUME || vol[1] > DBRI_MAX_VOLUME)
++			return -EINVAL;
++	} else {
++		if (vol[0] > DBRI_MAX_GAIN || vol[1] > DBRI_MAX_GAIN)
++			return -EINVAL;
++	}
++
++	if (info->left_gain != vol[0]) {
++		info->left_gain = vol[0];
+ 		changed = 1;
+ 	}
+-	if (info->right_gain != ucontrol->value.integer.value[1]) {
+-		info->right_gain = ucontrol->value.integer.value[1];
++	if (info->right_gain != vol[1]) {
++		info->right_gain = vol[1];
+ 		changed = 1;
+ 	}
+ 	if (changed) {
+diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
+index fee869b..89d6e9c 100644
+--- a/sound/spi/at73c213.c
++++ b/sound/spi/at73c213.c
+@@ -18,10 +18,10 @@
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/module.h>
++#include <linux/mutex.h>
+ #include <linux/platform_device.h>
+ #include <linux/io.h>
+ 
+-#include <sound/driver.h>
+ #include <sound/initval.h>
+ #include <sound/control.h>
+ #include <sound/core.h>
+@@ -76,8 +76,10 @@ struct snd_at73c213 {
+ 	u8				spi_rbuffer[2];
+ 	/* Image of the SPI registers in AT73C213. */
+ 	u8				reg_image[18];
+-	/* Protect registers against concurrent access. */
++	/* Protect SSC registers against concurrent access. */
+ 	spinlock_t			lock;
++	/* Protect mixer registers against concurrent access. */
++	struct mutex			mixer_lock;
+ };
+ 
+ #define get_chip(card) ((struct snd_at73c213 *)card->private_data)
+@@ -398,7 +400,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
+ 	int mask = (kcontrol->private_value >> 16) & 0xff;
+ 	int invert = (kcontrol->private_value >> 24) & 0xff;
+ 
+-	spin_lock_irq(&chip->lock);
++	mutex_lock(&chip->mixer_lock);
+ 
+ 	ucontrol->value.integer.value[0] =
+ 		(chip->reg_image[reg] >> shift) & mask;
+@@ -407,7 +409,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol,
+ 		ucontrol->value.integer.value[0] =
+ 			mask - ucontrol->value.integer.value[0];
+ 
+-	spin_unlock_irq(&chip->lock);
++	mutex_unlock(&chip->mixer_lock);
+ 
+ 	return 0;
+ }
+@@ -428,13 +430,13 @@ static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol,
+ 		val = mask - val;
+ 	val <<= shift;
+ 
+-	spin_lock_irq(&chip->lock);
++	mutex_lock(&chip->mixer_lock);
+ 
+ 	val = (chip->reg_image[reg] & ~(mask << shift)) | val;
+ 	change = val != chip->reg_image[reg];
+ 	retval = snd_at73c213_write_reg(chip, reg, val);
+ 
+-	spin_unlock_irq(&chip->lock);
++	mutex_unlock(&chip->mixer_lock);
+ 
+ 	if (retval)
+ 		return retval;
+@@ -470,7 +472,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol,
+ 	int mask = (kcontrol->private_value >> 24) & 0xff;
+ 	int invert = (kcontrol->private_value >> 22) & 1;
+ 
+-	spin_lock_irq(&chip->lock);
++	mutex_lock(&chip->mixer_lock);
+ 
+ 	ucontrol->value.integer.value[0] =
+ 		(chip->reg_image[left_reg] >> shift_left) & mask;
+@@ -484,7 +486,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol,
+ 			mask - ucontrol->value.integer.value[1];
+ 	}
+ 
+-	spin_unlock_irq(&chip->lock);
++	mutex_unlock(&chip->mixer_lock);
+ 
+ 	return 0;
+ }
+@@ -511,7 +513,7 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol,
+ 	val1 <<= shift_left;
+ 	val2 <<= shift_right;
+ 
+-	spin_lock_irq(&chip->lock);
++	mutex_lock(&chip->mixer_lock);
+ 
+ 	val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1;
+ 	val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2;
+@@ -519,16 +521,16 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol,
+ 		|| val2 != chip->reg_image[right_reg];
+ 	retval = snd_at73c213_write_reg(chip, left_reg, val1);
+ 	if (retval) {
+-		spin_unlock_irq(&chip->lock);
++		mutex_unlock(&chip->mixer_lock);
+ 		goto out;
+ 	}
+ 	retval = snd_at73c213_write_reg(chip, right_reg, val2);
+ 	if (retval) {
+-		spin_unlock_irq(&chip->lock);
++		mutex_unlock(&chip->mixer_lock);
+ 		goto out;
+ 	}
+ 
+-	spin_unlock_irq(&chip->lock);
++	mutex_unlock(&chip->mixer_lock);
+ 
+ 	return change;
+ 
+@@ -536,16 +538,7 @@ out:
+ 	return retval;
+ }
+ 
+-static int snd_at73c213_mono_switch_info(struct snd_kcontrol *kcontrol,
+-				  struct snd_ctl_elem_info *uinfo)
+-{
+-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+-	uinfo->count = 1;
+-	uinfo->value.integer.min = 0;
+-	uinfo->value.integer.max = 1;
+-
+-	return 0;
+-}
++#define snd_at73c213_mono_switch_info	snd_ctl_boolean_mono_info
+ 
+ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
+ 				 struct snd_ctl_elem_value *ucontrol)
+@@ -555,7 +548,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
+ 	int shift = (kcontrol->private_value >> 8) & 0xff;
+ 	int invert = (kcontrol->private_value >> 24) & 0xff;
+ 
+-	spin_lock_irq(&chip->lock);
++	mutex_lock(&chip->mixer_lock);
+ 
+ 	ucontrol->value.integer.value[0] =
+ 		(chip->reg_image[reg] >> shift) & 0x01;
+@@ -564,7 +557,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol,
+ 		ucontrol->value.integer.value[0] =
+ 			0x01 - ucontrol->value.integer.value[0];
+ 
+-	spin_unlock_irq(&chip->lock);
++	mutex_unlock(&chip->mixer_lock);
+ 
+ 	return 0;
+ }
+@@ -589,14 +582,14 @@ static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol,
+ 		val = mask - val;
+ 	val <<= shift;
+ 
+-	spin_lock_irq(&chip->lock);
++	mutex_lock(&chip->mixer_lock);
+ 
+ 	val |= (chip->reg_image[reg] & ~(mask << shift));
+ 	change = val != chip->reg_image[reg];
+ 
+ 	retval = snd_at73c213_write_reg(chip, reg, val);
+ 
+-	spin_unlock_irq(&chip->lock);
++	mutex_unlock(&chip->mixer_lock);
+ 
+ 	if (retval)
+ 		return retval;
+@@ -893,6 +886,7 @@ static int __devinit snd_at73c213_dev_init(struct snd_card *card,
+ 		return irq;
+ 
+ 	spin_lock_init(&chip->lock);
++	mutex_init(&chip->mixer_lock);
+ 	chip->card = card;
+ 	chip->irq = -1;
+ 
+diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
+index ebcac13..c89d2ea 100644
+--- a/sound/synth/emux/emux.c
++++ b/sound/synth/emux/emux.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/wait.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
+index 9b63814..0a53914 100644
+--- a/sound/synth/emux/emux_hwdep.c
++++ b/sound/synth/emux/emux_hwdep.c
+@@ -19,7 +19,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/hwdep.h>
+ #include <asm/uaccess.h>
+diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
+index 3436816..f60a98e 100644
+--- a/sound/synth/emux/emux_oss.c
++++ b/sound/synth/emux/emux_oss.c
+@@ -22,7 +22,6 @@
+  * 				midi emulation.
+  */
+ 
+-#include <sound/driver.h>
+ 
+ #ifdef CONFIG_SND_SEQUENCER_OSS
+ 
+diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
+index 680f2b7..687e6a1 100644
+--- a/sound/synth/emux/emux_proc.c
++++ b/sound/synth/emux/emux_proc.c
+@@ -18,7 +18,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/wait.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h
+index 0a56ca1..09711f8 100644
+--- a/sound/synth/emux/emux_voice.h
++++ b/sound/synth/emux/emux_voice.h
+@@ -22,7 +22,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/wait.h>
+ #include <linux/sched.h>
+ #include <sound/core.h>
+diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
+index 455e535..36d53bd 100644
+--- a/sound/synth/emux/soundfont.c
++++ b/sound/synth/emux/soundfont.c
+@@ -25,7 +25,6 @@
+  * of doing things so that the old sfxload utility can be used.
+  * Everything may change when there is an alsa way of doing things.
+  */
+-#include <sound/driver.h>
+ #include <asm/uaccess.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c
+index 6fc3d2b..deabe5f 100644
+--- a/sound/synth/util_mem.c
++++ b/sound/synth/util_mem.c
+@@ -19,7 +19,6 @@
+  */
+ 
+ #include <linux/mutex.h>
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
+diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
+index 7061438..9351b8a 100644
+--- a/sound/usb/Kconfig
++++ b/sound/usb/Kconfig
+@@ -31,17 +31,18 @@ config SND_USB_USX2Y
+ 
+ config SND_USB_CAIAQ
+ 	tristate "Native Instruments USB audio devices"
+-	 depends on SND && USB
+-	 select SND_HWDEP
+-	 select SND_RAWMIDI
+-	 select SND_PCM
+-	 help
++	depends on SND && USB
++	select SND_HWDEP
++	select SND_RAWMIDI
++	select SND_PCM
++	help
+ 	   Say Y here to include support for caiaq USB audio interfaces,
+ 	   namely:
+ 
+ 	    * Native Instruments RigKontrol2
+ 	    * Native Instruments RigKontrol3
+ 	    * Native Instruments Kore Controller
++	    * Native Instruments Kore Controller 2
+ 	    * Native Instruments Audio Kontrol 1
+ 	    * Native Instruments Audio 8 DJ
+ 
+@@ -51,12 +52,15 @@ config SND_USB_CAIAQ
+ config SND_USB_CAIAQ_INPUT
+ 	bool "enable input device for controllers"
+ 	depends on SND_USB_CAIAQ
++	depends on INPUT=y || INPUT=SND_USB_CAIAQ
+ 	help
+ 	  Say Y here to support input controllers like buttons, knobs,
+ 	  alpha dials and analog pedals on the following products:
+ 
+ 	   * Native Instruments RigKontrol2
+ 	   * Native Instruments RigKontrol3
++	   * Native Instruments Kore Controller
++	   * Native Instruments Kore Controller 2
+ 	   * Native Instruments Audio Kontrol 1
+ 
+ endmenu
+diff --git a/sound/usb/caiaq/Makefile b/sound/usb/caiaq/Makefile
+index 455c8c5..23dadd5 100644
+--- a/sound/usb/caiaq/Makefile
++++ b/sound/usb/caiaq/Makefile
+@@ -1,3 +1,4 @@
+-snd-usb-caiaq-objs := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-input.o
++snd-usb-caiaq-y := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-control.o
++snd-usb-caiaq-$(CONFIG_SND_USB_CAIAQ_INPUT) += caiaq-input.o
+ 
+ obj-$(CONFIG_SND_USB_CAIAQ) += snd-usb-caiaq.o
+diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
+index 0666908..9cc4cd8 100644
+--- a/sound/usb/caiaq/caiaq-audio.c
++++ b/sound/usb/caiaq/caiaq-audio.c
+@@ -16,7 +16,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+@@ -27,9 +26,7 @@
+ #include <sound/initval.h>
+ #include <sound/pcm.h>
+ #include <sound/rawmidi.h>
+-#ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ #include <linux/input.h>
+-#endif
+ 
+ #include "caiaq-device.h"
+ #include "caiaq-audio.h"
+@@ -60,7 +57,7 @@ static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
+ 	.channels_min	= CHANNELS_PER_STREAM,
+ 	.channels_max	= CHANNELS_PER_STREAM,
+ 	.buffer_bytes_max = MAX_BUFFER_SIZE,
+-	.period_bytes_min = 4096,
++	.period_bytes_min = 128,
+ 	.period_bytes_max = MAX_BUFFER_SIZE,
+ 	.periods_min	= 1,
+ 	.periods_max	= 1024,
+@@ -606,7 +603,7 @@ static void free_urbs(struct urb **urbs)
+ 	kfree(urbs);
+ }
+ 
+-int __devinit snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
++int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
+ {
+ 	int i, ret;
+ 
+diff --git a/sound/usb/caiaq/caiaq-control.c b/sound/usb/caiaq/caiaq-control.c
+new file mode 100644
+index 0000000..798ca12
+--- /dev/null
++++ b/sound/usb/caiaq/caiaq-control.c
+@@ -0,0 +1,315 @@
++/*
++ *   Copyright (c) 2007 Daniel Mack
++ *   friendly supported by NI.
++ *
++ *   This program 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 program 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 <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/usb.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/rawmidi.h>
++#include <sound/control.h>
++#include <linux/input.h>
++
++#include "caiaq-device.h"
++#include "caiaq-control.h"
++
++#define CNT_INTVAL 0x10000
++
++static int control_info(struct snd_kcontrol *kcontrol,
++			struct snd_ctl_elem_info *uinfo)
++{
++	struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
++	struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
++	int pos = kcontrol->private_value;
++	int is_intval = pos & CNT_INTVAL;
++
++	uinfo->count = 1;
++	pos &= ~CNT_INTVAL;
++
++	if (dev->chip.usb_id ==
++		USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)
++		&& (pos == 0)) {
++		/* current input mode of A8DJ */
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++		uinfo->value.integer.min = 0;
++		uinfo->value.integer.max = 2;
++		return 0;
++	}
++
++	if (is_intval) {
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++		uinfo->value.integer.min = 0;
++		uinfo->value.integer.max = 64;
++	} else {
++		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++		uinfo->value.integer.min = 0;
++		uinfo->value.integer.max = 1;
++	}
++
++	return 0;
++}
++
++static int control_get(struct snd_kcontrol *kcontrol,
++		       struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
++	struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
++	int pos = kcontrol->private_value;
++
++	if (pos & CNT_INTVAL)
++		ucontrol->value.integer.value[0]
++			= dev->control_state[pos & ~CNT_INTVAL];
++	else
++		ucontrol->value.integer.value[0]
++			= !!(dev->control_state[pos / 8] & (1 << pos % 8));
++
++	return 0;
++}
++
++static int control_put(struct snd_kcontrol *kcontrol,
++		       struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
++	struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
++	int pos = kcontrol->private_value;
++
++	if (pos & CNT_INTVAL) {
++		dev->control_state[pos & ~CNT_INTVAL]
++			= ucontrol->value.integer.value[0];
++		snd_usb_caiaq_send_command(dev, EP1_CMD_DIMM_LEDS,
++				dev->control_state, sizeof(dev->control_state));
++	} else {
++		if (ucontrol->value.integer.value[0])
++			dev->control_state[pos / 8] |= 1 << (pos % 8);
++		else
++			dev->control_state[pos / 8] &= ~(1 << (pos % 8));
++
++		snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
++				dev->control_state, sizeof(dev->control_state));
++	}
++
++	return 1;
++}
++
++static struct snd_kcontrol_new kcontrol_template __devinitdata = {
++	.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
++	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.index = 0,
++	.info = control_info,
++	.get  = control_get,
++	.put  = control_put,
++	/* name and private_value filled later */
++};
++
++struct caiaq_controller {
++	char *name;
++	int index;
++};
++
++static struct caiaq_controller ak1_controller[] = {
++	{ "LED left", 	2 },
++	{ "LED middle", 1 },
++	{ "LED right", 	0 },
++	{ "LED ring", 	3 }
++};
++
++static struct caiaq_controller rk2_controller[] = {
++	{ "LED 1",		5  },
++	{ "LED 2",		4  },
++	{ "LED 3",		3  },
++	{ "LED 4",		2  },
++	{ "LED 5",		1  },
++	{ "LED 6",		0  },
++	{ "LED pedal",		6  },
++	{ "LED 7seg_1b",	8  },
++	{ "LED 7seg_1c",	9  },
++	{ "LED 7seg_2a",	10 },
++	{ "LED 7seg_2b",	11 },
++	{ "LED 7seg_2c",	12 },
++	{ "LED 7seg_2d",	13 },
++	{ "LED 7seg_2e",	14 },
++	{ "LED 7seg_2f",	15 },
++	{ "LED 7seg_2g",	16 },
++	{ "LED 7seg_3a",	17 },
++	{ "LED 7seg_3b",	18 },
++	{ "LED 7seg_3c",	19 },
++	{ "LED 7seg_3d",	20 },
++	{ "LED 7seg_3e",	21 },
++	{ "LED 7seg_3f",	22 },
++	{ "LED 7seg_3g",	23 }
++};
++
++static struct caiaq_controller rk3_controller[] = {
++	{ "LED 7seg_1a",        0 + 0 },
++	{ "LED 7seg_1b",        0 + 1 },
++	{ "LED 7seg_1c",        0 + 2 },
++	{ "LED 7seg_1d",        0 + 3 },
++	{ "LED 7seg_1e",        0 + 4 },
++	{ "LED 7seg_1f",        0 + 5 },
++	{ "LED 7seg_1g",        0 + 6 },
++	{ "LED 7seg_1p",        0 + 7 },
++
++	{ "LED 7seg_2a",        8 + 0 },
++	{ "LED 7seg_2b",        8 + 1 },
++	{ "LED 7seg_2c",        8 + 2 },
++	{ "LED 7seg_2d",        8 + 3 },
++	{ "LED 7seg_2e",        8 + 4 },
++	{ "LED 7seg_2f",        8 + 5 },
++	{ "LED 7seg_2g",        8 + 6 },
++	{ "LED 7seg_2p",        8 + 7 },
++
++	{ "LED 7seg_3a",        16 + 0 },
++	{ "LED 7seg_3b",        16 + 1 },
++	{ "LED 7seg_3c",        16 + 2 },
++	{ "LED 7seg_3d",        16 + 3 },
++	{ "LED 7seg_3e",        16 + 4 },
++	{ "LED 7seg_3f",        16 + 5 },
++	{ "LED 7seg_3g",        16 + 6 },
++	{ "LED 7seg_3p",        16 + 7 },
++
++	{ "LED 7seg_4a",        24 + 0 },
++	{ "LED 7seg_4b",        24 + 1 },
++	{ "LED 7seg_4c",        24 + 2 },
++	{ "LED 7seg_4d",        24 + 3 },
++	{ "LED 7seg_4e",        24 + 4 },
++	{ "LED 7seg_4f",        24 + 5 },
++	{ "LED 7seg_4g",        24 + 6 },
++	{ "LED 7seg_4p",        24 + 7 },
++
++	{ "LED 1",		32 + 0 },
++	{ "LED 2",		32 + 1 },
++	{ "LED 3",		32 + 2 },
++	{ "LED 4",		32 + 3 },
++	{ "LED 5",		32 + 4 },
++	{ "LED 6",		32 + 5 },
++	{ "LED 7",		32 + 6 },
++	{ "LED 8",		32 + 7 },
++	{ "LED pedal",		32 + 8 }
++};
++
++static struct caiaq_controller kore_controller[] = {
++	{ "LED F1",		8   | CNT_INTVAL },
++	{ "LED F2",		12  | CNT_INTVAL },
++	{ "LED F3",		0   | CNT_INTVAL },
++	{ "LED F4",		4   | CNT_INTVAL },
++	{ "LED F5",		11  | CNT_INTVAL },
++	{ "LED F6",		15  | CNT_INTVAL },
++	{ "LED F7",		3   | CNT_INTVAL },
++	{ "LED F8",		7   | CNT_INTVAL },
++	{ "LED touch1",	     	10  | CNT_INTVAL },
++	{ "LED touch2",	     	14  | CNT_INTVAL },
++	{ "LED touch3",	     	2   | CNT_INTVAL },
++	{ "LED touch4",	     	6   | CNT_INTVAL },
++	{ "LED touch5",	     	9   | CNT_INTVAL },
++	{ "LED touch6",	     	13  | CNT_INTVAL },
++	{ "LED touch7",	     	1   | CNT_INTVAL },
++	{ "LED touch8",	     	5   | CNT_INTVAL },
++	{ "LED left",	       	18  | CNT_INTVAL },
++	{ "LED right",	     	22  | CNT_INTVAL },
++	{ "LED up",		16  | CNT_INTVAL },
++	{ "LED down",	       	20  | CNT_INTVAL },
++	{ "LED stop",	       	23  | CNT_INTVAL },
++	{ "LED play",	       	21  | CNT_INTVAL },
++	{ "LED record",	     	19  | CNT_INTVAL },
++	{ "LED listen",		17  | CNT_INTVAL },
++	{ "LED lcd",		30  | CNT_INTVAL },
++	{ "LED menu",		28  | CNT_INTVAL },
++	{ "LED sound",	 	31  | CNT_INTVAL },
++	{ "LED esc",		29  | CNT_INTVAL },
++	{ "LED view",		27  | CNT_INTVAL },
++	{ "LED enter",		24  | CNT_INTVAL },
++	{ "LED control",	26  | CNT_INTVAL }
++};
++
++static struct caiaq_controller a8dj_controller[] = {
++	{ "Current input mode",			0 | CNT_INTVAL 	},
++	{ "GND lift for TC Vinyl mode", 	24 + 0 		},
++	{ "GND lift for TC CD/Line mode", 	24 + 1 		},
++	{ "GND lift for phono mode", 		24 + 2 		},
++	{ "GND lift for TC Vinyl mode", 	24 + 3 		},
++	{ "Software lock", 			40 		}
++};
++
++int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
++{
++	int i;
++	struct snd_kcontrol *kc;
++
++	switch (dev->chip.usb_id) {
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
++		for (i = 0; i < ARRAY_SIZE(ak1_controller); i++) {
++			struct caiaq_controller *c = ak1_controller + i;
++			kcontrol_template.name = c->name;
++			kcontrol_template.private_value = c->index;
++			kc = snd_ctl_new1(&kcontrol_template, dev);
++			snd_ctl_add(dev->chip.card, kc);
++		}
++
++		break;
++
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
++		for (i = 0; i < ARRAY_SIZE(rk2_controller); i++) {
++			struct caiaq_controller *c = rk2_controller + i;
++			kcontrol_template.name = c->name;
++			kcontrol_template.private_value = c->index;
++			kc = snd_ctl_new1(&kcontrol_template, dev);
++			snd_ctl_add(dev->chip.card, kc);
++		}
++
++		break;
++
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
++		for (i = 0; i < ARRAY_SIZE(rk3_controller); i++) {
++			struct caiaq_controller *c = rk3_controller + i;
++			kcontrol_template.name = c->name;
++			kcontrol_template.private_value = c->index;
++			kc = snd_ctl_new1(&kcontrol_template, dev);
++			snd_ctl_add(dev->chip.card, kc);
++		}
++
++		break;
++
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
++		for (i = 0; i < ARRAY_SIZE(kore_controller); i++) {
++			struct caiaq_controller *c = kore_controller + i;
++			kcontrol_template.name = c->name;
++			kcontrol_template.private_value = c->index;
++			kc = snd_ctl_new1(&kcontrol_template, dev);
++			snd_ctl_add(dev->chip.card, kc);
++		}
++
++		break;
++
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
++		for (i = 0; i < ARRAY_SIZE(a8dj_controller); i++) {
++			struct caiaq_controller *c = a8dj_controller + i;
++			kcontrol_template.name = c->name;
++			kcontrol_template.private_value = c->index;
++			kc = snd_ctl_new1(&kcontrol_template, dev);
++			snd_ctl_add(dev->chip.card, kc);
++		}
++
++		break;
++	}
++
++	return 0;
++}
++
+diff --git a/sound/usb/caiaq/caiaq-control.h b/sound/usb/caiaq/caiaq-control.h
+new file mode 100644
+index 0000000..2e7ab1a
+--- /dev/null
++++ b/sound/usb/caiaq/caiaq-control.h
+@@ -0,0 +1,6 @@
++#ifndef CAIAQ_CONTROL_H
++#define CAIAQ_CONTROL_H
++
++int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev);
++
++#endif /* CAIAQ_CONTROL_H */
+diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
+index 58af814..58d25e4 100644
+--- a/sound/usb/caiaq/caiaq-device.c
++++ b/sound/usb/caiaq/caiaq-device.c
+@@ -26,26 +26,28 @@
+ #include <linux/usb.h>
+ #include <linux/input.h>
+ #include <linux/spinlock.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/initval.h>
+ #include <sound/pcm.h>
+ #include <sound/rawmidi.h>
++#include <sound/control.h>
+ 
+ #include "caiaq-device.h"
+ #include "caiaq-audio.h"
+ #include "caiaq-midi.h"
++#include "caiaq-control.h"
+ 
+ #ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ #include "caiaq-input.h"
+ #endif
+ 
+ MODULE_AUTHOR("Daniel Mack <daniel at caiaq.de>");
+-MODULE_DESCRIPTION("caiaq USB audio, version 1.2.0");
++MODULE_DESCRIPTION("caiaq USB audio, version 1.3.2");
+ MODULE_LICENSE("GPL");
+ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
+ 			 "{Native Instruments, RigKontrol3},"
+ 			 "{Native Instruments, Kore Controller},"
++			 "{Native Instruments, Kore Controller 2},"
+ 			 "{Native Instruments, Audio Kontrol 1}"
+ 			 "{Native Instruments, Audio 8 DJ}}");
+ 
+@@ -94,6 +96,11 @@ static struct usb_device_id snd_usb_id_table[] = {
+ 		.idProduct =	USB_PID_KORECONTROLLER
+ 	},
+ 	{
++		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
++		.idVendor =	USB_VID_NATIVEINSTRUMENTS,
++		.idProduct =	USB_PID_KORECONTROLLER2
++	},
++	{
+ 		.match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+ 		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
+ 		.idProduct =    USB_PID_AK1
+@@ -140,14 +147,21 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
+ 	case EP1_CMD_MIDI_READ:
+ 		snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]);
+ 		break;
+-
++	case EP1_CMD_READ_IO:
++		if (dev->chip.usb_id ==
++			USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) {
++			if (urb->actual_length > sizeof(dev->control_state))
++				urb->actual_length = sizeof(dev->control_state);
++			memcpy(dev->control_state, buf + 1, urb->actual_length);
++			wake_up(&dev->ep1_wait_queue);
++			break;
++		}
+ #ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ 	case EP1_CMD_READ_ERP:
+ 	case EP1_CMD_READ_ANALOG:
+-	case EP1_CMD_READ_IO:
+ 		snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length);
+-		break;
+ #endif
++		break;
+ 	}
+ 
+ 	dev->ep1_in_urb.actual_length = 0;
+@@ -156,10 +170,10 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
+ 		log("unable to submit urb. OOM!?\n");
+ }
+ 
+-static int send_command (struct snd_usb_caiaqdev *dev,
+-			 unsigned char command, 
+-			 const unsigned char *buffer,
+-			 int len)
++int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
++			       unsigned char command,
++			       const unsigned char *buffer,
++			       int len)
+ {
+ 	int actual_len;
+ 	struct usb_device *usb_dev = dev->chip.dev;
+@@ -207,7 +221,8 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
+ 		rate, depth, bpp);
+ 
+ 	dev->audio_parm_answer = -1;
+-	ret = send_command(dev, EP1_CMD_AUDIO_PARAMS, tmp, sizeof(tmp));
++	ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS,
++					 tmp, sizeof(tmp));
+ 
+ 	if (ret)
+ 		return ret;
+@@ -226,7 +241,8 @@ int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev,
+ 				int digital, int analog, int erp)
+ {
+ 	char tmp[3] = { digital, analog, erp };
+-	return send_command(dev, EP1_CMD_AUTO_MSG, tmp, sizeof(tmp));
++	return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
++					  tmp, sizeof(tmp));
+ }
+ 
+ static void setup_card(struct snd_usb_caiaqdev *dev)
+@@ -241,7 +257,7 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
+ 		val[0] = 0x00;
+ 		val[1] = 0x00;
+ 		val[2] = 0x01;
+-		send_command(dev, EP1_CMD_WRITE_IO, val, 3);
++		snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3);
+ 		break;
+ 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+ 		/* RigKontrol2 - display two centered dashes ('--') */
+@@ -249,22 +265,52 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
+ 		val[1] = 0x40;
+ 		val[2] = 0x40;
+ 		val[3] = 0x00;
+-		send_command(dev, EP1_CMD_WRITE_IO, val, 4);
++		snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4);
+ 		break;
+ 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
+ 		/* Audio Kontrol 1 - make USB-LED stop blinking */
+ 		val[0] = 0x00;
+-		send_command(dev, EP1_CMD_WRITE_IO, val, 1);
++		snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1);
++		break;
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
++		/* Audio 8 DJ - trigger read of current settings */
++		dev->control_state[0] = 0xff;
++		snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0);
++		snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0);
++
++		if (!wait_event_timeout(dev->ep1_wait_queue,
++					dev->control_state[0] != 0xff, HZ))
++			return;
++
++		/* fix up some defaults */
++		if ((dev->control_state[1] != 2) ||
++		    (dev->control_state[2] != 3) ||
++		    (dev->control_state[4] != 2)) {
++			dev->control_state[1] = 2;
++			dev->control_state[2] = 3;
++			dev->control_state[4] = 2;
++			snd_usb_caiaq_send_command(dev,
++				EP1_CMD_WRITE_IO, dev->control_state, 6);
++		}
++
+ 		break;
+ 	}
+ 	
+-	ret = snd_usb_caiaq_audio_init(dev);
+-	if (ret < 0)
+-		log("Unable to set up audio system (ret=%d)\n", ret);
++	if (dev->spec.num_analog_audio_out +
++	    dev->spec.num_analog_audio_in +
++	    dev->spec.num_digital_audio_out +
++	    dev->spec.num_digital_audio_in > 0) {
++		ret = snd_usb_caiaq_audio_init(dev);
++		if (ret < 0)
++			log("Unable to set up audio system (ret=%d)\n", ret);
++	}
+ 	
+-	ret = snd_usb_caiaq_midi_init(dev);
+-	if (ret < 0)
+-		log("Unable to set up MIDI system (ret=%d)\n", ret);
++	if (dev->spec.num_midi_in +
++	    dev->spec.num_midi_out > 0) {
++		ret = snd_usb_caiaq_midi_init(dev);
++		if (ret < 0)
++			log("Unable to set up MIDI system (ret=%d)\n", ret);
++	}
+ 
+ #ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ 	ret = snd_usb_caiaq_input_init(dev);
+@@ -278,6 +324,10 @@ static void setup_card(struct snd_usb_caiaqdev *dev)
+ 		log("snd_card_register() returned %d\n", ret);
+ 		snd_card_free(dev->chip.card);
+ 	}
++
++	ret = snd_usb_caiaq_control_init(dev);
++	if (ret < 0)
++		log("Unable to set up control system (ret=%d)\n", ret);
+ }
+ 
+ static struct snd_card* create_card(struct usb_device* usb_dev)
+@@ -340,7 +390,7 @@ static int init_card(struct snd_usb_caiaqdev *dev)
+ 	if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
+ 		return -EIO;
+ 
+-	err = send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
++	err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
+ 	if (err)
+ 		return err;
+ 
+diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h
+index 79bc5be..96a4913 100644
+--- a/sound/usb/caiaq/caiaq-device.h
++++ b/sound/usb/caiaq/caiaq-device.h
+@@ -7,7 +7,8 @@
+ 
+ #define USB_PID_RIGKONTROL2	0x1969
+ #define USB_PID_RIGKONTROL3	0x1940
+-#define USB_PID_KORECONTROLLER 	0x4711
++#define USB_PID_KORECONTROLLER	0x4711
++#define USB_PID_KORECONTROLLER2	0x4712
+ #define USB_PID_AK1		0x0815
+ #define USB_PID_AUDIO8DJ	0x1978
+ 
+@@ -35,6 +36,7 @@
+ #define EP1_CMD_MIDI_WRITE	0x7
+ #define EP1_CMD_AUDIO_PARAMS	0x9
+ #define EP1_CMD_AUTO_MSG	0xb
++#define EP1_CMD_DIMM_LEDS       0xc
+ 
+ struct caiaq_device_spec {
+ 	unsigned short fw_version;
+@@ -62,7 +64,7 @@ struct snd_usb_caiaqdev {
+ 	struct urb **data_urbs_in;
+ 	struct urb **data_urbs_out;
+ 	struct snd_usb_caiaq_cb_info *data_cb_info;
+-	
++
+ 	unsigned char ep1_in_buf[EP1_BUFSIZE];
+ 	unsigned char ep1_out_buf[EP1_BUFSIZE];
+ 	unsigned char midi_out_buf[EP1_BUFSIZE];
+@@ -72,7 +74,7 @@ struct snd_usb_caiaqdev {
+ 	wait_queue_head_t ep1_wait_queue;
+ 	wait_queue_head_t prepare_wait_queue;
+ 	int spec_received, audio_parm_answer;
+-	
++
+ 	char vendor_name[CAIAQ_USB_STR_LEN];
+ 	char product_name[CAIAQ_USB_STR_LEN];
+ 	char serial[CAIAQ_USB_STR_LEN];
+@@ -90,11 +92,16 @@ struct snd_usb_caiaqdev {
+ 	struct snd_pcm_substream *sub_playback[MAX_STREAMS];
+ 	struct snd_pcm_substream *sub_capture[MAX_STREAMS];
+ 
++	/* Controls */
++	unsigned char control_state[64];
++
+ 	/* Linux input */
+ #ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ 	struct input_dev *input_dev;
++	char phys[64];			/* physical device path */
++	unsigned short keycode[64];
+ #endif
+-	
++
+ 	/* ALSA */
+ 	struct snd_pcm *pcm;
+ 	struct snd_pcm_hardware pcm_info;
+@@ -112,6 +119,9 @@ struct snd_usb_caiaq_cb_info {
+ 
+ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp);
+ int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp);
+-
++int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
++			       unsigned char command,
++			       const unsigned char *buffer,
++			       int len);
+ 
+ #endif /* CAIAQ_DEVICE_H */
+diff --git a/sound/usb/caiaq/caiaq-input.c b/sound/usb/caiaq/caiaq-input.c
+index cd536ca..f743847 100644
+--- a/sound/usb/caiaq/caiaq-input.c
++++ b/sound/usb/caiaq/caiaq-input.c
+@@ -21,28 +21,61 @@
+ #include <linux/moduleparam.h>
+ #include <linux/input.h>
+ #include <linux/usb.h>
++#include <linux/usb/input.h>
+ #include <linux/spinlock.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/rawmidi.h>
+ #include <sound/pcm.h>
+ #include "caiaq-device.h"
+ #include "caiaq-input.h"
+ 
+-#ifdef CONFIG_SND_USB_CAIAQ_INPUT
+-
+-static unsigned char keycode_ak1[] =  { KEY_C, KEY_B, KEY_A };
+-static unsigned char keycode_rk2[] =  { KEY_1, KEY_2, KEY_3, KEY_4, 
+-					KEY_5, KEY_6, KEY_7 };
+-static unsigned char keycode_rk3[] =  { KEY_1, KEY_2, KEY_3, KEY_4,
+-					KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 };
+-
+-#define DEG90  (range/2)
+-#define DEG180 (range)
+-#define DEG270 (DEG90 + DEG180)
+-#define DEG360 (DEG180 * 2)
+-#define HIGH_PEAK (268)
+-#define LOW_PEAK (-7)
++static unsigned short keycode_ak1[] =  { KEY_C, KEY_B, KEY_A };
++static unsigned short keycode_rk2[] =  { KEY_1, KEY_2, KEY_3, KEY_4,
++					 KEY_5, KEY_6, KEY_7 };
++static unsigned short keycode_rk3[] =  { KEY_1, KEY_2, KEY_3, KEY_4,
++					 KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 };
++
++static unsigned short keycode_kore[] = {
++	KEY_FN_F1,      /* "menu"               */
++	KEY_FN_F7,      /* "lcd backlight       */
++	KEY_FN_F2,      /* "control"            */
++	KEY_FN_F3,      /* "enter"              */
++	KEY_FN_F4,      /* "view"               */
++	KEY_FN_F5,      /* "esc"                */
++	KEY_FN_F6,      /* "sound"              */
++	KEY_FN_F8,      /* array spacer, never triggered. */
++	KEY_RIGHT,
++	KEY_DOWN,
++	KEY_UP,
++	KEY_LEFT,
++	KEY_SOUND,      /* "listen"             */
++	KEY_RECORD,
++	KEY_PLAYPAUSE,
++	KEY_STOP,
++	BTN_4,          /* 8 softkeys */
++	BTN_3,
++	BTN_2,
++	BTN_1,
++	BTN_8,
++	BTN_7,
++	BTN_6,
++	BTN_5,
++	KEY_BRL_DOT4,   /* touch sensitive knobs */
++	KEY_BRL_DOT3,
++	KEY_BRL_DOT2,
++	KEY_BRL_DOT1,
++	KEY_BRL_DOT8,
++	KEY_BRL_DOT7,
++	KEY_BRL_DOT6,
++	KEY_BRL_DOT5
++};
++
++#define DEG90		(range / 2)
++#define DEG180		(range)
++#define DEG270		(DEG90 + DEG180)
++#define DEG360		(DEG180 * 2)
++#define HIGH_PEAK	(268)
++#define LOW_PEAK	(-7)
+ 
+ /* some of these devices have endless rotation potentiometers
+  * built in which use two tapers, 90 degrees phase shifted.
+@@ -56,8 +89,8 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
+ 	int range = HIGH_PEAK - LOW_PEAK;
+ 	int mid_value = (HIGH_PEAK + LOW_PEAK) / 2;
+ 
+-	weight_b = abs(mid_value-a) - (range/2 - 100)/2;
+-	
++	weight_b = abs(mid_value - a) - (range / 2 - 100) / 2;
++
+ 	if (weight_b < 0)
+ 		weight_b = 0;
+ 
+@@ -93,7 +126,7 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
+ 
+ 	if (ret < 0)
+ 		ret += 1000;
+-	
++
+ 	if (ret >= 1000)
+ 		ret -= 1000;
+ 
+@@ -108,76 +141,113 @@ static unsigned int decode_erp(unsigned char a, unsigned char b)
+ #undef LOW_PEAK
+ 
+ 
+-static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, 
++static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
+ 					const unsigned char *buf,
+ 					unsigned int len)
+ {
+-	switch(dev->input_dev->id.product) {
+-	case USB_PID_RIGKONTROL2:
+-		input_report_abs(dev->input_dev, ABS_X, (buf[4] << 8) |buf[5]);
+-		input_report_abs(dev->input_dev, ABS_Y, (buf[0] << 8) |buf[1]);
+-		input_report_abs(dev->input_dev, ABS_Z, (buf[2] << 8) |buf[3]);
+-		input_sync(dev->input_dev);
++	struct input_dev *input_dev = dev->input_dev;
++
++	switch (dev->chip.usb_id) {
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
++		input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]);
++		input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]);
++		input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]);
++		input_sync(input_dev);
+ 		break;
+-	case USB_PID_RIGKONTROL3:
+-		input_report_abs(dev->input_dev, ABS_X, (buf[0] << 8) |buf[1]);
+-		input_report_abs(dev->input_dev, ABS_Y, (buf[2] << 8) |buf[3]);
+-		input_report_abs(dev->input_dev, ABS_Z, (buf[4] << 8) |buf[5]);
+-		input_sync(dev->input_dev);
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
++		input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
++		input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
++		input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
++		input_sync(input_dev);
++		break;
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
++		input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]);
++		input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]);
++		input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
++		input_sync(input_dev);
+ 		break;
+ 	}
+ }
+ 
+-static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, 
++static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
+ 				     const char *buf, unsigned int len)
+ {
++	struct input_dev *input_dev = dev->input_dev;
+ 	int i;
+ 
+-	switch(dev->input_dev->id.product) {
+-	case USB_PID_AK1:
++	switch (dev->chip.usb_id) {
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
+ 		i = decode_erp(buf[0], buf[1]);
+-		input_report_abs(dev->input_dev, ABS_X, i);
+-		input_sync(dev->input_dev);
++		input_report_abs(input_dev, ABS_X, i);
++		input_sync(input_dev);
++		break;
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
++		i = decode_erp(buf[7], buf[5]);
++		input_report_abs(input_dev, ABS_HAT0X, i);
++		i = decode_erp(buf[12], buf[14]);
++		input_report_abs(input_dev, ABS_HAT0Y, i);
++		i = decode_erp(buf[15], buf[13]);
++		input_report_abs(input_dev, ABS_HAT1X, i);
++		i = decode_erp(buf[0], buf[2]);
++		input_report_abs(input_dev, ABS_HAT1Y, i);
++		i = decode_erp(buf[3], buf[1]);
++		input_report_abs(input_dev, ABS_HAT2X, i);
++		i = decode_erp(buf[8], buf[10]);
++		input_report_abs(input_dev, ABS_HAT2Y, i);
++		i = decode_erp(buf[11], buf[9]);
++		input_report_abs(input_dev, ABS_HAT3X, i);
++		i = decode_erp(buf[4], buf[6]);
++		input_report_abs(input_dev, ABS_HAT3Y, i);
++		input_sync(input_dev);
+ 		break;
+ 	}
+ }
+ 
+-static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, 
++static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
+ 				    char *buf, unsigned int len)
+ {
++	struct input_dev *input_dev = dev->input_dev;
++	unsigned short *keycode = input_dev->keycode;
+ 	int i;
+-	unsigned char *keycode = dev->input_dev->keycode;
+ 
+ 	if (!keycode)
+ 		return;
+ 
+-	if (dev->input_dev->id.product == USB_PID_RIGKONTROL2)
+-		for (i=0; i<len; i++)
++	if (input_dev->id.product == USB_PID_RIGKONTROL2)
++		for (i = 0; i < len; i++)
+ 			buf[i] = ~buf[i];
+ 
+-	for (i=0; (i<dev->input_dev->keycodemax) && (i < len); i++)
+-		input_report_key(dev->input_dev, keycode[i], 
+-					buf[i/8] & (1 << (i%8)));
++	for (i = 0; i < input_dev->keycodemax && i < len * 8; i++)
++		input_report_key(input_dev, keycode[i],
++				 buf[i / 8] & (1 << (i % 8)));
+ 
+-	input_sync(dev->input_dev);
++	if (dev->chip.usb_id ==
++		USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER) ||
++	    dev->chip.usb_id ==
++		USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2))
++		input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]);
++
++	input_sync(input_dev);
+ }
+ 
+-void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, 
+-				  char *buf, 
++void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev,
++				  char *buf,
+ 				  unsigned int len)
+ {
+-	if (!dev->input_dev || (len < 1))
++	if (!dev->input_dev || len < 1)
+ 		return;
+ 
+ 	switch (buf[0]) {
+ 	case EP1_CMD_READ_ANALOG:
+-		snd_caiaq_input_read_analog(dev, buf+1, len-1);
++		snd_caiaq_input_read_analog(dev, buf + 1, len - 1);
+ 		break;
+ 	case EP1_CMD_READ_ERP:
+-		snd_caiaq_input_read_erp(dev, buf+1, len-1);
++		snd_caiaq_input_read_erp(dev, buf + 1, len - 1);
+ 		break;
+ 	case EP1_CMD_READ_IO:
+-		snd_caiaq_input_read_io(dev, buf+1, len-1);
++		snd_caiaq_input_read_io(dev, buf + 1, len - 1);
+ 		break;
+ 	}
+ }
+@@ -192,37 +262,34 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
+ 	if (!input)
+ 		return -ENOMEM;
+ 
++	usb_make_path(usb_dev, dev->phys, sizeof(dev->phys));
++	strlcat(dev->phys, "/input0", sizeof(dev->phys));
++
+ 	input->name = dev->product_name;
+-	input->id.bustype = BUS_USB;
+-	input->id.vendor  = usb_dev->descriptor.idVendor;
+-	input->id.product = usb_dev->descriptor.idProduct;
+-	input->id.version = usb_dev->descriptor.bcdDevice;
++	input->phys = dev->phys;
++	usb_to_input_id(usb_dev, &input->id);
++	input->dev.parent = &usb_dev->dev;
+ 
+         switch (dev->chip.usb_id) {
+ 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
+ 		input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ 		input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+ 			BIT_MASK(ABS_Z);
+-		input->keycode = keycode_rk2;
+-		input->keycodesize = sizeof(char);
++		BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2));
++		memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2));
+ 		input->keycodemax = ARRAY_SIZE(keycode_rk2);
+-		for (i=0; i<ARRAY_SIZE(keycode_rk2); i++)
+-			set_bit(keycode_rk2[i], input->keybit);
+-
+ 		input_set_abs_params(input, ABS_X, 0, 4096, 0, 10);
+ 		input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
+ 		input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
+ 		snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0);
+ 		break;
+ 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+-		input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+-		input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_Z);
+-		input->keycode = keycode_rk3;
+-		input->keycodesize = sizeof(char);
++		input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
++		input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
++			BIT_MASK(ABS_Z);
++		BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3));
++		memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3));
+ 		input->keycodemax = ARRAY_SIZE(keycode_rk3);
+-		for (i=0; i<ARRAY_SIZE(keycode_rk3); i++)
+-			set_bit(keycode_rk3[i], input->keybit);
+-
+ 		input_set_abs_params(input, ABS_X, 0, 1024, 0, 10);
+ 		input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10);
+ 		input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10);
+@@ -231,21 +298,50 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
+ 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
+ 		input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ 		input->absbit[0] = BIT_MASK(ABS_X);
+-		input->keycode = keycode_ak1;
+-		input->keycodesize = sizeof(char);
++		BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1));
++		memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1));
+ 		input->keycodemax = ARRAY_SIZE(keycode_ak1);
+-		for (i=0; i<ARRAY_SIZE(keycode_ak1); i++)
+-			set_bit(keycode_ak1[i], input->keybit);
+-
+ 		input_set_abs_params(input, ABS_X, 0, 999, 0, 10);
+ 		snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5);
+ 		break;
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
++	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
++		input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
++		input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
++				   BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) |
++				   BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) |
++				   BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) |
++				   BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
++				   BIT_MASK(ABS_Z);
++		input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
++		BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_kore));
++		memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore));
++		input->keycodemax = ARRAY_SIZE(keycode_kore);
++		input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT1X, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT1Y, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT2X, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT2Y, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT3X, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_HAT3Y, 0, 999, 0, 10);
++		input_set_abs_params(input, ABS_X, 0, 4096, 0, 10);
++		input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
++		input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
++		input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1);
++		snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
++		break;
+ 	default:
+ 		/* no input methods supported on this device */
+ 		input_free_device(input);
+ 		return 0;
+ 	}
+ 
++	input->keycode = dev->keycode;
++	input->keycodesize = sizeof(unsigned short);
++	for (i = 0; i < input->keycodemax; i++)
++		__set_bit(dev->keycode[i], input->keybit);
++
+ 	ret = input_register_device(input);
+ 	if (ret < 0) {
+ 		input_free_device(input);
+@@ -265,5 +361,3 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
+ 	dev->input_dev = NULL;
+ }
+ 
+-#endif /* CONFIG_SND_USB_CAIAQ_INPUT */
+-
+diff --git a/sound/usb/caiaq/caiaq-midi.c b/sound/usb/caiaq/caiaq-midi.c
+index 793ca20..30b57f9 100644
+--- a/sound/usb/caiaq/caiaq-midi.c
++++ b/sound/usb/caiaq/caiaq-midi.c
+@@ -23,7 +23,6 @@
+ #include <linux/usb.h>
+ #include <linux/input.h>
+ #include <linux/spinlock.h>
+-#include <sound/driver.h>
+ #include <sound/core.h>
+ #include <sound/rawmidi.h>
+ #include <sound/pcm.h>
+@@ -124,7 +123,7 @@ void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev,
+ 	snd_rawmidi_receive(dev->midi_receive_substream, buf, len);
+ }
+ 
+-int __devinit snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
++int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
+ {
+ 	int ret;
+ 	struct snd_rawmidi *rmidi;
+diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
+index 967b823..8fa9356 100644
+--- a/sound/usb/usbaudio.c
++++ b/sound/usb/usbaudio.c
+@@ -38,7 +38,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/bitops.h>
+ #include <linux/init.h>
+ #include <linux/list.h>
+@@ -2078,6 +2077,14 @@ static int usb_audio_probe(struct usb_interface *intf,
+ 			   const struct usb_device_id *id);
+ static void usb_audio_disconnect(struct usb_interface *intf);
+ 
++#ifdef CONFIG_PM
++static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message);
++static int usb_audio_resume(struct usb_interface *intf);
++#else
++#define usb_audio_suspend NULL
++#define usb_audio_resume NULL
++#endif
++
+ static struct usb_device_id usb_audio_ids [] = {
+ #include "usbquirks.h"
+     { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
+@@ -2092,6 +2099,8 @@ static struct usb_driver usb_audio_driver = {
+ 	.name =		"snd-usb-audio",
+ 	.probe =	usb_audio_probe,
+ 	.disconnect =	usb_audio_disconnect,
++	.suspend =	usb_audio_suspend,
++	.resume =	usb_audio_resume,
+ 	.id_table =	usb_audio_ids,
+ };
+ 
+@@ -3654,6 +3663,45 @@ static void usb_audio_disconnect(struct usb_interface *intf)
+ 				 dev_get_drvdata(&intf->dev));
+ }
+ 
++#ifdef CONFIG_PM
++static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
++{
++	struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
++	struct list_head *p;
++	struct snd_usb_stream *as;
++
++	if (chip == (void *)-1L)
++		return 0;
++
++	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
++	if (!chip->num_suspended_intf++) {
++		list_for_each(p, &chip->pcm_list) {
++			as = list_entry(p, struct snd_usb_stream, list);
++			snd_pcm_suspend_all(as->pcm);
++		}
++	}
++
++	return 0;
++}
++
++static int usb_audio_resume(struct usb_interface *intf)
++{
++	struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev);
++
++	if (chip == (void *)-1L)
++		return 0;
++	if (--chip->num_suspended_intf)
++		return 0;
++	/*
++	 * ALSA leaves material resumption to user space
++	 * we just notify
++	 */
++
++	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
++
++	return 0;
++}
++#endif		/* CONFIG_PM */
+ 
+ static int __init snd_usb_audio_init(void)
+ {
+diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
+index 2272f45..7cf18c3 100644
+--- a/sound/usb/usbaudio.h
++++ b/sound/usb/usbaudio.h
+@@ -126,6 +126,7 @@ struct snd_usb_audio {
+ 	u32 usb_id;
+ 	int shutdown;
+ 	int num_interfaces;
++	int num_suspended_intf;
+ 
+ 	struct list_head pcm_list;	/* list of pcm streams */
+ 	int pcm_devs;
+diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
+index 6330788..750e929 100644
+--- a/sound/usb/usbmidi.c
++++ b/sound/usb/usbmidi.c
+@@ -35,7 +35,6 @@
+  * SUCH DAMAGE.
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/kernel.h>
+ #include <linux/types.h>
+ #include <linux/bitops.h>
+diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
+index 5e32969..89c63d0 100644
+--- a/sound/usb/usbmixer.c
++++ b/sound/usb/usbmixer.c
+@@ -26,7 +26,6 @@
+  *
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/bitops.h>
+ #include <linux/init.h>
+ #include <linux/list.h>
+@@ -1703,6 +1702,11 @@ static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
+ 	case 19: /* speaker out jacks */
+ 	case 20: /* headphones out jack */
+ 		break;
++	/* live24ext: 4 = line-in jack */
++	case 3:	/* hp-out jack (may actuate Mute) */
++		if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
++			snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
++		break;
+ 	default:
+ 		snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+ 		break;
+@@ -1951,6 +1955,9 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+ 	int i, err;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
++		if (i > 1 &&  /* Live24ext has 2 LEDs only */
++			mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
++			break; 
+ 		err = snd_ctl_add(mixer->chip->card,
+ 				  snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+ 		if (err < 0)
+@@ -1963,28 +1970,42 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
+ 				    struct snd_info_buffer *buffer)
+ {
+-	static const struct {
++	static const struct sb_jack {
+ 		int unitid;
+ 		const char *name;
+-	} jacks[] = {
++	}  jacks_audigy2nx[] = {
+ 		{4,  "dig in "},
+ 		{7,  "line in"},
+ 		{19, "spk out"},
+ 		{20, "hph out"},
++		{-1, NULL}
++	}, jacks_live24ext[] = {
++		{4,  "line in"}, /* &1=Line, &2=Mic*/
++		{3,  "hph out"}, /* headphones */
++		{0,  "RC     "}, /* last command, 6 bytes see rc_config above */
++		{-1, NULL}
+ 	};
++	const struct sb_jack *jacks;
+ 	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) {
++	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
++		jacks = jacks_audigy2nx;
++	else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
++		jacks = jacks_live24ext;
++	else
++		return;
++
++	for (i = 0; jacks[i].name; ++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)
++		if (err == 3 && (buf[0] == 3 || buf[0] == 6))
+ 			snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
+ 		else
+ 			snd_iprintf(buffer, "?\n");
+@@ -2022,7 +2043,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
+ 	if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
+ 		goto _error;
+ 
+-	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) {
++	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
++	    mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) {
+ 		struct snd_info_entry *entry;
+ 
+ 		if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
+diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
+index 7c4dcb3..d755be0 100644
+--- a/sound/usb/usbmixer_maps.c
++++ b/sound/usb/usbmixer_maps.c
+@@ -187,6 +187,13 @@ static struct usbmix_selector_map audigy2nx_selectors[] = {
+ 	{ 0 } /* terminator */
+ };
+ 
++/* Creative SoundBlaster Live! 24-bit External */
++static struct usbmix_name_map live24ext_map[] = {
++	/* 2: PCM Playback Volume */
++	{ 5, "Mic Capture" }, /* FU, default PCM Capture Volume */
++	{ 0 } /* terminator */
++};
++
+ /* LineX FM Transmitter entry - needed to bypass controls bug */
+ static struct usbmix_name_map linex_map[] = {
+ 	/* 1: IT pcm */
+@@ -273,6 +280,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
+ 		.map = audigy2nx_map,
+ 		.selector_map = audigy2nx_selectors,
+ 	},
++ 	{
++		.id = USB_ID(0x041e, 0x3040),
++		.map = live24ext_map,
++	},
+ 	{
+ 		/* Hercules DJ Console (Windows Edition) */
+ 		.id = USB_ID(0x06f8, 0xb000),
+diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
+index 59410f4..938dff5 100644
+--- a/sound/usb/usbquirks.h
++++ b/sound/usb/usbquirks.h
+@@ -1004,11 +1004,35 @@ YAMAHA_DEVICE(0x7010, "UB99"),
+ 	}
+ },
+ {
++	/* has ID 0x0049 when not in "Advanced Driver" mode */
++	USB_DEVICE(0x0582, 0x0047),
++	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
++		/* .vendor_name = "EDIROL", */
++		/* .product_name = "UR-80", */
++		.ifnum = QUIRK_ANY_INTERFACE,
++		.type = QUIRK_COMPOSITE,
++		.data = (const struct snd_usb_audio_quirk[]) {
++			/* in the 96 kHz modes, only interface 1 is there */
++			{
++				.ifnum = 1,
++				.type = QUIRK_AUDIO_STANDARD_INTERFACE
++			},
++			{
++				.ifnum = 2,
++				.type = QUIRK_AUDIO_STANDARD_INTERFACE
++			},
++			{
++				.ifnum = -1
++			}
++		}
++	}
++},
++{
+ 	/* has ID 0x004a when not in "Advanced Driver" mode */
+ 	USB_DEVICE(0x0582, 0x0048),
+ 	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+-		.vendor_name = "EDIROL",
+-		.product_name = "UR-80",
++		/* .vendor_name = "EDIROL", */
++		/* .product_name = "UR-80", */
+ 		.ifnum = 0,
+ 		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+ 		.data = & (const struct snd_usb_midi_endpoint_info) {
+diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
+index b76b3dd..6495534 100644
+--- a/sound/usb/usx2y/usX2Yhwdep.c
++++ b/sound/usb/usx2y/usX2Yhwdep.c
+@@ -20,7 +20,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  */
+ 
+-#include <sound/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/usb.h>
+ #include <sound/core.h>
+@@ -34,34 +33,31 @@
+ int usX2Y_hwdep_pcm_new(struct snd_card *card);
+ 
+ 
+-static struct page * snd_us428ctls_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
++static int snd_us428ctls_vm_fault(struct vm_area_struct *area,
++				  struct vm_fault *vmf)
+ {
+ 	unsigned long offset;
+ 	struct page * page;
+ 	void *vaddr;
+ 
+-	snd_printdd("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n",
++	snd_printdd("ENTER, start %lXh, pgoff %ld\n",
+ 		   area->vm_start,
+-		   address - area->vm_start,
+-		   (address - area->vm_start) >> PAGE_SHIFT,
+-		   address);
++		   vmf->pgoff);
+ 	
+-	offset = area->vm_pgoff << PAGE_SHIFT;
+-	offset += address - area->vm_start;
+-	snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS);
++	offset = vmf->pgoff << PAGE_SHIFT;
+ 	vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->us428ctls_sharedmem + offset;
+ 	page = virt_to_page(vaddr);
+ 	get_page(page);
+-	snd_printdd( "vaddr=%p made us428ctls_vm_nopage() return %p; offset=%lX\n", vaddr, page, offset);
++	vmf->page = page;
+ 
+-	if (type)
+-		*type = VM_FAULT_MINOR;
++	snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n",
++		    vaddr, page);
+ 
+-	return page;
++	return 0;
+ }
+ 
+ static struct vm_operations_struct us428ctls_vm_ops = {
+-	.nopage = snd_us428ctls_vm_nopage,
++	.fault = snd_us428ctls_vm_fault,
+ };
+ 
+ static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
+diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
+index e011fca..e5981a6 100644
+--- a/sound/usb/usx2y/usbusx2y.c
++++ b/sound/usb/usx2y/usbusx2y.c
+@@ -130,7 +130,6 @@
+  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+ 
+-#include <sound/driver.h>
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
+index 48e9aa3..9a608fa 100644
+--- a/sound/usb/usx2y/usbusx2yaudio.c
++++ b/sound/usb/usx2y/usbusx2yaudio.c
+@@ -31,7 +31,6 @@
+  */
+ 
+ 
+-#include <sound/driver.h>
+ #include <linux/interrupt.h>
+ #include <linux/usb.h>
+ #include <sound/core.h>
+diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
+index a5e7bcd..800b5ce 100644
+--- a/sound/usb/usx2y/usx2yhwdeppcm.c
++++ b/sound/usb/usx2y/usx2yhwdeppcm.c
+@@ -683,30 +683,24 @@ static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
+ }
+ 
+ 
+-static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
++static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
++					struct vm_fault *vmf)
+ {
+ 	unsigned long offset;
+-	struct page *page;
+ 	void *vaddr;
+ 
+-	offset = area->vm_pgoff << PAGE_SHIFT;
+-	offset += address - area->vm_start;
+-	snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
++	offset = vmf->pgoff << PAGE_SHIFT;
+ 	vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
+-	page = virt_to_page(vaddr);
+-	get_page(page);
+-
+-	if (type)
+-		*type = VM_FAULT_MINOR;
+-
+-	return page;
++	vmf->page = virt_to_page(vaddr);
++	get_page(vmf->page);
++	return 0;
+ }
+ 
+ 
+ static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
+ 	.open = snd_usX2Y_hwdep_pcm_vm_open,
+ 	.close = snd_usX2Y_hwdep_pcm_vm_close,
+-	.nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
++	.fault = snd_usX2Y_hwdep_pcm_vm_fault,
+ };
  
--static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
-+static const unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
  
- /* Magic definition of all other variables and things */
- I2C_CLIENT_INSMOD;
 diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
 new file mode 100644
 index 0000000..317f8e2

Modified: dists/trunk/linux-2.6/debian/patches/series/1~experimental.1
==============================================================================
--- dists/trunk/linux-2.6/debian/patches/series/1~experimental.1	(original)
+++ dists/trunk/linux-2.6/debian/patches/series/1~experimental.1	Fri Feb  1 14:00:08 2008
@@ -1,4 +1,4 @@
-+ bugfix/all/patch-2.6.24-git9
++ bugfix/all/patch-2.6.24-git10
 + debian/version.patch
 + debian/kernelvariables.patch
 + debian/doc-build-parallel.patch



More information about the Kernel-svn-changes mailing list