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-21 21:47:22.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/ALSA-Configuration.txt	2007-07-22 17:32:54.000000000 +0200
@@ -467,7 +467,12 @@
     above explicitly.
 
     The power-management is supported.
-    
+
+  Module snd-cs5530
+  _________________
+
+    Module for Cyrix/NatSemi Geode 5530 chip. 
+  
   Module snd-cs5535audio
   ----------------------
 
@@ -759,6 +764,7 @@
 
     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)
@@ -803,6 +809,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 +819,31 @@
 	  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
+	  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
+	  imac24	iMac 24'' with jack detection
 	  w2jc		ASUS W2JC
 	  auto		auto-config reading BIOS (default)
 
@@ -832,9 +855,15 @@
 	  6stack-dig-demo  6-jack digital for Intel demo board
 	  acer		Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
 	  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
+	  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 +882,9 @@
 	  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
 	  auto		auto-config reading BIOS (default)
 
 	CMI9880
@@ -864,12 +895,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
@@ -907,11 +952,18 @@
 	  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
+	  dell		Dell XPS M1210
+	  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)
 
 	STAC9202/9250/9251
 	  ref		Reference board, base config
@@ -956,6 +1008,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
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/Audiophile-Usb.txt	2007-07-22 17:32:55.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/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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/Documentation/sound/alsa/OSS-Emulation.txt	2007-07-22 17:32:55.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
 ====================
 
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/ak4xxx-adda.h	2007-07-22 17:33:22.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/cs46xx.h linux-2.6.22-alsa/include/sound/cs46xx.h
--- linux-2.6.22/include/sound/cs46xx.h	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/cs46xx.h	2007-07-22 17:33:22.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/cs46xx_dsp_spos.h	2007-07-22 17:33:22.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/emu10k1.h	2007-07-22 17:33:22.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 */
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/include/sound/sb.h	2007-07-22 17:33:22.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/sound/Kconfig linux-2.6.22-alsa/sound/Kconfig
--- linux-2.6.22/sound/Kconfig	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/Kconfig	2007-07-22 17:32:18.000000000 +0200
@@ -65,6 +65,8 @@
 
 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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/Makefile	2007-07-22 17:32:18.000000000 +0200
@@ -5,7 +5,7 @@
 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/ 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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/aoa/codecs/snd-aoa-codec-onyx.c	2007-07-22 17:32:18.000000000 +0200
@@ -661,7 +661,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 +713,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/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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/pcm_native.c	2007-07-22 17:32:19.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 */
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/seq_instr.c	2007-07-22 17:32:19.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_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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/seq/seq_virmidi.c	2007-07-22 17:32:19.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/sound.c	2007-07-22 17:32:19.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/core/timer.c	2007-07-22 17:32:19.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/dummy.c	2007-07-22 17:32:19.000000000 +0200
@@ -659,7 +659,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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/mpu401/mpu401.c	2007-07-22 17:32:20.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/portman2x4.c linux-2.6.22-alsa/sound/drivers/portman2x4.c
--- linux-2.6.22/sound/drivers/portman2x4.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/portman2x4.c	2007-07-22 17:32:20.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/serial-u16550.c	2007-07-22 17:32:19.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/drivers/virmidi.c	2007-07-22 17:32:19.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/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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/i2c/other/ak4xxx-adda.c	2007-07-22 17:32:20.000000000 +0200
@@ -481,8 +481,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 +585,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/isa/Kconfig linux-2.6.22-alsa/sound/isa/Kconfig
--- linux-2.6.22/sound/isa/Kconfig	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/Kconfig	2007-07-22 17:32:21.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).
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/ad1848/ad1848_lib.c	2007-07-22 17:32:20.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/opl3sa2.c linux-2.6.22-alsa/sound/isa/opl3sa2.c
--- linux-2.6.22/sound/isa/opl3sa2.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/opl3sa2.c	2007-07-22 17:32:21.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 */
 };
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/opti9xx/opti92x-ad1848.c	2007-07-22 17:32:20.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/Makefile	2007-07-22 17:32:20.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_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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb16_main.c	2007-07-22 17:32:20.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb_common.c	2007-07-22 17:32:20.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;
 	}
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sb/sb_mixer.c	2007-07-22 17:32:20.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/sscape.c	2007-07-22 17:32:21.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/isa/wavefront/wavefront_synth.c	2007-07-22 17:32:20.000000000 +0200
@@ -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;
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/oss/Makefile	2007-07-22 17:32:21.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/Kconfig	2007-07-22 17:32:25.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.
@@ -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
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/Makefile	2007-07-22 17:32:24.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/ali5451/ali5451.c linux-2.6.22-alsa/sound/pci/ali5451/ali5451.c
--- linux-2.6.22/sound/pci/ali5451/ali5451.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ali5451/ali5451.c	2007-07-22 17:39:47.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;
@@ -2343,7 +2342,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-07-22 17:32:22.000000000 +0200
@@ -0,0 +1,2389 @@
+/*
+ *  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}
+
+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;
+}
+
+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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/als300.c	2007-07-22 17:32:24.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/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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ca0106/ca0106_main.c	2007-07-22 17:39:47.000000000 +0200
@@ -168,6 +168,25 @@
 #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. */
+	 /*
+ 	  * 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]",
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-07-22 17:32:24.000000000 +0200
@@ -0,0 +1,1683 @@
+/*
+ *  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
+ *
+ *  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.
+ *
+ *  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. */
+	 /*
+ 	  * 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),
+	.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);
+}
+
+/* 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;
+	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;
+	/* 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) {
+		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;
+}
+
+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,
+};
+
+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++)
+			snd_ca0106_spi_write(chip, 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/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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/cs46xx_lib.c	2007-07-22 17:32:22.000000000 +0200
@@ -2897,6 +2897,10 @@
 	}
 #endif
 	
+#ifdef CONFIG_PM
+	kfree(chip->saved_regs);
+#endif
+
 	pci_disable_device(chip->pci);
 	kfree(chip);
 	return 0;
@@ -3140,6 +3144,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 +3235,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 +3674,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 +3697,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 +3719,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 +3737,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 +3761,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 +3934,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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/cs46xx_lib.h	2007-07-22 17:32:22.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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/cs46xx/dsp_spos.c	2007-07-22 17:32:22.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/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-07-22 17:32:21.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/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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emu10k1_main.c	2007-07-22 17:39:47.000000000 +0200
@@ -51,9 +51,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 +666,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 +702,37 @@
 	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 +766,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 +774,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;
@@ -850,6 +902,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 */
@@ -943,16 +1016,27 @@
 		/* 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;
+		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 != 0x55) {
+		if ((reg & 0x3f) != 0x15) {
 			/* FPGA failed to be programmed */
 			snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
 			return 0;
@@ -1227,9 +1311,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",
@@ -1665,12 +1755,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 +1907,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-07-22 17:32:24.000000000 +0200
@@ -0,0 +1,1942 @@
+/*
+ *  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 <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;
+}
+
+/*
+ * 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 ); 
+	/* ADAT input. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 );
+	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) */
+
+	/* 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 (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 & 0x3f) != 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);
+	}
+#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 );
+	}
+	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 = (void **)vmalloc(emu->max_cache_pages * sizeof(void*));
+	emu->page_addr_table = (unsigned long*)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/emufx.c linux-2.6.22-alsa/sound/pci/emu10k1/emufx.c
--- linux-2.6.22/sound/pci/emu10k1/emufx.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emufx.c	2007-07-22 17:32:24.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,7 +1198,11 @@
 	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);
@@ -1549,7 +1558,7 @@
 
 	if (emu->card_capabilities->emu1010) {
 		snd_printk("EMU inputs on\n");
-		/* Capture 8 channels of S32_LE sound */
+		/* 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. */
@@ -1560,6 +1569,11 @@
 		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;
@@ -1583,6 +1597,66 @@
 		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++) {
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emumixer.c	2007-07-22 17:32: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,
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-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/emu10k1/emupcm.c	2007-07-22 17:32: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/ens1370.c linux-2.6.22-alsa/sound/pci/ens1370.c
--- linux-2.6.22/sound/pci/ens1370.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ens1370.c	2007-07-22 17:39:47.000000000 +0200
@@ -1607,8 +1607,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 &&
diff -ruN linux-2.6.22/sound/pci/hda/hda_intel.c linux-2.6.22-alsa/sound/pci/hda/hda_intel.c
--- linux-2.6.22/sound/pci/hda/hda_intel.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/hda_intel.c	2007-07-22 17:32:23.000000000 +0200
@@ -341,6 +341,9 @@
 	unsigned int single_cmd :1;
 	unsigned int polling_mode :1;
 	unsigned int msi :1;
+
+	/* for debugging */
+	unsigned int last_cmd;	/* last issued command (to sync) */
 };
 
 /* driver types */
@@ -466,18 +469,10 @@
 }
 
 /* send a command */
-static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
-			     unsigned int verb, unsigned int para)
+static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
 {
 	struct azx *chip = codec->bus->private_data;
 	unsigned int wp;
-	u32 val;
-
-	val = (u32)(codec->addr & 0x0f) << 28;
-	val |= (u32)direct << 27;
-	val |= (u32)nid << 20;
-	val |= verb << 8;
-	val |= para;
 
 	/* add command to corb */
 	wp = azx_readb(chip, CORBWP);
@@ -538,12 +533,12 @@
 		}
 		if (! chip->rirb.cmds)
 			return chip->rirb.res; /* the last value */
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 	} while (time_after_eq(timeout, jiffies));
 
 	if (chip->msi) {
 		snd_printk(KERN_WARNING "hda_intel: No response from codec, "
-			   "disabling MSI...\n");
+			   "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
 		free_irq(chip->irq, chip);
 		chip->irq = -1;
 		pci_disable_msi(chip->pci);
@@ -555,13 +550,15 @@
 
 	if (!chip->polling_mode) {
 		snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
-			   "switching to polling mode...\n");
+			   "switching to polling mode: last cmd=0x%08x\n",
+			   chip->last_cmd);
 		chip->polling_mode = 1;
 		goto again;
 	}
 
 	snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
-		   "switching to single_cmd mode...\n");
+		   "switching to single_cmd mode: last cmd=0x%08x\n",
+		   chip->last_cmd);
 	chip->rirb.rp = azx_readb(chip, RIRBWP);
 	chip->rirb.cmds = 0;
 	/* switch to single_cmd mode */
@@ -581,20 +578,11 @@
  */
 
 /* send a command */
-static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid,
-			       int direct, unsigned int verb,
-			       unsigned int para)
+static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
 {
 	struct azx *chip = codec->bus->private_data;
-	u32 val;
 	int timeout = 50;
 
-	val = (u32)(codec->addr & 0x0f) << 28;
-	val |= (u32)direct << 27;
-	val |= (u32)nid << 20;
-	val |= verb << 8;
-	val |= para;
-
 	while (timeout--) {
 		/* check ICB busy bit */
 		if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
@@ -639,10 +627,19 @@
 			unsigned int para)
 {
 	struct azx *chip = codec->bus->private_data;
+	u32 val;
+
+	val = (u32)(codec->addr & 0x0f) << 28;
+	val |= (u32)direct << 27;
+	val |= (u32)nid << 20;
+	val |= verb << 8;
+	val |= para;
+	chip->last_cmd = val;
+
 	if (chip->single_cmd)
-		return azx_single_send_cmd(codec, nid, direct, verb, para);
+		return azx_single_send_cmd(codec, val);
 	else
-		return azx_corb_send_cmd(codec, nid, direct, verb, para);
+		return azx_corb_send_cmd(codec, val);
 }
 
 /* get a response */
@@ -1788,6 +1785,12 @@
 	{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
 	{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
 	{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
+	{ 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
+	{ 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
+	{ 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+	{ 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+	{ 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+	{ 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
diff -ruN linux-2.6.22/sound/pci/hda/hda_proc.c linux-2.6.22-alsa/sound/pci/hda/hda_proc.c
--- linux-2.6.22/sound/pci/hda/hda_proc.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/hda_proc.c	2007-07-22 17:32:23.000000000 +0200
@@ -250,6 +250,12 @@
 	snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
 	snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
 	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
+
+	if (codec->mfg)
+		snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
+	else
+		snd_iprintf(buffer, "No Modem Function Group found\n");
+
 	if (! codec->afg)
 		return;
 	snd_iprintf(buffer, "Default PCM:\n");
diff -ruN linux-2.6.22/sound/pci/hda/patch_analog.c linux-2.6.22-alsa/sound/pci/hda/patch_analog.c
--- linux-2.6.22/sound/pci/hda/patch_analog.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/patch_analog.c	2007-07-22 17:32:23.000000000 +0200
@@ -1,7 +1,8 @@
 /*
- * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988
+ * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
+ *   AD1986A, AD1988
  *
- * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
+ * Copyright (c) 2005-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
@@ -61,7 +62,7 @@
 	int num_channel_mode;
 
 	/* PCM information */
-	struct hda_pcm pcm_rec[2];	/* used in alc_build_pcms() */
+	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
 
 	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	unsigned int spdif_route;
@@ -2775,11 +2776,634 @@
 
 
 /*
+ * AD1884 / AD1984
+ *
+ * port-B - front line/mic-in
+ * port-E - aux in/out
+ * port-F - aux in/out
+ * port-C - rear line/mic-in
+ * port-D - rear line/hp-out
+ * port-A - front line/hp-out
+ *
+ * AD1984 = AD1884 + two digital mic-ins
+ *
+ * FIXME:
+ * For simplicity, we share the single DAC for both HP and line-outs
+ * right now.  The inidividual playbacks could be easily implemented,
+ * but no build-up framework is given, so far.
+ */
+
+static hda_nid_t ad1884_dac_nids[1] = {
+	0x04,
+};
+
+static hda_nid_t ad1884_adc_nids[2] = {
+	0x08, 0x09,
+};
+
+static hda_nid_t ad1884_capsrc_nids[2] = {
+	0x0c, 0x0d,
+};
+
+#define AD1884_SPDIF_OUT	0x02
+
+static struct hda_input_mux ad1884_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Front Mic", 0x0 },
+		{ "Mic", 0x1 },
+		{ "CD", 0x2 },
+		{ "Mix", 0x3 },
+	},
+};
+
+static struct snd_kcontrol_new ad1884_base_mixers[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
+	/*
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+	*/
+	HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* SPDIF controls */
+	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		/* identical with ad1983 */
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
+	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
+			     HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
+			   HDA_INPUT),
+	{ } /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1884_init_verbs[] = {
+	/* DACs; mute as default */
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-A (HP) mixer */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-A pin */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* HP selector - select DAC2 */
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* Port-D (Line-out) mixer */
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-D pin */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mono-out mixer */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Mono-out pin */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mono selector */
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* Port-B (front mic) pin */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Port-C (rear mic) pin */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog mixer; mute as default */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
+	/* SPDIF output selector */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{ } /* end */
+};
+
+static int patch_ad1884(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
+	spec->multiout.dac_nids = ad1884_dac_nids;
+	spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
+	spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
+	spec->adc_nids = ad1884_adc_nids;
+	spec->capsrc_nids = ad1884_capsrc_nids;
+	spec->input_mux = &ad1884_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1884_base_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1884_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	return 0;
+}
+
+/*
+ * Lenovo Thinkpad T61/X61
+ */
+static struct hda_input_mux ad1984_thinkpad_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Internal Mic", 0x1 },
+		{ "Mix", 0x3 },
+	},
+};
+
+static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+/* additional verbs */
+static struct hda_verb ad1984_thinkpad_init_verbs[] = {
+	/* Port-E (docking station mic) pin */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* docking mic boost */
+	{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog mixer - docking mic; mute as default */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* enable EAPD bit */
+	{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+	{ } /* end */
+};
+
+/* Digial MIC ADC NID 0x05 + 0x06 */
+static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   unsigned int stream_tag,
+				   unsigned int format,
+				   struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
+				   0, 0, 0);
+	return 0;
+}
+
+static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x05,
+	.ops = {
+		.prepare = ad1984_pcm_dmic_prepare,
+		.cleanup = ad1984_pcm_dmic_cleanup
+	},
+};
+
+static int ad1984_build_pcms(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	struct hda_pcm *info;
+	int err;
+
+	err = ad198x_build_pcms(codec);
+	if (err < 0)
+		return err;
+
+	info = spec->pcm_rec + codec->num_pcms;
+	codec->num_pcms++;
+	info->name = "AD1984 Digital Mic";
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
+	return 0;
+}
+
+/* models */
+enum {
+	AD1984_BASIC,
+	AD1984_THINKPAD,
+	AD1984_MODELS
+};
+
+static const char *ad1984_models[AD1984_MODELS] = {
+	[AD1984_BASIC]		= "basic",
+	[AD1984_THINKPAD]	= "thinkpad",
+};
+
+static struct snd_pci_quirk ad1984_cfg_tbl[] = {
+	/* Lenovo Thinkpad T61/X61 */
+	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+	{}
+};
+
+static int patch_ad1984(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config, err;
+
+	err = patch_ad1884(codec);
+	if (err < 0)
+		return err;
+	spec = codec->spec;
+	board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
+						  ad1984_models, ad1984_cfg_tbl);
+	switch (board_config) {
+	case AD1984_BASIC:
+		/* additional digital mics */
+		spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
+		codec->patch_ops.build_pcms = ad1984_build_pcms;
+		break;
+	case AD1984_THINKPAD:
+		spec->multiout.dig_out_nid = 0;
+		spec->input_mux = &ad1984_thinkpad_capture_source;
+		spec->mixers[0] = ad1984_thinkpad_mixers;
+		spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
+		break;
+	}
+	return 0;
+}
+
+
+/*
+ * AD1882
+ *
+ * port-A - front hp-out
+ * port-B - front mic-in
+ * port-C - rear line-in, shared surr-out (3stack)
+ * port-D - rear line-out
+ * port-E - rear mic-in, shared clfe-out (3stack)
+ * port-F - rear surr-out (6stack)
+ * port-G - rear clfe-out (6stack)
+ */
+
+static hda_nid_t ad1882_dac_nids[3] = {
+	0x04, 0x03, 0x05
+};
+
+static hda_nid_t ad1882_adc_nids[2] = {
+	0x08, 0x09,
+};
+
+static hda_nid_t ad1882_capsrc_nids[2] = {
+	0x0c, 0x0d,
+};
+
+#define AD1882_SPDIF_OUT	0x02
+
+/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
+static struct hda_input_mux ad1882_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Front Mic", 0x1 },
+		{ "Mic", 0x4 },
+		{ "Line", 0x2 },
+		{ "CD", 0x3 },
+		{ "Mix", 0x7 },
+	},
+};
+
+static struct snd_kcontrol_new ad1882_base_mixers[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* SPDIF controls */
+	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		/* identical with ad1983 */
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = ad198x_ch_mode_info,
+		.get = ad198x_ch_mode_get,
+		.put = ad198x_ch_mode_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+static struct hda_verb ad1882_ch2_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{ } /* end */
+};
+
+static struct hda_verb ad1882_ch4_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{ } /* end */
+};
+
+static struct hda_verb ad1882_ch6_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{ } /* end */
+};
+
+static struct hda_channel_mode ad1882_modes[3] = {
+	{ 2, ad1882_ch2_init },
+	{ 4, ad1882_ch4_init },
+	{ 6, ad1882_ch6_init },
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1882_init_verbs[] = {
+	/* DACs; mute as default */
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-A (HP) mixer */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-A pin */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* HP selector - select DAC2 */
+	{0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* Port-D (Line-out) mixer */
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-D pin */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mono-out mixer */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Mono-out pin */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Port-B (front mic) pin */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+	/* Port-C (line-in) pin */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+	/* Port-C mixer - mute as input */
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Port-E (mic-in) pin */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+	/* Port-E mixer - mute as input */
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Port-F (surround) */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Port-G (CLFE) */
+	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog mixer; mute as default */
+	/* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
+	/* SPDIF output selector */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{ } /* end */
+};
+
+/* models */
+enum {
+	AD1882_3STACK,
+	AD1882_6STACK,
+	AD1882_MODELS
+};
+
+static const char *ad1882_models[AD1986A_MODELS] = {
+	[AD1882_3STACK]		= "3stack",
+	[AD1882_6STACK]		= "6stack",
+};
+
+
+static int patch_ad1882(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 6;
+	spec->multiout.num_dacs = 3;
+	spec->multiout.dac_nids = ad1882_dac_nids;
+	spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
+	spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
+	spec->adc_nids = ad1882_adc_nids;
+	spec->capsrc_nids = ad1882_capsrc_nids;
+	spec->input_mux = &ad1882_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1882_base_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1882_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	/* override some parameters */
+	board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
+						  ad1882_models, NULL);
+	switch (board_config) {
+	default:
+	case AD1882_3STACK:
+		spec->num_mixers = 2;
+		spec->mixers[1] = ad1882_3stack_mixers;
+		spec->channel_mode = ad1882_modes;
+		spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
+		spec->need_dac_fix = 1;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		break;
+	case AD1882_6STACK:
+		spec->num_mixers = 2;
+		spec->mixers[1] = ad1882_6stack_mixers;
+		break;
+	}
+	return 0;
+}
+
+
+/*
  * patch entries
  */
 struct hda_codec_preset snd_hda_preset_analog[] = {
+	{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
+	{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
 	{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
 	{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
+	{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
 	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
 	{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
 	{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
diff -ruN linux-2.6.22/sound/pci/hda/patch_atihdmi.c linux-2.6.22-alsa/sound/pci/hda/patch_atihdmi.c
--- linux-2.6.22/sound/pci/hda/patch_atihdmi.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/patch_atihdmi.c	2007-07-22 17:32:22.000000000 +0200
@@ -172,6 +172,7 @@
  */
 struct hda_codec_preset snd_hda_preset_atihdmi[] = {
 	{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
+	{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
 	{} /* terminator */
diff -ruN linux-2.6.22/sound/pci/hda/patch_conexant.c linux-2.6.22-alsa/sound/pci/hda/patch_conexant.c
--- linux-2.6.22/sound/pci/hda/patch_conexant.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/patch_conexant.c	2007-07-22 17:32:23.000000000 +0200
@@ -801,7 +801,9 @@
 	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
+	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
+	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
 	{}
 };
diff -ruN linux-2.6.22/sound/pci/hda/patch_realtek.c linux-2.6.22-alsa/sound/pci/hda/patch_realtek.c
--- linux-2.6.22/sound/pci/hda/patch_realtek.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/patch_realtek.c	2007-07-22 17:32:23.000000000 +0200
@@ -94,10 +94,18 @@
 	ALC262_HP_BPC_D7000_WF,
 	ALC262_BENQ_ED8,
 	ALC262_SONY_ASSAMD,
+	ALC262_BENQ_T31,
 	ALC262_AUTO,
 	ALC262_MODEL_LAST /* last tag */
 };
 
+/* ALC268 models */
+enum {
+	ALC268_3ST,
+	ALC268_AUTO,
+	ALC268_MODEL_LAST /* last tag */
+};
+
 /* ALC861 models */
 enum {
 	ALC861_3ST,
@@ -115,6 +123,7 @@
 /* ALC861-VD models */
 enum {
 	ALC660VD_3ST,
+	ALC660VD_3ST_DIG,
 	ALC861VD_3ST,
 	ALC861VD_3ST_DIG,
 	ALC861VD_6ST_DIG,
@@ -144,6 +153,7 @@
 	ALC882_TARGA,
 	ALC882_ASUS_A7J,
 	ALC885_MACPRO,
+	ALC885_IMAC24,
 	ALC882_AUTO,
 	ALC882_MODEL_LAST,
 };
@@ -163,6 +173,8 @@
 	ALC883_LENOVO_101E_2ch,
 	ALC883_LENOVO_NB0763,
 	ALC888_LENOVO_MS7195_DIG,		
+	ALC888_6ST_HP,
+	ALC888_3ST_HP,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 };
@@ -713,6 +725,38 @@
 }
 
 /*
+ * Fix-up pin default configurations
+ */
+
+struct alc_pincfg {
+	hda_nid_t nid;
+	u32 val;
+};
+
+static void alc_fix_pincfg(struct hda_codec *codec,
+			   const struct snd_pci_quirk *quirk,
+			   const struct alc_pincfg **pinfix)
+{
+	const struct alc_pincfg *cfg;
+
+	quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+	if (!quirk)
+		return;
+
+	cfg = pinfix[quirk->value];
+	for (; cfg->nid; cfg++) {
+		int i;
+		u32 val = cfg->val;
+		for (i = 0; i < 4; i++) {
+			snd_hda_codec_write(codec, cfg->nid, 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
+				    val & 0xff);
+			val >>= 8;
+		}
+	}
+}
+
+/*
  * ALC880 3-stack model
  *
  * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
@@ -1878,31 +1922,53 @@
  * Pin assignment:
  *   Speaker-out: 0x14
  *   Mic-In: 0x18
- *   Built-in Mic-In: 0x19 (?)
- *   HP-Out: 0x1b
+ *   Built-in Mic-In: 0x19
+ *   Line-In: 0x1b
+ *   HP-Out: 0x1a
  *   SPDIF-Out: 0x1e
  */
 
-/* seems analog CD is not working */
 static struct hda_input_mux alc880_lg_lw_capture_source = {
-	.num_items = 2,
+	.num_items = 3,
 	.items = {
 		{ "Mic", 0x0 },
 		{ "Internal Mic", 0x1 },
+		{ "Line In", 0x2 },
 	},
 };
 
+#define alc880_lg_lw_modes alc880_threestack_modes
+
 static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
-	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
 	{ } /* end */
 };
 
 static struct hda_verb alc880_lg_lw_init_verbs[] = {
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
+
 	/* set capture source to mic-in */
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1912,7 +1978,6 @@
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* HP-out */
-	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* mic-in to input */
@@ -2856,11 +2921,11 @@
 		.mixers = { alc880_lg_lw_mixer },
 		.init_verbs = { alc880_volume_init_verbs,
 				alc880_lg_lw_init_verbs },
-		.num_dacs = 1,
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
 		.dac_nids = alc880_dac_nids,
 		.dig_out_nid = ALC880_DIGOUT_NID,
-		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
-		.channel_mode = alc880_2_jack_modes,
+		.num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
+		.channel_mode = alc880_lg_lw_modes,
 		.input_mux = &alc880_lg_lw_capture_source,
 		.unsol_event = alc880_lg_lw_unsol_event,
 		.init_hook = alc880_lg_lw_automute,
@@ -5054,6 +5119,60 @@
 	{ }
 };
 
+/* iMac 24 mixer. */
+static struct snd_kcontrol_new alc885_imac24_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT),
+	{ } /* end */
+};
+
+/* iMac 24 init verbs. */
+static struct hda_verb alc885_imac24_init_verbs[] = {
+	/* Internal speakers: output 0 (0x0c) */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Internal speakers: output 0 (0x0c) */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Headphone: output 0 (0x0c) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	/* Front Mic: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{ }
+};
+
+/* Toggle speaker-output according to the hp-jack state */
+static void alc885_imac24_automute(struct hda_codec *codec)
+{
+ 	unsigned int present;
+
+ 	present = snd_hda_codec_read(codec, 0x14, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x18, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x18, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+}
+
+/* Processes unsolicited events. */
+static void alc885_imac24_unsol_event(struct hda_codec *codec,
+				      unsigned int res)
+{
+	/* Headphone insertion or removal. */
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc885_imac24_automute(codec);
+}
+
 static struct hda_verb alc882_targa_verbs[] = {
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -5274,6 +5393,7 @@
 	[ALC882_ARIMA]		= "arima",
 	[ALC882_W2JC]		= "w2jc",
 	[ALC885_MACPRO]		= "macpro",
+	[ALC885_IMAC24]		= "imac24",
 	[ALC882_AUTO]		= "auto",
 };
 
@@ -5284,6 +5404,7 @@
 	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
 	SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
 	SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
+	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
 	{}
@@ -5345,6 +5466,19 @@
 		.channel_mode = alc882_ch_modes,
 		.input_mux = &alc882_capture_source,
 	},
+	[ALC885_IMAC24] = {
+		.mixers = { alc885_imac24_mixer },
+		.init_verbs = { alc885_imac24_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.input_mux = &alc882_capture_source,
+		.unsol_event = alc885_imac24_unsol_event,
+		.init_hook = alc885_imac24_automute,
+	},
 	[ALC882_TARGA] = {
 		.mixers = { alc882_targa_mixer, alc882_chmode_mixer,
 			    alc882_capture_mixer },
@@ -5379,6 +5513,29 @@
 
 
 /*
+ * Pin config fixes
+ */
+enum { 
+	PINFIX_ABIT_AW9D_MAX
+};
+
+static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
+	{ 0x15, 0x01080104 }, /* side */
+	{ 0x16, 0x01011012 }, /* rear */
+	{ 0x17, 0x01016011 }, /* clfe */
+	{ }
+};
+
+static const struct alc_pincfg *alc882_pin_fixes[] = {
+	[PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
+};
+
+static struct snd_pci_quirk alc882_pinfix_tbl[] = {
+	SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+	{}
+};
+
+/*
  * BIOS auto configuration
  */
 static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
@@ -5494,6 +5651,9 @@
 		case 0x106b0c00: /* Mac Pro */
 			board_config = ALC885_MACPRO;
 			break;
+		case 0x106b1000: /* iMac 24 */
+			board_config = ALC885_IMAC24;
+			break;
 		default:
 			printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
 		       			 "trying auto-probe from BIOS...\n");
@@ -5501,6 +5661,8 @@
 		}
 	}
 
+	alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
+
 	if (board_config == ALC882_AUTO) {
 		/* automatic parse from the BIOS config */
 		err = alc882_parse_auto_config(codec);
@@ -5518,7 +5680,7 @@
 	if (board_config != ALC882_AUTO)
 		setup_preset(spec, &alc882_presets[board_config]);
 
-	if (board_config == ALC885_MACPRO) {
+	if (board_config == ALC885_MACPRO || board_config == ALC885_IMAC24) {
 		alc882_gpio_mute(codec, 0, 0);
 		alc882_gpio_mute(codec, 1, 0);
 	}
@@ -5995,6 +6157,84 @@
 	{ } /* end */
 };	
 
+static struct snd_kcontrol_new alc888_6st_hp_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc883_chmode_mixer[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -6126,6 +6366,42 @@
 	{ } /* end */
 };
 
+static struct hda_verb alc888_6st_hp_verbs[] = {
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x02},	/* Rear : output 2 (0x0e) */
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x01},	/* CLFE : output 1 (0x0d) */
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},	/* Side : output 3 (0x0f) */
+	{ }
+};
+
+static struct hda_verb alc888_3st_hp_verbs[] = {
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Rear : output 1 (0x0d) */
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},	/* CLFE : output 2 (0x0e) */
+	{ }
+};
+
+static struct hda_verb alc888_3st_hp_2ch_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ }
+};
+
+static struct hda_verb alc888_3st_hp_6ch_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ }
+};
+
+static struct hda_channel_mode alc888_3st_hp_modes[2] = {
+	{ 2, alc888_3st_hp_2ch_init },
+	{ 6, alc888_3st_hp_6ch_init },
+};
+
 /* toggle front-jack and RCA according to the hp-jack state */
 static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
 {
@@ -6368,11 +6644,14 @@
 	[ALC883_LENOVO_101E_2ch] = "lenovo-101e",
 	[ALC883_LENOVO_NB0763]	= "lenovo-nb0763",
 	[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
+	[ALC888_6ST_HP]		= "6stack-hp",
+	[ALC888_3ST_HP]		= "3stack-hp",
 	[ALC883_AUTO]		= "auto",
 };
 
 static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
@@ -6381,6 +6660,8 @@
 	SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
@@ -6400,6 +6681,9 @@
 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
 	SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
+	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
 	{}
 };
@@ -6584,6 +6868,31 @@
 		.unsol_event = alc883_lenovo_ms7195_unsol_event,
 		.init_hook = alc888_lenovo_ms7195_front_automute,
 	},	
+	[ALC888_6ST_HP] = {
+		.mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC888_3ST_HP] = {
+		.mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes),
+		.channel_mode = alc888_3st_hp_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+	},
 };
 
 
@@ -6857,7 +7166,16 @@
 	{ } /* end */
 };
 
-
+static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	{ } /* end */
+};
 
 #define alc262_capture_mixer		alc882_capture_mixer
 #define alc262_capture_alt_mixer	alc882_capture_alt_mixer
@@ -7189,6 +7507,15 @@
 	{}
 };
 
+static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x3050},
+	{}
+};
+
 /* add playback controls from the parsed DAC table */
 static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 					     const struct auto_pin_cfg *cfg)
@@ -7584,7 +7911,8 @@
 	[ALC262_HP_BPC]		= "hp-bpc",
 	[ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
 	[ALC262_BENQ_ED8]	= "benq",
-	[ALC262_BENQ_ED8]	= "sony-assamd",
+	[ALC262_BENQ_T31]	= "benq-t31",
+	[ALC262_SONY_ASSAMD]	= "sony-assamd",
 	[ALC262_AUTO]		= "auto",
 };
 
@@ -7592,8 +7920,12 @@
 	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
 	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
@@ -7606,6 +7938,7 @@
 	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
 	SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
+	SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
@@ -7710,6 +8043,17 @@
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
+	},
+	[ALC262_BENQ_T31] = {
+		.mixers = { alc262_benq_t31_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_capture_source,
+		.unsol_event = alc262_hippo_unsol_event,
 	},	
 };
 
@@ -7800,31 +8144,540 @@
 }
 
 /*
- *  ALC861 channel source setting (2/6 channel selection for 3-stack)
+ *  ALC268 channel source setting (2 channel)
  */
+#define ALC268_DIGOUT_NID	ALC880_DIGOUT_NID
+#define alc268_modes		alc260_modes
+	
+static hda_nid_t alc268_dac_nids[2] = {
+	/* front, hp */
+	0x02, 0x03
+};
 
-/*
- * set the path ways for 2 channel output
- * need to set the codec line out and mic 1 pin widgets to inputs
- */
-static struct hda_verb alc861_threestack_ch2_init[] = {
-	/* set pin widget 1Ah (line in) for input */
-	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-	/* set pin widget 18h (mic1/2) for input, for mic also enable
-	 * the vref
-	 */
-	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+static hda_nid_t alc268_adc_nids[2] = {
+	/* ADC0-1 */
+	0x08, 0x07
+};
 
-	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
-	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
-	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
-	{ } /* end */
+static hda_nid_t alc268_adc_nids_alt[1] = {
+	/* ADC0 */
+	0x08
 };
-/*
- * 6ch mode
- * need to set the codec line out and mic 1 pin widgets to outputs
+
+static struct snd_kcontrol_new alc268_base_mixer[] = {
+	/* output mixer control */
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	{ }
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc268_base_init_verbs[] = {
+	/* Unmute DAC0-1 and set vol = 0 */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0e)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+        {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{ }
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc268_volume_init_verbs[] = {
+	/* set output DAC */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	/* set PCBEEP vol = 0 */
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))},
+
+	{ }
+};
+
+#define alc268_mux_enum_info alc_mux_enum_info
+#define alc268_mux_enum_get alc_mux_enum_get
+
+static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	const struct hda_input_mux *imux = spec->input_mux;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
+	hda_nid_t nid = capture_mixers[adc_idx];
+	unsigned int *cur_val = &spec->cur_mux[adc_idx];
+	unsigned int i, idx;
+
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (*cur_val == idx && !codec->in_resume)
+		return 0;
+	for (i = 0; i < imux->num_items; i++) {
+		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    v | (imux->items[i].index << 8));
+                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+				    idx );
+	}
+	*cur_val = idx;
+	return 1;
+}
+
+static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc268_mux_enum_info,
+		.get = alc268_mux_enum_get,
+		.put = alc268_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc268_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc268_mux_enum_info,
+		.get = alc268_mux_enum_get,
+		.put = alc268_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_input_mux alc268_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x3 },
+	},
+};
+
+/* create input playback/capture controls for the given pin */
+static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+				    const char *ctlname, int idx)
+{
+	char name[32];
+	int err;
+
+	sprintf(name, "%s Playback Volume", ctlname);
+	if (nid == 0x14) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+				  HDA_COMPOSE_AMP_VAL(0x02, 3, idx,
+						      HDA_OUTPUT));
+		if (err < 0)
+			return err;
+	} else if (nid == 0x15) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+				  HDA_COMPOSE_AMP_VAL(0x03, 3, idx,
+						      HDA_OUTPUT));
+		if (err < 0)
+			return err;
+	} else
+		return -1;
+	sprintf(name, "%s Playback Switch", ctlname);
+	err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+			  HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	hda_nid_t nid;
+	int err;
+
+	spec->multiout.num_dacs = 2;	/* only use one dac */
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	spec->multiout.dac_nids[0] = 2;
+	spec->multiout.dac_nids[1] = 3;
+
+	nid = cfg->line_out_pins[0];
+	if (nid)
+		alc268_new_analog_output(spec, nid, "Front", 0);	
+
+	nid = cfg->speaker_pins[0];
+	if (nid == 0x1d) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL,
+				  "Speaker Playback Volume",
+				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+		if (err < 0)
+			return err;
+	}
+	nid = cfg->hp_pins[0];
+	if (nid)
+		alc268_new_analog_output(spec, nid, "Headphone", 0);
+
+	nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
+	if (nid == 0x16) {
+		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+				  "Mono Playback Switch",
+				  HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT));
+		if (err < 0)
+			return err;
+	}
+	return 0;	
+}
+
+/* create playback/capture controls for input pins */
+static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	struct hda_input_mux *imux = &spec->private_imux;
+	int i, idx1;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		switch(cfg->input_pins[i]) {
+		case 0x18:
+			idx1 = 0;	/* Mic 1 */
+			break;
+		case 0x19:
+			idx1 = 1;	/* Mic 2 */
+			break;
+		case 0x1a:
+			idx1 = 2;	/* Line In */
+			break;
+		case 0x1c:	
+			idx1 = 3;	/* CD */
+			break;
+		default:
+			continue;
+		}
+		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+		imux->items[imux->num_items].index = idx1;
+		imux->num_items++;	
+	}
+	return 0;
+}
+
+static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
+	hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+	hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
+	unsigned int	dac_vol1, dac_vol2;
+
+	if (speaker_nid) {
+		snd_hda_codec_write(codec, speaker_nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_codec_write(codec, 0x0f, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_UNMUTE(1));
+		snd_hda_codec_write(codec, 0x10, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_UNMUTE(1));
+	} else {
+		snd_hda_codec_write(codec, 0x0f, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
+		snd_hda_codec_write(codec, 0x10, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
+	}
+
+	dac_vol1 = dac_vol2 = 0xb000 | 0x40;	/* set max volume  */
+	if (line_nid == 0x14)	
+		dac_vol2 = AMP_OUT_ZERO;
+	else if (line_nid == 0x15)
+		dac_vol1 = AMP_OUT_ZERO;
+	if (hp_nid == 0x14)	
+		dac_vol2 = AMP_OUT_ZERO;
+	else if (hp_nid == 0x15)
+		dac_vol1 = AMP_OUT_ZERO;
+	if (line_nid != 0x16 || hp_nid != 0x16 ||
+	    spec->autocfg.line_out_pins[1] != 0x16 ||
+	    spec->autocfg.line_out_pins[2] != 0x16)
+		dac_vol1 = dac_vol2 = AMP_OUT_ZERO;
+
+	snd_hda_codec_write(codec, 0x02, 0,
+			    AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1);
+	snd_hda_codec_write(codec, 0x03, 0,
+			    AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
+}
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc268_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc268_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc268_pcm_digital_playback	alc880_pcm_digital_playback
+
+/*
+ * BIOS auto configuration
+ */
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t alc268_ignore[] = { 0 };
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+					   alc268_ignore);
+	if (err < 0)
+		return err;
+	if (!spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
+
+	err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
+	spec->multiout.max_channels = 2;
+
+	/* digital only support output */
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+#define alc268_auto_init_multi_out	alc882_auto_init_multi_out
+#define alc268_auto_init_hp_out		alc882_auto_init_hp_out
+#define alc268_auto_init_analog_input	alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc268_auto_init(struct hda_codec *codec)
+{
+	alc268_auto_init_multi_out(codec);
+	alc268_auto_init_hp_out(codec);
+	alc268_auto_init_mono_speaker_out(codec);
+	alc268_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc268_models[ALC268_MODEL_LAST] = {
+	[ALC268_3ST]		= "3stack",
+	[ALC268_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc268_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
+	{}
+};
+
+static struct alc_config_preset alc268_presets[] = {
+	[ALC268_3ST] = {
+		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+		.init_verbs = { alc268_base_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
+		.dac_nids = alc268_dac_nids,
+                .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+                .adc_nids = alc268_adc_nids_alt,
+		.hp_nid = 0x03,
+		.dig_out_nid = ALC268_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc268_modes),
+		.channel_mode = alc268_modes,
+		.input_mux = &alc268_capture_source,
+	},
+};
+
+static int patch_alc268(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int board_config;
+	int err;
+
+	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
+						  alc268_models,
+						  alc268_cfg_tbl);
+
+	if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC268_AUTO;
+	}
+
+	if (board_config == ALC268_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc268_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (!err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC268_3ST;
+		}
+	}
+
+	if (board_config != ALC268_AUTO)
+		setup_preset(spec, &alc268_presets[board_config]);
+
+	spec->stream_name_analog = "ALC268 Analog";
+	spec->stream_analog_playback = &alc268_pcm_analog_playback;
+	spec->stream_analog_capture = &alc268_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC268 Digital";
+	spec->stream_digital_playback = &alc268_pcm_digital_playback;
+
+	if (board_config == ALC268_AUTO) {
+		if (!spec->adc_nids && spec->input_mux) {
+			/* check whether NID 0x07 is valid */
+			unsigned int wcap = get_wcaps(codec, 0x07);
+
+			/* get type */
+			wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+			if (wcap != AC_WID_AUD_IN) {
+				spec->adc_nids = alc268_adc_nids_alt;
+				spec->num_adc_nids =
+					ARRAY_SIZE(alc268_adc_nids_alt);
+				spec->mixers[spec->num_mixers] =
+					alc268_capture_alt_mixer;
+				spec->num_mixers++;
+			} else {
+				spec->adc_nids = alc268_adc_nids;
+				spec->num_adc_nids =
+					ARRAY_SIZE(alc268_adc_nids);
+				spec->mixers[spec->num_mixers] =
+					alc268_capture_mixer;
+				spec->num_mixers++;
+			}
+		}
+	}
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC268_AUTO)
+		spec->init_hook = alc268_auto_init;
+		
+	return 0;
+}
+
+/*
+ *  ALC861 channel source setting (2/6 channel selection for 3-stack)
+ */
+
+/*
+ * set the path ways for 2 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc861_threestack_ch2_init[] = {
+	/* set pin widget 1Ah (line in) for input */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* set pin widget 18h (mic1/2) for input, for mic also enable
+	 * the vref
+	 */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
+	{ } /* end */
+};
+/*
+ * 6ch mode
+ * need to set the codec line out and mic 1 pin widgets to outputs
  */
 static struct hda_verb alc861_threestack_ch6_init[] = {
 	/* set pin widget 1Ah (line in) for output (Back Surround)*/
@@ -8767,13 +9620,21 @@
 	SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
+	SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+	SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
 	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
-	SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA),
+	/* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
+	 *        Any other models that need this preset?
+	 */
+	/* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
 	SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+	SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
 	SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
 	SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
 	SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
+	SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
+	SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
 	{}
 };
 
@@ -9464,6 +10325,7 @@
  */
 static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
 	[ALC660VD_3ST]		= "3stack-660",
+	[ALC660VD_3ST_DIG]= "3stack-660-digout",
 	[ALC861VD_3ST]		= "3stack",
 	[ALC861VD_3ST_DIG]	= "3stack-digout",
 	[ALC861VD_6ST_DIG]	= "6stack-digout",
@@ -9475,7 +10337,7 @@
 static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
-	SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST),
+	SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
 	SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
 
@@ -9483,6 +10345,7 @@
 	SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
 	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
+	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
 	{}
 };
 
@@ -9499,6 +10362,19 @@
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_capture_source,
 	},
+	[ALC660VD_3ST_DIG] = {
+		.mixers = { alc861vd_3st_mixer },
+		.init_verbs = { alc861vd_volume_init_verbs,
+				 alc861vd_3stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+		.dac_nids = alc660vd_dac_nids,
+		.dig_out_nid = ALC861VD_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
+		.adc_nids = alc861vd_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+		.channel_mode = alc861vd_3stack_2ch_modes,
+		.input_mux = &alc861vd_capture_source,
+	},
 	[ALC861VD_3ST] = {
 		.mixers = { alc861vd_3st_mixer },
 		.init_verbs = { alc861vd_volume_init_verbs,
@@ -10420,7 +11296,7 @@
 	for (i = 0; i < cfg->line_outs; i++) {
 		if (!spec->multiout.dac_nids[i])
 			continue;
-		nid = alc880_idx_to_dac(i);
+		nid = alc880_idx_to_mixer(i);
 		if (i == 2) {
 			/* Center/LFE */
 			err = add_control(spec, ALC_CTL_WIDGET_VOL,
@@ -10643,14 +11519,10 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	
-	if (err < 0)
-		return err;
-	else if (err > 0)
-		/* hack - override the init verbs */
-		spec->init_verbs[0] = alc662_auto_init_verbs;
+	spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
 	spec->mixers[spec->num_mixers] = alc662_capture_mixer;
 	spec->num_mixers++;
-	return err;
+	return 1;
 }
 
 /* additional initialization for auto-configuration model */
@@ -10687,7 +11559,7 @@
 		if (err < 0) {
 			alc_free(codec);
 			return err;
-		} else if (err) {
+		} else if (!err) {
 			printk(KERN_INFO
 			       "hda_codec: Cannot set up configuration "
 			       "from BIOS.  Using base mode...\n");
@@ -10724,6 +11596,7 @@
 struct hda_codec_preset snd_hda_preset_realtek[] = {
 	{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
 	{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
+	{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
 	{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
 	  .patch = patch_alc861 },
 	{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
diff -ruN linux-2.6.22/sound/pci/hda/patch_si3054.c linux-2.6.22-alsa/sound/pci/hda/patch_si3054.c
--- linux-2.6.22/sound/pci/hda/patch_si3054.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/patch_si3054.c	2007-07-22 17:32:23.000000000 +0200
@@ -304,8 +304,12 @@
  	{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+	/* VIA HDA on Clevo m540 */
+	{ .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
 	/* Asus A8J Modem (SM56) */
 	{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+	/* LG LW20 modem */
+	{ .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
 	{}
 };
 
diff -ruN linux-2.6.22/sound/pci/hda/patch_sigmatel.c linux-2.6.22-alsa/sound/pci/hda/patch_sigmatel.c
--- linux-2.6.22/sound/pci/hda/patch_sigmatel.c	2007-07-21 21:47:22.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/hda/patch_sigmatel.c	2007-07-22 17:32:23.000000000 +0200
@@ -44,6 +44,7 @@
 
 enum {
 	STAC_9205_REF,
+	STAC_M43xx,
 	STAC_9205_MODELS
 };
 
@@ -59,11 +60,19 @@
 	STAC_D945_REF,
 	STAC_D945GTP3,
 	STAC_D945GTP5,
+	STAC_922X_DELL,
+	STAC_INTEL_MAC_V1,
+	STAC_INTEL_MAC_V2,
+	STAC_INTEL_MAC_V3,
+	STAC_INTEL_MAC_V4,
+	STAC_INTEL_MAC_V5,
+	/* for backward compitability */
 	STAC_MACMINI,
 	STAC_MACBOOK,
 	STAC_MACBOOK_PRO_V1,
 	STAC_MACBOOK_PRO_V2,
 	STAC_IMAC_INTEL,
+	STAC_IMAC_INTEL_20,
 	STAC_922X_MODELS
 };
 
@@ -210,7 +219,6 @@
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
 	0x0f, 0x14, 0x16, 0x17, 0x18,
 	0x21, 0x22,
-	
 };
 
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
@@ -326,8 +334,6 @@
 };
 
 static struct snd_kcontrol_new stac925x_mixer[] = {
-	HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Input Source",
@@ -549,44 +555,78 @@
 	0x02a19320, 0x40000100,
 };
 
-static unsigned int macbook_pro_v1_pin_configs[10] = {
-	0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010,
-	0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e,
-	0x02a19320, 0x400000fb
+static unsigned int intel_mac_v1_pin_configs[10] = {
+	0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
+	0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
+	0x400000fc, 0x400000fb,
+};
+
+static unsigned int intel_mac_v2_pin_configs[10] = {
+	0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+	0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
+	0x400000fc, 0x400000fb,
+};
+
+static unsigned int intel_mac_v3_pin_configs[10] = {
+	0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+	0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
+	0x400000fc, 0x400000fb,
 };
 
-static unsigned int macbook_pro_v2_pin_configs[10] = {
-	0x0221401f, 0x90a70120, 0x01813024, 0x01014010,
-	0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e,
+static unsigned int intel_mac_v4_pin_configs[10] = {
+	0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+	0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
 	0x400000fc, 0x400000fb,
 };
 
-static unsigned int imac_intel_pin_configs[10] = {
-	0x0121e230, 0x90a70120, 0x9017e110, 0x400000fe,
-	0x400000fd, 0x0181e021, 0x1145e040, 0x400000fa,
+static unsigned int intel_mac_v5_pin_configs[10] = {
+	0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+	0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
 	0x400000fc, 0x400000fb,
 };
 
+static unsigned int stac922x_dell_pin_configs[10] = {
+	0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310,
+	0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2,
+	0x50a003f3, 0x405003f4
+};
+
 static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
 	[STAC_D945_REF] = ref922x_pin_configs,
 	[STAC_D945GTP3] = d945gtp3_pin_configs,
 	[STAC_D945GTP5] = d945gtp5_pin_configs,
-	[STAC_MACMINI] = macbook_pro_v1_pin_configs,
-	[STAC_MACBOOK] = macbook_pro_v1_pin_configs,
-	[STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs,
-	[STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs,
-	[STAC_IMAC_INTEL] = imac_intel_pin_configs,
+	[STAC_922X_DELL] = stac922x_dell_pin_configs,
+	[STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
+	[STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
+	[STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
+	[STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
+	[STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
+	/* for backward compitability */
+	[STAC_MACMINI] = intel_mac_v3_pin_configs,
+	[STAC_MACBOOK] = intel_mac_v5_pin_configs,
+	[STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
+	[STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
+	[STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
+	[STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
 };
 
 static const char *stac922x_models[STAC_922X_MODELS] = {
 	[STAC_D945_REF]	= "ref",
 	[STAC_D945GTP5]	= "5stack",
 	[STAC_D945GTP3]	= "3stack",
+	[STAC_922X_DELL] = "dell",
+	[STAC_INTEL_MAC_V1] = "intel-mac-v1",
+	[STAC_INTEL_MAC_V2] = "intel-mac-v2",
+	[STAC_INTEL_MAC_V3] = "intel-mac-v3",
+	[STAC_INTEL_MAC_V4] = "intel-mac-v4",
+	[STAC_INTEL_MAC_V5] = "intel-mac-v5",
+	/* for backward compitability */
 	[STAC_MACMINI]	= "macmini",
 	[STAC_MACBOOK]	= "macbook",
 	[STAC_MACBOOK_PRO_V1]	= "macbook-pro-v1",
 	[STAC_MACBOOK_PRO_V2]	= "macbook-pro",
 	[STAC_IMAC_INTEL] = "imac-intel",
+	[STAC_IMAC_INTEL_20] = "imac-intel-20",
 };
 
 static struct snd_pci_quirk stac922x_cfg_tbl[] = {
@@ -649,7 +689,10 @@
 	/* other systems  */
 	/* Apple Mac Mini (early 2006) */
 	SND_PCI_QUIRK(0x8384, 0x7680,
-		      "Mac Mini", STAC_MACMINI),
+		      "Mac Mini", STAC_INTEL_MAC_V3),
+	/* Dell */
+	SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL),
+
 	{} /* terminator */
 };
 
@@ -730,7 +773,8 @@
 };
 
 static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
-	ref9205_pin_configs,
+	[STAC_REF] = ref9205_pin_configs,
+	[STAC_M43xx] = NULL,
 };
 
 static const char *stac9205_models[STAC_9205_MODELS] = {
@@ -741,6 +785,10 @@
 	/* SigmaTel reference board */
 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
 		      "DFI LanParty", STAC_9205_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01f8,
+		      "Dell Precision", STAC_M43xx),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01ff,
+		      "Dell Precision", STAC_M43xx),
 	{} /* terminator */
 };
 
@@ -770,33 +818,56 @@
 	return 0;
 }
 
+static void stac92xx_set_config_reg(struct hda_codec *codec,
+				    hda_nid_t pin_nid, unsigned int pin_config)
+{
+	int i;
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
+			    pin_config & 0x000000ff);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
+			    (pin_config & 0x0000ff00) >> 8);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
+			    (pin_config & 0x00ff0000) >> 16);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+			    pin_config >> 24);
+	i = snd_hda_codec_read(codec, pin_nid, 0,
+			       AC_VERB_GET_CONFIG_DEFAULT,
+			       0x00);	
+	snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
+		    pin_nid, i);
+}
+
 static void stac92xx_set_config_regs(struct hda_codec *codec)
 {
 	int i;
 	struct sigmatel_spec *spec = codec->spec;
-	unsigned int pin_cfg;
 
-	if (! spec->pin_nids || ! spec->pin_configs)
-		return;
+ 	if (!spec->pin_configs)
+ 		return;
 
-	for (i = 0; i < spec->num_pins; i++) {
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
-				    spec->pin_configs[i] & 0x000000ff);
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
-				    (spec->pin_configs[i] & 0x0000ff00) >> 8);
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
-				    (spec->pin_configs[i] & 0x00ff0000) >> 16);
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
-				    spec->pin_configs[i] >> 24);
-		pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
-					     AC_VERB_GET_CONFIG_DEFAULT,
-					     0x00);	
-		snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
-	}
+	for (i = 0; i < spec->num_pins; i++)
+		stac92xx_set_config_reg(codec, spec->pin_nids[i],
+					spec->pin_configs[i]);
+}
+
+static void stac92xx_enable_gpio_mask(struct hda_codec *codec,
+				      int gpio_mask, int gpio_data)
+{
+	/* Configure GPIOx as output */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DIRECTION, gpio_mask);
+	/* Configure GPIOx as CMOS */
+	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
+	/* Assert GPIOx */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DATA, gpio_data);
+	/* Enable GPIOx */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_MASK, gpio_mask);
 }
 
 /*
@@ -1168,7 +1239,7 @@
  * and 9202/925x. For those, dac_nids[] must be hard-coded.
  */
 static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
-				       const struct auto_pin_cfg *cfg)
+				       struct auto_pin_cfg *cfg)
 {
 	struct sigmatel_spec *spec = codec->spec;
 	int i, j, conn_len = 0; 
@@ -1193,6 +1264,13 @@
 		}
 
 		if (j == conn_len) {
+			if (spec->multiout.num_dacs > 0) {
+				/* we have already working output pins,
+				 * so let's drop the broken ones again
+				 */
+				cfg->line_outs = spec->multiout.num_dacs;
+				break;
+			}
 			/* error out, no available DAC found */
 			snd_printk(KERN_ERR
 				   "%s: No available DAC for pin 0x%x\n",
@@ -1334,7 +1412,15 @@
 			continue;
 		add_spec_dacs(spec, nid);
 	}
-
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
+					AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+		if (check_in_dac_nids(spec, nid))
+			nid = 0;
+		if (! nid)
+			continue;
+		add_spec_dacs(spec, nid);
+	}
 	for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
 		static const char *pfxs[] = {
 			"Speaker", "External Speaker", "Speaker2",
@@ -1891,7 +1977,7 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
-	spec->num_pins = 8;
+	spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
 	spec->pin_nids = stac9200_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
 							stac9200_models,
@@ -1941,7 +2027,7 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
-	spec->num_pins = 8;
+	spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
 	spec->pin_nids = stac925x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
 							stac925x_models,
@@ -2013,29 +2099,41 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
-	spec->num_pins = 10;
+	spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
 	spec->pin_nids = stac922x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
 							stac922x_models,
 							stac922x_cfg_tbl);
-	if (spec->board_config == STAC_MACMINI) {
+	if (spec->board_config == STAC_INTEL_MAC_V3) {
 		spec->gpio_mute = 1;
 		/* Intel Macs have all same PCI SSID, so we need to check
 		 * codec SSID to distinguish the exact models
 		 */
 		printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
 		switch (codec->subsystem_id) {
-		case 0x106b0a00: /* MacBook First generatoin */
-			spec->board_config = STAC_MACBOOK;
+
+		case 0x106b0800:
+			spec->board_config = STAC_INTEL_MAC_V1;
+			break;
+		case 0x106b0600:
+		case 0x106b0700:
+			spec->board_config = STAC_INTEL_MAC_V2;
 			break;
-		case 0x106b0200: /* MacBook Pro first generation */
-			spec->board_config = STAC_MACBOOK_PRO_V1;
+		case 0x106b0e00:
+		case 0x106b0f00:
+		case 0x106b1600:
+		case 0x106b1700:
+		case 0x106b0200:
+		case 0x106b1e00:
+			spec->board_config = STAC_INTEL_MAC_V3;
 			break;
-		case 0x106b1e00: /* MacBook Pro second generation */
-			spec->board_config = STAC_MACBOOK_PRO_V2;
+		case 0x106b1a00:
+		case 0x00000100:
+			spec->board_config = STAC_INTEL_MAC_V4;
 			break;
-		case 0x106b0700: /* Intel-based iMac */
-			spec->board_config = STAC_IMAC_INTEL;
+		case 0x106b0a00:
+		case 0x106b2200:
+			spec->board_config = STAC_INTEL_MAC_V5;
 			break;
 		}
 	}
@@ -2082,6 +2180,13 @@
 
 	codec->patch_ops = stac92xx_patch_ops;
 
+	/* Fix Mux capture level; max to 2 */
+	snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
+				  (0 << AC_AMPCAP_OFFSET_SHIFT) |
+				  (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+				  (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+				  (0 << AC_AMPCAP_MUTE_SHIFT));
+
 	return 0;
 }
 
@@ -2095,7 +2200,7 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
-	spec->num_pins = 14;
+	spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
 	spec->pin_nids = stac927x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
 							stac927x_models,
@@ -2141,7 +2246,9 @@
 	}
 
 	spec->multiout.dac_nids = spec->dac_nids;
-
+	/* GPIO0 High = Enable EAPD */
+	stac92xx_enable_gpio_mask(codec, 0x00000001, 0x00000001);
+	
 	err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
 	if (!err) {
 		if (spec->board_config < 0) {
@@ -2159,27 +2266,20 @@
 
 	codec->patch_ops = stac92xx_patch_ops;
 
-	/* Fix Mux capture level; max to 2 */
-	snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
-				  (0 << AC_AMPCAP_OFFSET_SHIFT) |
-				  (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-				  (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-				  (0 << AC_AMPCAP_MUTE_SHIFT));
-
 	return 0;
 }
 
 static int patch_stac9205(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec;
-	int err;
+	int err, gpio_mask, gpio_data;
 
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 		return -ENOMEM;
 
 	codec->spec = spec;
-	spec->num_pins = 14;
+	spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
 	spec->pin_nids = stac9205_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
 							stac9205_models,
@@ -2209,19 +2309,21 @@
 	spec->mixer = stac9205_mixer;
 
 	spec->multiout.dac_nids = spec->dac_nids;
+	
+	if (spec->board_config == STAC_M43xx) {
+		/* Enable SPDIF in/out */
+		stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
+		stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+
+		gpio_mask = 0x00000007; /* GPIO0-2 */
+		/* GPIO0 High = EAPD, GPIO1 Low = DRM,
+		 * GPIO2 High = Headphone Mute
+		 */
+		gpio_data = 0x00000005;
+	} else
+		gpio_mask = gpio_data = 0x00000001; /* GPIO0 High = EAPD */
 
-	/* Configure GPIO0 as EAPD output */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
-	/* Configure GPIO0 as CMOS */
-	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
-	/* Assert GPIO0 high */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DATA, 0x00000001);
-	/* Enable GPIO0 */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_MASK, 0x00000001);
-
+	stac92xx_enable_gpio_mask(codec, gpio_mask, gpio_data);
 	err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
 	if (!err) {
 		if (spec->board_config < 0) {
@@ -2256,8 +2358,8 @@
 	.num_items = 2,
 	.items = {
 		/* { "HP", 0x0 }, */
-		{ "Line", 0x1 },
-		{ "Mic", 0x2 },
+		{ "Mic Jack", 0x1 },
+		{ "Internal Mic", 0x2 },
 		{ "PCM", 0x3 },
 	}
 };
@@ -2268,7 +2370,7 @@
 	{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
 	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
@@ -2284,7 +2386,7 @@
 	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
 /*	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
 /*	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
diff -ruN linux-2.6.22/sound/pci/ice1712/revo.c linux-2.6.22-alsa/sound/pci/ice1712/revo.c
--- linux-2.6.22/sound/pci/ice1712/revo.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/ice1712/revo.c	2007-07-22 17:32:23.000000000 +0200
@@ -186,7 +186,12 @@
 #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
 
 static const struct snd_akm4xxx_dac_channel revo71_front[] = {
-	AK_DAC("PCM Playback Volume", 2)
+	{
+		.name = "PCM Playback Volume",
+		.num_channels = 2,
+		/* front channels DAC supports muting */
+		.switch_name = "PCM Playback Switch",
+	},
 };
 
 static const struct snd_akm4xxx_dac_channel revo71_surround[] = {
diff -ruN linux-2.6.22/sound/pci/mixart/mixart_hwdep.c linux-2.6.22-alsa/sound/pci/mixart/mixart_hwdep.c
--- linux-2.6.22/sound/pci/mixart/mixart_hwdep.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/mixart/mixart_hwdep.c	2007-07-22 17:32:23.000000000 +0200
@@ -24,6 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/firmware.h>
+#include <linux/vmalloc.h>
 #include <asm/io.h>
 #include <sound/core.h>
 #include "mixart.h"
diff -ruN linux-2.6.22/sound/pci/nm256/nm256.c linux-2.6.22-alsa/sound/pci/nm256/nm256.c
--- linux-2.6.22/sound/pci/nm256/nm256.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/nm256/nm256.c	2007-07-22 17:32:21.000000000 +0200
@@ -1533,7 +1533,8 @@
 				printk(KERN_ERR "  force the driver to load by "
 				       "passing in the module parameter\n");
 				printk(KERN_ERR "    force_ac97=1\n");
-				printk(KERN_ERR "  or try sb16 or cs423x drivers instead.\n");
+				printk(KERN_ERR "  or try sb16, opl3sa2, or "
+				       "cs423x drivers instead.\n");
 				err = -ENXIO;
 				goto __error;
 			}
diff -ruN linux-2.6.22/sound/pci/rme9652/rme9652.c linux-2.6.22-alsa/sound/pci/rme9652/rme9652.c
--- linux-2.6.22/sound/pci/rme9652/rme9652.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/rme9652/rme9652.c	2007-07-22 17:32:24.000000000 +0200
@@ -406,7 +406,7 @@
 		} else if (!frag)
 			return 0;
 		offset -= rme9652->max_jitter;
-		if (offset < 0)
+		if ((int)offset < 0)
 			offset += period_size * 2;
 	} else {
 		if (offset > period_size + rme9652->max_jitter) {
diff -ruN linux-2.6.22/sound/pci/via82xx.c linux-2.6.22-alsa/sound/pci/via82xx.c
--- linux-2.6.22/sound/pci/via82xx.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/via82xx.c	2007-07-22 17:39:47.000000000 +0200
@@ -2098,7 +2098,7 @@
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 			break;
-		schedule_timeout_uninterruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
@@ -2117,7 +2117,7 @@
 			chip->ac97_secondary = 1;
 			goto __ac97_ok2;
 		}
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 	/* This is ok, the most of motherboards have only one codec */
 
diff -ruN linux-2.6.22/sound/pci/via82xx_modem.c linux-2.6.22-alsa/sound/pci/via82xx_modem.c
--- linux-2.6.22/sound/pci/via82xx_modem.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/pci/via82xx_modem.c	2007-07-22 17:39:47.000000000 +0200
@@ -983,7 +983,7 @@
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 			break;
-		schedule_timeout_uninterruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
@@ -1001,7 +1001,7 @@
 			chip->ac97_secondary = 1;
 			goto __ac97_ok2;
 		}
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 	/* This is ok, the most of motherboards have only one codec */
 
diff -ruN linux-2.6.22/sound/ppc/Kconfig linux-2.6.22-alsa/sound/ppc/Kconfig
--- linux-2.6.22/sound/ppc/Kconfig	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/ppc/Kconfig	2007-07-22 17:32:25.000000000 +0200
@@ -33,3 +33,23 @@
 	  option.
 
 endmenu
+
+menu "ALSA PowerPC devices"
+	depends on SND!=n && ( PPC64 || PPC32 )
+
+config SND_PS3
+	tristate "PS3 Audio support"
+	depends on SND && PS3_PS3AV
+	select SND_PCM
+	default m
+	help
+	  Say Y here to include support for audio on the PS3
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd_ps3.
+
+config SND_PS3_DEFAULT_START_DELAY
+	int "Startup delay time in ms"
+	depends on SND_PS3
+	default "2000"
+endmenu
diff -ruN linux-2.6.22/sound/ppc/Makefile linux-2.6.22-alsa/sound/ppc/Makefile
--- linux-2.6.22/sound/ppc/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/ppc/Makefile	2007-07-22 17:32:25.000000000 +0200
@@ -6,4 +6,5 @@
 snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
+obj-$(CONFIG_SND_POWERMAC)	+= snd-powermac.o
+obj-$(CONFIG_SND_PS3)		+= snd_ps3.o
diff -ruN linux-2.6.22/sound/ppc/beep.c linux-2.6.22-alsa/sound/ppc/beep.c
--- linux-2.6.22/sound/ppc/beep.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/ppc/beep.c	2007-07-22 17:32:25.000000000 +0200
@@ -118,7 +118,7 @@
 	default: return -1;
 	}
 
-	chip = dev->private;
+	chip = input_get_drvdata(dev);
 	if (! chip || (beep = chip->beep) == NULL)
 		return -1;
 
@@ -239,8 +239,8 @@
 	input_dev->evbit[0] = BIT(EV_SND);
 	input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
 	input_dev->event = snd_pmac_beep_event;
-	input_dev->private = chip;
-	input_dev->cdev.dev = &chip->pdev->dev;
+	input_dev->dev.parent = &chip->pdev->dev;
+	input_set_drvdata(input_dev, chip);
 
 	beep->dev = input_dev;
 	beep->buf = dmabuf;
@@ -251,8 +251,8 @@
 	err = snd_ctl_add(chip->card, beep_ctl);
 	if (err < 0)
 		goto fail1;
- 
- 	chip->beep = beep;
+
+	chip->beep = beep;
 
 	err = input_register_device(beep->dev);
 	if (err)
diff -ruN linux-2.6.22/sound/ppc/snd_ps3.c linux-2.6.22-alsa/sound/ppc/snd_ps3.c
--- linux-2.6.22/sound/ppc/snd_ps3.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/ppc/snd_ps3.c	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,1125 @@
+/*
+ * Audio support for PS3
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * All rights reserved.
+ * Copyright 2006, 2007 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the Licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/asound.h>
+#include <sound/memalloc.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <asm/firmware.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+#include <asm/lv1call.h>
+#include <asm/ps3.h>
+#include <asm/ps3av.h>
+
+#include "snd_ps3_reg.h"
+#include "snd_ps3.h"
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PS3 sound driver");
+MODULE_AUTHOR("Sony Computer Entertainment Inc.");
+
+/* module  entries */
+static int __init snd_ps3_init(void);
+static void __exit snd_ps3_exit(void);
+
+/* ALSA snd driver ops */
+static int snd_ps3_pcm_open(struct snd_pcm_substream *substream);
+static int snd_ps3_pcm_close(struct snd_pcm_substream *substream);
+static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream);
+static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
+				 int cmd);
+static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream
+					     *substream);
+static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params);
+static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream);
+
+
+/* ps3_system_bus_driver entries */
+static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev);
+static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev);
+
+/* address setup */
+static int snd_ps3_map_mmio(void);
+static void snd_ps3_unmap_mmio(void);
+static int snd_ps3_allocate_irq(void);
+static void snd_ps3_free_irq(void);
+static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start);
+
+/* interrupt handler */
+static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id);
+
+
+/* set sampling rate/format */
+static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream);
+/* take effect parameter change */
+static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card);
+/* initialize avsetting and take it effect */
+static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card);
+/* setup dma */
+static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
+			       enum snd_ps3_dma_filltype filltype);
+static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card);
+
+static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void  *vaddr, int ch);
+
+
+module_init(snd_ps3_init);
+module_exit(snd_ps3_exit);
+
+/*
+ * global
+ */
+static struct snd_ps3_card_info the_card;
+
+static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY;
+
+module_param_named(start_delay, snd_ps3_start_delay, uint, 0644);
+MODULE_PARM_DESC(start_delay, "time to insert silent data in milisec");
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for PS3 soundchip.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for PS3 soundchip.");
+
+
+/*
+ * PS3 audio register access
+ */
+static inline u32 read_reg(unsigned int reg)
+{
+	return in_be32(the_card.mapped_mmio_vaddr + reg);
+}
+static inline void write_reg(unsigned int reg, u32 val)
+{
+	out_be32(the_card.mapped_mmio_vaddr + reg, val);
+}
+static inline void update_reg(unsigned int reg, u32 or_val)
+{
+	u32 newval = read_reg(reg) | or_val;
+	write_reg(reg, newval);
+}
+static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val)
+{
+	u32 newval = (read_reg(reg) & mask) | or_val;
+	write_reg(reg, newval);
+}
+
+/*
+ * ALSA defs
+ */
+const static struct snd_pcm_hardware snd_ps3_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_NONINTERLEAVED |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = (SNDRV_PCM_FMTBIT_S16_BE |
+		    SNDRV_PCM_FMTBIT_S24_BE),
+	.rates = (SNDRV_PCM_RATE_44100 |
+		  SNDRV_PCM_RATE_48000 |
+		  SNDRV_PCM_RATE_88200 |
+		  SNDRV_PCM_RATE_96000),
+	.rate_min = 44100,
+	.rate_max = 96000,
+
+	.channels_min = 2, /* stereo only */
+	.channels_max = 2,
+
+	.buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64,
+
+	/* interrupt by four stages */
+	.period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4,
+	.period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4,
+
+	.periods_min = 16,
+	.periods_max = 32, /* buffer_size_max/ period_bytes_max */
+
+	.fifo_size = PS3_AUDIO_FIFO_SIZE
+};
+
+static struct snd_pcm_ops snd_ps3_pcm_spdif_ops =
+{
+	.open = snd_ps3_pcm_open,
+	.close = snd_ps3_pcm_close,
+	.prepare = snd_ps3_pcm_prepare,
+	.ioctl = snd_pcm_lib_ioctl,
+	.trigger = snd_ps3_pcm_trigger,
+	.pointer = snd_ps3_pcm_pointer,
+	.hw_params = snd_ps3_pcm_hw_params,
+	.hw_free = snd_ps3_pcm_hw_free
+};
+
+static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card,
+				   int count, int force_stop)
+{
+	int dma_ch, done, retries, stop_forced = 0;
+	uint32_t status;
+
+	for (dma_ch = 0; dma_ch < 8; dma_ch ++) {
+		retries = count;
+		do {
+			status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
+				PS3_AUDIO_KICK_STATUS_MASK;
+			switch (status) {
+			case PS3_AUDIO_KICK_STATUS_DONE:
+			case PS3_AUDIO_KICK_STATUS_NOTIFY:
+			case PS3_AUDIO_KICK_STATUS_CLEAR:
+			case PS3_AUDIO_KICK_STATUS_ERROR:
+				done = 1;
+				break;
+			default:
+				done = 0;
+				udelay(10);
+			}
+		} while (!done && --retries);
+		if (!retries && force_stop) {
+			pr_info("%s: DMA ch %d is not stopped.",
+				__func__, dma_ch);
+			/* last resort. force to stop dma.
+			 *  NOTE: this cause DMA done interrupts
+			 */
+			update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR);
+			stop_forced = 1;
+		}
+	}
+	return stop_forced;
+}
+
+/*
+ * wait for all dma is done.
+ * NOTE: caller should reset card->running before call.
+ *       If not, the interrupt handler will re-start DMA,
+ *       then DMA is never stopped.
+ */
+static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card)
+{
+	int stop_forced;
+	/*
+	 * wait for the last dma is done
+	 */
+
+	/*
+	 * expected maximum DMA done time is 5.7ms + something (DMA itself).
+	 * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next
+	 * DMA kick event would occur.
+	 */
+	stop_forced = snd_ps3_verify_dma_stop(card, 700, 1);
+
+	/*
+	 * clear outstanding interrupts.
+	 */
+	update_reg(PS3_AUDIO_INTR_0, 0);
+	update_reg(PS3_AUDIO_AX_IS, 0);
+
+	/*
+	 *revert CLEAR bit since it will not reset automatically after DMA stop
+	 */
+	if (stop_forced)
+		update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0);
+	/* ensure the hardware sees changes */
+	wmb();
+}
+
+static void snd_ps3_kick_dma(struct snd_ps3_card_info *card)
+{
+
+	update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST);
+	/* ensure the hardware sees the change */
+	wmb();
+}
+
+/*
+ * convert virtual addr to ioif bus addr.
+ */
+static dma_addr_t v_to_bus(struct snd_ps3_card_info *card,
+			   void * paddr,
+			   int ch)
+{
+	return card->dma_start_bus_addr[ch] +
+		(paddr - card->dma_start_vaddr[ch]);
+};
+
+
+/*
+ * increment ring buffer pointer.
+ * NOTE: caller must hold write spinlock
+ */
+static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card,
+				enum snd_ps3_ch ch, size_t byte_count,
+				int stage)
+{
+	if (!stage)
+		card->dma_last_transfer_vaddr[ch] =
+			card->dma_next_transfer_vaddr[ch];
+	card->dma_next_transfer_vaddr[ch] += byte_count;
+	if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <=
+	    card->dma_next_transfer_vaddr[ch]) {
+		card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch];
+	}
+}
+/*
+ * setup dmac to send data to audio and attenuate samples on the ring buffer
+ */
+static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
+			       enum snd_ps3_dma_filltype filltype)
+{
+	/* this dmac does not support over 4G */
+	uint32_t dma_addr;
+	int fill_stages, dma_ch, stage;
+	enum snd_ps3_ch ch;
+	uint32_t ch0_kick_event = 0; /* initialize to mute gcc */
+	void *start_vaddr;
+	unsigned long irqsave;
+	int silent = 0;
+
+	switch (filltype) {
+	case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL:
+		silent = 1;
+		/* intentionally fall thru */
+	case SND_PS3_DMA_FILLTYPE_FIRSTFILL:
+		ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS;
+		break;
+
+	case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING:
+		silent = 1;
+		/* intentionally fall thru */
+	case SND_PS3_DMA_FILLTYPE_RUNNING:
+		ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY;
+		break;
+	}
+
+	snd_ps3_verify_dma_stop(card, 700, 0);
+	fill_stages = 4;
+	spin_lock_irqsave(&card->dma_lock, irqsave);
+	for (ch = 0; ch < 2; ch++) {
+		start_vaddr = card->dma_next_transfer_vaddr[0];
+		for (stage = 0; stage < fill_stages; stage ++) {
+			dma_ch = stage * 2 + ch;
+			if (silent)
+				dma_addr = card->null_buffer_start_dma_addr;
+			else
+				dma_addr =
+				v_to_bus(card,
+					 card->dma_next_transfer_vaddr[ch],
+					 ch);
+
+			write_reg(PS3_AUDIO_SOURCE(dma_ch),
+				  (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY |
+				   dma_addr));
+
+			/* dst: fixed to 3wire#0 */
+			if (ch == 0)
+				write_reg(PS3_AUDIO_DEST(dma_ch),
+					  (PS3_AUDIO_DEST_TARGET_AUDIOFIFO |
+					   PS3_AUDIO_AO_3W_LDATA(0)));
+			else
+				write_reg(PS3_AUDIO_DEST(dma_ch),
+					  (PS3_AUDIO_DEST_TARGET_AUDIOFIFO |
+					   PS3_AUDIO_AO_3W_RDATA(0)));
+
+			/* count always 1 DMA block (1/2 stage = 128 bytes) */
+			write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0);
+			/* bump pointer if needed */
+			if (!silent)
+				snd_ps3_bump_buffer(card, ch,
+						    PS3_AUDIO_DMAC_BLOCK_SIZE,
+						    stage);
+
+			/* kick event  */
+			if (dma_ch == 0)
+				write_reg(PS3_AUDIO_KICK(dma_ch),
+					  ch0_kick_event);
+			else
+				write_reg(PS3_AUDIO_KICK(dma_ch),
+					  PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch
+									 - 1) |
+					  PS3_AUDIO_KICK_REQUEST);
+		}
+	}
+	/* ensure the hardware sees the change */
+	wmb();
+	spin_unlock_irqrestore(&card->dma_lock, irqsave);
+
+	return 0;
+}
+
+/*
+ * audio mute on/off
+ * mute_on : 0 output enabled
+ *           1 mute
+ */
+static int snd_ps3_mute(int mute_on)
+{
+	return ps3av_audio_mute(mute_on);
+}
+
+/*
+ * PCM operators
+ */
+static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	int pcm_index;
+
+	pcm_index = substream->pcm->device;
+	/* to retrieve substream/runtime in interrupt handler */
+	card->substream = substream;
+
+	runtime->hw = snd_ps3_pcm_hw;
+
+	card->start_delay = snd_ps3_start_delay;
+
+	/* mute off */
+	snd_ps3_mute(0); /* this function sleep */
+
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				   PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2);
+	return 0;
+};
+
+static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	size_t size;
+
+	/* alloc transport buffer */
+	size = params_buffer_bytes(hw_params);
+	snd_pcm_lib_malloc_pages(substream, size);
+	return 0;
+};
+
+static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
+				  unsigned int delay_ms)
+{
+	int ret;
+	int rate ;
+
+	rate = substream->runtime->rate;
+	ret = snd_pcm_format_size(substream->runtime->format,
+				  rate * delay_ms / 1000)
+		* substream->runtime->channels;
+
+	pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
+		 __func__,
+		 delay_ms,
+		 rate,
+		 snd_pcm_format_size(substream->runtime->format, rate),
+		 rate * delay_ms / 1000,
+		 ret);
+
+	return ret;
+};
+
+static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	unsigned long irqsave;
+
+	if (!snd_ps3_set_avsetting(substream)) {
+		/* some parameter changed */
+		write_reg(PS3_AUDIO_AX_IE,
+			  PS3_AUDIO_AX_IE_ASOBEIE(0) |
+			  PS3_AUDIO_AX_IE_ASOBUIE(0));
+		/*
+		 * let SPDIF device re-lock with SPDIF signal,
+		 * start with some silence
+		 */
+		card->silent = snd_ps3_delay_to_bytes(substream,
+						      card->start_delay) /
+			(PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */
+	}
+
+	/* restart ring buffer pointer */
+	spin_lock_irqsave(&card->dma_lock, irqsave);
+	{
+		card->dma_buffer_size = runtime->dma_bytes;
+
+		card->dma_last_transfer_vaddr[SND_PS3_CH_L] =
+			card->dma_next_transfer_vaddr[SND_PS3_CH_L] =
+			card->dma_start_vaddr[SND_PS3_CH_L] =
+			runtime->dma_area;
+		card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr;
+
+		card->dma_last_transfer_vaddr[SND_PS3_CH_R] =
+			card->dma_next_transfer_vaddr[SND_PS3_CH_R] =
+			card->dma_start_vaddr[SND_PS3_CH_R] =
+			runtime->dma_area + (runtime->dma_bytes / 2);
+		card->dma_start_bus_addr[SND_PS3_CH_R] =
+			runtime->dma_addr + (runtime->dma_bytes / 2);
+
+		pr_debug("%s: vaddr=%p bus=%#lx\n", __func__,
+			 card->dma_start_vaddr[SND_PS3_CH_L],
+			 card->dma_start_bus_addr[SND_PS3_CH_L]);
+
+	}
+	spin_unlock_irqrestore(&card->dma_lock, irqsave);
+
+	/* ensure the hardware sees the change */
+	mb();
+
+	return 0;
+};
+
+static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
+			       int cmd)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* clear outstanding interrupts  */
+		update_reg(PS3_AUDIO_AX_IS, 0);
+
+		spin_lock(&card->dma_lock);
+		{
+			card->running = 1;
+		}
+		spin_unlock(&card->dma_lock);
+
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+		while (read_reg(PS3_AUDIO_KICK(7)) &
+		       PS3_AUDIO_KICK_STATUS_MASK) {
+			udelay(1);
+		}
+		snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+		snd_ps3_kick_dma(card);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock(&card->dma_lock);
+		{
+			card->running = 0;
+		}
+		spin_unlock(&card->dma_lock);
+		snd_ps3_wait_for_dma_stop(card);
+		break;
+	default:
+		break;
+
+	}
+
+	return ret;
+};
+
+/*
+ * report current pointer
+ */
+static snd_pcm_uframes_t snd_ps3_pcm_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	size_t bytes;
+	snd_pcm_uframes_t ret;
+
+	spin_lock(&card->dma_lock);
+	{
+		bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] -
+				 card->dma_start_vaddr[SND_PS3_CH_L]);
+	}
+	spin_unlock(&card->dma_lock);
+
+	ret = bytes_to_frames(substream->runtime, bytes * 2);
+
+	return ret;
+};
+
+static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	int ret;
+	ret = snd_pcm_lib_free_pages(substream);
+	return ret;
+};
+
+static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
+{
+	/* mute on */
+	snd_ps3_mute(1);
+	return 0;
+};
+
+static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
+{
+	/*
+	 * avsetting driver seems to never change the followings
+	 * so, init them here once
+	 */
+
+	/* no dma interrupt needed */
+	write_reg(PS3_AUDIO_INTR_EN_0, 0);
+
+	/* use every 4 buffer empty interrupt */
+	update_mask_reg(PS3_AUDIO_AX_IC,
+			PS3_AUDIO_AX_IC_AASOIMD_MASK,
+			PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
+
+	/* enable 3wire clocks */
+	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+			~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
+			  PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
+			0);
+	update_reg(PS3_AUDIO_AO_3WMCTRL,
+		   PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
+}
+
+/*
+ * av setting
+ * NOTE: calling this function may generate audio interrupt.
+ */
+static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
+{
+	int ret, retries, i;
+	pr_debug("%s: start\n", __func__);
+
+	ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
+				  card->avs.avs_audio_rate,
+				  card->avs.avs_audio_width,
+				  card->avs.avs_audio_format,
+				  card->avs.avs_audio_source);
+	/*
+	 * Reset the following unwanted settings:
+	 */
+
+	/* disable all 3wire buffers */
+	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+			~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
+			0);
+	wmb(); 	/* ensure the hardware sees the change */
+	/* wait for actually stopped */
+	retries = 1000;
+	while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
+		(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
+	       --retries) {
+		udelay(1);
+	}
+
+	/* reset buffer pointer */
+	for (i = 0; i < 4; i++) {
+		update_reg(PS3_AUDIO_AO_3WCTRL(i),
+			   PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
+		udelay(10);
+	}
+	wmb(); /* ensure the hardware actually start resetting */
+
+	/* enable 3wire#0 buffer */
+	update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
+
+
+	/* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
+	update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
+			~PS3_AUDIO_AO_3WCTRL_ASODF,
+			PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
+	update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
+			~PS3_AUDIO_AO_SPDCTRL_SPODF,
+			PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
+	/* ensure all the setting above is written back to register */
+	wmb();
+	/* avsetting driver altered AX_IE, caller must reset it if you want */
+	pr_debug("%s: end\n", __func__);
+	return ret;
+}
+
+static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
+{
+	int ret;
+	pr_debug("%s: start\n", __func__);
+	card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
+	card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+	card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+	card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
+	card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
+
+	ret = snd_ps3_change_avsetting(card);
+
+	snd_ps3_audio_fixup(card);
+
+	/* to start to generate SPDIF signal, fill data */
+	snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+	snd_ps3_kick_dma(card);
+	pr_debug("%s: end\n", __func__);
+	return ret;
+}
+
+/*
+ *  set sampling rate according to the substream
+ */
+static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	struct snd_ps3_avsetting_info avs;
+
+	avs = card->avs;
+
+	pr_debug("%s: called freq=%d width=%d\n", __func__,
+		 substream->runtime->rate,
+		 snd_pcm_format_width(substream->runtime->format));
+
+	pr_debug("%s: before freq=%d width=%d\n", __func__,
+		 card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+	/* sample rate */
+	switch (substream->runtime->rate) {
+	case 44100:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
+		break;
+	case 48000:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+		break;
+	case 88200:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
+		break;
+	case 96000:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
+		break;
+	default:
+		pr_info("%s: invalid rate %d\n", __func__,
+			substream->runtime->rate);
+		return 1;
+	}
+
+	/* width */
+	switch (snd_pcm_format_width(substream->runtime->format)) {
+	case 16:
+		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+		break;
+	case 24:
+		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
+		break;
+	default:
+		pr_info("%s: invalid width %d\n", __func__,
+			snd_pcm_format_width(substream->runtime->format));
+		return 1;
+	}
+
+	if ((card->avs.avs_audio_width != avs.avs_audio_width) ||
+	    (card->avs.avs_audio_rate != avs.avs_audio_rate)) {
+		card->avs = avs;
+		snd_ps3_change_avsetting(card);
+
+		pr_debug("%s: after freq=%d width=%d\n", __func__,
+			 card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+		return 0;
+	} else
+		return 1;
+}
+
+
+
+static int snd_ps3_map_mmio(void)
+{
+	the_card.mapped_mmio_vaddr =
+		ioremap(the_card.ps3_dev->m_region->bus_addr,
+			the_card.ps3_dev->m_region->len);
+
+	if (!the_card.mapped_mmio_vaddr) {
+		pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n",
+		       __func__, the_card.ps3_dev->m_region->lpar_addr,
+		       the_card.ps3_dev->m_region->len);
+		return -ENXIO;
+	}
+
+	return 0;
+};
+
+static void snd_ps3_unmap_mmio(void)
+{
+	iounmap(the_card.mapped_mmio_vaddr);
+	the_card.mapped_mmio_vaddr = NULL;
+}
+
+static int snd_ps3_allocate_irq(void)
+{
+	int ret;
+	u64 lpar_addr, lpar_size;
+	u64 __iomem *mapped;
+
+	/* FIXME: move this to device_init (H/W probe) */
+
+	/* get irq outlet */
+	ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size);
+	if (ret) {
+		pr_info("%s: device map 1 failed %d\n", __func__,
+			ret);
+		return -ENXIO;
+	}
+
+	mapped = ioremap(lpar_addr, lpar_size);
+	if (!mapped) {
+		pr_info("%s: ioremap 1 failed \n", __func__);
+		return -ENXIO;
+	}
+
+	the_card.audio_irq_outlet = in_be64(mapped);
+
+	iounmap(mapped);
+	ret = lv1_gpu_device_unmap(1);
+	if (ret)
+		pr_info("%s: unmap 1 failed\n", __func__);
+
+	/* irq */
+	ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY,
+				 the_card.audio_irq_outlet,
+				 &the_card.irq_no);
+	if (ret) {
+		pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED,
+			  SND_PS3_DRIVER_NAME, &the_card);
+	if (ret) {
+		pr_info("%s: request_irq failed (%d)\n", __func__, ret);
+		goto cleanup_irq;
+	}
+
+	return 0;
+
+ cleanup_irq:
+	ps3_irq_plug_destroy(the_card.irq_no);
+	return ret;
+};
+
+static void snd_ps3_free_irq(void)
+{
+	free_irq(the_card.irq_no, &the_card);
+	ps3_irq_plug_destroy(the_card.irq_no);
+}
+
+static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
+{
+	uint64_t val;
+	int ret;
+
+	val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) |
+		(0x03UL << 24) |
+		(0x0fUL << 12) |
+		(PS3_AUDIO_IOID);
+
+	ret = lv1_gpu_attribute(0x100, 0x007, val, 0, 0);
+	if (ret)
+		pr_info("%s: gpu_attribute failed %d\n", __func__,
+			ret);
+}
+
+static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
+{
+	int ret;
+	u64 lpar_addr, lpar_size;
+
+	BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1));
+	BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND);
+
+	the_card.ps3_dev = dev;
+
+	ret = ps3_open_hv_device(dev);
+
+	if (ret)
+		return -ENXIO;
+
+	/* setup MMIO */
+	ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size);
+	if (ret) {
+		pr_info("%s: device map 2 failed %d\n", __func__, ret);
+		goto clean_open;
+	}
+	ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size,
+		PAGE_SHIFT);
+
+	ret = snd_ps3_map_mmio();
+	if (ret)
+		goto clean_dev_map;
+
+	/* setup DMA area */
+	ps3_dma_region_init(dev, dev->d_region,
+			    PAGE_SHIFT, /* use system page size */
+			    0, /* dma type; not used */
+			    NULL,
+			    _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
+	dev->d_region->ioid = PS3_AUDIO_IOID;
+
+	ret = ps3_dma_region_create(dev->d_region);
+	if (ret) {
+		pr_info("%s: region_create\n", __func__);
+		goto clean_mmio;
+	}
+
+	snd_ps3_audio_set_base_addr(dev->d_region->bus_addr);
+
+	/* CONFIG_SND_PS3_DEFAULT_START_DELAY */
+	the_card.start_delay = snd_ps3_start_delay;
+
+	/* irq */
+	if (snd_ps3_allocate_irq()) {
+		ret = -ENXIO;
+		goto clean_dma_region;
+	}
+
+	/* create card instance */
+	the_card.card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (!the_card.card) {
+		ret = -ENXIO;
+		goto clean_irq;
+	}
+
+	strcpy(the_card.card->driver, "PS3");
+	strcpy(the_card.card->shortname, "PS3");
+	strcpy(the_card.card->longname, "PS3 sound");
+	/* create PCM devices instance */
+	/* NOTE:this driver works assuming pcm:substream = 1:1 */
+	ret = snd_pcm_new(the_card.card,
+			  "SPDIF",
+			  0, /* instance index, will be stored pcm.device*/
+			  1, /* output substream */
+			  0, /* input substream */
+			  &(the_card.pcm));
+	if (ret)
+		goto clean_card;
+
+	the_card.pcm->private_data = &the_card;
+	strcpy(the_card.pcm->name, "SPDIF");
+
+	/* set pcm ops */
+	snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_ps3_pcm_spdif_ops);
+
+	the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED;
+	/* pre-alloc PCM DMA buffer*/
+	ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm,
+					SNDRV_DMA_TYPE_DEV,
+					&dev->core,
+					SND_PS3_PCM_PREALLOC_SIZE,
+					SND_PS3_PCM_PREALLOC_SIZE);
+	if (ret < 0) {
+		pr_info("%s: prealloc failed\n", __func__);
+		goto clean_card;
+	}
+
+	/*
+	 * allocate null buffer
+	 * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2
+	 * PAGE_SIZE is enogh
+	 */
+	if (!(the_card.null_buffer_start_vaddr =
+	      dma_alloc_coherent(&the_card.ps3_dev->core,
+				 PAGE_SIZE,
+				 &the_card.null_buffer_start_dma_addr,
+				 GFP_KERNEL))) {
+		pr_info("%s: nullbuffer alloc failed\n", __func__);
+		goto clean_preallocate;
+	}
+	pr_debug("%s: null vaddr=%p dma=%#lx\n", __func__,
+		 the_card.null_buffer_start_vaddr,
+		 the_card.null_buffer_start_dma_addr);
+	/* set default sample rate/word width */
+	snd_ps3_init_avsetting(&the_card);
+
+	/* register the card */
+	ret = snd_card_register(the_card.card);
+	if (ret < 0)
+		goto clean_dma_map;
+
+	pr_info("%s started. start_delay=%dms\n",
+		the_card.card->longname, the_card.start_delay);
+	return 0;
+
+clean_dma_map:
+	dma_free_coherent(&the_card.ps3_dev->core,
+			  PAGE_SIZE,
+			  the_card.null_buffer_start_vaddr,
+			  the_card.null_buffer_start_dma_addr);
+clean_preallocate:
+	snd_pcm_lib_preallocate_free_for_all(the_card.pcm);
+clean_card:
+	snd_card_free(the_card.card);
+clean_irq:
+	snd_ps3_free_irq();
+clean_dma_region:
+	ps3_dma_region_free(dev->d_region);
+clean_mmio:
+	snd_ps3_unmap_mmio();
+clean_dev_map:
+	lv1_gpu_device_unmap(2);
+clean_open:
+	ps3_close_hv_device(dev);
+	/*
+	 * there is no destructor function to pcm.
+	 * midlayer automatically releases if the card removed
+	 */
+	return ret;
+}; /* snd_ps3_probe */
+
+/* called when module removal */
+static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
+{
+	int ret;
+	pr_info("%s:start id=%d\n", __func__,  dev->match_id);
+	if (dev->match_id != PS3_MATCH_ID_SOUND)
+		return -ENXIO;
+
+	/*
+	 * ctl and preallocate buffer will be freed in
+	 * snd_card_free
+	 */
+	ret = snd_card_free(the_card.card);
+	if (ret)
+		pr_info("%s: ctl freecard=%d\n", __func__, ret);
+
+	dma_free_coherent(&dev->core,
+			  PAGE_SIZE,
+			  the_card.null_buffer_start_vaddr,
+			  the_card.null_buffer_start_dma_addr);
+
+	ps3_dma_region_free(dev->d_region);
+
+	snd_ps3_free_irq();
+	snd_ps3_unmap_mmio();
+
+	lv1_gpu_device_unmap(2);
+	ps3_close_hv_device(dev);
+	pr_info("%s:end id=%d\n", __func__, dev->match_id);
+	return 0;
+} /* snd_ps3_remove */
+
+static struct ps3_system_bus_driver snd_ps3_bus_driver_info = {
+	.match_id = PS3_MATCH_ID_SOUND,
+	.probe = snd_ps3_driver_probe,
+	.remove = snd_ps3_driver_remove,
+	.shutdown = snd_ps3_driver_remove,
+	.core = {
+		.name = SND_PS3_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+
+/*
+ * Interrupt handler
+ */
+static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
+{
+
+	uint32_t port_intr;
+	int underflow_occured = 0;
+	struct snd_ps3_card_info *card = dev_id;
+
+	if (!card->running) {
+		update_reg(PS3_AUDIO_AX_IS, 0);
+		update_reg(PS3_AUDIO_INTR_0, 0);
+		return IRQ_HANDLED;
+	}
+
+	port_intr = read_reg(PS3_AUDIO_AX_IS);
+	/*
+	 *serial buffer empty detected (every 4 times),
+	 *program next dma and kick it
+	 */
+	if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
+		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
+		if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+			write_reg(PS3_AUDIO_AX_IS, port_intr);
+			underflow_occured = 1;
+		}
+		if (card->silent) {
+			/* we are still in silent time */
+			snd_ps3_program_dma(card,
+				(underflow_occured) ?
+				SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
+				SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+			snd_ps3_kick_dma(card);
+			card->silent --;
+		} else {
+			snd_ps3_program_dma(card,
+				(underflow_occured) ?
+				SND_PS3_DMA_FILLTYPE_FIRSTFILL :
+				SND_PS3_DMA_FILLTYPE_RUNNING);
+			snd_ps3_kick_dma(card);
+			snd_pcm_period_elapsed(card->substream);
+		}
+	} else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
+		/*
+		 * serial out underflow, but buffer empty not detected.
+		 * in this case, fill fifo with 0 to recover.  After
+		 * filling dummy data, serial automatically start to
+		 * consume them and then will generate normal buffer
+		 * empty interrupts.
+		 * If both buffer underflow and buffer empty are occured,
+		 * it is better to do nomal data transfer than empty one
+		 */
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+	}
+	/* clear interrupt cause */
+	return IRQ_HANDLED;
+};
+
+/*
+ * module/subsystem initialize/terminate
+ */
+static int __init snd_ps3_init(void)
+{
+	int ret;
+
+	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+		return -ENXIO;
+
+	memset(&the_card, 0, sizeof(the_card));
+	spin_lock_init(&the_card.dma_lock);
+
+	/* register systembus DRIVER, this calls our probe() func */
+	ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info);
+
+	return ret;
+}
+
+static void __exit snd_ps3_exit(void)
+{
+	ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND);
diff -ruN linux-2.6.22/sound/ppc/snd_ps3.h linux-2.6.22-alsa/sound/ppc/snd_ps3.h
--- linux-2.6.22/sound/ppc/snd_ps3.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/ppc/snd_ps3.h	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,135 @@
+/*
+ * Audio support for PS3
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * All rights reserved.
+ * Copyright 2006, 2007 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the Licence.
+ *
+ * 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
+ */
+
+#if !defined(_SND_PS3_H_)
+#define _SND_PS3_H_
+
+#include <linux/irqreturn.h>
+
+#define SND_PS3_DRIVER_NAME "snd_ps3"
+
+enum snd_ps3_out_channel {
+	SND_PS3_OUT_SPDIF_0,
+	SND_PS3_OUT_SPDIF_1,
+	SND_PS3_OUT_SERIAL_0,
+	SND_PS3_OUT_DEVS
+};
+
+enum snd_ps3_dma_filltype {
+	SND_PS3_DMA_FILLTYPE_FIRSTFILL,
+	SND_PS3_DMA_FILLTYPE_RUNNING,
+	SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL,
+	SND_PS3_DMA_FILLTYPE_SILENT_RUNNING
+};
+
+enum snd_ps3_ch {
+	SND_PS3_CH_L = 0,
+	SND_PS3_CH_R = 1,
+	SND_PS3_CH_MAX = 2
+};
+
+struct snd_ps3_avsetting_info {
+	uint32_t avs_audio_ch;     /* fixed */
+	uint32_t avs_audio_rate;
+	uint32_t avs_audio_width;
+	uint32_t avs_audio_format; /* fixed */
+	uint32_t avs_audio_source; /* fixed */
+};
+/*
+ * PS3 audio 'card' instance
+ * there should be only ONE hardware.
+ */
+struct snd_ps3_card_info {
+	struct ps3_system_bus_device *ps3_dev;
+	struct snd_card *card;
+
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+
+	/* hvc info */
+	u64 audio_lpar_addr;
+	u64 audio_lpar_size;
+
+	/* registers */
+	void __iomem *mapped_mmio_vaddr;
+
+	/* irq */
+	u64 audio_irq_outlet;
+	unsigned int irq_no;
+
+	/* remember avsetting */
+	struct snd_ps3_avsetting_info avs;
+
+	/* dma buffer management */
+	spinlock_t dma_lock;
+		/* dma_lock start */
+		void * dma_start_vaddr[2]; /* 0 for L, 1 for R */
+		dma_addr_t dma_start_bus_addr[2];
+		size_t dma_buffer_size;
+		void * dma_last_transfer_vaddr[2];
+		void * dma_next_transfer_vaddr[2];
+		int    silent;
+		/* dma_lock end */
+
+	int running;
+
+	/* null buffer */
+	void *null_buffer_start_vaddr;
+	dma_addr_t null_buffer_start_dma_addr;
+
+	/* start delay */
+	unsigned int start_delay;
+
+};
+
+
+/* PS3 audio DMAC block size in bytes */
+#define PS3_AUDIO_DMAC_BLOCK_SIZE (128)
+/* one stage (stereo)  of audio FIFO in bytes */
+#define PS3_AUDIO_FIFO_STAGE_SIZE (256)
+/* how many stages the fifo have */
+#define PS3_AUDIO_FIFO_STAGE_COUNT (8)
+/* fifo size 128 bytes * 8 stages * stereo (2ch) */
+#define PS3_AUDIO_FIFO_SIZE \
+	(PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT)
+
+/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/
+#define PS3_AUDIO_DMAC_MAX_BLOCKS  (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1)
+
+#define PS3_AUDIO_NORMAL_DMA_START_CH (0)
+#define PS3_AUDIO_NORMAL_DMA_COUNT    (8)
+#define PS3_AUDIO_NULL_DMA_START_CH \
+	(PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT)
+#define PS3_AUDIO_NULL_DMA_COUNT      (2)
+
+#define SND_PS3_MAX_VOL (0x0F)
+#define SND_PS3_MIN_VOL (0x00)
+#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL
+#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL
+
+#define SND_PS3_PCM_PREALLOC_SIZE \
+	(PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4)
+
+#define SND_PS3_DMA_REGION_SIZE \
+	(SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE)
+
+#define PS3_AUDIO_IOID       (1UL)
+
+#endif /* _SND_PS3_H_ */
diff -ruN linux-2.6.22/sound/ppc/snd_ps3_reg.h linux-2.6.22-alsa/sound/ppc/snd_ps3_reg.h
--- linux-2.6.22/sound/ppc/snd_ps3_reg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/ppc/snd_ps3_reg.h	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,891 @@
+/*
+ * Audio support for PS3
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2006, 2007 Sony Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the License.
+ *
+ * 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
+ */
+
+/*
+ * interrupt / configure registers
+ */
+
+#define PS3_AUDIO_INTR_0                 (0x00000100)
+#define PS3_AUDIO_INTR_EN_0              (0x00000140)
+#define PS3_AUDIO_CONFIG                 (0x00000200)
+
+/*
+ * DMAC registers
+ * n:0..9
+ */
+#define PS3_AUDIO_DMAC_REGBASE(x)         (0x0000210 + 0x20 * (x))
+
+#define PS3_AUDIO_KICK(n)                 (PS3_AUDIO_DMAC_REGBASE(n) + 0x00)
+#define PS3_AUDIO_SOURCE(n)               (PS3_AUDIO_DMAC_REGBASE(n) + 0x04)
+#define PS3_AUDIO_DEST(n)                 (PS3_AUDIO_DMAC_REGBASE(n) + 0x08)
+#define PS3_AUDIO_DMASIZE(n)              (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C)
+
+/*
+ * mute control
+ */
+#define PS3_AUDIO_AX_MCTRL                (0x00004000)
+#define PS3_AUDIO_AX_ISBP                 (0x00004004)
+#define PS3_AUDIO_AX_AOBP                 (0x00004008)
+#define PS3_AUDIO_AX_IC                   (0x00004010)
+#define PS3_AUDIO_AX_IE                   (0x00004014)
+#define PS3_AUDIO_AX_IS                   (0x00004018)
+
+/*
+ * three wire serial
+ * n:0..3
+ */
+#define PS3_AUDIO_AO_MCTRL                (0x00006000)
+#define PS3_AUDIO_AO_3WMCTRL              (0x00006004)
+
+#define PS3_AUDIO_AO_3WCTRL(n)            (0x00006200 + 0x200 * (n))
+
+/*
+ * S/PDIF
+ * n:0..1
+ * x:0..11
+ * y:0..5
+ */
+#define PS3_AUDIO_AO_SPD_REGBASE(n)       (0x00007200 + 0x200 * (n))
+
+#define PS3_AUDIO_AO_SPDCTRL(n) \
+	(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00)
+#define PS3_AUDIO_AO_SPDUB(n, x) \
+	(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x))
+#define PS3_AUDIO_AO_SPDCS(n, y) \
+	(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y))
+
+
+/*
+  PS3_AUDIO_INTR_0 register tells an interrupt handler which audio
+  DMA channel triggered the interrupt.  The interrupt status for a channel
+  can be cleared by writing a '1' to the corresponding bit.  A new interrupt
+  cannot be generated until the previous interrupt has been cleared.
+
+  Note that the status reported by PS3_AUDIO_INTR_0 is independent of the
+  value of PS3_AUDIO_INTR_EN_0.
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+#define PS3_AUDIO_INTR_0_CHAN(n)	(1 << ((n) * 2))
+#define PS3_AUDIO_INTR_0_CHAN9     PS3_AUDIO_INTR_0_CHAN(9)
+#define PS3_AUDIO_INTR_0_CHAN8     PS3_AUDIO_INTR_0_CHAN(8)
+#define PS3_AUDIO_INTR_0_CHAN7     PS3_AUDIO_INTR_0_CHAN(7)
+#define PS3_AUDIO_INTR_0_CHAN6     PS3_AUDIO_INTR_0_CHAN(6)
+#define PS3_AUDIO_INTR_0_CHAN5     PS3_AUDIO_INTR_0_CHAN(5)
+#define PS3_AUDIO_INTR_0_CHAN4     PS3_AUDIO_INTR_0_CHAN(4)
+#define PS3_AUDIO_INTR_0_CHAN3     PS3_AUDIO_INTR_0_CHAN(3)
+#define PS3_AUDIO_INTR_0_CHAN2     PS3_AUDIO_INTR_0_CHAN(2)
+#define PS3_AUDIO_INTR_0_CHAN1     PS3_AUDIO_INTR_0_CHAN(1)
+#define PS3_AUDIO_INTR_0_CHAN0     PS3_AUDIO_INTR_0_CHAN(0)
+
+/*
+  The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate
+  an interrupt to the PU.  Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the
+  corresponding bit in PS3_AUDIO_INTR_0.  The resulting bits are OR'd together
+  to generate the Audio interrupt.
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+  Bit assignments are same as PS3_AUDIO_INTR_0
+*/
+
+/*
+  PS3_AUDIO_CONFIG
+  31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+
+/* The CLEAR field cancels all pending transfers, and stops any running DMA
+   transfers.  Any interrupts associated with the canceled transfers
+   will occur as if the transfer had finished.
+   Since this bit is designed to recover from DMA related issues
+   which are caused by unpredictable situations, it is prefered to wait
+   for normal DMA transfer end without using this bit.
+*/
+#define PS3_AUDIO_CONFIG_CLEAR          (1 << 8)  /* RWIVF */
+
+/*
+  PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/* 3 Wire Audio Serial Output Channel Mutes (0..3)  */
+#define PS3_AUDIO_AX_MCTRL_ASOMT(n)     (1 << (3 - (n)))  /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO3MT       (1 << 0)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO2MT       (1 << 1)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO1MT       (1 << 2)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO0MT       (1 << 3)          /* RWIVF */
+
+/* S/PDIF mutes (0,1)*/
+#define PS3_AUDIO_AX_MCTRL_SPOMT(n)     (1 << (5 - (n)))  /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_SPO1MT       (1 << 4)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_SPO0MT       (1 << 5)          /* RWIVF */
+
+/* All 3 Wire Serial Outputs Mute */
+#define PS3_AUDIO_AX_MCTRL_AASOMT       (1 << 13)         /* RWIVF */
+
+/* All S/PDIF Mute */
+#define PS3_AUDIO_AX_MCTRL_ASPOMT       (1 << 14)         /* RWIVF */
+
+/* All Audio Outputs Mute */
+#define PS3_AUDIO_AX_MCTRL_AAOMT        (1 << 15)         /* RWIVF */
+
+/*
+  S/PDIF Outputs Buffer Read/Write Pointer Register
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+/*
+ S/PDIF Output Channel Read Buffer Numbers
+ Buffer number is  value of field.
+ Indicates current read access buffer ID from Audio Data
+ Transfer controller of S/PDIF Output
+*/
+
+#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK		(0x7 << 0) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK		(0x7 << 4) /* R-IUF */
+
+/*
+S/PDIF Output Channel Buffer Write Numbers
+Indicates current write access buffer ID from bus master.
+*/
+#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 <<  4 * (5 - (n))) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK		(0x7 << 16) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK		(0x7 << 20) /* R-IUF */
+
+/*
+  3 Wire Audio Serial Outputs Buffer Read/Write
+  Pointer Register
+  Buffer number is  value of field
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+3 Wire Audio Serial Output Channel Buffer Read Numbers
+Indicates current read access buffer Id from Audio Data Transfer
+Controller of 3 Wire Audio Serial Output Channels
+*/
+#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */
+
+#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK	(0x7 << 0) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK	(0x7 << 4) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK	(0x7 << 8) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK	(0x7 << 12) /* R-IUF */
+
+/*
+3 Wire Audio Serial Output Channel Buffer Write Numbers
+Indicates current write access buffer ID from bus master.
+*/
+#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */
+
+#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK        (0x7 << 16) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK        (0x7 << 20) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK        (0x7 << 24) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK        (0x7 << 28) /* R-IUF */
+
+
+
+/*
+Audio Port Interrupt Condition Register
+For the fields in this register, the following values apply:
+0 = Interrupt is generated every interrupt event.
+1 = Interrupt is generated every 2 interrupt events.
+2 = Interrupt is generated every 4 interrupt events.
+3 = Reserved
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*
+All 3-Wire Audio Serial Outputs Interrupt Mode
+Configures the Interrupt and Signal Notification
+condition of all 3-wire Audio Serial Outputs.
+*/
+#define PS3_AUDIO_AX_IC_AASOIMD_MASK          (0x3 << 12) /* RWIVF */
+#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1        (0x0 << 12) /* RWI-V */
+#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2        (0x1 << 12) /* RW--V */
+#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4        (0x2 << 12) /* RW--V */
+
+/*
+S/PDIF Output Channel Interrupt Modes
+Configures the Interrupt and signal Notification
+conditions of S/PDIF output channels.
+*/
+#define PS3_AUDIO_AX_IC_SPO1IMD_MASK          (0x3 << 16) /* RWIVF */
+#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1        (0x0 << 16) /* RWI-V */
+#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2        (0x1 << 16) /* RW--V */
+#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4        (0x2 << 16) /* RW--V */
+
+#define PS3_AUDIO_AX_IC_SPO0IMD_MASK          (0x3 << 20) /* RWIVF */
+#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1        (0x0 << 20) /* RWI-V */
+#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2        (0x1 << 20) /* RW--V */
+#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4        (0x2 << 20) /* RW--V */
+
+/*
+Audio Port interrupt Enable Register
+Configures whether to enable or disable each Interrupt Generation.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+
+/*
+3 Wire Audio Serial Output Channel Buffer Underflow
+Interrupt Enables
+Select enable/disable of Buffer Underflow Interrupts for
+3-Wire Audio Serial Output Channels
+DISABLED=Interrupt generation disabled.
+*/
+#define PS3_AUDIO_AX_IE_ASOBUIE(n)      (1 << (3 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO3BUIE        (1 << 0) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO2BUIE        (1 << 1) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO1BUIE        (1 << 2) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO0BUIE        (1 << 3) /* RWIVF */
+
+/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_SPOBUIE(n)      (1 << (7 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO1BUIE        (1 << 6) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO0BUIE        (1 << 7) /* RWIVF */
+
+/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_SPOBTCIE(n)     (1 << (11 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO1BTCIE       (1 << 10) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO0BTCIE       (1 << 11) /* RWIVF */
+
+/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_ASOBEIE(n)      (1 << (19 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO3BEIE        (1 << 16) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO2BEIE        (1 << 17) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO1BEIE        (1 << 18) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO0BEIE        (1 << 19) /* RWIVF */
+
+/* S/PDIF Output Channel Buffer Empty Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_SPOBEIE(n)      (1 << (23 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO1BEIE        (1 << 22) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO0BEIE        (1 << 23) /* RWIVF */
+
+/*
+Audio Port Interrupt Status Register
+Indicates Interrupt status, which interrupt has occured, and can clear
+each interrupt in this register.
+Writing 1b to a field containing 1b clears field and de-asserts interrupt.
+Writing 0b to a field has no effect.
+Field vaules are the following:
+0 - Interrupt hasn't occured.
+1 - Interrupt has occured.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+ Bit assignment are same as AX_IE
+*/
+
+/*
+Audio Output Master Control Register
+Configures Master Clock and other master Audio Output Settings
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0|SCKSE|0|SCKSE|  MR0  |  MR1  |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+MCLK Output Control
+Controls mclko[1] output.
+0 - Disable output (fixed at High)
+1 - Output clock produced by clock selected
+with scksel1 by mr1
+2 - Reserved
+3 - Reserved
+*/
+
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK		(0x3 << 12) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED	(0x0 << 12) /* RWI-V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED	(0x1 << 12) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2	(0x2 << 12) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3	(0x3 << 12) /* RW--V */
+
+/*
+MCLK Output Control
+Controls mclko[0] output.
+0 - Disable output (fixed at High)
+1 - Output clock produced by clock selected
+with SCKSEL0 by MR0
+2 - Reserved
+3 - Reserved
+*/
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK		(0x3 << 14) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED	(0x0 << 14) /* RWI-V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED	(0x1 << 14) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2	(0x2 << 14) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3	(0x3 << 14) /* RW--V */
+/*
+Master Clock Rate 1
+Sets the divide ration of Master Clock1 (clock output from
+mclko[1] for the input clock selected by scksel1.
+*/
+#define PS3_AUDIO_AO_MCTRL_MR1_MASK	(0xf << 16)
+#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT	(0x0 << 16) /* RWI-V */
+/*
+Master Clock Rate 0
+Sets the divide ratio of Master Clock0 (clock output from
+mclko[0] for the input clock selected by scksel0).
+*/
+#define PS3_AUDIO_AO_MCTRL_MR0_MASK	(0xf << 20) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT	(0x0 << 20) /* RWI-V */
+/*
+System Clock Select 0/1
+Selects the system clock to be used as Master Clock 0/1
+Input the system clock that is appropriate for the sampling
+rate.
+*/
+#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK		(0x7 << 24) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT	(0x2 << 24) /* RWI-V */
+
+#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK		(0x7 << 28) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT	(0x2 << 28) /* RWI-V */
+
+
+/*
+3-Wire Audio Output Master Control Register
+Configures clock, 3-Wire Audio Serial Output Enable, and
+other 3-Wire Audio Serial Output Master Settings
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+
+/*
+LRCKO Polarity
+0 - Reserved
+1 - default
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK 		(1 << 8) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT	(1 << 8) /* RW--V */
+
+/* LRCK Output Disable */
+
+#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD		(1 << 10) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED	(0 << 10) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED	(1 << 10) /* RWI-V */
+
+/* Bit Clock Output Disable */
+
+#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD		(1 << 11) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED	(0 << 11) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED	(1 << 11) /* RWI-V */
+
+/*
+3-Wire Audio Serial Output Channel 0-3 Operational
+Status.  Each bit becomes 1 after each 3-Wire Audio
+Serial Output Channel N is in action by setting 1 to
+asoen.
+Each bit becomes 0 after each 3-Wire Audio Serial Output
+Channel N is out of action by setting 0 to asoen.
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n)		(1 << (15 - (n))) /* R-IVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n)	(0 << (15 - (n))) /* R-I-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n)	(1 << (15 - (n))) /* R---V */
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN0		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(0)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN1		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(1)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN2		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(2)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN3		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(3)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3)
+
+/*
+Sampling Rate
+Specifies the divide ratio of the bit clock (clock output
+from bclko) used by the 3-wire Audio Output Clock, whcih
+is applied to the master clock selected by mcksel.
+Data output is synchronized with this clock.
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK		(0xf << 20) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2		(0x1 << 20) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4		(0x2 << 20) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8		(0x4 << 20) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12	(0x6 << 20) /* RW--V */
+
+/*
+Master Clock Select
+0 - Master Clock 0
+1 - Master Clock 1
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL		(1 << 24) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0	(0 << 24) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1	(1 << 24) /* RW--V */
+
+/*
+Enables and disables 4ch 3-Wire Audio Serial Output
+operation.  Each Bit from 0 to 3 corresponds to an
+output channel, which means that each output channel
+can be enabled or disabled individually.  When
+multiple channels are enabled at the same time, output
+operations are performed in synchronization.
+Bit 0 - Output Channel 0 (SDOUT[0])
+Bit 1 - Output Channel 1 (SDOUT[1])
+Bit 2 - Output Channel 2 (SDOUT[2])
+Bit 3 - Output Channel 3 (SDOUT[3])
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n)		(1 << (31 - (n))) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n)	(0 << (31 - (n))) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n)	(1 << (31 - (n))) /* RW--V */
+
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */
+#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */
+#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */
+#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */
+#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */
+#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */
+#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */
+#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */
+#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */
+#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */
+
+/*
+3-Wire Audio Serial output Channel 0-3 Control Register
+Configures settings for 3-Wire Serial Audio Output Channel 0-3
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+/*
+Data Bit Mode
+Specifies the number of data bits
+0 - 16 bits
+1 - reserved
+2 - 20 bits
+3 - 24 bits
+*/
+#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK	(0x3 << 8) /* RWIVF */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT	(0x0 << 8) /* RWI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD	(0x1 << 8) /* RWI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT	(0x2 << 8) /* RW--V */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT	(0x3 << 8) /* RW--V */
+/*
+Data Format Mode
+Specifies the data format where (LSB side or MSB) the data(in 20 bit
+or 24 bit resolution mode) is put in a 32 bit field.
+0 - Data put on LSB side
+1 - Data put on MSB side
+*/
+#define PS3_AUDIO_AO_3WCTRL_ASODF 	(1 << 11) /* RWIVF */
+#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB	(0 << 11) /* RWI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB	(1 << 11) /* RW--V */
+/*
+Buffer Reset
+Performs buffer reset.  Writing 1 to this bit initializes the
+corresponding 3-Wire Audio Output buffers(both L and R).
+*/
+#define PS3_AUDIO_AO_3WCTRL_ASOBRST 		(1 << 16) /* CWIVF */
+#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE	(0 << 16) /* -WI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET	(1 << 16) /* -W--T */
+
+/*
+S/PDIF Audio Output Channel 0/1 Control Register
+Configures settings for S/PDIF Audio Output Channel 0/1.
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*
+Buffer reset.  Writing 1 to this bit initializes the
+corresponding S/PDIF output buffer pointer.
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOBRST		(1 << 0) /* CWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE	(0 << 0) /* -WI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET	(1 << 0) /* -W--T */
+
+/*
+Data Bit Mode
+Specifies number of data bits
+0 - 16 bits
+1 - Reserved
+2 - 20 bits
+3 - 24 bits
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK		(0x3 << 8) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT	(0x0 << 8) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD	(0x1 << 8) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT	(0x2 << 8) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT	(0x3 << 8) /* RW--V */
+/*
+Data format Mode
+Specifies the data format, where (LSB side or MSB)
+the data(in 20 or 24 bit resolution) is put in the
+32 bit field.
+0 - LSB Side
+1 - MSB Side
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPODF	(1 << 11) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB	(0 << 11) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB	(1 << 11) /* RW--V */
+/*
+Source Select
+Specifies the source of the S/PDIF output.  When 0, output
+operation is controlled by 3wen[0] of AO_3WMCTRL register.
+The SR must have the same setting as the a0_3wmctrl reg.
+0 - 3-Wire Audio OUT Ch0 Buffer
+1 - S/PDIF buffer
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK		(0x3 << 16) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN		(0x0 << 16) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF	(0x1 << 16) /* RW--V */
+/*
+Sampling Rate
+Specifies the divide ratio of the bit clock (clock output
+from bclko) used by the S/PDIF Output Clock, which
+is applied to the master clock selected by mcksel.
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR		(0xf << 20) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2		(0x1 << 20) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4		(0x2 << 20) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8		(0x4 << 20) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12	(0x6 << 20) /* RW--V */
+/*
+Master Clock Select
+0 - Master Clock 0
+1 - Master Clock 1
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL		(1 << 24) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0	(0 << 24) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1	(1 << 24) /* RW--V */
+
+/*
+S/PDIF Output Channel Operational Status
+This bit becomes 1 after S/PDIF Output Channel is in
+action by setting 1 to spoen.  This bit becomes 0
+after S/PDIF Output Channel is out of action by setting
+0 to spoen.
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPORUN		(1 << 27) /* R-IVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED	(0 << 27) /* R-I-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING	(1 << 27) /* R---V */
+
+/*
+S/PDIF Audio Output Channel Output Enable
+Enables and disables output operation.  This bit is used
+only when sposs = 1
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOEN		(1 << 31) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED	(0 << 31) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED	(1 << 31) /* RW--V */
+
+/*
+S/PDIF Audio Output Channel Channel Status
+Setting Registers.
+Configures channel status bit settings for each block
+(192 bits).
+Output is performed from the MSB(AO_SPDCS0 register bit 31).
+The same value is added for subframes within the same frame.
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                             SPOCS                             | AO_SPDCS
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+S/PDIF Audio Output Channel User Bit Setting
+Configures user bit settings for each block (384 bits).
+Output is performed from the MSB(ao_spdub0 register bit 31).
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                             SPOUB                             | AO_SPDUB
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*****************************************************************************
+ *
+ * DMAC register
+ *
+ *****************************************************************************/
+/*
+The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor
+its status
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0|STATU|0 0 0|  EVENT  |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*
+The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT
+occurs.
+It will return to the DONE state when the request is completed.
+The registers for a DMA channel should only be written if REQUEST is IDLE.
+*/
+
+#define PS3_AUDIO_KICK_REQUEST                (1 << 0) /* RWIVF */
+#define PS3_AUDIO_KICK_REQUEST_IDLE           (0 << 0) /* RWI-V */
+#define PS3_AUDIO_KICK_REQUEST_ACTIVE         (1 << 0) /* -W--T */
+
+/*
+ *The EVENT field is used to set the event in which
+ *the DMA request becomes active.
+ */
+#define PS3_AUDIO_KICK_EVENT_MASK             (0x1f << 16) /* RWIVF */
+#define PS3_AUDIO_KICK_EVENT_ALWAYS           (0x00 << 16) /* RWI-V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW	(0x02 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY		(0x03 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW	(0x04 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY		(0x05 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW	(0x06 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY		(0x07 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW	(0x08 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \
+	(0x09 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW		(0x0A << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY		(0x0B << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \
+	(0x0C << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW		(0x0D << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY		(0x0E << 16) /* RW--V */
+
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \
+	((0x13 + (n)) << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0         (0x13 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1         (0x14 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2         (0x15 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3         (0x16 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4         (0x17 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5         (0x18 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6         (0x19 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7         (0x1A << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8         (0x1B << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9         (0x1C << 16) /* RW--V */
+
+/*
+The STATUS field can be used to monitor the progress of a DMA request.
+DONE indicates the previous request has completed.
+EVENT indicates that the DMA engine is waiting for the EVENT to occur.
+PENDING indicates that the DMA engine has not started processing this
+request, but the EVENT has occured.
+DMA indicates that the data transfer is in progress.
+NOTIFY indicates that the notifier signalling end of transfer is being written.
+CLEAR indicated that the previous transfer was cleared.
+ERROR indicates the previous transfer requested an unsupported
+source/destination combination.
+*/
+
+#define PS3_AUDIO_KICK_STATUS_MASK	(0x7 << 24) /* R-IVF */
+#define PS3_AUDIO_KICK_STATUS_DONE	(0x0 << 24) /* R-I-V */
+#define PS3_AUDIO_KICK_STATUS_EVENT	(0x1 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_PENDING	(0x2 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_DMA	(0x3 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_NOTIFY	(0x4 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_CLEAR	(0x5 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_ERROR	(0x6 << 24) /* R---V */
+
+/*
+The PS3_AUDIO_SOURCE register specifies the source address for transfers.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                      START                      |0 0 0 0 0|TAR| SOURCE
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
+to a 128 byte boundary.  The low seven bits are assumed to be 0.
+*/
+
+#define PS3_AUDIO_SOURCE_START_MASK	(0x01FFFFFF << 7) /* RWIUF */
+
+/*
+The TARGET field specifies the memory space containing the source address.
+*/
+
+#define PS3_AUDIO_SOURCE_TARGET_MASK 		(3 << 0) /* RWIVF */
+#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY	(2 << 0) /* RW--V */
+
+/*
+The PS3_AUDIO_DEST register specifies the destination address for transfers.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                      START                      |0 0 0 0 0|TAR| DEST
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
+to a 128 byte boundary.  The low seven bits are assumed to be 0.
+*/
+
+#define PS3_AUDIO_DEST_START_MASK	(0x01FFFFFF << 7) /* RWIUF */
+
+/*
+The TARGET field specifies the memory space containing the destination address
+AUDIOFIFO = Audio WriteData FIFO,
+*/
+
+#define PS3_AUDIO_DEST_TARGET_MASK		(3 << 0) /* RWIVF */
+#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO		(1 << 0) /* RW--V */
+
+/*
+PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer.
+So a value of 0 means 128-bytes will get transfered.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|   BLOCKS    | DMASIZE
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+
+#define PS3_AUDIO_DMASIZE_BLOCKS_MASK 	(0x7f << 0) /* RWIUF */
+
+/*
+ * source/destination address for internal fifos
+ */
+#define PS3_AUDIO_AO_3W_LDATA(n)	(0x1000 + (0x100 * (n)))
+#define PS3_AUDIO_AO_3W_RDATA(n)	(0x1080 + (0x100 * (n)))
+
+#define PS3_AUDIO_AO_SPD_DATA(n)	(0x2000 + (0x400 * (n)))
+
+
+/*
+ * field attiribute
+ *
+ *	Read
+ *	  ' ' = Other Information
+ *	  '-' = Field is part of a write-only register
+ *	  'C' = Value read is always the same, constant value line follows (C)
+ *	  'R' = Value is read
+ *
+ *	Write
+ *	  ' ' = Other Information
+ *	  '-' = Must not be written (D), value ignored when written (R,A,F)
+ *	  'W' = Can be written
+ *
+ *	Internal State
+ *	  ' ' = Other Information
+ *	  '-' = No internal state
+ *	  'X' = Internal state, initial value is unknown
+ *	  'I' = Internal state, initial value is known and follows (I)
+ *
+ *	Declaration/Size
+ *	  ' ' = Other Information
+ *	  '-' = Does Not Apply
+ *	  'V' = Type is void
+ *	  'U' = Type is unsigned integer
+ *	  'S' = Type is signed integer
+ *	  'F' = Type is IEEE floating point
+ *	  '1' = Byte size (008)
+ *	  '2' = Short size (016)
+ *	  '3' = Three byte size (024)
+ *	  '4' = Word size (032)
+ *	  '8' = Double size (064)
+ *
+ *	Define Indicator
+ *	  ' ' = Other Information
+ *	  'D' = Device
+ *	  'M' = Memory
+ *	  'R' = Register
+ *	  'A' = Array of Registers
+ *	  'F' = Field
+ *	  'V' = Value
+ *	  'T' = Task
+ */
+
diff -ruN linux-2.6.22/sound/sh/Kconfig linux-2.6.22-alsa/sound/sh/Kconfig
--- linux-2.6.22/sound/sh/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/sh/Kconfig	2007-07-22 17:32:34.000000000 +0200
@@ -0,0 +1,14 @@
+# ALSA SH drivers
+
+menu "SUPERH devices"
+	depends on SND!=n && SUPERH
+
+config SND_AICA
+	tristate "Dreamcast Yamaha AICA sound"
+	depends on SH_DREAMCAST && SND
+	select SND_PCM
+	help
+	  ALSA Sound driver for the SEGA Dreamcast console.
+
+endmenu
+
diff -ruN linux-2.6.22/sound/sh/Makefile linux-2.6.22-alsa/sound/sh/Makefile
--- linux-2.6.22/sound/sh/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/sh/Makefile	2007-07-22 17:32:34.000000000 +0200
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+#
+
+snd-aica-objs := aica.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_AICA) += snd-aica.o
diff -ruN linux-2.6.22/sound/sh/aica.c linux-2.6.22-alsa/sound/sh/aica.c
--- linux-2.6.22/sound/sh/aica.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/sh/aica.c	2007-07-22 17:32:34.000000000 +0200
@@ -0,0 +1,665 @@
+/*
+* This code is licenced under 
+* the General Public Licence
+* version 2
+*
+* Copyright Adrian McMenamin 2005, 2006, 2007
+* <adrian@mcmen.demon.co.uk>
+* Requires firmware (BSD licenced) available from:
+* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
+* or the maintainer
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of version 2 of the GNU General Public License as published by
+* the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*/
+
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/dreamcast/sysasic.h>
+#include "aica.h"
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
+MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
+
+/* module parameters */
+#define CARD_NAME "AICA"
+static int index = -1;
+static char *id;
+static int enable = 1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param(enable, bool, 0644);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+/* Use workqueue */
+static struct workqueue_struct *aica_queue;
+
+/* Simple platform device */
+static struct platform_device *pd;
+static struct resource aica_memory_space[2] = {
+	{
+	 .name = "AICA ARM CONTROL",
+	 .start = ARM_RESET_REGISTER,
+	 .flags = IORESOURCE_MEM,
+	 .end = ARM_RESET_REGISTER + 3,
+	 },
+	{
+	 .name = "AICA Sound RAM",
+	 .start = SPU_MEMORY_BASE,
+	 .flags = IORESOURCE_MEM,
+	 .end = SPU_MEMORY_BASE + 0x200000 - 1,
+	 },
+};
+
+/* SPU specific functions */
+/* spu_write_wait - wait for G2-SH FIFO to clear */
+static void spu_write_wait(void)
+{
+	int time_count;
+	time_count = 0;
+	while (1) {
+		if (!(readl(G2_FIFO) & 0x11))
+			break;
+		/* To ensure hardware failure doesn't wedge kernel */
+		time_count++;
+		if (time_count > 0x10000) {
+			snd_printk
+			    ("WARNING: G2 FIFO appears to be blocked.\n");
+			break;
+		}
+	}
+}
+
+/* spu_memset - write to memory in SPU address space */
+static void spu_memset(u32 toi, u32 what, int length)
+{
+	int i;
+	snd_assert(length % 4 == 0, return);
+	for (i = 0; i < length; i++) {
+		if (!(i % 8))
+			spu_write_wait();
+		writel(what, toi + SPU_MEMORY_BASE);
+		toi++;
+	}
+}
+
+/* spu_memload - write to SPU address space */
+static void spu_memload(u32 toi, void *from, int length)
+{
+	u32 *froml = from;
+	u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
+	int i;
+	u32 val;
+	length = DIV_ROUND_UP(length, 4);
+	spu_write_wait();
+	for (i = 0; i < length; i++) {
+		if (!(i % 8))
+			spu_write_wait();
+		val = *froml;
+		writel(val, to);
+		froml++;
+		to++;
+	}
+}
+
+/* spu_disable - set spu registers to stop sound output */
+static void spu_disable(void)
+{
+	int i;
+	u32 regval;
+	spu_write_wait();
+	regval = readl(ARM_RESET_REGISTER);
+	regval |= 1;
+	spu_write_wait();
+	writel(regval, ARM_RESET_REGISTER);
+	for (i = 0; i < 64; i++) {
+		spu_write_wait();
+		regval = readl(SPU_REGISTER_BASE + (i * 0x80));
+		regval = (regval & ~0x4000) | 0x8000;
+		spu_write_wait();
+		writel(regval, SPU_REGISTER_BASE + (i * 0x80));
+	}
+}
+
+/* spu_enable - set spu registers to enable sound output */
+static void spu_enable(void)
+{
+	u32 regval = readl(ARM_RESET_REGISTER);
+	regval &= ~1;
+	spu_write_wait();
+	writel(regval, ARM_RESET_REGISTER);
+}
+
+/* 
+ * Halt the sound processor, clear the memory,
+ * load some default ARM7 code, and then restart ARM7
+*/
+static void spu_reset(void)
+{
+	spu_disable();
+	spu_memset(0, 0, 0x200000 / 4);
+	/* Put ARM7 in endless loop */
+	ctrl_outl(0xea000002, SPU_MEMORY_BASE);
+	spu_enable();
+}
+
+/* aica_chn_start - write to spu to start playback */
+static void aica_chn_start(void)
+{
+	spu_write_wait();
+	writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
+}
+
+/* aica_chn_halt - write to spu to halt playback */
+static void aica_chn_halt(void)
+{
+	spu_write_wait();
+	writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
+}
+
+/* ALSA code below */
+static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
+	.info = (SNDRV_PCM_INFO_NONINTERLEAVED),
+	.formats =
+	    (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
+	     SNDRV_PCM_FMTBIT_IMA_ADPCM),
+	.rates = SNDRV_PCM_RATE_8000_48000,
+	.rate_min = 8000,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.buffer_bytes_max = AICA_BUFFER_SIZE,
+	.period_bytes_min = AICA_PERIOD_SIZE,
+	.period_bytes_max = AICA_PERIOD_SIZE,
+	.periods_min = AICA_PERIOD_NUMBER,
+	.periods_max = AICA_PERIOD_NUMBER,
+};
+
+static int aica_dma_transfer(int channels, int buffer_size,
+			     struct snd_pcm_substream *substream)
+{
+	int q, err, period_offset;
+	struct snd_card_aica *dreamcastcard;
+	struct snd_pcm_runtime *runtime;
+	err = 0;
+	dreamcastcard = substream->pcm->private_data;
+	period_offset = dreamcastcard->clicks;
+	period_offset %= (AICA_PERIOD_NUMBER / channels);
+	runtime = substream->runtime;
+	for (q = 0; q < channels; q++) {
+		err = dma_xfer(AICA_DMA_CHANNEL,
+			       (unsigned long) (runtime->dma_area +
+						(AICA_BUFFER_SIZE * q) /
+						channels +
+						AICA_PERIOD_SIZE *
+						period_offset),
+			       AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
+			       AICA_PERIOD_SIZE * period_offset,
+			       buffer_size / channels, AICA_DMA_MODE);
+		if (unlikely(err < 0))
+			break;
+		dma_wait_for_completion(AICA_DMA_CHANNEL);
+	}
+	return err;
+}
+
+static void startup_aica(struct snd_card_aica *dreamcastcard)
+{
+	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
+		    dreamcastcard->channel, sizeof(struct aica_channel));
+	aica_chn_start();
+}
+
+static void run_spu_dma(struct work_struct *work)
+{
+	int buffer_size;
+	struct snd_pcm_runtime *runtime;
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard =
+	    container_of(work, struct snd_card_aica, spu_dma_work);
+	runtime = dreamcastcard->substream->runtime;
+	if (unlikely(dreamcastcard->dma_check == 0)) {
+		buffer_size =
+		    frames_to_bytes(runtime, runtime->buffer_size);
+		if (runtime->channels > 1)
+			dreamcastcard->channel->flags |= 0x01;
+		aica_dma_transfer(runtime->channels, buffer_size,
+				  dreamcastcard->substream);
+		startup_aica(dreamcastcard);
+		dreamcastcard->clicks =
+		    buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
+		return;
+	} else {
+		aica_dma_transfer(runtime->channels,
+				  AICA_PERIOD_SIZE * runtime->channels,
+				  dreamcastcard->substream);
+		snd_pcm_period_elapsed(dreamcastcard->substream);
+		dreamcastcard->clicks++;
+		if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
+			dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
+		mod_timer(&dreamcastcard->timer, jiffies + 1);
+	}
+}
+
+static void aica_period_elapsed(unsigned long timer_var)
+{
+	/*timer function - so cannot sleep */
+	int play_period;
+	struct snd_pcm_runtime *runtime;
+	struct snd_pcm_substream *substream;
+	struct snd_card_aica *dreamcastcard;
+	substream = (struct snd_pcm_substream *) timer_var;
+	runtime = substream->runtime;
+	dreamcastcard = substream->pcm->private_data;
+	/* Have we played out an additional period? */
+	play_period =
+	    frames_to_bytes(runtime,
+			    readl
+			    (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
+	    AICA_PERIOD_SIZE;
+	if (play_period == dreamcastcard->current_period) {
+		/* reschedule the timer */
+		mod_timer(&(dreamcastcard->timer), jiffies + 1);
+		return;
+	}
+	if (runtime->channels > 1)
+		dreamcastcard->current_period = play_period;
+	if (unlikely(dreamcastcard->dma_check == 0))
+		dreamcastcard->dma_check = 1;
+	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+}
+
+static void spu_begin_dma(struct snd_pcm_substream *substream)
+{
+	struct snd_card_aica *dreamcastcard;
+	struct snd_pcm_runtime *runtime;
+	runtime = substream->runtime;
+	dreamcastcard = substream->pcm->private_data;
+	/*get the queue to do the work */
+	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+	/* Timer may already be running */
+	if (unlikely(dreamcastcard->timer.data)) {
+		mod_timer(&dreamcastcard->timer, jiffies + 4);
+		return;
+	}
+	init_timer(&(dreamcastcard->timer));
+	dreamcastcard->timer.data = (unsigned long) substream;
+	dreamcastcard->timer.function = aica_period_elapsed;
+	dreamcastcard->timer.expires = jiffies + 4;
+	add_timer(&(dreamcastcard->timer));
+}
+
+static int snd_aicapcm_pcm_open(struct snd_pcm_substream
+				*substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct aica_channel *channel;
+	struct snd_card_aica *dreamcastcard;
+	if (!enable)
+		return -ENOENT;
+	dreamcastcard = substream->pcm->private_data;
+	channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
+	if (!channel)
+		return -ENOMEM;
+	/* set defaults for channel */
+	channel->sfmt = SM_8BIT;
+	channel->cmd = AICA_CMD_START;
+	channel->vol = dreamcastcard->master_volume;
+	channel->pan = 0x80;
+	channel->pos = 0;
+	channel->flags = 0;	/* default to mono */
+	dreamcastcard->channel = channel;
+	runtime = substream->runtime;
+	runtime->hw = snd_pcm_aica_playback_hw;
+	spu_enable();
+	dreamcastcard->clicks = 0;
+	dreamcastcard->current_period = 0;
+	dreamcastcard->dma_check = 0;
+	return 0;
+}
+
+static int snd_aicapcm_pcm_close(struct snd_pcm_substream
+				 *substream)
+{
+	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
+	flush_workqueue(aica_queue);
+	if (dreamcastcard->timer.data)
+		del_timer(&dreamcastcard->timer);
+	kfree(dreamcastcard->channel);
+	spu_disable();
+	return 0;
+}
+
+static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
+				   *substream)
+{
+	/* Free the DMA buffer */
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
+				     *substream, struct snd_pcm_hw_params
+				     *hw_params)
+{
+	/* Allocate a DMA buffer using ALSA built-ins */
+	return
+	    snd_pcm_lib_malloc_pages(substream,
+				     params_buffer_bytes(hw_params));
+}
+
+static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
+				   *substream)
+{
+	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
+	if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
+		dreamcastcard->channel->sfmt = SM_16BIT;
+	dreamcastcard->channel->freq = substream->runtime->rate;
+	dreamcastcard->substream = substream;
+	return 0;
+}
+
+static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
+				   *substream, int cmd)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spu_begin_dma(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		aica_chn_halt();
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
+					     *substream)
+{
+	return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
+}
+
+static struct snd_pcm_ops snd_aicapcm_playback_ops = {
+	.open = snd_aicapcm_pcm_open,
+	.close = snd_aicapcm_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_aicapcm_pcm_hw_params,
+	.hw_free = snd_aicapcm_pcm_hw_free,
+	.prepare = snd_aicapcm_pcm_prepare,
+	.trigger = snd_aicapcm_pcm_trigger,
+	.pointer = snd_aicapcm_pcm_pointer,
+};
+
+/* TO DO: set up to handle more than one pcm instance */
+static int __init snd_aicapcmchip(struct snd_card_aica
+				  *dreamcastcard, int pcm_index)
+{
+	struct snd_pcm *pcm;
+	int err;
+	/* AICA has no capture ability */
+	err =
+	    snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
+			&pcm);
+	if (unlikely(err < 0))
+		return err;
+	pcm->private_data = dreamcastcard;
+	strcpy(pcm->name, "AICA PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_aicapcm_playback_ops);
+	/* Allocate the DMA buffers */
+	err =
+	    snd_pcm_lib_preallocate_pages_for_all(pcm,
+						  SNDRV_DMA_TYPE_CONTINUOUS,
+						  snd_dma_continuous_data
+						  (GFP_KERNEL),
+						  AICA_BUFFER_SIZE,
+						  AICA_BUFFER_SIZE);
+	return err;
+}
+
+/* Mixer controls */
+static int aica_pcmswitch_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;
+}
+
+static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 1;	/* TO DO: Fix me */
+	return 0;
+}
+
+static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	if (ucontrol->value.integer.value[0] == 1)
+		return 0;	/* TO DO: Fix me */
+	else
+		aica_chn_halt();
+	return 0;
+}
+
+static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xFF;
+	return 0;
+}
+
+static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = kcontrol->private_data;
+	if (unlikely(!dreamcastcard->channel))
+		return -ETXTBSY;	/* we've not yet been set up */
+	ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
+	return 0;
+}
+
+static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = kcontrol->private_data;
+	if (unlikely(!dreamcastcard->channel))
+		return -ETXTBSY;
+	if (unlikely(dreamcastcard->channel->vol ==
+		     ucontrol->value.integer.value[0]))
+		return 0;
+	dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
+	dreamcastcard->master_volume = ucontrol->value.integer.value[0];
+	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
+		    dreamcastcard->channel, sizeof(struct aica_channel));
+	return 1;
+}
+
+static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Switch",
+	.index = 0,
+	.info = aica_pcmswitch_info,
+	.get = aica_pcmswitch_get,
+	.put = aica_pcmswitch_put
+};
+
+static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Volume",
+	.index = 0,
+	.info = aica_pcmvolume_info,
+	.get = aica_pcmvolume_get,
+	.put = aica_pcmvolume_put
+};
+
+static int load_aica_firmware(void)
+{
+	int err;
+	const struct firmware *fw_entry;
+	spu_reset();
+	err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
+	if (unlikely(err))
+		return err;
+	/* write firware into memory */
+	spu_disable();
+	spu_memload(0, fw_entry->data, fw_entry->size);
+	spu_enable();
+	release_firmware(fw_entry);
+	return err;
+}
+
+static int __devinit add_aicamixer_controls(struct snd_card_aica
+					    *dreamcastcard)
+{
+	int err;
+	err = snd_ctl_add
+	    (dreamcastcard->card,
+	     snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
+	if (unlikely(err < 0))
+		return err;
+	err = snd_ctl_add
+	    (dreamcastcard->card,
+	     snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
+	if (unlikely(err < 0))
+		return err;
+	return 0;
+}
+
+static int snd_aica_remove(struct platform_device *devptr)
+{
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = platform_get_drvdata(devptr);
+	if (unlikely(!dreamcastcard))
+		return -ENODEV;
+	snd_card_free(dreamcastcard->card);
+	kfree(dreamcastcard);
+	platform_set_drvdata(devptr, NULL);
+	return 0;
+}
+
+static int __init snd_aica_probe(struct platform_device *devptr)
+{
+	int err;
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
+	if (unlikely(!dreamcastcard))
+		return -ENOMEM;
+	dreamcastcard->card =
+	    snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
+	if (unlikely(!dreamcastcard->card)) {
+		kfree(dreamcastcard);
+		return -ENODEV;
+	}
+	strcpy(dreamcastcard->card->driver, "snd_aica");
+	strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
+	strcpy(dreamcastcard->card->longname,
+	       "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
+	/* Prepare to use the queue */
+	INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
+	/* Load the PCM 'chip' */
+	err = snd_aicapcmchip(dreamcastcard, 0);
+	if (unlikely(err < 0))
+		goto freedreamcast;
+	snd_card_set_dev(dreamcastcard->card, &devptr->dev);
+	dreamcastcard->timer.data = 0;
+	dreamcastcard->channel = NULL;
+	/* Add basic controls */
+	err = add_aicamixer_controls(dreamcastcard);
+	if (unlikely(err < 0))
+		goto freedreamcast;
+	/* Register the card with ALSA subsystem */
+	err = snd_card_register(dreamcastcard->card);
+	if (unlikely(err < 0))
+		goto freedreamcast;
+	platform_set_drvdata(devptr, dreamcastcard);
+	aica_queue = create_workqueue(CARD_NAME);
+	if (unlikely(!aica_queue))
+		goto freedreamcast;
+	snd_printk
+	    ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
+	return 0;
+      freedreamcast:
+	snd_card_free(dreamcastcard->card);
+	kfree(dreamcastcard);
+	return err;
+}
+
+static struct platform_driver snd_aica_driver = {
+	.probe = snd_aica_probe,
+	.remove = snd_aica_remove,
+	.driver = {
+		   .name = SND_AICA_DRIVER},
+};
+
+static int __init aica_init(void)
+{
+	int err;
+	err = platform_driver_register(&snd_aica_driver);
+	if (unlikely(err < 0))
+		return err;
+	pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
+					     aica_memory_space, 2);
+	if (unlikely(IS_ERR(pd))) {
+		platform_driver_unregister(&snd_aica_driver);
+		return PTR_ERR(pd);
+	}
+	/* Load the firmware */
+	return load_aica_firmware();
+}
+
+static void __exit aica_exit(void)
+{
+	/* Destroy the aica kernel thread            *
+	 * being extra cautious to check if it exists*/
+	if (likely(aica_queue))
+		destroy_workqueue(aica_queue);
+	platform_device_unregister(pd);
+	platform_driver_unregister(&snd_aica_driver);
+	/* Kill any sound still playing and reset ARM7 to safe state */
+	spu_reset();
+}
+
+module_init(aica_init);
+module_exit(aica_exit);
diff -ruN linux-2.6.22/sound/sh/aica.h linux-2.6.22-alsa/sound/sh/aica.h
--- linux-2.6.22/sound/sh/aica.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/sh/aica.h	2007-07-22 17:32:34.000000000 +0200
@@ -0,0 +1,81 @@
+/* aica.h
+ * Header file for ALSA driver for
+ * Sega Dreamcast Yamaha AICA sound
+ * Copyright Adrian McMenamin
+ * <adrian@mcmen.demon.co.uk>
+ * 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* SPU memory and register constants etc */
+#define G2_FIFO 0xa05f688c
+#define SPU_MEMORY_BASE 0xA0800000
+#define ARM_RESET_REGISTER 0xA0702C00
+#define SPU_REGISTER_BASE 0xA0700000
+
+/* AICA channels stuff */
+#define AICA_CONTROL_POINT 0xA0810000
+#define AICA_CONTROL_CHANNEL_SAMPLE_NUMBER 0xA0810008
+#define AICA_CHANNEL0_CONTROL_OFFSET 0x10004
+
+/* Command values */
+#define AICA_CMD_KICK 0x80000000
+#define AICA_CMD_NONE 0
+#define AICA_CMD_START 1
+#define AICA_CMD_STOP 2
+#define AICA_CMD_VOL 3
+
+/* Sound modes */
+#define SM_8BIT		1
+#define SM_16BIT	0
+#define SM_ADPCM	2
+
+/* Buffer and period size */
+#define AICA_BUFFER_SIZE 0x8000
+#define AICA_PERIOD_SIZE 0x800
+#define AICA_PERIOD_NUMBER 16
+
+#define AICA_CHANNEL0_OFFSET 0x11000
+#define AICA_CHANNEL1_OFFSET 0x21000
+#define CHANNEL_OFFSET 0x10000
+
+#define AICA_DMA_CHANNEL 0
+#define AICA_DMA_MODE 5
+
+#define SND_AICA_DRIVER "AICA"
+
+struct aica_channel {
+	uint32_t cmd;		/* Command ID           */
+	uint32_t pos;		/* Sample position      */
+	uint32_t length;	/* Sample length        */
+	uint32_t freq;		/* Frequency            */
+	uint32_t vol;		/* Volume 0-255         */
+	uint32_t pan;		/* Pan 0-255            */
+	uint32_t sfmt;		/* Sound format         */
+	uint32_t flags;		/* Bit flags            */
+};
+
+struct snd_card_aica {
+	struct work_struct spu_dma_work;
+	struct snd_card *card;
+	struct aica_channel *channel;
+	struct snd_pcm_substream *substream;
+	int clicks;
+	int current_period;
+	struct timer_list timer;
+	int master_volume;
+	int dma_check;
+};
diff -ruN linux-2.6.22/sound/soc/Kconfig linux-2.6.22-alsa/sound/soc/Kconfig
--- linux-2.6.22/sound/soc/Kconfig	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/soc/Kconfig	2007-07-22 17:32:25.000000000 +0200
@@ -27,6 +27,7 @@
 source "sound/soc/at91/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
+source "sound/soc/sh/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff -ruN linux-2.6.22/sound/soc/Makefile linux-2.6.22-alsa/sound/soc/Makefile
--- linux-2.6.22/sound/soc/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/soc/Makefile	2007-07-22 17:32:25.000000000 +0200
@@ -1,4 +1,4 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
-obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/
+obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/ sh/
diff -ruN linux-2.6.22/sound/soc/s3c24xx/Kconfig linux-2.6.22-alsa/sound/soc/s3c24xx/Kconfig
--- linux-2.6.22/sound/soc/s3c24xx/Kconfig	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/Kconfig	2007-07-22 17:32:25.000000000 +0200
@@ -1,6 +1,7 @@
 config SND_S3C24XX_SOC
 	tristate "SoC Audio for the Samsung S3C24XX chips"
 	depends on ARCH_S3C2410 && SND_SOC
+	select SND_PCM
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the S3C24XX AC97, I2S or SSP interface. You will also need
@@ -8,3 +9,29 @@
 
 config SND_S3C24XX_SOC_I2S
 	tristate
+
+config SND_S3C2443_SOC_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+	
+config SND_S3C24XX_SOC_NEO1973_WM8753
+	tristate "SoC I2S Audio support for NEO1973 - WM8753"
+	depends on SND_S3C24XX_SOC && MACH_GTA01
+	select SND_S3C24XX_SOC_I2S
+	select SND_SOC_WM8753
+	help
+	  Say Y if you want to add support for SoC audio on smdk2440
+	  with the WM8753.
+
+config SND_S3C24XX_SOC_SMDK2443_WM9710
+	tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
+	depends on SND_S3C24XX_SOC && MACH_SMDK2443
+	select SND_S3C2443_SOC_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  Say Y if you want to add support for SoC audio on smdk2443
+	  with the WM9710.
+
+
diff -ruN linux-2.6.22/sound/soc/s3c24xx/Makefile linux-2.6.22-alsa/sound/soc/s3c24xx/Makefile
--- linux-2.6.22/sound/soc/s3c24xx/Makefile	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/Makefile	2007-07-22 17:32:25.000000000 +0200
@@ -1,6 +1,15 @@
 # S3c24XX Platform Support
 snd-soc-s3c24xx-objs := s3c24xx-pcm.o
 snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
+snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
+obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
+
+# S3C24XX Machine Support
+snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
+snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
+
+obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
diff -ruN linux-2.6.22/sound/soc/s3c24xx/lm4857.h linux-2.6.22-alsa/sound/soc/s3c24xx/lm4857.h
--- linux-2.6.22/sound/soc/s3c24xx/lm4857.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/lm4857.h	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,32 @@
+/*
+ * lm4857.h  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    18th Jun 2007   Initial version.
+ */
+
+#ifndef LM4857_H_
+#define LM4857_H_
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+#endif /*LM4857_H_*/
+
diff -ruN linux-2.6.22/sound/soc/s3c24xx/neo1973_wm8753.c linux-2.6.22-alsa/sound/soc/s3c24xx/neo1973_wm8753.c
--- linux-2.6.22/sound/soc/s3c24xx/neo1973_wm8753.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/neo1973_wm8753.c	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,670 @@
+/*
+ * neo1973_wm8753.c  --  SoC audio for Neo1973
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    20th Jan 2007   Initial version.
+ *    05th Feb 2007   Rename all to Neo1973
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <asm/arch/regs-iis.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+#include <asm/arch/audio.h>
+#include <asm/io.h>
+#include <asm/arch/spi-gpio.h>
+#include "../codecs/wm8753.h"
+#include "lm4857.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+
+/* define the scenarios */
+#define NEO_AUDIO_OFF			0
+#define NEO_GSM_CALL_AUDIO_HANDSET	1
+#define NEO_GSM_CALL_AUDIO_HEADSET	2
+#define NEO_GSM_CALL_AUDIO_BLUETOOTH	3
+#define NEO_STEREO_TO_SPEAKERS		4
+#define NEO_STEREO_TO_HEADPHONES	5
+#define NEO_CAPTURE_HANDSET		6
+#define NEO_CAPTURE_HEADSET		7
+#define NEO_CAPTURE_BLUETOOTH		8
+
+static struct snd_soc_machine neo1973;
+static struct i2c_client *i2c;
+
+static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_out = 0, bclk = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+		pll_out = 12288000;
+		break;
+	case 48000:
+		bclk = WM8753_BCLK_DIV_4;
+		pll_out = 12288000;
+		break;
+	case 96000:
+		bclk = WM8753_BCLK_DIV_2;
+		pll_out = 12288000;
+		break;
+	case 11025:
+		bclk = WM8753_BCLK_DIV_16;
+		pll_out = 11289600;
+		break;
+	case 22050:
+		bclk = WM8753_BCLK_DIV_8;
+		pll_out = 11289600;
+		break;
+	case 44100:
+		bclk = WM8753_BCLK_DIV_4;
+		pll_out = 11289600;
+		break;
+	case 88200:
+		bclk = WM8753_BCLK_DIV_2;
+		pll_out = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->dai_ops.set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set MCLK division for sample rate */
+	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+		S3C2410_IISMOD_32FS );
+	if (ret < 0)
+		return ret;
+
+	/* set codec BCLK division for sample rate */
+	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
+	if (ret < 0)
+		return ret;
+
+	/* set prescaler division for sample rate */
+	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+		S3C24XX_PRESCALE(4,4));
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
+		iis_clkrate / 4, pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+}
+
+/*
+ * Neo1973 WM8753 HiFi DAI opserations.
+ */
+static struct snd_soc_ops neo1973_hifi_ops = {
+	.hw_params = neo1973_hifi_hw_params,
+	.hw_free = neo1973_hifi_hw_free,
+};
+
+static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int pcmdiv = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	if (params_rate(params) != 8000)
+		return -EINVAL;
+	if (params_channels(params) != 1)
+		return -EINVAL;
+
+	pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
+
+	/* todo: gg check mode (DSP_B) against CSR datasheet */
+	/* set codec DAI configuration */
+	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set codec PCM division for sample rate */
+	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+	if (ret < 0)
+		return ret;
+
+	/* configue and enable PLL for 12.288MHz output */
+	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
+		iis_clkrate / 4, 12288000);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
+}
+
+static struct snd_soc_ops neo1973_voice_ops = {
+	.hw_params = neo1973_voice_hw_params,
+	.hw_free = neo1973_voice_hw_free,
+};
+
+static int neo1973_scenario = 0;
+
+static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = neo1973_scenario;
+	return 0;
+}
+
+static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
+{
+	switch(neo1973_scenario) {
+	case NEO_AUDIO_OFF:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_GSM_CALL_AUDIO_HANDSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		break;
+	case NEO_GSM_CALL_AUDIO_HEADSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_GSM_CALL_AUDIO_BLUETOOTH:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_STEREO_TO_SPEAKERS:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_STEREO_TO_HEADPHONES:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_CAPTURE_HANDSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		break;
+	case NEO_CAPTURE_HEADSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_CAPTURE_BLUETOOTH:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	default:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+
+	return 0;
+}
+
+static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	if (neo1973_scenario == ucontrol->value.integer.value[0])
+		return 0;
+
+	neo1973_scenario = ucontrol->value.integer.value[0];
+	set_scenario_endpoints(codec, neo1973_scenario);
+	return 1;
+}
+
+static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
+
+static void lm4857_write_regs(void)
+{
+	if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
+		printk(KERN_ERR "lm4857: i2c write failed\n");
+}
+
+static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int reg=kcontrol->private_value & 0xFF;
+	int shift = (kcontrol->private_value >> 8) & 0x0F;
+	int mask = (kcontrol->private_value >> 16) & 0xFF;
+
+	ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
+	return 0;
+}
+
+static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int reg = kcontrol->private_value & 0xFF;
+	int shift = (kcontrol->private_value >> 8) & 0x0F;
+	int mask = (kcontrol->private_value >> 16) & 0xFF;
+
+	if (((lm4857_regs[reg] >> shift ) & mask) ==
+		ucontrol->value.integer.value[0])
+		return 0;
+
+	lm4857_regs[reg] &= ~ (mask << shift);
+	lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
+	lm4857_write_regs();
+	return 1;
+}
+
+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
+
+	if (value)
+		value -= 5;
+
+	ucontrol->value.integer.value[0] = value;
+	return 0;
+}
+
+static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = ucontrol->value.integer.value[0];
+
+	if (value)
+		value += 5;
+
+	if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
+		return 0;
+
+	lm4857_regs[LM4857_CTRL] &= 0xF0;
+	lm4857_regs[LM4857_CTRL] |= value;
+	lm4857_write_regs();
+	return 1;
+}
+
+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Audio Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line In", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Call Mic", NULL),
+};
+
+
+/* example machine audio_mapnections */
+static const char* audio_map[][3] = {
+
+	/* Connections to the lm4857 amp */
+	{"Audio Out", NULL, "LOUT1"},
+	{"Audio Out", NULL, "ROUT1"},
+
+	/* Connections to the GSM Module */
+	{"GSM Line Out", NULL, "MONO1"},
+	{"GSM Line Out", NULL, "MONO2"},
+	{"RXP", NULL, "GSM Line In"},
+	{"RXN", NULL, "GSM Line In"},
+
+	/* Connections to Headset */
+	{"MIC1", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Headset Mic"},
+
+	/* Call Mic */
+	{"MIC2", NULL, "Mic Bias"},
+	{"MIC2N", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Call Mic"},
+
+	/* Connect the ALC pins */
+	{"ACIN", NULL, "ACOP"},
+
+	{NULL, NULL, NULL},
+};
+
+static const char *lm4857_mode[] = {
+	"Off",
+	"Call Speaker",
+	"Stereo Speakers",
+	"Stereo Speakers + Headphones",
+	"Headphones"
+};
+
+static const struct soc_enum lm4857_mode_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
+};
+
+static const char *neo_scenarios[] = {
+	"Off",
+	"GSM Handset",
+	"GSM Headset",
+	"GSM Bluetooth",
+	"Speakers",
+	"Headphones",
+	"Capture Handset",
+	"Capture Headset",
+	"Capture Bluetooth"
+};
+
+static const struct soc_enum neo_scenario_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios),neo_scenarios),
+};
+
+static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
+	SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
+		lm4857_get_mode, lm4857_set_mode),
+	SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
+		neo1973_get_scenario, neo1973_set_scenario),
+	SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+};
+
+/*
+ * This is an example machine initialisation for a wm8753 connected to a
+ * neo1973 II. It is missing logic to detect hp/mic insertions and logic
+ * to re-route the audio in such an event.
+ */
+static int neo1973_wm8753_init(struct snd_soc_codec *codec)
+{
+	int i, err;
+
+	/* set up NC codec pins */
+	snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
+	snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
+	snd_soc_dapm_set_endpoint(codec, "OUT3",  0);
+	snd_soc_dapm_set_endpoint(codec, "OUT4",  0);
+	snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
+	snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
+
+
+	/* set endpoints to default mode */
+	set_scenario_endpoints(codec, NEO_AUDIO_OFF);
+
+	/* Add neo1973 specific widgets */
+	for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
+		snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
+
+	/* add neo1973 specific controls */
+	for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8753_neo1973_controls[i],
+				codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	/* set up neo1973 specific audio path audio_mapnects */
+	for (i = 0; audio_map[i][0] != NULL; i++) {
+		snd_soc_dapm_connect_input(codec, audio_map[i][0],
+			audio_map[i][1], audio_map[i][2]);
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+	return 0;
+}
+
+/*
+ * BT Codec DAI
+ */
+static struct snd_soc_cpu_dai bt_dai =
+{	.name = "Bluetooth",
+	.id = 0,
+	.type = SND_SOC_DAI_PCM,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+};
+
+static struct snd_soc_dai_link neo1973_dai[] = {
+{ /* Hifi Playback - for similatious use with voice below */
+	.name = "WM8753",
+	.stream_name = "WM8753 HiFi",
+	.cpu_dai = &s3c24xx_i2s_dai,
+	.codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+	.init = neo1973_wm8753_init,
+	.ops = &neo1973_hifi_ops,
+},
+{ /* Voice via BT */
+	.name = "Bluetooth",
+	.stream_name = "Voice",
+	.cpu_dai = &bt_dai,
+	.codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
+	.ops = &neo1973_voice_ops,
+},
+};
+
+static struct snd_soc_machine neo1973 = {
+	.name = "neo1973",
+	.dai_link = neo1973_dai,
+	.num_links = ARRAY_SIZE(neo1973_dai),
+};
+
+static struct wm8753_setup_data neo1973_wm8753_setup = {
+	.i2c_address = 0x1a,
+};
+
+static struct snd_soc_device neo1973_snd_devdata = {
+	.machine = &neo1973,
+	.platform = &s3c24xx_soc_platform,
+	.codec_dev = &soc_codec_dev_wm8753,
+	.codec_data = &neo1973_wm8753_setup,
+};
+
+static struct i2c_client client_template;
+
+static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	int ret;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL)
+		return -ENOMEM;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr);
+		goto exit_err;
+	}
+
+	lm4857_write_regs();
+	return ret;
+
+exit_err:
+	kfree(i2c);
+	return ret;
+}
+
+static int lm4857_i2c_detach(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(client);
+	return 0;
+}
+
+static int lm4857_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, lm4857_amp_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver lm4857_i2c_driver = {
+	.driver = {
+		.name = "LM4857 I2C Amp",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_LM4857,
+	.attach_adapter = lm4857_i2c_attach,
+	.detach_client =  lm4857_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "LM4857",
+	.driver = &lm4857_i2c_driver,
+};
+
+static struct platform_device *neo1973_snd_device;
+
+static int __init neo1973_init(void)
+{
+	int ret;
+
+	neo1973_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!neo1973_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata);
+	neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
+	ret = platform_device_add(neo1973_snd_device);
+
+	if (ret)
+		platform_device_put(neo1973_snd_device);
+
+	ret = i2c_add_driver(&lm4857_i2c_driver);
+	if (ret != 0)
+		printk(KERN_ERR "can't add i2c driver");
+
+	return ret;
+}
+
+static void __exit neo1973_exit(void)
+{
+	platform_device_unregister(neo1973_snd_device);
+}
+
+module_init(neo1973_init);
+module_exit(neo1973_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.22/sound/soc/s3c24xx/s3c2443-ac97.c linux-2.6.22-alsa/sound/soc/s3c24xx/s3c2443-ac97.c
--- linux-2.6.22/sound/soc/s3c24xx/s3c2443-ac97.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/s3c2443-ac97.c	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,401 @@
+/*
+ * s3c2443-ac97.c  --  ALSA Soc Audio Layer
+ *
+ * (c) 2007 Wolfson Microelectronics PLC.
+ * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  Revision history
+ *	21st Mar 2007   Initial Version
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/arch/regs-ac97.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/audio.h>
+#include <asm/dma.h>
+#include <asm/arch/dma.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-ac97.h"
+
+struct s3c24xx_ac97_info {
+	void __iomem	*regs;
+	struct clk	*ac97_clk;
+};
+static struct s3c24xx_ac97_info s3c24xx_ac97;
+
+DECLARE_COMPLETION(ac97_completion);
+static u32 codec_ready;
+static DECLARE_MUTEX(ac97_mutex);
+
+static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
+	unsigned short reg)
+{
+	u32 ac_glbctrl;
+	u32 ac_codec_cmd;
+	u32 stat, addr, data;
+
+	down(&ac97_mutex);
+
+	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
+	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
+	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	wait_for_completion(&ac97_completion);
+
+	stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
+	addr = (stat >> 16) & 0x7f;
+	data = (stat & 0xffff);
+
+	if (addr != reg)
+		printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
+				" rep addr = %02x\n", reg, addr);
+
+	up(&ac97_mutex);
+
+	return (unsigned short)data;
+}
+
+static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+	unsigned short val)
+{
+	u32 ac_glbctrl;
+	u32 ac_codec_cmd;
+
+	down(&ac97_mutex);
+
+	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
+	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
+	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	wait_for_completion(&ac97_completion);
+
+	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
+	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	up(&ac97_mutex);
+
+}
+
+static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = 0;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = 0;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA |
+		S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+}
+
+static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
+{
+	int status;
+	u32 ac_glbctrl;
+
+	status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
+
+	if (status) {
+		ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
+		writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+		complete(&ac97_completion);
+	}
+	return IRQ_HANDLED;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read	= s3c2443_ac97_read,
+	.write	= s3c2443_ac97_write,
+	.warm_reset	= s3c2443_ac97_warm_reset,
+	.reset	= s3c2443_ac97_cold_reset,
+};
+
+static struct s3c2410_dma_client s3c2443_dma_client_out = {
+	.name = "AC97 PCM Stereo out"
+};
+
+static struct s3c2410_dma_client s3c2443_dma_client_in = {
+	.name = "AC97 PCM Stereo in"
+};
+
+static struct s3c2410_dma_client s3c2443_dma_client_micin = {
+	.name = "AC97 Mic Mono in"
+};
+
+static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
+	.client		= &s3c2443_dma_client_out,
+	.channel	= DMACH_PCM_OUT,
+	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
+	.dma_size	= 4,
+};
+
+static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
+	.client		= &s3c2443_dma_client_in,
+	.channel	= DMACH_PCM_IN,
+	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
+	.dma_size	= 4,
+};
+
+static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
+	.client		= &s3c2443_dma_client_micin,
+	.channel	= DMACH_MIC_IN,
+	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
+	.dma_size	= 4,
+};
+
+static int s3c2443_ac97_probe(struct platform_device *pdev)
+{
+	int ret;
+	u32 ac_glbctrl;
+
+	s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
+	if (s3c24xx_ac97.regs == NULL)
+		return -ENXIO;
+
+	s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+	if (s3c24xx_ac97.ac97_clk == NULL) {
+		printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n");
+		iounmap(s3c24xx_ac97.regs);
+		return -ENODEV;
+	}
+	clk_enable(s3c24xx_ac97.ac97_clk);
+
+	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET);
+	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC);
+	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK);
+	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI);
+	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = 0;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
+		IRQF_DISABLED, "AC97", NULL);
+	if (ret < 0) {
+		printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
+		clk_disable(s3c24xx_ac97.ac97_clk);
+		clk_put(s3c24xx_ac97.ac97_clk);
+		iounmap(s3c24xx_ac97.regs);
+	}
+	return ret;
+}
+
+static void s3c2443_ac97_remove(struct platform_device *pdev)
+{
+	free_irq(IRQ_S3C2443_AC97, NULL);
+	clk_disable(s3c24xx_ac97.ac97_clk);
+	clk_put(s3c24xx_ac97.ac97_clk);
+	iounmap(s3c24xx_ac97.regs);
+}
+
+static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
+	else
+		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
+
+	return 0;
+}
+
+static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	switch(cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		else
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+		else
+			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
+		break;
+	}
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	return 0;
+}
+
+static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return -ENODEV;
+	else
+		cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
+
+	return 0;
+}
+
+static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
+	int cmd)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	switch(cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+	}
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	return 0;
+}
+
+#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
+{
+	.name = "s3c2443-ac97",
+	.id = 0,
+	.type = SND_SOC_DAI_AC97,
+	.probe = s3c2443_ac97_probe,
+	.remove = s3c2443_ac97_remove,
+	.playback = {
+		.stream_name = "AC97 Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = s3c2443_AC97_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "AC97 Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = s3c2443_AC97_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = s3c2443_ac97_hw_params,
+		.trigger = s3c2443_ac97_trigger},
+},
+{
+	.name = "pxa2xx-ac97-mic",
+	.id = 1,
+	.type = SND_SOC_DAI_AC97,
+	.capture = {
+		.stream_name = "AC97 Mic Capture",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = s3c2443_AC97_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = s3c2443_ac97_hw_mic_params,
+		.trigger = s3c2443_ac97_mic_trigger,},
+},
+};
+
+EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.22/sound/soc/s3c24xx/s3c24xx-ac97.h linux-2.6.22-alsa/sound/soc/s3c24xx/s3c24xx-ac97.h
--- linux-2.6.22/sound/soc/s3c24xx/s3c24xx-ac97.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/s3c24xx-ac97.h	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,25 @@
+/*
+ * s3c24xx-ac97.c  --  ALSA Soc Audio Layer
+ *
+ * (c) 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    10th Nov 2006   Initial version.
+ */
+
+#ifndef S3C24XXAC97_H_
+#define S3C24XXAC97_H_
+
+#define AC_CMD_ADDR(x) (x << 16)
+#define AC_CMD_DATA(x) (x & 0xffff)
+
+extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+
+#endif /*S3C24XXAC97_H_*/
diff -ruN linux-2.6.22/sound/soc/s3c24xx/s3c24xx-i2s.c linux-2.6.22-alsa/sound/soc/s3c24xx/s3c24xx-i2s.c
--- linux-2.6.22/sound/soc/s3c24xx/s3c24xx-i2s.c	2007-07-09 01:32:17.000000000 +0200
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/s3c24xx-i2s.c	2007-07-22 17:32:25.000000000 +0200
@@ -344,11 +344,11 @@
 	DBG("Entered %s\n", __FUNCTION__);
 
 	switch (div_id) {
-	case S3C24XX_DIV_MCLK:
+	case S3C24XX_DIV_BCLK:
 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 		break;
-	case S3C24XX_DIV_BCLK:
+	case S3C24XX_DIV_MCLK:
 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 		break;
diff -ruN linux-2.6.22/sound/soc/s3c24xx/smdk2443_wm9710.c linux-2.6.22-alsa/sound/soc/s3c24xx/smdk2443_wm9710.c
--- linux-2.6.22/sound/soc/s3c24xx/smdk2443_wm9710.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/s3c24xx/smdk2443_wm9710.c	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,85 @@
+/*
+ * smdk2443_wm9710.c  --  SoC audio for smdk2443
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    8th Mar 2007   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/ac97.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-ac97.h"
+
+static struct snd_soc_machine smdk2443;
+
+static struct snd_soc_dai_link smdk2443_dai[] = {
+{
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai = &s3c2443_ac97_dai[0],
+	.codec_dai = &ac97_dai,
+},
+};
+
+static struct snd_soc_machine smdk2443 = {
+	.name = "SMDK2443",
+	.dai_link = smdk2443_dai,
+	.num_links = ARRAY_SIZE(smdk2443_dai),
+};
+
+static struct snd_soc_device smdk2443_snd_ac97_devdata = {
+	.machine = &smdk2443,
+	.platform = &s3c24xx_soc_platform,
+	.codec_dev = &soc_codec_dev_ac97,
+};
+
+static struct platform_device *smdk2443_snd_ac97_device;
+
+static int __init smdk2443_init(void)
+{
+	int ret;
+
+	smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+	if (!smdk2443_snd_ac97_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smdk2443_snd_ac97_device,
+				&smdk2443_snd_ac97_devdata);
+	smdk2443_snd_ac97_devdata.dev = &smdk2443_snd_ac97_device->dev;
+	ret = platform_device_add(smdk2443_snd_ac97_device);
+
+	if (ret)
+		platform_device_put(smdk2443_snd_ac97_device);
+
+	return ret;
+}
+
+static void __exit smdk2443_exit(void)
+{
+	platform_device_unregister(smdk2443_snd_ac97_device);
+}
+
+module_init(smdk2443_init);
+module_exit(smdk2443_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443");
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.22/sound/soc/sh/Kconfig linux-2.6.22-alsa/sound/soc/sh/Kconfig
--- linux-2.6.22/sound/soc/sh/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/sh/Kconfig	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,38 @@
+menu "SoC Audio support for SuperH"
+
+config SND_SOC_PCM_SH7760
+	tristate "SoC Audio support for Renesas SH7760"
+	depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
+	help
+	  Enable this option for SH7760 AC97/I2S audio support.
+
+
+##
+## Audio unit modules
+##
+
+config SND_SOC_SH4_HAC
+	select AC97_BUS
+	select SND_SOC_AC97_BUS
+	select SND_AC97_CODEC
+	tristate
+
+config SND_SOC_SH4_SSI
+	tristate
+
+
+
+##
+## Boards
+##
+
+config SND_SH7760_AC97
+	tristate "SH7760 AC97 sound support"
+	depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760
+	select SND_SOC_SH4_HAC
+	select SND_SOC_AC97_CODEC
+	help
+	  This option enables generic sound support for the first
+	  AC97 unit of the SH7760.
+
+endmenu
diff -ruN linux-2.6.22/sound/soc/sh/Makefile linux-2.6.22-alsa/sound/soc/sh/Makefile
--- linux-2.6.22/sound/soc/sh/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/sh/Makefile	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,14 @@
+## DMA engines
+snd-soc-dma-sh7760-objs	:= dma-sh7760.o
+obj-$(CONFIG_SND_SOC_PCM_SH7760)	+= snd-soc-dma-sh7760.o
+
+## audio units found on some SH-4
+snd-soc-hac-objs	:= hac.o
+snd-soc-ssi-objs	:= ssi.o
+obj-$(CONFIG_SND_SOC_SH4_HAC)	+= snd-soc-hac.o
+obj-$(CONFIG_SND_SOC_SH4_SSI)	+= snd-soc-ssi.o
+
+## boards
+snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
+
+obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
diff -ruN linux-2.6.22/sound/soc/sh/dma-sh7760.c linux-2.6.22-alsa/sound/soc/sh/dma-sh7760.c
--- linux-2.6.22/sound/soc/sh/dma-sh7760.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/sh/dma-sh7760.c	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,354 @@
+/*
+ * SH7760 ("camelot") DMABRG audio DMA unit support
+ *
+ * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
+ * trigger an interrupt when one half of the programmed transfer size
+ * has been xmitted.
+ *
+ * FIXME: little-endian only for now
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/dmabrg.h>
+
+
+/* registers and bits */
+#define BRGATXSAR	0x00
+#define BRGARXDAR	0x04
+#define BRGATXTCR	0x08
+#define BRGARXTCR	0x0C
+#define BRGACR		0x10
+#define BRGATXTCNT	0x14
+#define BRGARXTCNT	0x18
+
+#define ACR_RAR		(1 << 18)
+#define ACR_RDS		(1 << 17)
+#define ACR_RDE		(1 << 16)
+#define ACR_TAR		(1 << 2)
+#define ACR_TDS		(1 << 1)
+#define ACR_TDE		(1 << 0)
+
+/* receiver/transmitter data alignment */
+#define ACR_RAM_NONE	(0 << 24)
+#define ACR_RAM_4BYTE	(1 << 24)
+#define ACR_RAM_2WORD	(2 << 24)
+#define ACR_TAM_NONE	(0 << 8)
+#define ACR_TAM_4BYTE	(1 << 8)
+#define ACR_TAM_2WORD	(2 << 8)
+
+
+struct camelot_pcm {
+	unsigned long mmio;  /* DMABRG audio channel control reg MMIO */
+	unsigned int txid;    /* ID of first DMABRG IRQ for this unit */
+
+	struct snd_pcm_substream *tx_ss;
+	unsigned long tx_period_size;
+	unsigned int  tx_period;
+
+	struct snd_pcm_substream *rx_ss;
+	unsigned long rx_period_size;
+	unsigned int  rx_period;
+
+} cam_pcm_data[2] = {
+	{
+		.mmio	=	0xFE3C0040,
+		.txid	=	DMABRGIRQ_A0TXF,
+	},
+	{
+		.mmio	=	0xFE3C0060,
+		.txid	=	DMABRGIRQ_A1TXF,
+	},
+};
+
+#define BRGREG(x)	(*(unsigned long *)(cam->mmio + (x)))
+
+/*
+ * set a minimum of 16kb per period, to avoid interrupt-"storm" and
+ * resulting skipping. In general, the bigger the minimum size, the
+ * better for overall system performance. (The SH7760 is a puny CPU
+ * with a slow SDRAM interface and poor internal bus bandwidth,
+ * *especially* when the LCDC is active).  The minimum for the DMAC
+ * is 8 bytes; 16kbytes are enough to get skip-free playback of a
+ * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
+ * reasonable responsiveness in MPlayer.
+ */
+#define DMABRG_PERIOD_MIN		16 * 1024
+#define DMABRG_PERIOD_MAX		0x03fffffc
+#define DMABRG_PREALLOC_BUFFER		32 * 1024
+#define DMABRG_PREALLOC_BUFFER_MAX	32 * 1024
+
+/* support everything the SSI supports */
+#define DMABRG_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+#define DMABRG_FMTS	\
+	(SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |	\
+	 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |	\
+	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\
+	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_pcm_hardware camelot_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =	DMABRG_FMTS,
+	.rates =	DMABRG_RATES,
+	.rate_min =		8000,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		8,		/* max of the SSI */
+	.buffer_bytes_max =	DMABRG_PERIOD_MAX,
+	.period_bytes_min =	DMABRG_PERIOD_MIN,
+	.period_bytes_max =	DMABRG_PERIOD_MAX / 2,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		128,
+};
+
+static void camelot_txdma(void *data)
+{
+	struct camelot_pcm *cam = data;
+	cam->tx_period ^= 1;
+	snd_pcm_period_elapsed(cam->tx_ss);
+}
+
+static void camelot_rxdma(void *data)
+{
+	struct camelot_pcm *cam = data;
+	cam->rx_period ^= 1;
+	snd_pcm_period_elapsed(cam->rx_ss);
+}
+
+static int camelot_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int ret, dmairq;
+
+	snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
+
+	/* DMABRG buffer half/full events */
+	dmairq = (recv) ? cam->txid + 2 : cam->txid;
+	if (recv) {
+		cam->rx_ss = substream;
+		ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
+		if (unlikely(ret)) {
+			pr_debug("audio unit %d irqs already taken!\n",
+			     rtd->dai->cpu_dai->id);
+			return -EBUSY;
+		}
+		(void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
+	} else {
+		cam->tx_ss = substream;
+		ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
+		if (unlikely(ret)) {
+			pr_debug("audio unit %d irqs already taken!\n",
+			     rtd->dai->cpu_dai->id);
+			return -EBUSY;
+		}
+		(void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
+	}
+	return 0;
+}
+
+static int camelot_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int dmairq;
+
+	dmairq = (recv) ? cam->txid + 2 : cam->txid;
+
+	if (recv)
+		cam->rx_ss = NULL;
+	else
+		cam->tx_ss = NULL;
+
+	dmabrg_free_irq(dmairq + 1);
+	dmabrg_free_irq(dmairq);
+
+	return 0;
+}
+
+static int camelot_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	if (ret < 0)
+		return ret;
+
+	if (recv) {
+		cam->rx_period_size = params_period_bytes(hw_params);
+		cam->rx_period = 0;
+	} else {
+		cam->tx_period_size = params_period_bytes(hw_params);
+		cam->tx_period = 0;
+	}
+	return 0;
+}
+
+static int camelot_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int camelot_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+
+	pr_debug("PCM data: addr 0x%08ulx len %d\n",
+		 (u32)runtime->dma_addr, runtime->dma_bytes);
+ 
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
+		BRGREG(BRGATXTCR) = runtime->dma_bytes;
+	} else {
+		BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
+		BRGREG(BRGARXTCR) = runtime->dma_bytes;
+	}
+
+	return 0;
+}
+
+static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* start DMABRG engine: XFER start, auto-addr-reload */
+	BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
+}
+
+static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* forcibly terminate data transmission */
+	BRGREG(BRGACR) = acr | ACR_TDS;
+}
+
+static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* start DMABRG engine: recv start, auto-reload */
+	BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
+}
+
+static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* forcibly terminate data receiver */
+	BRGREG(BRGACR) = acr | ACR_RDS;
+}
+
+static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (recv)
+			dmabrg_rec_dma_start(cam);
+		else
+			dmabrg_play_dma_start(cam);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (recv)
+			dmabrg_rec_dma_stop(cam);
+		else
+			dmabrg_play_dma_stop(cam);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	unsigned long pos;
+
+	/* cannot use the DMABRG pointer register: under load, by the
+	 * time ALSA comes around to read the register, it is already
+	 * far ahead (or worse, already done with the fragment) of the
+	 * position at the time the IRQ was triggered, which results in
+	 * fast-playback sound in my test application (ScummVM)
+	 */
+	if (recv)
+		pos = cam->rx_period ? cam->rx_period_size : 0;
+	else
+		pos = cam->tx_period ? cam->tx_period_size : 0;
+
+	return bytes_to_frames(runtime, pos);
+}
+
+static struct snd_pcm_ops camelot_pcm_ops = {
+	.open		= camelot_pcm_open,
+	.close		= camelot_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= camelot_hw_params,
+	.hw_free	= camelot_hw_free,
+	.prepare	= camelot_prepare,
+	.trigger	= camelot_trigger,
+	.pointer	= camelot_pos,
+};
+
+static void camelot_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int camelot_pcm_new(struct snd_card *card,
+			   struct snd_soc_codec_dai *dai,
+			   struct snd_pcm *pcm)
+{
+	/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
+	 * in MMAP mode (i.e. aplay -M)
+	 */
+	snd_pcm_lib_preallocate_pages_for_all(pcm,
+		SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		DMABRG_PREALLOC_BUFFER,	DMABRG_PREALLOC_BUFFER_MAX);
+
+	return 0;
+}
+
+struct snd_soc_platform sh7760_soc_platform = {
+	.name		= "sh7760-pcm",
+	.pcm_ops 	= &camelot_pcm_ops,
+	.pcm_new	= camelot_pcm_new,
+	.pcm_free	= camelot_pcm_free,
+};
+EXPORT_SYMBOL_GPL(sh7760_soc_platform);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff -ruN linux-2.6.22/sound/soc/sh/hac.c linux-2.6.22-alsa/sound/soc/sh/hac.c
--- linux-2.6.22/sound/soc/sh/hac.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-alsa/sound/soc/sh/hac.c	2007-07-22 17:32:25.000000000 +0200
@@ -0,0 +1,322 @@
+/*
+ * Hitachi Audio Controller (AC97) support for SH7760/SH7780
+ *
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
+ * enable HAC output pins!
+ */
+
+/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
+ * the FIRST can be used since ASoC does not pass any information to the
+ * ac97_read/write() functions regarding WHICH unit to use.  You'll have
+ * to edit the code a bit to use the other AC97 unit.		--mlau
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+/* regs and bits */
+#define HACCR		0x08
+#define HACCSAR		0x20
+#define HACCSDR		0x24
+#define HACPCML		0x28
+#define HACPCMR		0x2C
+#define HACTIER		0x50
+#define	HACTSR		0x54
+#define HACRIER		0x58
+#define HACRSR		0x5C
+#define HACACR		0x60
+
+#define CR_CR		(1 << 15)	/* "codec-ready" indicator */
+#define CR_CDRT		(1 << 11)	/* cold reset */
+#define CR_WMRT		(1 << 10)	/* warm reset */
+#define CR_B9		(1 << 9)	/* the mysterious "bit 9" */
+#define CR_ST		(1 << 5)	/* AC97 link start bit */
+
+#define CSAR_RD		(1 << 19)	/* AC97 data read bit */
+#define CSAR_WR		(0)
+
+#define TSR_CMDAMT	(1 << 31)
+#define TSR_CMDDMT	(1 << 30)
+
+#define RSR_STARY	(1 << 22)
+#define RSR_STDRY	(1 << 21)
+
+#define ACR_DMARX16	(1 << 30)
+#define ACR_DMATX16	(1 << 29)
+#define ACR_TX12ATOM	(1 << 26)
+#define ACR_DMARX20	((1 << 24) | (1 << 22))
+#define ACR_DMATX20	((1 << 23) | (1 << 21))
+
+#define CSDR_SHIFT	4
+#define CSDR_MASK	(0xffff << CSDR_SHIFT)
+#define CSAR_SHIFT	12
+#define CSAR_MASK	(0x7f << CSAR_SHIFT)
+
+#define AC97_WRITE_RETRY	1
+#define AC97_READ_RETRY		5
+
+/* manual-suggested AC97 codec access timeouts (us) */
+#define TMO_E1	500	/* 21 < E1 < 1000 */
+#define TMO_E2	13	/* 13 < E2 */
+#define TMO_E3	21	/* 21 < E3 */
+#define TMO_E4	500	/* 21 < E4 < 1000 */
+
+struct hac_priv {
+	unsigned long mmio;	/* HAC base address */
+} hac_cpu_data[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+	{
+		.mmio	= 0xFE240000,
+	},
+	{
+		.mmio	= 0xFE250000,
+	},
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+	{
+		.mmio	= 0xFFE40000,
+	},
+#else
+#error "Unsupported SuperH SoC"
+#endif
+};
+
+#define HACREG(reg)	(*(unsigned long *)(hac->mmio + (reg)))
+
+/*
+ * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906)
+ */
+static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
+			      unsigned short *v)
+{
+	unsigned int to1, to2, i;
+	unsigned short adr;
+
+	for (i = 0; i < AC97_READ_RETRY; ++i) {
+		*v = 0;
+		/* wait for HAC to receive something from the codec */
+		for (to1 = TMO_E4;
+		     to1 && !(HACREG(HACRSR) & RSR_STARY);
+		     --to1)
+			udelay(1);
+		for (to2 = TMO_E4; 
+		     to2 && !(HACREG(HACRSR) & RSR_STDRY);
+		     --to2)
+			udelay(1);
+
+		if (!to1 && !to2)
+			return 0;	/* codec comm is down */
+
+		adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT);
+		*v  = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT);
+
+		HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+
+		if (r == adr)
+			break;
+
+		/* manual says: wait at least 21 usec before retrying */
+		udelay(21);
+	}
+	HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+	return (i < AC97_READ_RETRY);
+}
+
+static unsigned short hac_read_codec_aux(struct hac_priv *hac,
+					 unsigned short reg)
+{
+	unsigned short val;
+	unsigned int i, to;
+
+	for (i = 0; i < AC97_READ_RETRY; i++) {
+		/* send_read_request */
+		local_irq_disable();
+		HACREG(HACTSR) &= ~(TSR_CMDAMT);
+		HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD;
+		local_irq_enable();
+
+		for (to = TMO_E3;
+		     to && !(HACREG(HACTSR) & TSR_CMDAMT);
+		     --to)
+			udelay(1);
+
+		HACREG(HACTSR) &= ~TSR_CMDAMT;
+		val = 0;
+		if (hac_get_codec_data(hac, reg, &val) != 0)
+			break;
+	}
+
+	if (i == AC97_READ_RETRY)
+		return ~0;
+
+	return val;
+}
+
+static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+			   unsigned short val)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	unsigned int i, to;
+	/* write_codec_aux */
+	for (i = 0; i < AC97_WRITE_RETRY; i++) {
+		/* send_write_request */
+		local_irq_disable();
+		HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
+		HACREG(HACCSDR) = (val << CSDR_SHIFT);
+		HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD);
+		local_irq_enable();
+
+		/* poll-wait for CMDAMT and CMDDMT */
+		for (to = TMO_E1;
+		     to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT));
+		     --to)
+			udelay(1);
+
+		HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT);
+		if (to)
+			break;
+		/* timeout, try again */
+	}
+}
+
+static unsigned short hac_ac97_read(struct snd_ac97 *ac97,
+				    unsigned short reg)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	return hac_read_codec_aux(hac, re