diff -ruN linux-2.6.22/Documentation/sound/alsa/ALSA-Configuration.txt linux-2.6.22-alsa/Documentation/sound/alsa/ALSA-Configuration.txt
--- linux-2.6.22/Documentation/sound/alsa/ALSA-Configuration.txt	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/ALSA-Configuration.txt	2007-09-01 20:57:16.000000000 +0200
@@ -365,13 +365,15 @@
   Module snd-cmipci
   -----------------
 
-    Module for C-Media CMI8338 and 8738 PCI sound cards.
+    Module for C-Media CMI8338/8738/8768/8770 PCI sound cards.
 
-    mpu_port	- 0x300,0x310,0x320,0x330 = legacy port,
-		  1 = integrated PCI port,
+    mpu_port    - port address of MIDI interface:
+		  0x300,0x310,0x320,0x330 = legacy port,
+		  1 = integrated PCI port (8738 or later),
 		  0 = disable (default)
-    fm_port     - 0x388 = legacy port,
-		  1 = integrated PCI port (default),
+    fm_port     - port address of OPL-3 FM synthesizer (8x38 only):
+		  0x388 = legacy port,
+		  1 = integrated PCI port (default on 8738),
 		  0 = disable
     soft_ac3    - Software-conversion of raw SPDIF packets (model 033 only)
                   (default = 1)
@@ -467,7 +469,12 @@
     above explicitly.
 
     The power-management is supported.
-    
+
+  Module snd-cs5530
+  _________________
+
+    Module for Cyrix/NatSemi Geode 5530 chip. 
+  
   Module snd-cs5535audio
   ----------------------
 
@@ -759,9 +766,14 @@
 
     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_cmd  - Use single immediate commands to communicate with
 		codecs (for debugging only)
     enable_msi	- Enable Message Signaled Interrupt (MSI) (default = off)
+    power_save	- Automatic power-saving timtout (in second, 0 =
+		disable, default = 10)
+    power_save_controller - Reset HD-audio controller in power-saving mode
+		(default = on)
 
     This module supports one card and autoprobe.
 
@@ -803,6 +815,8 @@
 	  hp-3013	HP machines (3013-variant)
 	  fujitsu	Fujitsu S7020
 	  acer		Acer TravelMate
+	  will		Will laptops (PB V7900)
+	  replacer	Replacer 672V
 	  basic		fixed pin assignment (old default model)
 	  auto		auto-config reading BIOS (default)
 
@@ -811,16 +825,34 @@
 	  hp-bpc	HP xw4400/6400/8400/9400 laptops
 	  hp-bpc-d7000	HP BPC D7000
 	  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
 	  basic		fixed pin assignment w/o SPDIF
 	  auto		auto-config reading BIOS (default)
 
+	ALC268
+	  3stack	3-stack model
+	  toshiba	Toshiba A205
+	  acer		Acer laptops
+	  auto		auto-config reading BIOS (default)
+
+	ALC662
+	  3stack-dig	3-stack (2-channel) with SPDIF
+	  3stack-6ch	 3-stack (6-channel)
+	  3stack-6ch-dig 3-stack (6-channel) with SPDIF
+	  6stack-dig	 6-stack with SPDIF
+	  lenovo-101e	 Lenovo laptop
+	  auto		auto-config reading BIOS (default)
+
 	ALC882/885
 	  3stack-dig	3-jack with SPDIF I/O
 	  6stack-dig	6-jack digital with SPDIF I/O
 	  arima		Arima W820Di1
 	  macpro	MacPro support
+	  mbp3		Macbook Pro rev3
+	  imac24	iMac 24'' with jack detection
 	  w2jc		ASUS W2JC
 	  auto		auto-config reading BIOS (default)
 
@@ -831,10 +863,18 @@
 	  3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
 	  6stack-dig-demo  6-jack digital for Intel demo board
 	  acer		Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
+	  acer-aspire	Acer Aspire 9810
 	  medion	Medion Laptops
+	  medion-md2	Medion MD2
 	  targa-dig	Targa/MSI
 	  targa-2ch-dig	Targs/MSI with 2-channel
 	  laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
+	  lenovo-101e	Lenovo 101E
+	  lenovo-nb0763	Lenovo NB0763
+	  lenovo-ms7195-dig Lenovo MS7195
+	  haier-w66	Haier W66
+	  6stack-hp	HP machines with 6stack (Nettle boards)
+	  3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
 	  auto		auto-config reading BIOS (default)
 
 	ALC861/660
@@ -853,7 +893,10 @@
 	  3stack-dig	3-jack with SPDIF OUT
 	  6stack-dig	6-jack with SPDIF OUT
 	  3stack-660	3-jack (for ALC660VD)
+	  3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
 	  lenovo	Lenovo 3000 C200
+	  dallas	Dallas laptops
+	  hp		HP TX1000
 	  auto		auto-config reading BIOS (default)
 
 	CMI9880
@@ -864,12 +907,26 @@
 	  allout	5-jack in back, 2-jack in front, SPDIF out
 	  auto		auto-config reading BIOS (default)
 
+	AD1882
+	  3stack	3-stack mode (default)
+	  6stack	6-stack mode
+
+	AD1884
+	  N/A
+
 	AD1981
 	  basic		3-jack (default)
 	  hp		HP nx6320
 	  thinkpad	Lenovo Thinkpad T60/X60/Z60
 	  toshiba	Toshiba U205
 
+	AD1983
+	  N/A
+
+	AD1984
+	  basic		default configuration
+	  thinkpad	Lenovo Thinkpad T61/X61
+
 	AD1986A
 	  6stack	6-jack, separate surrounds (default)
 	  3stack	3-stack, shared surrounds
@@ -900,18 +957,41 @@
 			can be adjusted.  Appearing only when compiled with
 			$CONFIG_SND_DEBUG=y
 
-	STAC9200/9205/9254
+	STAC9200
+	  ref		Reference board
+	  dell-m21	Dell Inspiron 630m, Dell Inspiron 640m
+	  dell-m22	Dell Latitude D620, Dell Latitude D820
+	  dell-m23	Dell XPS M1710, Dell Precision M90
+	  dell-m24	Dell Latitude 120L
+	  dell-m25	Dell Inspiron E1505n
+	  dell-m26	Dell Inspiron 1501
+	  dell-m27	Dell Inspiron E1705/9400
+
+	STAC9205/9254
 	  ref		Reference board
+	  dell-m42	Dell (unknown)
+	  dell-m43	Dell Precision
+	  dell-m44	Dell Inspiron
 
 	STAC9220/9221
 	  ref		Reference board
 	  3stack	D945 3stack
 	  5stack	D945 5stack + SPDIF
-	  macmini	Intel Mac Mini
-	  macbook	Intel Mac Book
-	  macbook-pro-v1 Intel Mac Book Pro 1st generation
-	  macbook-pro	Intel Mac Book Pro 2nd generation
-	  imac-intel	Intel iMac
+	  intel-mac-v1	Intel Mac Type 1
+	  intel-mac-v2	Intel Mac Type 2
+	  intel-mac-v3	Intel Mac Type 3
+	  intel-mac-v4	Intel Mac Type 4
+	  intel-mac-v5	Intel Mac Type 5
+	  macmini	Intel Mac Mini (equivalent with type 3)
+	  macbook	Intel Mac Book (eq. type 5)
+	  macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
+	  macbook-pro	Intel Mac Book Pro 2nd generation (eq. type 3)
+	  imac-intel	Intel iMac (eq. type 2)
+	  imac-intel-20	Intel iMac (newer version) (eq. type 3)
+	  dell-d81	Dell (unknown)
+	  dell-d82	Dell (unknown)
+	  dell-m81	Dell (unknown)
+	  dell-m82	Dell XPS M1210
 
 	STAC9202/9250/9251
 	  ref		Reference board, base config
@@ -923,6 +1003,7 @@
 	  ref		Reference board
 	  3stack	D965 3stack
 	  5stack	D965 5stack + SPDIF
+	  dell-3stack	Dell Dimension E520
 
 	STAC9872
 	  vaio		Setup for VAIO FE550G/SZ110
@@ -937,6 +1018,12 @@
     subsystem ID (output of "lspci -nv") to ALSA BTS or alsa-devel
     ML (see the section "Links and Addresses").
 
+    When CONFIG_SND_HDA_POWER_SAVE is set, two options, power_save and
+    power_save_controller become available.  power_save specifies the
+    time to turn off the power automatically at idle status.  When
+    power_save_controller is true, the controller is also turned off.
+    This might result in more obvious click noise at turning on/off.
+
     Note 2: If you get click noises on output, try the module option
 	    position_fix=1 or 2.  position_fix=1 will use the SD_LPIB
 	    register value without FIFO size correction as the current
@@ -956,6 +1043,17 @@
     from the irq.  Remember this is a last resort, and should be
     avoided as much as possible...
     
+    MORE NOTES ON "azx_get_response timeout" PROBLEMS:
+    On some hardwares, you may need to add a proper probe_mask option
+    to avoid the "azx_get_response timeout" problem above, instead.
+    This occurs when the access to non-existing or non-working codec slot
+    (likely a modem one) causes a stall of the communication via HD-audio
+    bus.  You can see which codec slots are probed by enabling
+    CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec
+    proc files.  Then limit the slots to probe by probe_mask option.
+    For example, probe_mask=1 means to probe only the first slot, and
+    probe_mask=4 means only the third slot.
+
     The power-management is supported.
 
   Module snd-hdsp
@@ -1634,8 +1732,52 @@
     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)
+	If yes, then during driver loading, the state of the board is
+	ignored, and we reset the board and load the firmware anyway.
+    fx_raw	    - Assume that the FX process needs help (default:yes)
+	If false, we'll leave the FX processor in whatever state it is
+	when the driver is loaded.  The default is to download the
+	microprogram and associated coefficients to set it up for
+	"default" operation, whatever that means.
+    debug_default   - Debug parameters for card initialization
+    wait_usecs	    - How long to wait without sleeping, usecs
+		      (default:150)
+	This magic number seems to give pretty optimal throughput
+	based on my limited experimentation. 
+	If you want to play around with it and find a better value, be
+	my guest. Remember, the idea is to get a number that causes us
+	to just busy wait for as many WaveFront commands as possible,
+	without coming up with a number so large that we hog the whole
+	CPU. 
+	Specifically, with this number, out of about 134,000 status
+	waits, only about 250 result in a sleep. 
+    sleep_interval  - How long to sleep when waiting for reply
+		      (default: 100)
+    sleep_tries	    - How many times to try sleeping during a wait
+		      (default: 50)
+    ospath	    - Pathname to processed ICS2115 OS firmware
+		      (default:wavefront.os)
+	The path name of the ISC2115 OS firmware.  In the recent
+	version, it's handled via firmware loader framework, so it
+	must be installed in the proper path, typically,
+	/lib/firmware.
+    reset_time	    - How long to wait for a reset to take effect
+		      (default:2)
+    ramcheck_time   - How many seconds to wait for the RAM test
+		      (default:20)
+    osrun_time	    - How many seconds to wait for the ICS2115 OS
+		      (default:10)
+
     This module supports multiple cards and ISA PnP.
 
+    Note: the firmware file "wavefront.os" was located in the earlier
+          version in /etc.  Now it's loaded via firmware loader, and
+	  must be in the proper firmware path, such as /lib/firmware.
+	  Copy (or symlink) the file appropriately if you get an error
+	  regarding firmware downloading after upgrading the kernel.
+
   Module snd-sonicvibes
   ---------------------
 
diff -ruN linux-2.6.22/Documentation/sound/alsa/Audiophile-Usb.txt linux-2.6.22-alsa/Documentation/sound/alsa/Audiophile-Usb.txt
--- linux-2.6.22/Documentation/sound/alsa/Audiophile-Usb.txt	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/Audiophile-Usb.txt	2007-09-01 20:57:16.000000000 +0200
@@ -1,4 +1,4 @@
-	Guide to using M-Audio Audiophile USB with ALSA and Jack	v1.3
+	Guide to using M-Audio Audiophile USB with ALSA and Jack	v1.5
 	========================================================
 
 	    Thibault Le Meur <Thibault.LeMeur@supelec.fr>
@@ -6,8 +6,19 @@
 This document is a guide to using the M-Audio Audiophile USB (tm) device with 
 ALSA and JACK.
 
+History
+=======
+* v1.4 - Thibault Le Meur (2007-07-11)
+ - Added Low Endianness nature of 16bits-modes
+   found by Hakan Lennestal <Hakan.Lennestal@brfsodrahamn.se>
+ - Modifying document structure
+* v1.5 - Thibault Le Meur (2007-07-12)
+ - Added AC3/DTS passthru info
+
+
 1 - Audiophile USB Specs and correct usage
 ==========================================
+
 This part is a reminder of important facts about the functions and limitations 
 of the device.
 
@@ -25,18 +36,18 @@
 The internal DAC/ADC has the following characteristics:
 * sample depth of 16 or 24 bits
 * sample rate from 8kHz to 96kHz
-* Two ports can't use different sample depths at the same time. Moreover, the 
-Audiophile USB documentation gives the following Warning: "Please exit any 
-audio application running before switching between bit depths"
+* Two interfaces can't use different sample depths at the same time.
+Moreover, the Audiophile USB documentation gives the following Warning:
+"Please exit any audio application running before switching between bit depths"
 
 Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be 
 activated at the same time depending on the audio mode selected:
- * 16-bit/48kHz ==> 4 channels in/4 channels out
+ * 16-bit/48kHz ==> 4 channels in + 4 channels out
    - Ai+Ao+Di+Do
- * 24-bit/48kHz ==> 4 channels in/2 channels out, 
-                    or 2 channels in/4 channels out
+ * 24-bit/48kHz ==> 4 channels in + 2 channels out, 
+                    or 2 channels in + 4 channels out
    - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
- * 24-bit/96kHz ==> 2 channels in, or 2 channels out (half duplex only)
+ * 24-bit/96kHz ==> 2 channels in _or_ 2 channels out (half duplex only)
    - Ai or Ao or Di or Do
 
 Important facts about the Digital interface:
@@ -52,44 +63,56 @@
 synchronization error (for instance sound played at an odd sample rate)
 
 
-2 - Audiophile USB support in ALSA
-==================================
+2 - Audiophile USB MIDI support in ALSA
+=======================================
 
-2.1 - MIDI ports
-----------------
-The Audiophile USB MIDI ports will be automatically supported once the 
+The Audiophile USB MIDI ports will be automatically supported once the
 following modules have been loaded:
  * snd-usb-audio
  * snd-seq-midi
 
 No additional setting is required.
 
-2.2 - Audio ports
------------------
+
+3 - Audiophile USB Audio support in ALSA
+========================================
 
 Audio functions of the Audiophile USB device are handled by the snd-usb-audio 
 module. This module can work in a default mode (without any device-specific 
 parameter), or in an "advanced" mode with the device-specific parameter called 
 "device_setup".
 
-2.2.1 - Default Alsa driver mode
-
-The default behavior of the snd-usb-audio driver is to parse the device 
-capabilities at startup and enable all functions inside the device (including 
-all ports at any supported sample rates and sample depths). This approach 
-has the advantage to let the driver easily switch from sample rates/depths 
-automatically according to the need of the application claiming the device.
+3.1 - Default Alsa driver mode
+------------------------------
 
-In this case the Audiophile ports are mapped to alsa pcm devices in the 
-following way (I suppose the device's index is 1):
+The default behavior of the snd-usb-audio driver is to list the device 
+capabilities at startup and activate the required mode when required 
+by the applications: for instance if the user is recording in a 
+24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
+the snd-usb-audio module will reconfigure the device on the fly.
+
+This approach has the advantage to let the driver automatically switch from sample 
+rates/depths automatically according to the user's needs. However, those who 
+are using the device under windows know that this is not how the device is meant to
+work: under windows applications must be closed before using the m-audio control
+panel to switch the device working mode. Thus as we'll see in next section, this 
+Default Alsa driver mode can lead to device misconfigurations.
+
+Let's get back to the Default Alsa driver mode for now.  In this case the 
+Audiophile interfaces are mapped to alsa pcm devices in the following 
+way (I suppose the device's index is 1):
  * hw:1,0 is Ao in playback and Di in capture
  * hw:1,1 is Do in playback and Ai in capture
  * hw:1,2 is Do in AC3/DTS passthrough mode
 
-You must note as well that the device uses Big Endian byte encoding so that 
-supported audio format are S16_BE  for 16-bit depth modes and S24_3BE for 
-24-bits depth mode. One exception is the hw:1,2 port which is Little Endian 
-compliant and thus uses S16_LE.
+In this mode, the device uses Big Endian byte-encoding so that 
+supported audio format are S16_BE for 16-bit depth modes and S24_3BE for 
+24-bits depth mode.
+
+One exception is the hw:1,2 port which was reported to be Little Endian 
+compliant (supposedly supporting S16_LE) but processes in fact only S16_BE streams.
+This has been fixed in kernel 2.6.23 and above and now the hw:1,2 interface 
+is reported to be big endian in this default driver mode.
 
 Examples:
  * playing a S24_3BE encoded raw file to the Ao port
@@ -98,22 +121,26 @@
    % arecord -D hw:1,1 -c2  -t raw -r48000 -fS24_3BE test.raw
  * playing a S16_BE encoded raw file to the Do port
    % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
+ * playing an ac3 sample file to the Do port
+   % aplay -D hw:1,2 --channels=6 ac3_S16_BE_encoded_file.raw
 
-If you're happy with the default Alsa driver setup and don't experience any 
+If you're happy with the default Alsa driver mode and don't experience any 
 issue with this mode, then you can skip the following chapter.
 
-2.2.2 - Advanced module setup
+3.2 - Advanced module setup
+---------------------------
 
 Due to the hardware constraints described above, the device initialization made 
 by the Alsa driver in default mode may result in a corrupted state of the 
 device. For instance, a particularly annoying issue is that the sound captured 
-from the Ai port sounds distorted (as if boosted with an excessive high volume 
-gain).
+from the Ai interface sounds distorted (as if boosted with an excessive high
+volume gain).
 
 For people having this problem, the snd-usb-audio module has a new module 
-parameter called "device_setup".
+parameter called "device_setup" (this parameter was introduced in kernel
+release 2.6.17)
 
-2.2.2.1 - Initializing the working mode of the Audiophile USB
+3.2.1 - Initializing the working mode of the Audiophile USB
 
 As far as the Audiophile USB device is concerned, this value let the user 
 specify:
@@ -121,33 +148,57 @@
  * the sample rate
  * whether the Di port is used or not 
 
-Here is a list of supported device_setup values for this device:
- * device_setup=0x00 (or omitted)
-   - Alsa driver default mode
-   - maintains backward compatibility with setups that do not use this 
-     parameter by not introducing any change
-   - results sometimes in corrupted sound as described earlier
+When initialized with "device_setup=0x00", the snd-usb-audio module has
+the same behaviour as when the parameter is omitted (see paragraph "Default 
+Alsa driver mode" above)
+
+Others modes are described in the following subsections.
+
+3.2.1.1 - 16-bit modes
+
+The two supported modes are:
+
  * device_setup=0x01
    - 16bits 48kHz mode with Di disabled
    - Ai,Ao,Do can be used at the same time
    - hw:1,0 is not available in capture mode
    - hw:1,2 is not available
+
  * device_setup=0x11
    - 16bits 48kHz mode with Di enabled
    - Ai,Ao,Di,Do can be used at the same time
    - hw:1,0 is available in capture mode
    - hw:1,2 is not available
+
+In this modes the device operates only at 16bits-modes. Before kernel 2.6.23,
+the devices where reported to be Big-Endian when in fact they were Little-Endian
+so that playing a file was a matter of using:
+   % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test_S16_LE.raw
+where "test_S16_LE.raw" was in fact a little-endian sample file.
+
+Thanks to Hakan Lennestal (who discovered the Little-Endiannes of the device in
+these modes) a fix has been committed (expected in kernel 2.6.23) and
+Alsa now reports Little-Endian interfaces. Thus playing a file now is as simple as
+using:
+   % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_LE test_S16_LE.raw
+
+3.2.1.2 - 24-bit modes
+
+The three supported modes are:
+
  * device_setup=0x09
    - 24bits 48kHz mode with Di disabled
    - Ai,Ao,Do can be used at the same time
    - hw:1,0 is not available in capture mode
    - hw:1,2 is not available
+
  * device_setup=0x19
    - 24bits 48kHz mode with Di enabled
    - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
    - hw:1,0 is available in capture mode and an active digital source must be 
      connected to Di
    - hw:1,2 is not available
+
  * device_setup=0x0D or 0x10
    - 24bits 96kHz mode
    - Di is enabled by default for this mode but does not need to be connected 
@@ -155,34 +206,64 @@
    - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
    - hw:1,0 is available in captured mode
    - hw:1,2 is not available
+
+In these modes the device is only Big-Endian compliant (see "Default Alsa driver 
+mode" above for an aplay command example)
+
+3.2.1.3 - AC3 w/ DTS passthru mode
+
+Thanks to Hakan Lennestal, I now have a report saying that this mode works.
+
  * device_setup=0x03
    - 16bits 48kHz mode with only the Do port enabled 
-   - AC3 with DTS passthru (not tested)
+   - AC3 with DTS passthru
    - Caution with this setup the Do port is mapped to the pcm device hw:1,0
 
-2.2.2.2 - Setting and switching configurations with the device_setup parameter
+The command line used to playback the AC3/DTS encoded .wav-files in this mode:
+   % aplay -D hw:1,0 --channels=6 ac3_S16_LE_encoded_file.raw
+
+3.2.2 - How to use the device_setup parameter
+----------------------------------------------
 
 The parameter can be given:
+
  * By manually probing the device (as root):
    # modprobe -r snd-usb-audio
    # modprobe snd-usb-audio index=1 device_setup=0x09
+
  * Or while configuring the modules options in your modules configuration file
    - For Fedora distributions, edit the /etc/modprobe.conf file:
        alias snd-card-1 snd-usb-audio
        options snd-usb-audio index=1 device_setup=0x09
 
-IMPORTANT NOTE WHEN SWITCHING CONFIGURATION:
--------------------------------------------
- * You may need to _first_ initialize the module with the correct device_setup 
-   parameter and _only_after_ turn on the Audiophile USB device
- * This is especially true when switching the sample depth:
+CAUTION when initializaing the device
+-------------------------------------
+
+ * Correct initialization on the device requires that device_setup is given to
+   the module BEFORE the device is turned on. So, if you use the "manual probing"
+   method described above, take care to power-on the device AFTER this initialization.
+
+ * Failing to respect this will lead in a misconfiguration of the device. In this case
+   turn off the device, unproble the snd-usb-audio module, then probe it again with 
+   correct device_setup parameter and then (and only then) turn on the device again.
+
+ * If you've correctly initialized the device in a valid mode and then want to switch
+   to  another mode (possibly with another sample-depth), please use also the following 
+   procedure:
    - first turn off the device
    - de-register the snd-usb-audio module (modprobe -r)
    - change the device_setup parameter by changing the device_setup
      option in /etc/modprobe.conf 
    - turn on the device
+ * A workaround for this last issue has been applied to kernel 2.6.23, but it may not
+   be enough to ensure the 'stability' of the device initialization.
 
-2.2.2.3 - Audiophile USB's device_setup structure
+3.2.3 - Technical details for hackers
+-------------------------------------
+This section is for hackers, wanting to understand details about the device
+internals and how Alsa supports it.
+
+3.2.3.1 - Audiophile USB's device_setup structure
 
 If you want to understand the device_setup magic numbers for the Audiophile 
 USB, you need some very basic understanding of binary computation. However, 
@@ -228,12 +309,12 @@
    - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
      only be able to use one at the same time
 
-2.2.3 -  USB implementation details for this device
+3.2.3.2 -  USB implementation details for this device
 
 You may safely skip this section if you're not interested in driver 
-development.
+hacking.
 
-This section describes some internal aspects of the device and summarize the 
+This section describes some internal aspects of the device and summarizes the 
 data I got by usb-snooping the windows and Linux drivers.
 
 The M-Audio Audiophile USB has 7 USB Interfaces:
@@ -293,43 +374,45 @@
 "audiophile_skip_setting_quirk" in order to prevent AltSettings not 
 corresponding to device_setup from being registered in the driver.
 
-3 - Audiophile USB and Jack support
+4 - Audiophile USB and Jack support
 ===================================
 
 This section deals with support of the Audiophile USB device in Jack.
-The main issue regarding this support is that the device is Big Endian 
-compliant.
 
-3.1 - Using the plug alsa plugin
---------------------------------
+There are 2 main potential issues when using Jackd with the device:
+* support for Big-Endian devices in 24-bit modes
+* support for 4-in / 4-out channels
+
+4.1 - Direct support in Jackd
+-----------------------------
+
+Jack supports big endian devices only in recent versions (thanks to
+Andreas Steinmetz for his first big-endian patch). I can't remember 
+extacly when this support was released into jackd, let's just say that 
+with jackd version 0.103.0 it's almost ok (just a small bug is affecting 
+16bits Big-Endian devices, but since you've read  carefully the above 
+paragraphs, you're now using kernel >= 2.6.23 and your 16bits devices 
+are now Little Endians ;-) ).
 
-Jack doesn't directly support big endian devices. Thus, one way to have support 
-for this device with Alsa is to use the Alsa "plug" converter.
+You can run jackd with the following command for playback with Ao and
+record with Ai:
+  % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
+
+4.2 - Using Alsa plughw
+-----------------------
+If you don't have a recent Jackd installed, you can downgrade to using
+the Alsa "plug" converter.
 
 For instance here is one way to run Jack with 2 playback channels on Ao and 2 
 capture channels from Ai:
   % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
 
-
 However you may see the following warning message:
 "You appear to be using the ALSA software "plug" layer, probably a result of 
 using the "default" ALSA device. This is less efficient than it could be. 
 Consider using a hardware device instead rather than using the plug layer."
 
-3.2 - Patching alsa to use direct pcm device
---------------------------------------------
-A patch for Jack by Andreas Steinmetz adds support for Big Endian devices. 
-However it has not been included in the CVS tree.
-
-You can find it at the following URL:
-http://sourceforge.net/tracker/index.php?func=detail&aid=1289682&group_id=39687&
-atid=425939
-
-After having applied the patch you can run jackd with the following command 
-line:
-  % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
-
-3.2 - Getting 2 input and/or output interfaces in Jack
+4.3 - Getting 2 input and/or output interfaces in Jack
 ------------------------------------------------------
 
 As you can see, starting the Jack server this way will only enable 1 stereo
@@ -339,6 +422,7 @@
 * Jack can only open one capture device and one playback device at a time
 * The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
   (and optionally hw:1,2)
+
 If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
 combine the Alsa devices into one logical "complex" device.
 
@@ -348,13 +432,11 @@
 the Audiophile USB.
 
 Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
-* patching Jack with the previously mentioned "Big Endian" patch
-* patching Jackd with the MMAP_COMPLEX patch (see the ice1712 page)
-* patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
+* Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
+* (maybe) patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
 * define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
   file 
 * start jackd with this device
 
-I had no success in testing this for now, but this may be due to my OS
-configuration. If you have any success with this kind of setup, please
-drop me an email.
+I had no success in testing this for now, if you have any success with this kind 
+of setup, please drop me an email.
diff -ruN linux-2.6.22/Documentation/sound/alsa/CMIPCI.txt linux-2.6.22-alsa/Documentation/sound/alsa/CMIPCI.txt
--- linux-2.6.22/Documentation/sound/alsa/CMIPCI.txt	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/CMIPCI.txt	2007-09-01 20:57:16.000000000 +0200
@@ -1,5 +1,5 @@
-              Brief Notes on C-Media 8738/8338 Driver
-              =======================================
+         Brief Notes on C-Media 8338/8738/8768/8770 Driver
+         =================================================
 
                    Takashi Iwai <tiwai@suse.de>
 
@@ -212,7 +212,9 @@
 The MPU401-UART interface is disabled as default.  You need to set
 module option "mpu_port" with a valid I/O port address to enable the
 MIDI support.  The valid I/O ports are 0x300, 0x310, 0x320 and 0x330.
-Choose the value which doesn't conflict with other cards.
+Choose the value which doesn't conflict with other cards.  With
+CMI8738 and newer chips, you can use "mpu_port=1" to use a PCI port
+address that does not conflict with any other card.
 
 There is _no_ hardware wavetable function on this chip (except for
 OPL3 synth below).
@@ -230,6 +232,8 @@
 The output quality of FM OPL/3 is, however, very weird.
 I don't know why..
 
+CMI8768 and newer chips do not have the FM synth.
+
 
 Joystick and Modem
 ------------------
diff -ruN linux-2.6.22/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl linux-2.6.22-alsa/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
--- linux-2.6.22/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl	2007-09-01 20:57:16.000000000 +0200
@@ -18,8 +18,8 @@
       </affiliation>
      </author>
 
-     <date>November 17, 2005</date>
-     <edition>0.3.6</edition>
+     <date>July 26, 2007</date>
+     <edition>0.3.6.1</edition>
 
     <abstract>
       <para>
@@ -405,8 +405,9 @@
   /* definition of the chip-specific record */
   struct mychip {
           struct snd_card *card;
-          // rest of implementation will be in the section
-          // "PCI Resource Managements"
+          /* rest of implementation will be in the section
+           * "PCI Resource Managements"
+           */
   };
 
   /* chip-specific destructor
@@ -414,7 +415,7 @@
    */
   static int snd_mychip_free(struct mychip *chip)
   {
-          .... // will be implemented later...
+          .... /* will be implemented later... */
   }
 
   /* component-destructor
@@ -440,8 +441,9 @@
 
           *rchip = NULL;
 
-          // check PCI availability here
-          // (see "PCI Resource Managements")
+          /* check PCI availability here
+           * (see "PCI Resource Managements")
+           */
           ....
 
           /* allocate a chip-specific data with zero filled */
@@ -451,12 +453,13 @@
 
           chip->card = card;
 
-          // rest of initialization here; will be implemented
-          // later, see "PCI Resource Managements"
+          /* rest of initialization here; will be implemented
+           * later, see "PCI Resource Managements"
+           */
           ....
 
-          if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
-                                    chip, &ops)) < 0) {
+          err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+          if (err < 0) {
                   snd_mychip_free(chip);
                   return err;
           }
@@ -490,7 +493,8 @@
                   return -ENOMEM;
 
           /* (3) */
-          if ((err = snd_mychip_create(card, pci, &chip)) < 0) {
+          err = snd_mychip_create(card, pci, &chip);
+          if (err < 0) {
                   snd_card_free(card);
                   return err;
           }
@@ -502,10 +506,11 @@
                   card->shortname, chip->ioport, chip->irq);
 
           /* (5) */
-          .... // implemented later
+          .... /* implemented later */
 
           /* (6) */
-          if ((err = snd_card_register(card)) < 0) {
+          err = snd_card_register(card);
+          if (err < 0) {
                   snd_card_free(card);
                   return err;
           }
@@ -605,7 +610,8 @@
 <![CDATA[
   struct mychip *chip;
   ....
-  if ((err = snd_mychip_create(card, pci, &chip)) < 0) {
+  err = snd_mychip_create(card, pci, &chip);
+  if (err < 0) {
           snd_card_free(card);
           return err;
   }
@@ -666,7 +672,8 @@
           <informalexample>
             <programlisting>
 <![CDATA[
-  if ((err = snd_card_register(card)) < 0) {
+  err = snd_card_register(card);
+  if (err < 0) {
           snd_card_free(card);
           return err;
   }
@@ -1091,7 +1098,7 @@
   static int snd_mychip_free(struct mychip *chip)
   {
           /* disable hardware here if any */
-          .... // (not implemented in this document)
+          .... /* (not implemented in this document) */
 
           /* release the irq */
           if (chip->irq >= 0)
@@ -1119,7 +1126,8 @@
           *rchip = NULL;
 
           /* initialize the PCI entry */
-          if ((err = pci_enable_device(pci)) < 0)
+          err = pci_enable_device(pci);
+          if (err < 0)
                   return err;
           /* check PCI availability (28bit DMA) */
           if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
@@ -1141,7 +1149,8 @@
           chip->irq = -1;
 
           /* (1) PCI resource allocation */
-          if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+          err = pci_request_regions(pci, "My Chip");
+          if (err < 0) {
                   kfree(chip);
                   pci_disable_device(pci);
                   return err;
@@ -1156,10 +1165,10 @@
           chip->irq = pci->irq;
 
           /* (2) initialization of the chip hardware */
-          .... //   (not implemented in this document)
+          .... /*   (not implemented in this document) */
 
-          if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
-                                    chip, &ops)) < 0) {
+          err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+          if (err < 0) {
                   snd_mychip_free(chip);
                   return err;
           }
@@ -1233,7 +1242,8 @@
         <informalexample>
           <programlisting>
 <![CDATA[
-  if ((err = pci_enable_device(pci)) < 0)
+  err = pci_enable_device(pci);
+  if (err < 0)
           return err;
   if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
       pci_set_consistent_dma_mask(pci, DMA_28BIT_MASK) < 0) {
@@ -1294,7 +1304,8 @@
         <informalexample>
           <programlisting>
 <![CDATA[
-  if ((err = pci_request_regions(pci, "My Chip")) < 0) { 
+  err = pci_request_regions(pci, "My Chip");
+  if (err < 0) { 
           kfree(chip);
           pci_disable_device(pci);
           return err;
@@ -1322,7 +1333,7 @@
           <programlisting>
 <![CDATA[
   if (request_irq(pci->irq, snd_mychip_interrupt,
-                  IRQF_DISABLED|IRQF_SHARED, "My Chip", chip)) {
+                  IRQF_SHARED, "My Chip", chip)) {
           printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
           snd_mychip_free(chip);
           return -EBUSY;
@@ -1773,7 +1784,8 @@
           struct snd_pcm_runtime *runtime = substream->runtime;
 
           runtime->hw = snd_mychip_playback_hw;
-          // more hardware-initialization will be done here
+          /* more hardware-initialization will be done here */
+          ....
           return 0;
   }
 
@@ -1781,7 +1793,8 @@
   static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
   {
           struct mychip *chip = snd_pcm_substream_chip(substream);
-          // the hardware-specific codes will be here
+          /* the hardware-specific codes will be here */
+          ....
           return 0;
 
   }
@@ -1793,7 +1806,8 @@
           struct snd_pcm_runtime *runtime = substream->runtime;
 
           runtime->hw = snd_mychip_capture_hw;
-          // more hardware-initialization will be done here
+          /* more hardware-initialization will be done here */
+          ....
           return 0;
   }
 
@@ -1801,7 +1815,8 @@
   static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
   {
           struct mychip *chip = snd_pcm_substream_chip(substream);
-          // the hardware-specific codes will be here
+          /* the hardware-specific codes will be here */
+          ....
           return 0;
 
   }
@@ -1844,10 +1859,12 @@
   {
           switch (cmd) {
           case SNDRV_PCM_TRIGGER_START:
-                  // do something to start the PCM engine
+                  /* do something to start the PCM engine */
+                  ....
                   break;
           case SNDRV_PCM_TRIGGER_STOP:
-                  // do something to stop the PCM engine
+                  /* do something to stop the PCM engine */
+                  ....
                   break;
           default:
                   return -EINVAL;
@@ -1900,8 +1917,8 @@
           struct snd_pcm *pcm;
           int err;
 
-          if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1,
-                                 &pcm)) < 0) 
+          err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
+          if (err < 0) 
                   return err;
           pcm->private_data = chip;
           strcpy(pcm->name, "My Chip");
@@ -1939,8 +1956,8 @@
           struct snd_pcm *pcm;
           int err;
 
-          if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1,
-                                 &pcm)) < 0) 
+          err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
+          if (err < 0) 
                   return err;
           pcm->private_data = chip;
           strcpy(pcm->name, "My Chip");
@@ -2097,7 +2114,7 @@
           struct mychip *chip = snd_pcm_chip(pcm);
           /* free your own data */
           kfree(chip->my_private_pcm_data);
-          // do what you like else
+          /* do what you like else */
           ....
   }
 
@@ -2884,10 +2901,10 @@
 <![CDATA[
   switch (cmd) {
   case SNDRV_PCM_TRIGGER_START:
-          // do something to start the PCM engine
+          /* do something to start the PCM engine */
           break;
   case SNDRV_PCM_TRIGGER_STOP:
-          // do something to stop the PCM engine
+          /* do something to stop the PCM engine */
           break;
   default:
           return -EINVAL;
@@ -3071,7 +3088,7 @@
                   spin_unlock(&chip->lock);
                   snd_pcm_period_elapsed(chip->substream);
                   spin_lock(&chip->lock);
-                  // acknowledge the interrupt if necessary
+                  /* acknowledge the interrupt if necessary */
           }
           ....
           spin_unlock(&chip->lock);
@@ -3134,7 +3151,7 @@
                           snd_pcm_period_elapsed(substream);
                           spin_lock(&chip->lock);
                   }
-                  // acknowledge the interrupt if necessary
+                  /* acknowledge the interrupt if necessary */
           }
           ....
           spin_unlock(&chip->lock);
@@ -3604,7 +3621,7 @@
 	    <title>Example of info callback</title>
             <programlisting>
 <![CDATA[
-  static int snd_myctl_info(struct snd_kcontrol *kcontrol,
+  static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
                           struct snd_ctl_elem_info *uinfo)
   {
           uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -3639,7 +3656,7 @@
           <informalexample>
             <programlisting>
 <![CDATA[
-  static int snd_myctl_info(struct snd_kcontrol *kcontrol,
+  static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
                           struct snd_ctl_elem_info *uinfo)
   {
           static char *texts[4] = {
@@ -3658,6 +3675,16 @@
             </programlisting>
           </informalexample>
         </para>
+
+        <para>
+	  Some common info callbacks are prepared for easy use:
+	<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
+	boolean item, just like <function>snd_myctl_mono_info</function>
+	above, and the latter is for a stereo channel boolean item.
+	</para>
+
       </section>
 
       <section id="control-interface-callbacks-get">
@@ -3794,7 +3821,8 @@
         <informalexample>
           <programlisting>
 <![CDATA[
-  if ((err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip))) < 0)
+  err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));
+  if (err < 0)
           return err;
 ]]>
           </programlisting>
@@ -3880,7 +3908,7 @@
   {
           struct mychip *chip = ac97->private_data;
           ....
-          // read a register value here from the codec
+          /* read a register value here from the codec */
           return the_register_value;
   }
 
@@ -3889,7 +3917,7 @@
   {
           struct mychip *chip = ac97->private_data;
           ....
-          // write the given register value to the codec
+          /* write the given register value to the codec */
   }
 
   static int snd_mychip_ac97(struct mychip *chip)
@@ -3902,7 +3930,8 @@
                   .read = snd_mychip_ac97_read,
           };
 
-          if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
+          err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+          if (err < 0)
                   return err;
           memset(&ac97, 0, sizeof(ac97));
           ac97.private_data = chip;
@@ -4447,10 +4476,10 @@
         <informalexample>
           <programlisting>
 <![CDATA[
-  struct list_head *list;
   struct snd_rawmidi_substream *substream;
-  list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
-          substream = list_entry(list, struct snd_rawmidi_substream, list);
+  list_for_each_entry(substream,
+                      &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
+                      list {
           sprintf(substream->name, "My MIDI Port %d", substream->number + 1);
   }
   /* same for SNDRV_RAWMIDI_STREAM_INPUT */
diff -ruN linux-2.6.22/Documentation/sound/alsa/OSS-Emulation.txt linux-2.6.22-alsa/Documentation/sound/alsa/OSS-Emulation.txt
--- linux-2.6.22/Documentation/sound/alsa/OSS-Emulation.txt	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/OSS-Emulation.txt	2007-09-01 20:57:16.000000000 +0200
@@ -278,6 +278,21 @@
 image.
 
 
+Duplex Streams
+==============
+
+Note that when attempting to use a single device file for playback and
+capture, the OSS API provides no way to set the format, sample rate or
+number of channels different in each direction.  Thus
+	io_handle = open("device", O_RDWR)
+will only function correctly if the values are the same in each direction.
+
+To use different values in the two directions, use both
+	input_handle = open("device", O_RDONLY)
+	output_handle = open("device", O_WRONLY)
+and set the values for the corresponding handle.
+
+
 Unsupported Features
 ====================
 
@@ -288,10 +303,3 @@
 the buffer as the conventional (mono or 2-channels, 8 or 16bit) format
 on OSS.
 
-USB devices
------------
-Some USB devices support only 24bit format packed in 3bytes.  This
-format is not supported by OSS and no conversion is provided by kernel
-OSS emulation.  You can use the user-space OSS emulation via libaoss
-instead.
-
diff -ruN linux-2.6.22/Documentation/sound/alsa/hda_codec.txt linux-2.6.22-alsa/Documentation/sound/alsa/hda_codec.txt
--- linux-2.6.22/Documentation/sound/alsa/hda_codec.txt	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/hda_codec.txt	2007-09-01 20:57:16.000000000 +0200
@@ -49,6 +49,9 @@
 		       unsigned int verb, unsigned int parm);
 	unsigned int (*get_response)(struct hda_codec *codec);
 	void (*private_free)(struct hda_bus *);
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	void (*pm_notify)(struct hda_codec *codec);
+#endif
 };
 
 The command callback is called when the codec module needs to send a
@@ -56,9 +59,16 @@
 The get_response callback is called when the codec requires the answer
 for the last command.  These two callbacks are mandatory and have to
 be given.
-The last, private_free callback, is optional.  It's called in the
+The third, private_free callback, is optional.  It's called in the
 destructor to release any necessary data in the lowlevel driver.
 
+The pm_notify callback is available only with
+CONFIG_SND_HDA_POWER_SAVE kconfig.  It's called when the codec needs
+to power up or may power down.  The controller should check the all
+belonging codecs on the bus whether they are actually powered off
+(check codec->power_on), and optionally the driver may power down the
+contoller side, too.
+
 The bus instance is created via snd_hda_bus_new().  You need to pass
 the card instance, the template, and the pointer to store the
 resultant bus instance.
@@ -86,10 +96,8 @@
 The codec is stored in a linked list of bus instance.  You can follow
 the codec list like:
 
-	struct list_head *p;
 	struct hda_codec *codec;
-	list_for_each(p, &bus->codec_list) {
-		codec = list_entry(p, struct hda_codec, list);
+	list_for_each_entry(codec, &bus->codec_list, list) {
 		...
 	}
 
@@ -100,10 +108,15 @@
 Codec Access
 ============
 
-To access codec, use snd_codec_read() and snd_codec_write().
+To access codec, use snd_hda_codec_read() and snd_hda_codec_write().
 snd_hda_param_read() is for reading parameters.
 For writing a sequence of verbs, use snd_hda_sequence_write().
 
+There are variants of cached read/write, snd_hda_codec_write_cache(),
+snd_hda_sequence_write_cache().  These are used for recording the
+register states for the power-mangement resume.  When no PM is needed,
+these are equivalent with non-cached version.
+
 To retrieve the number of sub nodes connected to the given node, use
 snd_hda_get_sub_nodes().  The connection list can be obtained via
 snd_hda_get_connections() call.
@@ -239,6 +252,10 @@
 		int (*suspend)(struct hda_codec *codec, pm_message_t state);
 		int (*resume)(struct hda_codec *codec);
 	#endif
+	#ifdef CONFIG_SND_HDA_POWER_SAVE
+		int (*check_power_status)(struct hda_codec *codec,
+					  hda_nid_t nid);
+	#endif
 	};
 
 The build_controls callback is called from snd_hda_build_controls().
@@ -251,6 +268,18 @@
 received.
 
 The suspend and resume callbacks are for power management.
+They can be NULL if no special sequence is required.  When the resume
+callback is NULL, the driver calls the init callback and resumes the
+registers from the cache.  If other handling is needed, you'd need to
+write your own resume callback.  There, the amp values can be resumed
+via
+	void snd_hda_codec_resume_amp(struct hda_codec *codec);
+and the other codec registers via
+	void snd_hda_codec_resume_cache(struct hda_codec *codec);
+
+The check_power_status callback is called when the amp value of the
+given widget NID is changed.  The codec code can turn on/off the power
+appropriately from this information.
 
 Each entry can be NULL if not necessary to be called.
 
@@ -267,8 +296,7 @@
 ===========
 
 Call snd_hda_create_spdif_out_ctls() from the patch to create controls
-related with SPDIF out.  In the patch resume callback, call
-snd_hda_resume_spdif().
+related with SPDIF out.
 
 
 Helper Functions
@@ -284,12 +312,7 @@
 is found, it returns the config field value.
 
 snd_hda_add_new_ctls() can be used to create and add control entries.
-Pass the zero-terminated array of struct snd_kcontrol_new.  The same array
-can be passed to snd_hda_resume_ctls() for resume.
-Note that this will call control->put callback of these entries.  So,
-put callback should check codec->in_resume and force to restore the
-given value if it's non-zero even if the value is identical with the
-cached value.
+Pass the zero-terminated array of struct snd_kcontrol_new
 
 Macros HDA_CODEC_VOLUME(), HDA_CODEC_MUTE() and their variables can be
 used for the entry of struct snd_kcontrol_new.
diff -ruN linux-2.6.22/include/sound/ak4xxx-adda.h linux-2.6.22-alsa/include/sound/ak4xxx-adda.h
--- linux-2.6.22/include/sound/ak4xxx-adda.h	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/ak4xxx-adda.h	2007-09-01 20:56:34.000000000 +0200
@@ -43,6 +43,7 @@
 struct snd_akm4xxx_dac_channel {
 	char *name;		/* mixer volume name */
 	unsigned int num_channels;
+	char *switch_name;		/* mixer switch*/
 };
 
 /* ADC labels and channels */
diff -ruN linux-2.6.22/include/sound/asound.h linux-2.6.22-alsa/include/sound/asound.h
--- linux-2.6.22/include/sound/asound.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/asound.h	2007-09-01 20:56:34.000000000 +0200
@@ -92,6 +92,7 @@
 	SNDRV_HWDEP_IFACE_USX2Y_PCM,	/* Tascam US122, US224 & US428 rawusb pcm */
 	SNDRV_HWDEP_IFACE_PCXHR,	/* Digigram PCXHR */
 	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
+	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
 
 	/* Don't forget to change the following: */
 	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
diff -ruN linux-2.6.22/include/sound/control.h linux-2.6.22-alsa/include/sound/control.h
--- linux-2.6.22/include/sound/control.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/control.h	2007-09-01 20:56:34.000000000 +0200
@@ -161,4 +161,12 @@
 	return dst_id;
 }
 
+/*
+ * Frequently used control callbacks
+ */
+int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo);
+int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo);
+
 #endif	/* __SOUND_CONTROL_H */
diff -ruN linux-2.6.22/include/sound/cs4231.h linux-2.6.22-alsa/include/sound/cs4231.h
--- linux-2.6.22/include/sound/cs4231.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/cs4231.h	2007-09-01 20:56:34.000000000 +0200
@@ -210,7 +210,7 @@
 #define CS4231_HW_CS4239	0x0404	/* CS4239 - Crystal Clear (tm) stereo enhancement */
 /* compatible, but clones */
 #define CS4231_HW_INTERWAVE     0x1000	/* InterWave chip */
-#define CS4231_HW_OPL3SA2       0x1001	/* OPL3-SA2 chip */
+#define CS4231_HW_OPL3SA2       0x1101	/* OPL3-SA2 chip, similar to cs4231 */
 
 /* defines for codec.hwshare */
 #define CS4231_HWSHARE_IRQ	(1<<0)
diff -ruN linux-2.6.22/include/sound/cs46xx.h linux-2.6.22-alsa/include/sound/cs46xx.h
--- linux-2.6.22/include/sound/cs46xx.h	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/cs46xx.h	2007-09-01 20:56:34.000000000 +0200
@@ -1723,6 +1723,10 @@
 	struct snd_cs46xx_pcm *playback_pcm;
 	unsigned int play_ctl;
 #endif
+
+#ifdef CONFIG_PM
+	u32 *saved_regs;
+#endif
 };
 
 int snd_cs46xx_create(struct snd_card *card,
diff -ruN linux-2.6.22/include/sound/cs46xx_dsp_spos.h linux-2.6.22-alsa/include/sound/cs46xx_dsp_spos.h
--- linux-2.6.22/include/sound/cs46xx_dsp_spos.h	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/cs46xx_dsp_spos.h	2007-09-01 20:56:34.000000000 +0200
@@ -107,6 +107,7 @@
 	char scb_name[DSP_MAX_SCB_NAME];
 	u32 address;
 	int index;
+	u32 *data;
 
 	struct dsp_scb_descriptor * sub_list_ptr;
 	struct dsp_scb_descriptor * next_scb_ptr;
@@ -127,6 +128,7 @@
 	int size;
 	u32 address;
 	int index;
+	u32 *data;
 };
 
 struct dsp_pcm_channel_descriptor {
diff -ruN linux-2.6.22/include/sound/emu10k1.h linux-2.6.22-alsa/include/sound/emu10k1.h
--- linux-2.6.22/include/sound/emu10k1.h	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/emu10k1.h	2007-09-01 20:56:34.000000000 +0200
@@ -1120,6 +1120,16 @@
 /************************************************************************************************/
 /* EMU1010m HANA Destinations									*/
 /************************************************************************************************/
+/* 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.
+ */
+/* EMU32 = 32-bit serial channel between Alice2 (audigy) and Hana (FPGA) */
+/* EMU_DST_ALICE2_EMU32_X - data channels from Hana to Alice2 used for capture.
+ * Which data is fed into a EMU_DST_ALICE2_EMU32_X channel in Hana depends on
+ * setup of mixer control for each destination - see emumixer.c -
+ * snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
+ */
 #define EMU_DST_ALICE2_EMU32_0	0x000f	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_1	0x0000	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_2	0x0001	/* 16 EMU32 channels to Alice2 +0 to +0xf */
@@ -1199,6 +1209,12 @@
 /************************************************************************************************/
 /* EMU1010m HANA Sources									*/
 /************************************************************************************************/
+/* 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,
+ * or outputs from Alice (audigy) - 16 x EMU_SRC_ALICE_EMU32A +
+ * 16 x EMU_SRC_ALICE_EMU32B
+ */
 #define EMU_SRC_SILENCE		0x0000	/* Silence */
 #define EMU_SRC_DOCK_MIC_A1	0x0100	/* Audio Dock Mic A, 1st or 48kHz only */
 #define EMU_SRC_DOCK_MIC_A2	0x0101	/* Audio Dock Mic A, 2nd or 96kHz */
@@ -1440,6 +1456,9 @@
 	unsigned int adc_pads; /* bit mask */
 	unsigned int dac_pads; /* bit mask */
 	unsigned int internal_clock; /* 44100 or 48000 */
+	unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
+	unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
+	struct task_struct *firmware_thread;
 };
 
 struct snd_emu10k1 {
@@ -1583,9 +1602,9 @@
 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
 int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
 int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value);
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value);
-int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src);
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value);
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value);
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src);
 unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
@@ -1730,6 +1749,8 @@
 #define A_FXBUS2(x)	(0x80 + (x))	/* x = 0x00 - 0x1f extra outs used for EFX capture -> A_FXWC2 */
 #define A_EMU32OUTH(x)	(0xa0 + (x))	/* x = 0x00 - 0x0f "EMU32_OUT_10 - _1F" - ??? */
 #define A_EMU32OUTL(x)	(0xb0 + (x))	/* x = 0x00 - 0x0f "EMU32_OUT_1 - _F" - ??? */
+#define A3_EMU32IN(x)	(0x160 + (x))	/* x = 0x00 - 0x3f "EMU32_IN_00 - _3F" - Only when .device = 0x0008 */
+#define A3_EMU32OUT(x)	(0x1E0 + (x))	/* x = 0x00 - 0x0f "EMU32_OUT_00 - _3F" - Only when .device = 0x0008 */
 #define A_GPR(x)	(A_FXGPREGBASE + (x))
 
 /* cc_reg constants */
diff -ruN linux-2.6.22/include/sound/hda_hwdep.h linux-2.6.22-alsa/include/sound/hda_hwdep.h
--- linux-2.6.22/include/sound/hda_hwdep.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/include/sound/hda_hwdep.h	2007-09-01 20:56:34.000000000 +0200
@@ -0,0 +1,44 @@
+/*
+ * HWDEP Interface for HD-audio codec
+ *
+ * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_HDA_HWDEP_H
+#define __SOUND_HDA_HWDEP_H
+
+#define HDA_HWDEP_VERSION	((1 << 16) | (0 << 8) | (0 << 0)) /* 1.0.0 */
+
+/* verb */
+#define HDA_REG_NID_SHIFT	24
+#define HDA_REG_VERB_SHIFT	8
+#define HDA_REG_VAL_SHIFT	0
+#define HDA_VERB(nid,verb,param)	((nid)<<24 | (verb)<<8 | (param))
+
+struct hda_verb_ioctl {
+	u32 verb;	/* HDA_VERB() */
+	u32 res;	/* response */
+};
+
+/*
+ * ioctls
+ */
+#define HDA_IOCTL_PVERSION		_IOR('H', 0x10, int)
+#define HDA_IOCTL_VERB_WRITE		_IOWR('H', 0x11, struct hda_verb_ioctl)
+#define HDA_IOCTL_GET_WCAP		_IOWR('H', 0x12, struct hda_verb_ioctl)
+
+#endif
diff -ruN linux-2.6.22/include/sound/hdspm.h linux-2.6.22-alsa/include/sound/hdspm.h
--- linux-2.6.22/include/sound/hdspm.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/hdspm.h	2007-09-01 20:56:34.000000000 +0200
@@ -1,4 +1,4 @@
-#ifndef __SOUND_HDSPM_H		/* -*- linux-c -*- */
+#ifndef __SOUND_HDSPM_H
 #define __SOUND_HDSPM_H
 /*
  *   Copyright (C) 2003 Winfried Ritsch (IEM)
@@ -61,7 +61,8 @@
 };
 
 /* use indirect access due to the limit of ioctl bit size */
-#define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS _IOR('H', 0x40, struct hdspm_peak_rms_ioctl)
+#define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS \
+	_IOR('H', 0x40, struct hdspm_peak_rms_ioctl)
 
 /* ------------ CONFIG block IOCTL ---------------------- */
 
@@ -79,7 +80,8 @@
 	unsigned int analog_out;
 };
 
-#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO _IOR('H', 0x41, struct hdspm_config_info)
+#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO \
+	_IOR('H', 0x41, struct hdspm_config_info)
 
 
 /* get Soundcard Version */
@@ -93,10 +95,14 @@
 
 /* ------------- get Matrix Mixer IOCTL --------------- */
 
-/* MADI mixer: 64inputs+64playback in 64outputs = 8192 => *4Byte = 32768 Bytes */
+/* MADI mixer: 64inputs+64playback in 64outputs = 8192 => *4Byte =
+ * 32768 Bytes
+ */
 
 /* organisation is 64 channelfader in a continous memory block */
-/* equivalent to hardware definition, maybe for future feature of mmap of them */
+/* equivalent to hardware definition, maybe for future feature of mmap of
+ * them
+ */
 /* each of 64 outputs has 64 infader and 64 outfader: 
    Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */
 
diff -ruN linux-2.6.22/include/sound/pcm.h linux-2.6.22-alsa/include/sound/pcm.h
--- linux-2.6.22/include/sound/pcm.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/pcm.h	2007-09-01 20:56:34.000000000 +0200
@@ -922,7 +922,10 @@
 snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
 				    void __user **bufs, snd_pcm_uframes_t frames);
 
+extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
+
 int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
+unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate);
 
 static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,
 					      struct snd_dma_buffer *bufp)
diff -ruN linux-2.6.22/include/sound/sb.h linux-2.6.22-alsa/include/sound/sb.h
--- linux-2.6.22/include/sound/sb.h	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/sb.h	2007-09-01 20:56:34.000000000 +0200
@@ -38,6 +38,7 @@
 	SB_HW_ALS100,		/* Avance Logic ALS100 chip */
 	SB_HW_ALS4000,		/* Avance Logic ALS4000 chip */
 	SB_HW_DT019X,		/* Diamond Tech. DT-019X / Avance Logic ALS-007 */
+	SB_HW_CS5530,		/* Cyrix/NatSemi 5530 VSA1 */
 };
 
 #define SB_OPEN_PCM			0x01
diff -ruN linux-2.6.22/include/sound/soc.h linux-2.6.22-alsa/include/sound/soc.h
--- linux-2.6.22/include/sound/soc.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/soc.h	2007-09-01 20:56:34.000000000 +0200
@@ -201,8 +201,7 @@
 	struct snd_ctl_elem_info *uinfo);
 int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_info *uinfo);
-int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo);
+#define snd_soc_info_bool_ext		snd_ctl_boolean_mono
 int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
diff -ruN linux-2.6.22/sound/Kconfig linux-2.6.22-alsa/sound/Kconfig
--- linux-2.6.22/sound/Kconfig	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/Kconfig	2007-09-01 20:55:22.000000000 +0200
@@ -63,8 +63,14 @@
 
 source "sound/arm/Kconfig"
 
+if SPI
+source "sound/spi/Kconfig"
+endif
+
 source "sound/mips/Kconfig"
 
+source "sound/sh/Kconfig"
+
 # the following will depend on the order of config.
 # here assuming USB is defined before ALSA
 source "sound/usb/Kconfig"
diff -ruN linux-2.6.22/sound/Makefile linux-2.6.22-alsa/sound/Makefile
--- linux-2.6.22/sound/Makefile	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -5,7 +5,8 @@
 obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
+	sparc/ spi/ parisc/ pcmcia/ mips/ soc/
 obj-$(CONFIG_SND_AOA) += aoa/
 
 # This one must be compilable even if sound is configured out
diff -ruN linux-2.6.22/sound/aoa/codecs/snd-aoa-codec-onyx.c linux-2.6.22-alsa/sound/aoa/codecs/snd-aoa-codec-onyx.c
--- linux-2.6.22/sound/aoa/codecs/snd-aoa-codec-onyx.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/aoa/codecs/snd-aoa-codec-onyx.c	2007-09-01 20:55:22.000000000 +0200
@@ -297,15 +297,7 @@
 	.put = onyx_snd_capture_source_put,
 };
 
-static int onyx_snd_mute_info(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define onyx_snd_mute_info	snd_ctl_boolean_stereo_info
 
 static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
@@ -359,15 +351,7 @@
 };
 
 
-static int onyx_snd_single_bit_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 onyx_snd_single_bit_info	snd_ctl_boolean_mono_info
 
 #define FLAG_POLARITY_INVERT	1
 #define FLAG_SPDIFLOCK		2
@@ -661,7 +645,7 @@
 		.tag = 2,
 	},
 #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
-Once alsa gets supports for this kind of thing we can add it...
+	/* Once alsa gets supports for this kind of thing we can add it... */
 	{
 		/* digital compressed output */
 		.formats =  SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
@@ -713,7 +697,7 @@
 	if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
 		/* mute and lock analog output */
 		onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
-		if (onyx_write_register(onyx
+		if (onyx_write_register(onyx,
 					ONYX_REG_DAC_CONTROL,
 					v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
 			goto out_unlock;
diff -ruN linux-2.6.22/sound/aoa/codecs/snd-aoa-codec-tas.c linux-2.6.22-alsa/sound/aoa/codecs/snd-aoa-codec-tas.c
--- linux-2.6.22/sound/aoa/codecs/snd-aoa-codec-tas.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/aoa/codecs/snd-aoa-codec-tas.c	2007-09-01 20:55:22.000000000 +0200
@@ -272,15 +272,7 @@
 	.put = tas_snd_vol_put,
 };
 
-static int tas_snd_mute_info(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define tas_snd_mute_info	snd_ctl_boolean_stereo_info
 
 static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
@@ -431,15 +423,7 @@
 	.put = tas_snd_drc_range_put,
 };
 
-static int tas_snd_drc_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 tas_snd_drc_switch_info		snd_ctl_boolean_mono_info
 
 static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
@@ -743,6 +727,7 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
 /* we are controlled via i2c and assume that is always up
  * If that wasn't the case, we'd have to suspend once
  * our i2c device is suspended, and then take note of that! */
@@ -768,7 +753,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
 static int _tas_suspend(struct codec_info_item *cii, pm_message_t state)
 {
 	return tas_suspend(cii->codec_data);
@@ -778,7 +762,10 @@
 {
 	return tas_resume(cii->codec_data);
 }
-#endif
+#else /* CONFIG_PM */
+#define _tas_suspend	NULL
+#define _tas_resume	NULL
+#endif /* CONFIG_PM */
 
 static struct codec_info tas_codec_info = {
 	.transfers = tas_transfers,
@@ -791,10 +778,8 @@
 	.owner = THIS_MODULE,
 	.usable = tas_usable,
 	.switch_clock = tas_switch_clock,
-#ifdef CONFIG_PM
 	.suspend = _tas_suspend,
 	.resume = _tas_resume,
-#endif
 };
 
 static int tas_init_codec(struct aoa_codec *codec)
diff -ruN linux-2.6.22/sound/aoa/fabrics/snd-aoa-fabric-layout.c linux-2.6.22-alsa/sound/aoa/fabrics/snd-aoa-fabric-layout.c
--- linux-2.6.22/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2007-09-01 20:55:22.000000000 +0200
@@ -582,15 +582,7 @@
  * make the fabric handle all the card stuff, etc... */
 static struct layout_dev *layout_device;
 
-static int control_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 control_info	snd_ctl_boolean_mono_info
 
 #define AMP_CONTROL(n, description)					\
 static int n##_control_get(struct snd_kcontrol *kcontrol,		\
diff -ruN linux-2.6.22/sound/arm/sa11xx-uda1341.c linux-2.6.22-alsa/sound/arm/sa11xx-uda1341.c
--- linux-2.6.22/sound/arm/sa11xx-uda1341.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/arm/sa11xx-uda1341.c	2007-09-01 20:55:22.000000000 +0200
@@ -79,12 +79,6 @@
 #include <asm/mach-types.h>
 #include <asm/dma.h>
 
-#ifdef CONFIG_H3600_HAL
-#include <asm/semaphore.h>
-#include <asm/uaccess.h>
-#include <asm/arch/h3600_hal.h>
-#endif
-
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/initval.h>
@@ -100,9 +94,6 @@
  * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this
  * module for Familiar 0.6.1
  */
-#ifdef CONFIG_H3600_HAL
-#define HH_VERSION 1
-#endif
 
 /* {{{ Type definitions */
 
@@ -238,11 +229,8 @@
 		rate = 8000;
 
 	/* Set the external clock generator */
-#ifdef CONFIG_H3600_HAL
-	h3600_audio_clock(rate);
-#else	
+	
 	sa11xx_uda1341_set_audio_clock(rate);
-#endif
 
 	/* Select the clock divisor */
 	switch (rate) {
@@ -307,13 +295,10 @@
 	local_irq_restore(flags);
 
 	/* Enable the audio power */
-#ifdef CONFIG_H3600_HAL
-	h3600_audio_power(AUDIO_RATE_DEFAULT);
-#else
+
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-#endif
  
 	/* Wait for the UDA1341 to wake up */
 	mdelay(1); //FIXME - was removed by Perex - Why?
@@ -331,21 +316,13 @@
 	/* make the left and right channels unswapped (flip the WS latch) */
 	Ser4SSDR = 0;
 
-#ifdef CONFIG_H3600_HAL
-	h3600_audio_mute(0);
-#else	
-	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);        
-#endif     
+	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
 }
 
 static void sa11xx_uda1341_audio_shutdown(struct sa11xx_uda1341 *sa11xx_uda1341)
 {
 	/* mute on */
-#ifdef CONFIG_H3600_HAL
-	h3600_audio_mute(1);
-#else	
 	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-#endif
 	
 	/* disable the audio power and all signals leading to the audio chip */
 	l3_close(sa11xx_uda1341->uda1341);
@@ -354,13 +331,9 @@
 
 	/* power off and mute off */
 	/* FIXME - is muting off necesary??? */
-#ifdef CONFIG_H3600_HAL
-	h3600_audio_power(0);
-	h3600_audio_mute(0);
-#else	
+
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
 	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-#endif	
 }
 
 /* }}} */
diff -ruN linux-2.6.22/sound/core/Makefile linux-2.6.22-alsa/sound/core/Makefile
--- linux-2.6.22/sound/core/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -3,18 +3,15 @@
 # Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
-snd-objs     := sound.o init.o memory.o info.o control.o misc.o device.o
-ifeq ($(CONFIG_ISA_DMA_API),y)
-snd-objs     += isadma.o
-endif
-ifeq ($(CONFIG_SND_OSSEMUL),y)
-snd-objs     += sound_oss.o info_oss.o
-endif
+snd-y     := sound.o init.o memory.o info.o control.o misc.o device.o
+snd-$(CONFIG_ISA_DMA_API) += isadma.o
+snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
 
 snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
 		pcm_memory.o
 
-snd-page-alloc-objs := memalloc.o sgbuf.o
+snd-page-alloc-y := memalloc.o
+snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
 
 snd-rawmidi-objs  := rawmidi.o
 snd-timer-objs    := timer.o
diff -ruN linux-2.6.22/sound/core/control.c linux-2.6.22-alsa/sound/core/control.c
--- linux-2.6.22/sound/core/control.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/control.c	2007-09-01 20:55:22.000000000 +0200
@@ -1486,3 +1486,30 @@
 	snd_assert(card != NULL, return -ENXIO);
 	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
 }
+
+/*
+ * Frequently used control callbacks
+ */
+int snd_ctl_boolean_mono_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;
+}
+
+EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
+
+int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
diff -ruN linux-2.6.22/sound/core/memalloc.c linux-2.6.22-alsa/sound/core/memalloc.c
--- linux-2.6.22/sound/core/memalloc.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/memalloc.c	2007-09-01 20:55:22.000000000 +0200
@@ -205,6 +205,7 @@
  *
  */
 
+#ifdef CONFIG_HAS_DMA
 /* allocate the coherent DMA pages */
 static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
 {
@@ -238,6 +239,7 @@
 	dec_snd_pages(pg);
 	dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
 }
+#endif /* CONFIG_HAS_DMA */
 
 #ifdef CONFIG_SBUS
 
@@ -311,12 +313,14 @@
 		dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
 		break;
 #endif
+#ifdef CONFIG_HAS_DMA
 	case SNDRV_DMA_TYPE_DEV:
 		dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
 		break;
 	case SNDRV_DMA_TYPE_DEV_SG:
 		snd_malloc_sgbuf_pages(device, size, dmab, NULL);
 		break;
+#endif
 	default:
 		printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
 		dmab->area = NULL;
@@ -382,12 +386,14 @@
 		snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
 		break;
 #endif
+#ifdef CONFIG_HAS_DMA
 	case SNDRV_DMA_TYPE_DEV:
 		snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
 		break;
 	case SNDRV_DMA_TYPE_DEV_SG:
 		snd_free_sgbuf_pages(dmab);
 		break;
+#endif
 	default:
 		printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
 	}
diff -ruN linux-2.6.22/sound/core/oss/Makefile linux-2.6.22-alsa/sound/core/oss/Makefile
--- linux-2.6.22/sound/core/oss/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -5,8 +5,9 @@
 
 snd-mixer-oss-objs := mixer_oss.o
 
-snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
-		    io.o copy.o linear.o mulaw.o route.o rate.o
+snd-pcm-oss-y := pcm_oss.o
+snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \
+	io.o copy.o linear.o mulaw.o route.o rate.o
 
 obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
 obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o
diff -ruN linux-2.6.22/sound/core/oss/copy.c linux-2.6.22-alsa/sound/core/oss/copy.c
--- linux-2.6.22/sound/core/oss/copy.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/copy.c	2007-09-01 20:55:22.000000000 +0200
@@ -20,9 +20,6 @@
  */
 
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -88,5 +85,3 @@
 	*r_plugin = plugin;
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/oss/io.c linux-2.6.22-alsa/sound/core/oss/io.c
--- linux-2.6.22/sound/core/oss/io.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/io.c	2007-09-01 20:55:22.000000000 +0200
@@ -20,9 +20,6 @@
  */
   
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -135,5 +132,3 @@
 	*r_plugin = plugin;
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/oss/linear.c linux-2.6.22-alsa/sound/core/oss/linear.c
--- linux-2.6.22/sound/core/oss/linear.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/linear.c	2007-09-01 20:55:22.000000000 +0200
@@ -21,9 +21,6 @@
  */
 
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -34,19 +31,34 @@
  */
  
 struct linear_priv {
-	int conv;
+	int cvt_endian;		/* need endian conversion? */
+	unsigned int src_ofs;	/* byte offset in source format */
+	unsigned int dst_ofs;	/* byte soffset in destination format */
+	unsigned int copy_ofs;	/* byte offset in temporary u32 data */
+	unsigned int dst_bytes;		/* byte size of destination format */
+	unsigned int copy_bytes;	/* bytes to copy per conversion */
+	unsigned int flip; /* MSB flip for signeness, done after endian conv */
 };
 
+static inline void do_convert(struct linear_priv *data,
+			      unsigned char *dst, unsigned char *src)
+{
+	unsigned int tmp = 0;
+	unsigned char *p = (unsigned char *)&tmp;
+
+	memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes);
+	if (data->cvt_endian)
+		tmp = swab32(tmp);
+	tmp ^= data->flip;
+	memcpy(dst, p + data->dst_ofs, data->dst_bytes);
+}
+
 static void convert(struct snd_pcm_plugin *plugin,
 		    const struct snd_pcm_plugin_channel *src_channels,
 		    struct snd_pcm_plugin_channel *dst_channels,
 		    snd_pcm_uframes_t frames)
 {
-#define CONV_LABELS
-#include "plugin_ops.h"
-#undef CONV_LABELS
 	struct linear_priv *data = (struct linear_priv *)plugin->extra_data;
-	void *conv = conv_labels[data->conv];
 	int channel;
 	int nchannels = plugin->src_format.channels;
 	for (channel = 0; channel < nchannels; ++channel) {
@@ -67,11 +79,7 @@
 		dst_step = dst_channels[channel].area.step / 8;
 		frames1 = frames;
 		while (frames1-- > 0) {
-			goto *conv;
-#define CONV_END after
-#include "plugin_ops.h"
-#undef CONV_END
-		after:
+			do_convert(data, dst, src);
 			src += src_step;
 			dst += dst_step;
 		}
@@ -106,29 +114,36 @@
 	return frames;
 }
 
-static int conv_index(int src_format, int dst_format)
+static void init_data(struct linear_priv *data, int src_format, int dst_format)
 {
-	int src_endian, dst_endian, sign, src_width, dst_width;
+	int src_le, dst_le, src_bytes, dst_bytes;
 
-	sign = (snd_pcm_format_signed(src_format) !=
-		snd_pcm_format_signed(dst_format));
-#ifdef SNDRV_LITTLE_ENDIAN
-	src_endian = snd_pcm_format_big_endian(src_format);
-	dst_endian = snd_pcm_format_big_endian(dst_format);
-#else
-	src_endian = snd_pcm_format_little_endian(src_format);
-	dst_endian = snd_pcm_format_little_endian(dst_format);
-#endif
-
-	if (src_endian < 0)
-		src_endian = 0;
-	if (dst_endian < 0)
-		dst_endian = 0;
-
-	src_width = snd_pcm_format_width(src_format) / 8 - 1;
-	dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
-
-	return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
+	src_bytes = snd_pcm_format_width(src_format) / 8;
+	dst_bytes = snd_pcm_format_width(dst_format) / 8;
+	src_le = snd_pcm_format_little_endian(src_format) > 0;
+	dst_le = snd_pcm_format_little_endian(dst_format) > 0;
+
+	data->dst_bytes = dst_bytes;
+	data->cvt_endian = src_le != dst_le;
+	data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes;
+	if (src_le) {
+		data->copy_ofs = 4 - data->copy_bytes;
+		data->src_ofs = src_bytes - data->copy_bytes;
+	} else
+		data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 -
+			src_bytes;
+	if (dst_le)
+		data->dst_ofs = 4 - data->dst_bytes;
+	else
+		data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 -
+			dst_bytes;
+	if (snd_pcm_format_signed(src_format) !=
+	    snd_pcm_format_signed(dst_format)) {
+		if (dst_le)
+			data->flip = cpu_to_le32(0x80000000);
+		else
+			data->flip = cpu_to_be32(0x80000000);
+	}
 }
 
 int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
@@ -154,10 +169,8 @@
 	if (err < 0)
 		return err;
 	data = (struct linear_priv *)plugin->extra_data;
-	data->conv = conv_index(src_format->format, dst_format->format);
+	init_data(data, src_format->format, dst_format->format);
 	plugin->transfer = linear_transfer;
 	*r_plugin = plugin;
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/oss/mulaw.c linux-2.6.22-alsa/sound/core/oss/mulaw.c
--- linux-2.6.22/sound/core/oss/mulaw.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/mulaw.c	2007-09-01 20:55:22.000000000 +0200
@@ -22,9 +22,6 @@
  */
   
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -149,19 +146,32 @@
 
 struct mulaw_priv {
 	mulaw_f func;
-	int conv;
+	int cvt_endian;			/* need endian conversion? */
+	unsigned int native_ofs;	/* byte offset in native format */
+	unsigned int copy_ofs;		/* byte offset in s16 format */
+	unsigned int native_bytes;	/* byte size of the native format */
+	unsigned int copy_bytes;	/* bytes to copy per conversion */
+	u16 flip; /* MSB flip for signedness, done after endian conversion */
 };
 
+static inline void cvt_s16_to_native(struct mulaw_priv *data,
+				     unsigned char *dst, u16 sample)
+{
+	sample ^= data->flip;
+	if (data->cvt_endian)
+		sample = swab16(sample);
+	if (data->native_bytes > data->copy_bytes)
+		memset(dst, 0, data->native_bytes);
+	memcpy(dst + data->native_ofs, (char *)&sample + data->copy_ofs,
+	       data->copy_bytes);
+}
+
 static void mulaw_decode(struct snd_pcm_plugin *plugin,
 			const struct snd_pcm_plugin_channel *src_channels,
 			struct snd_pcm_plugin_channel *dst_channels,
 			snd_pcm_uframes_t frames)
 {
-#define PUT_S16_LABELS
-#include "plugin_ops.h"
-#undef PUT_S16_LABELS
 	struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
-	void *put = put_s16_labels[data->conv];
 	int channel;
 	int nchannels = plugin->src_format.channels;
 	for (channel = 0; channel < nchannels; ++channel) {
@@ -183,30 +193,33 @@
 		frames1 = frames;
 		while (frames1-- > 0) {
 			signed short sample = ulaw2linear(*src);
-			goto *put;
-#define PUT_S16_END after
-#include "plugin_ops.h"
-#undef PUT_S16_END
-		after:
+			cvt_s16_to_native(data, dst, sample);
 			src += src_step;
 			dst += dst_step;
 		}
 	}
 }
 
+static inline signed short cvt_native_to_s16(struct mulaw_priv *data,
+					     unsigned char *src)
+{
+	u16 sample = 0;
+	memcpy((char *)&sample + data->copy_ofs, src + data->native_ofs,
+	       data->copy_bytes);
+	if (data->cvt_endian)
+		sample = swab16(sample);
+	sample ^= data->flip;
+	return (signed short)sample;
+}
+
 static void mulaw_encode(struct snd_pcm_plugin *plugin,
 			const struct snd_pcm_plugin_channel *src_channels,
 			struct snd_pcm_plugin_channel *dst_channels,
 			snd_pcm_uframes_t frames)
 {
-#define GET_S16_LABELS
-#include "plugin_ops.h"
-#undef GET_S16_LABELS
 	struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
-	void *get = get_s16_labels[data->conv];
 	int channel;
 	int nchannels = plugin->src_format.channels;
-	signed short sample = 0;
 	for (channel = 0; channel < nchannels; ++channel) {
 		char *src;
 		char *dst;
@@ -225,11 +238,7 @@
 		dst_step = dst_channels[channel].area.step / 8;
 		frames1 = frames;
 		while (frames1-- > 0) {
-			goto *get;
-#define GET_S16_END after
-#include "plugin_ops.h"
-#undef GET_S16_END
-		after:
+			signed short sample = cvt_native_to_s16(data, src);
 			*dst = linear2ulaw(sample);
 			src += src_step;
 			dst += dst_step;
@@ -265,23 +274,25 @@
 	return frames;
 }
 
-static int getput_index(int format)
+static void init_data(struct mulaw_priv *data, int format)
 {
-	int sign, width, endian;
-	sign = !snd_pcm_format_signed(format);
-	width = snd_pcm_format_width(format) / 8 - 1;
-	if (width < 0 || width > 3) {
-		snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format);
-		width = 0;
-	}
 #ifdef SNDRV_LITTLE_ENDIAN
-	endian = snd_pcm_format_big_endian(format);
+	data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
 #else
-	endian = snd_pcm_format_little_endian(format);
+	data->cvt_endian = snd_pcm_format_little_endian(format) > 0;
 #endif
-	if (endian < 0)
-		endian = 0;
-	return width * 4 + endian * 2 + sign;
+	if (!snd_pcm_format_signed(format))
+		data->flip = 0x8000;
+	data->native_bytes = snd_pcm_format_physical_width(format) / 8;
+	data->copy_bytes = data->native_bytes < 2 ? 1 : 2;
+	if (snd_pcm_format_little_endian(format)) {
+		data->native_ofs = data->native_bytes - data->copy_bytes;
+		data->copy_ofs = 2 - data->copy_bytes;
+	} else {
+		/* S24 in 4bytes need an 1 byte offset */
+		data->native_ofs = data->native_bytes -
+			snd_pcm_format_width(format) / 8;
+	}
 }
 
 int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
@@ -322,11 +333,8 @@
 		return err;
 	data = (struct mulaw_priv *)plugin->extra_data;
 	data->func = func;
-	data->conv = getput_index(format->format);
-	snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL);
+	init_data(data, format->format);
 	plugin->transfer = mulaw_transfer;
 	*r_plugin = plugin;
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/oss/pcm_oss.c linux-2.6.22-alsa/sound/core/oss/pcm_oss.c
--- linux-2.6.22/sound/core/oss/pcm_oss.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/pcm_oss.c	2007-09-01 20:55:22.000000000 +0200
@@ -633,6 +633,22 @@
 	return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
 }
 
+/* define extended formats in the recent OSS versions (if any) */
+/* linear formats */
+#define AFMT_S32_LE      0x00001000
+#define AFMT_S32_BE      0x00002000
+#define AFMT_S24_LE      0x00008000
+#define AFMT_S24_BE      0x00010000
+#define AFMT_S24_PACKED  0x00040000
+
+/* other supported formats */
+#define AFMT_FLOAT       0x00004000
+#define AFMT_SPDIF_RAW   0x00020000
+
+/* unsupported formats */
+#define AFMT_AC3         0x00000400
+#define AFMT_VORBIS      0x00000800
+
 static int snd_pcm_oss_format_from(int format)
 {
 	switch (format) {
@@ -646,6 +662,13 @@
 	case AFMT_U16_LE:	return SNDRV_PCM_FORMAT_U16_LE;
 	case AFMT_U16_BE:	return SNDRV_PCM_FORMAT_U16_BE;
 	case AFMT_MPEG:		return SNDRV_PCM_FORMAT_MPEG;
+	case AFMT_S32_LE:	return SNDRV_PCM_FORMAT_S32_LE;
+	case AFMT_S32_BE:	return SNDRV_PCM_FORMAT_S32_BE;
+	case AFMT_S24_LE:	return SNDRV_PCM_FORMAT_S24_LE;
+	case AFMT_S24_BE:	return SNDRV_PCM_FORMAT_S24_BE;
+	case AFMT_S24_PACKED:	return SNDRV_PCM_FORMAT_S24_3LE;
+	case AFMT_FLOAT:	return SNDRV_PCM_FORMAT_FLOAT;
+	case AFMT_SPDIF_RAW:	return SNDRV_PCM_FORMAT_IEC958_SUBFRAME;
 	default:		return SNDRV_PCM_FORMAT_U8;
 	}
 }
@@ -663,6 +686,13 @@
 	case SNDRV_PCM_FORMAT_U16_LE:	return AFMT_U16_LE;
 	case SNDRV_PCM_FORMAT_U16_BE:	return AFMT_U16_BE;
 	case SNDRV_PCM_FORMAT_MPEG:		return AFMT_MPEG;
+	case SNDRV_PCM_FORMAT_S32_LE:	return AFMT_S32_LE;
+	case SNDRV_PCM_FORMAT_S32_BE:	return AFMT_S32_BE;
+	case SNDRV_PCM_FORMAT_S24_LE:	return AFMT_S24_LE;
+	case SNDRV_PCM_FORMAT_S24_BE:	return AFMT_S24_BE;
+	case SNDRV_PCM_FORMAT_S24_3LE:	return AFMT_S24_PACKED;
+	case SNDRV_PCM_FORMAT_FLOAT:	return AFMT_FLOAT;
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME: return AFMT_SPDIF_RAW;
 	default:			return -EINVAL;
 	}
 }
@@ -1725,7 +1755,10 @@
 		return AFMT_MU_LAW | AFMT_U8 |
 		       AFMT_S16_LE | AFMT_S16_BE |
 		       AFMT_S8 | AFMT_U16_LE |
-		       AFMT_U16_BE;
+		       AFMT_U16_BE |
+			AFMT_S32_LE | AFMT_S32_BE |
+			AFMT_S24_LE | AFMT_S24_LE |
+			AFMT_S24_PACKED;
 	params = kmalloc(sizeof(*params), GFP_KERNEL);
 	if (!params)
 		return -ENOMEM;
diff -ruN linux-2.6.22/sound/core/oss/pcm_plugin.c linux-2.6.22-alsa/sound/core/oss/pcm_plugin.c
--- linux-2.6.22/sound/core/oss/pcm_plugin.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/pcm_plugin.c	2007-09-01 20:55:22.000000000 +0200
@@ -25,9 +25,6 @@
 #endif
 
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/vmalloc.h>
@@ -267,6 +264,8 @@
 		       SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
 		       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
 		       SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
+		       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
+		       SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
 		       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
 		       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
 	snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
@@ -283,6 +282,10 @@
 	SNDRV_PCM_FORMAT_S16_BE,
 	SNDRV_PCM_FORMAT_U16_LE,
 	SNDRV_PCM_FORMAT_U16_BE,
+	SNDRV_PCM_FORMAT_S24_3LE,
+	SNDRV_PCM_FORMAT_S24_3BE,
+	SNDRV_PCM_FORMAT_U24_3LE,
+	SNDRV_PCM_FORMAT_U24_3BE,
 	SNDRV_PCM_FORMAT_S24_LE,
 	SNDRV_PCM_FORMAT_S24_BE,
 	SNDRV_PCM_FORMAT_U24_LE,
@@ -297,41 +300,37 @@
 
 int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
 {
+	int i;
+
 	if (snd_mask_test(format_mask, format))
 		return format;
 	if (! snd_pcm_plug_formats(format_mask, format))
 		return -EINVAL;
 	if (snd_pcm_format_linear(format)) {
-		int width = snd_pcm_format_width(format);
-		int unsignd = snd_pcm_format_unsigned(format);
-		int big = snd_pcm_format_big_endian(format);
-		int format1;
-		int wid, width1=width;
-		int dwidth1 = 8;
-		for (wid = 0; wid < 4; ++wid) {
-			int end, big1 = big;
-			for (end = 0; end < 2; ++end) {
-				int sgn, unsignd1 = unsignd;
-				for (sgn = 0; sgn < 2; ++sgn) {
-					format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
-					if (format1 >= 0 &&
-					    snd_mask_test(format_mask, format1))
-						goto _found;
-					unsignd1 = !unsignd1;
-				}
-				big1 = !big1;
-			}
-			if (width1 == 32) {
-				dwidth1 = -dwidth1;
-				width1 = width;
+		unsigned int width = snd_pcm_format_width(format);
+		int unsignd = snd_pcm_format_unsigned(format) > 0;
+		int big = snd_pcm_format_big_endian(format) > 0;
+		unsigned int badness, best = -1;
+		int best_format = -1;
+		for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
+			int f = preferred_formats[i];
+			unsigned int w;
+			if (!snd_mask_test(format_mask, f))
+				continue;
+			w = snd_pcm_format_width(f);
+			if (w >= width)
+				badness = w - width;
+			else
+				badness = width - w + 32;
+			badness += snd_pcm_format_unsigned(f) != unsignd;
+			badness += snd_pcm_format_big_endian(f) != big;
+			if (badness < best) {
+				best_format = f;
+				best = badness;
 			}
-			width1 += dwidth1;
 		}
-		return -EINVAL;
-	_found:
-		return format1;
+		return best_format >= 0 ? best_format : -EINVAL;
 	} else {
-		unsigned int i;
 		switch (format) {
 		case SNDRV_PCM_FORMAT_MU_LAW:
 			for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
@@ -740,5 +739,3 @@
 	}
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/oss/rate.c linux-2.6.22-alsa/sound/core/oss/rate.c
--- linux-2.6.22/sound/core/oss/rate.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/rate.c	2007-09-01 20:55:22.000000000 +0200
@@ -20,9 +20,6 @@
  */
   
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -340,5 +337,3 @@
 	*r_plugin = plugin;
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/oss/route.c linux-2.6.22-alsa/sound/core/oss/route.c
--- linux-2.6.22/sound/core/oss/route.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/oss/route.c	2007-09-01 20:55:22.000000000 +0200
@@ -20,9 +20,6 @@
  */
 
 #include <sound/driver.h>
-
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
-
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -108,5 +105,3 @@
 	*r_plugin = plugin;
 	return 0;
 }
-
-#endif
diff -ruN linux-2.6.22/sound/core/pcm_misc.c linux-2.6.22-alsa/sound/core/pcm_misc.c
--- linux-2.6.22/sound/core/pcm_misc.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/pcm_misc.c	2007-09-01 20:55:22.000000000 +0200
@@ -422,38 +422,6 @@
 
 EXPORT_SYMBOL(snd_pcm_format_set_silence);
 
-/* [width][unsigned][bigendian] */
-static int linear_formats[4][2][2] = {
-	{{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8},
-	 { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}},
-	{{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE},
-	 {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}},
-	{{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE},
-	 {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}},
-	{{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE},
-	 {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}}
-};
-
-/**
- * snd_pcm_build_linear_format - return the suitable linear format for the given condition
- * @width: the bit-width
- * @unsignd: 1 if unsigned, 0 if signed.
- * @big_endian: 1 if big-endian, 0 if little-endian
- *
- * Returns the suitable linear format for the given condition.
- */
-snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian)
-{
-	if (width & 7)
-		return SND_PCM_FORMAT_UNKNOWN;
-	width = (width / 8) - 1;
-	if (width < 0 || width >= 4)
-		return SND_PCM_FORMAT_UNKNOWN;
-	return linear_formats[width][!!unsignd][!!big_endian];
-}
-
-EXPORT_SYMBOL(snd_pcm_build_linear_format);
-
 /**
  * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
  * @runtime: the runtime instance
@@ -465,21 +433,16 @@
  */
 int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
 {
-	static unsigned rates[] = {
-		/* ATTENTION: these values depend on the definition in pcm.h! */
-		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
-		64000, 88200, 96000, 176400, 192000
-	};
 	int i;
-	for (i = 0; i < (int)ARRAY_SIZE(rates); i++) {
+	for (i = 0; i < (int)snd_pcm_known_rates.count; i++) {
 		if (runtime->hw.rates & (1 << i)) {
-			runtime->hw.rate_min = rates[i];
+			runtime->hw.rate_min = snd_pcm_known_rates.list[i];
 			break;
 		}
 	}
-	for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) {
+	for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) {
 		if (runtime->hw.rates & (1 << i)) {
-			runtime->hw.rate_max = rates[i];
+			runtime->hw.rate_max = snd_pcm_known_rates.list[i];
 			break;
 		}
 	}
@@ -487,3 +450,21 @@
 }
 
 EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
+
+/**
+ * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit
+ * @rate: the sample rate to convert
+ *
+ * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
+ * SNDRV_PCM_RATE_KNOT for an unknown rate.
+ */
+unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate)
+{
+	unsigned int i;
+
+	for (i = 0; i < snd_pcm_known_rates.count; i++)
+		if (snd_pcm_known_rates.list[i] == rate)
+			return 1u << i;
+	return SNDRV_PCM_RATE_KNOT;
+}
+EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit);
diff -ruN linux-2.6.22/sound/core/pcm_native.c linux-2.6.22-alsa/sound/core/pcm_native.c
--- linux-2.6.22/sound/core/pcm_native.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/pcm_native.c	2007-09-01 20:55:22.000000000 +0200
@@ -1487,7 +1487,7 @@
 
 	snd_pcm_stream_lock_irq(substream);
 	/* resume pause */
-	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
 		snd_pcm_pause(substream, 0);
 
 	/* pre-start/stop - all running streams are changed to DRAINING state */
@@ -1787,12 +1787,18 @@
 static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
                                  48000, 64000, 88200, 96000, 176400, 192000 };
 
+const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list = rates,
+};
+
 static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params,
 				struct snd_pcm_hw_rule *rule)
 {
 	struct snd_pcm_hardware *hw = rule->private;
 	return snd_interval_list(hw_param_interval(params, rule->var),
-				 ARRAY_SIZE(rates), rates, hw->rates);
+				 snd_pcm_known_rates.count,
+				 snd_pcm_known_rates.list, hw->rates);
 }		
 
 static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
diff -ruN linux-2.6.22/sound/core/rawmidi.c linux-2.6.22-alsa/sound/core/rawmidi.c
--- linux-2.6.22/sound/core/rawmidi.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/rawmidi.c	2007-09-01 20:55:22.000000000 +0200
@@ -30,7 +30,6 @@
 #include <linux/mutex.h>
 #include <linux/moduleparam.h>
 #include <linux/delay.h>
-#include <linux/wait.h>
 #include <sound/rawmidi.h>
 #include <sound/info.h>
 #include <sound/control.h>
diff -ruN linux-2.6.22/sound/core/seq/oss/seq_oss_init.c linux-2.6.22-alsa/sound/core/seq/oss/seq_oss_init.c
--- linux-2.6.22/sound/core/seq/oss/seq_oss_init.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/oss/seq_oss_init.c	2007-09-01 20:55:22.000000000 +0200
@@ -176,29 +176,29 @@
 	int i, rc;
 	struct seq_oss_devinfo *dp;
 
-	if ((dp = kzalloc(sizeof(*dp), GFP_KERNEL)) == NULL) {
+	dp = kzalloc(sizeof(*dp), GFP_KERNEL);
+	if (!dp) {
 		snd_printk(KERN_ERR "can't malloc device info\n");
 		return -ENOMEM;
 	}
 	debug_printk(("oss_open: dp = %p\n", dp));
 
+	dp->cseq = system_client;
+	dp->port = -1;
+	dp->queue = -1;
+
 	for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
 		if (client_table[i] == NULL)
 			break;
 	}
+
+	dp->index = i;
 	if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
 		snd_printk(KERN_ERR "too many applications\n");
-		kfree(dp);
-		return -ENOMEM;
+		rc = -ENOMEM;
+		goto _error;
 	}
 
-	dp->index = i;
-	dp->cseq = system_client;
-	dp->port = -1;
-	dp->queue = -1;
-	dp->readq = NULL;
-	dp->writeq = NULL;
-
 	/* look up synth and midi devices */
 	snd_seq_oss_synth_setup(dp);
 	snd_seq_oss_midi_setup(dp);
@@ -211,14 +211,16 @@
 
 	/* create port */
 	debug_printk(("create new port\n"));
-	if ((rc = create_port(dp)) < 0) {
+	rc = create_port(dp);
+	if (rc < 0) {
 		snd_printk(KERN_ERR "can't create port\n");
 		goto _error;
 	}
 
 	/* allocate queue */
 	debug_printk(("allocate queue\n"));
-	if ((rc = alloc_seq_queue(dp)) < 0)
+	rc = alloc_seq_queue(dp);
+	if (rc < 0)
 		goto _error;
 
 	/* set address */
@@ -235,7 +237,8 @@
 	/* initialize read queue */
 	debug_printk(("initialize read queue\n"));
 	if (is_read_mode(dp->file_mode)) {
-		if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
+		dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
+		if (!dp->readq) {
 			rc = -ENOMEM;
 			goto _error;
 		}
@@ -245,7 +248,7 @@
 	debug_printk(("initialize write queue\n"));
 	if (is_write_mode(dp->file_mode)) {
 		dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
-		if (dp->writeq == NULL) {
+		if (!dp->writeq) {
 			rc = -ENOMEM;
 			goto _error;
 		}
@@ -253,7 +256,8 @@
 
 	/* initialize timer */
 	debug_printk(("initialize timer\n"));
-	if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
+	dp->timer = snd_seq_oss_timer_new(dp);
+	if (!dp->timer) {
 		snd_printk(KERN_ERR "can't alloc timer\n");
 		rc = -ENOMEM;
 		goto _error;
@@ -276,11 +280,13 @@
 	return 0;
 
  _error:
+	snd_seq_oss_writeq_delete(dp->writeq);
+	snd_seq_oss_readq_delete(dp->readq);
 	snd_seq_oss_synth_cleanup(dp);
 	snd_seq_oss_midi_cleanup(dp);
-	i = dp->queue;
 	delete_port(dp);
-	delete_seq_queue(i);
+	delete_seq_queue(dp->queue);
+	kfree(dp);
 
 	return rc;
 }
diff -ruN linux-2.6.22/sound/core/seq/oss/seq_oss_writeq.c linux-2.6.22-alsa/sound/core/seq/oss/seq_oss_writeq.c
--- linux-2.6.22/sound/core/seq/oss/seq_oss_writeq.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/oss/seq_oss_writeq.c	2007-09-01 20:55:22.000000000 +0200
@@ -63,8 +63,10 @@
 void
 snd_seq_oss_writeq_delete(struct seq_oss_writeq *q)
 {
-	snd_seq_oss_writeq_clear(q);	/* to be sure */
-	kfree(q);
+	if (q) {
+		snd_seq_oss_writeq_clear(q);	/* to be sure */
+		kfree(q);
+	}
 }
 
 
diff -ruN linux-2.6.22/sound/core/seq/seq_instr.c linux-2.6.22-alsa/sound/core/seq/seq_instr.c
--- linux-2.6.22/sound/core/seq/seq_instr.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/seq_instr.c	2007-09-01 20:55:22.000000000 +0200
@@ -109,7 +109,7 @@
 			spin_lock_irqsave(&list->lock, flags);
 			while (instr->use) {
 				spin_unlock_irqrestore(&list->lock, flags);
-				schedule_timeout_interruptible(1);
+				schedule_timeout(1);
 				spin_lock_irqsave(&list->lock, flags);
 			}				
 			spin_unlock_irqrestore(&list->lock, flags);
@@ -199,7 +199,7 @@
 			instr = flist;
 			flist = instr->next;
 			while (instr->use)
-				schedule_timeout_interruptible(1);
+				schedule_timeout(1);
 			if (snd_seq_instr_free(instr, atomic)<0)
 				snd_printk(KERN_WARNING "instrument free problem\n");
 			instr = next;
@@ -555,7 +555,7 @@
 					   SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
 		while (instr->use) {
 			spin_unlock_irqrestore(&list->lock, flags);
-			schedule_timeout_interruptible(1);
+			schedule_timeout(1);
 			spin_lock_irqsave(&list->lock, flags);
 		}				
 		spin_unlock_irqrestore(&list->lock, flags);
diff -ruN linux-2.6.22/sound/core/seq/seq_midi_event.c linux-2.6.22-alsa/sound/core/seq/seq_midi_event.c
--- linux-2.6.22/sound/core/seq/seq_midi_event.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/seq_midi_event.c	2007-09-01 20:55:22.000000000 +0200
@@ -32,10 +32,9 @@
 MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
 MODULE_LICENSE("GPL");
 
-/* queue type */
-/* from 0 to 7 are normal commands (note off, on, etc.) */
-#define ST_NOTEOFF	0
-#define ST_NOTEON	1
+/* event type, index into status_event[] */
+/* from 0 to 6 are normal commands (note off, on, etc.) for 0x9?-0xe? */
+#define ST_INVALID	7
 #define ST_SPECIAL	8
 #define ST_SYSEX	ST_SPECIAL
 /* from 8 to 15 are events for 0xf0-0xf7 */
@@ -65,32 +64,33 @@
 	void (*encode)(struct snd_midi_event *dev, struct snd_seq_event *ev);
 	void (*decode)(struct snd_seq_event *ev, unsigned char *buf);
 } status_event[] = {
-	/* 0x80 - 0xf0 */
-	{SNDRV_SEQ_EVENT_NOTEOFF,	2, note_event, note_decode},
-	{SNDRV_SEQ_EVENT_NOTEON,	2, note_event, note_decode},
-	{SNDRV_SEQ_EVENT_KEYPRESS,	2, note_event, note_decode},
-	{SNDRV_SEQ_EVENT_CONTROLLER,	2, two_param_ctrl_event, two_param_decode},
-	{SNDRV_SEQ_EVENT_PGMCHANGE,	1, one_param_ctrl_event, one_param_decode},
-	{SNDRV_SEQ_EVENT_CHANPRESS,	1, one_param_ctrl_event, one_param_decode},
-	{SNDRV_SEQ_EVENT_PITCHBEND,	2, pitchbend_ctrl_event, pitchbend_decode},
-	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf0 */
+	/* 0x80 - 0xef */
+	{SNDRV_SEQ_EVENT_NOTEOFF,	 2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_NOTEON,	 2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_KEYPRESS,	 2, note_event, note_decode},
+	{SNDRV_SEQ_EVENT_CONTROLLER,	 2, two_param_ctrl_event, two_param_decode},
+	{SNDRV_SEQ_EVENT_PGMCHANGE,	 1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_CHANPRESS,	 1, one_param_ctrl_event, one_param_decode},
+	{SNDRV_SEQ_EVENT_PITCHBEND,	 2, pitchbend_ctrl_event, pitchbend_decode},
+	/* invalid */
+	{SNDRV_SEQ_EVENT_NONE,		-1, NULL, NULL},
 	/* 0xf0 - 0xff */
-	{SNDRV_SEQ_EVENT_SYSEX,		1, NULL, NULL}, /* sysex: 0xf0 */
-	{SNDRV_SEQ_EVENT_QFRAME,	1, one_param_event, one_param_decode}, /* 0xf1 */
-	{SNDRV_SEQ_EVENT_SONGPOS,	2, songpos_event, songpos_decode}, /* 0xf2 */
-	{SNDRV_SEQ_EVENT_SONGSEL,	1, one_param_event, one_param_decode}, /* 0xf3 */
-	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf4 */
-	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf5 */
-	{SNDRV_SEQ_EVENT_TUNE_REQUEST,	0, NULL, NULL},	/* 0xf6 */
-	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf7 */
-	{SNDRV_SEQ_EVENT_CLOCK,		0, NULL, NULL}, /* 0xf8 */
-	{SNDRV_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf9 */
-	{SNDRV_SEQ_EVENT_START,		0, NULL, NULL}, /* 0xfa */
-	{SNDRV_SEQ_EVENT_CONTINUE,	0, NULL, NULL}, /* 0xfb */
-	{SNDRV_SEQ_EVENT_STOP, 		0, NULL, NULL}, /* 0xfc */
-	{SNDRV_SEQ_EVENT_NONE, 		0, NULL, NULL}, /* 0xfd */
-	{SNDRV_SEQ_EVENT_SENSING, 	0, NULL, NULL}, /* 0xfe */
-	{SNDRV_SEQ_EVENT_RESET, 	0, NULL, NULL}, /* 0xff */
+	{SNDRV_SEQ_EVENT_SYSEX,		 1, NULL, NULL}, /* sysex: 0xf0 */
+	{SNDRV_SEQ_EVENT_QFRAME,	 1, one_param_event, one_param_decode}, /* 0xf1 */
+	{SNDRV_SEQ_EVENT_SONGPOS,	 2, songpos_event, songpos_decode}, /* 0xf2 */
+	{SNDRV_SEQ_EVENT_SONGSEL,	 1, one_param_event, one_param_decode}, /* 0xf3 */
+	{SNDRV_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf4 */
+	{SNDRV_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf5 */
+	{SNDRV_SEQ_EVENT_TUNE_REQUEST,	 0, NULL, NULL}, /* 0xf6 */
+	{SNDRV_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf7 */
+	{SNDRV_SEQ_EVENT_CLOCK,		 0, NULL, NULL}, /* 0xf8 */
+	{SNDRV_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf9 */
+	{SNDRV_SEQ_EVENT_START,		 0, NULL, NULL}, /* 0xfa */
+	{SNDRV_SEQ_EVENT_CONTINUE,	 0, NULL, NULL}, /* 0xfb */
+	{SNDRV_SEQ_EVENT_STOP, 		 0, NULL, NULL}, /* 0xfc */
+	{SNDRV_SEQ_EVENT_NONE, 		-1, NULL, NULL}, /* 0xfd */
+	{SNDRV_SEQ_EVENT_SENSING, 	 0, NULL, NULL}, /* 0xfe */
+	{SNDRV_SEQ_EVENT_RESET, 	 0, NULL, NULL}, /* 0xff */
 };
 
 static int extra_decode_ctrl14(struct snd_midi_event *dev, unsigned char *buf, int len,
@@ -129,6 +129,7 @@
 	}
 	dev->bufsize = bufsize;
 	dev->lastcmd = 0xff;
+	dev->type = ST_INVALID;
 	spin_lock_init(&dev->lock);
 	*rdev = dev;
 	return 0;
@@ -149,7 +150,7 @@
 {
 	dev->read = 0;
 	dev->qlen = 0;
-	dev->type = 0;
+	dev->type = ST_INVALID;
 }
 
 void snd_midi_event_reset_encode(struct snd_midi_event *dev)
@@ -251,29 +252,31 @@
 		ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
 		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
 		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
-		return 1;
+		return ev->type != SNDRV_SEQ_EVENT_NONE;
 	}
 
 	spin_lock_irqsave(&dev->lock, flags);
-	if (dev->qlen > 0) {
-		/* rest of command */
-		dev->buf[dev->read++] = c;
-		if (dev->type != ST_SYSEX)
-			dev->qlen--;
-	} else {
+	if ((c & 0x80) &&
+	    (c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) {
 		/* new command */
+		dev->buf[0] = c;
+		if ((c & 0xf0) == 0xf0) /* system messages */
+			dev->type = (c & 0x0f) + ST_SPECIAL;
+		else
+			dev->type = (c >> 4) & 0x07;
 		dev->read = 1;
-		if (c & 0x80) {
-			dev->buf[0] = c;
-			if ((c & 0xf0) == 0xf0) /* special events */
-				dev->type = (c & 0x0f) + ST_SPECIAL;
-			else
-				dev->type = (c >> 4) & 0x07;
-			dev->qlen = status_event[dev->type].qlen;
-		} else {
-			/* process this byte as argument */
+		dev->qlen = status_event[dev->type].qlen;
+	} else {
+		if (dev->qlen > 0) {
+			/* rest of command */
 			dev->buf[dev->read++] = c;
+			if (dev->type != ST_SYSEX)
+				dev->qlen--;
+		} else {
+			/* running status */
+			dev->buf[1] = c;
 			dev->qlen = status_event[dev->type].qlen - 1;
+			dev->read = 2;
 		}
 	}
 	if (dev->qlen == 0) {
@@ -282,6 +285,8 @@
 		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
 		if (status_event[dev->type].encode) /* set data values */
 			status_event[dev->type].encode(dev, ev);
+		if (dev->type >= ST_SPECIAL)
+			dev->type = ST_INVALID;
 		rc = 1;
 	} else 	if (dev->type == ST_SYSEX) {
 		if (c == MIDI_CMD_COMMON_SYSEX_END ||
diff -ruN linux-2.6.22/sound/core/seq/seq_virmidi.c linux-2.6.22-alsa/sound/core/seq/seq_virmidi.c
--- linux-2.6.22/sound/core/seq/seq_virmidi.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/seq_virmidi.c	2007-09-01 20:55:22.000000000 +0200
@@ -363,7 +363,7 @@
 	if (rdev->client >= 0)
 		return 0;
 
-	pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
+	pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
 	if (!pinfo) {
 		err = -ENOMEM;
 		goto __error;
@@ -380,7 +380,6 @@
 	rdev->client = client;
 
 	/* create a port */
-	memset(pinfo, 0, sizeof(*pinfo));
 	pinfo->addr.client = client;
 	sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
 	/* set all capabilities */
diff -ruN linux-2.6.22/sound/core/sound.c linux-2.6.22-alsa/sound/core/sound.c
--- linux-2.6.22/sound/core/sound.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/sound.c	2007-09-01 20:55:22.000000000 +0200
@@ -446,8 +446,7 @@
 {
 	snd_info_minor_unregister();
 	snd_info_done();
-	if (unregister_chrdev(major, "alsa") != 0)
-		snd_printk(KERN_ERR "unable to unregister major device number %d\n", major);
+	unregister_chrdev(major, "alsa");
 }
 
 module_init(alsa_sound_init)
diff -ruN linux-2.6.22/sound/core/timer.c linux-2.6.22-alsa/sound/core/timer.c
--- linux-2.6.22/sound/core/timer.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/timer.c	2007-09-01 20:55:22.000000000 +0200
@@ -1549,9 +1549,11 @@
 	int err = 0;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	t = tu->timeri->timer;
-	snd_assert(t != NULL, return -ENXIO);
+	if (!t)
+		return -EBADFD;
 
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (! info)
@@ -1579,9 +1581,11 @@
 	int err;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	t = tu->timeri->timer;
-	snd_assert(t != NULL, return -ENXIO);
+	if (!t)
+		return -EBADFD;
 	if (copy_from_user(&params, _params, sizeof(params)))
 		return -EFAULT;
 	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
@@ -1675,7 +1679,8 @@
 	struct snd_timer_status status;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	memset(&status, 0, sizeof(status));
 	status.tstamp = tu->tstamp;
 	status.resolution = snd_timer_resolution(tu->timeri);
@@ -1695,7 +1700,8 @@
 	struct snd_timer_user *tu;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	snd_timer_stop(tu->timeri);
 	tu->timeri->lost = 0;
 	tu->last_resolution = 0;
@@ -1708,7 +1714,8 @@
 	struct snd_timer_user *tu;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
 }
 
@@ -1718,7 +1725,8 @@
 	struct snd_timer_user *tu;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	tu->timeri->lost = 0;
 	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
 }
@@ -1729,7 +1737,8 @@
 	struct snd_timer_user *tu;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
 }
 
diff -ruN linux-2.6.22/sound/drivers/dummy.c linux-2.6.22-alsa/sound/drivers/dummy.c
--- linux-2.6.22/sound/drivers/dummy.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/dummy.c	2007-09-01 20:55:22.000000000 +0200
@@ -510,15 +510,7 @@
   .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \
   .private_value = addr }
 
-static int snd_dummy_capsrc_info(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define snd_dummy_capsrc_info	snd_ctl_boolean_stereo_info
  
 static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
@@ -659,7 +651,7 @@
 	},
 };
 
-static void __init_or_module snd_dummy_unregister_all(void)
+static void snd_dummy_unregister_all(void)
 {
 	int i;
 
diff -ruN linux-2.6.22/sound/drivers/mpu401/mpu401.c linux-2.6.22-alsa/sound/drivers/mpu401/mpu401.c
--- linux-2.6.22/sound/drivers/mpu401/mpu401.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/mpu401/mpu401.c	2007-09-01 20:55:22.000000000 +0200
@@ -228,7 +228,7 @@
 static struct pnp_driver snd_mpu401_pnp_driver;
 #endif
 
-static void __init_or_module snd_mpu401_unregister_all(void)
+static void snd_mpu401_unregister_all(void)
 {
 	int i;
 
diff -ruN linux-2.6.22/sound/drivers/mts64.c linux-2.6.22-alsa/sound/drivers/mts64.c
--- linux-2.6.22/sound/drivers/mts64.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/mts64.c	2007-09-01 20:55:22.000000000 +0200
@@ -440,15 +440,7 @@
  *********************************************************************/
 
 /* SMPTE Switch */
-static int snd_mts64_ctl_smpte_switch_info(struct snd_kcontrol *kctl,
-					   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_mts64_ctl_smpte_switch_info		snd_ctl_boolean_mono_info
 
 static int snd_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl,
 					  struct snd_ctl_elem_value *uctl)
diff -ruN linux-2.6.22/sound/drivers/opl3/Makefile linux-2.6.22-alsa/sound/drivers/opl3/Makefile
--- linux-2.6.22/sound/drivers/opl3/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/opl3/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -4,10 +4,8 @@
 #
 
 snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
-snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o
-ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
-snd-opl3-synth-objs += opl3_oss.o
-endif
+snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
+snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
 
 #
 # this function returns:
diff -ruN linux-2.6.22/sound/drivers/portman2x4.c linux-2.6.22-alsa/sound/drivers/portman2x4.c
--- linux-2.6.22/sound/drivers/portman2x4.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/portman2x4.c	2007-09-01 20:55:22.000000000 +0200
@@ -833,7 +833,7 @@
 /*********************************************************************
  * module init stuff
  *********************************************************************/
-static void __init_or_module snd_portman_unregister_all(void)
+static void snd_portman_unregister_all(void)
 {
 	int i;
 
diff -ruN linux-2.6.22/sound/drivers/serial-u16550.c linux-2.6.22-alsa/sound/drivers/serial-u16550.c
--- linux-2.6.22/sound/drivers/serial-u16550.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/serial-u16550.c	2007-09-01 20:55:22.000000000 +0200
@@ -998,7 +998,7 @@
 	},
 };
 
-static void __init_or_module snd_serial_unregister_all(void)
+static void snd_serial_unregister_all(void)
 {
 	int i;
 
diff -ruN linux-2.6.22/sound/drivers/virmidi.c linux-2.6.22-alsa/sound/drivers/virmidi.c
--- linux-2.6.22/sound/drivers/virmidi.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/virmidi.c	2007-09-01 20:55:22.000000000 +0200
@@ -145,7 +145,7 @@
 	},
 };
 
-static void __init_or_module snd_virmidi_unregister_all(void)
+static void snd_virmidi_unregister_all(void)
 {
 	int i;
 
diff -ruN linux-2.6.22/sound/drivers/vx/vx_mixer.c linux-2.6.22-alsa/sound/drivers/vx/vx_mixer.c
--- linux-2.6.22/sound/drivers/vx/vx_mixer.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/vx/vx_mixer.c	2007-09-01 20:55:22.000000000 +0200
@@ -647,14 +647,7 @@
 	return 0;
 }
 
-static int vx_audio_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define vx_audio_sw_info	snd_ctl_boolean_stereo_info
 
 static int vx_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -865,14 +858,7 @@
 	return 0;
 }
 
-static int vx_saturation_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define vx_saturation_info	snd_ctl_boolean_stereo_info
 
 static int vx_saturation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
diff -ruN linux-2.6.22/sound/i2c/Makefile linux-2.6.22-alsa/sound/i2c/Makefile
--- linux-2.6.22/sound/i2c/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -7,9 +7,7 @@
 snd-cs8427-objs := cs8427.o
 snd-tea6330t-objs := tea6330t.o
 
-ifeq ($(subst m,y,$(CONFIG_L3)),y)
-  obj-$(CONFIG_L3) += l3/
-endif
+obj-$(CONFIG_L3) += l3/
 
 obj-$(CONFIG_SND) += other/
 
diff -ruN linux-2.6.22/sound/i2c/other/ak4114.c linux-2.6.22-alsa/sound/i2c/other/ak4114.c
--- linux-2.6.22/sound/i2c/other/ak4114.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/other/ak4114.c	2007-09-01 20:55:22.000000000 +0200
@@ -200,15 +200,7 @@
 	return 0;
 }
 
-static int snd_ak4114_in_bit_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_ak4114_in_bit_info		snd_ctl_boolean_mono_info
 
 static int snd_ak4114_in_bit_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/i2c/other/ak4117.c linux-2.6.22-alsa/sound/i2c/other/ak4117.c
--- linux-2.6.22/sound/i2c/other/ak4117.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/other/ak4117.c	2007-09-01 20:55:22.000000000 +0200
@@ -181,15 +181,7 @@
 	return 0;
 }
 
-static int snd_ak4117_in_bit_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_ak4117_in_bit_info		snd_ctl_boolean_mono_info
 
 static int snd_ak4117_in_bit_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/i2c/other/ak4xxx-adda.c linux-2.6.22-alsa/sound/i2c/other/ak4xxx-adda.c
--- linux-2.6.22/sound/i2c/other/ak4xxx-adda.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/other/ak4xxx-adda.c	2007-09-01 20:55:22.000000000 +0200
@@ -463,15 +463,7 @@
 	return change;
 }
 
-static int ak4xxx_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 ak4xxx_switch_info	snd_ctl_boolean_mono_info
 
 static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
 			     struct snd_ctl_elem_value *ucontrol)
@@ -481,8 +473,8 @@
 	int addr = AK_GET_ADDR(kcontrol->private_value);
 	int shift = AK_GET_SHIFT(kcontrol->private_value);
 	int invert = AK_GET_INVERT(kcontrol->private_value);
-	unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-
+	/* we observe the (1<<shift) bit only */
+	unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift);
 	if (invert)
 		val = ! val;
 	ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
@@ -585,6 +577,26 @@
 
 	mixer_ch = 0;
 	for (idx = 0; idx < ak->num_dacs; ) {
+		/* mute control for Revolution 7.1 - AK4381 */
+		if (ak->type == SND_AK4381 
+				&&  ak->dac_info[mixer_ch].switch_name) {
+			memset(&knew, 0, sizeof(knew));
+			knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			knew.count = 1;
+			knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+			knew.name = ak->dac_info[mixer_ch].switch_name;
+			knew.info = ak4xxx_switch_info;
+			knew.get = ak4xxx_switch_get;
+			knew.put = ak4xxx_switch_put;
+			knew.access = 0;
+			/* register 1, bit 0 (SMUTE): 0 = normal operation,
+			   1 = mute */
+			knew.private_value =
+				AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT;
+			err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+			if (err < 0)
+				return err;
+		}
 		memset(&knew, 0, sizeof(knew));
 		if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
 			knew.name = "DAC Volume";
diff -ruN linux-2.6.22/sound/i2c/other/pt2258.c linux-2.6.22-alsa/sound/i2c/other/pt2258.c
--- linux-2.6.22/sound/i2c/other/pt2258.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/other/pt2258.c	2007-09-01 20:55:22.000000000 +0200
@@ -140,15 +140,7 @@
 	return -EIO;
 }
 
-static int pt2258_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 pt2258_switch_info	snd_ctl_boolean_mono_info
 
 static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
 			     struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/i2c/tea6330t.c linux-2.6.22-alsa/sound/i2c/tea6330t.c
--- linux-2.6.22/sound/i2c/tea6330t.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/tea6330t.c	2007-09-01 20:55:22.000000000 +0200
@@ -142,15 +142,7 @@
   .info = snd_tea6330t_info_master_switch, \
   .get = snd_tea6330t_get_master_switch, .put = snd_tea6330t_put_master_switch }
 
-static int snd_tea6330t_info_master_switch(struct snd_kcontrol *kcontrol,
-					   struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define snd_tea6330t_info_master_switch		snd_ctl_boolean_stereo_info
 
 static int snd_tea6330t_get_master_switch(struct snd_kcontrol *kcontrol,
 					  struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/isa/Kconfig linux-2.6.22-alsa/sound/isa/Kconfig
--- linux-2.6.22/sound/isa/Kconfig	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/Kconfig	2007-09-01 20:55:22.000000000 +0200
@@ -1,8 +1,5 @@
 # ALSA ISA drivers
 
-menu "ISA devices"
-	depends on SND!=n && ISA && ISA_DMA_API
-
 config SND_AD1848_LIB
         tristate
         select SND_PCM
@@ -11,6 +8,22 @@
         tristate
         select SND_PCM
 
+config SND_SB_COMMON
+        tristate
+
+config SND_SB8_DSP
+        tristate
+        select SND_PCM
+        select SND_SB_COMMON
+
+config SND_SB16_DSP
+        tristate
+        select SND_PCM
+        select SND_SB_COMMON
+
+menu "ISA devices"
+	depends on SND!=n && ISA && ISA_DMA_API
+
 config SND_ADLIB
 	tristate "AdLib FM card"
 	depends on SND
@@ -55,7 +68,7 @@
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	  Say Y here to include support for soundcards based on Avance
 	  Logic ALS100, ALS110, ALS120 and ALS200 chips.
@@ -81,6 +94,7 @@
 	tristate "C-Media CMI8330"
 	depends on SND
 	select SND_AD1848_LIB
+	select SND_SB16_DSP
 	help
 	  Say Y here to include support for soundcards based on the
 	  C-Media CMI8330 chip.
@@ -132,7 +146,7 @@
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	  Say Y here to include support for soundcards based on the
 	  Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
@@ -145,7 +159,7 @@
 	depends on SND && PNP && ISA
 	select ISAPNP
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB8_DSP
 	help
 	  Say Y here to include support for ESS AudioDrive ES968 chips.
 
@@ -321,7 +335,7 @@
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_RAWMIDI
-	select SND_PCM
+	select SND_SB8_DSP
 	help
 	  Say Y here to include support for Creative Sound Blaster 1.0/
 	  2.0/Pro (8-bit) or 100% compatible soundcards.
@@ -334,7 +348,7 @@
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	  Say Y here to include support for Sound Blaster 16 soundcards
 	  (including the Plug and Play version).
@@ -347,7 +361,7 @@
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	  Say Y here to include support for Sound Blaster AWE soundcards
 	  (including the Plug and Play version).
@@ -400,7 +414,7 @@
 config SND_WAVEFRONT
 	tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)"
 	depends on SND
-	select FW_LOADER if !SND_WAVEFRONT_FIRMWARE_IN_KERNEL
+	select FW_LOADER
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -416,8 +430,9 @@
 	depends on SND_WAVEFRONT
 	default y
 	help
-	  Say Y here to include the static firmware built in the kernel
-	  for the Wavefront driver.  If you choose N here, you need to
-	  install the firmware files from the alsa-firmware package.
+	  Say Y here to include the static firmware for FX DSP built in
+	  the kernel for the Wavefront driver.  If you choose N here,
+	  you need to install the firmware files from the
+	  alsa-firmware package.
 
 endmenu
diff -ruN linux-2.6.22/sound/isa/ad1816a/ad1816a_lib.c linux-2.6.22-alsa/sound/isa/ad1816a/ad1816a_lib.c
--- linux-2.6.22/sound/isa/ad1816a/ad1816a_lib.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/ad1816a/ad1816a_lib.c	2007-09-01 20:55:22.000000000 +0200
@@ -453,7 +453,6 @@
 
 	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0)
 		return error;
-	snd_pcm_set_sync(substream);
 	runtime->hw = snd_ad1816a_playback;
 	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
 	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
@@ -469,7 +468,6 @@
 
 	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0)
 		return error;
-	snd_pcm_set_sync(substream);
 	runtime->hw = snd_ad1816a_capture;
 	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
 	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
diff -ruN linux-2.6.22/sound/isa/ad1848/Makefile linux-2.6.22-alsa/sound/isa/ad1848/Makefile
--- linux-2.6.22/sound/isa/ad1848/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/ad1848/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -7,9 +7,6 @@
 snd-ad1848-objs := ad1848.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o
-obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o
-obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o
-obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o
+obj-$(CONFIG_SND_AD1848) += snd-ad1848.o
+obj-$(CONFIG_SND_AD1848_LIB) += snd-ad1848-lib.o
 
-obj-m := $(sort $(obj-m))
diff -ruN linux-2.6.22/sound/isa/ad1848/ad1848_lib.c linux-2.6.22-alsa/sound/isa/ad1848/ad1848_lib.c
--- linux-2.6.22/sound/isa/ad1848/ad1848_lib.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/ad1848/ad1848_lib.c	2007-09-01 20:55:22.000000000 +0200
@@ -245,7 +245,7 @@
 			snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n");
 			return;
 		}
-		time = schedule_timeout_interruptible(time);
+		time = schedule_timeout(time);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 	}
 #if 0
@@ -258,7 +258,7 @@
 			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
 			return;
 		}
-		time = schedule_timeout_interruptible(time);
+		time = schedule_timeout(time);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 	}
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
diff -ruN linux-2.6.22/sound/isa/cs423x/Makefile linux-2.6.22-alsa/sound/isa/cs423x/Makefile
--- linux-2.6.22/sound/isa/cs423x/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/cs423x/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -10,17 +10,8 @@
 snd-cs4236-objs := cs4236.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_MIRO) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o
-obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o
-obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o
-obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o
-obj-$(CONFIG_SND_SSCAPE) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_CS4231_LIB) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
+obj-$(CONFIG_SND_CS4232) += snd-cs4232.o
+obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o
 
-obj-m := $(sort $(obj-m))
diff -ruN linux-2.6.22/sound/isa/cs423x/cs4231_lib.c linux-2.6.22-alsa/sound/isa/cs423x/cs4231_lib.c
--- linux-2.6.22/sound/isa/cs423x/cs4231_lib.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/cs423x/cs4231_lib.c	2007-09-01 20:55:22.000000000 +0200
@@ -555,6 +555,8 @@
 			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
 		}
 		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (chip->hardware == CS4231_HW_OPL3SA2)
+			udelay(100);	/* this seems to help */
 		snd_cs4231_mce_down(chip);
 	}
 	snd_cs4231_calibrate_mute(chip, 0);
diff -ruN linux-2.6.22/sound/isa/es18xx.c linux-2.6.22-alsa/sound/isa/es18xx.c
--- linux-2.6.22/sound/isa/es18xx.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/es18xx.c	2007-09-01 20:55:22.000000000 +0200
@@ -1071,14 +1071,7 @@
 	return (snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val) || retVal;
 }
 
-static int snd_es18xx_info_spatializer_enable(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_es18xx_info_spatializer_enable	snd_ctl_boolean_mono_info
 
 static int snd_es18xx_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -1120,14 +1113,7 @@
 	return 0;
 }
 
-static int snd_es18xx_info_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define snd_es18xx_info_hw_switch	snd_ctl_boolean_stereo_info
 
 static int snd_es18xx_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -2042,6 +2028,7 @@
 
 static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
 	{ .id = "ESS1869" },
+	{ .id = "ESS1879" },
 	{ .id = "" }		/* end */
 };
 
diff -ruN linux-2.6.22/sound/isa/gus/gus_mixer.c linux-2.6.22-alsa/sound/isa/gus/gus_mixer.c
--- linux-2.6.22/sound/isa/gus/gus_mixer.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/gus/gus_mixer.c	2007-09-01 20:55:22.000000000 +0200
@@ -36,14 +36,7 @@
   .get = snd_gf1_get_single, .put = snd_gf1_put_single, \
   .private_value = shift | (invert << 8) }
 
-static int snd_gf1_info_single(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_gf1_info_single	snd_ctl_boolean_mono_info
 
 static int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
diff -ruN linux-2.6.22/sound/isa/opl3sa2.c linux-2.6.22-alsa/sound/isa/opl3sa2.c
--- linux-2.6.22/sound/isa/opl3sa2.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/opl3sa2.c	2007-09-01 20:55:22.000000000 +0200
@@ -164,6 +164,8 @@
 	{ .id = "YMH0801", .devs = { { "YMH0021" } } },
 	/* NeoMagic MagicWave 3DX */
 	{ .id = "NMX2200", .devs = { { "YMH2210" } } },
+	/* NeoMagic MagicWave 3D */
+	{ .id = "NMX2200", .devs = { { "NMX2210" } } },
 	/* --- */
 	{ .id = "" }	/* end */
 };
@@ -251,6 +253,7 @@
 		/* 0x03 - YM715B */
 		/* 0x04 - YM719 - OPL-SA4? */
 		/* 0x05 - OPL3-SA3 - Libretto 100 */
+		/* 0x07 - unknown - Neomagic MagicWave 3D */
 		break;
 	}
 	str[0] = chip->version + '0';
diff -ruN linux-2.6.22/sound/isa/opti9xx/miro.c linux-2.6.22-alsa/sound/isa/opti9xx/miro.c
--- linux-2.6.22/sound/isa/opti9xx/miro.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/opti9xx/miro.c	2007-09-01 20:55:22.000000000 +0200
@@ -242,14 +242,7 @@
  *  MIXER part
  */
 
-static int snd_miro_info_capture(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 1;
-
-	return 0;
-}
+#define snd_miro_info_capture	snd_ctl_boolean_mono_info
 
 static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
@@ -344,14 +337,7 @@
 	return change;
 }
 
-static int snd_miro_info_amp(struct snd_kcontrol *kcontrol,
-			     struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 1;
-
-	return 0;
-}
+#define snd_miro_info_amp	snd_ctl_boolean_mono_info
 
 static int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
 			    struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/isa/opti9xx/opti92x-ad1848.c linux-2.6.22-alsa/sound/isa/opti9xx/opti92x-ad1848.c
--- linux-2.6.22/sound/isa/opti9xx/opti92x-ad1848.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/opti9xx/opti92x-ad1848.c	2007-09-01 20:55:22.000000000 +0200
@@ -1927,10 +1927,12 @@
 static int __devinit snd_opti9xx_isa_match(struct device *devptr,
 					   unsigned int dev)
 {
+#ifdef CONFIG_PNP
 	if (snd_opti9xx_pnp_is_probed)
 		return 0;
 	if (isapnp)
 		return 0;
+#endif
 	return 1;
 }
 
@@ -2096,6 +2098,7 @@
 	pnp_register_card_driver(&opti9xx_pnpc_driver);
 	if (snd_opti9xx_pnp_is_probed)
 		return 0;
+	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
 #endif
 	return isa_register_driver(&snd_opti9xx_driver, 1);
 }
diff -ruN linux-2.6.22/sound/isa/sb/Makefile linux-2.6.22-alsa/sound/isa/sb/Makefile
--- linux-2.6.22/sound/isa/sb/Makefile	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -22,14 +22,13 @@
 sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o
+obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
+obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
+obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
+obj-$(CONFIG_SND_SB8) += snd-sb8.o
+obj-$(CONFIG_SND_SB16) += snd-sb16.o
+obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
+obj-$(CONFIG_SND_ES968) += snd-es968.o
 ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
diff -ruN linux-2.6.22/sound/isa/sb/sb16_csp.c linux-2.6.22-alsa/sound/isa/sb/sb16_csp.c
--- linux-2.6.22/sound/isa/sb/sb16_csp.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb16_csp.c	2007-09-01 20:55:22.000000000 +0200
@@ -979,14 +979,7 @@
  * QSound mixer control for PCM
  */
 
-static int snd_sb_qsound_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_sb_qsound_switch_info	snd_ctl_boolean_mono_info
 
 static int snd_sb_qsound_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
diff -ruN linux-2.6.22/sound/isa/sb/sb16_main.c linux-2.6.22-alsa/sound/isa/sb/sb16_main.c
--- linux-2.6.22/sound/isa/sb/sb16_main.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb16_main.c	2007-09-01 20:55:22.000000000 +0200
@@ -563,6 +563,11 @@
       __open_ok:
 	if (chip->hardware == SB_HW_ALS100)
 		runtime->hw.rate_max = 48000;
+	if (chip->hardware == SB_HW_CS5530) {
+		runtime->hw.buffer_bytes_max = 32 * 1024;
+		runtime->hw.periods_min = 2;
+		runtime->hw.rate_min = 44100;
+	}
 	if (chip->mode & SB_RATE_LOCK)
 		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
 	chip->playback_substream = substream;
@@ -633,6 +638,11 @@
       __open_ok:
 	if (chip->hardware == SB_HW_ALS100)
 		runtime->hw.rate_max = 48000;
+	if (chip->hardware == SB_HW_CS5530) {
+		runtime->hw.buffer_bytes_max = 32 * 1024;
+		runtime->hw.periods_min = 2;
+		runtime->hw.rate_min = 44100;
+	}
 	if (chip->mode & SB_RATE_LOCK)
 		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
 	chip->capture_substream = substream;
diff -ruN linux-2.6.22/sound/isa/sb/sb_common.c linux-2.6.22-alsa/sound/isa/sb/sb_common.c
--- linux-2.6.22/sound/isa/sb/sb_common.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb_common.c	2007-09-01 20:55:22.000000000 +0200
@@ -128,7 +128,7 @@
 	minor = version & 0xff;
 	snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
 		    chip->port, major, minor);
-	
+
 	switch (chip->hardware) {
 	case SB_HW_AUTO:
 		switch (major) {
@@ -168,6 +168,9 @@
 	case SB_HW_DT019X:
 		str = "(DT019X/ALS007)";
 		break;
+	case SB_HW_CS5530:
+		str = "16 (CS5530)";
+		break;
 	default:
 		return -ENODEV;
 	}
@@ -231,7 +234,9 @@
 	chip->dma16 = -1;
 	chip->port = port;
 	
-	if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ?
+	if (request_irq(irq, irq_handler,
+			(hardware == SB_HW_ALS4000 ||
+			 hardware == SB_HW_CS5530) ?
 			IRQF_SHARED : IRQF_DISABLED,
 			"SoundBlaster", (void *) chip)) {
 		snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
diff -ruN linux-2.6.22/sound/isa/sb/sb_mixer.c linux-2.6.22-alsa/sound/isa/sb/sb_mixer.c
--- linux-2.6.22/sound/isa/sb/sb_mixer.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb_mixer.c	2007-09-01 20:55:22.000000000 +0200
@@ -821,6 +821,7 @@
 		break;
 	case SB_HW_16:
 	case SB_HW_ALS100:
+	case SB_HW_CS5530:
 		if ((err = snd_sbmixer_init(chip,
 					    snd_sb16_controls,
 					    ARRAY_SIZE(snd_sb16_controls),
@@ -950,6 +951,7 @@
 		break;
 	case SB_HW_16:
 	case SB_HW_ALS100:
+	case SB_HW_CS5530:
 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
 		break;
 	case SB_HW_ALS4000:
@@ -975,6 +977,7 @@
 		break;
 	case SB_HW_16:
 	case SB_HW_ALS100:
+	case SB_HW_CS5530:
 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
 		break;
 	case SB_HW_ALS4000:
diff -ruN linux-2.6.22/sound/isa/sscape.c linux-2.6.22-alsa/sound/isa/sscape.c
--- linux-2.6.22/sound/isa/sscape.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sscape.c	2007-09-01 20:55:22.000000000 +0200
@@ -382,7 +382,7 @@
 		unsigned long flags;
 		unsigned char x;
 
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 
 		spin_lock_irqsave(&s->lock, flags);
 		x = inb(HOST_DATA_IO(s->io_base));
@@ -409,7 +409,7 @@
 		unsigned long flags;
 		unsigned char x;
 
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 
 		spin_lock_irqsave(&s->lock, flags);
 		x = inb(HOST_DATA_IO(s->io_base));
diff -ruN linux-2.6.22/sound/isa/wavefront/wavefront_synth.c linux-2.6.22-alsa/sound/isa/wavefront/wavefront_synth.c
--- linux-2.6.22/sound/isa/wavefront/wavefront_synth.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/wavefront/wavefront_synth.c	2007-09-01 20:55:22.000000000 +0200
@@ -27,6 +27,7 @@
 #include <linux/delay.h>
 #include <linux/time.h>
 #include <linux/wait.h>
+#include <linux/firmware.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
 #include <sound/snd_wavefront.h>
@@ -53,9 +54,8 @@
 
 /* XXX this needs to be made firmware and hardware version dependent */
 
-static char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed
-						    version of the WaveFront OS
-						 */
+#define DEFAULT_OSPATH	"wavefront.os"
+static char *ospath = DEFAULT_OSPATH; /* the firmware file name */
 
 static int wait_usecs = 150; /* This magic number seems to give pretty optimal
 				throughput based on my limited experimentation.
@@ -97,7 +97,7 @@
 module_param(sleep_tries, int, 0444);
 MODULE_PARM_DESC(sleep_tries, "how many times to try sleeping during a wait");
 module_param(ospath, charp, 0444);
-MODULE_PARM_DESC(ospath, "full pathname to processed ICS2115 OS firmware");
+MODULE_PARM_DESC(ospath, "pathname to processed ICS2115 OS firmware");
 module_param(reset_time, int, 0444);
 MODULE_PARM_DESC(reset_time, "how long to wait for a reset to take effect");
 module_param(ramcheck_time, int, 0444);
@@ -1780,7 +1780,7 @@
 	outb (val,port);
 	spin_unlock_irq(&dev->irq_lock);
 	while (1) {
-		if ((timeout = schedule_timeout_interruptible(timeout)) == 0)
+		if ((timeout = schedule_timeout(timeout)) == 0)
 			return;
 		if (dev->irq_ok)
 			return;
@@ -1938,111 +1938,75 @@
 	return (1);
 }
 
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/unistd.h>
-#include <linux/syscalls.h>
-#include <asm/uaccess.h>
-
-
 static int __devinit
 wavefront_download_firmware (snd_wavefront_t *dev, char *path)
 
 {
-	unsigned char section[WF_SECTION_MAX];
-	signed char section_length; /* yes, just a char; max value is WF_SECTION_MAX */
+	unsigned char *buf;
+	int len, err;
 	int section_cnt_downloaded = 0;
-	int fd;
-	int c;
-	int i;
-	mm_segment_t fs;
+	const struct firmware *firmware;
 
-	/* This tries to be a bit cleverer than the stuff Alan Cox did for
-	   the generic sound firmware, in that it actually knows
-	   something about the structure of the Motorola firmware. In
-	   particular, it uses a version that has been stripped of the
-	   20K of useless header information, and had section lengths
-	   added, making it possible to load the entire OS without any
-	   [kv]malloc() activity, since the longest entity we ever read is
-	   42 bytes (well, WF_SECTION_MAX) long.
-	*/
-
-	fs = get_fs();
-	set_fs (get_ds());
-
-	if ((fd = sys_open ((char __user *) path, 0, 0)) < 0) {
-		snd_printk ("Unable to load \"%s\".\n",
-			path);
+	err = request_firmware(&firmware, path, dev->card->dev);
+	if (err < 0) {
+		snd_printk(KERN_ERR "firmware (%s) download failed!!!\n", path);
 		return 1;
 	}
 
-	while (1) {
-		int x;
-
-		if ((x = sys_read (fd, (char __user *) &section_length, sizeof (section_length))) !=
-		    sizeof (section_length)) {
-			snd_printk ("firmware read error.\n");
-			goto failure;
-		}
-
-		if (section_length == 0) {
+	len = 0;
+	buf = firmware->data;
+	for (;;) {
+		int section_length = *(signed char *)buf;
+		if (section_length == 0)
 			break;
-		}
-
 		if (section_length < 0 || section_length > WF_SECTION_MAX) {
-			snd_printk ("invalid firmware section length %d\n",
-				    section_length);
+			snd_printk(KERN_ERR
+				   "invalid firmware section length %d\n",
+				   section_length);
 			goto failure;
 		}
+		buf++;
+		len++;
 
-		if (sys_read (fd, (char __user *) section, section_length) != section_length) {
-			snd_printk ("firmware section "
-				"read error.\n");
+		if (firmware->size < len + section_length) {
+			snd_printk(KERN_ERR "firmware section read error.\n");
 			goto failure;
 		}
 
 		/* Send command */
-	
-		if (wavefront_write (dev, WFC_DOWNLOAD_OS)) {
+		if (wavefront_write(dev, WFC_DOWNLOAD_OS))
 			goto failure;
-		}
 	
-		for (i = 0; i < section_length; i++) {
-			if (wavefront_write (dev, section[i])) {
+		for (; section_length; section_length--) {
+			if (wavefront_write(dev, *buf))
 				goto failure;
-			}
+			buf++;
+			len++;
 		}
 	
 		/* get ACK */
-	
-		if (wavefront_wait (dev, STAT_CAN_READ)) {
-
-			if ((c = inb (dev->data_port)) != WF_ACK) {
-
-				snd_printk ("download "
-					    "of section #%d not "
-					    "acknowledged, ack = 0x%x\n",
-					    section_cnt_downloaded + 1, c);
-				goto failure;
-		
-			}
-
-		} else {
-			snd_printk ("time out for firmware ACK.\n");
+		if (!wavefront_wait(dev, STAT_CAN_READ)) {
+			snd_printk(KERN_ERR "time out for firmware ACK.\n");
+			goto failure;
+		}
+		err = inb(dev->data_port);
+		if (err != WF_ACK) {
+			snd_printk(KERN_ERR
+				   "download of section #%d not "
+				   "acknowledged, ack = 0x%x\n",
+				   section_cnt_downloaded + 1, err);
 			goto failure;
 		}
 
+		section_cnt_downloaded++;
 	}
 
-	sys_close (fd);
-	set_fs (fs);
+	release_firmware(firmware);
 	return 0;
 
  failure:
-	sys_close (fd);
-	set_fs (fs);
-	snd_printk ("firmware download failed!!!\n");
+	release_firmware(firmware);
+	snd_printk(KERN_ERR "firmware download failed!!!\n");
 	return 1;
 }
 
@@ -2232,3 +2196,5 @@
 
 	return 0;
 }
+
+MODULE_FIRMWARE(DEFAULT_OSPATH);
diff -ruN linux-2.6.22/sound/oss/Makefile linux-2.6.22-alsa/sound/oss/Makefile
--- linux-2.6.22/sound/oss/Makefile	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/oss/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -15,42 +15,69 @@
 obj-$(CONFIG_SOUND_AEDSP16)	+= aedsp16.o
 obj-$(CONFIG_SOUND_PSS)		+= pss.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_OPL3SA1)	+= opl3sa.o ad1848.o uart401.o
 obj-$(CONFIG_SOUND_SSCAPE)	+= sscape.o ad1848.o mpu401.o
+obj-$(CONFIG_SOUND_MAD16)	+= mad16.o ad1848.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_CS4232)	+= cs4232.o uart401.o
 obj-$(CONFIG_SOUND_MSS)		+= ad1848.o
 obj-$(CONFIG_SOUND_OPL3SA2)	+= opl3sa2.o ad1848.o mpu401.o
 obj-$(CONFIG_SOUND_PAS)		+= pas2.o sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_SB)		+= sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_KAHLUA)	+= kahlua.o
+obj-$(CONFIG_SOUND_WAVEFRONT)	+= wavefront.o
+obj-$(CONFIG_SOUND_MAUI)	+= maui.o mpu401.o
 obj-$(CONFIG_SOUND_MPU401)	+= mpu401.o
 obj-$(CONFIG_SOUND_UART6850)	+= uart6850.o
+obj-$(CONFIG_SOUND_GUS)		+= gus.o ad1848.o
 obj-$(CONFIG_SOUND_ADLIB)	+= adlib_card.o opl3.o
 obj-$(CONFIG_SOUND_YM3812)	+= opl3.o
 obj-$(CONFIG_SOUND_VMIDI)	+= v_midi.o
 obj-$(CONFIG_SOUND_VIDC)	+= vidc_mod.o
 obj-$(CONFIG_SOUND_WAVEARTIST)	+= waveartist.o
+obj-$(CONFIG_SOUND_SGALAXY)	+= sgalaxy.o ad1848.o
 obj-$(CONFIG_SOUND_AD1816)	+= ad1816.o
 obj-$(CONFIG_SOUND_AD1889)	+= ad1889.o ac97_codec.o
 obj-$(CONFIG_SOUND_ACI_MIXER)	+= aci.o
+obj-$(CONFIG_SOUND_AWE32_SYNTH)	+= awe_wave.o
 
 obj-$(CONFIG_SOUND_VIA82CXXX)	+= via82cxxx_audio.o ac97_codec.o
 ifeq ($(CONFIG_MIDI_VIA82CXXX),y)
   obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o
 endif
+obj-$(CONFIG_SOUND_YMFPCI)	+= ymfpci.o ac97_codec.o
+ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
+  obj-$(CONFIG_SOUND_YMFPCI)    += opl3.o uart401.o
+endif
 obj-$(CONFIG_SOUND_MSNDCLAS)	+= msnd.o msnd_classic.o
 obj-$(CONFIG_SOUND_MSNDPIN)	+= msnd.o msnd_pinnacle.o
 obj-$(CONFIG_SOUND_VWSND)	+= vwsnd.o
 obj-$(CONFIG_SOUND_NM256)	+= nm256_audio.o ac97.o
 obj-$(CONFIG_SOUND_ICH)		+= i810_audio.o ac97_codec.o
+obj-$(CONFIG_SOUND_SONICVIBES)	+= sonicvibes.o
+obj-$(CONFIG_SOUND_CMPCI)	+= cmpci.o
+ifeq ($(CONFIG_SOUND_CMPCI_FM),y)
+  obj-$(CONFIG_SOUND_CMPCI)     += sound.o opl3.o
+endif
+ifeq ($(CONFIG_SOUND_CMPCI_MIDI),y)
+  obj-$(CONFIG_SOUND_CMPCI)     += sound.o mpu401.o
+endif
+obj-$(CONFIG_SOUND_ES1370)	+= es1370.o
 obj-$(CONFIG_SOUND_ES1371)	+= es1371.o ac97_codec.o
 obj-$(CONFIG_SOUND_VRC5477)	+= nec_vrc5477.o ac97_codec.o
-obj-$(CONFIG_SOUND_AU1550_AC97)	+= au1550_ac97.o ac97_codec.o
+obj-$(CONFIG_SOUND_ESSSOLO1)	+= esssolo1.o
 obj-$(CONFIG_SOUND_FUSION)	+= cs46xx.o ac97_codec.o
+obj-$(CONFIG_SOUND_MAESTRO)	+= maestro.o
+obj-$(CONFIG_SOUND_MAESTRO3)	+= maestro3.o ac97_codec.o
 obj-$(CONFIG_SOUND_TRIDENT)	+= trident.o ac97_codec.o
+obj-$(CONFIG_SOUND_HARMONY)	+= harmony.o
 obj-$(CONFIG_SOUND_EMU10K1)	+= ac97_codec.o
-obj-$(CONFIG_SOUND_BCM_CS4297A)	+= swarm_cs4297a.o
+obj-$(CONFIG_SOUND_RME96XX)     += rme96xx.o
 obj-$(CONFIG_SOUND_BT878)	+= btaudio.o
+obj-$(CONFIG_SOUND_ALI5455)	+= ali5455.o ac97_codec.o
+obj-$(CONFIG_SOUND_IT8172)	+= ite8172.o ac97_codec.o
+obj-$(CONFIG_SOUND_FORTE)	+= forte.o ac97_codec.o
 
+obj-$(CONFIG_SOUND_AD1980)	+= ac97_plugin_ad1980.o
 obj-$(CONFIG_SOUND_WM97XX)	+= ac97_plugin_wm97xx.o
 
 ifeq ($(CONFIG_MIDI_EMU10K1),y)
@@ -63,20 +90,22 @@
 # Declare multi-part drivers.
 
 sound-objs	:= 							\
-    dev_table.o soundcard.o 		\
-    audio.o dmabuf.o					\
-    midi_synth.o midibuf.o					\
-    sequencer.o sound_timer.o sys_timer.o
+    dev_table.o soundcard.o sound_syms.o		\
+    audio.o audio_syms.o dmabuf.o					\
+    midi_syms.o midi_synth.o midibuf.o					\
+    sequencer.o sequencer_syms.o sound_timer.o sys_timer.o
 
+gus-objs	:= gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
 pas2-objs	:= pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
 sb-objs		:= sb_card.o
 sb_lib-objs	:= sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
 vidc_mod-objs	:= vidc.o vidc_fill.o
+wavefront-objs  := wavfront.o wf_midi.o yss225.o
 
-hostprogs-y	:= bin2hex hex2hex
+host-progs	:= bin2hex hex2hex
 
 # Files generated that shall be removed upon make clean
-clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \
+clean-files := maui_boot.h msndperm.c msndinit.c pndsperm.c pndspini.c \
                pss_boot.h trix_boot.h
 
 # Firmware files that need translation
diff -ruN linux-2.6.22/sound/pci/Kconfig linux-2.6.22-alsa/sound/pci/Kconfig
--- linux-2.6.22/sound/pci/Kconfig	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/Kconfig	2007-09-01 20:55:23.000000000 +0200
@@ -33,6 +33,7 @@
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
+	select SND_SB_COMMON
 	help
 	  Say Y here to include support for soundcards based on Avance Logic
 	  ALS4000 chips.
@@ -169,14 +170,14 @@
 	  will be called snd-ca0106.
 
 config SND_CMIPCI
-	tristate "C-Media 8738, 8338"
+	tristate "C-Media 8338, 8738, 8768, 8770"
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
 	help
-	  If you want to use soundcards based on C-Media CMI8338 or CMI8738
-	  chips, say Y here and read
+	  If you want to use soundcards based on C-Media CMI8338, CMI8738,
+	  CMI8768 or CMI8770 chips, say Y here and read
 	  <file:Documentation/sound/alsa/CMIPCI.txt>.
 
 	  To compile this driver as a module, choose M here: the module
@@ -215,6 +216,16 @@
 
 	  This works better than the old code, so say Y.
 
+config SND_CS5530
+	tristate "CS5530 Audio"
+	depends on SND && ISA_DMA_API
+	select SND_SB16_DSP
+	help
+	  Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-cs5530.
+
 config SND_CS5535AUDIO
 	tristate "CS5535/CS5536 Audio"
 	depends on SND && X86 && !X86_64
@@ -489,6 +500,95 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-hda-intel.
 
+config SND_HDA_HWDEP
+	bool "Build hwdep interface for HD-audio driver"
+	depends on SND_HDA_INTEL
+	select SND_HWDEP
+	help
+	  Say Y here to build a hwdep interface for HD-audio driver.
+	  This interface can be used for out-of-bound communication
+	  with codecs for debugging purposes.
+
+config SND_HDA_CODEC_REALTEK
+	bool "Build Realtek HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Realtek HD-audio codec support in
+	  snd-hda-intel driver, such as ALC880.
+
+config SND_HDA_CODEC_ANALOG
+	bool "Build Analog Device HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Analog Device HD-audio codec support in
+	  snd-hda-intel driver, such as AD1986A.
+
+config SND_HDA_CODEC_SIGMATEL
+	bool "Build IDT/Sigmatel HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include IDT (Sigmatel) HD-audio codec support in
+	  snd-hda-intel driver, such as STAC9200.
+
+config SND_HDA_CODEC_VIA
+	bool "Build VIA HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include VIA HD-audio codec support in
+	  snd-hda-intel driver, such as VT1708.
+
+config SND_HDA_CODEC_ATIHDMI
+	bool "Build ATI HDMI HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include ATI HDMI HD-audio codec support in
+	  snd-hda-intel driver, such as ATI RS600 HDMI.
+
+config SND_HDA_CODEC_CONEXANT
+	bool "Build Conexant HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Conexant HD-audio codec support in
+	  snd-hda-intel driver, such as CX20549.
+
+config SND_HDA_CODEC_CMEDIA
+	bool "Build C-Media HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include C-Media HD-audio codec support in
+	  snd-hda-intel driver, such as CMI9880.
+
+config SND_HDA_CODEC_SI3054
+	bool "Build Silicon Labs 3054 HD-modem codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Silicon Labs 3054 HD-modem codec
+	  (and compatibles) support in snd-hda-intel driver.
+
+config SND_HDA_GENERIC
+	bool "Enable generic HD-audio codec parser"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to enable the generic HD-audio codec parser
+	  in snd-hda-intel driver.
+
+config SND_HDA_POWER_SAVE
+	bool "Aggressive power-saving on HD-audio"
+	depends on SND_HDA_INTEL && EXPERIMENTAL
+	help
+	  Say Y here to enable more aggressive power-saving mode on
+	  HD-audio driver.  The power-saving timeout can be configured
+	  via power_save option or over sysfs on-the-fly.
+
 config SND_HDSP
 	tristate "RME Hammerfall DSP Audio"
 	depends on SND
diff -ruN linux-2.6.22/sound/pci/Makefile linux-2.6.22-alsa/sound/pci/Makefile
--- linux-2.6.22/sound/pci/Makefile	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/Makefile	2007-09-01 20:55:23.000000000 +0200
@@ -12,6 +12,7 @@
 snd-bt87x-objs := bt87x.o
 snd-cmipci-objs := cmipci.o
 snd-cs4281-objs := cs4281.o
+snd-cs5530-objs := cs5530.o
 snd-ens1370-objs := ens1370.o
 snd-ens1371-objs := ens1371.o
 snd-es1938-objs := es1938.o
@@ -36,6 +37,7 @@
 obj-$(CONFIG_SND_BT87X) += snd-bt87x.o
 obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
 obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
+obj-$(CONFIG_SND_CS5530) += snd-cs5530.o
 obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
 obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
 obj-$(CONFIG_SND_ES1938) += snd-es1938.o
diff -ruN linux-2.6.22/sound/pci/ac97/ac97_codec.c linux-2.6.22-alsa/sound/pci/ac97/ac97_codec.c
--- linux-2.6.22/sound/pci/ac97/ac97_codec.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ac97/ac97_codec.c	2007-09-01 20:55:22.000000000 +0200
@@ -2036,11 +2036,12 @@
 	else {
 		udelay(50);
 		if (ac97->scaps & AC97_SCAP_SKIP_AUDIO)
-			err = ac97_reset_wait(ac97, HZ/2, 1);
+			err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 1);
 		else {
-			err = ac97_reset_wait(ac97, HZ/2, 0);
+			err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 0);
 			if (err < 0)
-				err = ac97_reset_wait(ac97, HZ/2, 1);
+				err = ac97_reset_wait(ac97,
+						      msecs_to_jiffies(500), 1);
 		}
 		if (err < 0) {
 			snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);
@@ -2104,7 +2105,7 @@
 		}
 		/* nothing should be in powerdown mode */
 		snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);
-		end_time = jiffies + (HZ / 10);
+		end_time = jiffies + msecs_to_jiffies(100);
 		do {
 			if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
 				goto __ready_ok;
@@ -2136,7 +2137,7 @@
 		udelay(100);
 		/* nothing should be in powerdown mode */
 		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
-		end_time = jiffies + (HZ / 10);
+		end_time = jiffies + msecs_to_jiffies(100);
 		do {
 			if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
 				goto __ready_ok;
@@ -2354,7 +2355,8 @@
 		 * (for avoiding loud click noises for many (OSS) apps
 		 *  that open/close frequently)
 		 */
-		schedule_delayed_work(&ac97->power_work, HZ*2);
+		schedule_delayed_work(&ac97->power_work,
+				      msecs_to_jiffies(2000));
 	else {
 		cancel_delayed_work(&ac97->power_work);
 		update_power_regs(ac97);
@@ -2436,7 +2438,7 @@
 /*
  * restore ac97 status
  */
-void snd_ac97_restore_status(struct snd_ac97 *ac97)
+static void snd_ac97_restore_status(struct snd_ac97 *ac97)
 {
 	int i;
 
@@ -2457,7 +2459,7 @@
 /*
  * restore IEC958 status
  */
-void snd_ac97_restore_iec958(struct snd_ac97 *ac97)
+static void snd_ac97_restore_iec958(struct snd_ac97 *ac97)
 {
 	if (ac97->ext_id & AC97_EI_SPDIF) {
 		if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) {
@@ -2494,7 +2496,10 @@
 
 	snd_ac97_write(ac97, AC97_POWERDOWN, 0);
 	if (! (ac97->flags & AC97_DEFAULT_POWER_OFF)) {
-		snd_ac97_write(ac97, AC97_RESET, 0);
+		if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
+			snd_ac97_write(ac97, AC97_RESET, 0);
+		else if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
+			snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
 		udelay(100);
 		snd_ac97_write(ac97, AC97_POWERDOWN, 0);
 	}
diff -ruN linux-2.6.22/sound/pci/ac97/ac97_patch.c linux-2.6.22-alsa/sound/pci/ac97/ac97_patch.c
--- linux-2.6.22/sound/pci/ac97/ac97_patch.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ac97/ac97_patch.c	2007-09-01 20:55:22.000000000 +0200
@@ -1880,14 +1880,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1888_lohpsel_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_ac97_ad1888_lohpsel_info	snd_ctl_boolean_mono_info
 
 static int snd_ac97_ad1888_lohpsel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -2186,15 +2179,7 @@
 	return 0;
 }
 
-static int snd_ac97_ad1986_bool_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_ac97_ad1986_bool_info	snd_ctl_boolean_mono_info
 
 static int snd_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol,
 				       struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/pci/ali5451/ali5451.c linux-2.6.22-alsa/sound/pci/ali5451/ali5451.c
--- linux-2.6.22/sound/pci/ali5451/ali5451.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ali5451/ali5451.c	2007-09-02 13:30:43.000000000 +0200
@@ -239,7 +239,7 @@
 
 
 struct snd_ali {
-	unsigned long	irq;
+	int		irq;
 	unsigned long	port;
 	unsigned char	revision;
 
@@ -731,8 +731,7 @@
 		return;
 	}
 
-	count = 0;
-	while (count++ <= 50000) {
+	for (count = 0; count <= 50000; count++) {
 		snd_ali_delay(codec, 6);
 		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
 		R2 = bval & 0x1F;
@@ -1805,15 +1804,7 @@
 .info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \
 .put = snd_ali5451_spdif_put, .private_value = value}
 
-static int snd_ali5451_spdif_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_ali5451_spdif_info		snd_ctl_boolean_mono_info
 
 static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
@@ -2343,7 +2334,7 @@
 	strcpy(card->driver, "ALI5451");
 	strcpy(card->shortname, "ALI 5451");
 	
-	sprintf(card->longname, "%s at 0x%lx, irq %li",
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
 		card->shortname, codec->port, codec->irq);
 
 	snd_ali_printk("register card.\n");
diff -ruN linux-2.6.22/sound/pci/ali5451/ali5451.c.orig linux-2.6.22-alsa/sound/pci/ali5451/ali5451.c.orig
--- linux-2.6.22/sound/pci/ali5451/ali5451.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/ali5451/ali5451.c.orig	2007-09-01 20:55:23.000000000 +0200
@@ -0,0 +1,2381 @@
+/*
+ *  Matt Wu <Matt_Wu@acersoftech.com.cn>
+ *  Apr 26, 2001
+ *  Routines for control of ALi pci audio M5451
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public Lcodecnse as published by
+ *   the Free Software Foundation; either version 2 of the Lcodecnse, 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 Lcodecnse for more details.
+ *
+ *   You should have received a copy of the GNU General Public Lcodecnse
+ *   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/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
+MODULE_DESCRIPTION("ALI M5451");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}");
+
+static int index = SNDRV_DEFAULT_IDX1;	/* Index */
+static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
+static int pcm_channels = 32;
+static int spdif;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio.");
+module_param(pcm_channels, int, 0444);
+MODULE_PARM_DESC(pcm_channels, "PCM Channels");
+module_param(spdif, bool, 0444);
+MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
+
+/* just for backward compatibility */
+static int enable;
+module_param(enable, bool, 0444);
+
+
+/*
+ *  Debug part definitions
+ */
+
+/* #define ALI_DEBUG */
+
+#ifdef ALI_DEBUG
+#define snd_ali_printk(format, args...) printk(KERN_DEBUG format, ##args);
+#else
+#define snd_ali_printk(format, args...)
+#endif
+
+/*
+ *  Constants definition
+ */
+
+#define DEVICE_ID_ALI5451	((PCI_VENDOR_ID_AL<<16)|PCI_DEVICE_ID_AL_M5451)
+
+
+#define ALI_CHANNELS		32
+
+#define ALI_PCM_IN_CHANNEL	31
+#define ALI_SPDIF_IN_CHANNEL	19
+#define ALI_SPDIF_OUT_CHANNEL	15
+#define ALI_CENTER_CHANNEL	24
+#define ALI_LEF_CHANNEL		23
+#define ALI_SURR_LEFT_CHANNEL	26
+#define ALI_SURR_RIGHT_CHANNEL	25
+#define ALI_MODEM_IN_CHANNEL    21
+#define ALI_MODEM_OUT_CHANNEL   20
+
+#define	SNDRV_ALI_VOICE_TYPE_PCM	01
+#define SNDRV_ALI_VOICE_TYPE_OTH	02
+
+#define	ALI_5451_V02		0x02
+
+/*
+ *  Direct Registers
+ */
+
+#define ALI_LEGACY_DMAR0        0x00  /* ADR0 */
+#define ALI_LEGACY_DMAR4        0x04  /* CNT0 */
+#define ALI_LEGACY_DMAR11       0x0b  /* MOD  */
+#define ALI_LEGACY_DMAR15       0x0f  /* MMR  */
+#define ALI_MPUR0		0x20
+#define ALI_MPUR1		0x21
+#define ALI_MPUR2		0x22
+#define ALI_MPUR3		0x23
+
+#define	ALI_AC97_WRITE		0x40
+#define ALI_AC97_READ		0x44
+
+#define ALI_SCTRL		0x48
+#define   ALI_SPDIF_OUT_ENABLE		0x20
+#define   ALI_SCTRL_LINE_IN2		(1 << 9)
+#define   ALI_SCTRL_GPIO_IN2		(1 << 13)
+#define   ALI_SCTRL_LINE_OUT_EN 	(1 << 20)
+#define   ALI_SCTRL_GPIO_OUT_EN 	(1 << 23)
+#define   ALI_SCTRL_CODEC1_READY	(1 << 24)
+#define   ALI_SCTRL_CODEC2_READY	(1 << 25)
+#define ALI_AC97_GPIO		0x4c
+#define   ALI_AC97_GPIO_ENABLE		0x8000
+#define   ALI_AC97_GPIO_DATA_SHIFT	16
+#define ALI_SPDIF_CS		0x70
+#define ALI_SPDIF_CTRL		0x74
+#define   ALI_SPDIF_IN_FUNC_ENABLE	0x02
+#define   ALI_SPDIF_IN_CH_STATUS	0x40
+#define   ALI_SPDIF_OUT_CH_STATUS	0xbf
+#define ALI_START		0x80
+#define ALI_STOP		0x84
+#define ALI_CSPF		0x90
+#define ALI_AINT		0x98
+#define ALI_GC_CIR		0xa0
+	#define ENDLP_IE		0x00001000
+	#define MIDLP_IE		0x00002000
+#define ALI_AINTEN		0xa4
+#define ALI_VOLUME		0xa8
+#define ALI_SBDELTA_DELTA_R     0xac
+#define ALI_MISCINT		0xb0
+	#define ADDRESS_IRQ		0x00000020
+	#define TARGET_REACHED		0x00008000
+	#define MIXER_OVERFLOW		0x00000800
+	#define MIXER_UNDERFLOW		0x00000400
+	#define GPIO_IRQ		0x01000000
+#define ALI_SBBL_SBCL           0xc0
+#define ALI_SBCTRL_SBE2R_SBDD   0xc4
+#define ALI_STIMER		0xc8
+#define ALI_GLOBAL_CONTROL	0xd4
+#define   ALI_SPDIF_OUT_SEL_PCM		0x00000400 /* bit 10 */
+#define   ALI_SPDIF_IN_SUPPORT		0x00000800 /* bit 11 */
+#define   ALI_SPDIF_OUT_CH_ENABLE	0x00008000 /* bit 15 */
+#define   ALI_SPDIF_IN_CH_ENABLE	0x00080000 /* bit 19 */
+#define   ALI_PCM_IN_ENABLE		0x80000000 /* bit 31 */
+
+#define ALI_CSO_ALPHA_FMS	0xe0
+#define ALI_LBA			0xe4
+#define ALI_ESO_DELTA		0xe8
+#define ALI_GVSEL_PAN_VOC_CTRL_EC	0xf0
+#define ALI_EBUF1		0xf4
+#define ALI_EBUF2		0xf8
+
+#define ALI_REG(codec, x) ((codec)->port + x)
+
+#define MAX_CODECS 2
+
+
+struct snd_ali;
+struct snd_ali_voice;
+
+struct snd_ali_channel_control {
+	/* register data */
+	struct REGDATA {
+		unsigned int start;
+		unsigned int stop;
+		unsigned int aint;
+		unsigned int ainten;
+	} data;
+		
+	/* register addresses */
+	struct REGS {
+		unsigned int start;
+		unsigned int stop;
+		unsigned int aint;
+		unsigned int ainten;
+		unsigned int ac97read;
+		unsigned int ac97write;
+	} regs;
+
+};
+
+struct snd_ali_voice {
+	unsigned int number;
+	unsigned int use :1,
+		pcm :1,
+		midi :1,
+		mode :1,
+		synth :1,
+		running :1;
+
+	/* PCM data */
+	struct snd_ali *codec;
+	struct snd_pcm_substream *substream;
+	struct snd_ali_voice *extra;
+	
+	int eso;                /* final ESO value for channel */
+	int count;              /* runtime->period_size */
+
+	/* --- */
+
+	void *private_data;
+	void (*private_free)(void *private_data);
+};
+
+
+struct snd_alidev {
+
+	struct snd_ali_voice voices[ALI_CHANNELS];	
+
+	unsigned int	chcnt;			/* num of opened channels */
+	unsigned int	chmap;			/* bitmap for opened channels */
+	unsigned int synthcount;
+
+};
+
+
+#define ALI_GLOBAL_REGS		56
+#define ALI_CHANNEL_REGS	8
+struct snd_ali_image {
+	u32 regs[ALI_GLOBAL_REGS];
+	u32 channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];
+};
+
+
+struct snd_ali {
+	int		irq;
+	unsigned long	port;
+	unsigned char	revision;
+
+	unsigned int hw_initialized :1;
+	unsigned int spdif_support :1;
+
+	struct pci_dev	*pci;
+	struct pci_dev	*pci_m1533;
+	struct pci_dev	*pci_m7101;
+
+	struct snd_card	*card;
+	struct snd_pcm	*pcm[MAX_CODECS];
+	struct snd_alidev	synth;
+	struct snd_ali_channel_control chregs;
+
+	/* S/PDIF Mask */
+	unsigned int	spdif_mask;
+
+	unsigned int spurious_irq_count;
+	unsigned int spurious_irq_max_delta;
+
+	unsigned int num_of_codecs;
+
+	struct snd_ac97_bus *ac97_bus;
+	struct snd_ac97 *ac97[MAX_CODECS];
+	unsigned short	ac97_ext_id;
+	unsigned short	ac97_ext_status;
+
+	spinlock_t	reg_lock;
+	spinlock_t	voice_alloc;
+
+#ifdef CONFIG_PM
+	struct snd_ali_image *image;
+#endif
+};
+
+static struct pci_device_id snd_ali_ids[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
+	{0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ali_ids);
+
+static void snd_ali_clear_voices(struct snd_ali *, unsigned int, unsigned int);
+static unsigned short snd_ali_codec_peek(struct snd_ali *, int, unsigned short);
+static void snd_ali_codec_poke(struct snd_ali *, int, unsigned short,
+			       unsigned short);
+
+/*
+ *  AC97 ACCESS
+ */
+
+static inline unsigned int snd_ali_5451_peek(struct snd_ali *codec,
+					     unsigned int port)
+{
+	return (unsigned int)inl(ALI_REG(codec, port)); 
+}
+
+static inline void snd_ali_5451_poke(struct snd_ali *codec,
+				     unsigned int port,
+				     unsigned int val)
+{
+	outl((unsigned int)val, ALI_REG(codec, port));
+}
+
+static int snd_ali_codec_ready(struct snd_ali *codec,
+			       unsigned int port)
+{
+	unsigned long end_time;
+	unsigned int res;
+	
+	end_time = jiffies + msecs_to_jiffies(250);
+	do {
+		res = snd_ali_5451_peek(codec,port);
+		if (!(res & 0x8000))
+			return 0;
+		schedule_timeout_uninterruptible(1);
+	} while (time_after_eq(end_time, jiffies));
+	snd_ali_5451_poke(codec, port, res & ~0x8000);
+	snd_printdd("ali_codec_ready: codec is not ready.\n ");
+	return -EIO;
+}
+
+static int snd_ali_stimer_ready(struct snd_ali *codec)
+{
+	unsigned long end_time;
+	unsigned long dwChk1,dwChk2;
+	
+	dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
+	dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+
+	end_time = jiffies + msecs_to_jiffies(250);
+	do {
+		dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
+		if (dwChk2 != dwChk1)
+			return 0;
+		schedule_timeout_uninterruptible(1);
+	} while (time_after_eq(end_time, jiffies));
+	snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n");
+	return -EIO;
+}
+
+static void snd_ali_codec_poke(struct snd_ali *codec,int secondary,
+			       unsigned short reg,
+			       unsigned short val)
+{
+	unsigned int dwVal;
+	unsigned int port;
+
+	if (reg >= 0x80) {
+		snd_printk(KERN_ERR "ali_codec_poke: reg(%xh) invalid.\n", reg);
+		return;
+	}
+
+	port = codec->chregs.regs.ac97write;
+
+	if (snd_ali_codec_ready(codec, port) < 0)
+		return;
+	if (snd_ali_stimer_ready(codec) < 0)
+		return;
+
+	dwVal  = (unsigned int) (reg & 0xff);
+	dwVal |= 0x8000 | (val << 16);
+	if (secondary)
+		dwVal |= 0x0080;
+	if (codec->revision == ALI_5451_V02)
+		dwVal |= 0x0100;
+
+	snd_ali_5451_poke(codec, port, dwVal);
+
+	return ;
+}
+
+static unsigned short snd_ali_codec_peek(struct snd_ali *codec,
+					 int secondary,
+					 unsigned short reg)
+{
+	unsigned int dwVal;
+	unsigned int port;
+
+	if (reg >= 0x80) {
+		snd_printk(KERN_ERR "ali_codec_peek: reg(%xh) invalid.\n", reg);
+		return ~0;
+	}
+
+	port = codec->chregs.regs.ac97read;
+
+	if (snd_ali_codec_ready(codec, port) < 0)
+		return ~0;
+	if (snd_ali_stimer_ready(codec) < 0)
+		return ~0;
+
+	dwVal  = (unsigned int) (reg & 0xff);
+	dwVal |= 0x8000;				/* bit 15*/
+	if (secondary)
+		dwVal |= 0x0080;
+
+	snd_ali_5451_poke(codec, port, dwVal);
+
+	if (snd_ali_stimer_ready(codec) < 0)
+		return ~0;
+	if (snd_ali_codec_ready(codec, port) < 0)
+		return ~0;
+	
+	return (snd_ali_5451_peek(codec, port) & 0xffff0000) >> 16;
+}
+
+static void snd_ali_codec_write(struct snd_ac97 *ac97,
+				unsigned short reg,
+				unsigned short val )
+{
+	struct snd_ali *codec = ac97->private_data;
+
+	snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
+	if (reg == AC97_GPIO_STATUS) {
+		outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE,
+		     ALI_REG(codec, ALI_AC97_GPIO));
+		return;
+	}
+	snd_ali_codec_poke(codec, ac97->num, reg, val);
+	return ;
+}
+
+
+static unsigned short snd_ali_codec_read(struct snd_ac97 *ac97,
+					 unsigned short reg)
+{
+	struct snd_ali *codec = ac97->private_data;
+
+	snd_ali_printk("codec_read reg=%xh.\n", reg);
+	return snd_ali_codec_peek(codec, ac97->num, reg);
+}
+
+/*
+ *	AC97 Reset
+ */
+
+static int snd_ali_reset_5451(struct snd_ali *codec)
+{
+	struct pci_dev *pci_dev;
+	unsigned short wCount, wReg;
+	unsigned int   dwVal;
+	
+	pci_dev = codec->pci_m1533;
+	if (pci_dev) {
+		pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+		pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+		udelay(5000);
+		pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+		pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+		udelay(5000);
+	}
+	
+	pci_dev = codec->pci;
+	pci_read_config_dword(pci_dev, 0x44, &dwVal);
+	pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000);
+	udelay(500);
+	pci_read_config_dword(pci_dev, 0x44, &dwVal);
+	pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
+	udelay(5000);
+	
+	wCount = 200;
+	while(wCount--) {
+		wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
+		if ((wReg & 0x000f) == 0x000f)
+			return 0;
+		udelay(5000);
+	}
+
+	/* non-fatal if you have a non PM capable codec */
+	/* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */
+	return 0;
+}
+
+#ifdef CODEC_RESET
+
+static int snd_ali_reset_codec(struct snd_ali *codec)
+{
+	struct pci_dev *pci_dev;
+	unsigned char bVal;
+	unsigned int   dwVal;
+	unsigned short wCount, wReg;
+
+	pci_dev = codec->pci_m1533;
+	
+	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+	pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
+	udelay(5000);
+	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
+	pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
+	udelay(5000);
+
+	bVal = inb(ALI_REG(codec,ALI_SCTRL));
+	bVal |= 0x02;
+	outb(ALI_REG(codec,ALI_SCTRL),bVal);
+	udelay(5000);
+	bVal = inb(ALI_REG(codec,ALI_SCTRL));
+	bVal &= 0xfd;
+	outb(ALI_REG(codec,ALI_SCTRL),bVal);
+	udelay(15000);
+
+	wCount = 200;
+	while (wCount--) {
+		wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
+		if ((wReg & 0x000f) == 0x000f)
+			return 0;
+		udelay(5000);
+	}
+	return -1;
+}
+
+#endif
+
+/*
+ *  ALI 5451 Controller
+ */
+
+static void snd_ali_enable_special_channel(struct snd_ali *codec,
+					   unsigned int channel)
+{
+	unsigned long dwVal;
+
+	dwVal  = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	dwVal |= 1 << (channel & 0x0000001f);
+	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_disable_special_channel(struct snd_ali *codec,
+					    unsigned int channel)
+{
+	unsigned long dwVal;
+
+	dwVal  = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	dwVal &= ~(1 << (channel & 0x0000001f));
+	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+}
+
+static void snd_ali_enable_address_interrupt(struct snd_ali *codec)
+{
+	unsigned int gc;
+
+	gc  = inl(ALI_REG(codec, ALI_GC_CIR));
+	gc |= ENDLP_IE;
+	gc |= MIDLP_IE;
+	outl( gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+static void snd_ali_disable_address_interrupt(struct snd_ali *codec)
+{
+	unsigned int gc;
+
+	gc  = inl(ALI_REG(codec, ALI_GC_CIR));
+	gc &= ~ENDLP_IE;
+	gc &= ~MIDLP_IE;
+	outl(gc, ALI_REG(codec, ALI_GC_CIR));
+}
+
+#if 0 /* not used */
+static void snd_ali_enable_voice_irq(struct snd_ali *codec,
+				     unsigned int channel)
+{
+	unsigned int mask;
+	struct snd_ali_channel_control *pchregs = &(codec->chregs);
+
+	snd_ali_printk("enable_voice_irq channel=%d\n",channel);
+	
+	mask = 1 << (channel & 0x1f);
+	pchregs->data.ainten  = inl(ALI_REG(codec, pchregs->regs.ainten));
+	pchregs->data.ainten |= mask;
+	outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten));
+}
+#endif
+
+static void snd_ali_disable_voice_irq(struct snd_ali *codec,
+				      unsigned int channel)
+{
+	unsigned int mask;
+	struct snd_ali_channel_control *pchregs = &(codec->chregs);
+
+	snd_ali_printk("disable_voice_irq channel=%d\n",channel);
+
+	mask = 1 << (channel & 0x1f);
+	pchregs->data.ainten  = inl(ALI_REG(codec, pchregs->regs.ainten));
+	pchregs->data.ainten &= ~mask;
+	outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten));
+}
+
+static int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel)
+{
+	unsigned int idx =  channel & 0x1f;
+
+	if (codec->synth.chcnt >= ALI_CHANNELS){
+		snd_printk(KERN_ERR
+			   "ali_alloc_pcm_channel: no free channels.\n");
+		return -1;
+	}
+
+	if (!(codec->synth.chmap & (1 << idx))) {
+		codec->synth.chmap |= 1 << idx;
+		codec->synth.chcnt++;
+		snd_ali_printk("alloc_pcm_channel no. %d.\n",idx);
+		return idx;
+	}
+	return -1;
+}
+
+static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
+{
+	int idx;
+	int result = -1;
+
+	snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm");
+
+	/* recording */
+	if (rec) {
+		if (codec->spdif_support &&
+		    (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) &
+		     ALI_SPDIF_IN_SUPPORT))
+			idx = ALI_SPDIF_IN_CHANNEL;
+		else
+			idx = ALI_PCM_IN_CHANNEL;
+
+		result = snd_ali_alloc_pcm_channel(codec, idx);
+		if (result >= 0)
+			return result;
+		else {
+			snd_printk(KERN_ERR "ali_find_free_channel: "
+				   "record channel is busy now.\n");
+			return -1;
+		}
+	}
+
+	/* playback... */
+	if (codec->spdif_support &&
+	    (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) &
+	     ALI_SPDIF_OUT_CH_ENABLE)) {
+		idx = ALI_SPDIF_OUT_CHANNEL;
+		result = snd_ali_alloc_pcm_channel(codec, idx);
+		if (result >= 0)
+			return result;
+		else
+			snd_printk(KERN_ERR "ali_find_free_channel: "
+				   "S/PDIF out channel is in busy now.\n");
+	}
+
+	for (idx = 0; idx < ALI_CHANNELS; idx++) {
+		result = snd_ali_alloc_pcm_channel(codec, idx);
+		if (result >= 0)
+			return result;
+	}
+	snd_printk(KERN_ERR "ali_find_free_channel: no free channels.\n");
+	return -1;
+}
+
+static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
+{
+	unsigned int idx = channel & 0x0000001f;
+
+	snd_ali_printk("free_channel_pcm channel=%d\n",channel);
+
+	if (channel < 0 || channel >= ALI_CHANNELS)
+		return;
+
+	if (!(codec->synth.chmap & (1 << idx))) {
+		snd_printk(KERN_ERR "ali_free_channel_pcm: "
+			   "channel %d is not in use.\n", channel);
+		return;
+	} else {
+		codec->synth.chmap &= ~(1 << idx);
+		codec->synth.chcnt--;
+	}
+}
+
+#if 0 /* not used */
+static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+	
+	snd_ali_printk("start_voice: channel=%d\n",channel);
+	outl(mask, ALI_REG(codec,codec->chregs.regs.start));
+}
+#endif
+
+static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
+{
+	unsigned int mask = 1 << (channel & 0x1f);
+
+	snd_ali_printk("stop_voice: channel=%d\n",channel);
+	outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
+}
+
+/*
+ *    S/PDIF Part
+ */
+
+static void snd_ali_delay(struct snd_ali *codec,int interval)
+{
+	unsigned long  begintimer,currenttimer;
+
+	begintimer   = inl(ALI_REG(codec, ALI_STIMER));
+	currenttimer = inl(ALI_REG(codec, ALI_STIMER));
+
+	while (currenttimer < begintimer + interval) {
+		if (snd_ali_stimer_ready(codec) < 0)
+			break;
+		currenttimer = inl(ALI_REG(codec,  ALI_STIMER));
+		cpu_relax();
+	}
+}
+
+static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
+{
+	u16 wval;
+	u16 count = 0;
+	u8  bval, R1 = 0, R2;
+
+	bval  = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1));
+	bval |= 0x1F;
+	outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL + 1));
+
+	while ((R1 < 0x0b || R1 > 0x0e) && R1 != 0x12 && count <= 50000) {
+		count ++;
+		snd_ali_delay(codec, 6);
+		bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1));
+		R1 = bval & 0x1F;
+	}
+
+	if (count > 50000) {
+		snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n");
+		return;
+	}
+
+	for (count = 0; count <= 50000; count++) {
+		snd_ali_delay(codec, 6);
+		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+		R2 = bval & 0x1F;
+		if (R2 != R1)
+			R1 = R2;
+		else
+			break;
+	}
+
+	if (count > 50000) {
+		snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n");
+		return;
+	}
+
+	if (R2 >= 0x0b && R2 <= 0x0e) {
+		wval  = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2));
+		wval &= 0xe0f0;
+		wval |= (0x09 << 8) | 0x05;
+		outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)) & 0xf0;
+		outb(bval | 0x02, ALI_REG(codec, ALI_SPDIF_CS + 3));
+	} else if (R2 == 0x12) {
+		wval  = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2));
+		wval &= 0xe0f0;
+		wval |= (0x0e << 8) | 0x08;
+		outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)) & 0xf0;
+		outb(bval | 0x03, ALI_REG(codec, ALI_SPDIF_CS + 3));
+	}
+}
+
+static unsigned int snd_ali_get_spdif_in_rate(struct snd_ali *codec)
+{
+	u32	dwRate;
+	u8	bval;
+
+	bval  = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	bval &= 0x7f;
+	bval |= 0x40;
+	outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+	snd_ali_detect_spdif_rate(codec);
+
+	bval  = inb(ALI_REG(codec, ALI_SPDIF_CS + 3));
+	bval &= 0x0f;
+
+	switch (bval) {
+	case 0: dwRate = 44100; break;
+	case 1: dwRate = 48000; break;
+	case 2: dwRate = 32000; break;
+	default: dwRate = 0; break;
+	}
+
+	return dwRate;
+}
+
+static void snd_ali_enable_spdif_in(struct snd_ali *codec)
+{	
+	unsigned int dwVal;
+
+	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	dwVal |= ALI_SPDIF_IN_SUPPORT;
+	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+	dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	dwVal |= 0x02;
+	outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+
+	snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_in(struct snd_ali *codec)
+{
+	unsigned int dwVal;
+	
+	dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	dwVal &= ~ALI_SPDIF_IN_SUPPORT;
+	outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	
+	snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL);	
+}
+
+
+static void snd_ali_set_spdif_out_rate(struct snd_ali *codec, unsigned int rate)
+{
+	unsigned char  bVal;
+	unsigned int  dwRate;
+	
+	switch (rate) {
+	case 32000: dwRate = 0x300; break;
+	case 48000: dwRate = 0x200; break;
+	default: dwRate = 0; break;
+	}
+	
+	bVal  = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	bVal &= (unsigned char)(~(1<<6));
+	
+	bVal |= 0x80;		/* select right */
+	outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+	outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2));
+	
+	bVal &= ~0x80;	/* select left */
+	outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL));
+	outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2));
+}
+
+static void snd_ali_enable_spdif_out(struct snd_ali *codec)
+{
+	unsigned short wVal;
+	unsigned char bVal;
+        struct pci_dev *pci_dev;
+
+        pci_dev = codec->pci_m1533;
+        if (pci_dev == NULL)
+                return;
+        pci_read_config_byte(pci_dev, 0x61, &bVal);
+        bVal |= 0x40;
+        pci_write_config_byte(pci_dev, 0x61, bVal);
+        pci_read_config_byte(pci_dev, 0x7d, &bVal);
+        bVal |= 0x01;
+        pci_write_config_byte(pci_dev, 0x7d, bVal);
+
+        pci_read_config_byte(pci_dev, 0x7e, &bVal);
+        bVal &= (~0x20);
+        bVal |= 0x10;
+        pci_write_config_byte(pci_dev, 0x7e, bVal);
+
+	bVal = inb(ALI_REG(codec, ALI_SCTRL));
+	outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
+
+	bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL));
+	outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL));
+   
+	wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	wVal |= ALI_SPDIF_OUT_SEL_PCM;
+	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	snd_ali_disable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_enable_spdif_chnout(struct snd_ali *codec)
+{
+	unsigned short wVal;
+
+	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+   	wVal &= ~ALI_SPDIF_OUT_SEL_PCM;
+   	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+/*
+	wVal = inw(ALI_REG(codec, ALI_SPDIF_CS));
+	if (flag & ALI_SPDIF_OUT_NON_PCM)
+   		wVal |= 0x0002;
+	else	
+		wVal &= (~0x0002);
+   	outw(wVal, ALI_REG(codec, ALI_SPDIF_CS));
+*/
+	snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_chnout(struct snd_ali *codec)
+{
+	unsigned short wVal;
+
+  	wVal  = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL));
+   	wVal |= ALI_SPDIF_OUT_SEL_PCM;
+   	outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+
+	snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL);
+}
+
+static void snd_ali_disable_spdif_out(struct snd_ali *codec)
+{
+	unsigned char  bVal;
+
+	bVal = inb(ALI_REG(codec, ALI_SCTRL));
+	outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL));
+
+	snd_ali_disable_spdif_chnout(codec);
+}
+
+static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
+{
+	struct snd_ali_voice *pvoice;
+	struct snd_pcm_runtime *runtime;
+	struct snd_ali_channel_control *pchregs;
+	unsigned int old, mask;
+#ifdef ALI_DEBUG
+	unsigned int temp, cspf;
+#endif
+
+	pchregs = &(codec->chregs);
+
+	/* check if interrupt occurred for channel */
+	old  = pchregs->data.aint;
+	mask = 1U << (channel & 0x1f);
+
+	if (!(old & mask))
+		return;
+
+	pvoice = &codec->synth.voices[channel];
+	runtime = pvoice->substream->runtime;
+
+	udelay(100);
+	spin_lock(&codec->reg_lock);
+
+	if (pvoice->pcm && pvoice->substream) {
+		/* pcm interrupt */
+#ifdef ALI_DEBUG
+		outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR));
+		temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+		cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask;
+#endif
+		if (pvoice->running) {
+			snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",
+				       (u16)temp, cspf);
+			spin_unlock(&codec->reg_lock);
+			snd_pcm_period_elapsed(pvoice->substream);
+			spin_lock(&codec->reg_lock);
+		} else {
+			snd_ali_stop_voice(codec, channel);
+			snd_ali_disable_voice_irq(codec, channel);
+		}	
+	} else if (codec->synth.voices[channel].synth) {
+		/* synth interrupt */
+	} else if (codec->synth.voices[channel].midi) {
+		/* midi interrupt */
+	} else {
+		/* unknown interrupt */
+		snd_ali_stop_voice(codec, channel);
+		snd_ali_disable_voice_irq(codec, channel);
+	}
+	spin_unlock(&codec->reg_lock);
+	outl(mask,ALI_REG(codec,pchregs->regs.aint));
+	pchregs->data.aint = old & (~mask);
+}
+
+static irqreturn_t snd_ali_card_interrupt(int irq, void *dev_id)
+{
+	struct snd_ali 	*codec = dev_id;
+	int channel;
+	unsigned int audio_int;
+	struct snd_ali_channel_control *pchregs;
+
+	if (codec == NULL || !codec->hw_initialized)
+		return IRQ_NONE;
+
+	audio_int = inl(ALI_REG(codec, ALI_MISCINT));
+	if (!audio_int)
+		return IRQ_NONE;
+
+	pchregs = &(codec->chregs);
+	if (audio_int & ADDRESS_IRQ) {
+		/* get interrupt status for all channels */
+		pchregs->data.aint = inl(ALI_REG(codec, pchregs->regs.aint));
+		for (channel = 0; channel < ALI_CHANNELS; channel++)
+			snd_ali_update_ptr(codec, channel);
+	}
+	outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW),
+		ALI_REG(codec, ALI_MISCINT));
+
+	return IRQ_HANDLED;
+}
+
+
+static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
+						 int type, int rec, int channel)
+{
+	struct snd_ali_voice *pvoice;
+	int idx;
+
+	snd_ali_printk("alloc_voice: type=%d rec=%d\n", type, rec);
+
+	spin_lock_irq(&codec->voice_alloc);
+	if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
+		idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) :
+			snd_ali_find_free_channel(codec,rec);
+		if (idx < 0) {
+			snd_printk(KERN_ERR "ali_alloc_voice: err.\n");
+			spin_unlock_irq(&codec->voice_alloc);
+			return NULL;
+		}
+		pvoice = &(codec->synth.voices[idx]);
+		pvoice->codec = codec;
+		pvoice->use = 1;
+		pvoice->pcm = 1;
+		pvoice->mode = rec;
+		spin_unlock_irq(&codec->voice_alloc);
+		return pvoice;
+	}
+	spin_unlock_irq(&codec->voice_alloc);
+	return NULL;
+}
+
+
+static void snd_ali_free_voice(struct snd_ali * codec,
+			       struct snd_ali_voice *pvoice)
+{
+	void (*private_free)(void *);
+	void *private_data;
+
+	snd_ali_printk("free_voice: channel=%d\n",pvoice->number);
+	if (pvoice == NULL || !pvoice->use)
+		return;
+	snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
+	spin_lock_irq(&codec->voice_alloc);
+	private_free = pvoice->private_free;
+	private_data = pvoice->private_data;
+	pvoice->private_free = NULL;
+	pvoice->private_data = NULL;
+	if (pvoice->pcm)
+		snd_ali_free_channel_pcm(codec, pvoice->number);
+	pvoice->use = pvoice->pcm = pvoice->synth = 0;
+	pvoice->substream = NULL;
+	spin_unlock_irq(&codec->voice_alloc);
+	if (private_free)
+		private_free(private_data);
+}
+
+
+static void snd_ali_clear_voices(struct snd_ali *codec,
+				 unsigned int v_min,
+				 unsigned int v_max)
+{
+	unsigned int i;
+
+	for (i = v_min; i <= v_max; i++) {
+		snd_ali_stop_voice(codec, i);
+		snd_ali_disable_voice_irq(codec, i);
+	}
+}
+
+static void snd_ali_write_voice_regs(struct snd_ali *codec,
+			 unsigned int Channel,
+			 unsigned int LBA,
+			 unsigned int CSO,
+			 unsigned int ESO,
+			 unsigned int DELTA,
+			 unsigned int ALPHA_FMS,
+			 unsigned int GVSEL,
+			 unsigned int PAN,
+			 unsigned int VOL,
+			 unsigned int CTRL,
+			 unsigned int EC)
+{
+	unsigned int ctlcmds[4];
+	
+	outb((unsigned char)(Channel & 0x001f), ALI_REG(codec, ALI_GC_CIR));
+
+	ctlcmds[0] =  (CSO << 16) | (ALPHA_FMS & 0x0000ffff);
+	ctlcmds[1] =  LBA;
+	ctlcmds[2] =  (ESO << 16) | (DELTA & 0x0ffff);
+	ctlcmds[3] =  (GVSEL << 31) |
+		      ((PAN & 0x0000007f) << 24) |
+		      ((VOL & 0x000000ff) << 16) |
+		      ((CTRL & 0x0000000f) << 12) |
+		      (EC & 0x00000fff);
+
+	outb(Channel, ALI_REG(codec, ALI_GC_CIR));
+
+	outl(ctlcmds[0], ALI_REG(codec, ALI_CSO_ALPHA_FMS));
+	outl(ctlcmds[1], ALI_REG(codec, ALI_LBA));
+	outl(ctlcmds[2], ALI_REG(codec, ALI_ESO_DELTA));
+	outl(ctlcmds[3], ALI_REG(codec, ALI_GVSEL_PAN_VOC_CTRL_EC));
+
+	outl(0x30000000, ALI_REG(codec, ALI_EBUF1));	/* Still Mode */
+	outl(0x30000000, ALI_REG(codec, ALI_EBUF2));	/* Still Mode */
+}
+
+static unsigned int snd_ali_convert_rate(unsigned int rate, int rec)
+{
+	unsigned int delta;
+
+	if (rate < 4000)
+		rate = 4000;
+	if (rate > 48000)
+		rate = 48000;
+
+	if (rec) {
+		if (rate == 44100)
+			delta = 0x116a;
+		else if (rate == 8000)
+			delta = 0x6000;
+		else if (rate == 48000)
+			delta = 0x1000;
+		else
+			delta = ((48000 << 12) / rate) & 0x0000ffff;
+	} else {
+		if (rate == 44100)
+			delta = 0xeb3;
+		else if (rate == 8000)
+			delta = 0x2ab;
+		else if (rate == 48000)
+			delta = 0x1000;
+		else 
+			delta = (((rate << 12) + rate) / 48000) & 0x0000ffff;
+	}
+
+	return delta;
+}
+
+static unsigned int snd_ali_control_mode(struct snd_pcm_substream *substream)
+{
+	unsigned int CTRL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/* set ctrl mode
+	   CTRL default: 8-bit (unsigned) mono, loop mode enabled
+	 */
+	CTRL = 0x00000001;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		CTRL |= 0x00000008;	/* 16-bit data */
+	if (!snd_pcm_format_unsigned(runtime->format))
+		CTRL |= 0x00000002;	/* signed data */
+	if (runtime->channels > 1)
+		CTRL |= 0x00000004;	/* stereo data */
+	return CTRL;
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_ali_trigger(struct snd_pcm_substream *substream,
+			       int cmd)
+				    
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_substream *s;
+	unsigned int what, whati, capture_flag;
+	struct snd_ali_voice *pvoice, *evoice;
+	unsigned int val;
+	int do_start;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		do_start = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		do_start = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	what = whati = capture_flag = 0;
+	snd_pcm_group_for_each_entry(s, substream) {
+		if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) {
+			pvoice = s->runtime->private_data;
+			evoice = pvoice->extra;
+			what |= 1 << (pvoice->number & 0x1f);
+			if (evoice == NULL)
+				whati |= 1 << (pvoice->number & 0x1f);
+			else {
+				whati |= 1 << (evoice->number & 0x1f);
+				what |= 1 << (evoice->number & 0x1f);
+			}
+			if (do_start) {
+				pvoice->running = 1;
+				if (evoice != NULL)
+					evoice->running = 1;
+			} else {
+				pvoice->running = 0;
+				if (evoice != NULL)
+					evoice->running = 0;
+			}
+			snd_pcm_trigger_done(s, substream);
+			if (pvoice->mode)
+				capture_flag = 1;
+		}
+	}
+	spin_lock(&codec->reg_lock);
+	if (!do_start)
+		outl(what, ALI_REG(codec, ALI_STOP));
+	val = inl(ALI_REG(codec, ALI_AINTEN));
+	if (do_start)
+		val |= whati;
+	else
+		val &= ~whati;
+	outl(val, ALI_REG(codec, ALI_AINTEN));
+	if (do_start)
+		outl(what, ALI_REG(codec, ALI_START));
+	snd_ali_printk("trigger: what=%xh whati=%xh\n", what, whati);
+	spin_unlock(&codec->reg_lock);
+
+	return 0;
+}
+
+static int snd_ali_playback_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	struct snd_ali_voice *evoice = pvoice->extra;
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+	
+	/* voice management */
+
+	if (params_buffer_size(hw_params) / 2 !=
+	    params_period_size(hw_params)) {
+		if (!evoice) {
+			evoice = snd_ali_alloc_voice(codec,
+						     SNDRV_ALI_VOICE_TYPE_PCM,
+						     0, -1);
+			if (!evoice)
+				return -ENOMEM;
+			pvoice->extra = evoice;
+			evoice->substream = substream;
+		}
+	} else {
+		if (evoice) {
+			snd_ali_free_voice(codec, evoice);
+			pvoice->extra = evoice = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static int snd_ali_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL;
+
+	snd_pcm_lib_free_pages(substream);
+	if (evoice) {
+		snd_ali_free_voice(codec, evoice);
+		pvoice->extra = NULL;
+	}
+	return 0;
+}
+
+static int snd_ali_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 snd_ali_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	struct snd_ali_voice *evoice = pvoice->extra;
+
+	unsigned int LBA;
+	unsigned int Delta;
+	unsigned int ESO;
+	unsigned int CTRL;
+	unsigned int GVSEL;
+	unsigned int PAN;
+	unsigned int VOL;
+	unsigned int EC;
+	
+	snd_ali_printk("playback_prepare ...\n");
+
+	spin_lock_irq(&codec->reg_lock);	
+	
+	/* set Delta (rate) value */
+	Delta = snd_ali_convert_rate(runtime->rate, 0);
+
+	if (pvoice->number == ALI_SPDIF_IN_CHANNEL || 
+	    pvoice->number == ALI_PCM_IN_CHANNEL)
+		snd_ali_disable_special_channel(codec, pvoice->number);
+	else if (codec->spdif_support &&
+		 (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) &
+		  ALI_SPDIF_OUT_CH_ENABLE)
+		 && pvoice->number == ALI_SPDIF_OUT_CHANNEL) {
+		snd_ali_set_spdif_out_rate(codec, runtime->rate);
+		Delta = 0x1000;
+	}
+	
+	/* set Loop Back Address */
+	LBA = runtime->dma_addr;
+
+	/* set interrupt count size */
+	pvoice->count = runtime->period_size;
+
+	/* set target ESO for channel */
+	pvoice->eso = runtime->buffer_size; 
+
+	snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",
+		       pvoice->eso, pvoice->count);
+
+	/* set ESO to capture first MIDLP interrupt */
+	ESO = pvoice->eso -1;
+	/* set ctrl mode */
+	CTRL = snd_ali_control_mode(substream);
+
+	GVSEL = 1;
+	PAN = 0;
+	VOL = 0;
+	EC = 0;
+	snd_ali_printk("playback_prepare:\n");
+	snd_ali_printk("ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",
+		       pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
+	snd_ali_write_voice_regs(codec,
+				 pvoice->number,
+				 LBA,
+				 0,	/* cso */
+				 ESO,
+				 Delta,
+				 0,	/* alpha */
+				 GVSEL,
+				 PAN,
+				 VOL,
+				 CTRL,
+				 EC);
+	if (evoice) {
+		evoice->count = pvoice->count;
+		evoice->eso = pvoice->count << 1;
+		ESO = evoice->eso - 1;
+		snd_ali_write_voice_regs(codec,
+					 evoice->number,
+					 LBA,
+					 0,	/* cso */
+					 ESO,
+					 Delta,
+					 0,	/* alpha */
+					 GVSEL,
+					 0x7f,
+					 0x3ff,
+					 CTRL,
+					 EC);
+	}
+	spin_unlock_irq(&codec->reg_lock);
+	return 0;
+}
+
+
+static int snd_ali_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	unsigned int LBA;
+	unsigned int Delta;
+	unsigned int ESO;
+	unsigned int CTRL;
+	unsigned int GVSEL;
+	unsigned int PAN;
+	unsigned int VOL;
+	unsigned int EC;
+	u8	 bValue;
+
+	spin_lock_irq(&codec->reg_lock);
+
+	snd_ali_printk("ali_prepare...\n");
+
+	snd_ali_enable_special_channel(codec,pvoice->number);
+
+	Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL ||
+		 pvoice->number == ALI_MODEM_OUT_CHANNEL) ? 
+		0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode);
+
+	/* Prepare capture intr channel */
+	if (pvoice->number == ALI_SPDIF_IN_CHANNEL) {
+
+		unsigned int rate;
+		
+		spin_unlock_irq(&codec->reg_lock);
+		if (codec->revision != ALI_5451_V02)
+			return -1;
+
+		rate = snd_ali_get_spdif_in_rate(codec);
+		if (rate == 0) {
+			snd_printk(KERN_WARNING "ali_capture_preapre: "
+				   "spdif rate detect err!\n");
+			rate = 48000;
+		}
+		spin_lock_irq(&codec->reg_lock);
+		bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
+		if (bValue & 0x10) {
+			outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
+			printk(KERN_WARNING "clear SPDIF parity error flag.\n");
+		}
+
+		if (rate != 48000)
+			Delta = ((rate << 12) / runtime->rate) & 0x00ffff;
+	}
+
+	/* set target ESO for channel  */
+	pvoice->eso = runtime->buffer_size; 
+
+	/* set interrupt count size  */
+	pvoice->count = runtime->period_size;
+
+	/* set Loop Back Address  */
+	LBA = runtime->dma_addr;
+
+	/* set ESO to capture first MIDLP interrupt  */
+	ESO = pvoice->eso - 1;
+	CTRL = snd_ali_control_mode(substream);
+	GVSEL = 0;
+	PAN = 0x00;
+	VOL = 0x00;
+	EC = 0;
+
+	snd_ali_write_voice_regs(    codec,
+				     pvoice->number,
+				     LBA,
+				     0,	/* cso */
+				     ESO,
+				     Delta,
+				     0,	/* alpha */
+				     GVSEL,
+				     PAN,
+				     VOL,
+				     CTRL,
+				     EC);
+
+	spin_unlock_irq(&codec->reg_lock);
+
+	return 0;
+}
+
+
+static snd_pcm_uframes_t
+snd_ali_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	unsigned int cso;
+
+	spin_lock(&codec->reg_lock);
+	if (!pvoice->running) {
+		spin_unlock(&codec->reg_lock);
+		return 0;
+	}
+	outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+	cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+	spin_unlock(&codec->reg_lock);
+	snd_ali_printk("playback pointer returned cso=%xh.\n", cso);
+
+	return cso;
+}
+
+
+static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	unsigned int cso;
+
+	spin_lock(&codec->reg_lock);
+	if (!pvoice->running) {
+		spin_unlock_irq(&codec->reg_lock);
+		return 0;
+	}
+	outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
+	cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
+	spin_unlock(&codec->reg_lock);
+
+	return cso;
+}
+
+static struct snd_pcm_hardware snd_ali_playback =
+{
+	.info =		(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+			 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_RESUME |
+			 SNDRV_PCM_INFO_SYNC_START),
+	.formats =	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+			 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(256*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static struct snd_pcm_hardware snd_ali_capture =
+{
+	.info =		(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+			 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_RESUME |
+			 SNDRV_PCM_INFO_SYNC_START),
+	.formats =	(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+			 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
+	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	struct snd_ali_voice *pvoice = runtime->private_data;
+	struct snd_ali *codec;
+
+	if (pvoice) {
+		codec = pvoice->codec;
+		snd_ali_free_voice(pvoice->codec, pvoice);
+	}
+}
+
+static int snd_ali_open(struct snd_pcm_substream *substream, int rec,
+			int channel, struct snd_pcm_hardware *phw)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ali_voice *pvoice;
+
+	pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec,
+				     channel);
+	if (!pvoice)
+		return -EAGAIN;
+
+	pvoice->substream = substream;
+	runtime->private_data = pvoice;
+	runtime->private_free = snd_ali_pcm_free_substream;
+
+	runtime->hw = *phw;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+				     0, 64*1024);
+	return 0;
+}
+
+static int snd_ali_playback_open(struct snd_pcm_substream *substream)
+{
+	return snd_ali_open(substream, 0, -1, &snd_ali_playback);
+}
+
+static int snd_ali_capture_open(struct snd_pcm_substream *substream)
+{
+	return snd_ali_open(substream, 1, -1, &snd_ali_capture);
+}
+
+static int snd_ali_playback_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int snd_ali_close(struct snd_pcm_substream *substream)
+{
+	struct snd_ali *codec = snd_pcm_substream_chip(substream);
+	struct snd_ali_voice *pvoice = substream->runtime->private_data;
+
+	snd_ali_disable_special_channel(codec,pvoice->number);
+
+	return 0;
+}
+
+static struct snd_pcm_ops snd_ali_playback_ops = {
+	.open =		snd_ali_playback_open,
+	.close =	snd_ali_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ali_playback_hw_params,
+	.hw_free =	snd_ali_playback_hw_free,
+	.prepare =	snd_ali_playback_prepare,
+	.trigger =	snd_ali_trigger,
+	.pointer =	snd_ali_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_ali_capture_ops = {
+	.open =		snd_ali_capture_open,
+	.close =	snd_ali_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ali_hw_params,
+	.hw_free =	snd_ali_hw_free,
+	.prepare =	snd_ali_prepare,
+	.trigger =	snd_ali_trigger,
+	.pointer =	snd_ali_pointer,
+};
+
+/*
+ * Modem PCM
+ */
+
+static int snd_ali_modem_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_ali *chip = snd_pcm_substream_chip(substream);
+	unsigned int modem_num = chip->num_of_codecs - 1;
+	snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE,
+		       params_rate(hw_params));
+	snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0);
+	return snd_ali_hw_params(substream, hw_params);
+}
+
+static struct snd_pcm_hardware snd_ali_modem =
+{
+	.info =		(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+			 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_RESUME |
+			 SNDRV_PCM_INFO_SYNC_START),
+	.formats =	SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =	(SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000 |
+			 SNDRV_PCM_RATE_16000),
+	.rate_min =		8000,
+	.rate_max =		16000,
+	.channels_min =		1,
+	.channels_max =		1,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(256*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec,
+			      int channel)
+{
+	static unsigned int rates[] = {8000, 9600, 12000, 16000};
+	static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
+		.count = ARRAY_SIZE(rates),
+		.list = rates,
+		.mask = 0,
+	};
+	int err = snd_ali_open(substream, rec, channel, &snd_ali_modem);
+
+	if (err)
+		return err;
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates);
+}
+
+static int snd_ali_modem_playback_open(struct snd_pcm_substream *substream)
+{
+	return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL);
+}
+
+static int snd_ali_modem_capture_open(struct snd_pcm_substream *substream)
+{
+	return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL);
+}
+
+static struct snd_pcm_ops snd_ali_modem_playback_ops = {
+	.open =		snd_ali_modem_playback_open,
+	.close =	snd_ali_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ali_modem_hw_params,
+	.hw_free =	snd_ali_hw_free,
+	.prepare =	snd_ali_prepare,
+	.trigger =	snd_ali_trigger,
+	.pointer =	snd_ali_pointer,
+};
+
+static struct snd_pcm_ops snd_ali_modem_capture_ops = {
+	.open =		snd_ali_modem_capture_open,
+	.close =	snd_ali_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ali_modem_hw_params,
+	.hw_free =	snd_ali_hw_free,
+	.prepare =	snd_ali_prepare,
+	.trigger =	snd_ali_trigger,
+	.pointer =	snd_ali_pointer,
+};
+
+
+struct ali_pcm_description {
+	char *name;
+	unsigned int playback_num;
+	unsigned int capture_num;
+	struct snd_pcm_ops *playback_ops;
+	struct snd_pcm_ops *capture_ops;
+	unsigned short class;
+};
+
+
+static void snd_ali_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_ali *codec = pcm->private_data;
+	codec->pcm[pcm->device] = NULL;
+}
+
+
+static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
+				 struct ali_pcm_description *desc)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(codec->card, desc->name, device,
+			  desc->playback_num, desc->capture_num, &pcm);
+	if (err < 0) {
+		snd_printk(KERN_ERR "snd_ali_pcm: err called snd_pcm_new.\n");
+		return err;
+	}
+	pcm->private_data = codec;
+	pcm->private_free = snd_ali_pcm_free;
+	if (desc->playback_ops)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				desc->playback_ops);
+	if (desc->capture_ops)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+				desc->capture_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(codec->pci),
+					      64*1024, 128*1024);
+
+	pcm->info_flags = 0;
+	pcm->dev_class = desc->class;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, desc->name);
+	codec->pcm[0] = pcm;
+	return 0;
+}
+
+static struct ali_pcm_description ali_pcms[] = {
+	{ .name = "ALI 5451",
+	  .playback_num = ALI_CHANNELS,
+	  .capture_num = 1,
+	  .playback_ops = &snd_ali_playback_ops,
+	  .capture_ops = &snd_ali_capture_ops
+	},
+	{ .name = "ALI 5451 modem",
+	  .playback_num = 1,
+	  .capture_num = 1,
+	  .playback_ops = &snd_ali_modem_playback_ops,
+	  .capture_ops = &snd_ali_modem_capture_ops,
+	  .class = SNDRV_PCM_CLASS_MODEM
+	}
+};
+
+static int __devinit snd_ali_build_pcms(struct snd_ali *codec)
+{
+	int i, err;
+	for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) {
+		err = snd_ali_pcm(codec, i, &ali_pcms[i]);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+
+#define ALI5451_SPDIF(xname, xindex, value) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
+.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \
+.put = snd_ali5451_spdif_put, .private_value = value}
+
+#define snd_ali5451_spdif_info		snd_ctl_boolean_mono_info
+
+static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ali *codec = kcontrol->private_data;
+	unsigned int enable;
+
+	enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+	spin_lock_irq(&codec->reg_lock);
+	switch (kcontrol->private_value) {
+	case 0:
+		enable = (codec->spdif_mask & 0x02) ? 1 : 0;
+		break;
+	case 1:
+		enable = ((codec->spdif_mask & 0x02) &&
+			  (codec->spdif_mask & 0x04)) ? 1 : 0;
+		break;
+	case 2:
+		enable = (codec->spdif_mask & 0x01) ? 1 : 0;
+		break;
+	default:
+		break;
+	}
+	ucontrol->value.integer.value[0] = enable;
+	spin_unlock_irq(&codec->reg_lock);
+	return 0;
+}
+
+static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ali *codec = kcontrol->private_data;
+	unsigned int change = 0, enable = 0;
+
+	enable = ucontrol->value.integer.value[0] ? 1 : 0;
+
+	spin_lock_irq(&codec->reg_lock);
+	switch (kcontrol->private_value) {
+	case 0:
+		change = (codec->spdif_mask & 0x02) ? 1 : 0;
+		change = change ^ enable;
+		if (change) {
+			if (enable) {
+				codec->spdif_mask |= 0x02;
+				snd_ali_enable_spdif_out(codec);
+			} else {
+				codec->spdif_mask &= ~(0x02);
+				codec->spdif_mask &= ~(0x04);
+				snd_ali_disable_spdif_out(codec);
+			}
+		}
+		break;
+	case 1: 
+		change = (codec->spdif_mask & 0x04) ? 1 : 0;
+		change = change ^ enable;
+		if (change && (codec->spdif_mask & 0x02)) {
+			if (enable) {
+				codec->spdif_mask |= 0x04;
+				snd_ali_enable_spdif_chnout(codec);
+			} else {
+				codec->spdif_mask &= ~(0x04);
+				snd_ali_disable_spdif_chnout(codec);
+			}
+		}
+		break;
+	case 2:
+		change = (codec->spdif_mask & 0x01) ? 1 : 0;
+		change = change ^ enable;
+		if (change) {
+			if (enable) {
+				codec->spdif_mask |= 0x01;
+				snd_ali_enable_spdif_in(codec);
+			} else {
+				codec->spdif_mask &= ~(0x01);
+				snd_ali_disable_spdif_in(codec);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irq(&codec->reg_lock);
+	
+	return change;
+}
+
+static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] __devinitdata = {
+	/* spdif aplayback switch */
+	/* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */
+	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0),
+	/* spdif out to spdif channel */
+	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Channel Output ",NONE,SWITCH), 0, 1),
+	/* spdif in from spdif channel */
+	ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2)
+};
+
+static int __devinit snd_ali_mixer(struct snd_ali * codec)
+{
+	struct snd_ac97_template ac97;
+	unsigned int idx;
+	int i, err;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_ali_codec_write,
+		.read = snd_ali_codec_read,
+	};
+
+	err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus);
+	if (err < 0)
+		return err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = codec;
+
+	for (i = 0; i < codec->num_of_codecs; i++) {
+		ac97.num = i;
+		err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]);
+		if (err < 0) {
+			snd_printk(KERN_ERR
+				   "ali mixer %d creating error.\n", i);
+			if (i == 0)
+				return err;
+			codec->num_of_codecs = 1;
+			break;
+		}
+	}
+
+	if (codec->spdif_support) {
+		for (idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) {
+			err = snd_ctl_add(codec->card,
+					  snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ali_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct snd_ali *chip = card->private_data;
+	struct snd_ali_image *im;
+	int i, j;
+
+	im = chip->image;
+	if (!im)
+		return 0;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	for (i = 0; i < chip->num_of_codecs; i++) {
+		snd_pcm_suspend_all(chip->pcm[i]);
+		snd_ac97_suspend(chip->ac97[i]);
+	}
+
+	spin_lock_irq(&chip->reg_lock);
+	
+	im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
+	/* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */
+	im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP));
+	
+	/* disable all IRQ bits */
+	outl(0, ALI_REG(chip, ALI_MISCINT));
+	
+	for (i = 0; i < ALI_GLOBAL_REGS; i++) {	
+		if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP))
+			continue;
+		im->regs[i] = inl(ALI_REG(chip, i*4));
+	}
+	
+	for (i = 0; i < ALI_CHANNELS; i++) {
+		outb(i, ALI_REG(chip, ALI_GC_CIR));
+		for (j = 0; j < ALI_CHANNEL_REGS; j++) 
+			im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0));
+	}
+
+	/* stop all HW channel */
+	outl(0xffffffff, ALI_REG(chip, ALI_STOP));
+
+	spin_unlock_irq(&chip->reg_lock);
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+	return 0;
+}
+
+static int ali_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct snd_ali *chip = card->private_data;
+	struct snd_ali_image *im;
+	int i, j;
+
+	im = chip->image;
+	if (!im)
+		return 0;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		printk(KERN_ERR "ali5451: pci_enable_device failed, "
+		       "disabling device\n");
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+	pci_set_master(pci);
+
+	spin_lock_irq(&chip->reg_lock);
+	
+	for (i = 0; i < ALI_CHANNELS; i++) {
+		outb(i, ALI_REG(chip, ALI_GC_CIR));
+		for (j = 0; j < ALI_CHANNEL_REGS; j++) 
+			outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
+	}
+	
+	for (i = 0; i < ALI_GLOBAL_REGS; i++) {	
+		if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) ||
+		    (i*4 == ALI_START))
+			continue;
+		outl(im->regs[i], ALI_REG(chip, i*4));
+	}
+	
+	/* start HW channel */
+	outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
+	/* restore IRQ enable bits */
+	outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
+	
+	spin_unlock_irq(&chip->reg_lock);
+
+	for (i = 0 ; i < chip->num_of_codecs; i++)
+		snd_ac97_resume(chip->ac97[i]);
+	
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_ali_free(struct snd_ali * codec)
+{
+	if (codec->hw_initialized)
+		snd_ali_disable_address_interrupt(codec);
+	if (codec->irq >= 0) {
+		synchronize_irq(codec->irq);
+		free_irq(codec->irq, codec);
+	}
+	if (codec->port)
+		pci_release_regions(codec->pci);
+	pci_disable_device(codec->pci);
+#ifdef CONFIG_PM
+	kfree(codec->image);
+#endif
+	pci_dev_put(codec->pci_m1533);
+	pci_dev_put(codec->pci_m7101);
+	kfree(codec);
+	return 0;
+}
+
+static int snd_ali_chip_init(struct snd_ali *codec)
+{
+	unsigned int legacy;
+	unsigned char temp;
+	struct pci_dev *pci_dev;
+
+	snd_ali_printk("chip initializing ... \n");
+
+	if (snd_ali_reset_5451(codec)) {
+		snd_printk(KERN_ERR "ali_chip_init: reset 5451 error.\n");
+		return -1;
+	}
+
+	if (codec->revision == ALI_5451_V02) {
+        	pci_dev = codec->pci_m1533;
+		pci_read_config_byte(pci_dev, 0x59, &temp);
+		temp |= 0x80;
+		pci_write_config_byte(pci_dev, 0x59, temp);
+	
+		pci_dev = codec->pci_m7101;
+		pci_read_config_byte(pci_dev, 0xb8, &temp);
+		temp |= 0x20;
+		pci_write_config_byte(pci_dev, 0xB8, temp);
+	}
+
+	pci_read_config_dword(codec->pci, 0x44, &legacy);
+	legacy &= 0xff00ff00;
+	legacy |= 0x000800aa;
+	pci_write_config_dword(codec->pci, 0x44, legacy);
+
+	outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL));
+	outl(0x00000000, ALI_REG(codec, ALI_AINTEN));
+	outl(0xffffffff, ALI_REG(codec, ALI_AINT));
+	outl(0x00000000, ALI_REG(codec, ALI_VOLUME));
+	outb(0x10, 	 ALI_REG(codec, ALI_MPUR2));
+
+	codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID);
+	codec->ac97_ext_status = snd_ali_codec_peek(codec, 0,
+						    AC97_EXTENDED_STATUS);
+	if (codec->spdif_support) {
+		snd_ali_enable_spdif_out(codec);
+		codec->spdif_mask = 0x00000002;
+	}
+
+	codec->num_of_codecs = 1;
+
+	/* secondary codec - modem */
+	if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) {
+		codec->num_of_codecs++;
+		outl(inl(ALI_REG(codec, ALI_SCTRL)) |
+		     (ALI_SCTRL_LINE_IN2 | ALI_SCTRL_GPIO_IN2 |
+		      ALI_SCTRL_LINE_OUT_EN),
+		     ALI_REG(codec, ALI_SCTRL));
+	}
+
+	snd_ali_printk("chip initialize succeed.\n");
+	return 0;
+
+}
+
+/* proc for register dump */
+static void snd_ali_proc_read(struct snd_info_entry *entry,
+			      struct snd_info_buffer *buf)
+{
+	struct snd_ali *codec = entry->private_data;
+	int i;
+	for (i = 0; i < 256 ; i+= 4)
+		snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i)));
+}
+
+static void __devinit snd_ali_proc_init(struct snd_ali *codec)
+{
+	struct snd_info_entry *entry;
+	if (!snd_card_proc_new(codec->card, "ali5451", &entry))
+		snd_info_set_text_ops(entry, codec, snd_ali_proc_read);
+}
+
+static int __devinit snd_ali_resources(struct snd_ali *codec)
+{
+	int err;
+
+	snd_ali_printk("resouces allocation ...\n");
+	err = pci_request_regions(codec->pci, "ALI 5451");
+	if (err < 0)
+		return err;
+	codec->port = pci_resource_start(codec->pci, 0);
+
+	if (request_irq(codec->pci->irq, snd_ali_card_interrupt,
+			IRQF_SHARED, "ALI 5451", codec)) {
+		snd_printk(KERN_ERR "Unable to request irq.\n");
+		return -EBUSY;
+	}
+	codec->irq = codec->pci->irq;
+	snd_ali_printk("resouces allocated.\n");
+	return 0;
+}
+static int snd_ali_dev_free(struct snd_device *device)
+{
+	struct snd_ali *codec = device->device_data;
+	snd_ali_free(codec);
+	return 0;
+}
+
+static int __devinit snd_ali_create(struct snd_card *card,
+				    struct pci_dev *pci,
+				    int pcm_streams,
+				    int spdif_support,
+				    struct snd_ali ** r_ali)
+{
+	struct snd_ali *codec;
+	int i, err;
+	unsigned short cmdw;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_ali_dev_free,
+        };
+
+	*r_ali = NULL;
+
+	snd_ali_printk("creating ...\n");
+
+	/* enable PCI device */
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 31 bits */
+	if (pci_set_dma_mask(pci, DMA_31BIT_MASK) < 0 ||
+	    pci_set_consistent_dma_mask(pci, DMA_31BIT_MASK) < 0) {
+		snd_printk(KERN_ERR "architecture does not support "
+			   "31bit PCI busmaster DMA\n");
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+
+	codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+	if (!codec) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&codec->reg_lock);
+	spin_lock_init(&codec->voice_alloc);
+
+	codec->card = card;
+	codec->pci = pci;
+	codec->irq = -1;
+	codec->revision = pci->revision;
+	codec->spdif_support = spdif_support;
+
+	if (pcm_streams < 1)
+		pcm_streams = 1;
+	if (pcm_streams > 32)
+		pcm_streams = 32;
+	
+	pci_set_master(pci);
+	pci_read_config_word(pci, PCI_COMMAND, &cmdw);
+	if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) {
+		cmdw |= PCI_COMMAND_IO;
+		pci_write_config_word(pci, PCI_COMMAND, cmdw);
+	}
+	pci_set_master(pci);
+	
+	if (snd_ali_resources(codec)) {
+		snd_ali_free(codec);
+		return -EBUSY;
+	}
+
+	synchronize_irq(pci->irq);
+
+	codec->synth.chmap = 0;
+	codec->synth.chcnt = 0;
+	codec->spdif_mask = 0;
+	codec->synth.synthcount = 0;
+
+	if (codec->revision == ALI_5451_V02)
+		codec->chregs.regs.ac97read = ALI_AC97_WRITE;
+	else
+		codec->chregs.regs.ac97read = ALI_AC97_READ;
+	codec->chregs.regs.ac97write = ALI_AC97_WRITE;
+
+	codec->chregs.regs.start  = ALI_START;
+	codec->chregs.regs.stop   = ALI_STOP;
+	codec->chregs.regs.aint   = ALI_AINT;
+	codec->chregs.regs.ainten = ALI_AINTEN;
+
+	codec->chregs.data.start  = 0x00;
+	codec->chregs.data.stop   = 0x00;
+	codec->chregs.data.aint   = 0x00;
+	codec->chregs.data.ainten = 0x00;
+
+	/* M1533: southbridge */
+	codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL);
+	if (!codec->pci_m1533) {
+		snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n");
+		snd_ali_free(codec);
+		return -ENODEV;
+	}
+	/* M7101: power management */
+	codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL);
+	if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) {
+		snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n");
+		snd_ali_free(codec);
+		return -ENODEV;
+	}
+
+	snd_ali_printk("snd_device_new is called.\n");
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops);
+	if (err < 0) {
+		snd_ali_free(codec);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+	/* initialise synth voices*/
+	for (i = 0; i < ALI_CHANNELS; i++)
+		codec->synth.voices[i].number = i;
+
+	err = snd_ali_chip_init(codec);
+	if (err < 0) {
+		snd_printk(KERN_ERR "ali create: chip init error.\n");
+		return err;
+	}
+
+#ifdef CONFIG_PM
+	codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
+	if (!codec->image)
+		snd_printk(KERN_WARNING "can't allocate apm buffer\n");
+#endif
+
+	snd_ali_enable_address_interrupt(codec);
+	codec->hw_initialized = 1;
+
+	*r_ali = codec;
+	snd_ali_printk("created.\n");
+	return 0;
+}
+
+static int __devinit snd_ali_probe(struct pci_dev *pci,
+				   const struct pci_device_id *pci_id)
+{
+	struct snd_card *card;
+	struct snd_ali *codec;
+	int err;
+
+	snd_ali_printk("probe ...\n");
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (!card)
+		return -ENOMEM;
+
+	err = snd_ali_create(card, pci, pcm_channels, spdif, &codec);
+	if (err < 0)
+		goto error;
+	card->private_data = codec;
+
+	snd_ali_printk("mixer building ...\n");
+	err = snd_ali_mixer(codec);
+	if (err < 0)
+		goto error;
+	
+	snd_ali_printk("pcm building ...\n");
+	err = snd_ali_build_pcms(codec);
+	if (err < 0)
+		goto error;
+
+	snd_ali_proc_init(codec);
+
+	strcpy(card->driver, "ALI5451");
+	strcpy(card->shortname, "ALI 5451");
+	
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, codec->port, codec->irq);
+
+	snd_ali_printk("register card.\n");
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+
+	pci_set_drvdata(pci, card);
+	return 0;
+
+ error:
+	snd_card_free(card);
+	return err;
+}
+
+static void __devexit snd_ali_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "ALI 5451",
+	.id_table = snd_ali_ids,
+	.probe = snd_ali_probe,
+	.remove = __devexit_p(snd_ali_remove),
+#ifdef CONFIG_PM
+	.suspend = ali_suspend,
+	.resume = ali_resume,
+#endif
+};                                
+
+static int __init alsa_card_ali_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_ali_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ali_init)
+module_exit(alsa_card_ali_exit)
diff -ruN linux-2.6.22/sound/pci/als300.c linux-2.6.22-alsa/sound/pci/als300.c
--- linux-2.6.22/sound/pci/als300.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/als300.c	2007-09-01 20:55:23.000000000 +0200
@@ -88,8 +88,8 @@
 #define PLAYBACK_BLOCK_COUNTER	0x9A
 #define RECORD_BLOCK_COUNTER	0x9B
 
-#define DEBUG_CALLS	1
-#define DEBUG_PLAY_REC	1
+#define DEBUG_CALLS	0
+#define DEBUG_PLAY_REC	0
 
 #if DEBUG_CALLS
 #define snd_als300_dbgcalls(format, args...) printk(format, ##args)
@@ -733,7 +733,8 @@
 
 	snd_als300_init(chip);
 
-	if (snd_als300_ac97(chip) < 0) {
+	err = snd_als300_ac97(chip);
+	if (err < 0) {
 		snd_printk(KERN_WARNING "Could not create ac97\n");
 		snd_als300_free(chip);
 		return err;
diff -ruN linux-2.6.22/sound/pci/au88x0/au88x0.c linux-2.6.22-alsa/sound/pci/au88x0/au88x0.c
--- linux-2.6.22/sound/pci/au88x0/au88x0.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/au88x0/au88x0.c	2007-09-02 13:30:43.000000000 +0200
@@ -232,6 +232,7 @@
 	pci_disable_device(chip->pci_dev);
 	//FIXME: this not the right place to unregister the gameport
 	vortex_gameport_unregister(chip);
+	kfree(chip);
 	return err;
 }
 
diff -ruN linux-2.6.22/sound/pci/au88x0/au88x0.c.orig linux-2.6.22-alsa/sound/pci/au88x0/au88x0.c.orig
--- linux-2.6.22/sound/pci/au88x0/au88x0.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/au88x0/au88x0.c.orig	2007-09-01 20:55:23.000000000 +0200
@@ -0,0 +1,400 @@
+/*
+ * ALSA driver for the Aureal Vortex family of soundprocessors.
+ * Author: Manuel Jander (mjander@embedded.cl)
+ *
+ *   This driver is the result of the OpenVortex Project from Savannah
+ * (savannah.nongnu.org/projects/openvortex). I would like to thank
+ * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from
+ * whom i got plenty of help, and their codebase was invaluable.
+ *   Thanks to the ALSA developers, they helped a lot working out
+ * the ALSA part.
+ *   Thanks also to Sourceforge for maintaining the old binary drivers,
+ * and the forum, where developers could comunicate.
+ *
+ * Now at least i can play Legacy DOOM with MIDI music :-)
+ */
+
+#include "au88x0.h"
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <sound/initval.h>
+
+// module parameters (see "Module Parameters")
+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 int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 };
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+module_param_array(pcifix, int, NULL, 0444);
+MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
+
+MODULE_DESCRIPTION("Aureal vortex");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
+
+MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
+
+static void vortex_fix_latency(struct pci_dev *vortex)
+{
+	int rc;
+	if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
+			printk(KERN_INFO CARD_NAME
+			       ": vortex latency is 0xff\n");
+	} else {
+		printk(KERN_WARNING CARD_NAME
+				": could not set vortex latency: pci error 0x%x\n", rc);
+	}
+}
+
+static void vortex_fix_agp_bridge(struct pci_dev *via)
+{
+	int rc;
+	u8 value;
+
+	/*
+	 * only set the bit (Extend PCI#2 Internal Master for
+	 * Efficient Handling of Dummy Requests) if the can
+	 * read the config and it is not already set
+	 */
+
+	if (!(rc = pci_read_config_byte(via, 0x42, &value))
+			&& ((value & 0x10)
+				|| !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
+		printk(KERN_INFO CARD_NAME
+				": bridge config is 0x%x\n", value | 0x10);
+	} else {
+		printk(KERN_WARNING CARD_NAME
+				": could not set vortex latency: pci error 0x%x\n", rc);
+	}
+}
+
+static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
+{
+	struct pci_dev *via = NULL;
+
+	/* autodetect if workarounds are required */
+	if (fix == 255) {
+		/* VIA KT133 */
+		via = pci_get_device(PCI_VENDOR_ID_VIA,
+			PCI_DEVICE_ID_VIA_8365_1, NULL);
+		/* VIA Apollo */
+		if (via == NULL) {
+			via = pci_get_device(PCI_VENDOR_ID_VIA,
+				PCI_DEVICE_ID_VIA_82C598_1, NULL);
+			/* AMD Irongate */
+			if (via == NULL)
+				via = pci_get_device(PCI_VENDOR_ID_AMD,
+					PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+		}
+		if (via) {
+			printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n");
+			vortex_fix_latency(vortex);
+			vortex_fix_agp_bridge(via);
+		}
+	} else {
+		if (fix & 0x1)
+			vortex_fix_latency(vortex);
+		if ((fix & 0x2) && (via = pci_get_device(PCI_VENDOR_ID_VIA,
+				PCI_DEVICE_ID_VIA_8365_1, NULL)))
+			vortex_fix_agp_bridge(via);
+		if ((fix & 0x4) && (via = pci_get_device(PCI_VENDOR_ID_VIA,
+				PCI_DEVICE_ID_VIA_82C598_1, NULL)))
+			vortex_fix_agp_bridge(via);
+		if ((fix & 0x8) && (via = pci_get_device(PCI_VENDOR_ID_AMD,
+				PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL)))
+			vortex_fix_agp_bridge(via);
+	}
+	pci_dev_put(via);
+}
+
+// component-destructor
+// (see "Management of Cards and Components")
+static int snd_vortex_dev_free(struct snd_device *device)
+{
+	vortex_t *vortex = device->device_data;
+
+	vortex_gameport_unregister(vortex);
+	vortex_core_shutdown(vortex);
+	// Take down PCI interface.
+	synchronize_irq(vortex->irq);
+	free_irq(vortex->irq, vortex);
+	iounmap(vortex->mmio);
+	pci_release_regions(vortex->pci_dev);
+	pci_disable_device(vortex->pci_dev);
+	kfree(vortex);
+
+	return 0;
+}
+
+// chip-specific constructor
+// (see "Management of Cards and Components")
+static int __devinit
+snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
+{
+	vortex_t *chip;
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_vortex_dev_free,
+	};
+
+	*rchip = NULL;
+
+	// check PCI availability (DMA).
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
+	    pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
+		printk(KERN_ERR "error to set DMA mask\n");
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	chip->card = card;
+
+	// initialize the stuff
+	chip->pci_dev = pci;
+	chip->io = pci_resource_start(pci, 0);
+	chip->vendor = pci->vendor;
+	chip->device = pci->device;
+	chip->card = card;
+	chip->irq = -1;
+
+	// (1) PCI resource allocation
+	// Get MMIO area
+	//
+	if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0)
+		goto regions_out;
+
+	chip->mmio = ioremap_nocache(pci_resource_start(pci, 0),
+	                             pci_resource_len(pci, 0));
+	if (!chip->mmio) {
+		printk(KERN_ERR "MMIO area remap failed.\n");
+		err = -ENOMEM;
+		goto ioremap_out;
+	}
+
+	/* Init audio core.
+	 * This must be done before we do request_irq otherwise we can get spurious
+	 * interupts that we do not handle properly and make a mess of things */
+	if ((err = vortex_core_init(chip)) != 0) {
+		printk(KERN_ERR "hw core init failed\n");
+		goto core_out;
+	}
+
+	if ((err = request_irq(pci->irq, vortex_interrupt,
+	                       IRQF_SHARED, CARD_NAME_SHORT,
+	                       chip)) != 0) {
+		printk(KERN_ERR "cannot grab irq\n");
+		goto irq_out;
+	}
+	chip->irq = pci->irq;
+
+	pci_set_master(pci);
+	// End of PCI setup.
+
+	// Register alsa root device.
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		goto alloc_out;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*rchip = chip;
+
+	return 0;
+
+      alloc_out:
+	synchronize_irq(chip->irq);
+	free_irq(chip->irq, chip);
+      irq_out:
+	vortex_core_shutdown(chip);
+      core_out:
+	iounmap(chip->mmio);
+      ioremap_out:
+	pci_release_regions(chip->pci_dev);
+      regions_out:
+	pci_disable_device(chip->pci_dev);
+	//FIXME: this not the right place to unregister the gameport
+	vortex_gameport_unregister(chip);
+	kfree(chip);
+	return err;
+}
+
+// constructor -- see "Constructor" sub-section
+static int __devinit
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	vortex_t *chip;
+	int err;
+
+	// (1)
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	// (2)
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	// (3)
+	if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_vortex_workaround(pci, pcifix[dev]);
+
+	// Card details needed in snd_vortex_midi
+	strcpy(card->driver, CARD_NAME_SHORT);
+	sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT);
+	sprintf(card->longname, "%s at 0x%lx irq %i",
+		card->shortname, chip->io, chip->irq);
+
+	// (4) Alloc components.
+	// ADB pcm.
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#ifndef CHIP_AU8820
+	// ADB SPDIF
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	// A3D
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	/*
+	   // ADB I2S
+	   if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
+	   snd_card_free(card);
+	   return err;
+	   }
+	 */
+#ifndef CHIP_AU8810
+	// WT pcm.
+	if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+	// snd_ac97_mixer and Vortex mixer.
+	if ((err = snd_vortex_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_vortex_midi(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	vortex_gameport_register(chip);
+
+#if 0
+	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
+			       sizeof(snd_vortex_synth_arg_t), &wave) < 0
+	    || wave == NULL) {
+		snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n");
+	} else {
+		snd_vortex_synth_arg_t *arg;
+
+		arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+		strcpy(wave->name, "Aureal Synth");
+		arg->hwptr = vortex;
+		arg->index = 1;
+		arg->seq_ports = seq_ports[dev];
+		arg->max_voices = max_synth_voices[dev];
+	}
+#endif
+
+	// (5)
+	if ((err = pci_read_config_word(pci, PCI_DEVICE_ID,
+				  &(chip->device))) < 0) {
+		snd_card_free(card);
+		return err;
+	}	
+	if ((err = pci_read_config_word(pci, PCI_VENDOR_ID,
+				  &(chip->vendor))) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	chip->rev = pci->revision;
+#ifdef CHIP_AU8830
+	if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
+		printk(KERN_ALERT
+		       "vortex: The revision (%x) of your card has not been seen before.\n",
+		       chip->rev);
+		printk(KERN_ALERT
+		       "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+		snd_card_free(card);
+		err = -ENODEV;
+		return err;
+	}
+#endif
+
+	// (6)
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	// (7)
+	pci_set_drvdata(pci, card);
+	dev++;
+	vortex_connect_default(chip, 1);
+	vortex_enable_int(chip);
+	return 0;
+}
+
+// destructor -- see "Destructor" sub-section
+static void __devexit snd_vortex_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+// pci_driver definition
+static struct pci_driver driver = {
+	.name = CARD_NAME_SHORT,
+	.id_table = snd_vortex_ids,
+	.probe = snd_vortex_probe,
+	.remove = __devexit_p(snd_vortex_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_vortex_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+// clean up the module
+static void __exit alsa_card_vortex_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_vortex_init)
+module_exit(alsa_card_vortex_exit)
diff -ruN linux-2.6.22/sound/pci/au88x0/au88x0_eq.c linux-2.6.22-alsa/sound/pci/au88x0/au88x0_eq.c
--- linux-2.6.22/sound/pci/au88x0/au88x0_eq.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/au88x0/au88x0_eq.c	2007-09-01 20:55:23.000000000 +0200
@@ -728,15 +728,7 @@
 /* ALSA interface */
 
 /* Control interface */
-static int
-snd_vortex_eqtoggle_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_vortex_eqtoggle_info	snd_ctl_boolean_mono_info
 
 static int
 snd_vortex_eqtoggle_get(struct snd_kcontrol *kcontrol,
diff -ruN linux-2.6.22/sound/pci/bt87x.c linux-2.6.22-alsa/sound/pci/bt87x.c
--- linux-2.6.22/sound/pci/bt87x.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/bt87x.c	2007-09-01 20:55:23.000000000 +0200
@@ -340,28 +340,9 @@
 
 static int snd_bt87x_set_digital_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime)
 {
-	static struct {
-		int rate;
-		unsigned int bit;
-	} ratebits[] = {
-		{8000, SNDRV_PCM_RATE_8000},
-		{11025, SNDRV_PCM_RATE_11025},
-		{16000, SNDRV_PCM_RATE_16000},
-		{22050, SNDRV_PCM_RATE_22050},
-		{32000, SNDRV_PCM_RATE_32000},
-		{44100, SNDRV_PCM_RATE_44100},
-		{48000, SNDRV_PCM_RATE_48000}
-	};
-	int i;
-
 	chip->reg_control |= CTL_DA_IOM_DA;
 	runtime->hw = snd_bt87x_digital_hw;
-	runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
-	for (i = 0; i < ARRAY_SIZE(ratebits); ++i)
-		if (chip->dig_rate == ratebits[i].rate) {
-			runtime->hw.rates = ratebits[i].bit;
-			break;
-		}
+	runtime->hw.rates = snd_pcm_rate_to_rate_bit(chip->dig_rate);
 	runtime->hw.rate_min = chip->dig_rate;
 	runtime->hw.rate_max = chip->dig_rate;
 	return 0;
@@ -569,15 +550,7 @@
 	.put = snd_bt87x_capture_volume_put,
 };
 
-static int snd_bt87x_capture_boost_info(struct snd_kcontrol *kcontrol,
-					struct snd_ctl_elem_info *info)
-{
-	info->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	info->count = 1;
-	info->value.integer.min = 0;
-	info->value.integer.max = 1;
-	return 0;
-}
+#define snd_bt87x_capture_boost_info	snd_ctl_boolean_mono_info
 
 static int snd_bt87x_capture_boost_get(struct snd_kcontrol *kcontrol,
 				       struct snd_ctl_elem_value *value)
@@ -781,6 +754,8 @@
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, 0x0070, 0x13eb, 32000),
 	/* Viewcast Osprey 200 */
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, 44100),
+	/* Viewcast Osprey 440 (rate is configurable via gpio) */
+	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff07, 32000),
 	/* ATI TV-Wonder */
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1002, 0x0001, 32000),
 	/* Leadtek Winfast tv 2000xp delux */
@@ -789,6 +764,10 @@
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, 32000),
 	/* AVerMedia Studio No. 103, 203, ...? */
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000),
+	/* Prolink PixelView PV-M4900 */
+	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1554, 0x4011, 32000),
+	/* Pinnacle  Studio PCTV rave */
+	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0xbd11, 0x1200, 32000),
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, snd_bt87x_ids);
diff -ruN linux-2.6.22/sound/pci/ca0106/ca0106.h linux-2.6.22-alsa/sound/pci/ca0106/ca0106.h
--- linux-2.6.22/sound/pci/ca0106/ca0106.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca0106.h	2007-09-02 13:30:43.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.21
+ *  Version: 0.0.22
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -47,6 +47,8 @@
  *    Added GPIO info for SB Live 24bit.
  *  0.0.21
  *   Implement support for Line-in capture on SB Live 24bit.
+ *  0.0.22
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
  *
  *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
@@ -552,6 +554,95 @@
 #define CONTROL_CENTER_LFE_CHANNEL 1
 #define CONTROL_UNKNOWN_CHANNEL 2
 
+
+/* Based on WM8768 Datasheet Rev 4.2 page 32 */
+#define SPI_REG_MASK	0x1ff	/* 16-bit SPI writes have a 7-bit address */
+#define SPI_REG_SHIFT	9	/* followed by 9 bits of data */
+
+#define SPI_LDA1_REG		0	/* digital attenuation */
+#define SPI_RDA1_REG		1
+#define SPI_LDA2_REG		4
+#define SPI_RDA2_REG		5
+#define SPI_LDA3_REG		6
+#define SPI_RDA3_REG		7
+#define SPI_LDA4_REG		13
+#define SPI_RDA4_REG		14
+#define SPI_MASTDA_REG		8
+
+#define SPI_DA_BIT_UPDATE	(1<<8)	/* update attenuation values */
+#define SPI_DA_BIT_0dB		0xff	/* 0 dB */
+#define SPI_DA_BIT_infdB	0x00	/* inf dB attenuation (mute) */
+
+#define SPI_PL_REG		2
+#define SPI_PL_BIT_L_M		(0<<5)	/* left channel = mute */
+#define SPI_PL_BIT_L_L		(1<<5)	/* left channel = left */
+#define SPI_PL_BIT_L_R		(2<<5)	/* left channel = right */
+#define SPI_PL_BIT_L_C		(3<<5)	/* left channel = (L+R)/2 */
+#define SPI_PL_BIT_R_M		(0<<7)	/* right channel = mute */
+#define SPI_PL_BIT_R_L		(1<<7)	/* right channel = left */
+#define SPI_PL_BIT_R_R		(2<<7)	/* right channel = right */
+#define SPI_PL_BIT_R_C		(3<<7)	/* right channel = (L+R)/2 */
+#define SPI_IZD_REG		2
+#define SPI_IZD_BIT		(1<<4)	/* infinite zero detect */
+
+#define SPI_FMT_REG		3
+#define SPI_FMT_BIT_RJ		(0<<0)	/* right justified mode */
+#define SPI_FMT_BIT_LJ		(1<<0)	/* left justified mode */
+#define SPI_FMT_BIT_I2S		(2<<0)	/* I2S mode */
+#define SPI_FMT_BIT_DSP		(3<<0)	/* DSP Modes A or B */
+#define SPI_LRP_REG		3
+#define SPI_LRP_BIT		(1<<2)	/* invert LRCLK polarity */
+#define SPI_BCP_REG		3
+#define SPI_BCP_BIT		(1<<3)	/* invert BCLK polarity */
+#define SPI_IWL_REG		3
+#define SPI_IWL_BIT_16		(0<<4)	/* 16-bit world length */
+#define SPI_IWL_BIT_20		(1<<4)	/* 20-bit world length */
+#define SPI_IWL_BIT_24		(2<<4)	/* 24-bit world length */
+#define SPI_IWL_BIT_32		(3<<4)	/* 32-bit world length */
+
+#define SPI_MS_REG		10
+#define SPI_MS_BIT		(1<<5)	/* master mode */
+#define SPI_RATE_REG		10	/* only applies in master mode */
+#define SPI_RATE_BIT_128	(0<<6)	/* MCLK = LRCLK * 128 */
+#define SPI_RATE_BIT_192	(1<<6)
+#define SPI_RATE_BIT_256	(2<<6)
+#define SPI_RATE_BIT_384	(3<<6)
+#define SPI_RATE_BIT_512	(4<<6)
+#define SPI_RATE_BIT_768	(5<<6)
+
+/* They really do label the bit for the 4th channel "4" and not "3" */
+#define SPI_DMUTE0_REG		9
+#define SPI_DMUTE1_REG		9
+#define SPI_DMUTE2_REG		9
+#define SPI_DMUTE4_REG		15
+#define SPI_DMUTE0_BIT		(1<<3)
+#define SPI_DMUTE1_BIT		(1<<4)
+#define SPI_DMUTE2_BIT		(1<<5)
+#define SPI_DMUTE4_BIT		(1<<2)
+
+#define SPI_PHASE0_REG		3
+#define SPI_PHASE1_REG		3
+#define SPI_PHASE2_REG		3
+#define SPI_PHASE4_REG		15
+#define SPI_PHASE0_BIT		(1<<6)
+#define SPI_PHASE1_BIT		(1<<7)
+#define SPI_PHASE2_BIT		(1<<8)
+#define SPI_PHASE4_BIT		(1<<3)
+
+#define SPI_PDWN_REG		2	/* power down all DACs */
+#define SPI_PDWN_BIT		(1<<2)
+#define SPI_DACD0_REG		10	/* power down individual DACs */
+#define SPI_DACD1_REG		10
+#define SPI_DACD2_REG		10
+#define SPI_DACD4_REG		15
+#define SPI_DACD0_BIT		(1<<1)
+#define SPI_DACD1_BIT		(1<<2)
+#define SPI_DACD2_BIT		(1<<3)
+#define SPI_DACD4_BIT		(1<<0)	/* datasheet error says it's 1 */
+
+#define SPI_PWRDNALL_REG	10	/* power down everything */
+#define SPI_PWRDNALL_BIT	(1<<4)
+
 #include "ca_midi.h"
 
 struct snd_ca0106;
@@ -612,6 +703,8 @@
 
 	struct snd_ca_midi midi;
 	struct snd_ca_midi midi2;
+
+	u16 spi_dac_reg[16];
 };
 
 int snd_ca0106_mixer(struct snd_ca0106 *emu);
@@ -628,4 +721,5 @@
 
 int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
 
-
+int snd_ca0106_spi_write(struct snd_ca0106 * emu,
+				   unsigned int data);
diff -ruN linux-2.6.22/sound/pci/ca0106/ca0106.h.orig linux-2.6.22-alsa/sound/pci/ca0106/ca0106.h.orig
--- linux-2.6.22/sound/pci/ca0106/ca0106.h.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca0106.h.orig	2007-09-01 20:55:23.000000000 +0200
@@ -0,0 +1,724 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.22
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separated ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Implement 192000 sample rate.
+ *  0.0.17
+ *    Add support for SB0410 and SB0413.
+ *  0.0.18
+ *    Modified Copyright message.
+ *  0.0.19
+ *    Added I2C and SPI registers. Filled in interrupt enable.
+ *  0.0.20
+ *    Added GPIO info for SB Live 24bit.
+ *  0.0.21
+ *   Implement support for Line-in capture on SB Live 24bit.
+ *  0.0.22
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
+ *
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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; 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
+ *
+ */
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+						/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+						/* be modified independently of each other.	*/
+						/* CNL[1:0], ADDR[27:16]                        */
+
+#define DATA			0x04		/* Indexed register set data register		*/
+						/* DATA[31:0]					*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+						/* Clear pending interrupts by writing a 1 to	*/
+						/* the relevant bits and zero to the other bits	*/
+#define IPR_MIDI_RX_B		0x00020000	/* MIDI UART-B Receive buffer non-empty		*/
+#define IPR_MIDI_TX_B		0x00010000	/* MIDI UART-B Transmit buffer empty		*/
+#define IPR_SPDIF_IN_USER	0x00004000      /* SPDIF input user data has 16 more bits	*/
+#define IPR_SPDIF_OUT_USER	0x00002000      /* SPDIF output user data needs 16 more bits	*/
+#define IPR_SPDIF_OUT_FRAME	0x00001000      /* SPDIF frame about to start			*/
+#define IPR_SPI			0x00000800      /* SPI transaction completed			*/
+#define IPR_I2C_EEPROM		0x00000400      /* I2C EEPROM transaction completed		*/
+#define IPR_I2C_DAC		0x00000200      /* I2C DAC transaction completed		*/
+#define IPR_AI			0x00000100      /* Audio pending register changed. See PTR reg 0x76	*/
+#define IPR_GPI			0x00000080      /* General Purpose input changed		*/
+#define IPR_SRC_LOCKED          0x00000040      /* SRC lock status changed			*/
+#define IPR_SPDIF_STATUS        0x00000020      /* SPDIF status changed				*/
+#define IPR_TIMER2              0x00000010      /* 192000Hz Timer				*/
+#define IPR_TIMER1              0x00000008      /* 44100Hz Timer				*/
+#define IPR_MIDI_RX_A		0x00000004	/* MIDI UART-A Receive buffer non-empty		*/
+#define IPR_MIDI_TX_A		0x00000002	/* MIDI UART-A Transmit buffer empty		*/
+#define IPR_PCI			0x00000001	/* PCI Bus error				*/
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+
+#define INTE_MIDI_RX_B		0x00020000	/* MIDI UART-B Receive buffer non-empty		*/
+#define INTE_MIDI_TX_B		0x00010000	/* MIDI UART-B Transmit buffer empty		*/
+#define INTE_SPDIF_IN_USER	0x00004000      /* SPDIF input user data has 16 more bits	*/
+#define INTE_SPDIF_OUT_USER	0x00002000      /* SPDIF output user data needs 16 more bits	*/
+#define INTE_SPDIF_OUT_FRAME	0x00001000      /* SPDIF frame about to start			*/
+#define INTE_SPI		0x00000800      /* SPI transaction completed			*/
+#define INTE_I2C_EEPROM		0x00000400      /* I2C EEPROM transaction completed		*/
+#define INTE_I2C_DAC		0x00000200      /* I2C DAC transaction completed		*/
+#define INTE_AI			0x00000100      /* Audio pending register changed. See PTR reg 0x75 */
+#define INTE_GPI		0x00000080      /* General Purpose input changed		*/
+#define INTE_SRC_LOCKED         0x00000040      /* SRC lock status changed			*/
+#define INTE_SPDIF_STATUS       0x00000020      /* SPDIF status changed				*/
+#define INTE_TIMER2             0x00000010      /* 192000Hz Timer				*/
+#define INTE_TIMER1             0x00000008      /* 44100Hz Timer				*/
+#define INTE_MIDI_RX_A		0x00000004	/* MIDI UART-A Receive buffer non-empty		*/
+#define INTE_MIDI_TX_A		0x00000002	/* MIDI UART-A Transmit buffer empty		*/
+#define INTE_PCI		0x00000001	/* PCI Bus error				*/
+
+#define UNKNOWN10		0x10		/* Unknown ??. Defaults to 0 */
+#define HCFG			0x14		/* Hardware config register			*/
+						/* 0x1000 causes AC3 to fails. It adds a dither bit. */
+
+#define HCFG_STAC		0x10000000	/* Special mode for STAC9460 Codec. */
+#define HCFG_CAPTURE_I2S_BYPASS	0x08000000	/* 1 = bypass I2S input async SRC. */
+#define HCFG_CAPTURE_SPDIF_BYPASS 0x04000000	/* 1 = bypass SPDIF input async SRC. */
+#define HCFG_PLAYBACK_I2S_BYPASS 0x02000000	/* 0 = I2S IN mixer output, 1 = I2S IN1. */
+#define HCFG_FORCE_LOCK		0x01000000	/* For test only. Force input SRC tracker to lock. */
+#define HCFG_PLAYBACK_ATTENUATION 0x00006000	/* Playback attenuation mask. 0 = 0dB, 1 = 6dB, 2 = 12dB, 3 = Mute. */
+#define HCFG_PLAYBACK_DITHER	0x00001000	/* 1 = Add dither bit to all playback channels. */
+#define HCFG_PLAYBACK_S32_LE	0x00000800	/* 1 = S32_LE, 0 = S16_LE                       */
+#define HCFG_CAPTURE_S32_LE	0x00000400	/* 1 = S32_LE, 0 = S16_LE (S32_LE current not working)	*/
+#define HCFG_8_CHANNEL_PLAY	0x00000200	/* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_8_CHANNEL_CAPTURE	0x00000100	/* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_MONO		0x00000080	/* 1 = I2S Input mono                           */
+#define HCFG_I2S_OUTPUT		0x00000010	/* 1 = I2S Output disabled                      */
+#define HCFG_AC97		0x00000008	/* 0 = AC97 1.0, 1 = AC97 2.0                   */
+#define HCFG_LOCK_PLAYBACK_CACHE 0x00000004	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_LOCK_CAPTURE_CACHE	0x00000002	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+						/* Should be set to 1 when the EMU10K1 is	*/
+						/* completely initialized.			*/
+#define GPIO			0x18		/* Defaults: 005f03a3-Analog, 005f02a2-SPDIF.   */
+						/* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
+						/* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
+						/* SB Live 24bit:
+						 * bit 8 0 = SPDIF in and out / 1 = Analog (Mic or Line)-in.
+						 * bit 9 0 = Mute / 1 = Analog out.
+						 * bit 10 0 = Line-in / 1 = Mic-in.
+						 * bit 11 0 = ? / 1 = ?
+						 * bit 12 0 = 48 Khz / 1 = 96 Khz Analog out on SB Live 24bit.
+						 * bit 13 0 = ? / 1 = ?
+						 * bit 14 0 = Mute / 1 = Analog out
+						 * bit 15 0 = ? / 1 = ?
+						 * Both bit 9 and bit 14 have to be set for analog sound to work on the SB Live 24bit.
+						 */
+						/* 8 general purpose programmable In/Out pins.
+						 * GPI [8:0] Read only. Default 0.
+						 * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
+						 * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
+						 */
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+
+/********************************************************************************************************/
+/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers                     */
+/********************************************************************************************************/
+                                                                                                                           
+/* Initally all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR	0x00		/* Base DMA address of a list of pointers to each period/size */
+						/* One list entry: 4 bytes for DMA address, 
+						 * 4 bytes for period_size << 16.
+						 * One list entry is 8 bytes long.
+						 * One list entry for each period in the buffer.
+						 */
+						/* ADDR[31:0], Default: 0x0 */
+#define PLAYBACK_LIST_SIZE	0x01		/* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+						/* SIZE[21:16], Default: 0x8 */
+#define PLAYBACK_LIST_PTR	0x02		/* Pointer to the current period being played */
+						/* PTR[5:0], Default: 0x0 */
+#define PLAYBACK_UNKNOWN3	0x03		/* Not used ?? */
+#define PLAYBACK_DMA_ADDR	0x04		/* Playback DMA addresss */
+						/* DMA[31:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_SIZE	0x05		/* Playback period size. win2000 uses 0x04000000 */
+						/* SIZE[31:16], Default: 0x0 */
+#define PLAYBACK_POINTER	0x06		/* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+						/* POINTER[15:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_END_ADDR 0x07		/* Playback fifo end address */
+						/* END_ADDR[15:0], FLAG[16] 0 = don't stop, 1 = stop */
+#define PLAYBACK_FIFO_OFFSET_ADDRESS	0x08	/* Current fifo offset address [21:16] */
+						/* Cache size valid [5:0] */
+#define PLAYBACK_UNKNOWN9	0x09		/* 0x9 to 0xf Unused */
+#define CAPTURE_DMA_ADDR	0x10		/* Capture DMA address */
+						/* DMA[31:0], Default: 0x0 */
+#define CAPTURE_BUFFER_SIZE	0x11		/* Capture buffer size */
+						/* SIZE[31:16], Default: 0x0 */
+#define CAPTURE_POINTER		0x12		/* Capture buffer pointer. Sample currently in ADC */
+						/* POINTER[15:0], Default: 0x0 */
+#define CAPTURE_FIFO_OFFSET_ADDRESS	0x13	/* Current fifo offset address [21:16] */
+						/* Cache size valid [5:0] */
+#define PLAYBACK_LAST_SAMPLE    0x20		/* The sample currently being played */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT         0x40		/* Used by both playback and capture interrupt handler */
+						/* Playback (0x1<<channel_id) */
+						/* Capture  (0x100<<channel_id) */
+						/* Playback sample rate 96000 = 0x20000 */
+						/* Start Playback [3:0] (one bit per channel)
+						 * Start Capture [11:8] (one bit per channel)
+						 * Playback rate [23:16] (2 bits per channel) (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * Playback mixer in enable [27:24] (one bit per channel)
+						 * Playback mixer out enable [31:28] (one bit per channel)
+						 */
+/* The Digital out jack is shared with the Center/LFE Analogue output. 
+ * The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
+ * For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
+ * For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
+ */
+/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
+ * The Rear SPDIF can be used for Stereo PCM and also AC3/DTS
+ * The Center/LFE SPDIF cannot be used for AC3/DTS, but can be used for Stereo PCM.
+ * Summary: For ALSA we use the Rear channel for SPDIF Digital AC3/DTS output
+ */
+/* A standard 2 pole mono mini-jack to RCA plug can be used for SPDIF Stereo PCM output from the Front channel.
+ * A standard 3 pole stereo mini-jack to 2 RCA plugs can be used for SPDIF AC3/DTS and Stereo PCM output utilising the Rear channel and just one of the RCA plugs. 
+ */
+#define SPCS0			0x41		/* SPDIF output Channel Status 0 register. For Rear. default=0x02108004, non-audio=0x02108006	*/
+#define SPCS1			0x42		/* SPDIF output Channel Status 1 register. For Front */
+#define SPCS2			0x43		/* SPDIF output Channel Status 2 register. For Center/LFE */
+#define SPCS3			0x44		/* SPDIF output Channel Status 3 register. Unknown */
+						/* When Channel set to 0: */
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+						/* When Channel set to 1: */
+#define SPCS_WORD_LENGTH_MASK	0x0000000f	/* Word Length Mask				*/
+#define SPCS_WORD_LENGTH_16	0x00000008	/* Word Length 16 bit				*/
+#define SPCS_WORD_LENGTH_17	0x00000006	/* Word Length 17 bit				*/
+#define SPCS_WORD_LENGTH_18	0x00000004	/* Word Length 18 bit				*/
+#define SPCS_WORD_LENGTH_19	0x00000002	/* Word Length 19 bit				*/
+#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				*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_MASK	0x000000f0 /* Original Sample rate			*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_NONE	0x00000000 /* Original Sample rate not indicated	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_16000	0x00000010 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES1	0x00000020 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_32000	0x00000030 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_12000	0x00000040 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_11025	0x00000050 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_8000	0x00000060 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES2	0x00000070 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_192000 0x00000080 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_24000	0x00000090 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_96000	0x000000a0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_48000	0x000000b0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_176400 0x000000c0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_22050	0x000000d0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_88200	0x000000e0 /* Original Sample rate	*/
+#define SPCS_ORIGINAL_SAMPLE_RATE_44100	0x000000f0 /* Original Sample rate	*/
+
+#define SPDIF_SELECT1		0x45		/* Enables SPDIF or Analogue outputs 0-SPDIF, 0xf00-Analogue */
+						/* 0x100 - Front, 0x800 - Rear, 0x200 - Center/LFE.
+						 * But as the jack is shared, use 0xf00.
+						 * The Windows2000 driver uses 0x0000000f for both digital and analog.
+						 * 0xf00 introduces interesting noises onto the Center/LFE.
+						 * If you turn the volume up, you hear computer noise,
+						 * e.g. mouse moving, changing between app windows etc.
+						 * So, I am going to set this to 0x0000000f all the time now,
+						 * same as the windows driver does.
+						 * Use register SPDIF_SELECT2(0x72) to switch between SPDIF and Analog.
+						 */
+						/* When Channel = 0:
+						 * Wide SPDIF format [3:0] (one bit for each channel) (0=20bit, 1=24bit)
+						 * Tristate SPDIF Output [11:8] (one bit for each channel) (0=Not tristate, 1=Tristate)
+						 * SPDIF Bypass enable [19:16] (one bit for each channel) (0=Not bypass, 1=Bypass)
+						 */
+						/* When Channel = 1:
+						 * SPDIF 0 User data [7:0]
+						 * SPDIF 1 User data [15:8]
+						 * SPDIF 0 User data [23:16]
+						 * SPDIF 0 User data [31:24]
+						 * User data can be sent by using the SPDIF output frame pending and SPDIF output user bit interrupts.
+						 */
+#define WATERMARK		0x46		/* Test bit to indicate cache usage level */
+#define SPDIF_INPUT_STATUS	0x49		/* SPDIF Input status register. Bits the same as SPCS.
+						 * When Channel = 0: Bits the same as SPCS channel 0.
+						 * When Channel = 1: Bits the same as SPCS channel 1.
+						 * When Channel = 2:
+						 * SPDIF Input User data [16:0]
+						 * SPDIF Input Frame count [21:16]
+						 */
+#define CAPTURE_CACHE_DATA	0x50		/* 0x50-0x5f Recorded samples. */
+#define CAPTURE_SOURCE          0x60            /* Capture Source 0 = MIC */
+#define CAPTURE_SOURCE_CHANNEL0 0xf0000000	/* Mask for selecting the Capture sources */
+#define CAPTURE_SOURCE_CHANNEL1 0x0f000000	/* 0 - SPDIF mixer output. */
+#define CAPTURE_SOURCE_CHANNEL2 0x00f00000      /* 1 - What you hear or . 2 - ?? */
+#define CAPTURE_SOURCE_CHANNEL3 0x000f0000	/* 3 - Mic in, Line in, TAD in, Aux in. */
+#define CAPTURE_SOURCE_RECORD_MAP 0x0000ffff	/* Default 0x00e4 */
+						/* Record Map [7:0] (2 bits per channel) 0=mapped to channel 0, 1=mapped to channel 1, 2=mapped to channel2, 3=mapped to channel3 
+						 * Record source select for channel 0 [18:16]
+						 * Record source select for channel 1 [22:20]
+						 * Record source select for channel 2 [26:24]
+						 * Record source select for channel 3 [30:28]
+						 * 0 - SPDIF mixer output.
+						 * 1 - i2s mixer output.
+						 * 2 - SPDIF input.
+						 * 3 - i2s input.
+						 * 4 - AC97 capture.
+						 * 5 - SRC output.
+						 */
+#define CAPTURE_VOLUME1         0x61            /* Capture  volume per channel 0-3 */
+#define CAPTURE_VOLUME2         0x62            /* Capture  volume per channel 4-7 */
+
+#define PLAYBACK_ROUTING1       0x63            /* Playback routing of channels 0-7. Effects AC3 output. Default 0x32765410 */
+#define ROUTING1_REAR           0x77000000      /* Channel_id 0 sends to 10, Channel_id 1 sends to 32 */
+#define ROUTING1_NULL           0x00770000      /* Channel_id 2 sends to 54, Channel_id 3 sends to 76 */
+#define ROUTING1_CENTER_LFE     0x00007700      /* 0x32765410 means, send Channel_id 0 to FRONT, Channel_id 1 to REAR */
+#define ROUTING1_FRONT          0x00000077	/* Channel_id 2 to CENTER_LFE, Channel_id 3 to NULL. */
+						/* Channel_id's handle stereo channels. Channel X is a single mono channel */
+						/* Host is input from the PCI bus. */
+						/* Host channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+						 * Host channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+						 */
+
+#define PLAYBACK_ROUTING2       0x64            /* Playback Routing . Feeding Capture channels back into Playback. Effects AC3 output. Default 0x76767676 */
+						/* SRC is input from the capture inputs. */
+						/* SRC channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+						 * SRC channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+						 */
+
+#define PLAYBACK_MUTE           0x65            /* Unknown. While playing 0x0, while silent 0x00fc0000 */
+						/* SPDIF Mixer input control:
+						 * Invert SRC to SPDIF Mixer [7-0] (One bit per channel)
+						 * Invert Host to SPDIF Mixer [15:8] (One bit per channel)
+						 * SRC to SPDIF Mixer disable [23:16] (One bit per channel)
+						 * Host to SPDIF Mixer disable [31:24] (One bit per channel)
+						 */
+#define PLAYBACK_VOLUME1        0x66            /* Playback SPDIF volume per channel. Set to the same PLAYBACK_VOLUME(0x6a) */
+						/* PLAYBACK_VOLUME1 must be set to 30303030 for SPDIF AC3 Playback */
+						/* SPDIF mixer input volume. 0=12dB, 0x30=0dB, 0xFE=-51.5dB, 0xff=Mute */
+						/* One register for each of the 4 stereo streams. */
+						/* SRC Right volume [7:0]
+						 * SRC Left  volume [15:8]
+						 * Host Right volume [23:16]
+						 * Host Left  volume [31:24]
+						 */
+#define CAPTURE_ROUTING1        0x67            /* Capture Routing. Default 0x32765410 */
+						/* Similar to register 0x63, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_ROUTING2        0x68            /* Unknown Routing. Default 0x76767676 */
+						/* Similar to register 0x64, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_MUTE            0x69            /* Unknown. While capturing 0x0, while silent 0x00fc0000 */
+						/* Similar to register 0x65, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define PLAYBACK_VOLUME2        0x6a            /* Playback Analog volume per channel. Does not effect AC3 output */
+						/* Similar to register 0x66, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define UNKNOWN6b               0x6b            /* Unknown. Readonly. Default 00400000 00400000 00400000 00400000 */
+#define MIDI_UART_A_DATA		0x6c            /* Midi Uart A Data */
+#define MIDI_UART_A_CMD		0x6d            /* Midi Uart A Command/Status */
+#define MIDI_UART_B_DATA		0x6e            /* Midi Uart B Data (currently unused) */
+#define MIDI_UART_B_CMD		0x6f            /* Midi Uart B Command/Status (currently unused) */
+
+/* unique channel identifier for midi->channel */
+
+#define CA0106_MIDI_CHAN_A		0x1
+#define CA0106_MIDI_CHAN_B		0x2
+
+/* from mpu401 */
+
+#define CA0106_MIDI_INPUT_AVAIL 	0x80
+#define CA0106_MIDI_OUTPUT_READY	0x40
+#define CA0106_MPU401_RESET		0xff
+#define CA0106_MPU401_ENTER_UART	0x3f
+#define CA0106_MPU401_ACK		0xfe
+
+#define SAMPLE_RATE_TRACKER_STATUS 0x70         /* Readonly. Default 00108000 00108000 00500000 00500000 */
+						/* Estimated sample rate [19:0] Relative to 48kHz. 0x8000 =  1.0
+						 * Rate Locked [20]
+						 * SPDIF Locked [21] For SPDIF channel only.
+						 * Valid Audio [22] For SPDIF channel only.
+						 */
+#define CAPTURE_CONTROL         0x71            /* Some sort of routing. default = 40c81000 30303030 30300000 00700000 */
+						/* Channel_id 0: 0x40c81000 must be changed to 0x40c80000 for SPDIF AC3 input or output. */
+						/* Channel_id 1: 0xffffffff(mute) 0x30303030(max) controls CAPTURE feedback into PLAYBACK. */
+						/* Sample rate output control register Channel=0
+						 * Sample output rate [1:0] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * Sample input rate [3:2] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+						 * SRC input source select [4] 0=Audio from digital mixer, 1=Audio from analog source.
+						 * Record rate [9:8] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+						 * Record mixer output enable [12:10] 
+						 * I2S input rate master mode [15:14] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * I2S output rate [17:16] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * I2S output source select [18] (0=Audio from host, 1=Audio from SRC)
+						 * Record mixer I2S enable [20:19] (enable/disable i2sin1 and i2sin0)
+						 * I2S output master clock select [21] (0=256*I2S output rate, 1=512*I2S output rate.)
+						 * I2S input master clock select [22] (0=256*I2S input rate, 1=512*I2S input rate.)
+						 * I2S input mode [23] (0=Slave, 1=Master)
+						 * SPDIF output rate [25:24] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+						 * SPDIF output source select [26] (0=host, 1=SRC)
+						 * Not used [27]
+						 * Record Source 0 input [29:28] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+						 * Record Source 1 input [31:30] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+						 */ 
+						/* Sample rate output control register Channel=1
+						 * I2S Input 0 volume Right [7:0]
+						 * I2S Input 0 volume Left [15:8]
+						 * I2S Input 1 volume Right [23:16]
+						 * I2S Input 1 volume Left [31:24]
+						 */
+						/* Sample rate output control register Channel=2
+						 * SPDIF Input volume Right [23:16]
+						 * SPDIF Input volume Left [31:24]
+						 */
+						/* Sample rate output control register Channel=3
+						 * No used
+						 */
+#define SPDIF_SELECT2           0x72            /* Some sort of routing. Channel_id 0 only. default = 0x0f0f003f. Analog 0x000b0000, Digital 0x0b000000 */
+#define ROUTING2_FRONT_MASK     0x00010000      /* Enable for Front speakers. */
+#define ROUTING2_CENTER_LFE_MASK 0x00020000     /* Enable for Center/LFE speakers. */
+#define ROUTING2_REAR_MASK      0x00080000      /* Enable for Rear speakers. */
+						/* Audio output control
+						 * AC97 output enable [5:0]
+						 * I2S output enable [19:16]
+						 * SPDIF output enable [27:24]
+						 */ 
+#define UNKNOWN73               0x73            /* Unknown. Readonly. Default 0x0 */
+#define CHIP_VERSION            0x74            /* P17 Chip version. Channel_id 0 only. Default 00000071 */
+#define EXTENDED_INT_MASK       0x75            /* Used by both playback and capture interrupt handler */
+						/* Sets which Interrupts are enabled. */
+						/* 0x00000001 = Half period. Playback.
+						 * 0x00000010 = Full period. Playback.
+						 * 0x00000100 = Half buffer. Playback.
+						 * 0x00001000 = Full buffer. Playback.
+						 * 0x00010000 = Half buffer. Capture.
+						 * 0x00100000 = Full buffer. Capture.
+						 * Capture can only do 2 periods.
+						 * 0x01000000 = End audio. Playback.
+						 * 0x40000000 = Half buffer Playback,Caputre xrun.
+						 * 0x80000000 = Full buffer Playback,Caputre xrun.
+						 */
+#define EXTENDED_INT            0x76            /* Used by both playback and capture interrupt handler */
+						/* Shows which interrupts are active at the moment. */
+						/* Same bit layout as EXTENDED_INT_MASK */
+#define COUNTER77               0x77		/* Counter range 0 to 0x3fffff, 192000 counts per second. */
+#define COUNTER78               0x78		/* Counter range 0 to 0x3fffff, 44100 counts per second. */
+#define EXTENDED_INT_TIMER      0x79            /* Channel_id 0 only. Used by both playback and capture interrupt handler */
+						/* Causes interrupts based on timer intervals. */
+#define SPI			0x7a		/* SPI: Serial Interface Register */
+#define I2C_A			0x7b		/* I2C Address. 32 bit */
+#define I2C_D0			0x7c		/* I2C Data Port 0. 32 bit */
+#define I2C_D1			0x7d		/* I2C Data Port 1. 32 bit */
+//I2C values
+#define I2C_A_ADC_ADD_MASK	0x000000fe	//The address is a 7 bit address
+#define I2C_A_ADC_RW_MASK	0x00000001	//bit mask for R/W
+#define I2C_A_ADC_TRANS_MASK	0x00000010  	//Bit mask for I2c address DAC value
+#define I2C_A_ADC_ABORT_MASK	0x00000020	//Bit mask for I2C transaction abort flag
+#define I2C_A_ADC_LAST_MASK	0x00000040	//Bit mask for Last word transaction
+#define I2C_A_ADC_BYTE_MASK	0x00000080	//Bit mask for Byte Mode
+
+#define I2C_A_ADC_ADD		0x00000034	//This is the Device address for ADC 
+#define I2C_A_ADC_READ		0x00000001	//To perform a read operation
+#define I2C_A_ADC_START		0x00000100	//Start I2C transaction
+#define I2C_A_ADC_ABORT		0x00000200	//I2C transaction abort
+#define I2C_A_ADC_LAST		0x00000400	//I2C last transaction
+#define I2C_A_ADC_BYTE		0x00000800	//I2C one byte mode
+
+#define I2C_D_ADC_REG_MASK	0xfe000000  	//ADC address register 
+#define I2C_D_ADC_DAT_MASK	0x01ff0000  	//ADC data register
+
+#define ADC_TIMEOUT		0x00000007	//ADC Timeout Clock Disable
+#define ADC_IFC_CTRL		0x0000000b	//ADC Interface Control
+#define ADC_MASTER		0x0000000c	//ADC Master Mode Control
+#define ADC_POWER		0x0000000d	//ADC PowerDown Control
+#define ADC_ATTEN_ADCL		0x0000000e	//ADC Attenuation ADCL
+#define ADC_ATTEN_ADCR		0x0000000f	//ADC Attenuation ADCR
+#define ADC_ALC_CTRL1		0x00000010	//ADC ALC Control 1
+#define ADC_ALC_CTRL2		0x00000011	//ADC ALC Control 2
+#define ADC_ALC_CTRL3		0x00000012	//ADC ALC Control 3
+#define ADC_NOISE_CTRL		0x00000013	//ADC Noise Gate Control
+#define ADC_LIMIT_CTRL		0x00000014	//ADC Limiter Control
+#define ADC_MUX			0x00000015  	//ADC Mux offset
+
+#if 0
+/* FIXME: Not tested yet. */
+#define ADC_GAIN_MASK		0x000000ff	//Mask for ADC Gain
+#define ADC_ZERODB		0x000000cf	//Value to set ADC to 0dB
+#define ADC_MUTE_MASK		0x000000c0	//Mask for ADC mute
+#define ADC_MUTE		0x000000c0	//Value to mute ADC
+#define ADC_OSR			0x00000008	//Mask for ADC oversample rate select
+#define ADC_TIMEOUT_DISABLE	0x00000008	//Value and mask to disable Timeout clock
+#define ADC_HPF_DISABLE		0x00000100	//Value and mask to disable High pass filter
+#define ADC_TRANWIN_MASK	0x00000070	//Mask for Length of Transient Window
+#endif
+
+#define ADC_MUX_MASK		0x0000000f	//Mask for ADC Mux
+#define ADC_MUX_PHONE		0x00000001	//Value to select TAD at ADC Mux (Not used)
+#define ADC_MUX_MIC		0x00000002	//Value to select Mic at ADC Mux
+#define ADC_MUX_LINEIN		0x00000004	//Value to select LineIn at ADC Mux
+#define ADC_MUX_AUX		0x00000008	//Value to select Aux at ADC Mux
+
+#define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_UNKNOWN_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_UNKNOWN_CHANNEL 2
+
+
+/* Based on WM8768 Datasheet Rev 4.2 page 32 */
+#define SPI_REG_MASK	0x1ff	/* 16-bit SPI writes have a 7-bit address */
+#define SPI_REG_SHIFT	9	/* followed by 9 bits of data */
+
+#define SPI_LDA1_REG		0	/* digital attenuation */
+#define SPI_RDA1_REG		1
+#define SPI_LDA2_REG		4
+#define SPI_RDA2_REG		5
+#define SPI_LDA3_REG		6
+#define SPI_RDA3_REG		7
+#define SPI_LDA4_REG		13
+#define SPI_RDA4_REG		14
+#define SPI_MASTDA_REG		8
+
+#define SPI_DA_BIT_UPDATE	(1<<8)	/* update attenuation values */
+#define SPI_DA_BIT_0dB		0xff	/* 0 dB */
+#define SPI_DA_BIT_infdB	0x00	/* inf dB attenuation (mute) */
+
+#define SPI_PL_REG		2
+#define SPI_PL_BIT_L_M		(0<<5)	/* left channel = mute */
+#define SPI_PL_BIT_L_L		(1<<5)	/* left channel = left */
+#define SPI_PL_BIT_L_R		(2<<5)	/* left channel = right */
+#define SPI_PL_BIT_L_C		(3<<5)	/* left channel = (L+R)/2 */
+#define SPI_PL_BIT_R_M		(0<<7)	/* right channel = mute */
+#define SPI_PL_BIT_R_L		(1<<7)	/* right channel = left */
+#define SPI_PL_BIT_R_R		(2<<7)	/* right channel = right */
+#define SPI_PL_BIT_R_C		(3<<7)	/* right channel = (L+R)/2 */
+#define SPI_IZD_REG		2
+#define SPI_IZD_BIT		(1<<4)	/* infinite zero detect */
+
+#define SPI_FMT_REG		3
+#define SPI_FMT_BIT_RJ		(0<<0)	/* right justified mode */
+#define SPI_FMT_BIT_LJ		(1<<0)	/* left justified mode */
+#define SPI_FMT_BIT_I2S		(2<<0)	/* I2S mode */
+#define SPI_FMT_BIT_DSP		(3<<0)	/* DSP Modes A or B */
+#define SPI_LRP_REG		3
+#define SPI_LRP_BIT		(1<<2)	/* invert LRCLK polarity */
+#define SPI_BCP_REG		3
+#define SPI_BCP_BIT		(1<<3)	/* invert BCLK polarity */
+#define SPI_IWL_REG		3
+#define SPI_IWL_BIT_16		(0<<4)	/* 16-bit world length */
+#define SPI_IWL_BIT_20		(1<<4)	/* 20-bit world length */
+#define SPI_IWL_BIT_24		(2<<4)	/* 24-bit world length */
+#define SPI_IWL_BIT_32		(3<<4)	/* 32-bit world length */
+
+#define SPI_MS_REG		10
+#define SPI_MS_BIT		(1<<5)	/* master mode */
+#define SPI_RATE_REG		10	/* only applies in master mode */
+#define SPI_RATE_BIT_128	(0<<6)	/* MCLK = LRCLK * 128 */
+#define SPI_RATE_BIT_192	(1<<6)
+#define SPI_RATE_BIT_256	(2<<6)
+#define SPI_RATE_BIT_384	(3<<6)
+#define SPI_RATE_BIT_512	(4<<6)
+#define SPI_RATE_BIT_768	(5<<6)
+
+/* They really do label the bit for the 4th channel "4" and not "3" */
+#define SPI_DMUTE0_REG		9
+#define SPI_DMUTE1_REG		9
+#define SPI_DMUTE2_REG		9
+#define SPI_DMUTE4_REG		15
+#define SPI_DMUTE0_BIT		(1<<3)
+#define SPI_DMUTE1_BIT		(1<<4)
+#define SPI_DMUTE2_BIT		(1<<5)
+#define SPI_DMUTE4_BIT		(1<<2)
+
+#define SPI_PHASE0_REG		3
+#define SPI_PHASE1_REG		3
+#define SPI_PHASE2_REG		3
+#define SPI_PHASE4_REG		15
+#define SPI_PHASE0_BIT		(1<<6)
+#define SPI_PHASE1_BIT		(1<<7)
+#define SPI_PHASE2_BIT		(1<<8)
+#define SPI_PHASE4_BIT		(1<<3)
+
+#define SPI_PDWN_REG		2	/* power down all DACs */
+#define SPI_PDWN_BIT		(1<<2)
+#define SPI_DACD0_REG		10	/* power down individual DACs */
+#define SPI_DACD1_REG		10
+#define SPI_DACD2_REG		10
+#define SPI_DACD4_REG		15
+#define SPI_DACD0_BIT		(1<<1)
+#define SPI_DACD1_BIT		(1<<2)
+#define SPI_DACD2_BIT		(1<<3)
+#define SPI_DACD4_BIT		(1<<0)	/* datasheet error says it's 1 */
+
+#define SPI_PWRDNALL_REG	10	/* power down everything */
+#define SPI_PWRDNALL_BIT	(1<<4)
+
+#include "ca_midi.h"
+
+struct snd_ca0106;
+
+struct snd_ca0106_channel {
+	struct snd_ca0106 *emu;
+	int number;
+	int use;
+	void (*interrupt)(struct snd_ca0106 *emu, struct snd_ca0106_channel *channel);
+	struct snd_ca0106_pcm *epcm;
+};
+
+struct snd_ca0106_pcm {
+	struct snd_ca0106 *emu;
+	struct snd_pcm_substream *substream;
+        int channel_id;
+	unsigned short running;
+};
+
+struct snd_ca0106_details {
+        u32 serial;
+        char * name;
+        int ac97;
+	int gpio_type;
+	int i2c_adc;
+	int spi_dac;
+};
+
+// definition of the chip-specific record
+struct snd_ca0106 {
+	struct snd_card *card;
+	struct snd_ca0106_details *details;
+	struct pci_dev *pci;
+
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+
+	unsigned int serial;            /* serial number */
+	unsigned short model;		/* subsystem id */
+
+	spinlock_t emu_lock;
+
+	struct snd_ac97 *ac97;
+	struct snd_pcm *pcm;
+
+	struct snd_ca0106_channel playback_channels[4];
+	struct snd_ca0106_channel capture_channels[4];
+	u32 spdif_bits[4];             /* s/pdif out setup */
+	int spdif_enable;
+	int capture_source;
+	int i2c_capture_source;
+	u8 i2c_capture_volume[4][2];
+	int capture_mic_line_in;
+
+	struct snd_dma_buffer buffer;
+
+	struct snd_ca_midi midi;
+	struct snd_ca_midi midi2;
+
+	u16 spi_dac_reg[16];
+};
+
+int snd_ca0106_mixer(struct snd_ca0106 *emu);
+int snd_ca0106_proc_init(struct snd_ca0106 * emu);
+
+unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, 
+				 unsigned int reg, 
+				 unsigned int chn);
+
+void snd_ca0106_ptr_write(struct snd_ca0106 *emu, 
+			  unsigned int reg, 
+			  unsigned int chn, 
+			  unsigned int data);
+
+int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
+
+int snd_ca0106_spi_write(struct snd_ca0106 * emu,
+				   unsigned int data);
diff -ruN linux-2.6.22/sound/pci/ca0106/ca0106_main.c linux-2.6.22-alsa/sound/pci/ca0106/ca0106_main.c
--- linux-2.6.22/sound/pci/ca0106/ca0106_main.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca0106_main.c	2007-09-02 13:30:43.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.23
+ *  Version: 0.0.25
  *
  *  FEATURES currently supported:
  *    Front, Rear and Center/LFE.
@@ -79,6 +79,10 @@
  *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
  *  0.0.23
  *    Implement support for Line-in capture on SB Live 24bit.
+ *  0.0.24
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
+ *  0.0.25
+ *    Powerdown SPI DAC channels when not in use
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-ca0106 kernel module.
@@ -168,6 +172,34 @@
 #include "ca0106.h"
 
 static struct snd_ca0106_details ca0106_chip_details[] = {
+	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
+	 /* It is really just a normal SB Live 24bit. */
+	 /* Tested:
+	  * See ALSA bug#3251
+	  */
+	 { .serial = 0x10131102,
+	   .name   = "X-Fi Extreme Audio [SBxxxx]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
+	 /* It is really just a normal SB Live 24bit. */
+	 /*
+ 	  * CTRL:CA0111-WTLF
+	  * ADC: WM8775SEDS
+	  * DAC: CS4382-KQZ
+	  */
+	 /* Tested:
+	  * Playback on front, rear, center/lfe speakers
+	  * Capture from Mic in.
+	  * Not-Tested:
+	  * Capture from Line in.
+	  * Playback to digital out.
+	  */
+	 { .serial = 0x10121102,
+	   .name   = "X-Fi Extreme Audio [SB0790]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
 	 /* AudigyLS[SB0310] */
 	 { .serial = 0x10021102,
 	   .name   = "AudigyLS [SB0310]",
@@ -242,10 +274,11 @@
 
 /* hardware definition */
 static struct snd_pcm_hardware snd_ca0106_playback_hw = {
-	.info =			(SNDRV_PCM_INFO_MMAP | 
-				 SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID),
+	.info =			SNDRV_PCM_INFO_MMAP | 
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_SYNC_START,
 	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
 	.rates =		(SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
 				 SNDRV_PCM_RATE_192000),
@@ -428,6 +461,19 @@
 	kfree(runtime->private_data);
 }
 
+static const int spi_dacd_reg[] = {
+	[PCM_FRONT_CHANNEL]	= SPI_DACD4_REG,
+	[PCM_REAR_CHANNEL]	= SPI_DACD0_REG,
+	[PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG,
+	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_REG,
+};
+static const int spi_dacd_bit[] = {
+	[PCM_FRONT_CHANNEL]	= SPI_DACD4_BIT,
+	[PCM_REAR_CHANNEL]	= SPI_DACD0_BIT,
+	[PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT,
+	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_BIT,
+};
+
 /* open_playback callback */
 static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
 						int channel_id)
@@ -462,6 +508,17 @@
                 return err;
 	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
                 return err;
+	snd_pcm_set_sync(substream);
+
+	if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) {
+		const int reg = spi_dacd_reg[channel_id];
+
+		/* Power up dac */
+		chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id];
+		err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+		if (err < 0)
+			return err;
+	}
 	return 0;
 }
 
@@ -472,6 +529,14 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
         struct snd_ca0106_pcm *epcm = runtime->private_data;
 	chip->playback_channels[epcm->channel_id].use = 0;
+
+	if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
+		const int reg = spi_dacd_reg[epcm->channel_id];
+
+		/* Power down DAC */
+		chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id];
+		snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+	}
 	/* FIXME: maybe zero others */
 	return 0;
 }
@@ -790,6 +855,9 @@
 		break;
 	}
         snd_pcm_group_for_each_entry(s, substream) {
+		if (snd_pcm_substream_chip(s) != emu ||
+		    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			continue;
 		runtime = s->runtime;
 		epcm = runtime->private_data;
 		channel = epcm->channel_id;
@@ -1195,28 +1263,23 @@
 	return 0;
 }
 
+#define SPI_REG(reg, value)	(((reg) << SPI_REG_SHIFT) | (value))
 static unsigned int spi_dac_init[] = {
-	0x00ff,
-	0x02ff,
-	0x0400,
-	0x0520,
-	0x0620, /* Set 24 bit. Was 0x0600 */
-	0x08ff,
-	0x0aff,
-	0x0cff,
-	0x0eff,
-	0x10ff,
-	0x1200,
-	0x1400,
-	0x1480,
-	0x1800,
-	0x1aff,
-	0x1cff,
-	0x1e00,
-	0x0530,
-	0x0602,
-	0x0622,
-	0x1400,
+	SPI_REG(SPI_LDA1_REG,	SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
+	SPI_REG(SPI_RDA1_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
+	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S | SPI_IWL_BIT_24),
+	SPI_REG(SPI_LDA2_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA2_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_LDA3_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA3_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_MASTDA_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(9,		0x00),
+	SPI_REG(SPI_MS_REG,	SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT),
+	SPI_REG(12,		0x00),
+	SPI_REG(SPI_LDA4_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA4_REG,	SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE),
+	SPI_REG(SPI_DACD4_REG,	0x00),
 };
 
 static unsigned int i2c_adc_init[][2] = {
@@ -1457,8 +1520,13 @@
 		int size, n;
 
 		size = ARRAY_SIZE(spi_dac_init);
-		for (n=0; n < size; n++)
+		for (n = 0; n < size; n++) {
+			int reg = spi_dac_init[n] >> SPI_REG_SHIFT;
+
 			snd_ca0106_spi_write(chip, spi_dac_init[n]);
+			if (reg < ARRAY_SIZE(chip->spi_dac_reg))
+				chip->spi_dac_reg[reg] = spi_dac_init[n];
+		}
 	}
 
 	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
diff -ruN linux-2.6.22/sound/pci/ca0106/ca0106_main.c.orig linux-2.6.22-alsa/sound/pci/ca0106/ca0106_main.c.orig
--- linux-2.6.22/sound/pci/ca0106/ca0106_main.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca0106_main.c.orig	2007-09-01 20:55:23.000000000 +0200
@@ -0,0 +1,1732 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.25
+ *
+ *  FEATURES currently supported:
+ *    Front, Rear and Center/LFE.
+ *    Surround40 and Surround51.
+ *    Capture from MIC an LINE IN input.
+ *    SPDIF digital playback of PCM stereo and AC3/DTS works.
+ *    (One can use a standard mono mini-jack to one RCA plugs cable.
+ *     or one can use a standard stereo mini-jack to two RCA plugs cable.
+ *     Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
+ *    ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. )
+ *    Notes on how to capture sound:
+ *      The AC97 is used in the PLAYBACK direction.
+ *      The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC.
+ *      So, to record from the MIC, set the MIC Playback volume to max,
+ *      unmute the MIC and turn up the MASTER Playback volume.
+ *      So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume.
+ *   
+ *    The only playback controls that currently do anything are: -
+ *    Analog Front
+ *    Analog Rear
+ *    Analog Center/LFE
+ *    SPDIF Front
+ *    SPDIF Rear
+ *    SPDIF Center/LFE
+ *   
+ *    For capture from Mic in or Line in.
+ *    Digital/Analog ( switch must be in Analog mode for CAPTURE. )
+ * 
+ *    CAPTURE feedback into PLAYBACK
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Minor updates.
+ *  0.0.16
+ *    Implement 192000 sample rate.
+ *  0.0.17
+ *    Add support for SB0410 and SB0413.
+ *  0.0.18
+ *    Modified Copyright message.
+ *  0.0.19
+ *    Finally fix support for SB Live 24 bit. SB0410 and SB0413.
+ *    The output codec needs resetting, otherwise all output is muted.
+ *  0.0.20
+ *    Merge "pci_disable_device(pci);" fixes.
+ *  0.0.21
+ *    Add 4 capture channels. (SPDIF only comes in on channel 0. )
+ *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
+ *  0.0.22
+ *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
+ *  0.0.23
+ *    Implement support for Line-in capture on SB Live 24bit.
+ *  0.0.24
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
+ *  0.0.25
+ *    Powerdown SPI DAC channels when not in use
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-ca0106 kernel module.
+ *    --
+ *
+ *  TODO:
+ *    4 Capture channels, only one implemented so far.
+ *    Other capture rates apart from 48khz not implemented.
+ *    MIDI
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0310
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: WM8746EDS (6-channel, 24bit, 192Khz)
+ *
+ *  GENERAL INFO:
+ *    Model: SB0410
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: None
+ *    ADC: WM8775EDS (4 Channel)
+ *    DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support)
+ *    SPDIF Out control switches between Mic in and SPDIF out.
+ *    No sound out or mic input working yet.
+ * 
+ *  GENERAL INFO:
+ *    Model: SB0413
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: None.
+ *    ADC: Unknown
+ *    DAC: Unknown
+ *    Trying to handle it like the SB0410.
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.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; 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/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
+MODULE_DESCRIPTION("CA0106");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");
+
+// module parameters (see "Module Parameters")
+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 uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the CA0106 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the CA0106 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
+module_param_array(subsystem, uint, NULL, 0444);
+MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
+
+#include "ca0106.h"
+
+static struct snd_ca0106_details ca0106_chip_details[] = {
+	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
+	 /* It is really just a normal SB Live 24bit. */
+	 /* Tested:
+	  * See ALSA bug#3251
+	  */
+	 { .serial = 0x10131102,
+	   .name   = "X-Fi Extreme Audio [SBxxxx]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
+	 /* It is really just a normal SB Live 24bit. */
+	 /*
+ 	  * CTRL:CA0111-WTLF
+	  * ADC: WM8775SEDS
+	  * DAC: CS4382-KQZ
+	  */
+	 /* Tested:
+	  * Playback on front, rear, center/lfe speakers
+	  * Capture from Mic in.
+	  * Not-Tested:
+	  * Capture from Line in.
+	  * Playback to digital out.
+	  */
+	 { .serial = 0x10121102,
+	   .name   = "X-Fi Extreme Audio [SB0790]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+	 /* AudigyLS[SB0310] */
+	 { .serial = 0x10021102,
+	   .name   = "AudigyLS [SB0310]",
+	   .ac97   = 1 } , 
+	 /* Unknown AudigyLS that also says SB0310 on it */
+	 { .serial = 0x10051102,
+	   .name   = "AudigyLS [SB0310b]",
+	   .ac97   = 1 } ,
+	 /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+	 { .serial = 0x10061102,
+	   .name   = "Live! 7.1 24bit [SB0410]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+	 { .serial = 0x10071102,
+	   .name   = "Live! 7.1 24bit [SB0413]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Audigy SE. Has a different DAC. */
+	 /* SB0570:
+	  * CTRL:CA0106-DAT
+	  * ADC: WM8775EDS
+	  * DAC: WM8768GEDS
+	  */
+	 { .serial = 0x100a1102,
+	   .name   = "Audigy SE [SB0570]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1,
+	   .spi_dac = 1 } ,
+	 /* New Audigy LS. Has a different DAC. */
+	 /* SB0570:
+	  * CTRL:CA0106-DAT
+	  * ADC: WM8775EDS
+	  * DAC: WM8768GEDS
+	  */
+	 { .serial = 0x10111102,
+	   .name   = "Audigy SE OEM [SB0570a]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1,
+	   .spi_dac = 1 } ,
+	 /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+	 /* SB0438
+	  * CTRL:CA0106-DAT
+	  * ADC: WM8775SEDS
+	  * DAC: CS4382-KQZ
+	  */
+	 { .serial = 0x10091462,
+	   .name   = "MSI K8N Diamond MB [SB0438]",
+	   .gpio_type = 2,
+	   .i2c_adc = 1 } ,
+	 /* Shuttle XPC SD31P which has an onboard Creative Labs
+	  * Sound Blaster Live! 24-bit EAX
+	  * high-definition 7.1 audio processor".
+	  * Added using info from andrewvegan in alsa bug #1298
+	  */
+	 { .serial = 0x30381297,
+	   .name   = "Shuttle XPC SD31P [SD31P]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	/* Shuttle XPC SD11G5 which has an onboard Creative Labs
+	 * Sound Blaster Live! 24-bit EAX
+	 * high-definition 7.1 audio processor".
+	 * Fixes ALSA bug#1600
+         */
+	{ .serial = 0x30411297,
+	  .name = "Shuttle XPC SD11G5 [SD11G5]",
+	  .gpio_type = 1,
+	  .i2c_adc = 1 } ,
+	 { .serial = 0,
+	   .name   = "AudigyLS [Unknown]" }
+};
+
+/* hardware definition */
+static struct snd_pcm_hardware snd_ca0106_playback_hw = {
+	.info =			SNDRV_PCM_INFO_MMAP | 
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_SYNC_START,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000),
+	.rate_min =		48000,
+	.rate_max =		192000,
+	.channels_min =		2,  //1,
+	.channels_max =		2,  //6,
+	.buffer_bytes_max =	((65536 - 64) * 8),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 64),
+	.periods_min =		2,
+	.periods_max =		8,
+	.fifo_size =		0,
+};
+
+static struct snd_pcm_hardware snd_ca0106_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+				 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	((65536 - 64) * 8),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 64),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, 
+					  unsigned int reg, 
+					  unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+  
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + PTR);
+	val = inl(emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+void snd_ca0106_ptr_write(struct snd_ca0106 *emu, 
+				   unsigned int reg, 
+				   unsigned int chn, 
+				   unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + PTR);
+	outl(data, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+int snd_ca0106_spi_write(struct snd_ca0106 * emu,
+				   unsigned int data)
+{
+	unsigned int reset, set;
+	unsigned int reg, tmp;
+	int n, result;
+	reg = SPI;
+	if (data > 0xffff) /* Only 16bit values allowed */
+		return 1;
+	tmp = snd_ca0106_ptr_read(emu, reg, 0);
+	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
+	set = reset | 0x10000; /* Set xxx1xxxx */
+	snd_ca0106_ptr_write(emu, reg, 0, reset | data);
+	tmp = snd_ca0106_ptr_read(emu, reg, 0); /* write post */
+	snd_ca0106_ptr_write(emu, reg, 0, set | data);
+	result = 1;
+	/* Wait for status bit to return to 0 */
+	for (n = 0; n < 100; n++) {
+		udelay(10);
+		tmp = snd_ca0106_ptr_read(emu, reg, 0);
+		if (!(tmp & 0x10000)) {
+			result = 0;
+			break;
+		}
+	}
+	if (result) /* Timed out */
+		return 1;
+	snd_ca0106_ptr_write(emu, reg, 0, reset | data);
+	tmp = snd_ca0106_ptr_read(emu, reg, 0); /* Write post */
+	return 0;
+}
+
+/* The ADC does not support i2c read, so only write is implemented */
+int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
+				u32 reg,
+				u32 value)
+{
+	u32 tmp;
+	int timeout = 0;
+	int status;
+	int retry;
+	if ((reg > 0x7f) || (value > 0x1ff)) {
+		snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+		return -EINVAL;
+	}
+
+	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_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
+
+	/* This controls the I2C connected to the WM8775 ADC Codec */
+	snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp);
+
+	for (retry = 0; retry < 10; retry++) {
+		/* Send the data to i2c */
+		//tmp = snd_ca0106_ptr_read(emu, I2C_A, 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_ca0106_ptr_write(emu, I2C_A, 0, tmp);
+
+		/* Wait till the transaction ends */
+		while (1) {
+			status = snd_ca0106_ptr_read(emu, I2C_A, 0);
+                	//snd_printk("I2C:status=0x%x\n", status);
+			timeout++;
+			if ((status & I2C_A_ADC_START) == 0)
+				break;
+
+			if (timeout > 1000)
+				break;
+		}
+		//Read back and see if the transaction is successful
+		if ((status & I2C_A_ADC_ABORT) == 0)
+			break;
+	}
+
+	if (retry == 10) {
+		snd_printk(KERN_ERR "Writing to ADC failed!\n");
+		return -EINVAL;
+	}
+    
+    	return 0;
+}
+
+
+static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) | intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) & ~intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+
+static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	kfree(runtime->private_data);
+}
+
+static const int spi_dacd_reg[] = {
+	[PCM_FRONT_CHANNEL]	= SPI_DACD4_REG,
+	[PCM_REAR_CHANNEL]	= SPI_DACD0_REG,
+	[PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG,
+	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_REG,
+};
+static const int spi_dacd_bit[] = {
+	[PCM_FRONT_CHANNEL]	= SPI_DACD4_BIT,
+	[PCM_REAR_CHANNEL]	= SPI_DACD0_BIT,
+	[PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT,
+	[PCM_UNKNOWN_CHANNEL]	= SPI_DACD1_BIT,
+};
+
+/* open_playback callback */
+static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
+						int channel_id)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+        struct snd_ca0106_channel *channel = &(chip->playback_channels[channel_id]);
+	struct snd_ca0106_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = chip;
+	epcm->substream = substream;
+        epcm->channel_id=channel_id;
+  
+	runtime->private_data = epcm;
+	runtime->private_free = snd_ca0106_pcm_free_substream;
+  
+	runtime->hw = snd_ca0106_playback_hw;
+
+        channel->emu = chip;
+        channel->number = channel_id;
+
+	channel->use = 1;
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+	channel->epcm = epcm;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+	snd_pcm_set_sync(substream);
+
+	if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) {
+		const int reg = spi_dacd_reg[channel_id];
+
+		/* Power up dac */
+		chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id];
+		err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+        struct snd_ca0106_pcm *epcm = runtime->private_data;
+	chip->playback_channels[epcm->channel_id].use = 0;
+
+	if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
+		const int reg = spi_dacd_reg[epcm->channel_id];
+
+		/* Power down DAC */
+		chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id];
+		snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+	}
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+static int snd_ca0106_pcm_open_playback_front(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_center_lfe(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_unknown(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_rear(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL);
+}
+
+/* open_capture callback */
+static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substream,
+					       int channel_id)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+        struct snd_ca0106_channel *channel = &(chip->capture_channels[channel_id]);
+	struct snd_ca0106_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL) {
+		snd_printk(KERN_ERR "open_capture_channel: failed epcm alloc\n");
+		return -ENOMEM;
+        }
+	epcm->emu = chip;
+	epcm->substream = substream;
+        epcm->channel_id=channel_id;
+  
+	runtime->private_data = epcm;
+	runtime->private_free = snd_ca0106_pcm_free_substream;
+  
+	runtime->hw = snd_ca0106_capture_hw;
+
+        channel->emu = chip;
+        channel->number = channel_id;
+
+	channel->use = 1;
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+        channel->epcm = epcm;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+	//snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+	return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+        struct snd_ca0106_pcm *epcm = runtime->private_data;
+	chip->capture_channels[epcm->channel_id].use = 0;
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+static int snd_ca0106_pcm_open_0_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 0);
+}
+
+static int snd_ca0106_pcm_open_1_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 1);
+}
+
+static int snd_ca0106_pcm_open_2_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 2);
+}
+
+static int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream)
+{
+	return snd_ca0106_pcm_open_capture_channel(substream, 3);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_playback(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_playback(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare playback callback */
+static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	int channel = epcm->channel_id;
+	u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel));
+	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+	u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
+	u32 hcfg_set = 0x00000000;
+	u32 hcfg;
+	u32 reg40_mask = 0x30000 << (channel<<1);
+	u32 reg40_set = 0;
+	u32 reg40;
+	/* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */
+	u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */
+	u32 reg71_set = 0;
+	u32 reg71;
+	int i;
+	
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+	//snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+	/* Rate can be set per channel. */
+	/* reg40 control host to fifo */
+	/* reg71 controls DAC rate. */
+	switch (runtime->rate) {
+	case 44100:
+		reg40_set = 0x10000 << (channel<<1);
+		reg71_set = 0x01010000; 
+		break;
+        case 48000:
+		reg40_set = 0;
+		reg71_set = 0; 
+		break;
+	case 96000:
+		reg40_set = 0x20000 << (channel<<1);
+		reg71_set = 0x02020000; 
+		break;
+	case 192000:
+		reg40_set = 0x30000 << (channel<<1);
+		reg71_set = 0x03030000; 
+		break;
+	default:
+		reg40_set = 0;
+		reg71_set = 0; 
+		break;
+	}
+	/* Format is a global setting */
+	/* FIXME: Only let the first channel accessed set this. */
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hcfg_set = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hcfg_set = HCFG_PLAYBACK_S32_LE;
+		break;
+	default:
+		hcfg_set = 0;
+		break;
+	}
+	hcfg = inl(emu->port + HCFG) ;
+	hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+	outl(hcfg, emu->port + HCFG);
+	reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
+	reg40 = (reg40 & ~reg40_mask) | reg40_set;
+	snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
+	reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+	reg71 = (reg71 & ~reg71_mask) | reg71_set;
+	snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+
+	/* FIXME: Check emu->buffer.size before actually writing to it. */
+        for(i=0; i < runtime->periods; i++) {
+		table_base[i*2] = runtime->dma_addr + (i * period_size_bytes);
+		table_base[i*2+1] = period_size_bytes << 16;
+	}
+ 
+	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel));
+	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+	snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+	snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+	/* FIXME  test what 0 bytes does. */
+	snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
+	snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0);
+	snd_ca0106_ptr_write(emu, 0x07, channel, 0x0);
+	snd_ca0106_ptr_write(emu, 0x08, channel, 0);
+        snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */
+#if 0
+	snd_ca0106_ptr_write(emu, SPCS0, 0,
+			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			       SPCS_GENERATIONSTATUS | 0x00001200 |
+			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT );
+	}
+#endif
+
+	return 0;
+}
+
+/* prepare capture callback */
+static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	int channel = epcm->channel_id;
+	u32 hcfg_mask = HCFG_CAPTURE_S32_LE;
+	u32 hcfg_set = 0x00000000;
+	u32 hcfg;
+	u32 over_sampling=0x2;
+	u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */
+	u32 reg71_set = 0;
+	u32 reg71;
+	
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+	//snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+	/* reg71 controls ADC rate. */
+	switch (runtime->rate) {
+	case 44100:
+		reg71_set = 0x00004000;
+		break;
+        case 48000:
+		reg71_set = 0; 
+		break;
+	case 96000:
+		reg71_set = 0x00008000;
+		over_sampling=0xa;
+		break;
+	case 192000:
+		reg71_set = 0x0000c000; 
+		over_sampling=0xa;
+		break;
+	default:
+		reg71_set = 0; 
+		break;
+	}
+	/* Format is a global setting */
+	/* FIXME: Only let the first channel accessed set this. */
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hcfg_set = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hcfg_set = HCFG_CAPTURE_S32_LE;
+		break;
+	default:
+		hcfg_set = 0;
+		break;
+	}
+	hcfg = inl(emu->port + HCFG) ;
+	hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+	outl(hcfg, emu->port + HCFG);
+	reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+	reg71 = (reg71 & ~reg71_mask) | reg71_set;
+	snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+        if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+	        snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */
+	}
+
+
+        //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
+	snd_ca0106_ptr_write(emu, 0x13, channel, 0);
+	snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+	snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+	snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0);
+
+	return 0;
+}
+
+/* trigger_playback callback */
+static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime;
+	struct snd_ca0106_pcm *epcm;
+	int channel;
+	int result = 0;
+        struct snd_pcm_substream *s;
+	u32 basic = 0;
+	u32 extended = 0;
+	int running=0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		running=1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	default:
+		running=0;
+		break;
+	}
+        snd_pcm_group_for_each_entry(s, substream) {
+		if (snd_pcm_substream_chip(s) != emu ||
+		    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			continue;
+		runtime = s->runtime;
+		epcm = runtime->private_data;
+		channel = epcm->channel_id;
+		//snd_printk("channel=%d\n",channel);
+		epcm->running = running;
+		basic |= (0x1<<channel);
+		extended |= (0x10<<channel);
+                snd_pcm_trigger_done(s, substream);
+        }
+	//snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* trigger_capture callback */
+static int snd_ca0106_pcm_trigger_capture(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	int channel = epcm->channel_id;
+	int result = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+		epcm->running = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+	int channel = epcm->channel_id;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+	ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+	ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+	if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr2+= (ptr4 >> 3) * runtime->period_size;
+	ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+	//printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+	return ptr;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ca0106_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+	int channel = channel=epcm->channel_id;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+	//printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+	return ptr;
+}
+
+/* operators */
+static struct snd_pcm_ops snd_ca0106_playback_front_ops = {
+	.open =        snd_ca0106_pcm_open_playback_front,
+	.close =       snd_ca0106_pcm_close_playback,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_playback,
+	.hw_free =     snd_ca0106_pcm_hw_free_playback,
+	.prepare =     snd_ca0106_pcm_prepare_playback,
+	.trigger =     snd_ca0106_pcm_trigger_playback,
+	.pointer =     snd_ca0106_pcm_pointer_playback,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_0_ops = {
+	.open =        snd_ca0106_pcm_open_0_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_1_ops = {
+	.open =        snd_ca0106_pcm_open_1_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_2_ops = {
+	.open =        snd_ca0106_pcm_open_2_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_capture_3_ops = {
+	.open =        snd_ca0106_pcm_open_3_capture,
+	.close =       snd_ca0106_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_ca0106_pcm_hw_params_capture,
+	.hw_free =     snd_ca0106_pcm_hw_free_capture,
+	.prepare =     snd_ca0106_pcm_prepare_capture,
+	.trigger =     snd_ca0106_pcm_trigger_capture,
+	.pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
+        .open =         snd_ca0106_pcm_open_playback_center_lfe,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+        .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
+        .open =         snd_ca0106_pcm_open_playback_unknown,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+        .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+static struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
+        .open =         snd_ca0106_pcm_open_playback_rear,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+		.hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+
+static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
+					     unsigned short reg)
+{
+	struct snd_ca0106 *emu = ac97->private_data;
+	unsigned long flags;
+	unsigned short val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	val = inw(emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
+				    unsigned short reg, unsigned short val)
+{
+	struct snd_ca0106 *emu = ac97->private_data;
+	unsigned long flags;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	outw(val, emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_ca0106_ac97(struct snd_ca0106 *chip)
+{
+	struct snd_ac97_bus *pbus;
+	struct snd_ac97_template ac97;
+	int err;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_ca0106_ac97_write,
+		.read = snd_ca0106_ac97_read,
+	};
+  
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+		return err;
+	pbus->no_vra = 1; /* we don't need VRA */
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+	ac97.scaps = AC97_SCAP_NO_SPDIF;
+	return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_ca0106_free(struct snd_ca0106 *chip)
+{
+	if (chip->res_port != NULL) {    /* avoid access to already used hardware */
+		// disable interrupts
+		snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
+		outl(0, chip->port + INTE);
+		snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
+		udelay(1000);
+		// disable audio
+		//outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+		outl(0, chip->port + HCFG);
+		/* FIXME: We need to stop and DMA transfers here.
+		 *        But as I am not sure how yet, we cannot from the dma pages.
+		 * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
+		 */
+	}
+	// release the data
+#if 1
+	if (chip->buffer.area)
+		snd_dma_free_pages(&chip->buffer);
+#endif
+
+	// release the i/o port
+	release_and_free_resource(chip->res_port);
+
+	// release the irq
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_ca0106_dev_free(struct snd_device *device)
+{
+	struct snd_ca0106 *chip = device->device_data;
+	return snd_ca0106_free(chip);
+}
+
+static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
+{
+	unsigned int status;
+
+	struct snd_ca0106 *chip = dev_id;
+	int i;
+	int mask;
+        unsigned int stat76;
+	struct snd_ca0106_channel *pchannel;
+
+	status = inl(chip->port + IPR);
+	if (! status)
+		return IRQ_NONE;
+
+        stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
+	//snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76);
+	//snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
+        mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
+	for(i = 0; i < 4; i++) {
+		pchannel = &(chip->playback_channels[i]);
+		if (stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+			if(pchannel->use) {
+				snd_pcm_period_elapsed(pchannel->epcm->substream);
+				//printk(KERN_INFO "interrupt [%d] used\n", i);
+                        }
+		}
+	        //printk(KERN_INFO "channel=%p\n",pchannel);
+	        //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+		mask <<= 1;
+	}
+        mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
+	for(i = 0; i < 4; i++) {
+		pchannel = &(chip->capture_channels[i]);
+		if (stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+			if(pchannel->use) {
+				snd_pcm_period_elapsed(pchannel->epcm->substream);
+				//printk(KERN_INFO "interrupt [%d] used\n", i);
+                        }
+		}
+	        //printk(KERN_INFO "channel=%p\n",pchannel);
+	        //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+		mask <<= 1;
+	}
+
+        snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76);
+
+	if (chip->midi.dev_id &&
+	    (status & (chip->midi.ipr_tx|chip->midi.ipr_rx))) {
+		if (chip->midi.interrupt)
+			chip->midi.interrupt(&chip->midi, status);
+		else
+			chip->midi.interrupt_disable(&chip->midi, chip->midi.tx_enable | chip->midi.rx_enable);
+	}
+
+	// acknowledge the interrupt if necessary
+	outl(status, chip->port+IPR);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+  
+	if (rpcm)
+		*rpcm = NULL;
+	if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
+		return err;
+  
+	pcm->private_data = emu;
+
+	switch (device) {
+	case 0:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+          break;
+	case 1:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+          break;
+	case 2:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+          break;
+	case 3:
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
+	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+          break;
+        }
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "CA0106");
+	emu->pcm = pcm;
+
+	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
+	    substream; 
+	    substream = substream->next) {
+		if ((err = snd_pcm_lib_preallocate_pages(substream, 
+							 SNDRV_DMA_TYPE_DEV, 
+							 snd_dma_pci_data(emu->pci), 
+							 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+			return err;
+	}
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 
+	      substream; 
+	      substream = substream->next) {
+ 		if ((err = snd_pcm_lib_preallocate_pages(substream, 
+	                                           SNDRV_DMA_TYPE_DEV, 
+	                                           snd_dma_pci_data(emu->pci), 
+	                                           64*1024, 64*1024)) < 0)
+			return err;
+	}
+  
+	if (rpcm)
+		*rpcm = pcm;
+  
+	return 0;
+}
+
+#define SPI_REG(reg, value)	(((reg) << SPI_REG_SHIFT) | (value))
+static unsigned int spi_dac_init[] = {
+	SPI_REG(SPI_LDA1_REG,	SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
+	SPI_REG(SPI_RDA1_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
+	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S | SPI_IWL_BIT_24),
+	SPI_REG(SPI_LDA2_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA2_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_LDA3_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA3_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_MASTDA_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(9,		0x00),
+	SPI_REG(SPI_MS_REG,	SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT),
+	SPI_REG(12,		0x00),
+	SPI_REG(SPI_LDA4_REG,	SPI_DA_BIT_0dB),
+	SPI_REG(SPI_RDA4_REG,	SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE),
+	SPI_REG(SPI_DACD4_REG,	0x00),
+};
+
+static unsigned int i2c_adc_init[][2] = {
+	{ 0x17, 0x00 }, /* Reset */
+	{ 0x07, 0x00 }, /* Timeout */
+	{ 0x0b, 0x22 },  /* Interface control */
+	{ 0x0c, 0x22 },  /* Master mode control */
+	{ 0x0d, 0x08 },  /* Powerdown control */
+	{ 0x0e, 0xcf },  /* Attenuation Left  0x01 = -103dB, 0xff = 24dB */
+	{ 0x0f, 0xcf },  /* Attenuation Right 0.5dB steps */
+	{ 0x10, 0x7b },  /* ALC Control 1 */
+	{ 0x11, 0x00 },  /* ALC Control 2 */
+	{ 0x12, 0x32 },  /* ALC Control 3 */
+	{ 0x13, 0x00 },  /* Noise gate control */
+	{ 0x14, 0xa6 },  /* Limiter control */
+	{ 0x15, ADC_MUX_LINEIN },  /* ADC Mixer control */
+};
+
+static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
+					 struct pci_dev *pci,
+					 struct snd_ca0106 **rchip)
+{
+	struct snd_ca0106 *chip;
+	struct snd_ca0106_details *c;
+	int err;
+	int ch;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_ca0106_dev_free,
+	};
+  
+	*rchip = NULL;
+  
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
+	    pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
+		printk(KERN_ERR "error to set 32bit mask DMA\n");
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+  
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+  
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	spin_lock_init(&chip->emu_lock);
+  
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 0x20,
+					     "snd_ca0106")) == NULL) { 
+		snd_ca0106_free(chip);
+		printk(KERN_ERR "cannot allocate the port\n");
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_ca0106_interrupt,
+			IRQF_SHARED, "snd_ca0106", chip)) {
+		snd_ca0106_free(chip);
+		printk(KERN_ERR "cannot grab irq\n");
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+  
+ 	/* This stores the periods table. */ 
+	if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
+		snd_ca0106_free(chip);
+		return -ENOMEM;
+	}
+
+	pci_set_master(pci);
+	/* read serial */
+	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+#if 1
+	printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model,
+	       pci->revision, chip->serial);
+#endif
+	strcpy(card->driver, "CA0106");
+	strcpy(card->shortname, "CA0106");
+
+	for (c = ca0106_chip_details; c->serial; c++) {
+		if (subsystem[dev]) {
+			if (c->serial == subsystem[dev])
+				break;
+		} else if (c->serial == chip->serial)
+			break;
+	}
+	chip->details = c;
+	if (subsystem[dev]) {
+		printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n",
+                        c->name, chip->serial, subsystem[dev]);
+	}
+
+	sprintf(card->longname, "%s at 0x%lx irq %i",
+		c->name, chip->port, chip->irq);
+
+	outl(0, chip->port + INTE);
+
+	/*
+	 *  Init to 0x02109204 :
+	 *  Clock accuracy    = 0     (1000ppm)
+	 *  Sample Rate       = 2     (48kHz)
+	 *  Audio Channel     = 1     (Left of 2)
+	 *  Source Number     = 0     (Unspecified)
+	 *  Generation Status = 1     (Original for Cat Code 12)
+	 *  Cat Code          = 12    (Digital Signal Mixer)
+	 *  Mode              = 0     (Mode 0)
+	 *  Emphasis          = 0     (None)
+	 *  CP                = 1     (Copyright unasserted)
+	 *  AN                = 0     (Audio data)
+	 *  P                 = 0     (Consumer)
+	 */
+	snd_ca0106_ptr_write(chip, SPCS0, 0,
+				chip->spdif_bits[0] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	/* Only SPCS1 has been tested */
+	snd_ca0106_ptr_write(chip, SPCS1, 0,
+				chip->spdif_bits[1] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_ca0106_ptr_write(chip, SPCS2, 0,
+				chip->spdif_bits[2] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_ca0106_ptr_write(chip, SPCS3, 0,
+				chip->spdif_bits[3] =
+				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+				SPCS_GENERATIONSTATUS | 0x00001200 |
+				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+        snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
+        snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
+
+        /* Write 0x8000 to AC97_REC_GAIN to mute it. */
+        outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
+        outw(0x8000, chip->port + AC97DATA);
+#if 0
+	snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
+	snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
+	snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
+	snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
+#endif
+
+	//snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
+	/* Analog or Digital output */
+	snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
+	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
+	chip->spdif_enable = 0; /* Set digital SPDIF output off */
+	//snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
+	//snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
+
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
+	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
+	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
+	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
+	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
+	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
+	for(ch = 0; ch < 4; ch++) {
+		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
+		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
+		//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
+		//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
+		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
+		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
+	}
+	if (chip->details->i2c_adc == 1) {
+	        /* Select MIC, Line in, TAD in, AUX in */
+	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
+		/* Default to CAPTURE_SOURCE to i2s in */
+		chip->capture_source = 3;
+	} else if (chip->details->ac97 == 1) {
+	        /* Default to AC97 in */
+	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4);
+		/* Default to CAPTURE_SOURCE to AC97 in */
+		chip->capture_source = 4;
+	} else {
+	        /* Select MIC, Line in, TAD in, AUX in */
+	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
+		/* Default to Set CAPTURE_SOURCE to i2s in */
+		chip->capture_source = 3;
+	}
+
+        if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */
+		/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+		outl(0x0, chip->port+GPIO);
+		//outl(0x00f0e000, chip->port+GPIO); /* Analog */
+		outl(0x005f5301, chip->port+GPIO); /* Analog */
+	} else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
+		/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+		outl(0x0, chip->port+GPIO);
+		//outl(0x00f0e000, chip->port+GPIO); /* Analog */
+		outl(0x005f5301, chip->port+GPIO); /* Analog */
+	} else {
+		outl(0x0, chip->port+GPIO);
+		outl(0x005f03a3, chip->port+GPIO); /* Analog */
+		//outl(0x005f02a2, chip->port+GPIO);   /* SPDIF */
+	}
+	snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
+
+	//outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+	//outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
+	//outl(0x00000009, chip->port+HCFG);
+	outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
+
+        if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+		int size, n;
+
+		size = ARRAY_SIZE(i2c_adc_init);
+                //snd_printk("I2C:array size=0x%x\n", size);
+		for (n=0; n < size; n++) {
+			snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]);
+		}
+		for (n=0; n < 4; n++) {
+			chip->i2c_capture_volume[n][0]= 0xcf;
+			chip->i2c_capture_volume[n][1]= 0xcf;
+		}
+		chip->i2c_capture_source=2; /* Line in */
+	        //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+	}
+        if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */
+		int size, n;
+
+		size = ARRAY_SIZE(spi_dac_init);
+		for (n = 0; n < size; n++) {
+			int reg = spi_dac_init[n] >> SPI_REG_SHIFT;
+
+			snd_ca0106_spi_write(chip, spi_dac_init[n]);
+			if (reg < ARRAY_SIZE(chip->spi_dac_reg))
+				chip->spi_dac_reg[reg] = spi_dac_init[n];
+		}
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+				  chip, &ops)) < 0) {
+		snd_ca0106_free(chip);
+		return err;
+	}
+	*rchip = chip;
+	return 0;
+}
+
+
+static void ca0106_midi_interrupt_enable(struct snd_ca_midi *midi, int intr)
+{
+	snd_ca0106_intr_enable((struct snd_ca0106 *)(midi->dev_id), intr);
+}
+
+static void ca0106_midi_interrupt_disable(struct snd_ca_midi *midi, int intr)
+{
+	snd_ca0106_intr_disable((struct snd_ca0106 *)(midi->dev_id), intr);
+}
+
+static unsigned char ca0106_midi_read(struct snd_ca_midi *midi, int idx)
+{
+	return (unsigned char)snd_ca0106_ptr_read((struct snd_ca0106 *)(midi->dev_id),
+						  midi->port + idx, 0);
+}
+
+static void ca0106_midi_write(struct snd_ca_midi *midi, int data, int idx)
+{
+	snd_ca0106_ptr_write((struct snd_ca0106 *)(midi->dev_id), midi->port + idx, 0, data);
+}
+
+static struct snd_card *ca0106_dev_id_card(void *dev_id)
+{
+	return ((struct snd_ca0106 *)dev_id)->card;
+}
+
+static int ca0106_dev_id_port(void *dev_id)
+{
+	return ((struct snd_ca0106 *)dev_id)->port;
+}
+
+static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
+{
+	struct snd_ca_midi *midi;
+	char *name;
+	int err;
+
+	if (channel == CA0106_MIDI_CHAN_B) {
+		name = "CA0106 MPU-401 (UART) B";
+		midi =  &chip->midi2;
+		midi->tx_enable = INTE_MIDI_TX_B;
+		midi->rx_enable = INTE_MIDI_RX_B;
+		midi->ipr_tx = IPR_MIDI_TX_B;
+		midi->ipr_rx = IPR_MIDI_RX_B;
+		midi->port = MIDI_UART_B_DATA;
+	} else {
+		name = "CA0106 MPU-401 (UART)";
+		midi =  &chip->midi;
+		midi->tx_enable = INTE_MIDI_TX_A;
+		midi->rx_enable = INTE_MIDI_TX_B;
+		midi->ipr_tx = IPR_MIDI_TX_A;
+		midi->ipr_rx = IPR_MIDI_RX_A;
+		midi->port = MIDI_UART_A_DATA;
+	}
+
+	midi->reset = CA0106_MPU401_RESET;
+	midi->enter_uart = CA0106_MPU401_ENTER_UART;
+	midi->ack = CA0106_MPU401_ACK;
+
+	midi->input_avail = CA0106_MIDI_INPUT_AVAIL;
+	midi->output_ready = CA0106_MIDI_OUTPUT_READY;
+
+	midi->channel = channel;
+
+	midi->interrupt_enable = ca0106_midi_interrupt_enable;
+	midi->interrupt_disable = ca0106_midi_interrupt_disable;
+
+	midi->read = ca0106_midi_read;
+	midi->write = ca0106_midi_write;
+
+	midi->get_dev_id_card = ca0106_dev_id_card;
+	midi->get_dev_id_port = ca0106_dev_id_port;
+
+	midi->dev_id = chip;
+	
+	if ((err = ca_midi_init(chip, midi, 0, name)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+static int __devinit snd_ca0106_probe(struct pci_dev *pci,
+					const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct snd_ca0106 *chip;
+	int err;
+
+	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 == NULL)
+		return -ENOMEM;
+
+	if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
+		if ((err = snd_ca0106_ac97(chip)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	if ((err = snd_ca0106_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_printdd("ca0106: probe for MIDI channel A ...");
+	if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) {
+		snd_card_free(card);
+		snd_printdd(" failed, err=0x%x\n",err);
+		return err;
+	}
+	snd_printdd(" done.\n");
+
+#ifdef CONFIG_PROC_FS
+	snd_ca0106_proc_init(chip);
+#endif
+
+	snd_card_set_dev(card, &pci->dev);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_ca0106_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_ca0106_ids[] = {
+	{ 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* Audigy LS or Live 24bit */
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+	.name = "CA0106",
+	.id_table = snd_ca0106_ids,
+	.probe = snd_ca0106_probe,
+	.remove = __devexit_p(snd_ca0106_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_ca0106_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+// clean up the module
+static void __exit alsa_card_ca0106_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ca0106_init)
+module_exit(alsa_card_ca0106_exit)
diff -ruN linux-2.6.22/sound/pci/ca0106/ca0106_mixer.c linux-2.6.22-alsa/sound/pci/ca0106/ca0106_mixer.c
--- linux-2.6.22/sound/pci/ca0106/ca0106_mixer.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca0106_mixer.c	2007-09-01 20:55:23.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.17
+ *  Version: 0.0.18
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -39,6 +39,8 @@
  *    Modified Copyright message.
  *  0.0.17
  *    Implement Mic and Line in Capture.
+ *  0.0.18
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
  *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
@@ -77,15 +79,7 @@
 static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
 static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
 
-static int snd_ca0106_shared_spdif_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_ca0106_shared_spdif_info	snd_ctl_boolean_mono_info
 
 static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_value *ucontrol)
@@ -470,6 +464,42 @@
 	return change;
 }
 
+#define spi_mute_info	snd_ctl_boolean_mono_info
+
+static int spi_mute_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
+	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
+
+	ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
+	return 0;
+}
+
+static int spi_mute_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
+	unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
+	int ret;
+
+	ret = emu->spi_dac_reg[reg] & bit;
+	if (ucontrol->value.integer.value[0]) {
+		if (!ret)	/* bit already cleared, do nothing */
+			return 0;
+		emu->spi_dac_reg[reg] &= ~bit;
+	} else {
+		if (ret)	/* bit already set, do nothing */
+			return 0;
+		emu->spi_dac_reg[reg] |= bit;
+	}
+
+	ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
+	return ret ? -1 : 1;
+}
+
 #define CA_VOLUME(xname,chid,reg) \
 {								\
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
@@ -562,6 +592,28 @@
         I2C_VOLUME("Aux Capture Volume", 3),
 };
 
+#define SPI_SWITCH(xname,reg,bit) \
+{								\
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+	.info	= spi_mute_info,				\
+	.get	= spi_mute_get,					\
+	.put	= spi_mute_put,					\
+	.private_value = (reg<<SPI_REG_SHIFT) | (bit)		\
+}
+
+static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
+__devinitdata = {
+	SPI_SWITCH("Analog Front Playback Switch",
+		   SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
+	SPI_SWITCH("Analog Rear Playback Switch",
+		   SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
+	SPI_SWITCH("Analog Center/LFE Playback Switch",
+		   SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
+	SPI_SWITCH("Analog Side Playback Switch",
+		   SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
+};
+
 static int __devinit remove_ctl(struct snd_card *card, const char *name)
 {
 	struct snd_ctl_elem_id id;
@@ -591,9 +643,19 @@
 	return -ENOENT;
 }
 
+#define ADD_CTLS(emu, ctls)						\
+	do {								\
+		int i, err;						\
+		for (i = 0; i < ARRAY_SIZE(ctls); i++) {		\
+			err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
+			if (err < 0)					\
+				return err;				\
+		}							\
+	} while (0)
+
 int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
 {
-	int i, err;
+	int err;
         struct snd_card *card = emu->card;
 	char **c;
 	static char *ca0106_remove_ctls[] = {
@@ -640,17 +702,9 @@
 		rename_ctl(card, c[0], c[1]);
 #endif
 
-	for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) {
-		err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu));
-		if (err < 0)
-			return err;
-	}
+	ADD_CTLS(emu, snd_ca0106_volume_ctls);
 	if (emu->details->i2c_adc == 1) {
-		for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) {
-			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu));
-			if (err < 0)
-				return err;
-		}
+		ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
 		if (emu->details->gpio_type == 1)
 			err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
 		else  /* gpio_type == 2 */
@@ -658,6 +712,8 @@
 		if (err < 0)
 			return err;
 	}
+	if (emu->details->spi_dac == 1)
+		ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
         return 0;
 }
 
diff -ruN linux-2.6.22/sound/pci/ca0106/ca_midi.h linux-2.6.22-alsa/sound/pci/ca0106/ca_midi.h
--- linux-2.6.22/sound/pci/ca0106/ca_midi.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca_midi.h	2007-09-01 20:55:23.000000000 +0200
@@ -22,9 +22,9 @@
  *
  */
 
-#include<linux/spinlock.h>
-#include<sound/rawmidi.h>
-#include<sound/mpu401.h>
+#include <linux/spinlock.h>
+#include <sound/rawmidi.h>
+#include <sound/mpu401.h>
 
 #define CA_MIDI_MODE_INPUT	MPU401_MODE_INPUT
 #define CA_MIDI_MODE_OUTPUT	MPU401_MODE_OUTPUT
diff -ruN linux-2.6.22/sound/pci/cmipci.c linux-2.6.22-alsa/sound/pci/cmipci.c
--- linux-2.6.22/sound/pci/cmipci.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cmipci.c	2007-09-01 20:55:22.000000000 +0200
@@ -424,7 +424,6 @@
 
 	int chip_version;
 	int max_channels;
-	unsigned int has_dual_dac: 1;
 	unsigned int can_ac3_sw: 1;
 	unsigned int can_ac3_hw: 1;
 	unsigned int can_multi_ch: 1;
@@ -2139,15 +2138,7 @@
 					 */
 };
 
-static int snd_cmipci_uswitch_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_cmipci_uswitch_info		snd_ctl_boolean_mono_info
 
 static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_value *ucontrol,
@@ -2633,46 +2624,40 @@
 	if (! detect) {
 		/* check reg 08h, bit 24-28 */
 		detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1;
-		if (! detect) {
+		switch (detect) {
+		case 0:
 			cm->chip_version = 33;
-			cm->max_channels = 2;
 			if (cm->do_soft_ac3)
 				cm->can_ac3_sw = 1;
 			else
 				cm->can_ac3_hw = 1;
-			cm->has_dual_dac = 1;
-		} else {
+			break;
+		case 1:
 			cm->chip_version = 37;
-			cm->max_channels = 2;
 			cm->can_ac3_hw = 1;
-			cm->has_dual_dac = 1;
+			break;
+		default:
+			cm->chip_version = 39;
+			cm->can_ac3_hw = 1;
+			break;
 		}
+		cm->max_channels = 2;
 	} else {
-		/* check reg 0Ch, bit 26 */
-		if (detect & CM_CHIP_8768) {
-			cm->chip_version = 68;
-			cm->max_channels = 8;
-			cm->can_ac3_hw = 1;
-			cm->has_dual_dac = 1;
-			cm->can_multi_ch = 1;
-		} else if (detect & CM_CHIP_055) {
-			cm->chip_version = 55;
-			cm->max_channels = 6;
-			cm->can_ac3_hw = 1;
-			cm->has_dual_dac = 1;
-			cm->can_multi_ch = 1;
-		} else if (detect & CM_CHIP_039) {
+		if (detect & CM_CHIP_039) {
 			cm->chip_version = 39;
 			if (detect & CM_CHIP_039_6CH) /* 4 or 6 channels */
 				cm->max_channels = 6;
 			else
 				cm->max_channels = 4;
-			cm->can_ac3_hw = 1;
-			cm->has_dual_dac = 1;
-			cm->can_multi_ch = 1;
+		} else if (detect & CM_CHIP_8768) {
+			cm->chip_version = 68;
+			cm->max_channels = 8;
 		} else {
-			printk(KERN_ERR "chip %x version not supported\n", detect);
+			cm->chip_version = 55;
+			cm->max_channels = 6;
 		}
+		cm->can_ac3_hw = 1;
+		cm->can_multi_ch = 1;
 	}
 }
 
@@ -2782,10 +2767,14 @@
 	if (!fm_port)
 		goto disable_fm;
 
-	/* first try FM regs in PCI port range */
-	iosynth = cm->iobase + CM_REG_FM_PCI;
-	err = snd_opl3_create(cm->card, iosynth, iosynth + 2,
-			      OPL3_HW_OPL3, 1, &opl3);
+	if (cm->chip_version > 33) {
+		/* first try FM regs in PCI port range */
+		iosynth = cm->iobase + CM_REG_FM_PCI;
+		err = snd_opl3_create(cm->card, iosynth, iosynth + 2,
+				      OPL3_HW_OPL3, 1, &opl3);
+	} else {
+		err = -EIO;
+	}
 	if (err < 0) {
 		/* then try legacy ports */
 		val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK;
@@ -2829,9 +2818,9 @@
 	static struct snd_device_ops ops = {
 		.dev_free =	snd_cmipci_dev_free,
 	};
-	unsigned int val = 0;
+	unsigned int val;
 	long iomidi;
-	int integrated_midi;
+	int integrated_midi = 0;
 	int pcm_index, pcm_spdif_index;
 	static struct pci_device_id intel_82437vx[] = {
 		{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) },
@@ -2931,15 +2920,54 @@
 		break;
 	}
 
+	sprintf(card->shortname, "C-Media %s", card->driver);
+	if (cm->chip_version < 68) {
+		val = pci->device < 0x110 ? 8338 : 8738;
+		sprintf(card->longname,
+			"C-Media CMI%d (model %d) at 0x%lx, irq %i",
+			val, cm->chip_version, cm->iobase, cm->irq);
+	} else {
+		switch (snd_cmipci_read_b(cm, CM_REG_INT_HLDCLR + 3) & 0x03) {
+		case 0:
+			val = 8769;
+			break;
+		case 2:
+			val = 8762;
+			break;
+		default:
+			switch ((pci->subsystem_vendor << 16) |
+				pci->subsystem_device) {
+			case 0x13f69761:
+			case 0x584d3741:
+			case 0x584d3751:
+			case 0x584d3761:
+			case 0x584d3771:
+			case 0x72848384:
+				val = 8770;
+				break;
+			default:
+				val = 8768;
+				break;
+			}
+		}
+		sprintf(card->longname, "C-Media CMI%d at 0x%lx, irq %i",
+			val, cm->iobase, cm->irq);
+	}
+
 	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
 		snd_cmipci_free(cm);
 		return err;
 	}
 
-	integrated_midi = snd_cmipci_read_b(cm, CM_REG_MPU_PCI) != 0xff;
-	if (integrated_midi && mpu_port[dev] == 1)
-		iomidi = cm->iobase + CM_REG_MPU_PCI;
-	else {
+	val = 0;
+	if (cm->chip_version > 33 && mpu_port[dev] == 1) {
+		val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1);
+		if (val != 0x00 && val != 0xff) {
+			iomidi = cm->iobase + CM_REG_MPU_PCI;
+			integrated_midi = 1;
+		}
+	}
+	if (!integrated_midi) {
 		iomidi = mpu_port[dev];
 		switch (iomidi) {
 		case 0x320: val = CM_VMPU_320; break;
@@ -2956,8 +2984,11 @@
 		}
 	}
 
-	if ((err = snd_cmipci_create_fm(cm, fm_port[dev])) < 0)
-		return err;
+	if (cm->chip_version < 68) {
+		err = snd_cmipci_create_fm(cm, fm_port[dev]);
+		if (err < 0)
+			return err;
+	}
 
 	/* reset mixer */
 	snd_cmipci_mixer_write(cm, 0, 0);
@@ -2969,11 +3000,9 @@
 	if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
 		return err;
 	pcm_index++;
-	if (cm->has_dual_dac) {
-		if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
-			return err;
-		pcm_index++;
-	}
+	if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+		return err;
+	pcm_index++;
 	if (cm->can_ac3_hw || cm->can_ac3_sw) {
 		pcm_spdif_index = pcm_index;
 		if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
@@ -3057,15 +3086,6 @@
 	}
 	card->private_data = cm;
 
-	sprintf(card->shortname, "C-Media PCI %s", card->driver);
-	sprintf(card->longname, "%s (model %d) at 0x%lx, irq %i",
-		card->shortname,
-		cm->chip_version,
-		cm->iobase,
-		cm->irq);
-
-	//snd_printd("%s is detected\n", card->longname);
-
 	if ((err = snd_card_register(card)) < 0) {
 		snd_card_free(card);
 		return err;
diff -ruN linux-2.6.22/sound/pci/cs4281.c linux-2.6.22-alsa/sound/pci/cs4281.c
--- linux-2.6.22/sound/pci/cs4281.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs4281.c	2007-09-01 20:55:23.000000000 +0200
@@ -842,12 +842,11 @@
 
 static struct snd_pcm_hardware snd_cs4281_playback =
 {
-	.info =			(SNDRV_PCM_INFO_MMAP |
-				 SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE |
-				 SNDRV_PCM_INFO_RESUME |
-				 SNDRV_PCM_INFO_SYNC_START),
+	.info =			SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_PAUSE |
+				SNDRV_PCM_INFO_RESUME,
 	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
 				SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
 				SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
@@ -868,12 +867,11 @@
 
 static struct snd_pcm_hardware snd_cs4281_capture =
 {
-	.info =			(SNDRV_PCM_INFO_MMAP |
-				 SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE |
-				 SNDRV_PCM_INFO_RESUME |
-				 SNDRV_PCM_INFO_SYNC_START),
+	.info =			SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_PAUSE |
+				SNDRV_PCM_INFO_RESUME,
 	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
 				SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
 				SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
@@ -904,7 +902,6 @@
 	dma->right_slot = 1;
 	runtime->private_data = dma;
 	runtime->hw = snd_cs4281_playback;
-	snd_pcm_set_sync(substream);
 	/* should be detected from the AC'97 layer, but it seems
 	   that although CS4297A rev B reports 18-bit ADC resolution,
 	   samples are 20-bit */
@@ -924,7 +921,6 @@
 	dma->right_slot = 11;
 	runtime->private_data = dma;
 	runtime->hw = snd_cs4281_capture;
-	snd_pcm_set_sync(substream);
 	/* should be detected from the AC'97 layer, but it seems
 	   that although CS4297A rev B reports 18-bit ADC resolution,
 	   samples are 20-bit */
diff -ruN linux-2.6.22/sound/pci/cs46xx/Makefile linux-2.6.22-alsa/sound/pci/cs46xx/Makefile
--- linux-2.6.22/sound/pci/cs46xx/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/Makefile	2007-09-01 20:55:23.000000000 +0200
@@ -3,10 +3,8 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
-snd-cs46xx-objs := cs46xx.o cs46xx_lib.o
-ifeq ($(CONFIG_SND_CS46XX_NEW_DSP),y)
-  snd-cs46xx-objs += dsp_spos.o dsp_spos_scb_lib.o
-endif
+snd-cs46xx-y := cs46xx.o cs46xx_lib.o
+snd-cs46xx-$(CONFIG_SND_CS46XX_NEW_DSP) += dsp_spos.o dsp_spos_scb_lib.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o
diff -ruN linux-2.6.22/sound/pci/cs46xx/cs46xx_lib.c linux-2.6.22-alsa/sound/pci/cs46xx/cs46xx_lib.c
--- linux-2.6.22/sound/pci/cs46xx/cs46xx_lib.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/cs46xx_lib.c	2007-09-01 20:55:23.000000000 +0200
@@ -1818,15 +1818,7 @@
 }
 #endif
 
-static int snd_mixer_boolean_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_mixer_boolean_info		snd_ctl_boolean_mono_info
 
 static int snd_cs46xx_iec958_get(struct snd_kcontrol *kcontrol, 
                                  struct snd_ctl_elem_value *ucontrol)
@@ -2897,6 +2889,10 @@
 	}
 #endif
 	
+#ifdef CONFIG_PM
+	kfree(chip->saved_regs);
+#endif
+
 	pci_disable_device(chip->pci);
 	kfree(chip);
 	return 0;
@@ -3140,6 +3136,23 @@
 /*
  *  start and load DSP 
  */
+
+static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
+{
+	unsigned int tmp;
+
+	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+        
+	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
+
+	tmp = snd_cs46xx_peek(chip, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000001;
+	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
+}
+
 int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
 {	
 	unsigned int tmp;
@@ -3214,19 +3227,7 @@
 
 	snd_cs46xx_proc_start(chip);
 
-	/*
-	 *  Enable interrupts on the part.
-	 */
-	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
-        
-	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
-	tmp &= ~0x0000f03f;
-	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
-
-	tmp = snd_cs46xx_peek(chip, BA1_CIE);
-	tmp &= ~0x0000003f;
-	tmp |=  0x00000001;
-	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
+	cs46xx_enable_stream_irqs(chip);
 	
 #ifndef CONFIG_SND_CS46XX_NEW_DSP
 	/* set the attenuation to 0dB */ 
@@ -3665,11 +3666,19 @@
  * APM support
  */
 #ifdef CONFIG_PM
+static unsigned int saved_regs[] = {
+	BA0_ACOSV,
+	BA0_ASER_FADDR,
+	BA0_ASER_MASTER,
+	BA1_PVOL,
+	BA1_CVOL,
+};
+
 int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_cs46xx *chip = card->private_data;
-	int amp_saved;
+	int i, amp_saved;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	chip->in_suspend = 1;
@@ -3680,6 +3689,10 @@
 	snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
 	snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 
+	/* save some registers */
+	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+		chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]);
+
 	amp_saved = chip->amplifier;
 	/* turn off amp */
 	chip->amplifier_ctrl(chip, -chip->amplifier);
@@ -3698,7 +3711,7 @@
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_cs46xx *chip = card->private_data;
-	int amp_saved;
+	int i, amp_saved;
 
 	pci_set_power_state(pci, PCI_D0);
 	pci_restore_state(pci);
@@ -3716,6 +3729,16 @@
 
 	snd_cs46xx_chip_init(chip);
 
+	snd_cs46xx_reset(chip);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	cs46xx_dsp_resume(chip);
+	/* restore some registers */
+	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+		snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]);
+#else
+	snd_cs46xx_download_image(chip);
+#endif
+
 #if 0
 	snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, 
 			       chip->ac97_general_purpose);
@@ -3730,6 +3753,13 @@
 	snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
 	snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 
+	/* reset playback/capture */
+	snd_cs46xx_set_play_sample_rate(chip, 8000);
+	snd_cs46xx_set_capture_sample_rate(chip, 8000);
+	snd_cs46xx_proc_start(chip);
+
+	cs46xx_enable_stream_irqs(chip);
+
 	if (amp_saved)
 		chip->amplifier_ctrl(chip, 1); /* turn amp on */
 	else
@@ -3896,6 +3926,15 @@
 	
 	snd_cs46xx_proc_init(card, chip);
 
+#ifdef CONFIG_PM
+	chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
+				   ARRAY_SIZE(saved_regs), GFP_KERNEL);
+	if (!chip->saved_regs) {
+		snd_cs46xx_free(chip);
+		return -ENOMEM;
+	}
+#endif
+
 	chip->active_ctrl(chip, -1); /* disable CLKRUN */
 
 	snd_card_set_dev(card, &pci->dev);
diff -ruN linux-2.6.22/sound/pci/cs46xx/cs46xx_lib.h linux-2.6.22-alsa/sound/pci/cs46xx/cs46xx_lib.h
--- linux-2.6.22/sound/pci/cs46xx/cs46xx_lib.h	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/cs46xx_lib.h	2007-09-01 20:55:23.000000000 +0200
@@ -86,6 +86,9 @@
 struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
 void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
 int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
+#ifdef CONFIG_PM
+int cs46xx_dsp_resume(struct snd_cs46xx * chip);
+#endif
 struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
 						   int symbol_type);
 #ifdef CONFIG_PROC_FS
diff -ruN linux-2.6.22/sound/pci/cs46xx/dsp_spos.c linux-2.6.22-alsa/sound/pci/cs46xx/dsp_spos.c
--- linux-2.6.22/sound/pci/cs46xx/dsp_spos.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/dsp_spos.c	2007-09-01 20:55:23.000000000 +0200
@@ -306,13 +306,59 @@
 	mutex_unlock(&chip->spos_mutex);
 }
 
+static int dsp_load_parameter(struct snd_cs46xx *chip,
+			      struct dsp_segment_desc *parameter)
+{
+	u32 doffset, dsize;
+
+	if (!parameter) {
+		snd_printdd("dsp_spos: module got no parameter segment\n");
+		return 0;
+	}
+
+	doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
+	dsize   = parameter->size * 4;
+
+	snd_printdd("dsp_spos: "
+		    "downloading parameter data to chip (%08x-%08x)\n",
+		    doffset,doffset + dsize);
+	if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
+		snd_printk(KERN_ERR "dsp_spos: "
+			   "failed to download parameter data to DSP\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dsp_load_sample(struct snd_cs46xx *chip,
+			   struct dsp_segment_desc *sample)
+{
+	u32 doffset, dsize;
+
+	if (!sample) {
+		snd_printdd("dsp_spos: module got no sample segment\n");
+		return 0;
+	}
+
+	doffset = (sample->offset * 4  + DSP_SAMPLE_BYTE_OFFSET);
+	dsize   =  sample->size * 4;
+
+	snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+		    doffset,doffset + dsize);
+
+	if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
+		snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module)
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
-	struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
-	struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
 	u32 doffset, dsize;
+	int err;
 
 	if (ins->nmodules == DSP_MAX_MODULES - 1) {
 		snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
@@ -326,49 +372,20 @@
 		snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
 	}
   
-	if (parameter == NULL) {
-		snd_printdd("dsp_spos: module got no parameter segment\n");
-	} else {
-		if (ins->nmodules > 0) {
-			snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n");
-		}
-
-		doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
-		dsize   = parameter->size * 4;
-
-		snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
-			    doffset,doffset + dsize);
-
-		if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
-			snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n");
-			return -EINVAL;
-		}
-	}
+	err = dsp_load_parameter(chip, get_segment_desc(module,
+							SEGTYPE_SP_PARAMETER));
+	if (err < 0)
+		return err;
 
 	if (ins->nmodules == 0) {
 		snd_printdd("dsp_spos: clearing sample area\n");
 		snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
 	}
 
-	if (sample == NULL) {
-		snd_printdd("dsp_spos: module got no sample segment\n");
-	} else {
-		if (ins->nmodules > 0) {
-			snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n");
-		}
-
-		doffset = (sample->offset * 4  + DSP_SAMPLE_BYTE_OFFSET);
-		dsize   =  sample->size * 4;
-
-		snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
-			    doffset,doffset + dsize);
-
-		if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
-			snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
-			return -EINVAL;
-		}
-	}
-
+	err = dsp_load_sample(chip, get_segment_desc(module,
+						     SEGTYPE_SP_SAMPLE));
+	if (err < 0)
+		return err;
 
 	if (ins->nmodules == 0) {
 		snd_printdd("dsp_spos: clearing code area\n");
@@ -986,7 +1003,10 @@
 		return NULL;
 	}
 
-	strcpy(ins->tasks[ins->ntask].task_name,name);
+	if (name)
+		strcpy(ins->tasks[ins->ntask].task_name, name);
+	else
+		strcpy(ins->tasks[ins->ntask].task_name, "(NULL)");
 	ins->tasks[ins->ntask].address = dest;
 	ins->tasks[ins->ntask].size = size;
 
@@ -995,7 +1015,8 @@
 	desc = (ins->tasks + ins->ntask);
 	ins->ntask++;
 
-	add_symbol (chip,name,dest,SYMBOL_PARAMETER);
+	if (name)
+		add_symbol (chip,name,dest,SYMBOL_PARAMETER);
 	return desc;
 }
 
@@ -1006,6 +1027,7 @@
 
 	desc = _map_scb (chip,name,dest);
 	if (desc) {
+		desc->data = scb_data;
 		_dsp_create_scb(chip,scb_data,dest);
 	} else {
 		snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
@@ -1023,6 +1045,7 @@
 
 	desc = _map_task_tree (chip,name,dest,size);
 	if (desc) {
+		desc->data = task_data;
 		_dsp_create_task_tree(chip,task_data,dest,size);
 	} else {
 		snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
@@ -1320,8 +1343,10 @@
 			0x0000ffff
 		};
     
-		/* dirty hack ... */
-		_dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2);
+		if (!cs46xx_dsp_create_task_tree(chip, NULL,
+						 (u32 *)&mix2_ostream_spb,
+						 WRITE_BACK_SPB, 2))
+			goto _fail_end;
 	}
 
 	/* input sample converter */
@@ -1622,7 +1647,6 @@
 	return 0;
 }
 
-
 static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
@@ -1894,3 +1918,61 @@
 
 	return 0;
 }
+
+#ifdef CONFIG_PM
+int cs46xx_dsp_resume(struct snd_cs46xx * chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i, err;
+
+	/* clear parameter, sample and code areas */
+	snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET,
+			     DSP_PARAMETER_BYTE_SIZE);
+	snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET,
+			     DSP_SAMPLE_BYTE_SIZE);
+	snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
+
+	for (i = 0; i < ins->nmodules; i++) {
+		struct dsp_module_desc *module = &ins->modules[i];
+		struct dsp_segment_desc *seg;
+		u32 doffset, dsize;
+
+		seg = get_segment_desc(module, SEGTYPE_SP_PARAMETER);
+		err = dsp_load_parameter(chip, seg);
+		if (err < 0)
+			return err;
+
+		seg = get_segment_desc(module, SEGTYPE_SP_SAMPLE);
+		err = dsp_load_sample(chip, seg);
+		if (err < 0)
+			return err;
+
+		seg = get_segment_desc(module, SEGTYPE_SP_PROGRAM);
+		if (!seg)
+			continue;
+
+		doffset = seg->offset * 4 + module->load_address * 4
+			+ DSP_CODE_BYTE_OFFSET;
+		dsize   = seg->size * 4;
+		err = snd_cs46xx_download(chip,
+					  ins->code.data + module->load_address,
+					  doffset, dsize);
+		if (err < 0)
+			return err;
+	}
+
+	for (i = 0; i < ins->ntask; i++) {
+		struct dsp_task_descriptor *t = &ins->tasks[i];
+		_dsp_create_task_tree(chip, t->data, t->address, t->size);
+	}
+
+	for (i = 0; i < ins->nscb; i++) {
+		struct dsp_scb_descriptor *s = &ins->scbs[i];
+		if (s->deleted)
+			continue;
+		_dsp_create_scb(chip, s->data, s->address);
+	}
+
+	return 0;
+}
+#endif
diff -ruN linux-2.6.22/sound/pci/cs46xx/dsp_spos_scb_lib.c linux-2.6.22-alsa/sound/pci/cs46xx/dsp_spos_scb_lib.c
--- linux-2.6.22/sound/pci/cs46xx/dsp_spos_scb_lib.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/dsp_spos_scb_lib.c	2007-09-01 20:55:23.000000000 +0200
@@ -1480,7 +1480,7 @@
 	if (!pcm_channel->src_scb->ref_count) {
 		cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
 
-		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR,
+		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot < DSP_MAX_SRC_NR,
 			    return );
 
 		ins->src_scb_slots[pcm_channel->src_slot] = 0;
diff -ruN linux-2.6.22/sound/pci/cs5530.c linux-2.6.22-alsa/sound/pci/cs5530.c
--- linux-2.6.22/sound/pci/cs5530.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/cs5530.c	2007-09-01 20:55:22.000000000 +0200
@@ -0,0 +1,306 @@
+/*
+ * cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
+ *
+ * 	(C) Copyright 2007 Ash Willis <ashwillis@programmer.net>
+ *	(C) Copyright 2003 Red Hat Inc <alan@redhat.com>
+ *
+ * This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did
+ * mess with it a bit. The chip seems to have to have trouble with full duplex
+ * mode. If we're recording in 8bit 8000kHz, say, and we then attempt to
+ * simultaneously play back audio at 16bit 44100kHz, the device actually plays
+ * back in the same format in which it is capturing. By forcing the chip to
+ * always play/capture in 16/44100, we can let alsa-lib convert the samples and
+ * that way we can hack up some full duplex audio. 
+ * 
+ * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
+ * The older version (VSA1) provides fairly good soundblaster emulation
+ * although there are a couple of bugs: large DMA buffers break record,
+ * and the MPU event handling seems suspect. VSA2 allows the native driver
+ * to control the AC97 audio engine directly and requires a different driver.
+ *
+ * Thanks to National Semiconductor for providing the needed information
+ * on the XpressAudio(tm) internals.
+ *
+ * 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, 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.
+ *
+ * TO DO:
+ *	Investigate whether we can portably support Cognac (5520) in the
+ *	same manner.
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Ash Willis");
+MODULE_DESCRIPTION("CS5530 Audio");
+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;
+
+struct snd_cs5530 {
+	struct snd_card *card;
+	struct pci_dev *pci;
+	struct snd_sb *sb;
+	unsigned long pci_base;
+};
+
+static struct pci_device_id snd_cs5530_ids[] = {
+	{PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
+							PCI_ANY_ID, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
+
+static int snd_cs5530_free(struct snd_cs5530 *chip)
+{
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_cs5530_dev_free(struct snd_device *device)
+{
+	struct snd_cs5530 *chip = device->device_data;
+	return snd_cs5530_free(chip);
+}
+
+static void __devexit snd_cs5530_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
+{
+	outb(reg, io + 4);
+	udelay(20);
+	reg = inb(io + 5);
+	udelay(20);
+	return reg;
+}
+
+static int __devinit snd_cs5530_create(struct snd_card *card,
+				       struct pci_dev *pci,
+				       struct snd_cs5530 **rchip)
+{
+	struct snd_cs5530 *chip;
+	unsigned long sb_base;
+	u8 irq, dma8, dma16 = 0;
+	u16 map;
+	void __iomem *mem;
+	int err;
+
+	static struct snd_device_ops ops = {
+		.dev_free = snd_cs5530_dev_free,
+	};
+	*rchip = NULL;
+
+	err = pci_enable_device(pci);
+ 	if (err < 0)
+		return err;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	chip->card = card;
+	chip->pci = pci;
+
+	err = pci_request_regions(pci, "CS5530");
+	if (err < 0) {
+		kfree(chip); 
+		pci_disable_device(pci);
+		return err;
+	}
+	chip->pci_base = pci_resource_start(pci, 0);
+
+	mem = ioremap_nocache(chip->pci_base, pci_resource_len(pci, 0));
+	if (mem == NULL) {
+		kfree(chip);
+		pci_disable_device(pci);
+		return -EBUSY;
+	}
+
+	map = readw(mem + 0x18);
+	iounmap(mem);
+
+	/* Map bits
+		0:1	* 0x20 + 0x200 = sb base
+		2	sb enable
+		3	adlib enable
+		5	MPU enable 0x330
+		6	MPU enable 0x300
+
+	   The other bits may be used internally so must be masked */
+
+	sb_base = 0x220 + 0x20 * (map & 3);
+
+	if (map & (1<<2))
+		printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
+	else {
+		printk(KERN_ERR "Could not find XpressAudio!\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	if (map & (1<<5))
+		printk(KERN_INFO "CS5530: MPU at 0x300\n");
+	else if (map & (1<<6))
+		printk(KERN_INFO "CS5530: MPU at 0x330\n");
+
+	irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
+	dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
+
+	if (dma8 & 0x20)
+		dma16 = 5;
+	else if (dma8 & 0x40)
+		dma16 = 6;
+	else if (dma8 & 0x80)
+		dma16 = 7;
+	else {
+		printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	if (dma8 & 0x01)
+		dma8 = 0;
+	else if (dma8 & 02)
+		dma8 = 1;
+	else if (dma8 & 0x08)
+		dma8 = 3;
+	else {
+		printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	if (irq & 1)
+		irq = 9;
+	else if (irq & 2)
+		irq = 5;
+	else if (irq & 4)
+		irq = 7;
+	else if (irq & 8)
+		irq = 10;
+	else {
+		printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, 
+									dma16);
+
+	err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
+						dma16, SB_HW_CS5530, &chip->sb);
+	if (err < 0) {
+		printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
+	if (err < 0) {
+		printk(KERN_ERR "CS5530: Could not create PCM\n");
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	err = snd_sbmixer_new(chip->sb);
+	if (err < 0) {
+		printk(KERN_ERR "CS5530: Could not create Mixer\n");
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+	*rchip = chip;
+	return 0;
+}
+
+static int __devinit snd_cs5530_probe(struct pci_dev *pci,
+					const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct snd_cs5530 *chip = NULL;
+	int err;
+
+	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 == NULL)
+		return -ENOMEM;
+
+	err = snd_cs5530_create(card, pci, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "CS5530");
+	strcpy(card->shortname, "CS5530 Audio");
+	sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static struct pci_driver driver = {
+	.name = "CS5530_Audio",
+	.id_table = snd_cs5530_ids,
+	.probe = snd_cs5530_probe,
+	.remove = __devexit_p(snd_cs5530_remove),
+};
+
+static int __init alsa_card_cs5530_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_cs5530_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs5530_init)
+module_exit(alsa_card_cs5530_exit)
+
diff -ruN linux-2.6.22/sound/pci/cs5535audio/Makefile linux-2.6.22-alsa/sound/pci/cs5535audio/Makefile
--- linux-2.6.22/sound/pci/cs5535audio/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs5535audio/Makefile	2007-09-01 20:55:22.000000000 +0200
@@ -2,11 +2,8 @@
 # Makefile for cs5535audio
 #
 
-snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o
-
-ifeq ($(CONFIG_PM),y)
-snd-cs5535audio-objs += cs5535audio_pm.o
-endif
+snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
+snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
diff -ruN linux-2.6.22/sound/pci/cs5535audio/cs5535audio_pcm.c linux-2.6.22-alsa/sound/pci/cs5535audio/cs5535audio_pcm.c
--- linux-2.6.22/sound/pci/cs5535audio/cs5535audio_pcm.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs5535audio/cs5535audio_pcm.c	2007-09-01 20:55:22.000000000 +0200
@@ -43,7 +43,6 @@
 		 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
 		 		SNDRV_PCM_INFO_MMAP_VALID |
 		 		SNDRV_PCM_INFO_PAUSE |
-				SNDRV_PCM_INFO_SYNC_START |
 				SNDRV_PCM_INFO_RESUME
 				),
 	.formats =		(
@@ -71,8 +70,7 @@
 				SNDRV_PCM_INFO_MMAP |
 				SNDRV_PCM_INFO_INTERLEAVED |
 		 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
-		 		SNDRV_PCM_INFO_MMAP_VALID |
-				SNDRV_PCM_INFO_SYNC_START
+		 		SNDRV_PCM_INFO_MMAP_VALID
 				),
 	.formats =		(
 				SNDRV_PCM_FMTBIT_S16_LE
@@ -102,7 +100,6 @@
 	runtime->hw = snd_cs5535audio_playback;
 	cs5535au->playback_substream = substream;
 	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
-	snd_pcm_set_sync(substream);
 	if ((err = snd_pcm_hw_constraint_integer(runtime,
 				SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
@@ -348,7 +345,6 @@
 	runtime->hw = snd_cs5535audio_capture;
 	cs5535au->capture_substream = substream;
 	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
-	snd_pcm_set_sync(substream);
 	if ((err = snd_pcm_hw_constraint_integer(runtime,
 					 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
diff -ruN linux-2.6.22/sound/pci/echoaudio/echoaudio.c linux-2.6.22-alsa/sound/pci/echoaudio/echoaudio.c
--- linux-2.6.22/sound/pci/echoaudio/echoaudio.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/echoaudio/echoaudio.c	2007-09-01 20:55:23.000000000 +0200
@@ -1595,15 +1595,7 @@
 #ifdef ECHOCARD_HAS_PHANTOM_POWER
 
 /******************* Phantom power switch *******************/
-static int snd_echo_phantom_power_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_echo_phantom_power_info	snd_ctl_boolean_mono_info
 
 static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
 				      struct snd_ctl_elem_value *ucontrol)
@@ -1646,15 +1638,7 @@
 #ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
 
 /******************* Digital input automute switch *******************/
-static int snd_echo_automute_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_echo_automute_info		snd_ctl_boolean_mono_info
 
 static int snd_echo_automute_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
@@ -1695,18 +1679,7 @@
 
 
 /******************* VU-meters switch *******************/
-static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol,
-					 struct snd_ctl_elem_info *uinfo)
-{
-	struct echoaudio *chip;
-
-	chip = snd_kcontrol_chip(kcontrol);
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
+#define snd_echo_vumeters_switch_info		snd_ctl_boolean_mono_info
 
 static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/pci/emu10k1/emu10k1_main.c linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1_main.c
--- linux-2.6.22/sound/pci/emu10k1/emu10k1_main.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1_main.c	2007-09-02 13:30:43.000000000 +0200
@@ -31,6 +31,8 @@
  *
  */
 
+#include <linux/sched.h>
+#include <linux/kthread.h>
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
@@ -51,9 +53,15 @@
 
 #define HANA_FILENAME "emu/hana.fw"
 #define DOCK_FILENAME "emu/audio_dock.fw"
+#define EMU1010B_FILENAME "emu/emu1010b.fw"
+#define MICRO_DOCK_FILENAME "emu/micro_dock.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(EMU1010_NOTEBOOK_FILENAME);
 
 
 /*************************************************************************
@@ -660,10 +668,12 @@
 		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
@@ -694,6 +704,96 @@
 	return 0;
 }
 
+int emu1010_firmware_thread(void *data) {
+	struct snd_emu10k1 * emu = data;
+	int tmp,tmp2;
+	int reg;
+	int err;
+
+	for (;;) {
+		/* Delay to allow Audio Dock to settle */
+		msleep(1000);
+		if (kthread_should_stop())
+			break;
+		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
+		snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
+		if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
+			/* Audio Dock attached */
+			/* 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 ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+					return err;
+				}
+			} else if (emu->card_capabilities->emu1010 == 2) {
+				if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+					return err;
+				}
+			} else if (emu->card_capabilities->emu1010 == 3) {
+				if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+					return err;
+				}
+			}
+
+			snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
+			snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
+			snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+			/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+			snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+			snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+			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;
+			}
+			snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
+			snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
+			snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
+			snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
+			/* Sync clocking between 1010 and Dock */
+			/* Allow DLL to settle */
+			msleep(10);
+			/* Unmute all. Default is muted after a firmware load */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+ * EMU-1010 - details found out from this driver, official MS Win drivers,
+ * testing the card:
+ *
+ * Audigy2 (aka Alice2):
+ * ---------------------
+ * 	* communication over PCI
+ * 	* conversion of 32-bit data coming over EMU32 links from HANA FPGA
+ *	  to 2 x 16-bit, using internal DSP instructions
+ * 	* slave mode, clock supplied by HANA
+ * 	* linked to HANA using:
+ * 		32 x 32-bit serial EMU32 output channels
+ * 		16 x EMU32 input channels
+ * 		(?) x I2S I/O channels (?)
+ *
+ * FPGA (aka HANA):
+ * ---------------
+ * 	* provides all (?) physical inputs and outputs of the card
+ * 		(ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
+ * 	* provides clock signal for the card and Alice2
+ * 	* two crystals - for 44.1kHz and 48kHz multiples
+ * 	* provides internal routing of signal sources to signal destinations
+ * 	* inputs/outputs to Alice2 - see above
+ *
+ * Current status of the driver:
+ * ----------------------------
+ * 	* only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
+ * 	* PCM device nb. 2:
+ *		16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
+ * 		16 x 32-bit capture - snd_emu10k1_capture_efx_ops
+ */
 static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 {
 	unsigned int i;
@@ -727,7 +827,7 @@
 	/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_printdd("reg1=0x%x\n",reg);
-	if (reg == 0x55) {
+	if ((reg & 0x3f) == 0x15) {
 		/* FPGA netlist already present so clear it */
 		/* Return to programming mode */
 
@@ -735,19 +835,32 @@
 	}
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_printdd("reg2=0x%x\n",reg);
-	if (reg == 0x55) {
+	if ((reg & 0x3f) == 0x15) {
 		/* FPGA failed to return to programming mode */
+		snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
 		return -ENODEV;
 	}
 	snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
-	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;
+	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;
+		}
 	}
 
 	/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
-	if (reg != 0x55) {
+	if ((reg & 0x3f) != 0x15) {
 		/* FPGA failed to be programmed */
 		snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
 		return -ENODEV;
@@ -765,8 +878,16 @@
 	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
 	snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
 	snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); 
-	/* ADAT input. */
-	snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 );
+	/* Optical -> ADAT I/O  */
+	/* 0 : SPDIF
+	 * 1 : ADAT
+	 */
+	emu->emu1010.optical_in = 1; /* IN_ADAT */
+	emu->emu1010.optical_out = 1; /* IN_ADAT */
+	tmp = 0;
+	tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
+		(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+	snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp );
 	snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp );
 	/* Set no attenuation on Audio Dock pads. */
 	snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 );
@@ -850,6 +971,27 @@
 		EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
 	snd_emu1010_fpga_link_dst_src_write(emu,
 		EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
+	/* Pavel Hofman - setting defaults for 8 more capture channels
+	 * Defaults only, users will set their own values anyways, let's
+	 * just copy/paste.
+	 */
+	
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
 #endif
 #if 0
 	/* Original */
@@ -931,38 +1073,12 @@
 	snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); 
 	snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
 
-	/* Delay to allow Audio Dock to settle */
-	msleep(100);
-	snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
-	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
-	/* FIXME: The loading of this should be able to happen any time,
-	 * as the user can plug/unplug it at any time
-	 */
-	if (reg & (EMU_HANA_OPTION_DOCK_ONLINE | EMU_HANA_OPTION_DOCK_OFFLINE) ) {
-		/* Audio Dock attached */
-		/* 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 ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
-			return err;
-		}
-		snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
-		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
-		snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
-		/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
-		snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
-		snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
-		if (reg != 0x55) {
-			/* 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;
-		}
-		snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
-		snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
-		snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
-		snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
-	}
+	/* 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 0
 	snd_emu1010_fpga_link_dst_src_write(emu,
 		EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
@@ -1048,7 +1164,7 @@
 	emu->emu1010.output_source[23] = 28;
 
 	/* TEMP: Select SPDIF in/out */
-	snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+	//snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
 
 	/* TEMP: Select 48kHz SPDIF out */
 	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
@@ -1089,6 +1205,7 @@
 	if (emu->card_capabilities->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->memhdr)
 		snd_util_memhdr_free(emu->memhdr);
@@ -1227,9 +1344,15 @@
 	 .emu10k2_chip = 1,
 	 .ca0108_chip = 1,
 	 .ca_cardbus_chip = 1,
-	 .spi_dac = 1,
-	 .i2c_adc = 1,
-	 .spk71 = 1} ,
+	 .spk71 = 1 ,
+	 .emu1010 = 3} ,
+	{.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} ,
 	{.vendor = 0x1102, .device = 0x0008, 
 	 .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
 	 .id = "Audigy2",
@@ -1634,8 +1757,9 @@
 		goto error;
 	}
 
-	emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*));
-	emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long));
+	emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
+	emu->page_addr_table = vmalloc(emu->max_cache_pages *
+				       sizeof(unsigned long));
 	if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
 		err = -ENOMEM;
 		goto error;
@@ -1665,12 +1789,13 @@
 	emu->fx8010.extout_mask = extout_mask;
 	emu->enable_ir = enable_ir;
 
+	if (emu->card_capabilities->ca_cardbus_chip) {
+		if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+			goto error;
+	}
 	if (emu->card_capabilities->ecard) {
 		if ((err = snd_emu10k1_ecard_init(emu)) < 0)
 			goto error;
-	} else if (emu->card_capabilities->ca_cardbus_chip) {
-		if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
-			goto error;
  	} else if (emu->card_capabilities->emu1010) {
  		if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
  			snd_emu10k1_free(emu);
@@ -1816,10 +1941,10 @@
 
 void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
 {
+	if (emu->card_capabilities->ca_cardbus_chip)
+		snd_emu10k1_cardbus_init(emu);
 	if (emu->card_capabilities->ecard)
 		snd_emu10k1_ecard_init(emu);
-	else if (emu->card_capabilities->ca_cardbus_chip)
-		snd_emu10k1_cardbus_init(emu);
 	else if (emu->card_capabilities->emu1010)
  		snd_emu10k1_emu1010_init(emu);
 	else
diff -ruN linux-2.6.22/sound/pci/emu10k1/emu10k1_main.c.orig linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1_main.c.orig
--- linux-2.6.22/sound/pci/emu10k1/emu10k1_main.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1_main.c.orig	2007-09-01 20:55:23.000000000 +0200
@@ -0,0 +1,1976 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *      Added support for Audigy 2 Value.
+ *  	Added EMU 1010 support.
+ *  	General bug fixes and enhancements.
+ *
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   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/sched.h>
+#include <linux/kthread.h>
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mutex.h>
+
+
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <linux/firmware.h>
+#include "p16v.h"
+#include "tina2.h"
+#include "p17v.h"
+
+
+#define HANA_FILENAME "emu/hana.fw"
+#define DOCK_FILENAME "emu/audio_dock.fw"
+#define EMU1010B_FILENAME "emu/emu1010b.fw"
+#define MICRO_DOCK_FILENAME "emu/micro_dock.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(EMU1010_NOTEBOOK_FILENAME);
+
+
+/*************************************************************************
+ * EMU10K1 init / done
+ *************************************************************************/
+
+void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int ch)
+{
+	snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+	snd_emu10k1_ptr_write(emu, IP, ch, 0);
+	snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+	snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+	snd_emu10k1_ptr_write(emu, CCR, ch, 0);
+
+	snd_emu10k1_ptr_write(emu, PSST, ch, 0);
+	snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
+	snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
+	snd_emu10k1_ptr_write(emu, Z1, ch, 0);
+	snd_emu10k1_ptr_write(emu, Z2, ch, 0);
+	snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
+
+	snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
+	snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
+	snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
+	snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
+	snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24);	/* 1 Hz */
+	snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24);	/* 1 Hz */
+	snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
+
+	/*** these are last so OFF prevents writing ***/
+	snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
+	snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
+	snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
+	snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
+	snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+
+	/* Audigy extra stuffs */
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
+		snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+	}
+}
+
+static unsigned int spi_dac_init[] = {
+		0x00ff,
+		0x02ff,
+		0x0400,
+		0x0520,
+		0x0600,
+		0x08ff,
+		0x0aff,
+		0x0cff,
+		0x0eff,
+		0x10ff,
+		0x1200,
+		0x1400,
+		0x1480,
+		0x1800,
+		0x1aff,
+		0x1cff,
+		0x1e00,
+		0x0530,
+		0x0602,
+		0x0622,
+		0x1400,
+};
+
+static unsigned int i2c_adc_init[][2] = {
+	{ 0x17, 0x00 }, /* Reset */
+	{ 0x07, 0x00 }, /* Timeout */
+	{ 0x0b, 0x22 },  /* Interface control */
+	{ 0x0c, 0x22 },  /* Master mode control */
+	{ 0x0d, 0x08 },  /* Powerdown control */
+	{ 0x0e, 0xcf },  /* Attenuation Left  0x01 = -103dB, 0xff = 24dB */
+	{ 0x0f, 0xcf },  /* Attenuation Right 0.5dB steps */
+	{ 0x10, 0x7b },  /* ALC Control 1 */
+	{ 0x11, 0x00 },  /* ALC Control 2 */
+	{ 0x12, 0x32 },  /* ALC Control 3 */
+	{ 0x13, 0x00 },  /* Noise gate control */
+	{ 0x14, 0xa6 },  /* Limiter control */
+	{ 0x15, ADC_MUX_2 },  /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */
+};
+	
+static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+{
+	unsigned int silent_page;
+	int ch;
+	u32 tmp;
+
+	/* disable audio and lock cache */
+	outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE,
+	     emu->port + HCFG);
+
+	/* reset recording buffers */
+	snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+
+	/* disable channel interrupt */
+	outl(0, emu->port + INTE);
+	snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+	if (emu->audigy){
+		/* set SPDIF bypass mode */
+		snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT);
+		/* enable rear left + rear right AC97 slots */
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT |
+				      AC97SLOT_REAR_LEFT);
+	}
+
+	/* init envelope engine */
+	for (ch = 0; ch < NUM_G; ch++)
+		snd_emu10k1_voice_init(emu, ch);
+
+	snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]);
+	snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
+	snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
+
+	if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+		/* Hacks for Alice3 to work independent of haP16V driver */
+		//Setup SRCMulti_I2S SamplingRate
+		tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+		tmp &= 0xfffff1ff;
+		tmp |= (0x2<<9);
+		snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+		
+		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+		snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
+		/* Setup SRCMulti Input Audio Enable */
+		/* Use 0xFFFFFFFF to enable P16V sounds. */
+		snd_emu10k1_ptr20_write(emu, SRCMULTI_ENABLE, 0, 0xFFFFFFFF);
+
+		/* Enabled Phased (8-channel) P16V playback */
+		outl(0x0201, emu->port + HCFG2);
+		/* Set playback routing. */
+		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
+	}
+	if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
+		/* Hacks for Alice3 to work independent of haP16V driver */
+		snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
+		//Setup SRCMulti_I2S SamplingRate
+		tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+		tmp &= 0xfffff1ff;
+		tmp |= (0x2<<9);
+		snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+		outl(0x600000, emu->port + 0x20);
+		outl(0x14, emu->port + 0x24);
+
+		/* Setup SRCMulti Input Audio Enable */
+		outl(0x7b0000, emu->port + 0x20);
+		outl(0xFF000000, emu->port + 0x24);
+
+		/* Setup SPDIF Out Audio Enable */
+		/* The Audigy 2 Value has a separate SPDIF out,
+		 * so no need for a mixer switch
+		 */
+		outl(0x7a0000, emu->port + 0x20);
+		outl(0xFF000000, emu->port + 0x24);
+		tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+		outl(tmp, emu->port + A_IOCFG);
+	}
+	if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
+		int size, n;
+
+		size = ARRAY_SIZE(spi_dac_init);
+		for (n = 0; n < size; n++)
+			snd_emu10k1_spi_write(emu, spi_dac_init[n]);
+
+		snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10);
+		/* Enable GPIOs
+		 * GPIO0: Unknown
+		 * GPIO1: Speakers-enabled.
+		 * GPIO2: Unknown
+		 * GPIO3: Unknown
+		 * GPIO4: IEC958 Output on.
+		 * GPIO5: Unknown
+		 * GPIO6: Unknown
+		 * 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;
+
+		snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
+		tmp = inl(emu->port + A_IOCFG);
+		outl(tmp | 0x4, emu->port + A_IOCFG);  /* Set bit 2 for mic input */
+		tmp = inl(emu->port + A_IOCFG);
+		size = ARRAY_SIZE(i2c_adc_init);
+		for (n = 0; n < size; n++)
+			snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
+		for (n=0; n < 4; n++) {
+			emu->i2c_capture_volume[n][0]= 0xcf;
+			emu->i2c_capture_volume[n][1]= 0xcf;
+		}
+
+	}
+
+	
+	snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);	/* taken from original driver */
+	snd_emu10k1_ptr_write(emu, TCBS, 0, 4);	/* taken from original driver */
+
+	silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK;
+	for (ch = 0; ch < NUM_G; ch++) {
+		snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
+		snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
+	}
+
+	if (emu->card_capabilities->emu1010) {
+		outl(HCFG_AUTOMUTE_ASYNC |
+			HCFG_EMU32_SLAVE |
+			HCFG_AUDIOENABLE, emu->port + HCFG);
+	/*
+	 *  Hokay, setup HCFG
+	 *   Mute Disable Audio = 0
+	 *   Lock Tank Memory = 1
+	 *   Lock Sound Memory = 0
+	 *   Auto Mute = 1
+	 */
+	} else if (emu->audigy) {
+		if (emu->revision == 4) /* audigy2 */
+			outl(HCFG_AUDIOENABLE |
+			     HCFG_AC3ENABLE_CDSPDIF |
+			     HCFG_AC3ENABLE_GPSPDIF |
+			     HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+		else
+			outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+	/* FIXME: Remove all these emu->model and replace it with a card recognition parameter,
+	 * e.g. card_capabilities->joystick */
+	} else if (emu->model == 0x20 ||
+	    emu->model == 0xc400 ||
+	    (emu->model == 0x21 && emu->revision < 6))
+		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
+	else
+		// With on-chip joystick
+		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+
+	if (enable_ir) {	/* enable IR for SB Live */
+		if (emu->card_capabilities->emu1010) {
+			;  /* Disable all access to A_IOCFG for the emu1010 */
+		} else if (emu->card_capabilities->i2c_adc) {
+			;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+		} else if (emu->audigy) {
+			unsigned int reg = inl(emu->port + A_IOCFG);
+			outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+			udelay(500);
+			outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+			udelay(100);
+			outl(reg, emu->port + A_IOCFG);
+		} else {
+			unsigned int reg = inl(emu->port + HCFG);
+			outl(reg | HCFG_GPOUT2, emu->port + HCFG);
+			udelay(500);
+			outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
+			udelay(100);
+			outl(reg, emu->port + HCFG);
+ 		}
+	}
+	
+	if (emu->card_capabilities->emu1010) {
+		;  /* Disable all access to A_IOCFG for the emu1010 */
+	} else if (emu->card_capabilities->i2c_adc) {
+		;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+	} else if (emu->audigy) {	/* enable analog output */
+		unsigned int reg = inl(emu->port + A_IOCFG);
+		outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+	}
+
+	return 0;
+}
+
+static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
+{
+	/*
+	 *  Enable the audio bit
+	 */
+	outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
+
+	/* Enable analog/digital outs on audigy */
+	if (emu->card_capabilities->emu1010) {
+		;  /* Disable all access to A_IOCFG for the emu1010 */
+	} else if (emu->card_capabilities->i2c_adc) {
+		;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+	} else if (emu->audigy) {
+		outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+ 
+		if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+			/* Unmute Analog now.  Set GPO6 to 1 for Apollo.
+			 * This has to be done after init ALice3 I2SOut beyond 48KHz.
+			 * So, sequence is important. */
+			outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+		} else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
+			/* Unmute Analog now. */
+			outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+		} else {
+			/* Disable routing from AC97 line out to Front speakers */
+			outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+		}
+	}
+	
+#if 0
+	{
+	unsigned int tmp;
+	/* FIXME: the following routine disables LiveDrive-II !! */
+	// TOSLink detection
+	emu->tos_link = 0;
+	tmp = inl(emu->port + HCFG);
+	if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
+		outl(tmp|0x800, emu->port + HCFG);
+		udelay(50);
+		if (tmp != (inl(emu->port + HCFG) & ~0x800)) {
+			emu->tos_link = 1;
+			outl(tmp, emu->port + HCFG);
+		}
+	}
+	}
+#endif
+
+	snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+}
+
+int snd_emu10k1_done(struct snd_emu10k1 * emu)
+{
+	int ch;
+
+	outl(0, emu->port + INTE);
+
+	/*
+	 *  Shutdown the chip
+	 */
+	for (ch = 0; ch < NUM_G; ch++)
+		snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+	for (ch = 0; ch < NUM_G; ch++) {
+		snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
+		snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
+		snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+		snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+	}
+
+	/* reset recording buffers */
+	snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
+	snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+	snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP);
+
+	/* disable channel interrupt */
+	snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+	/* disable audio and lock cache */
+	outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+	snd_emu10k1_ptr_write(emu, PTB, 0, 0);
+
+	return 0;
+}
+
+/*************************************************************************
+ * ECARD functional implementation
+ *************************************************************************/
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT		(1L << 12)
+#define HANDN_BIT		(1L << 11)
+#define PULSEN_BIT		(1L << 10)
+
+#define EC_GDI1			(1 << 13)
+#define EC_GDI0			(1 << 14)
+
+#define EC_NUM_CONTROL_BITS	20
+
+#define EC_AC3_DATA_SELN	0x0001L
+#define EC_EE_DATA_SEL		0x0002L
+#define EC_EE_CNTRL_SELN	0x0004L
+#define EC_EECLK		0x0008L
+#define EC_EECS			0x0010L
+#define EC_EESDO		0x0020L
+#define EC_TRIM_CSN		0x0040L
+#define EC_TRIM_SCLK		0x0080L
+#define EC_TRIM_SDATA		0x0100L
+#define EC_TRIM_MUTEN		0x0200L
+#define EC_ADCCAL		0x0400L
+#define EC_ADCRSTN		0x0800L
+#define EC_DACCAL		0x1000L
+#define EC_DACMUTEN		0x2000L
+#define EC_LEDN			0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT	15
+#define EC_SPDIF1_SEL_SHIFT	17
+#define EC_SPDIF0_SEL_MASK	(0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK	(0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x)	(((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x)	(((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01	/* Self-explanatory.  This should
+					 * be incremented any time the EEPROM's
+					 * format is changed.  */
+
+#define EC_EEPROM_SIZE		0x40	/* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR	0x20	/* Address of the current prom version */
+#define EC_BOARDREV0_ADDR	0x21	/* LSW of board rev */
+#define EC_BOARDREV1_ADDR	0x22	/* MSW of board rev */
+
+#define EC_LAST_PROMFILE_ADDR	0x2f
+
+#define EC_SERIALNUM_ADDR	0x30	/* First word of serial number.  The 
+					 * can be up to 30 characters in length
+					 * and is stored as a NULL-terminated
+					 * ASCII string.  Any unused bytes must be
+					 * filled with zeros */
+#define EC_CHECKSUM_ADDR	0x3f	/* Location at which checksum is stored */
+
+
+/* Most of this stuff is pretty self-evident.  According to the hardware 
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC 
+ * offset problem.  Weird.
+ */
+#define EC_RAW_RUN_MODE		(EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
+				 EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN	0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL	0x0
+#define EC_DEFAULT_SPDIF1_SEL	0x4
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch.  The Ecard uses a
+ *  control latch will is loaded bit-serially by toggling the Modem control
+ *  lines from function 2 on the E8010.  This function hides these details
+ *  and presents the illusion that we are actually writing to a distinct
+ *  register.
+ */
+
+static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value)
+{
+	unsigned short count;
+	unsigned int data;
+	unsigned long hc_port;
+	unsigned int hc_value;
+
+	hc_port = emu->port + HCFG;
+	hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT);
+	outl(hc_value, hc_port);
+
+	for (count = 0; count < EC_NUM_CONTROL_BITS; count++) {
+
+		/* Set up the value */
+		data = ((value & 0x1) ? PULSEN_BIT : 0);
+		value >>= 1;
+
+		outl(hc_value | data, hc_port);
+
+		/* Clock the shift register */
+		outl(hc_value | data | HANDN_BIT, hc_port);
+		outl(hc_value | data, hc_port);
+	}
+
+	/* Latch the bits */
+	outl(hc_value | HOOKN_BIT, hc_port);
+	outl(hc_value, hc_port);
+}
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller.  The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel.  The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
+					 unsigned short gain)
+{
+	unsigned int bit;
+
+	/* Enable writing to the TRIM registers */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+	/* Do it again to insure that we meet hold time requirements */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+	for (bit = (1 << 15); bit; bit >>= 1) {
+		unsigned int value;
+		
+		value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
+
+		if (gain & bit)
+			value |= EC_TRIM_SDATA;
+
+		/* Clock the bit */
+		snd_emu10k1_ecard_write(emu, value);
+		snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK);
+		snd_emu10k1_ecard_write(emu, value);
+	}
+
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+}
+
+static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
+{
+	unsigned int hc_value;
+
+	/* Set up the initial settings */
+	emu->ecard_ctrl = EC_RAW_RUN_MODE |
+			  EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
+			  EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
+
+	/* Step 0: Set the codec type in the hardware control register 
+	 * and enable audio output */
+	hc_value = inl(emu->port + HCFG);
+	outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
+	inl(emu->port + HCFG);
+
+	/* Step 1: Turn off the led and deassert TRIM_CS */
+	snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 2: Calibrate the ADC and DAC */
+	snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 3: Wait for awhile;   XXX We can't get away with this
+	 * under a real operating system; we'll need to block and wait that
+	 * way. */
+	snd_emu10k1_wait(emu, 48000);
+
+	/* Step 4: Switch off the DAC and ADC calibration.  Note
+	 * That ADC_CAL is actually an inverted signal, so we assert
+	 * it here to stop calibration.  */
+	snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 4: Switch into run mode */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+
+	/* Step 5: Set the analog input gain */
+	snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN);
+
+	return 0;
+}
+
+static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
+{
+	unsigned long special_port;
+	unsigned int value;
+
+	/* Special initialisation routine
+	 * before the rest of the IO-Ports become active.
+	 */
+	special_port = emu->port + 0x38;
+	value = inl(special_port);
+	outl(0x00d00000, special_port);
+	value = inl(special_port);
+	outl(0x00d00001, special_port);
+	value = inl(special_port);
+	outl(0x00d0005f, special_port);
+	value = inl(special_port);
+	outl(0x00d0007f, special_port);
+	value = inl(special_port);
+	outl(0x0090007f, special_port);
+	value = inl(special_port);
+
+	snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
+	return 0;
+}
+
+static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename)
+{
+	int err;
+	int n, i;
+	int reg;
+	int value;
+	const struct firmware *fw_entry;
+
+	if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
+		snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err);
+		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
+	 * GPIO6 -> FPGA CCLK
+	 * GPIO5 -> FPGA DIN
+	 * FPGA CONFIG OFF -> FPGA PGMN
+	 */
+	outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
+	udelay(1);
+	outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
+	udelay(100); /* Allow FPGA memory to clean */
+	for(n = 0; n < fw_entry->size; n++) {
+		value=fw_entry->data[n];	
+		for(i = 0; i < 8; i++) {
+			reg = 0x80;
+			if (value & 0x1)
+				reg = reg | 0x20;
+			value = value >> 1;   
+			outl(reg, emu->port + A_IOCFG);
+			outl(reg | 0x40, emu->port + A_IOCFG);
+		}
+	}
+	/* After programming, set GPIO bit 4 high again. */
+	outl(0x10, emu->port + A_IOCFG);
+	
+
+        release_firmware(fw_entry);
+	return 0;
+}
+
+int emu1010_firmware_thread(void *data) {
+	struct snd_emu10k1 * emu = data;
+	int tmp,tmp2;
+	int reg;
+	int err;
+
+	for (;;) {
+		/* Delay to allow Audio Dock to settle */
+		msleep(1000);
+		if (kthread_should_stop())
+			break;
+		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
+		snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
+		if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
+			/* Audio Dock attached */
+			/* 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 ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+					return err;
+				}
+			} else if (emu->card_capabilities->emu1010 == 2) {
+				if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+					return err;
+				}
+			} else if (emu->card_capabilities->emu1010 == 3) {
+				if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+					return err;
+				}
+			}
+
+			snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
+			snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
+			snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+			/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+			snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+			snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+			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;
+			}
+			snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
+			snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
+			snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
+			snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
+			/* Sync clocking between 1010 and Dock */
+			/* Allow DLL to settle */
+			msleep(10);
+			/* Unmute all. Default is muted after a firmware load */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+ * EMU-1010 - details found out from this driver, official MS Win drivers,
+ * testing the card:
+ *
+ * Audigy2 (aka Alice2):
+ * ---------------------
+ * 	* communication over PCI
+ * 	* conversion of 32-bit data coming over EMU32 links from HANA FPGA
+ *	  to 2 x 16-bit, using internal DSP instructions
+ * 	* slave mode, clock supplied by HANA
+ * 	* linked to HANA using:
+ * 		32 x 32-bit serial EMU32 output channels
+ * 		16 x EMU32 input channels
+ * 		(?) x I2S I/O channels (?)
+ *
+ * FPGA (aka HANA):
+ * ---------------
+ * 	* provides all (?) physical inputs and outputs of the card
+ * 		(ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
+ * 	* provides clock signal for the card and Alice2
+ * 	* two crystals - for 44.1kHz and 48kHz multiples
+ * 	* provides internal routing of signal sources to signal destinations
+ * 	* inputs/outputs to Alice2 - see above
+ *
+ * Current status of the driver:
+ * ----------------------------
+ * 	* only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
+ * 	* PCM device nb. 2:
+ *		16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
+ * 		16 x 32-bit capture - snd_emu10k1_capture_efx_ops
+ */
+static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+{
+	unsigned int i;
+	int tmp,tmp2;
+	int reg;
+	int err;
+
+	snd_printk(KERN_INFO "emu1010: Special config.\n");
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Sound Memory Cache, Lock Tank Memory Cache,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a00c, emu->port + HCFG);
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Tank Memory Cache,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a004, emu->port + HCFG); 
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a000, emu->port + HCFG);
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a000, emu->port + HCFG);
+
+	/* Disable 48Volt power to Audio Dock */
+	snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  0 );
+
+	/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
+	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+	snd_printdd("reg1=0x%x\n",reg);
+	if ((reg & 0x3f) == 0x15) {
+		/* FPGA netlist already present so clear it */
+		/* Return to programming mode */
+
+		snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0x02 );
+	}
+	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+	snd_printdd("reg2=0x%x\n",reg);
+	if ((reg & 0x3f) == 0x15) {
+		/* FPGA failed to return to programming mode */
+		snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
+		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;
+		}
+	}
+
+	/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+	if ((reg & 0x3f) != 0x15) {
+		/* FPGA failed to be programmed */
+		snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
+		return -ENODEV;
+	}
+
+	snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
+	snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp );
+	snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 );
+	snd_printk("Hana ver:%d.%d\n",tmp ,tmp2);
+	/* Enable 48Volt power to Audio Dock */
+	snd_emu1010_fpga_write(emu,  EMU_HANA_DOCK_PWR,  EMU_HANA_DOCK_PWR_ON );
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+	snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+	snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); 
+	/* Optical -> ADAT I/O  */
+	/* 0 : SPDIF
+	 * 1 : ADAT
+	 */
+	emu->emu1010.optical_in = 1; /* IN_ADAT */
+	emu->emu1010.optical_out = 1; /* IN_ADAT */
+	tmp = 0;
+	tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
+		(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+	snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp );
+	snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp );
+	/* Set no attenuation on Audio Dock pads. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 );
+	emu->emu1010.adc_pads = 0x00;
+	snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+	/* Unmute Audio dock DACs, Headphone source DAC-4. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+	snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp );
+	/* DAC PADs. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f );
+	emu->emu1010.dac_pads = 0x0f;
+	snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
+	snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+	/* SPDIF Format. Set Consumer mode, 24bit, copy enable */
+	snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 );
+	/* MIDI routing */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 );
+	/* Unknown. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c );
+	/* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */
+	/* IRQ Enable: All off */
+	snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 );
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
+	snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg);
+	/* Default WCLK set to 48kHz. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 );
+	/* Word Clock source, Internal 48kHz x1 */
+	snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
+	//snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+	/* Audio Dock LEDs. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+
+#if 0
+	/* For 96kHz */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2);
+#endif
+#if 0
+	/* For 192kHz */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4);
+#endif
+#if 1
+	/* For 48kHz */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
+	/* Pavel Hofman - setting defaults for 8 more capture channels
+	 * Defaults only, users will set their own values anyways, let's
+	 * just copy/paste.
+	 */
+	
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
+#endif
+#if 0
+	/* Original */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
+#endif
+	for (i = 0;i < 0x20; i++ ) {
+		/* AudioDock Elink <-  Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE);
+	}
+	for (i = 0;i < 4; i++) {
+		/* Hana SPDIF Out <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE);
+	}
+	for (i = 0;i < 7; i++) {
+		/* Hamoa DAC <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE);
+	}
+	for (i = 0;i < 7; i++) {
+		/* Hana ADAT Out <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
+	}
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
+	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
+	
+	/* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Sound Memory Cache, Lock Tank Memory Cache,
+	 * Mute all codecs.
+	 */
+	outl(0x0000a000, emu->port + HCFG); 
+	/* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Sound Memory Cache, Lock Tank Memory Cache,
+	 * Un-Mute all codecs.
+	 */
+	outl(0x0000a001, emu->port + HCFG);
+ 
+	/* Initial boot complete. Now patches */
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
+	snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); 
+	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 0
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */
+	snd_emu1010_fpga_link_dst_src_write(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;
+
+	/* TEMP: Select SPDIF in/out */
+	//snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+
+	/* TEMP: Select 48kHz SPDIF out */
+	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
+	/* Word Clock source, Internal 48kHz x1 */
+	snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
+	//snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+	emu->emu1010.internal_clock = 1; /* 48000 */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */
+	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
+	//snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */
+	//snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */
+	//snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */
+
+	return 0;
+}
+/*
+ *  Create the EMU10K1 instance
+ */
+
+#ifdef CONFIG_PM
+static int alloc_pm_buffer(struct snd_emu10k1 *emu);
+static void free_pm_buffer(struct snd_emu10k1 *emu);
+#endif
+
+static int snd_emu10k1_free(struct snd_emu10k1 *emu)
+{
+	if (emu->port) {	/* avoid access to already used hardware */
+	       	snd_emu10k1_fx8010_tram_setup(emu, 0);
+		snd_emu10k1_done(emu);
+		/* remove reserved page */
+		if (emu->reserved_page) {
+			snd_emu10k1_synth_free(emu, (struct snd_util_memblk *)emu->reserved_page);
+			emu->reserved_page = NULL;
+		}
+		snd_emu10k1_free_efx(emu);
+       	}
+	if (emu->card_capabilities->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->memhdr)
+		snd_util_memhdr_free(emu->memhdr);
+	if (emu->silent_page.area)
+		snd_dma_free_pages(&emu->silent_page);
+	if (emu->ptb_pages.area)
+		snd_dma_free_pages(&emu->ptb_pages);
+	vfree(emu->page_ptr_table);
+	vfree(emu->page_addr_table);
+#ifdef CONFIG_PM
+	free_pm_buffer(emu);
+#endif
+	if (emu->irq >= 0)
+		free_irq(emu->irq, emu);
+	if (emu->port)
+		pci_release_regions(emu->pci);
+	if (emu->card_capabilities->ca0151_chip) /* P16V */	
+		snd_p16v_free(emu);
+	pci_disable_device(emu->pci);
+	kfree(emu);
+	return 0;
+}
+
+static int snd_emu10k1_dev_free(struct snd_device *device)
+{
+	struct snd_emu10k1 *emu = device->device_data;
+	return snd_emu10k1_free(emu);
+}
+
+static struct snd_emu_chip_details emu_chip_details[] = {
+	/* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	/* DSP: CA0108-IAT
+	 * DAC: CS4382-KQ
+	 * ADC: Philips 1361T
+	 * AC97: STAC9750
+	 * CA0151: None
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
+	 .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .ac97_chip = 1} ,
+	/* Audigy4 (Not PRO) SB0610 */
+	/* Tested by James@superbug.co.uk 4th April 2006 */
+	/* A_IOCFG bits
+	 * Output
+	 * 0: ?
+	 * 1: ?
+	 * 2: ?
+	 * 3: 0 - Digital Out, 1 - Line in
+	 * 4: ?
+	 * 5: ?
+	 * 6: ?
+	 * 7: ?
+	 * Input
+	 * 8: ?
+	 * 9: ?
+	 * A: Green jack sense (Front)
+	 * B: ?
+	 * C: Black jack sense (Rear/Side Right)
+	 * D: Yellow jack sense (Center/LFE/Side Left)
+	 * E: ?
+	 * F: ?
+	 *
+	 * Digital Out/Line in switch using A_IOCFG bit 3 (0x08)
+	 * 0 - Digital Out
+	 * 1 - Line in
+	 */
+	/* Mic input not tested.
+	 * Analog CD input not tested
+	 * Digital Out not tested.
+	 * Line in working.
+	 * Audio output 5.1 working. Side outputs not working.
+	 */
+	/* DSP: CA10300-IAT LF
+	 * DAC: Cirrus Logic CS4382-KQZ
+	 * ADC: Philips 1361T
+	 * AC97: Sigmatel STAC9750
+	 * CA0151: None
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
+	 .driver = "Audigy2", .name = "Audigy 4 [SB0610]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+	 .ac97_chip = 1} ,
+	/* Audigy 2 ZS Notebook Cardbus card.*/
+	/* Tested by James@superbug.co.uk 6th November 2006 */
+	/* Audio output 7.1/Headphones working.
+	 * Digital output working. (AC3 not checked, only PCM)
+	 * Audio Mic/Line inputs working.
+	 * Digital input not tested.
+	 */ 
+	/* DSP: Tina2
+	 * DAC: Wolfson WM8768/WM8568
+	 * ADC: Wolfson WM8775
+	 * AC97: None
+	 * CA0151: None
+	 */
+	/* Tested by James@superbug.co.uk 4th April 2006 */
+	/* A_IOCFG bits
+	 * Output
+	 * 0: Not Used
+	 * 1: 0 = Mute all the 7.1 channel out. 1 = unmute.
+	 * 2: Analog input 0 = line in, 1 = mic in
+	 * 3: Not Used
+	 * 4: Digital output 0 = off, 1 = on.
+	 * 5: Not Used
+	 * 6: Not Used
+	 * 7: Not Used
+	 * Input
+	 *      All bits 1 (0x3fxx) means nothing plugged in.
+	 * 8-9: 0 = Line in/Mic, 2 = Optical in, 3 = Nothing.
+	 * A-B: 0 = Headphones, 2 = Optical out, 3 = Nothing.
+	 * C-D: 2 = Front/Rear/etc, 3 = nothing.
+	 * E-F: Always 0
+	 *
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
+	 .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .ca_cardbus_chip = 1,
+	 .spi_dac = 1,
+	 .i2c_adc = 1,
+	 .spk71 = 1} ,
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
+	 .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", 
+	 .id = "EMU1010",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .ca_cardbus_chip = 1,
+	 .spk71 = 1 ,
+	 .emu1010 = 3} ,
+	{.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} ,
+	{.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@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@superbug.co.uk 3rd July 2005 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
+	 .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	/* Tested by shane-alsa@cm.nu 5th Nov 2005 */
+	/* The 0x20061102 does have SB0350 written on it
+	 * Just like 0x20021102
+	 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20061102,
+	 .driver = "Audigy2", .name = "Audigy 2 [SB0350b]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
+	 .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
+	 .driver = "Audigy2", .name = "Audigy 2 ZS [2001]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	/* Audigy 2 */
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	/* DSP: CA0102-IAT
+	 * DAC: CS4382-KQ
+	 * ADC: Philips 1361T
+	 * AC97: STAC9721
+	 * CA0151: Yes
+	 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
+	 .driver = "Audigy2", .name = "Audigy 2 [SB0240]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
+	 .driver = "Audigy2", .name = "Audigy 2 EX [1005]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1} ,
+	/* Dell OEM/Creative Labs Audigy 2 ZS */
+	/* See ALSA bug#1365 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102,
+	 .driver = "Audigy2", .name = "Audigy 2 ZS [SB0353]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
+	 .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]", 
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
+	 .driver = "Audigy2", .name = "Audigy 2 [Unknown]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
+	 .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102,
+	 .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
+	 .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004,
+	 .driver = "Audigy", .name = "Audigy 1 [Unknown]", 
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
+	 .driver = "EMU10K1", .name = "SBLive! [SB0105]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by ALSA bug#1680 26th December 2005 */
+	/* note: It really has SB0220 written on the card. */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80661102,
+	 .driver = "EMU10K1", .name = "SB Live 5.1 Dell OEM [SB0220]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by Thomas Zehetbauer 27th Aug 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102,
+	 .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
+	 .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
+	 .driver = "EMU10K1", .name = "SB Live 5.1", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by alsa bugtrack user "hus" bug #1297 12th Aug 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+	 .driver = "EMU10K1", .name = "SBLive 5.1 [SB0060]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 2, /* ac97 is optional; both SBLive 5.1 and platinum
+			  * share the same IDs!
+			  */
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
+	 .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
+	 .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
+	 .driver = "EMU10K1", .name = "E-mu APS [4001]", 
+	 .id = "APS",
+	 .emu10k1_chip = 1,
+	 .ecard = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
+	 .driver = "EMU10K1", .name = "SBLive! [CT4620]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
+	 .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002,
+	 .driver = "EMU10K1", .name = "SB Live [Unknown]", 
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{ } /* terminator */
+};
+
+int __devinit snd_emu10k1_create(struct snd_card *card,
+		       struct pci_dev * pci,
+		       unsigned short extin_mask,
+		       unsigned short extout_mask,
+		       long max_cache_bytes,
+		       int enable_ir,
+		       uint subsystem,
+		       struct snd_emu10k1 ** remu)
+{
+	struct snd_emu10k1 *emu;
+	int idx, err;
+	int is_audigy;
+	unsigned int silent_page;
+	const struct snd_emu_chip_details *c;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_emu10k1_dev_free,
+	};
+	
+	*remu = NULL;
+
+	/* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	emu = kzalloc(sizeof(*emu), GFP_KERNEL);
+	if (emu == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	emu->card = card;
+	spin_lock_init(&emu->reg_lock);
+	spin_lock_init(&emu->emu_lock);
+	spin_lock_init(&emu->voice_lock);
+	spin_lock_init(&emu->synth_lock);
+	spin_lock_init(&emu->memblk_lock);
+	mutex_init(&emu->fx8010.lock);
+	INIT_LIST_HEAD(&emu->mapped_link_head);
+	INIT_LIST_HEAD(&emu->mapped_order_link_head);
+	emu->pci = pci;
+	emu->irq = -1;
+	emu->synth = NULL;
+	emu->get_synth_voice = NULL;
+	/* read revision & serial */
+	emu->revision = pci->revision;
+	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
+	snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
+
+	for (c = emu_chip_details; c->vendor; c++) {
+		if (c->vendor == pci->vendor && c->device == pci->device) {
+			if (subsystem) {
+				if (c->subsystem && (c->subsystem == subsystem) ) {
+					break;
+				} else continue;
+			} else {
+				if (c->subsystem && (c->subsystem != emu->serial) )
+					continue;
+				if (c->revision && c->revision != emu->revision)
+					continue;
+			}
+			break;
+		}
+	}
+	if (c->vendor == 0) {
+		snd_printk(KERN_ERR "emu10k1: Card not recognised\n");
+		kfree(emu);
+		pci_disable_device(pci);
+		return -ENOENT;
+	}
+	emu->card_capabilities = c;
+	if (c->subsystem && !subsystem)
+		snd_printdd("Sound card name=%s\n", c->name);
+	else if (subsystem) 
+		snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n",
+		       	c->name, pci->vendor, pci->device, emu->serial, c->subsystem);
+	else 
+		snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n",
+		      	c->name, pci->vendor, pci->device, emu->serial);
+	
+	if (!*card->id && c->id) {
+		int i, n = 0;
+		strlcpy(card->id, c->id, sizeof(card->id));
+		for (;;) {
+			for (i = 0; i < snd_ecards_limit; i++) {
+				if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
+					break;
+			}
+			if (i >= snd_ecards_limit)
+				break;
+			n++;
+			if (n >= SNDRV_CARDS)
+				break;
+			snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
+		}
+	}
+
+	is_audigy = emu->audigy = c->emu10k2_chip;
+
+	/* set the DMA transfer mask */
+	emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK;
+	if (pci_set_dma_mask(pci, emu->dma_mask) < 0 ||
+	    pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) {
+		snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask);
+		kfree(emu);
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+	if (is_audigy)
+		emu->gpr_base = A_FXGPREGBASE;
+	else
+		emu->gpr_base = FXGPREGBASE;
+
+	if ((err = pci_request_regions(pci, "EMU10K1")) < 0) {
+		kfree(emu);
+		pci_disable_device(pci);
+		return err;
+	}
+	emu->port = pci_resource_start(pci, 0);
+
+	if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED,
+			"EMU10K1", emu)) {
+		err = -EBUSY;
+		goto error;
+	}
+	emu->irq = pci->irq;
+
+	emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+				32 * 1024, &emu->ptb_pages) < 0) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
+	emu->page_addr_table = vmalloc(emu->max_cache_pages *
+				       sizeof(unsigned long));
+	if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+				EMUPAGESIZE, &emu->silent_page) < 0) {
+		err = -ENOMEM;
+		goto error;
+	}
+	emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
+	if (emu->memhdr == NULL) {
+		err = -ENOMEM;
+		goto error;
+	}
+	emu->memhdr->block_extra_size = sizeof(struct snd_emu10k1_memblk) -
+		sizeof(struct snd_util_memblk);
+
+	pci_set_master(pci);
+
+	emu->fx8010.fxbus_mask = 0x303f;
+	if (extin_mask == 0)
+		extin_mask = 0x3fcf;
+	if (extout_mask == 0)
+		extout_mask = 0x7fff;
+	emu->fx8010.extin_mask = extin_mask;
+	emu->fx8010.extout_mask = extout_mask;
+	emu->enable_ir = enable_ir;
+
+	if (emu->card_capabilities->ca_cardbus_chip) {
+		if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+			goto error;
+	}
+	if (emu->card_capabilities->ecard) {
+		if ((err = snd_emu10k1_ecard_init(emu)) < 0)
+			goto error;
+ 	} else if (emu->card_capabilities->emu1010) {
+ 		if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
+ 			snd_emu10k1_free(emu);
+ 			return err;
+ 		}
+	} else {
+		/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
+			does not support this, it shouldn't do any harm */
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+	}
+
+	/* initialize TRAM setup */
+	emu->fx8010.itram_size = (16 * 1024)/2;
+	emu->fx8010.etram_pages.area = NULL;
+	emu->fx8010.etram_pages.bytes = 0;
+
+	/*
+	 *  Init to 0x02109204 :
+	 *  Clock accuracy    = 0     (1000ppm)
+	 *  Sample Rate       = 2     (48kHz)
+	 *  Audio Channel     = 1     (Left of 2)
+	 *  Source Number     = 0     (Unspecified)
+	 *  Generation Status = 1     (Original for Cat Code 12)
+	 *  Cat Code          = 12    (Digital Signal Mixer)
+	 *  Mode              = 0     (Mode 0)
+	 *  Emphasis          = 0     (None)
+	 *  CP                = 1     (Copyright unasserted)
+	 *  AN                = 0     (Audio data)
+	 *  P                 = 0     (Consumer)
+	 */
+	emu->spdif_bits[0] = emu->spdif_bits[1] =
+		emu->spdif_bits[2] = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+		SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+		SPCS_GENERATIONSTATUS | 0x00001200 |
+		0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
+
+	emu->reserved_page = (struct snd_emu10k1_memblk *)
+		snd_emu10k1_synth_alloc(emu, 4096);
+	if (emu->reserved_page)
+		emu->reserved_page->map_locked = 1;
+	
+	/* Clear silent pages and set up pointers */
+	memset(emu->silent_page.area, 0, PAGE_SIZE);
+	silent_page = emu->silent_page.addr << 1;
+	for (idx = 0; idx < MAXPAGES; idx++)
+		((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
+
+	/* set up voice indices */
+	for (idx = 0; idx < NUM_G; idx++) {
+		emu->voices[idx].emu = emu;
+		emu->voices[idx].number = idx;
+	}
+
+	if ((err = snd_emu10k1_init(emu, enable_ir, 0)) < 0)
+		goto error;
+#ifdef CONFIG_PM
+	if ((err = alloc_pm_buffer(emu)) < 0)
+		goto error;
+#endif
+
+	/*  Initialize the effect engine */
+	if ((err = snd_emu10k1_init_efx(emu)) < 0)
+		goto error;
+	snd_emu10k1_audio_enable(emu);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0)
+		goto error;
+
+#ifdef CONFIG_PROC_FS
+	snd_emu10k1_proc_init(emu);
+#endif
+
+	snd_card_set_dev(card, &pci->dev);
+	*remu = emu;
+	return 0;
+
+ error:
+	snd_emu10k1_free(emu);
+	return err;
+}
+
+#ifdef CONFIG_PM
+static unsigned char saved_regs[] = {
+	CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP,
+	FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL,
+	ATKHLDM, DCYSUSM, LFOVAL2, IP, IFATN, PEFE, FMMOD, TREMFRQ, FM2FRQ2,
+	TEMPENV, ADCCR, FXWC, MICBA, ADCBA, FXBA,
+	MICBS, ADCBS, FXBS, CDCS, GPSCS, SPCS0, SPCS1, SPCS2,
+	SPBYPASS, AC97SLOT, CDSRCS, GPSRCS, ZVSRCS, MICIDX, ADCIDX, FXIDX,
+	0xff /* end */
+};
+static unsigned char saved_regs_audigy[] = {
+	A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE,
+	A_FXRT2, A_SENDAMOUNTS, A_FXRT1,
+	0xff /* end */
+};
+
+static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+	int size;
+
+	size = ARRAY_SIZE(saved_regs);
+	if (emu->audigy)
+		size += ARRAY_SIZE(saved_regs_audigy);
+	emu->saved_ptr = vmalloc(4 * NUM_G * size);
+	if (! emu->saved_ptr)
+		return -ENOMEM;
+	if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
+		return -ENOMEM;
+	if (emu->card_capabilities->ca0151_chip &&
+	    snd_p16v_alloc_pm_buffer(emu) < 0)
+		return -ENOMEM;
+	return 0;
+}
+
+static void free_pm_buffer(struct snd_emu10k1 *emu)
+{
+	vfree(emu->saved_ptr);
+	snd_emu10k1_efx_free_pm_buffer(emu);
+	if (emu->card_capabilities->ca0151_chip)
+		snd_p16v_free_pm_buffer(emu);
+}
+
+void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
+{
+	int i;
+	unsigned char *reg;
+	unsigned int *val;
+
+	val = emu->saved_ptr;
+	for (reg = saved_regs; *reg != 0xff; reg++)
+		for (i = 0; i < NUM_G; i++, val++)
+			*val = snd_emu10k1_ptr_read(emu, *reg, i);
+	if (emu->audigy) {
+		for (reg = saved_regs_audigy; *reg != 0xff; reg++)
+			for (i = 0; i < NUM_G; i++, val++)
+				*val = snd_emu10k1_ptr_read(emu, *reg, i);
+	}
+	if (emu->audigy)
+		emu->saved_a_iocfg = inl(emu->port + A_IOCFG);
+	emu->saved_hcfg = inl(emu->port + HCFG);
+}
+
+void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
+{
+	if (emu->card_capabilities->ca_cardbus_chip)
+		snd_emu10k1_cardbus_init(emu);
+	if (emu->card_capabilities->ecard)
+		snd_emu10k1_ecard_init(emu);
+	else if (emu->card_capabilities->emu1010)
+ 		snd_emu10k1_emu1010_init(emu);
+	else
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+	snd_emu10k1_init(emu, emu->enable_ir, 1);
+}
+
+void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
+{
+	int i;
+	unsigned char *reg;
+	unsigned int *val;
+
+	snd_emu10k1_audio_enable(emu);
+
+	/* resore for spdif */
+	if (emu->audigy)
+		outl(emu->saved_a_iocfg, emu->port + A_IOCFG);
+	outl(emu->saved_hcfg, emu->port + HCFG);
+
+	val = emu->saved_ptr;
+	for (reg = saved_regs; *reg != 0xff; reg++)
+		for (i = 0; i < NUM_G; i++, val++)
+			snd_emu10k1_ptr_write(emu, *reg, i, *val);
+	if (emu->audigy) {
+		for (reg = saved_regs_audigy; *reg != 0xff; reg++)
+			for (i = 0; i < NUM_G; i++, val++)
+				snd_emu10k1_ptr_write(emu, *reg, i, *val);
+	}
+}
+#endif
diff -ruN linux-2.6.22/sound/pci/emu10k1/emu10k1x.c linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1x.c
--- linux-2.6.22/sound/pci/emu10k1/emu10k1x.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1x.c	2007-09-02 13:30:43.000000000 +0200
@@ -1062,14 +1062,7 @@
 	return 0;
 }
 
-static int snd_emu10k1x_shared_spdif_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_emu10k1x_shared_spdif_info	snd_ctl_boolean_mono_info
 
 static int snd_emu10k1x_shared_spdif_get(struct snd_kcontrol *kcontrol,
 					 struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/pci/emu10k1/emufx.c linux-2.6.22-alsa/sound/pci/emu10k1/emufx.c
--- linux-2.6.22/sound/pci/emu10k1/emufx.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emufx.c	2007-09-01 20:55:23.000000000 +0200
@@ -1123,6 +1123,11 @@
 	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
 }
 
+/*
+ * Used for emu1010 - conversion from 32-bit capture inputs from HANA
+ * to 2 x 16-bit registers in audigy - their values are read via DMA.
+ * Conversion is performed by Audigy DSP instructions of FX8010.
+ */
 static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
 				struct snd_emu10k1_fx8010_code *icode,
 				u32 *ptr, int tmp, int bit_shifter16,
@@ -1193,12 +1198,16 @@
 	snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
 
 #if 1
-	/* PCM front Playback Volume (independent from stereo mix) */
+	/* PCM front Playback Volume (independent from stereo mix)
+	 * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
+	 * where gpr contains attenuation from corresponding mixer control
+	 * (snd_emu10k1_init_stereo_control)
+	 */
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
 	snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
 	gpr += 2;
-	
+
 	/* PCM Surround Playback (independent from stereo mix) */
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
@@ -1258,8 +1267,16 @@
 
 	/* emu1212 DSP 0 and DSP 1 Capture */
 	if (emu->card_capabilities->emu1010) {
-		A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
-		A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+		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);
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_GPR(tmp));
+			A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x1), A_C_00000001);
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr), A_GPR(tmp));
+		} else {
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+		}
 		snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
 		gpr += 2;
 	}
@@ -1507,7 +1524,11 @@
 		/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
 		snd_printk("EMU outputs on\n");
 		for (z = 0; z < 8; z++) {
-			A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+			if (emu->card_capabilities->ca0108_chip) {
+				A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+			} else {
+				A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+			}
 		}
 	}
 
@@ -1548,41 +1569,116 @@
 #endif
 
 	if (emu->card_capabilities->emu1010) {
-		snd_printk("EMU inputs on\n");
-		/* Capture 8 channels of S32_LE sound */
-		
-		/* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
-		/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
-		/* A_P16VIN(0) is delayed by one sample,
-		 * so all other A_P16VIN channels will need to also be delayed
-		 */
-		/* Left ADC in. 1 of 2 */
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
-		/* Right ADC in 1 of 2 */
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
-		/* For 96kHz mode */
-		/* Left ADC in. 2 of 2 */
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
-		/* Right ADC in 2 of 2 */
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
-		gpr_map[gpr++] = 0x00000000;
-		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
-		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
+		if (emu->card_capabilities->ca0108_chip) {
+			snd_printk("EMU2 inputs on\n");
+			for (z = 0; z < 0x10; z++) {
+				snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, 
+									bit_shifter16,
+									A3_EMU32IN(z),
+									A_FXBUS2(z*2) );
+			}
+		} else {
+			snd_printk("EMU inputs on\n");
+			/* Capture 16 (originally 8) channels of S32_LE sound */
+
+			/* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
+			/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
+			/* A_P16VIN(0) is delayed by one sample,
+			 * so all other A_P16VIN channels will need to also be delayed
+			 */
+			/* Left ADC in. 1 of 2 */
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
+			/* Right ADC in 1 of 2 */
+			gpr_map[gpr++] = 0x00000000;
+			/* Delaying by one sample: instead of copying the input
+			 * value A_P16VIN to output A_FXBUS2 as in the first channel,
+			 * we use an auxiliary register, delaying the value by one
+			 * sample
+			 */
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
+			/* For 96kHz mode */
+			/* Left ADC in. 2 of 2 */
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
+			/* Right ADC in 2 of 2 */
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
+			/* Pavel Hofman - we still have voices, A_FXBUS2s, and
+			 * A_P16VINs available -
+			 * let's add 8 more capture channels - total of 16
+			 */
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x10));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x12));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x14));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x16));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x18));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x1a));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x1c));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x1e));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
+			     A_C_00000000, A_C_00000000);
+		}
 
 #if 0
 		for (z = 4; z < 8; z++) {
@@ -2344,14 +2440,13 @@
 		strcpy(dst, src);
 }
 
-static int snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
+static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
 				   struct snd_emu10k1_fx8010_info *info)
 {
 	char **fxbus, **extin, **extout;
 	unsigned short fxbus_mask, extin_mask, extout_mask;
 	int res;
 
-	memset(info, 0, sizeof(info));
 	info->internal_tram_size = emu->fx8010.itram_size;
 	info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
 	fxbus = fxbuses;
@@ -2368,7 +2463,6 @@
 	for (res = 16; res < 32; res++, extout++)
 		copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
 	info->gpr_controls = emu->fx8010.gpr_count;
-	return 0;
 }
 
 static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg)
@@ -2389,10 +2483,7 @@
 		info = kmalloc(sizeof(*info), GFP_KERNEL);
 		if (!info)
 			return -ENOMEM;
-		if ((res = snd_emu10k1_fx8010_info(emu, info)) < 0) {
-			kfree(info);
-			return res;
-		}
+		snd_emu10k1_fx8010_info(emu, info);
 		if (copy_to_user(argp, info, sizeof(*info))) {
 			kfree(info);
 			return -EFAULT;
diff -ruN linux-2.6.22/sound/pci/emu10k1/emumixer.c linux-2.6.22-alsa/sound/pci/emu10k1/emumixer.c
--- linux-2.6.22/sound/pci/emu10k1/emumixer.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emumixer.c	2007-09-01 20:55:23.000000000 +0200
@@ -77,6 +77,10 @@
 	return 0;
 }
 
+/*
+ * Items labels in enum mixer controls assigning source data to
+ * each destination
+ */
 static char *emu1010_src_texts[] = { 
 	"Silence",
 	"Dock Mic A",
@@ -133,6 +137,9 @@
 	"DSP 31",
 };
 
+/*
+ * List of data sources available for each destination
+ */
 static unsigned int emu1010_src_regs[] = {
 	EMU_SRC_SILENCE,/* 0 */
 	EMU_SRC_DOCK_MIC_A1, /* 1 */
@@ -189,6 +196,10 @@
 	EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
 };
 
+/*
+ * Data destinations - physical EMU outputs.
+ * Each destination has an enum mixer control to choose a data source
+ */
 static unsigned int emu1010_output_dst[] = {
 	EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
 	EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
@@ -216,6 +227,11 @@
 	EMU_DST_HANA_ADAT+7, /* 23 */
 };
 
+/*
+ * Data destinations - HANA outputs going to Alice2 (audigy) for
+ *   capture (EMU32 + I2S links)
+ * Each destination has an enum mixer control to choose a data source
+ */
 static unsigned int emu1010_input_dst[] = {
 	EMU_DST_ALICE2_EMU32_0,
 	EMU_DST_ALICE2_EMU32_1,
@@ -384,15 +400,7 @@
 
 
 
-
-static int snd_emu1010_adc_pads_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_emu1010_adc_pads_info	snd_ctl_boolean_mono_info
 
 static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -440,14 +448,7 @@
 	EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1),
 };
 
-static int snd_emu1010_dac_pads_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_emu1010_dac_pads_info	snd_ctl_boolean_mono_info
 
 static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -500,17 +501,19 @@
 static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
 					  struct snd_ctl_elem_info *uinfo)
 {
-	static char *texts[2] = {
-		"44100", "48000"
+	static char *texts[4] = {
+		"44100", "48000", "SPDIF", "ADAT"
 	};
-
+		
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = 2;
-	if (uinfo->value.enumerated.item > 1)
-                uinfo->value.enumerated.item = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
 	return 0;
+	
+	
 }
 
 static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
@@ -568,6 +571,44 @@
 			/* Unmute all */
 			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
 			break;
+			
+		case 2: /* Take clock from S/PDIF IN */
+			/* Mute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+			/* Default fallback clock 48kHz */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+			/* Word Clock source, sync to S/PDIF input */
+			snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+				EMU_HANA_WCLOCK_HANA_SPDIF_IN | EMU_HANA_WCLOCK_1X );
+			/* Set LEDs on Audio Dock */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+				EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
+			/* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */	
+			/* Allow DLL to settle */
+			msleep(10);
+			/* Unmute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			break;
+		
+		case 3: 			
+			/* Take clock from ADAT IN */
+			/* Mute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+			/* Default fallback clock 48kHz */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+			/* Word Clock source, sync to ADAT input */
+			snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+				EMU_HANA_WCLOCK_HANA_ADAT_IN | EMU_HANA_WCLOCK_1X );
+			/* Set LEDs on Audio Dock */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
+			/* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */	
+			/* Allow DLL to settle */
+			msleep(10);
+			/*   Unmute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			 
+			
+			break;		
 		}
 	}
         return change;
@@ -855,7 +896,7 @@
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
-	.count =	4,
+	.count =	3,
 	.info =         snd_emu10k1_spdif_info,
 	.get =          snd_emu10k1_spdif_get_mask
 };
@@ -864,7 +905,7 @@
 {
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
 	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	.count =	4,
+	.count =	3,
 	.info =         snd_emu10k1_spdif_info,
 	.get =          snd_emu10k1_spdif_get,
 	.put =          snd_emu10k1_spdif_put
@@ -1310,14 +1351,7 @@
 	.put =          snd_emu10k1_efx_attn_put
 };
 
-static int snd_emu10k1_shared_spdif_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_emu10k1_shared_spdif_info	snd_ctl_boolean_mono_info
 
 static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
 					struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/pci/emu10k1/emupcm.c linux-2.6.22-alsa/sound/pci/emu10k1/emupcm.c
--- linux-2.6.22/sound/pci/emu10k1/emupcm.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emupcm.c	2007-09-01 20:55:23.000000000 +0200
@@ -1233,24 +1233,26 @@
 	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
 	spin_lock_irq(&emu->reg_lock);
 	if (emu->card_capabilities->emu1010) {
-		/* TODO 
+		/*  Nb. of channels has been increased to 16 */
+		/* TODO
 		 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
 		 * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
 		 * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
 		 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
 		 * rate_min = 44100,
 		 * rate_max = 192000,
-		 * channels_min = 8,
-		 * channels_max = 8,
+		 * channels_min = 16,
+		 * channels_max = 16,
 		 * Need to add mixer control to fix sample rate
 		 *                 
-		 * There are 16 mono channels of 16bits each.
+		 * There are 32 mono channels of 16bits each.
 		 * 24bit Audio uses 2x channels over 16bit
 		 * 96kHz uses 2x channels over 48kHz
 		 * 192kHz uses 4x channels over 48kHz
-		 * So, for 48kHz 24bit, one has 8 channels
-		 * for 96kHz 24bit, one has 4 channels
-		 * for 192kHz 24bit, one has 2 channels
+		 * So, for 48kHz 24bit, one has 16 channels
+		 * for 96kHz 24bit, one has 8 channels
+		 * for 192kHz 24bit, one has 4 channels
+		 *
 		 */
 #if 1
 		switch (emu->emu1010.internal_clock) {
@@ -1258,13 +1260,15 @@
 			/* For 44.1kHz */
 			runtime->hw.rates = SNDRV_PCM_RATE_44100;
 			runtime->hw.rate_min = runtime->hw.rate_max = 44100;
-			runtime->hw.channels_min = runtime->hw.channels_max = 8;
+			runtime->hw.channels_min =
+				runtime->hw.channels_max = 16;
 			break;
 		case 1:
 			/* For 48kHz */
 			runtime->hw.rates = SNDRV_PCM_RATE_48000;
 			runtime->hw.rate_min = runtime->hw.rate_max = 48000;
-			runtime->hw.channels_min = runtime->hw.channels_max = 8;
+			runtime->hw.channels_min =
+				runtime->hw.channels_max = 16;
 			break;
 		};
 #endif
@@ -1282,7 +1286,7 @@
 #endif
 		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
 		/* efx_voices_mask[0] is expected to be zero
- 		 * efx_voices_mask[1] is expected to have 16bits set
+ 		 * efx_voices_mask[1] is expected to have 32bits set
 		 */
 	} else {
 		runtime->hw.channels_min = runtime->hw.channels_max = 0;
@@ -1787,11 +1791,24 @@
 	/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
 	if (emu->audigy) {
 		emu->efx_voices_mask[0] = 0;
-		emu->efx_voices_mask[1] = 0xffff;
+		if (emu->card_capabilities->emu1010)
+			/* Pavel Hofman - 32 voices will be used for
+			 * capture (write mode) -
+			 * each bit = corresponding voice
+			 */
+			emu->efx_voices_mask[1] = 0xffffffff;
+		else
+			emu->efx_voices_mask[1] = 0xffff;
 	} else {
 		emu->efx_voices_mask[0] = 0xffff0000;
 		emu->efx_voices_mask[1] = 0;
 	}
+	/* For emu1010, the control has to set 32 upper bits (voices)
+	 * out of the 64 bits (voices) to true for the 16-channels capture
+	 * to work correctly. Correct A_FXWC2 initial value (0xffffffff)
+	 * is already defined but the snd_emu10k1_pcm_efx_voices_mask
+	 * control can override this register's value.
+	 */
 	kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
 	if (!kctl)
 		return -ENOMEM;
diff -ruN linux-2.6.22/sound/pci/emu10k1/emuproc.c linux-2.6.22-alsa/sound/pci/emu10k1/emuproc.c
--- linux-2.6.22/sound/pci/emu10k1/emuproc.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emuproc.c	2007-09-01 20:55:23.000000000 +0200
@@ -240,8 +240,42 @@
 				  struct snd_info_buffer *buffer)
 {
 	struct snd_emu10k1 *emu = entry->private_data;
-	snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
-	snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
+	u32 value;
+	u32 value2;
+	unsigned long flags;
+	u32 rate;
+
+	if (emu->card_capabilities->emu1010) {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		snd_emu1010_fpga_read(emu, 0x38, &value);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		if ((value & 0x1) == 0) {
+			spin_lock_irqsave(&emu->emu_lock, flags);
+			snd_emu1010_fpga_read(emu, 0x2a, &value);
+			snd_emu1010_fpga_read(emu, 0x2b, &value2);
+			spin_unlock_irqrestore(&emu->emu_lock, flags);
+			rate = 0x1770000 / (((value << 5) | value2)+1);	
+			snd_iprintf(buffer, "ADAT Locked : %u\n", rate);
+		} else {
+			snd_iprintf(buffer, "ADAT Unlocked\n");
+		}
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		snd_emu1010_fpga_read(emu, 0x20, &value);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		if ((value & 0x4) == 0) {
+			spin_lock_irqsave(&emu->emu_lock, flags);
+			snd_emu1010_fpga_read(emu, 0x28, &value);
+			snd_emu1010_fpga_read(emu, 0x29, &value2);
+			spin_unlock_irqrestore(&emu->emu_lock, flags);
+			rate = 0x1770000 / (((value << 5) | value2)+1);	
+			snd_iprintf(buffer, "SPDIF Locked : %d\n", rate);
+		} else {
+			snd_iprintf(buffer, "SPDIF Unlocked\n");
+		}
+	} else {
+		snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
+		snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
+	}
 #if 0
 	val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
 	snd_iprintf(buffer, "\nZoomed Video\n");
@@ -379,20 +413,16 @@
 				     struct snd_info_buffer *buffer)
 {
 	struct snd_emu10k1 *emu = entry->private_data;
-	unsigned long value;
+	int value;
 	unsigned long flags;
-	unsigned long regs;
 	int i;
 	snd_iprintf(buffer, "EMU1010 Registers:\n\n");
 
-	for(i = 0; i < 0x30; i+=1) {
+	for(i = 0; i < 0x40; i+=1) {
 		spin_lock_irqsave(&emu->emu_lock, flags);
-		regs=i+0x40; /* 0x40 upwards are registers. */
-		outl(regs, emu->port + A_IOCFG);
-		outl(regs | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
-		value = inl(emu->port + A_IOCFG);
+		snd_emu1010_fpga_read(emu, i, &value);
 		spin_unlock_irqrestore(&emu->emu_lock, flags);
-		snd_iprintf(buffer, "%02X: %08lX, %02lX\n", i, value, (value >> 8) & 0x7f);
+		snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f);
 	}
 }
 
@@ -555,9 +585,9 @@
 {
 	struct snd_info_entry *entry;
 #ifdef CONFIG_SND_DEBUG
-	if ((emu->card_capabilities->emu1010) &&
-	     snd_card_proc_new(emu->card, "emu1010_regs", &entry)) {
-		snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
+	if (emu->card_capabilities->emu1010) {
+		if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) 
+			snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
 	}
 	if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
 		snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
diff -ruN linux-2.6.22/sound/pci/emu10k1/io.c linux-2.6.22-alsa/sound/pci/emu10k1/io.c
--- linux-2.6.22/sound/pci/emu10k1/io.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/io.c	2007-09-01 20:55:23.000000000 +0200
@@ -226,9 +226,9 @@
     	return 0;
 }
 
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
 {
-	if (reg < 0 || reg > 0x3f)
+	if (reg > 0x3f)
 		return 1;
 	reg += 0x40; /* 0x40 upwards are registers. */
 	if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
@@ -244,9 +244,9 @@
 	return 0;
 }
 
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value)
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
 {
-	if (reg < 0 || reg > 0x3f)
+	if (reg > 0x3f)
 		return 1;
 	reg += 0x40; /* 0x40 upwards are registers. */
 	outl(reg, emu->port + A_IOCFG);
@@ -261,7 +261,7 @@
 /* Each Destination has one and only one Source,
  * but one Source can feed any number of Destinations simultaneously.
  */
-int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src)
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
 {
 	snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
 	snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
diff -ruN linux-2.6.22/sound/pci/emu10k1/p16v.c linux-2.6.22-alsa/sound/pci/emu10k1/p16v.c
--- linux-2.6.22/sound/pci/emu10k1/p16v.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/p16v.c	2007-09-01 20:55:23.000000000 +0200
@@ -124,11 +124,12 @@
 
  /* hardware definition */
 static struct snd_pcm_hardware snd_p16v_playback_hw = {
-	.info =			(SNDRV_PCM_INFO_MMAP | 
-				 SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_RESUME |
-				 SNDRV_PCM_INFO_MMAP_VALID),
+	.info =			SNDRV_PCM_INFO_MMAP | 
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_RESUME |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_SYNC_START,
 	.formats =		SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
 	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
 	.rate_min =		44100,
@@ -207,6 +208,11 @@
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
                 return err;
 
+	runtime->sync.id32[0] = substream->pcm->card->number;
+	runtime->sync.id32[1] = 'P';
+	runtime->sync.id32[2] = 16;
+	runtime->sync.id32[3] = 'V';
+
 	return 0;
 }
 /* open_capture callback */
@@ -448,6 +454,9 @@
 		break;
 	}
         snd_pcm_group_for_each_entry(s, substream) {
+		if (snd_pcm_substream_chip(s) != emu ||
+		    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			continue;
 		runtime = s->runtime;
 		epcm = runtime->private_data;
 		channel = substream->pcm->device-emu->p16v_device_offset;
diff -ruN linux-2.6.22/sound/pci/ens1370.c linux-2.6.22-alsa/sound/pci/ens1370.c
--- linux-2.6.22/sound/pci/ens1370.c	2007-07-28 13:40:44.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ens1370.c	2007-09-02 13:30:43.000000000 +0200
@@ -1419,15 +1419,7 @@
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \
   .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put }
 
-static int snd_es1371_spdif_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_es1371_spdif_info		snd_ctl_boolean_mono_info
 
 static int snd_es1371_spdif_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
@@ -1489,15 +1481,7 @@
 };
 
 
-static int snd_es1373_rear_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_es1373_rear_info		snd_ctl_boolean_mono_info
 
 static int snd_es1373_rear_get(struct snd_kcontrol *kcontrol,
 			       struct snd_ctl_elem_value *ucontrol)
@@ -1542,15 +1526,7 @@
 	.put =		snd_es1373_rear_put,
 };
 
-static int snd_es1373_line_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_es1373_line_info		snd_ctl_boolean_mono_info
 
 static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
 			       struct snd_ctl_elem_value *ucontrol)
@@ -1607,8 +1583,8 @@
 	unsigned char rev;		/* revision */
 };
 
-static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq,
-					 struct es1371_quirk *list)
+static int es1371_quirk_lookup(struct ensoniq *ensoniq,
+				struct es1371_quirk *list)
 {
 	while (list->vid != (unsigned short)PCI_ANY_ID) {
 		if (ensoniq->pci->vendor == list->vid &&
@@ -1707,15 +1683,7 @@
   .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \
   .private_value = mask }
 
-static int snd_ensoniq_control_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_ensoniq_control_info	snd_ctl_boolean_mono_info
 
 static int snd_ensoniq_control_get(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_value *ucontrol)
diff -ruN linux-2.6.22/sound/pci/ens1370.c.orig linux-2.6.22-alsa/sound/pci/ens1370.c.orig
--- linux-2.6.22/sound/pci/ens1370.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/pci/ens1370.c.orig	2007-09-01 20:55:23.000000000 +0200
@@ -0,0 +1,2496 @@
+/*
+ *  Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ *		     Thomas Sailer <sailer@ife.ee.ethz.ch>
+ *
+ *   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
+ *
+ */
+
+/* Power-Management-Code ( CONFIG_PM )
+ * for ens1371 only ( FIXME )
+ * derived from cs4281.c, atiixp.c and via82xx.c
+ * using http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c1540.htm
+ * by Kurt J. Bosch
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#ifdef CHIP1371
+#include <sound/ac97_codec.h>
+#else
+#include <sound/ak4531_codec.h>
+#endif
+#include <sound/initval.h>
+#include <sound/asoundef.h>
+
+#ifndef CHIP1371
+#undef CHIP1370
+#define CHIP1370
+#endif
+
+#ifdef CHIP1370
+#define DRIVER_NAME "ENS1370"
+#else
+#define DRIVER_NAME "ENS1371"
+#endif
+
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Thomas Sailer <sailer@ife.ee.ethz.ch>");
+MODULE_LICENSE("GPL");
+#ifdef CHIP1370
+MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370");
+MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370},"
+	        "{Creative Labs,SB PCI64/128 (ES1370)}}");
+#endif
+#ifdef CHIP1371
+MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+");
+MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73},"
+		"{Ensoniq,AudioPCI ES1373},"
+		"{Creative Labs,Ectiva EV1938},"
+		"{Creative Labs,SB PCI64/128 (ES1371/73)},"
+		"{Creative Labs,Vibra PCI128},"
+		"{Ectiva,EV1938}}");
+#endif
+
+#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#define SUPPORT_JOYSTICK
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable switches */
+#ifdef SUPPORT_JOYSTICK
+#ifdef CHIP1371
+static int joystick_port[SNDRV_CARDS];
+#else
+static int joystick[SNDRV_CARDS];
+#endif
+#endif
+#ifdef CHIP1371
+static int spdif[SNDRV_CARDS];
+static int lineio[SNDRV_CARDS];
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Ensoniq AudioPCI soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Ensoniq AudioPCI soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard.");
+#ifdef SUPPORT_JOYSTICK
+#ifdef CHIP1371
+module_param_array(joystick_port, int, NULL, 0444);
+MODULE_PARM_DESC(joystick_port, "Joystick port address.");
+#else
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable joystick.");
+#endif
+#endif /* SUPPORT_JOYSTICK */
+#ifdef CHIP1371
+module_param_array(spdif, int, NULL, 0444);
+MODULE_PARM_DESC(spdif, "S/PDIF output (-1 = none, 0 = auto, 1 = force).");
+module_param_array(lineio, int, NULL, 0444);
+MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force).");
+#endif
+
+/* ES1371 chip ID */
+/* This is a little confusing because all ES1371 compatible chips have the
+   same DEVICE_ID, the only thing differentiating them is the REV_ID field.
+   This is only significant if you want to enable features on the later parts.
+   Yes, I know it's stupid and why didn't we use the sub IDs?
+*/
+#define ES1371REV_ES1373_A  0x04
+#define ES1371REV_ES1373_B  0x06
+#define ES1371REV_CT5880_A  0x07
+#define CT5880REV_CT5880_C  0x02
+#define CT5880REV_CT5880_D  0x03	/* ??? -jk */
+#define CT5880REV_CT5880_E  0x04	/* mw */
+#define ES1371REV_ES1371_B  0x09
+#define EV1938REV_EV1938_A  0x00
+#define ES1371REV_ES1373_8  0x08
+
+/*
+ * Direct registers
+ */
+
+#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x)
+
+#define ES_REG_CONTROL	0x00	/* R/W: Interrupt/Chip select control register */
+#define   ES_1370_ADC_STOP	(1<<31)		/* disable capture buffer transfers */
+#define   ES_1370_XCTL1 	(1<<30)		/* general purpose output bit */
+#define   ES_1373_BYPASS_P1	(1<<31)		/* bypass SRC for PB1 */
+#define   ES_1373_BYPASS_P2	(1<<30)		/* bypass SRC for PB2 */
+#define   ES_1373_BYPASS_R	(1<<29)		/* bypass SRC for REC */
+#define   ES_1373_TEST_BIT	(1<<28)		/* should be set to 0 for normal operation */
+#define   ES_1373_RECEN_B	(1<<27)		/* mix record with playback for I2S/SPDIF out */
+#define   ES_1373_SPDIF_THRU	(1<<26)		/* 0 = SPDIF thru mode, 1 = SPDIF == dig out */
+#define   ES_1371_JOY_ASEL(o)	(((o)&0x03)<<24)/* joystick port mapping */
+#define   ES_1371_JOY_ASELM	(0x03<<24)	/* mask for above */
+#define   ES_1371_JOY_ASELI(i)  (((i)>>24)&0x03)
+#define   ES_1371_GPIO_IN(i)	(((i)>>20)&0x0f)/* GPIO in [3:0] pins - R/O */
+#define   ES_1370_PCLKDIVO(o)	(((o)&0x1fff)<<16)/* clock divide ratio for DAC2 */
+#define   ES_1370_PCLKDIVM	((0x1fff)<<16)	/* mask for above */
+#define   ES_1370_PCLKDIVI(i)	(((i)>>16)&0x1fff)/* clock divide ratio for DAC2 */
+#define   ES_1371_GPIO_OUT(o)	(((o)&0x0f)<<16)/* GPIO out [3:0] pins - W/R */
+#define   ES_1371_GPIO_OUTM     (0x0f<<16)	/* mask for above */
+#define   ES_MSFMTSEL		(1<<15)		/* MPEG serial data format; 0 = SONY, 1 = I2S */
+#define   ES_1370_M_SBB		(1<<14)		/* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */
+#define   ES_1371_SYNC_RES	(1<<14)		/* Warm AC97 reset */
+#define   ES_1370_WTSRSEL(o)	(((o)&0x03)<<12)/* fixed frequency clock for DAC1 */
+#define   ES_1370_WTSRSELM	(0x03<<12)	/* mask for above */
+#define   ES_1371_ADC_STOP	(1<<13)		/* disable CCB transfer capture information */
+#define   ES_1371_PWR_INTRM	(1<<12)		/* power level change interrupts enable */
+#define   ES_1370_DAC_SYNC	(1<<11)		/* DAC's are synchronous */
+#define   ES_1371_M_CB		(1<<11)		/* capture clock source; 0 = AC'97 ADC; 1 = I2S */
+#define   ES_CCB_INTRM		(1<<10)		/* CCB voice interrupts enable */
+#define   ES_1370_M_CB		(1<<9)		/* capture clock source; 0 = ADC; 1 = MPEG */
+#define   ES_1370_XCTL0		(1<<8)		/* generap purpose output bit */
+#define   ES_1371_PDLEV(o)	(((o)&0x03)<<8)	/* current power down level */
+#define   ES_1371_PDLEVM	(0x03<<8)	/* mask for above */
+#define   ES_BREQ		(1<<7)		/* memory bus request enable */
+#define   ES_DAC1_EN		(1<<6)		/* DAC1 playback channel enable */
+#define   ES_DAC2_EN		(1<<5)		/* DAC2 playback channel enable */
+#define   ES_ADC_EN		(1<<4)		/* ADC capture channel enable */
+#define   ES_UART_EN		(1<<3)		/* UART enable */
+#define   ES_JYSTK_EN		(1<<2)		/* Joystick module enable */
+#define   ES_1370_CDC_EN	(1<<1)		/* Codec interface enable */
+#define   ES_1371_XTALCKDIS	(1<<1)		/* Xtal clock disable */
+#define   ES_1370_SERR_DISABLE	(1<<0)		/* PCI serr signal disable */
+#define   ES_1371_PCICLKDIS     (1<<0)		/* PCI clock disable */
+#define ES_REG_STATUS	0x04	/* R/O: Interrupt/Chip select status register */
+#define   ES_INTR               (1<<31)		/* Interrupt is pending */
+#define   ES_1371_ST_AC97_RST	(1<<29)		/* CT5880 AC'97 Reset bit */
+#define   ES_1373_REAR_BIT27	(1<<27)		/* rear bits: 000 - front, 010 - mirror, 101 - separate */
+#define   ES_1373_REAR_BIT26	(1<<26)
+#define   ES_1373_REAR_BIT24	(1<<24)
+#define   ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */
+#define   ES_1373_SPDIF_EN	(1<<18)		/* SPDIF enable */
+#define   ES_1373_SPDIF_TEST	(1<<17)		/* SPDIF test */
+#define   ES_1371_TEST          (1<<16)		/* test ASIC */
+#define   ES_1373_GPIO_INT(i)	(((i)&0x0f)>>12)/* GPIO [3:0] pins - interrupt pending */
+#define   ES_1370_CSTAT		(1<<10)		/* CODEC is busy or register write in progress */
+#define   ES_1370_CBUSY         (1<<9)		/* CODEC is busy */
+#define   ES_1370_CWRIP		(1<<8)		/* CODEC register write in progress */
+#define   ES_1371_SYNC_ERR	(1<<8)		/* CODEC synchronization error occurred */
+#define   ES_1371_VC(i)         (((i)>>6)&0x03)	/* voice code from CCB module */
+#define   ES_1370_VC(i)		(((i)>>5)&0x03)	/* voice code from CCB module */
+#define   ES_1371_MPWR          (1<<5)		/* power level interrupt pending */
+#define   ES_MCCB		(1<<4)		/* CCB interrupt pending */
+#define   ES_UART		(1<<3)		/* UART interrupt pending */
+#define   ES_DAC1		(1<<2)		/* DAC1 channel interrupt pending */
+#define   ES_DAC2		(1<<1)		/* DAC2 channel interrupt pending */
+#define   ES_ADC		(1<<0)		/* ADC channel interrupt pending */
+#define ES_REG_UART_DATA 0x08	/* R/W: UART data register */
+#define ES_REG_UART_STATUS 0x09	/* R/O: UART status register */
+#define   ES_RXINT		(1<<7)		/* RX interrupt occurred */
+#define   ES_TXINT		(1<<2)		/* TX interrupt occurred */
+#define   ES_TXRDY		(1<<1)		/* transmitter ready */
+#define   ES_RXRDY		(1<<0)		/* receiver ready */
+#define ES_REG_UART_CONTROL 0x09	/* W/O: UART control register */
+#define   ES_RXINTEN		(1<<7)		/* RX interrupt enable */
+#define   ES_TXINTENO(o)	(((o)&0x03)<<5)	/* TX interrupt enable */
+#define   ES_TXINTENM		(0x03<<5)	/* mask for above */
+#define   ES_TXINTENI(i)	(((i)>>5)&0x03)
+#define   ES_CNTRL(o)		(((o)&0x03)<<0)	/* control */
+#define   ES_CNTRLM		(0x03<<0)	/* mask for above */
+#define ES_REG_UART_RES	0x0a	/* R/W: UART reserver register */
+#define   ES_TEST_MODE		(1<<0)		/* test mode enabled */
+#define ES_REG_MEM_PAGE	0x0c	/* R/W: Memory page register */
+#define   ES_MEM_PAGEO(o)	(((o)&0x0f)<<0)	/* memory page select - out */
+#define   ES_MEM_PAGEM		(0x0f<<0)	/* mask for above */
+#define   ES_MEM_PAGEI(i)	(((i)>>0)&0x0f) /* memory page select - in */
+#define ES_REG_1370_CODEC 0x10	/* W/O: Codec write register address */
+#define   ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0))
+#define ES_REG_1371_CODEC 0x14	/* W/R: Codec Read/Write register address */
+#define   ES_1371_CODEC_RDY	   (1<<31)	/* codec ready */
+#define   ES_1371_CODEC_WIP	   (1<<30)	/* codec register access in progress */
+#define   ES_1371_CODEC_PIRD	   (1<<23)	/* codec read/write select register */
+#define   ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0))
+#define   ES_1371_CODEC_READS(a)   ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD)
+#define   ES_1371_CODEC_READ(i)    (((i)>>0)&0xffff)
+
+#define ES_REG_1371_SMPRATE 0x10	/* W/R: Codec rate converter interface register */
+#define   ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)/* address of the sample rate converter */
+#define   ES_1371_SRC_RAM_ADDRM	   (0x7f<<25)	/* mask for above */
+#define   ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)/* address of the sample rate converter */
+#define   ES_1371_SRC_RAM_WE	   (1<<24)	/* R/W: read/write control for sample rate converter */
+#define   ES_1371_SRC_RAM_BUSY     (1<<23)	/* R/O: sample rate memory is busy */
+#define   ES_1371_SRC_DISABLE      (1<<22)	/* sample rate converter disable */
+#define   ES_1371_DIS_P1	   (1<<21)	/* playback channel 1 accumulator update disable */
+#define   ES_1371_DIS_P2	   (1<<20)	/* playback channel 1 accumulator update disable */
+#define   ES_1371_DIS_R1	   (1<<19)	/* capture channel accumulator update disable */
+#define   ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)/* current value of the sample rate converter */
+#define   ES_1371_SRC_RAM_DATAM	   (0xffff<<0)	/* mask for above */
+#define   ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)/* current value of the sample rate converter */
+
+#define ES_REG_1371_LEGACY 0x18	/* W/R: Legacy control/status register */
+#define   ES_1371_JFAST		(1<<31)		/* fast joystick timing */
+#define   ES_1371_HIB		(1<<30)		/* host interrupt blocking enable */
+#define   ES_1371_VSB		(1<<29)		/* SB; 0 = addr 0x220xH, 1 = 0x22FxH */
+#define   ES_1371_VMPUO(o)	(((o)&0x03)<<27)/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */
+#define   ES_1371_VMPUM		(0x03<<27)	/* mask for above */
+#define   ES_1371_VMPUI(i)	(((i)>>27)&0x03)/* base register address */
+#define   ES_1371_VCDCO(o)	(((o)&0x03)<<25)/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */
+#define   ES_1371_VCDCM		(0x03<<25)	/* mask for above */
+#define   ES_1371_VCDCI(i)	(((i)>>25)&0x03)/* CODEC address */
+#define   ES_1371_FIRQ		(1<<24)		/* force an interrupt */
+#define   ES_1371_SDMACAP	(1<<23)		/* enable event capture for slave DMA controller */
+#define   ES_1371_SPICAP	(1<<22)		/* enable event capture for slave IRQ controller */
+#define   ES_1371_MDMACAP	(1<<21)		/* enable event capture for master DMA controller */
+#define   ES_1371_MPICAP	(1<<20)		/* enable event capture for master IRQ controller */
+#define   ES_1371_ADCAP		(1<<19)		/* enable event capture for ADLIB register; 0x388xH */
+#define   ES_1371_SVCAP		(1<<18)		/* enable event capture for SB registers */
+#define   ES_1371_CDCCAP	(1<<17)		/* enable event capture for CODEC registers */
+#define   ES_1371_BACAP		(1<<16)		/* enable event capture for SoundScape base address */
+#define   ES_1371_EXI(i)	(((i)>>8)&0x07)	/* event number */
+#define   ES_1371_AI(i)		(((i)>>3)&0x1f)	/* event significant I/O address */
+#define   ES_1371_WR		(1<<2)	/* event capture; 0 = read; 1 = write */
+#define   ES_1371_LEGINT	(1<<0)	/* interrupt for legacy events; 0 = interrupt did occur */
+
+#define ES_REG_CHANNEL_STATUS 0x1c /* R/W: first 32-bits from S/PDIF channel status block, es1373 */
+
+#define ES_REG_SERIAL	0x20	/* R/W: Serial interface control register */
+#define   ES_1371_DAC_TEST	(1<<22)		/* DAC test mode enable */
+#define   ES_P2_END_INCO(o)	(((o)&0x07)<<19)/* binary offset value to increment / loop end */
+#define   ES_P2_END_INCM	(0x07<<19)	/* mask for above */
+#define   ES_P2_END_INCI(i)	(((i)>>16)&0x07)/* binary offset value to increment / loop end */
+#define   ES_P2_ST_INCO(o)	(((o)&0x07)<<16)/* binary offset value to increment / start */
+#define   ES_P2_ST_INCM		(0x07<<16)	/* mask for above */
+#define   ES_P2_ST_INCI(i)	(((i)<<16)&0x07)/* binary offset value to increment / start */
+#define   ES_R1_LOOP_SEL	(1<<15)		/* ADC; 0 - loop mode; 1 = stop mode */
+#define   ES_P2_LOOP_SEL	(1<<14)		/* DAC2; 0 - loop mode; 1 = stop mode */
+#define   ES_P1_LOOP_SEL	(1<<13)		/* DAC1; 0 - loop mode; 1 = stop mode */
+#define   ES_P2_PAUSE		(1<<12)		/* DAC2; 0 - play mode; 1 = pause mode */
+#define   ES_P1_PAUSE		(1<<11)		/* DAC1; 0 - play mode; 1 = pause mode */
+#define   ES_R1_INT_EN		(1<<10)		/* ADC interrupt enable */
+#define   ES_P2_INT_EN		(1<<9)		/* DAC2 interrupt enable */
+#define   ES_P1_INT_EN		(1<<8)		/* DAC1 interrupt enable */
+#define   ES_P1_SCT_RLD		(1<<7)		/* force sample counter reload for DAC1 */
+#define   ES_P2_DAC_SEN		(1<<6)		/* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */
+#define   ES_R1_MODEO(o)	(((o)&0x03)<<4)	/* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */
+#define   ES_R1_MODEM		(0x03<<4)	/* mask for above */
+#define   ES_R1_MODEI(i)	(((i)>>4)&0x03)
+#define   ES_P2_MODEO(o)	(((o)&0x03)<<2)	/* DAC2 mode; -- '' -- */
+#define   ES_P2_MODEM		(0x03<<2)	/* mask for above */
+#define   ES_P2_MODEI(i)	(((i)>>2)&0x03)
+#define   ES_P1_MODEO(o)	(((o)&0x03)<<0)	/* DAC1 mode; -- '' -- */
+#define   ES_P1_MODEM		(0x03<<0)	/* mask for above */
+#define   ES_P1_MODEI(i)	(((i)>>0)&0x03)
+
+#define ES_REG_DAC1_COUNT 0x24	/* R/W: DAC1 sample count register */
+#define ES_REG_DAC2_COUNT 0x28	/* R/W: DAC2 sample count register */
+#define ES_REG_ADC_COUNT  0x2c	/* R/W: ADC sample count register */
+#define   ES_REG_CURR_COUNT(i)  (((i)>>16)&0xffff)
+#define   ES_REG_COUNTO(o)	(((o)&0xffff)<<0)
+#define   ES_REG_COUNTM		(0xffff<<0)
+#define   ES_REG_COUNTI(i)	(((i)>>0)&0xffff)
+
+#define ES_REG_DAC1_FRAME 0x30	/* R/W: PAGE 0x0c; DAC1 frame address */
+#define ES_REG_DAC1_SIZE  0x34	/* R/W: PAGE 0x0c; DAC1 frame size */
+#define ES_REG_DAC2_FRAME 0x38	/* R/W: PAGE 0x0c; DAC2 frame address */
+#define ES_REG_DAC2_SIZE  0x3c	/* R/W: PAGE 0x0c; DAC2 frame size */
+#define ES_REG_ADC_FRAME  0x30	/* R/W: PAGE 0x0d; ADC frame address */
+#define ES_REG_ADC_SIZE	  0x34	/* R/W: PAGE 0x0d; ADC frame size */
+#define   ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16)
+#define   ES_REG_FCURR_COUNTM    (0xffff<<16)
+#define   ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc)
+#define   ES_REG_FSIZEO(o)	 (((o)&0xffff)<<0)
+#define   ES_REG_FSIZEM		 (0xffff<<0)
+#define   ES_REG_FSIZEI(i)	 (((i)>>0)&0xffff)
+#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */
+#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */
+
+#define ES_REG_UART_FIFO  0x30	/* R/W: PAGE 0x0e; UART FIFO register */
+#define   ES_REG_UF_VALID	 (1<<8)
+#define   ES_REG_UF_BYTEO(o)	 (((o)&0xff)<<0)
+#define   ES_REG_UF_BYTEM	 (0xff<<0)
+#define   ES_REG_UF_BYTEI(i)	 (((i)>>0)&0xff)
+
+
+/*
+ *  Pages
+ */
+
+#define ES_PAGE_DAC	0x0c
+#define ES_PAGE_ADC	0x0d
+#define ES_PAGE_UART	0x0e
+#define ES_PAGE_UART1	0x0f
+
+/*
+ *  Sample rate converter addresses
+ */
+
+#define ES_SMPREG_DAC1		0x70
+#define ES_SMPREG_DAC2		0x74
+#define ES_SMPREG_ADC		0x78
+#define ES_SMPREG_VOL_ADC	0x6c
+#define ES_SMPREG_VOL_DAC1	0x7c
+#define ES_SMPREG_VOL_DAC2	0x7e
+#define ES_SMPREG_TRUNC_N	0x00
+#define ES_SMPREG_INT_REGS	0x01
+#define ES_SMPREG_ACCUM_FRAC	0x02
+#define ES_SMPREG_VFREQ_FRAC	0x03
+
+/*
+ *  Some contants
+ */
+
+#define ES_1370_SRCLOCK	   1411200
+#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2)
+
+/*
+ *  Open modes
+ */
+
+#define ES_MODE_PLAY1	0x0001
+#define ES_MODE_PLAY2	0x0002
+#define ES_MODE_CAPTURE	0x0004
+
+#define ES_MODE_OUTPUT	0x0001	/* for MIDI */
+#define ES_MODE_INPUT	0x0002	/* for MIDI */
+
+/*
+
+ */
+
+struct ensoniq {
+	spinlock_t reg_lock;
+	struct mutex src_mutex;
+
+	int irq;
+
+	unsigned long playback1size;
+	unsigned long playback2size;
+	unsigned long capture3size;
+
+	unsigned long port;
+	unsigned int mode;
+	unsigned int uartm;	/* UART mode */
+
+	unsigned int ctrl;	/* control register */
+	unsigned int sctrl;	/* serial control register */
+	unsigned int cssr;	/* control status register */
+	unsigned int uartc;	/* uart control register */
+	unsigned int rev;	/* chip revision */
+
+	union {
+#ifdef CHIP1371
+		struct {
+			struct snd_ac97 *ac97;
+		} es1371;
+#else
+		struct {
+			int pclkdiv_lock;
+			struct snd_ak4531 *ak4531;
+		} es1370;
+#endif
+	} u;
+
+	struct pci_dev *pci;
+	struct snd_card *card;
+	struct snd_pcm *pcm1;	/* DAC1/ADC PCM */
+	struct snd_pcm *pcm2;	/* DAC2 PCM */
+	struct snd_pcm_substream *playback1_substream;
+	struct snd_pcm_substream *playback2_substream;
+	struct snd_pcm_substream *capture_substream;
+	unsigned int p1_dma_size;
+	unsigned int p2_dma_size;
+	unsigned int c_dma_size;
+	unsigned int p1_period_size;
+	unsigned int p2_period_size;
+	unsigned int c_period_size;
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *midi_input;
+	struct snd_rawmidi_substream *midi_output;
+
+	unsigned int spdif;
+	unsigned int spdif_default;
+	unsigned int spdif_stream;
+
+#ifdef CHIP1370
+	struct snd_dma_buffer dma_bug;
+#endif
+
+#ifdef SUPPORT_JOYSTICK
+	struct gameport *gameport;
+#endif
+};
+
+static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id);
+
+static struct pci_device_id snd_audiopci_ids[] = {
+#ifdef CHIP1370
+	{ 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1370 */
+#endif
+#ifdef CHIP1371
+	{ 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1371 */
+	{ 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* ES1373 - CT5880 */
+	{ 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* Ectiva EV1938 */
+#endif
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_audiopci_ids);
+
+/*
+ *  constants
+ */
+
+#define POLL_COUNT	0xa000
+
+#ifdef CHIP1370
+static unsigned int snd_es1370_fixed_rates[] =
+	{5512, 11025, 22050, 44100};
+static struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = {
+	.count = 4, 
+	.list = snd_es1370_fixed_rates,
+	.mask = 0,
+};
+static struct snd_ratnum es1370_clock = {
+	.num = ES_1370_SRCLOCK,
+	.den_min = 29, 
+	.den_max = 353,
+	.den_step = 1,
+};
+static struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = {
+	.nrats = 1,
+	.rats = &es1370_clock,
+};
+#else
+static struct snd_ratden es1371_dac_clock = {
+	.num_min = 3000 * (1 << 15),
+	.num_max = 48000 * (1 << 15),
+	.num_step = 3000,
+	.den = 1 << 15,
+};
+static struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = {
+	.nrats = 1,
+	.rats = &es1371_dac_clock,
+};
+static struct snd_ratnum es1371_adc_clock = {
+	.num = 48000 << 15,
+	.den_min = 32768, 
+	.den_max = 393216,
+	.den_step = 1,
+};
+static struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = {
+	.nrats = 1,
+	.rats = &es1371_adc_clock,
+};
+#endif
+static const unsigned int snd_ensoniq_sample_shift[] =
+	{0, 1, 1, 2};
+
+/*
+ *  common I/O routines
+ */
+
+#ifdef CHIP1371
+
+static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq)
+{
+	unsigned int t, r = 0;
+
+	for (t = 0; t < POLL_COUNT; t++) {
+		r = inl(ES_REG(ensoniq, 1371_SMPRATE));
+		if ((r & ES_1371_SRC_RAM_BUSY) == 0)
+			return r;
+		cond_resched();
+	}
+	snd_printk(KERN_ERR "wait source ready timeout 0x%lx [0x%x]\n",
+		   ES_REG(ensoniq, 1371_SMPRATE), r);
+	return 0;
+}
+
+static unsigned int snd_es1371_src_read(struct ensoniq * ensoniq, unsigned short reg)
+{
+	unsigned int temp, i, orig, r;
+
+	/* wait for ready */
+	temp = orig = snd_es1371_wait_src_ready(ensoniq);
+
+	/* expose the SRC state bits */
+	r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+		    ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+
+	/* now, wait for busy and the correct time to read */
+	temp = snd_es1371_wait_src_ready(ensoniq);
+	
+	if ((temp & 0x00870000) != 0x00010000) {
+		/* wait for the right state */
+		for (i = 0; i < POLL_COUNT; i++) {
+			temp = inl(ES_REG(ensoniq, 1371_SMPRATE));
+			if ((temp & 0x00870000) == 0x00010000)
+				break;
+		}
+	}
+
+	/* hide the state bits */	
+	r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+		   ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg);
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	
+	return temp;
+}
+
+static void snd_es1371_src_write(struct ensoniq * ensoniq,
+				 unsigned short reg, unsigned short data)
+{
+	unsigned int r;
+
+	r = snd_es1371_wait_src_ready(ensoniq) &
+	    (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+	     ES_1371_DIS_P2 | ES_1371_DIS_R1);
+	r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data);
+	outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE));
+}
+
+#endif /* CHIP1371 */
+
+#ifdef CHIP1370
+
+static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
+				   unsigned short reg, unsigned short val)
+{
+	struct ensoniq *ensoniq = ak4531->private_data;
+	unsigned long end_time = jiffies + HZ / 10;
+
+#if 0
+	printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n",
+	       reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+#endif
+	do {
+		if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
+			outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
+			return;
+		}
+		schedule_timeout_uninterruptible(1);
+	} while (time_after(end_time, jiffies));
+	snd_printk(KERN_ERR "codec write timeout, status = 0x%x\n",
+		   inl(ES_REG(ensoniq, STATUS)));
+}
+
+#endif /* CHIP1370 */
+
+#ifdef CHIP1371
+
+static void snd_es1371_codec_write(struct snd_ac97 *ac97,
+				   unsigned short reg, unsigned short val)
+{
+	struct ensoniq *ensoniq = ac97->private_data;
+	unsigned int t, x;
+
+	mutex_lock(&ensoniq->src_mutex);
+	for (t = 0; t < POLL_COUNT; t++) {
+		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+			/* save the current state for latter */
+			x = snd_es1371_wait_src_ready(ensoniq);
+			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+			     ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for not busy (state 0) first to avoid
+			   transition states */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00000000)
+					break;
+			}
+			/* wait for a SAFE time to write addr/data and then do it, dammit */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00010000)
+					break;
+			}
+			outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
+			/* restore SRC reg */
+			snd_es1371_wait_src_ready(ensoniq);
+			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+			mutex_unlock(&ensoniq->src_mutex);
+			return;
+		}
+	}
+	mutex_unlock(&ensoniq->src_mutex);
+	snd_printk(KERN_ERR "codec write timeout at 0x%lx [0x%x]\n",
+		   ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+}
+
+static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
+					    unsigned short reg)
+{
+	struct ensoniq *ensoniq = ac97->private_data;
+	unsigned int t, x, fail = 0;
+
+      __again:
+	mutex_lock(&ensoniq->src_mutex);
+	for (t = 0; t < POLL_COUNT; t++) {
+		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
+			/* save the current state for latter */
+			x = snd_es1371_wait_src_ready(ensoniq);
+			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
+			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
+			     ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for not busy (state 0) first to avoid
+			   transition states */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00000000)
+					break;
+			}
+			/* wait for a SAFE time to write addr/data and then do it, dammit */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) ==
+				    0x00010000)
+					break;
+			}
+			outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));
+			/* restore SRC reg */
+			snd_es1371_wait_src_ready(ensoniq);
+			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
+			/* wait for WIP again */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP))
+					break;		
+			}
+			/* now wait for the stinkin' data (RDY) */
+			for (t = 0; t < POLL_COUNT; t++) {
+				if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
+					mutex_unlock(&ensoniq->src_mutex);
+					return ES_1371_CODEC_READ(x);
+				}
+			}
+			mutex_unlock(&ensoniq->src_mutex);
+			if (++fail > 10) {
+				snd_printk(KERN_ERR "codec read timeout (final) "
+					   "at 0x%lx, reg = 0x%x [0x%x]\n",
+					   ES_REG(ensoniq, 1371_CODEC), reg,
+					   inl(ES_REG(ensoniq, 1371_CODEC)));
+				return 0;
+			}
+			goto __again;
+		}
+	}
+	mutex_unlock(&ensoniq->src_mutex);
+	snd_printk(KERN_ERR "es1371: codec read timeout at 0x%lx [0x%x]\n",
+		   ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
+	return 0;
+}
+
+static void snd_es1371_codec_wait(struct snd_ac97 *ac97)
+{
+	msleep(750);
+	snd_es1371_codec_read(ac97, AC97_RESET);
+	snd_es1371_codec_read(ac97, AC97_VENDOR_ID1);
+	snd_es1371_codec_read(ac97, AC97_VENDOR_ID2);
+	msleep(50);
+}
+
+static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
+{
+	unsigned int n, truncm, freq, result;
+
+	mutex_lock(&ensoniq->src_mutex);
+	n = rate / 3000;
+	if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+		n--;
+	truncm = (21 * n - 1) | 1;
+	freq = ((48000UL << 15) / rate) * n;
+	result = (48000UL << 15) / (freq / n);
+	if (rate >= 24000) {
+		if (truncm > 239)
+			truncm = 239;
+		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+				(((239 - truncm) >> 1) << 9) | (n << 4));
+	} else {
+		if (truncm > 119)
+			truncm = 119;
+		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
+				0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+	}
+	snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC +
+						  ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
+	snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
+	mutex_unlock(&ensoniq->src_mutex);
+}
+
+static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate)
+{
+	unsigned int freq, r;
+
+	mutex_lock(&ensoniq->src_mutex);
+	freq = ((rate << 15) + 1500) / 3000;
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P2 | ES_1371_DIS_R1)) |
+		ES_1371_DIS_P1;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 +
+						  ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P2 | ES_1371_DIS_R1));
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	mutex_unlock(&ensoniq->src_mutex);
+}
+
+static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate)
+{
+	unsigned int freq, r;
+
+	mutex_lock(&ensoniq->src_mutex);
+	freq = ((rate << 15) + 1500) / 3000;
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P1 | ES_1371_DIS_R1)) |
+		ES_1371_DIS_P2;
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
+			     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 +
+						  ES_SMPREG_INT_REGS) & 0x00ff) |
+			     ((freq >> 5) & 0xfc00));
+	snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC,
+			     freq & 0x7fff);
+	r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
+						   ES_1371_DIS_P1 | ES_1371_DIS_R1));
+	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
+	mutex_unlock(&ensoniq->src_mutex);
+}
+
+#endif /*