Merge branch 'for-linus' of git://git.alsa-project.org/alsa-kernel

* 'for-linus' of git://git.alsa-project.org/alsa-kernel: (258 commits)
  ALSA: hda: VREF powerdown for headphones
  ALSA: hda: STAC_HP_M4
  ALSA: ASoC: Check for machine type in GTA01 machine driver
  ALSA: mtpav - Fix race in probe
  ALSA: usb-audio: dynamic detection of MIDI interfaces in uaxx-quirk
  ALSA: Add a note on dependency of RTC stuff
  ALSA: ASoC: add new param mux to dapm_mux_update_power
  ALSA: Increase components array size
  ALSA: ASoC: Correct inverted Mic PGA Switch control in wm8510 driver
  ALSA: hda: comment typo fix
  ALSA: hda: comment typo fix
  ALSA: hda - Fix PCI SSID for ASROCK K18N78FullHD-hSLI
  ALSA: snd-usb-audio: support for Edirol UA-4FX device
  ALSA: usb - Fix possible Oops at USB-MIDI disconnection
  ALSA: hda - Fix another ALC889A (rev 0x100101)
  ALSA: hda: add more board-specific information for Realtek ALC662 rev1
  ALSA: Correct Vladimir Barinov's e-mail address
  ALSA: cs46xx: Add PCI IDs for TerraTec and Hercules cards
  ALSA: hda: SPDIF stream muting support
  ALSA: hda: appletv support
  ...
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index b117e42..e0e54a2 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -746,8 +746,10 @@
   Module snd-hda-intel
   --------------------
 
-    Module for Intel HD Audio (ICH6, ICH6M, ESB2, ICH7, ICH8),
-		ATI SB450, SB600, RS600,
+    Module for Intel HD Audio (ICH6, ICH6M, ESB2, ICH7, ICH8, ICH9, ICH10,
+			PCH, SCH),
+		ATI SB450, SB600, R600, RS600, RS690, RS780, RV610, RV620,
+			RV630, RV635, RV670, RV770,
 		VIA VT8251/VT8237A,
 		SIS966, ULI M5461
 
@@ -807,6 +809,7 @@
 	ALC260
 	  hp		HP machines
 	  hp-3013	HP machines (3013-variant)
+	  hp-dc7600	HP DC7600
 	  fujitsu	Fujitsu S7020
 	  acer		Acer TravelMate
 	  will		Will laptops (PB V7900)
@@ -828,8 +831,11 @@
 	  hippo		Hippo (ATI) with jack detection, Sony UX-90s
 	  hippo_1	Hippo (Benq) with jack detection
 	  sony-assamd	Sony ASSAMD
+	  toshiba-s06	Toshiba S06
+	  toshiba-rx1	Toshiba RX1
 	  ultra		Samsung Q1 Ultra Vista model
 	  lenovo-3000	Lenovo 3000 y410
+	  nec		NEC Versa S9100
 	  basic		fixed pin assignment w/o SPDIF
 	  auto		auto-config reading BIOS (default)
 
@@ -838,6 +844,7 @@
 	  3stack	3-stack model
 	  toshiba	Toshiba A205
 	  acer		Acer laptops
+	  acer-aspire	Acer Aspire One
 	  dell		Dell OEM laptops (Vostro 1200)
 	  zepto		Zepto laptops
 	  test		for testing/debugging purpose, almost all controls can
@@ -847,6 +854,9 @@
 
 	ALC269
 	  basic		Basic preset
+	  quanta	Quanta FL1
+	  eeepc-p703	ASUS Eeepc P703 P900A
+	  eeepc-p901	ASUS Eeepc P901 S101
 
 	ALC662/663
 	  3stack-dig	3-stack (2-channel) with SPDIF
@@ -856,10 +866,17 @@
 	  lenovo-101e	 Lenovo laptop
 	  eeepc-p701	ASUS Eeepc P701
 	  eeepc-ep20	ASUS Eeepc EP20
+	  ecs		ECS/Foxconn mobo
 	  m51va		ASUS M51VA
 	  g71v		ASUS G71V
 	  h13		ASUS H13
 	  g50v		ASUS G50V
+	  asus-mode1	ASUS
+	  asus-mode2	ASUS
+	  asus-mode3	ASUS
+	  asus-mode4	ASUS
+	  asus-mode5	ASUS
+	  asus-mode6	ASUS
 	  auto		auto-config reading BIOS (default)
 
 	ALC882/885
@@ -891,12 +908,14 @@
 	  lenovo-101e	Lenovo 101E
 	  lenovo-nb0763	Lenovo NB0763
 	  lenovo-ms7195-dig Lenovo MS7195
+	  lenovo-sky	Lenovo Sky
 	  haier-w66	Haier W66
 	  3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
 	  6stack-dell	Dell machines with 6stack (Inspiron 530)
 	  mitac		Mitac 8252D
 	  clevo-m720	Clevo M720 laptop series
 	  fujitsu-pi2515 Fujitsu AMILO Pi2515
+	  3stack-6ch-intel Intel DG33* boards
 	  auto		auto-config reading BIOS (default)
 
 	ALC861/660
@@ -929,7 +948,7 @@
 	  allout	5-jack in back, 2-jack in front, SPDIF out
 	  auto		auto-config reading BIOS (default)
 
-	AD1882
+	AD1882 / AD1882A
 	  3stack	3-stack mode (default)
 	  6stack	6-stack mode
 
@@ -1079,7 +1098,7 @@
 	    register value without FIFO size correction as the current
 	    DMA pointer.  position_fix=2 will make the driver to use
 	    the position buffer instead of reading SD_LPIB register.
-	    (Usually SD_LPLIB register is more accurate than the
+	    (Usually SD_LPIB register is more accurate than the
 	    position buffer.)
 
     NB: If you get many "azx_get_response timeout" messages at
@@ -1166,6 +1185,7 @@
 			* Event Electronics, EZ8
                         * Digigram VX442
 			* Lionstracs, Mediastaton
+			* Terrasoniq TS 88
 
     model       - Use the given board model, one of the following:
 		  delta1010, dio2496, delta66, delta44, audiophile, delta410,
@@ -1200,7 +1220,10 @@
 			* TerraTec Phase 22
 			* TerraTec Phase 28
 			* AudioTrak Prodigy 7.1
-			* AudioTrak Prodigy 7.1LT
+			* AudioTrak Prodigy 7.1 LT
+			* AudioTrak Prodigy 7.1 XT
+			* AudioTrak Prodigy 7.1 HIFI
+			* AudioTrak Prodigy 7.1 HD2
 			* AudioTrak Prodigy 192
 			* Pontis MS300
 			* Albatron K8X800 Pro II 
@@ -1211,12 +1234,16 @@
 			* Shuttle SN25P
 			* Onkyo SE-90PCI
 			* Onkyo SE-200PCI
+			* ESI Juli@
+			* Hercules Fortissimo IV
+			* EGO-SYS WaveTerminal 192M
 
     model       - Use the given board model, one of the following:
 		  revo51, revo71, amp2000, prodigy71, prodigy71lt,
-		  prodigy192, aureon51, aureon71, universe, ap192,
-		  k8x800, phase22, phase28, ms300, av710, se200pci,
-		  se90pci
+		  prodigy71xt, prodigy71hifi, prodigyhd2, prodigy192,
+		  juli, aureon51, aureon71, universe, ap192, k8x800,
+		  phase22, phase28, ms300, av710, se200pci, se90pci,
+		  fortissimo4, sn25p, WT192M
 
     This module supports multiple cards and autoprobe.
 
@@ -1255,7 +1282,7 @@
 
     Module for AC'97 motherboards from Intel and compatibles.
 			* Intel i810/810E, i815, i820, i830, i84x, MX440
-				ICH5, ICH6, ICH7, ESB2
+				ICH5, ICH6, ICH7, 6300ESB, ESB2
 			* SiS 7012 (SiS 735)
 			* NVidia NForce, NForce2, NForce3, MCP04, CK804
 				 CK8, CK8S, MCP501
@@ -1951,6 +1978,8 @@
 			* CHIC  True Sound 4Dwave
 			* Shark  Predator4D-PCI
 			* Jaton  SonicWave 4D
+			* SiS SI7018 PCI Audio
+			* Hoontech SoundTrack Digital 4DWave NX
 
     pcm_channels   - max channels (voices) reserved for PCM
     wavetable_size - max wavetable size in kB (4-?kb)
@@ -1966,12 +1995,25 @@
 
     vid             - Vendor ID for the device (optional)
     pid             - Product ID for the device (optional)
+    nrpacks	    - Max. number of packets per URB (default: 8)
+    async_unlink    - Use async unlink mode (default: yes)
     device_setup    - Device specific magic number (optional)
                     - Influence depends on the device
                     - Default: 0x0000 
+    ignore_ctl_error - Ignore any USB-controller regarding mixer
+    		       interface (default: no)
 
     This module supports multiple devices, autoprobe and hotplugging.
 
+    NB: nrpacks parameter can be modified dynamically via sysfs.
+        Don't put the value over 20.  Changing via sysfs has no sanity
+	check.
+    NB: async_unlink=0 would cause Oops.  It remains just for
+        debugging purpose (if any).
+    NB: ignore_ctl_error=1 may help when you get an error at accessing
+        the mixer element such as URB error -22.  This happens on some
+        buggy USB device or the controller.
+
   Module snd-usb-caiaq
   --------------------
 
@@ -2078,7 +2120,7 @@
   -------------------
 
     Module for sound cards based on the Asus AV100/AV200 chips,
-    i.e., Xonar D1, DX, D2 and D2X.
+    i.e., Xonar D1, DX, D2, D2X and HDAV1.3 (Deluxe).
 
     This module supports autoprobe and multiple cards.
 
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
index e13c4e6..b54cb50 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
@@ -6135,44 +6135,58 @@
       </para>
     </section>
 
-    <section id="useful-functions-snd-assert">
-      <title><function>snd_assert()</function></title>
-      <para>
-        <function>snd_assert()</function> macro is similar with the
-      normal <function>assert()</function> macro. For example,  
-
-        <informalexample>
-          <programlisting>
-<![CDATA[
-  snd_assert(pointer != NULL, return -EINVAL);
-]]>
-          </programlisting>
-        </informalexample>
-      </para>
-
-      <para>
-        The first argument is the expression to evaluate, and the
-      second argument is the action if it fails. When
-      <constant>CONFIG_SND_DEBUG</constant>, is set, it will show an
-      error message such as <computeroutput>BUG? (xxx)</computeroutput>
-      together with stack trace.
-      </para>
-      <para>
-	 When no debug flag is set, this macro is ignored. 
-      </para>
-    </section>
-
     <section id="useful-functions-snd-bug">
       <title><function>snd_BUG()</function></title>
       <para>
         It shows the <computeroutput>BUG?</computeroutput> message and
-      stack trace as well as <function>snd_assert</function> at the point.
+      stack trace as well as <function>snd_BUG_ON</function> at the point.
       It's useful to show that a fatal error happens there. 
       </para>
       <para>
 	 When no debug flag is set, this macro is ignored. 
       </para>
     </section>
+
+    <section id="useful-functions-snd-bug-on">
+      <title><function>snd_BUG_ON()</function></title>
+      <para>
+        <function>snd_BUG_ON()</function> macro is similar with
+	<function>WARN_ON()</function> macro. For example,  
+
+        <informalexample>
+          <programlisting>
+<![CDATA[
+  snd_BUG_ON(!pointer);
+]]>
+          </programlisting>
+        </informalexample>
+
+	or it can be used as the condition,
+        <informalexample>
+          <programlisting>
+<![CDATA[
+  if (snd_BUG_ON(non_zero_is_bug))
+          return -EINVAL;
+]]>
+          </programlisting>
+        </informalexample>
+
+      </para>
+
+      <para>
+        The macro takes an conditional expression to evaluate.
+	When <constant>CONFIG_SND_DEBUG</constant>, is set, the
+	expression is actually evaluated. If it's non-zero, it shows
+	the warning message such as
+	<computeroutput>BUG? (xxx)</computeroutput>
+	normally followed by stack trace.  It returns the evaluated
+	value.
+	When no <constant>CONFIG_SND_DEBUG</constant> is set, this
+	macro always returns zero.
+      </para>
+
+    </section>
+
   </chapter>
 
 
diff --git a/MAINTAINERS b/MAINTAINERS
index 7a03bd5..ece4e27 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3828,6 +3828,8 @@
 SOUND
 P:	Jaroslav Kysela
 M:	perex@perex.cz
+P:	Takashi Iwai
+M:	tiwai@suse.de
 L:	alsa-devel@alsa-project.org (subscribers-only)
 S:	Maintained
 
diff --git a/arch/arm/plat-omap/include/mach/mtd-xip.h b/arch/arm/plat-omap/include/mach/mtd-xip.h
index 5cee7e1..39b591f 100644
--- a/arch/arm/plat-omap/include/mach/mtd-xip.h
+++ b/arch/arm/plat-omap/include/mach/mtd-xip.h
@@ -3,7 +3,7 @@
  *
  * Do not include this file directly. It's included from linux/mtd/xip.h
  *
- * Author: Vladimir Barinov <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov <vbarinov@embeddedalley.com>
  *
  * (c) 2005 MontaVista Software, Inc.  This file is licensed under the
  * terms of the GNU General Public License version 2.  This program is
diff --git a/arch/um/Kconfig.char b/arch/um/Kconfig.char
index 1b238eb..70dabd1 100644
--- a/arch/um/Kconfig.char
+++ b/arch/um/Kconfig.char
@@ -203,6 +203,10 @@
 	tristate
 	default UML_SOUND
 
+config SOUND_OSS_CORE
+	bool
+	default UML_SOUND
+
 config HOSTAUDIO
 	tristate
 	default UML_SOUND
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index 9929d20..26194a0 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -488,10 +488,12 @@
 	period_size = params_period_bytes(hw_params);
 	periods = params_periods(hw_params);
 
-	snd_assert(period_size >= 0x100 && period_size <= 0x10000,
-		   return -EINVAL);
-	snd_assert(periods >= 4, return -EINVAL);
-	snd_assert(period_size * periods <= 1024 * 1024, return -EINVAL);
+	if (period_size < 0x100 || period_size > 0x10000)
+		return -EINVAL;
+	if (periods < 4)
+		return -EINVAL;
+	if (period_size * periods > 1024 * 1024)
+		return -EINVAL;
 
 	dev = saa7134->dev;
 
@@ -942,7 +944,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(chip != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip))
+		return -EINVAL;
 	strcpy(card->mixername, "SAA7134 Mixer");
 
 	for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_controls); idx++) {
diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c
index f9575c4..9c32063 100644
--- a/drivers/usb/host/ehci-ixp4xx.c
+++ b/drivers/usb/host/ehci-ixp4xx.c
@@ -1,7 +1,7 @@
 /*
  * IXP4XX EHCI Host Controller Driver
  *
- * Author: Vladimir Barinov <vbarinov@ru.mvista.com>
+ * Author: Vladimir Barinov <vbarinov@embeddedalley.com>
  *
  * Based on "ehci-fsl.c" by Randy Vinson <rvinson@mvista.com>
  *
diff --git a/include/sound/ad1848.h b/include/sound/ad1848.h
deleted file mode 100644
index d9aebdf..0000000
--- a/include/sound/ad1848.h
+++ /dev/null
@@ -1,218 +0,0 @@
-#ifndef __SOUND_AD1848_H
-#define __SOUND_AD1848_H
-
-/*
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *  Definitions for AD1847/AD1848/CS4248 chips
- *
- *
- *   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 "pcm.h"
-#include <linux/interrupt.h>
-
-/* IO ports */
-
-#define AD1848P( chip, x ) ( (chip) -> port + c_d_c_AD1848##x )
-
-#define c_d_c_AD1848REGSEL	0
-#define c_d_c_AD1848REG		1
-#define c_d_c_AD1848STATUS	2
-#define c_d_c_AD1848PIO		3
-
-/* codec registers */
-
-#define AD1848_LEFT_INPUT	0x00	/* left input control */
-#define AD1848_RIGHT_INPUT	0x01	/* right input control */
-#define AD1848_AUX1_LEFT_INPUT	0x02	/* left AUX1 input control */
-#define AD1848_AUX1_RIGHT_INPUT	0x03	/* right AUX1 input control */
-#define AD1848_AUX2_LEFT_INPUT	0x04	/* left AUX2 input control */
-#define AD1848_AUX2_RIGHT_INPUT	0x05	/* right AUX2 input control */
-#define AD1848_LEFT_OUTPUT	0x06	/* left output control register */
-#define AD1848_RIGHT_OUTPUT	0x07	/* right output control register */
-#define AD1848_DATA_FORMAT	0x08	/* clock and data format - playback/capture - bits 7-0 MCE */
-#define AD1848_IFACE_CTRL	0x09	/* interface control - bits 7-2 MCE */
-#define AD1848_PIN_CTRL		0x0a	/* pin control */
-#define AD1848_TEST_INIT	0x0b	/* test and initialization */
-#define AD1848_MISC_INFO	0x0c	/* miscellaneous information */
-#define AD1848_LOOPBACK		0x0d	/* loopback control */
-#define AD1848_DATA_UPR_CNT	0x0e	/* playback/capture upper base count */
-#define AD1848_DATA_LWR_CNT	0x0f	/* playback/capture lower base count */
-
-/* definitions for codec register select port - CODECP( REGSEL ) */
-
-#define AD1848_INIT		0x80	/* CODEC is initializing */
-#define AD1848_MCE		0x40	/* mode change enable */
-#define AD1848_TRD		0x20	/* transfer request disable */
-
-/* definitions for codec status register - CODECP( STATUS ) */
-
-#define AD1848_GLOBALIRQ	0x01	/* IRQ is active */
-
-/* definitions for AD1848_LEFT_INPUT and AD1848_RIGHT_INPUT registers */
-
-#define AD1848_ENABLE_MIC_GAIN	0x20
-
-#define AD1848_MIXS_LINE1	0x00
-#define AD1848_MIXS_AUX1	0x40
-#define AD1848_MIXS_LINE2	0x80
-#define AD1848_MIXS_ALL		0xc0
-
-/* definitions for clock and data format register - AD1848_PLAYBK_FORMAT */
-
-#define AD1848_LINEAR_8		0x00	/* 8-bit unsigned data */
-#define AD1848_ALAW_8		0x60	/* 8-bit A-law companded */
-#define AD1848_ULAW_8		0x20	/* 8-bit U-law companded */
-#define AD1848_LINEAR_16	0x40	/* 16-bit twos complement data - little endian */
-#define AD1848_STEREO		0x10	/* stereo mode */
-/* bits 3-1 define frequency divisor */
-#define AD1848_XTAL1		0x00	/* 24.576 crystal */
-#define AD1848_XTAL2		0x01	/* 16.9344 crystal */
-
-/* definitions for interface control register - AD1848_IFACE_CTRL */
-
-#define AD1848_CAPTURE_PIO	0x80	/* capture PIO enable */
-#define AD1848_PLAYBACK_PIO	0x40	/* playback PIO enable */
-#define AD1848_CALIB_MODE	0x18	/* calibration mode bits */
-#define AD1848_AUTOCALIB	0x08	/* auto calibrate */
-#define AD1848_SINGLE_DMA	0x04	/* use single DMA channel */
-#define AD1848_CAPTURE_ENABLE	0x02	/* capture enable */
-#define AD1848_PLAYBACK_ENABLE	0x01	/* playback enable */
-
-/* definitions for pin control register - AD1848_PIN_CTRL */
-
-#define AD1848_IRQ_ENABLE	0x02	/* enable IRQ */
-#define AD1848_XCTL1		0x40	/* external control #1 */
-#define AD1848_XCTL0		0x80	/* external control #0 */
-
-/* definitions for test and init register - AD1848_TEST_INIT */
-
-#define AD1848_CALIB_IN_PROGRESS 0x20	/* auto calibrate in progress */
-#define AD1848_DMA_REQUEST	0x10	/* DMA request in progress */
-
-/* defines for codec.mode */
-
-#define AD1848_MODE_NONE	0x0000
-#define AD1848_MODE_PLAY	0x0001
-#define AD1848_MODE_CAPTURE	0x0002
-#define AD1848_MODE_TIMER	0x0004
-#define AD1848_MODE_OPEN	(AD1848_MODE_PLAY|AD1848_MODE_CAPTURE|AD1848_MODE_TIMER)
-#define AD1848_MODE_RUNNING	0x0010
-
-/* defines for codec.hardware */
-
-#define AD1848_HW_DETECT	0x0000	/* let AD1848 driver detect chip */
-#define AD1848_HW_AD1847	0x0001	/* AD1847 chip */
-#define AD1848_HW_AD1848	0x0002	/* AD1848 chip */
-#define AD1848_HW_CS4248	0x0003	/* CS4248 chip */
-#define AD1848_HW_CMI8330	0x0004	/* CMI8330 chip */
-#define AD1848_HW_THINKPAD	0x0005	/* Thinkpad 360/750/755 */
-
-/* IBM Thinkpad specific stuff */
-#define AD1848_THINKPAD_CTL_PORT1		0x15e8
-#define AD1848_THINKPAD_CTL_PORT2		0x15e9
-#define AD1848_THINKPAD_CS4248_ENABLE_BIT	0x02
-
-struct snd_ad1848 {
-	unsigned long port;		/* i/o port */
-	struct resource *res_port;
-	int irq;			/* IRQ line */
-	int dma;			/* data DMA */
-	unsigned short version;		/* version of CODEC chip */
-	unsigned short mode;		/* see to AD1848_MODE_XXXX */
-	unsigned short hardware;	/* see to AD1848_HW_XXXX */
-	unsigned short single_dma:1;	/* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */
-
-	struct snd_pcm *pcm;
-	struct snd_pcm_substream *playback_substream;
-	struct snd_pcm_substream *capture_substream;
-	struct snd_card *card;
-
-	unsigned char image[32];	/* SGalaxy needs an access to extended registers */
-	int mce_bit;
-	int calibrate_mute;
-	int dma_size;
-	int thinkpad_flag;		/* Thinkpad CS4248 needs some extra help */
-
-#ifdef CONFIG_PM
-	void (*suspend)(struct snd_ad1848 *chip);
-	void (*resume)(struct snd_ad1848 *chip);
-#endif
-
-	spinlock_t reg_lock;
-};
-
-/* exported functions */
-
-void snd_ad1848_out(struct snd_ad1848 *chip, unsigned char reg, unsigned char value);
-
-int snd_ad1848_create(struct snd_card *card,
-		      unsigned long port,
-		      int irq, int dma,
-		      unsigned short hardware,
-		      struct snd_ad1848 ** chip);
-
-int snd_ad1848_pcm(struct snd_ad1848 * chip, int device, struct snd_pcm **rpcm);
-const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction);
-int snd_ad1848_mixer(struct snd_ad1848 * chip);
-
-/* exported mixer stuffs */
-enum { AD1848_MIX_SINGLE, AD1848_MIX_DOUBLE, AD1848_MIX_CAPTURE };
-
-#define AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) \
-	((reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24))
-#define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \
-	((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22))
-
-/* for ease of use */
-struct ad1848_mix_elem {
-	const char *name;
-	int index;
-	int type;
-	unsigned long private_value;
-	const unsigned int *tlv;
-};
-
-#define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ .name = xname, \
-  .index = xindex, \
-  .type = AD1848_MIX_SINGLE, \
-  .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) }
-
-#define AD1848_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
-{ .name = xname, \
-  .index = xindex, \
-  .type = AD1848_MIX_SINGLE, \
-  .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert), \
-  .tlv = xtlv }
-
-#define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ .name = xname, \
-  .index = xindex, \
-  .type = AD1848_MIX_DOUBLE, \
-  .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) }
-
-#define AD1848_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
-{ .name = xname, \
-  .index = xindex, \
-  .type = AD1848_MIX_DOUBLE, \
-  .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert), \
-  .tlv = xtlv }
-
-int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c);
-
-#endif /* __SOUND_AD1848_H */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 3eaf155b..2c4dc90 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -93,9 +93,10 @@
 	SNDRV_HWDEP_IFACE_PCXHR,	/* Digigram PCXHR */
 	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
 	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
+	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_HDA
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
 };
 
 struct snd_hwdep_info {
@@ -296,29 +297,39 @@
 	unsigned char reserved[64];	/* reserved for future... */
 };
 
-typedef int __bitwise snd_pcm_hw_param_t;
-#define	SNDRV_PCM_HW_PARAM_ACCESS	((__force snd_pcm_hw_param_t) 0) /* Access type */
-#define	SNDRV_PCM_HW_PARAM_FORMAT	((__force snd_pcm_hw_param_t) 1) /* Format */
-#define	SNDRV_PCM_HW_PARAM_SUBFORMAT	((__force snd_pcm_hw_param_t) 2) /* Subformat */
+typedef int snd_pcm_hw_param_t;
+#define	SNDRV_PCM_HW_PARAM_ACCESS	0	/* Access type */
+#define	SNDRV_PCM_HW_PARAM_FORMAT	1	/* Format */
+#define	SNDRV_PCM_HW_PARAM_SUBFORMAT	2	/* Subformat */
 #define	SNDRV_PCM_HW_PARAM_FIRST_MASK	SNDRV_PCM_HW_PARAM_ACCESS
 #define	SNDRV_PCM_HW_PARAM_LAST_MASK	SNDRV_PCM_HW_PARAM_SUBFORMAT
 
-#define	SNDRV_PCM_HW_PARAM_SAMPLE_BITS	((__force snd_pcm_hw_param_t) 8) /* Bits per sample */
-#define	SNDRV_PCM_HW_PARAM_FRAME_BITS	((__force snd_pcm_hw_param_t) 9) /* Bits per frame */
-#define	SNDRV_PCM_HW_PARAM_CHANNELS	((__force snd_pcm_hw_param_t) 10) /* Channels */
-#define	SNDRV_PCM_HW_PARAM_RATE		((__force snd_pcm_hw_param_t) 11) /* Approx rate */
-#define	SNDRV_PCM_HW_PARAM_PERIOD_TIME	((__force snd_pcm_hw_param_t) 12) /* Approx distance between interrupts in us */
-#define	SNDRV_PCM_HW_PARAM_PERIOD_SIZE	((__force snd_pcm_hw_param_t) 13) /* Approx frames between interrupts */
-#define	SNDRV_PCM_HW_PARAM_PERIOD_BYTES	((__force snd_pcm_hw_param_t) 14) /* Approx bytes between interrupts */
-#define	SNDRV_PCM_HW_PARAM_PERIODS	((__force snd_pcm_hw_param_t) 15) /* Approx interrupts per buffer */
-#define	SNDRV_PCM_HW_PARAM_BUFFER_TIME	((__force snd_pcm_hw_param_t) 16) /* Approx duration of buffer in us */
-#define	SNDRV_PCM_HW_PARAM_BUFFER_SIZE	((__force snd_pcm_hw_param_t) 17) /* Size of buffer in frames */
-#define	SNDRV_PCM_HW_PARAM_BUFFER_BYTES	((__force snd_pcm_hw_param_t) 18) /* Size of buffer in bytes */
-#define	SNDRV_PCM_HW_PARAM_TICK_TIME	((__force snd_pcm_hw_param_t) 19) /* Approx tick duration in us */
+#define	SNDRV_PCM_HW_PARAM_SAMPLE_BITS	8	/* Bits per sample */
+#define	SNDRV_PCM_HW_PARAM_FRAME_BITS	9	/* Bits per frame */
+#define	SNDRV_PCM_HW_PARAM_CHANNELS	10	/* Channels */
+#define	SNDRV_PCM_HW_PARAM_RATE		11	/* Approx rate */
+#define	SNDRV_PCM_HW_PARAM_PERIOD_TIME	12	/* Approx distance between
+						 * interrupts in us
+						 */
+#define	SNDRV_PCM_HW_PARAM_PERIOD_SIZE	13	/* Approx frames between
+						 * interrupts
+						 */
+#define	SNDRV_PCM_HW_PARAM_PERIOD_BYTES	14	/* Approx bytes between
+						 * interrupts
+						 */
+#define	SNDRV_PCM_HW_PARAM_PERIODS	15	/* Approx interrupts per
+						 * buffer
+						 */
+#define	SNDRV_PCM_HW_PARAM_BUFFER_TIME	16	/* Approx duration of buffer
+						 * in us
+						 */
+#define	SNDRV_PCM_HW_PARAM_BUFFER_SIZE	17	/* Size of buffer in frames */
+#define	SNDRV_PCM_HW_PARAM_BUFFER_BYTES	18	/* Size of buffer in bytes */
+#define	SNDRV_PCM_HW_PARAM_TICK_TIME	19	/* Approx tick duration in us */
 #define	SNDRV_PCM_HW_PARAM_FIRST_INTERVAL	SNDRV_PCM_HW_PARAM_SAMPLE_BITS
 #define	SNDRV_PCM_HW_PARAM_LAST_INTERVAL	SNDRV_PCM_HW_PARAM_TICK_TIME
 
-#define SNDRV_PCM_HW_PARAMS_NORESAMPLE		(1<<0)	/* avoid rate resampling */
+#define SNDRV_PCM_HW_PARAMS_NORESAMPLE	(1<<0)	/* avoid rate resampling */
 
 struct snd_interval {
 	unsigned int min, max;
@@ -696,7 +707,7 @@
  *                                                                          *
  ****************************************************************************/
 
-#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 5)
+#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 6)
 
 struct snd_ctl_card_info {
 	int card;			/* card number */
@@ -707,8 +718,7 @@
 	unsigned char longname[80];	/* name + info text about soundcard */
 	unsigned char reserved_[16];	/* reserved for future (was ID of mixer) */
 	unsigned char mixername[80];	/* visual mixer identification */
-	unsigned char components[80];	/* card components / fine identification, delimited with one space (AC97 etc..) */
-	unsigned char reserved[48];	/* reserved for future */
+	unsigned char components[128];	/* card components / fine identification, delimited with one space (AC97 etc..) */
 };
 
 typedef int __bitwise snd_ctl_elem_type_t;
diff --git a/include/sound/asoundef.h b/include/sound/asoundef.h
index a6e0fac..20ebf32 100644
--- a/include/sound/asoundef.h
+++ b/include/sound/asoundef.h
@@ -60,35 +60,56 @@
 #define IEC958_AES1_PRO_USERBITS_UDEF	(12<<4)	/* user defined application */
 #define IEC958_AES1_CON_CATEGORY	0x7f
 #define IEC958_AES1_CON_GENERAL		0x00
-#define IEC958_AES1_CON_EXPERIMENTAL	0x40
-#define IEC958_AES1_CON_SOLIDMEM_MASK	0x0f
-#define IEC958_AES1_CON_SOLIDMEM_ID	0x08
-#define IEC958_AES1_CON_BROADCAST1_MASK 0x07
-#define IEC958_AES1_CON_BROADCAST1_ID	0x04
-#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07
-#define IEC958_AES1_CON_DIGDIGCONV_ID	0x02
-#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f
-#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x06
-#define IEC958_AES1_CON_ADC_MASK	0x1f
-#define IEC958_AES1_CON_ADC_ID		0x16
-#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f
-#define IEC958_AES1_CON_BROADCAST2_ID	0x0e
 #define IEC958_AES1_CON_LASEROPT_MASK	0x07
 #define IEC958_AES1_CON_LASEROPT_ID	0x01
-#define IEC958_AES1_CON_MUSICAL_MASK	0x07
-#define IEC958_AES1_CON_MUSICAL_ID	0x05
-#define IEC958_AES1_CON_MAGNETIC_MASK	0x07
-#define IEC958_AES1_CON_MAGNETIC_ID	0x03
 #define IEC958_AES1_CON_IEC908_CD	(IEC958_AES1_CON_LASEROPT_ID|0x00)
 #define IEC958_AES1_CON_NON_IEC908_CD	(IEC958_AES1_CON_LASEROPT_ID|0x08)
+#define IEC958_AES1_CON_MINI_DISC	(IEC958_AES1_CON_LASEROPT_ID|0x48)
+#define IEC958_AES1_CON_DVD		(IEC958_AES1_CON_LASEROPT_ID|0x18)
+#define IEC958_AES1_CON_LASTEROPT_OTHER	(IEC958_AES1_CON_LASEROPT_ID|0x78)
+#define IEC958_AES1_CON_DIGDIGCONV_MASK 0x07
+#define IEC958_AES1_CON_DIGDIGCONV_ID	0x02
 #define IEC958_AES1_CON_PCM_CODER	(IEC958_AES1_CON_DIGDIGCONV_ID|0x00)
-#define IEC958_AES1_CON_SAMPLER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x20)
 #define IEC958_AES1_CON_MIXER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x10)
 #define IEC958_AES1_CON_RATE_CONVERTER	(IEC958_AES1_CON_DIGDIGCONV_ID|0x18)
-#define IEC958_AES1_CON_SYNTHESIZER	(IEC958_AES1_CON_MUSICAL_ID|0x00)
-#define IEC958_AES1_CON_MICROPHONE	(IEC958_AES1_CON_MUSICAL_ID|0x08)
+#define IEC958_AES1_CON_SAMPLER		(IEC958_AES1_CON_DIGDIGCONV_ID|0x20)
+#define IEC958_AES1_CON_DSP		(IEC958_AES1_CON_DIGDIGCONV_ID|0x28)
+#define IEC958_AES1_CON_DIGDIGCONV_OTHER (IEC958_AES1_CON_DIGDIGCONV_ID|0x78)
+#define IEC958_AES1_CON_MAGNETIC_MASK	0x07
+#define IEC958_AES1_CON_MAGNETIC_ID	0x03
 #define IEC958_AES1_CON_DAT		(IEC958_AES1_CON_MAGNETIC_ID|0x00)
 #define IEC958_AES1_CON_VCR		(IEC958_AES1_CON_MAGNETIC_ID|0x08)
+#define IEC958_AES1_CON_DCC		(IEC958_AES1_CON_MAGNETIC_ID|0x40)
+#define IEC958_AES1_CON_MAGNETIC_DISC	(IEC958_AES1_CON_MAGNETIC_ID|0x18)
+#define IEC958_AES1_CON_MAGNETIC_OTHER	(IEC958_AES1_CON_MAGNETIC_ID|0x78)
+#define IEC958_AES1_CON_BROADCAST1_MASK 0x07
+#define IEC958_AES1_CON_BROADCAST1_ID	0x04
+#define IEC958_AES1_CON_DAB_JAPAN	(IEC958_AES1_CON_BROADCAST1_ID|0x00)
+#define IEC958_AES1_CON_DAB_EUROPE	(IEC958_AES1_CON_BROADCAST1_ID|0x08)
+#define IEC958_AES1_CON_DAB_USA		(IEC958_AES1_CON_BROADCAST1_ID|0x60)
+#define IEC958_AES1_CON_SOFTWARE	(IEC958_AES1_CON_BROADCAST1_ID|0x40)
+#define IEC958_AES1_CON_IEC62105	(IEC958_AES1_CON_BROADCAST1_ID|0x20)
+#define IEC958_AES1_CON_BROADCAST1_OTHER (IEC958_AES1_CON_BROADCAST1_ID|0x78)
+#define IEC958_AES1_CON_BROADCAST2_MASK 0x0f
+#define IEC958_AES1_CON_BROADCAST2_ID	0x0e
+#define IEC958_AES1_CON_MUSICAL_MASK	0x07
+#define IEC958_AES1_CON_MUSICAL_ID	0x05
+#define IEC958_AES1_CON_SYNTHESIZER	(IEC958_AES1_CON_MUSICAL_ID|0x00)
+#define IEC958_AES1_CON_MICROPHONE	(IEC958_AES1_CON_MUSICAL_ID|0x08)
+#define IEC958_AES1_CON_MUSICAL_OTHER	(IEC958_AES1_CON_MUSICAL_ID|0x78)
+#define IEC958_AES1_CON_ADC_MASK	0x1f
+#define IEC958_AES1_CON_ADC_ID		0x06
+#define IEC958_AES1_CON_ADC		(IEC958_AES1_CON_ADC_ID|0x00)
+#define IEC958_AES1_CON_ADC_OTHER	(IEC958_AES1_CON_ADC_ID|0x60)
+#define IEC958_AES1_CON_ADC_COPYRIGHT_MASK 0x1f
+#define IEC958_AES1_CON_ADC_COPYRIGHT_ID 0x16
+#define IEC958_AES1_CON_ADC_COPYRIGHT	(IEC958_AES1_CON_ADC_COPYRIGHT_ID|0x00)
+#define IEC958_AES1_CON_ADC_COPYRIGHT_OTHER (IEC958_AES1_CON_ADC_COPYRIGHT_ID|0x60)
+#define IEC958_AES1_CON_SOLIDMEM_MASK	0x0f
+#define IEC958_AES1_CON_SOLIDMEM_ID	0x08
+#define IEC958_AES1_CON_SOLIDMEM_DIGITAL_RECORDER_PLAYER (IEC958_AES1_CON_SOLIDMEM_ID|0x00)
+#define IEC958_AES1_CON_SOLIDMEM_OTHER	(IEC958_AES1_CON_SOLIDMEM_ID|0x70)
+#define IEC958_AES1_CON_EXPERIMENTAL	0x40
 #define IEC958_AES1_CON_ORIGINAL	(1<<7)	/* this bits depends on the category code */
 #define IEC958_AES2_PRO_SBITS		(7<<0)	/* mask - sample bits */
 #define IEC958_AES2_PRO_SBITS_20	(2<<0)	/* 20-bit - coordination */
@@ -106,8 +127,16 @@
 #define IEC958_AES2_CON_CHANNEL_UNSPEC	(0<<4)	/* unspecified */
 #define IEC958_AES3_CON_FS		(15<<0)	/* mask - sample frequency */
 #define IEC958_AES3_CON_FS_44100	(0<<0)	/* 44.1kHz */
+#define IEC958_AES3_CON_FS_NOTID	(1<<0)	/* non indicated */
 #define IEC958_AES3_CON_FS_48000	(2<<0)	/* 48kHz */
 #define IEC958_AES3_CON_FS_32000	(3<<0)	/* 32kHz */
+#define IEC958_AES3_CON_FS_22050	(4<<0)	/* 22.05kHz */
+#define IEC958_AES3_CON_FS_24000	(6<<0)	/* 24kHz */
+#define IEC958_AES3_CON_FS_88200	(8<<0)	/* 88.2kHz */
+#define IEC958_AES3_CON_FS_768000	(9<<0)	/* 768kHz */
+#define IEC958_AES3_CON_FS_96000	(10<<0)	/* 96kHz */
+#define IEC958_AES3_CON_FS_176400	(12<<0)	/* 176.4kHz */
+#define IEC958_AES3_CON_FS_192000	(14<<0)	/* 192kHz */
 #define IEC958_AES3_CON_CLOCK		(3<<4)	/* mask - clock accuracy */
 #define IEC958_AES3_CON_CLOCK_1000PPM	(0<<4)	/* 1000 ppm */
 #define IEC958_AES3_CON_CLOCK_50PPM	(1<<4)	/* 50 ppm */
@@ -120,6 +149,26 @@
 #define IEC958_AES4_CON_WORDLEN_23_19	(4<<1)	/* 23-bit or 19-bit */
 #define IEC958_AES4_CON_WORDLEN_24_20	(5<<1)	/* 24-bit or 20-bit */
 #define IEC958_AES4_CON_WORDLEN_21_17	(6<<1)	/* 21-bit or 17-bit */
+#define IEC958_AES4_CON_ORIGFS		(15<<4)	/* mask - original sample frequency */
+#define IEC958_AES4_CON_ORIGFS_NOTID	(0<<4)	/* not indicated */
+#define IEC958_AES4_CON_ORIGFS_192000	(1<<4)	/* 192kHz */
+#define IEC958_AES4_CON_ORIGFS_12000	(2<<4)	/* 12kHz */
+#define IEC958_AES4_CON_ORIGFS_176400	(3<<4)	/* 176.4kHz */
+#define IEC958_AES4_CON_ORIGFS_96000	(5<<4)	/* 96kHz */
+#define IEC958_AES4_CON_ORIGFS_8000	(6<<4)	/* 8kHz */
+#define IEC958_AES4_CON_ORIGFS_88200	(7<<4)	/* 88.2kHz */
+#define IEC958_AES4_CON_ORIGFS_16000	(8<<4)	/* 16kHz */
+#define IEC958_AES4_CON_ORIGFS_24000	(9<<4)	/* 24kHz */
+#define IEC958_AES4_CON_ORIGFS_11025	(10<<4)	/* 11.025kHz */
+#define IEC958_AES4_CON_ORIGFS_22050	(11<<4)	/* 22.05kHz */
+#define IEC958_AES4_CON_ORIGFS_32000	(12<<4)	/* 32kHz */
+#define IEC958_AES4_CON_ORIGFS_48000	(13<<4)	/* 48kHz */
+#define IEC958_AES4_CON_ORIGFS_44100	(15<<4)	/* 44.1kHz */
+#define IEC958_AES5_CON_CGMSA		(3<<0)	/* mask - CGMS-A */
+#define IEC958_AES5_CON_CGMSA_COPYFREELY (0<<0)	/* copying is permitted without restriction */
+#define IEC958_AES5_CON_CGMSA_COPYONCE	(1<<0)	/* one generation of copies may be made */
+#define IEC958_AES5_CON_CGMSA_COPYNOMORE (2<<0)	/* condition not be used */
+#define IEC958_AES5_CON_CGMSA_COPYNEVER	(3<<0)	/* no copying is permitted */
 
 /*****************************************************************************
  *                                                                           *
diff --git a/include/sound/core.h b/include/sound/core.h
index 558b962..e5eec5f 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -28,6 +28,7 @@
 #include <linux/rwsem.h>		/* struct rw_semaphore */
 #include <linux/pm.h>			/* pm_message_t */
 #include <linux/device.h>
+#include <linux/stringify.h>
 
 /* number of supported soundcards */
 #ifdef CONFIG_SND_DYNAMIC_MINORS
@@ -63,6 +64,7 @@
 #define	SNDRV_DEV_INFO		((__force snd_device_type_t) 0x1006)
 #define	SNDRV_DEV_BUS		((__force snd_device_type_t) 0x1007)
 #define	SNDRV_DEV_CODEC		((__force snd_device_type_t) 0x1008)
+#define	SNDRV_DEV_JACK          ((__force snd_device_type_t) 0x1009)
 #define	SNDRV_DEV_LOWLEVEL	((__force snd_device_type_t) 0x2000)
 
 typedef int __bitwise snd_device_state_t;
@@ -114,7 +116,7 @@
 	char shortname[32];		/* short name of this soundcard */
 	char longname[80];		/* name of this soundcard */
 	char mixername[80];		/* mixer name */
-	char components[80];		/* card components delimited with
+	char components[128];		/* card components delimited with
 								space */
 	struct module *module;		/* top-level module */
 
@@ -366,8 +368,6 @@
 
 #ifdef CONFIG_SND_DEBUG
 
-#define __ASTRING__(x) #x
-
 #ifdef CONFIG_SND_VERBOSE_PRINTK
 /**
  * snd_printd - debug printk
@@ -382,33 +382,15 @@
 #define snd_printd(fmt, args...) \
 	printk(fmt ,##args)
 #endif
-/**
- * snd_assert - run-time assertion macro
- * @expr: expression
- *
- * This macro checks the expression in run-time and invokes the commands
- * given in the rest arguments if the assertion is failed.
- * When CONFIG_SND_DEBUG is not set, the expression is executed but
- * not checked.
- */
-#define snd_assert(expr, args...) do {					\
-	if (unlikely(!(expr))) {					\
-		snd_printk(KERN_ERR "BUG? (%s)\n", __ASTRING__(expr));	\
-		dump_stack();						\
-		args;							\
-	}								\
-} while (0)
 
-#define snd_BUG() do {				\
-	snd_printk(KERN_ERR "BUG?\n");		\
-	dump_stack();				\
-} while (0)
+#define snd_BUG()		WARN(1, "BUG?\n")
+#define snd_BUG_ON(cond)	WARN((cond), "BUG? (%s)\n", __stringify(cond))
 
 #else /* !CONFIG_SND_DEBUG */
 
 #define snd_printd(fmt, args...)	/* nothing */
-#define snd_assert(expr, args...)	(void)(expr)
 #define snd_BUG()			/* nothing */
+#define snd_BUG_ON(cond)	({/*(void)(cond);*/ 0;})  /* always false */
 
 #endif /* CONFIG_SND_DEBUG */
 
diff --git a/include/sound/cs4231.h b/include/sound/cs4231.h
deleted file mode 100644
index f0785f9..0000000
--- a/include/sound/cs4231.h
+++ /dev/null
@@ -1,175 +0,0 @@
-#ifndef __SOUND_CS4231_H
-#define __SOUND_CS4231_H
-
-/*
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *  Definitions for CS4231 & InterWave chips & compatible chips
- *
- *
- *   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 "control.h"
-#include "pcm.h"
-#include "timer.h"
-
-#include "cs4231-regs.h"
-
-/* defines for codec.mode */
-
-#define CS4231_MODE_NONE	0x0000
-#define CS4231_MODE_PLAY	0x0001
-#define CS4231_MODE_RECORD	0x0002
-#define CS4231_MODE_TIMER	0x0004
-#define CS4231_MODE_OPEN	(CS4231_MODE_PLAY|CS4231_MODE_RECORD|CS4231_MODE_TIMER)
-
-/* defines for codec.hardware */
-
-#define CS4231_HW_DETECT        0x0000	/* let CS4231 driver detect chip */
-#define CS4231_HW_DETECT3	0x0001	/* allow mode 3 */
-#define CS4231_HW_TYPE_MASK	0xff00	/* type mask */
-#define CS4231_HW_CS4231_MASK   0x0100	/* CS4231 serie */
-#define CS4231_HW_CS4231        0x0100	/* CS4231 chip */
-#define CS4231_HW_CS4231A       0x0101	/* CS4231A chip */
-#define CS4231_HW_AD1845	0x0102	/* AD1845 chip */
-#define CS4231_HW_CS4232_MASK   0x0200	/* CS4232 serie (has control ports) */
-#define CS4231_HW_CS4232        0x0200	/* CS4232 */
-#define CS4231_HW_CS4232A       0x0201	/* CS4232A */
-#define CS4231_HW_CS4236	0x0202	/* CS4236 */
-#define CS4231_HW_CS4236B_MASK	0x0400	/* CS4236B serie (has extended control regs) */
-#define CS4231_HW_CS4235	0x0400	/* CS4235 - Crystal Clear (tm) stereo enhancement */
-#define CS4231_HW_CS4236B       0x0401	/* CS4236B */
-#define CS4231_HW_CS4237B       0x0402	/* CS4237B - SRS 3D */
-#define CS4231_HW_CS4238B	0x0403	/* CS4238B - QSOUND 3D */
-#define CS4231_HW_CS4239	0x0404	/* CS4239 - Crystal Clear (tm) stereo enhancement */
-/* compatible, but clones */
-#define CS4231_HW_INTERWAVE     0x1000	/* InterWave chip */
-#define CS4231_HW_OPL3SA2       0x1101	/* OPL3-SA2 chip, similar to cs4231 */
-#define CS4231_HW_OPTI93X 	0x1102	/* Opti 930/931/933 */
-
-/* defines for codec.hwshare */
-#define CS4231_HWSHARE_IRQ	(1<<0)
-#define CS4231_HWSHARE_DMA1	(1<<1)
-#define CS4231_HWSHARE_DMA2	(1<<2)
-
-struct snd_cs4231 {
-	unsigned long port;		/* base i/o port */
-	struct resource *res_port;
-	unsigned long cport;		/* control base i/o port (CS4236) */
-	struct resource *res_cport;
-	int irq;			/* IRQ line */
-	int dma1;			/* playback DMA */
-	int dma2;			/* record DMA */
-	unsigned short version;		/* version of CODEC chip */
-	unsigned short mode;		/* see to CS4231_MODE_XXXX */
-	unsigned short hardware;	/* see to CS4231_HW_XXXX */
-	unsigned short hwshare;		/* shared resources */
-	unsigned short single_dma:1,	/* forced single DMA mode (GUS 16-bit daughter board) or dma1 == dma2 */
-		       ebus_flag:1;	/* SPARC: EBUS present */
-
-	struct snd_card *card;
-	struct snd_pcm *pcm;
-	struct snd_pcm_substream *playback_substream;
-	struct snd_pcm_substream *capture_substream;
-	struct snd_timer *timer;
-
-	unsigned char image[32];	/* registers image */
-	unsigned char eimage[32];	/* extended registers image */
-	unsigned char cimage[16];	/* control registers image */
-	int mce_bit;
-	int calibrate_mute;
-	int sw_3d_bit;
-	unsigned int p_dma_size;
-	unsigned int c_dma_size;
-
-	spinlock_t reg_lock;
-	struct mutex mce_mutex;
-	struct mutex open_mutex;
-
-	int (*rate_constraint) (struct snd_pcm_runtime *runtime);
-	void (*set_playback_format) (struct snd_cs4231 *chip, struct snd_pcm_hw_params *hw_params, unsigned char pdfr);
-	void (*set_capture_format) (struct snd_cs4231 *chip, struct snd_pcm_hw_params *hw_params, unsigned char cdfr);
-	void (*trigger) (struct snd_cs4231 *chip, unsigned int what, int start);
-#ifdef CONFIG_PM
-	void (*suspend) (struct snd_cs4231 *chip);
-	void (*resume) (struct snd_cs4231 *chip);
-#endif
-	void *dma_private_data;
-	int (*claim_dma) (struct snd_cs4231 *chip, void *dma_private_data, int dma);
-	int (*release_dma) (struct snd_cs4231 *chip, void *dma_private_data, int dma);
-};
-
-/* exported functions */
-
-void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val);
-unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg);
-void snd_cs4236_ext_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val);
-unsigned char snd_cs4236_ext_in(struct snd_cs4231 *chip, unsigned char reg);
-void snd_cs4231_mce_up(struct snd_cs4231 *chip);
-void snd_cs4231_mce_down(struct snd_cs4231 *chip);
-
-void snd_cs4231_overrange(struct snd_cs4231 *chip);
-
-irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id);
-
-const char *snd_cs4231_chip_id(struct snd_cs4231 *chip);
-
-int snd_cs4231_create(struct snd_card *card,
-		      unsigned long port,
-		      unsigned long cport,
-		      int irq, int dma1, int dma2,
-		      unsigned short hardware,
-		      unsigned short hwshare,
-		      struct snd_cs4231 ** rchip);
-int snd_cs4231_pcm(struct snd_cs4231 * chip, int device, struct snd_pcm **rpcm);
-int snd_cs4231_timer(struct snd_cs4231 * chip, int device, struct snd_timer **rtimer);
-int snd_cs4231_mixer(struct snd_cs4231 * chip);
-
-int snd_cs4236_create(struct snd_card *card,
-		      unsigned long port,
-		      unsigned long cport,
-		      int irq, int dma1, int dma2,
-		      unsigned short hardware,
-		      unsigned short hwshare,
-		      struct snd_cs4231 ** rchip);
-int snd_cs4236_pcm(struct snd_cs4231 * chip, int device, struct snd_pcm **rpcm);
-int snd_cs4236_mixer(struct snd_cs4231 * chip);
-
-/*
- *  mixer library
- */
-
-#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_cs4231_info_single, \
-  .get = snd_cs4231_get_single, .put = snd_cs4231_put_single, \
-  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
-
-int snd_cs4231_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
-int snd_cs4231_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
-int snd_cs4231_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
-
-#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_cs4231_info_double, \
-  .get = snd_cs4231_get_double, .put = snd_cs4231_put_double, \
-  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
-
-int snd_cs4231_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
-int snd_cs4231_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
-int snd_cs4231_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
-
-#endif /* __SOUND_CS4231_H */
diff --git a/include/sound/jack.h b/include/sound/jack.h
new file mode 100644
index 0000000..b1b2b8b
--- /dev/null
+++ b/include/sound/jack.h
@@ -0,0 +1,75 @@
+#ifndef __SOUND_JACK_H
+#define __SOUND_JACK_H
+
+/*
+ *  Jack abstraction layer
+ *
+ *  Copyright 2008 Wolfson Microelectronics plc
+ *
+ *
+ *   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/core.h>
+
+struct input_dev;
+
+/**
+ * Jack types which can be reported.  These values are used as a
+ * bitmask.
+ */
+enum snd_jack_types {
+	SND_JACK_HEADPHONE	= 0x0001,
+	SND_JACK_MICROPHONE	= 0x0002,
+	SND_JACK_HEADSET	= SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
+};
+
+struct snd_jack {
+	struct input_dev *input_dev;
+	int registered;
+	int type;
+	const char *id;
+	char name[100];
+};
+
+#ifdef CONFIG_SND_JACK
+
+int snd_jack_new(struct snd_card *card, const char *id, int type,
+		 struct snd_jack **jack);
+void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
+
+void snd_jack_report(struct snd_jack *jack, int status);
+
+#else
+
+static inline int snd_jack_new(struct snd_card *card, const char *id, int type,
+			       struct snd_jack **jack)
+{
+	return 0;
+}
+
+static inline void snd_jack_set_parent(struct snd_jack *jack,
+				       struct device *parent)
+{
+}
+
+static inline void snd_jack_report(struct snd_jack *jack, int status)
+{
+}
+
+#endif
+
+#endif
diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index ae2921d..d787a6b 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -65,6 +65,11 @@
 /*
  * Scatter-Gather generic device pages
  */
+void *snd_malloc_sgbuf_pages(struct device *device,
+			     size_t size, struct snd_dma_buffer *dmab,
+			     size_t *res_size);
+int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
+
 struct snd_sg_page {
 	void *buf;
 	dma_addr_t addr;
@@ -92,9 +97,18 @@
  */
 static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
 {
-	return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
+	dma_addr_t addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
+	addr &= PAGE_MASK;
+	return addr + offset % PAGE_SIZE;
 }
 
+/*
+ * return the virtual address at the corresponding offset
+ */
+static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
+{
+	return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
+}
 
 /* allocate/release a buffer */
 int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
diff --git a/include/sound/minors.h b/include/sound/minors.h
index 46bcd20..a81798a 100644
--- a/include/sound/minors.h
+++ b/include/sound/minors.h
@@ -21,6 +21,8 @@
  *
  */
 
+#define SNDRV_OS_MINORS			256
+
 #define SNDRV_MINOR_DEVICES		32
 #define SNDRV_MINOR_CARD(minor)		((minor) >> 5)
 #define SNDRV_MINOR_DEVICE(minor)	((minor) & 0x001f)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 51d58cc..40c5a6f 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -25,6 +25,7 @@
 
 #include <sound/asound.h>
 #include <sound/memalloc.h>
+#include <sound/minors.h>
 #include <linux/poll.h>
 #include <linux/mm.h>
 #include <linux/bitops.h>
@@ -84,7 +85,11 @@
  *
  */
 
-#define SNDRV_PCM_DEVICES		8
+#if defined(CONFIG_SND_DYNAMIC_MINORS)
+#define SNDRV_PCM_DEVICES	(SNDRV_OS_MINORS-2)
+#else
+#define SNDRV_PCM_DEVICES	8
+#endif
 
 #define SNDRV_PCM_IOCTL1_FALSE		((void *)0)
 #define SNDRV_PCM_IOCTL1_TRUE		((void *)1)
@@ -416,7 +421,7 @@
 struct snd_pcm {
 	struct snd_card *card;
 	struct list_head list;
-	unsigned int device;	/* device number */
+	int device; /* device number */
 	unsigned int info_flags;
 	unsigned short dev_class;
 	unsigned short dev_subclass;
@@ -969,10 +974,30 @@
 int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
 int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
 
-#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_buffer_p->private_data)
-#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size)
-#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs)
-struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset);
+/*
+ * SG-buffer handling
+ */
+#define snd_pcm_substream_sgbuf(substream) \
+	((substream)->runtime->dma_buffer_p->private_data)
+
+static inline dma_addr_t
+snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
+	return snd_sgbuf_get_addr(sg, ofs);
+}
+
+static inline void *
+snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
+	return snd_sgbuf_get_ptr(sg, ofs);
+}
+
+struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
+				    unsigned long offset);
+unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
+					  unsigned int ofs, unsigned int size);
 
 /* handle mmap counter - PCM mmap callback should handle this counter properly */
 static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
@@ -1010,4 +1035,6 @@
 					 (IEC958_AES1_CON_PCM_CODER<<8)|\
 					 (IEC958_AES3_CON_FS_48000<<24))
 
+#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
+
 #endif /* __SOUND_PCM_H */
diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h
new file mode 100644
index 0000000..2fd3d25
--- /dev/null
+++ b/include/sound/pxa2xx-lib.h
@@ -0,0 +1,45 @@
+#ifndef PXA2XX_LIB_H
+#define PXA2XX_LIB_H
+
+#include <linux/platform_device.h>
+#include <sound/ac97_codec.h>
+
+/* PCM */
+
+struct pxa2xx_pcm_dma_params {
+	char *name;			/* stream identifier */
+	u32 dcmd;			/* DMA descriptor dcmd field */
+	volatile u32 *drcmr;		/* the DMA request channel to use */
+	u32 dev_addr;			/* device physical address for DMA */
+};
+
+extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params);
+extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream);
+extern int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
+extern snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream);
+extern int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream);
+extern void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id);
+extern int __pxa2xx_pcm_open(struct snd_pcm_substream *substream);
+extern int __pxa2xx_pcm_close(struct snd_pcm_substream *substream);
+extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
+	struct vm_area_struct *vma);
+extern int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream);
+extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm);
+
+/* AC97 */
+
+extern unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
+extern void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val);
+
+extern bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97);
+extern bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97);
+extern void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97);
+
+extern int pxa2xx_ac97_hw_suspend(void);
+extern int pxa2xx_ac97_hw_resume(void);
+
+extern int pxa2xx_ac97_hw_probe(struct platform_device *dev);
+extern void pxa2xx_ac97_hw_remove(struct platform_device *dev);
+
+#endif
diff --git a/include/sound/sb.h b/include/sound/sb.h
index d0c9ed3..85f93c5 100644
--- a/include/sound/sb.h
+++ b/include/sound/sb.h
@@ -240,11 +240,15 @@
 #define SB_DT019X_CAP_MAIN	0x07
 
 #define SB_ALS4000_MONO_IO_CTRL	0x4b
+#define SB_ALS4000_OUT_MIXER_CTRL_2	0x4c
 #define SB_ALS4000_MIC_IN_GAIN	0x4d
+#define SB_ALS4000_ANALOG_REFRNC_VOLT_CTRL 0x4e
 #define SB_ALS4000_FMDAC	0x4f
 #define SB_ALS4000_3D_SND_FX	0x50
 #define SB_ALS4000_3D_TIME_DELAY	0x51
 #define SB_ALS4000_3D_AUTO_MUTE	0x52
+#define SB_ALS4000_ANALOG_BLOCK_CTRL 0x53
+#define SB_ALS4000_3D_DELAYLINE_PATTERN 0x54
 #define SB_ALS4000_QSOUND	0xdb
 
 /* IRQ setting bitmap */
@@ -257,6 +261,7 @@
 #define SB_IRQTYPE_8BIT		0x01
 #define SB_IRQTYPE_16BIT	0x02
 #define SB_IRQTYPE_MPUIN	0x04
+#define ALS4K_IRQTYPE_CR1E_DMA	0x20
 
 /* DMA setting bitmap */
 #define SB_DMASETUP_DMA0	0x01
diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h
index 9688d4b..fa149ca 100644
--- a/include/sound/snd_wavefront.h
+++ b/include/sound/snd_wavefront.h
@@ -1,7 +1,6 @@
 #ifndef __SOUND_SND_WAVEFRONT_H__
 #define __SOUND_SND_WAVEFRONT_H__
 
-#include "cs4231.h"
 #include "mpu401.h"
 #include "hwdep.h"
 #include "rawmidi.h"
diff --git a/include/sound/soc-of-simple.h b/include/sound/soc-of-simple.h
new file mode 100644
index 0000000..a064e19
--- /dev/null
+++ b/include/sound/soc-of-simple.h
@@ -0,0 +1,25 @@
+/*
+ * OF helpers for ALSA SoC
+ *
+ * Copyright (C) 2008, Secret Lab Technologies Ltd.
+ */
+
+#ifndef _INCLUDE_SOC_OF_H_
+#define _INCLUDE_SOC_OF_H_
+
+#if defined(CONFIG_SND_SOC_OF_SIMPLE) || defined(CONFIG_SND_SOC_OF_SIMPLE_MODULE)
+
+#include <linux/of.h>
+#include <sound/soc.h>
+
+int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
+			      void *codec_data, struct snd_soc_dai *dai,
+			      struct device_node *node);
+
+int of_snd_soc_register_platform(struct snd_soc_platform *platform,
+				 struct device_node *node,
+				 struct snd_soc_dai *cpu_dai);
+
+#endif
+
+#endif /* _INCLUDE_SOC_OF_H_ */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 1890d87..a1e0357 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -26,10 +26,12 @@
 /*
  * Convenience kcontrol builders
  */
-#define SOC_SINGLE_VALUE(reg, shift, max, invert) ((reg) | ((shift) << 8) |\
-	((shift) << 12) | ((max) << 16) | ((invert) << 24))
-#define SOC_SINGLE_VALUE_EXT(reg, max, invert) ((reg) | ((max) << 16) |\
-	((invert) << 31))
+#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
+	((unsigned long)&(struct soc_mixer_control) \
+	{.reg = xreg, .shift = xshift, .max = xmax, .invert = xinvert})
+#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
+	((unsigned long)&(struct soc_mixer_control) \
+	{.reg = xreg, .max = xmax, .invert = xinvert})
 #define SOC_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
@@ -43,64 +45,68 @@
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
 	.put = snd_soc_put_volsw, \
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
-#define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
+#define SOC_DOUBLE(xname, xreg, shift_left, shift_right, xmax, xinvert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
 	.put = snd_soc_put_volsw, \
-	.private_value = (reg) | ((shift_left) << 8) | \
-		((shift_right) << 12) | ((max) << 16) | ((invert) << 24) }
-#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, max, invert) \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .shift = shift_left, .rshift = shift_right, \
+		 .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 	.info = snd_soc_info_volsw_2r, \
 	.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
-	.private_value = (reg_left) | ((shift) << 8)  | \
-		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
-#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+		.max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert, tlv_array) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
 		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
 	.tlv.p = (tlv_array), \
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
 	.put = snd_soc_put_volsw, \
-	.private_value = (reg) | ((shift_left) << 8) | \
-		((shift_right) << 12) | ((max) << 16) | ((invert) << 24) }
-#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .shift = shift_left, .rshift = shift_right,\
+		 .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
 		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
 	.tlv.p = (tlv_array), \
 	.info = snd_soc_info_volsw_2r, \
 	.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
-	.private_value = (reg_left) | ((shift) << 8)  | \
-		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
-#define SOC_DOUBLE_S8_TLV(xname, reg, min, max, tlv_array) \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+		.max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
 {	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
 		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
 	.tlv.p  = (tlv_array), \
 	.info   = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
 	.put    = snd_soc_put_volsw_s8, \
-	.private_value = (reg) | (((signed char)max) << 16) | \
-			 (((signed char)min) << 24) }
-#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .min = xmin, .max = xmax} }
+#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
 {	.reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
-	.mask = xmask, .texts = xtexts }
-#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
-	SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
-#define SOC_ENUM_SINGLE_EXT(xmask, xtexts) \
-{	.mask = xmask, .texts = xtexts }
+	.max = xmax, .texts = xtexts }
+#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
+	SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
+#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \
+{	.max = xmax, .texts = xtexts }
 #define SOC_ENUM(xname, xenum) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
 	.info = snd_soc_info_enum_double, \
 	.get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
 	.private_value = (unsigned long)&xenum }
-#define SOC_SINGLE_EXT(xname, xreg, xshift, xmask, xinvert,\
+#define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
 	 xhandler_get, xhandler_put) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
 	.get = xhandler_get, .put = xhandler_put, \
-	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) }
-#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmask, xinvert,\
+	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
 	 xhandler_get, xhandler_put, tlv_array) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
@@ -108,7 +114,7 @@
 	.tlv.p = (tlv_array), \
 	.info = snd_soc_info_volsw, \
 	.get = xhandler_get, .put = xhandler_put, \
-	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) }
+	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
 #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_bool_ext, \
@@ -410,6 +416,8 @@
 	void *control_data; /* codec control (i2c/3wire) data */
 	unsigned int (*read)(struct snd_soc_codec *, unsigned int);
 	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+	int (*display_register)(struct snd_soc_codec *, char *,
+				size_t, unsigned int);
 	hw_write_t hw_write;
 	hw_read_t hw_read;
 	void *reg_cache;
@@ -516,13 +524,19 @@
 	struct snd_soc_device *socdev;
 };
 
+/* mixer control */
+struct soc_mixer_control {
+	int min, max;
+	unsigned int reg, rreg, shift, rshift, invert;
+};
+
 /* enumerated kcontrol */
 struct soc_enum {
 	unsigned short reg;
 	unsigned short reg2;
 	unsigned char shift_l;
 	unsigned char shift_r;
-	unsigned int mask;
+	unsigned int max;
 	const char **texts;
 	void *dapm;
 };
diff --git a/include/sound/version.h b/include/sound/version.h
index 6b78aff..4aafeda 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
 /* include/version.h */
-#define CONFIG_SND_VERSION "1.0.17"
+#define CONFIG_SND_VERSION "1.0.18rc3"
 #define CONFIG_SND_DATE ""
diff --git a/include/sound/vx_core.h b/include/sound/vx_core.h
index 4830651..5456343 100644
--- a/include/sound/vx_core.h
+++ b/include/sound/vx_core.h
@@ -235,37 +235,31 @@
  */
 static inline int vx_test_and_ack(struct vx_core *chip)
 {
-	snd_assert(chip->ops->test_and_ack, return -ENXIO);
 	return chip->ops->test_and_ack(chip);
 }
 
 static inline void vx_validate_irq(struct vx_core *chip, int enable)
 {
-	snd_assert(chip->ops->validate_irq, return);
 	chip->ops->validate_irq(chip, enable);
 }
 
 static inline unsigned char snd_vx_inb(struct vx_core *chip, int reg)
 {
-	snd_assert(chip->ops->in8, return 0);
 	return chip->ops->in8(chip, reg);
 }
 
 static inline unsigned int snd_vx_inl(struct vx_core *chip, int reg)
 {
-	snd_assert(chip->ops->in32, return 0);
 	return chip->ops->in32(chip, reg);
 }
 
 static inline void snd_vx_outb(struct vx_core *chip, int reg, unsigned char val)
 {
-	snd_assert(chip->ops->out8, return);
 	chip->ops->out8(chip, reg, val);
 }
 
 static inline void snd_vx_outl(struct vx_core *chip, int reg, unsigned int val)
 {
-	snd_assert(chip->ops->out32, return);
 	chip->ops->out32(chip, reg, val);
 }
 
@@ -276,7 +270,6 @@
 
 static inline void vx_reset_dsp(struct vx_core *chip)
 {
-	snd_assert(chip->ops->reset_dsp, return);
 	chip->ops->reset_dsp(chip);
 }
 
@@ -304,14 +297,12 @@
 static inline void vx_pseudo_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,
 				       struct vx_pipe *pipe, int count)
 {
-	snd_assert(chip->ops->dma_write, return);
 	chip->ops->dma_write(chip, runtime, pipe, count);
 }
 
 static inline void vx_pseudo_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,
 				      struct vx_pipe *pipe, int count)
 {
-	snd_assert(chip->ops->dma_read, return);
 	chip->ops->dma_read(chip, runtime, pipe, count);
 }
 
diff --git a/include/sound/wss.h b/include/sound/wss.h
new file mode 100644
index 0000000..fd01f22
--- /dev/null
+++ b/include/sound/wss.h
@@ -0,0 +1,235 @@
+#ifndef __SOUND_WSS_H
+#define __SOUND_WSS_H
+
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *  Definitions for CS4231 & InterWave chips & compatible chips
+ *
+ *
+ *   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 "control.h"
+#include "pcm.h"
+#include "timer.h"
+
+#include "cs4231-regs.h"
+
+/* defines for codec.mode */
+
+#define WSS_MODE_NONE	0x0000
+#define WSS_MODE_PLAY	0x0001
+#define WSS_MODE_RECORD	0x0002
+#define WSS_MODE_TIMER	0x0004
+#define WSS_MODE_OPEN	(WSS_MODE_PLAY|WSS_MODE_RECORD|WSS_MODE_TIMER)
+
+/* defines for codec.hardware */
+
+#define WSS_HW_DETECT        0x0000	/* let CS4231 driver detect chip */
+#define WSS_HW_DETECT3	0x0001	/* allow mode 3 */
+#define WSS_HW_TYPE_MASK	0xff00	/* type mask */
+#define WSS_HW_CS4231_MASK   0x0100	/* CS4231 serie */
+#define WSS_HW_CS4231        0x0100	/* CS4231 chip */
+#define WSS_HW_CS4231A       0x0101	/* CS4231A chip */
+#define WSS_HW_AD1845	0x0102	/* AD1845 chip */
+#define WSS_HW_CS4232_MASK   0x0200	/* CS4232 serie (has control ports) */
+#define WSS_HW_CS4232        0x0200	/* CS4232 */
+#define WSS_HW_CS4232A       0x0201	/* CS4232A */
+#define WSS_HW_CS4236	0x0202	/* CS4236 */
+#define WSS_HW_CS4236B_MASK	0x0400	/* CS4236B serie (has extended control regs) */
+#define WSS_HW_CS4235	0x0400	/* CS4235 - Crystal Clear (tm) stereo enhancement */
+#define WSS_HW_CS4236B       0x0401	/* CS4236B */
+#define WSS_HW_CS4237B       0x0402	/* CS4237B - SRS 3D */
+#define WSS_HW_CS4238B	0x0403	/* CS4238B - QSOUND 3D */
+#define WSS_HW_CS4239	0x0404	/* CS4239 - Crystal Clear (tm) stereo enhancement */
+#define WSS_HW_AD1848_MASK	0x0800	/* AD1848 serie (half duplex) */
+#define WSS_HW_AD1847		0x0801	/* AD1847 chip */
+#define WSS_HW_AD1848		0x0802	/* AD1848 chip */
+#define WSS_HW_CS4248		0x0803	/* CS4248 chip */
+#define WSS_HW_CMI8330		0x0804	/* CMI8330 chip */
+#define WSS_HW_THINKPAD		0x0805	/* Thinkpad 360/750/755 */
+/* compatible, but clones */
+#define WSS_HW_INTERWAVE     0x1000	/* InterWave chip */
+#define WSS_HW_OPL3SA2       0x1101	/* OPL3-SA2 chip, similar to cs4231 */
+#define WSS_HW_OPTI93X 	0x1102	/* Opti 930/931/933 */
+
+/* defines for codec.hwshare */
+#define WSS_HWSHARE_IRQ	(1<<0)
+#define WSS_HWSHARE_DMA1	(1<<1)
+#define WSS_HWSHARE_DMA2	(1<<2)
+
+/* IBM Thinkpad specific stuff */
+#define AD1848_THINKPAD_CTL_PORT1		0x15e8
+#define AD1848_THINKPAD_CTL_PORT2		0x15e9
+#define AD1848_THINKPAD_CS4248_ENABLE_BIT	0x02
+
+struct snd_wss {
+	unsigned long port;		/* base i/o port */
+	struct resource *res_port;
+	unsigned long cport;		/* control base i/o port (CS4236) */
+	struct resource *res_cport;
+	int irq;			/* IRQ line */
+	int dma1;			/* playback DMA */
+	int dma2;			/* record DMA */
+	unsigned short version;		/* version of CODEC chip */
+	unsigned short mode;		/* see to WSS_MODE_XXXX */
+	unsigned short hardware;	/* see to WSS_HW_XXXX */
+	unsigned short hwshare;		/* shared resources */
+	unsigned short single_dma:1,	/* forced single DMA mode (GUS 16-bit */
+					/* daughter board) or dma1 == dma2 */
+		       ebus_flag:1,	/* SPARC: EBUS present */
+		       thinkpad_flag:1;	/* Thinkpad CS4248 needs extra help */
+
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *playback_substream;
+	struct snd_pcm_substream *capture_substream;
+	struct snd_timer *timer;
+
+	unsigned char image[32];	/* registers image */
+	unsigned char eimage[32];	/* extended registers image */
+	unsigned char cimage[16];	/* control registers image */
+	int mce_bit;
+	int calibrate_mute;
+	int sw_3d_bit;
+	unsigned int p_dma_size;
+	unsigned int c_dma_size;
+
+	spinlock_t reg_lock;
+	struct mutex mce_mutex;
+	struct mutex open_mutex;
+
+	int (*rate_constraint) (struct snd_pcm_runtime *runtime);
+	void (*set_playback_format) (struct snd_wss *chip,
+				     struct snd_pcm_hw_params *hw_params,
+				     unsigned char pdfr);
+	void (*set_capture_format) (struct snd_wss *chip,
+				    struct snd_pcm_hw_params *hw_params,
+				    unsigned char cdfr);
+	void (*trigger) (struct snd_wss *chip, unsigned int what, int start);
+#ifdef CONFIG_PM
+	void (*suspend) (struct snd_wss *chip);
+	void (*resume) (struct snd_wss *chip);
+#endif
+	void *dma_private_data;
+	int (*claim_dma) (struct snd_wss *chip,
+			  void *dma_private_data, int dma);
+	int (*release_dma) (struct snd_wss *chip,
+			    void *dma_private_data, int dma);
+};
+
+/* exported functions */
+
+void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char val);
+unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg);
+void snd_cs4236_ext_out(struct snd_wss *chip,
+			unsigned char reg, unsigned char val);
+unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg);
+void snd_wss_mce_up(struct snd_wss *chip);
+void snd_wss_mce_down(struct snd_wss *chip);
+
+void snd_wss_overrange(struct snd_wss *chip);
+
+irqreturn_t snd_wss_interrupt(int irq, void *dev_id);
+
+const char *snd_wss_chip_id(struct snd_wss *chip);
+
+int snd_wss_create(struct snd_card *card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      struct snd_wss **rchip);
+int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm);
+int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer);
+int snd_wss_mixer(struct snd_wss *chip);
+
+const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction);
+
+int snd_cs4236_create(struct snd_card *card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      struct snd_wss **rchip);
+int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm);
+int snd_cs4236_mixer(struct snd_wss *chip);
+
+/*
+ *  mixer library
+ */
+
+#define WSS_SINGLE(xname, xindex, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_wss_info_single, \
+  .get = snd_wss_get_single, \
+  .put = snd_wss_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+
+int snd_wss_info_single(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo);
+int snd_wss_get_single(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol);
+int snd_wss_put_single(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol);
+
+#define WSS_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_wss_info_double, \
+  .get = snd_wss_get_double, \
+  .put = snd_wss_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+		   (shift_right << 19) | (mask << 24) | (invert << 22) }
+
+#define WSS_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_wss_info_single, \
+  .get = snd_wss_get_single, \
+  .put = snd_wss_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+  .tlv = { .p = (xtlv) } }
+
+#define WSS_DOUBLE_TLV(xname, xindex, left_reg, right_reg, \
+			shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_wss_info_double, \
+  .get = snd_wss_get_double, \
+  .put = snd_wss_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | \
+		   (shift_right << 19) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = (xtlv) } }
+
+
+int snd_wss_info_double(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo);
+int snd_wss_get_double(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol);
+int snd_wss_put_double(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol);
+
+#endif /* __SOUND_WSS_H */
diff --git a/sound/Kconfig b/sound/Kconfig
index 8ebf512..200aca1 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -28,6 +28,10 @@
 
 if SOUND
 
+config SOUND_OSS_CORE
+	bool
+	default n
+
 source "sound/oss/dmasound/Kconfig"
 
 if !M68K
@@ -80,6 +84,7 @@
 
 menuconfig SOUND_PRIME
 	tristate "Open Sound System (DEPRECATED)"
+	select SOUND_OSS_CORE
 	help
 	  Say 'Y' or 'M' to enable Open Sound System drivers.
 
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
index 7a16a33..6c515b2 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
+++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
@@ -654,15 +654,13 @@
 static struct transfer_info tas_transfers[] = {
 	{
 		/* input */
-		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
-			   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE,
+		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
 		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
 		.transfer_in = 1,
 	},
 	{
 		/* output */
-		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE |
-			   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE,
+		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
 		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
 		.transfer_in = 0,
 	},
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 351e19e..f8e6de4 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -32,11 +32,20 @@
 	tristate
 	select SND_PCM
 
+config SND_PXA2XX_LIB
+	tristate
+	select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
+
+config SND_PXA2XX_LIB_AC97
+	bool
+
 config SND_PXA2XX_AC97
 	tristate "AC97 driver for the Intel PXA2xx chip"
 	depends on ARCH_PXA
 	select SND_PXA2XX_PCM
 	select SND_AC97_CODEC
+	select SND_PXA2XX_LIB
+	select SND_PXA2XX_LIB_AC97
 	help
 	  Say Y or M if you want to support any AC97 codec attached to
 	  the PXA2xx AC97 interface.
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 4ef6dd0..2054de1 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -11,5 +11,9 @@
 obj-$(CONFIG_SND_PXA2XX_PCM)	+= snd-pxa2xx-pcm.o
 snd-pxa2xx-pcm-objs		:= pxa2xx-pcm.o
 
+obj-$(CONFIG_SND_PXA2XX_LIB)	+= snd-pxa2xx-lib.o
+snd-pxa2xx-lib-y		:= pxa2xx-pcm-lib.o
+snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97)	+= pxa2xx-ac97-lib.o
+
 obj-$(CONFIG_SND_PXA2XX_AC97)	+= snd-pxa2xx-ac97.o
 snd-pxa2xx-ac97-objs		:= pxa2xx-ac97.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index b0a4744..89096e8 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -999,7 +999,7 @@
 	card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 			    THIS_MODULE, sizeof(struct aaci));
 	if (card == NULL)
-		return ERR_PTR(-ENOMEM);
+		return NULL;
 
 	card->private_free = aaci_free_card;
 
@@ -1083,8 +1083,8 @@
 		return ret;
 
 	aaci = aaci_init_card(dev);
-	if (IS_ERR(aaci)) {
-		ret = PTR_ERR(aaci);
+	if (!aaci) {
+		ret = -ENOMEM;
 		goto out;
 	}
 
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
new file mode 100644
index 0000000..99026df
--- /dev/null
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -0,0 +1,384 @@
+/*
+ * Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c
+ * which contain:
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Dec 02, 2004
+ * Copyright:	MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <sound/ac97_codec.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-gpio.h>
+#include <mach/audio.h>
+
+static DEFINE_MUTEX(car_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
+static volatile long gsr_bits;
+static struct clk *ac97_clk;
+static struct clk *ac97conf_clk;
+
+/*
+ * Beware PXA27x bugs:
+ *
+ *   o Slot 12 read from modem space will hang controller.
+ *   o CDONE, SDONE interrupt fails after any slot 12 IO.
+ *
+ * We therefore have an hybrid approach for waiting on SDONE (interrupt or
+ * 1 jiffy timeout if interrupt never comes).
+ */
+
+unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	unsigned short val = -1;
+	volatile u32 *reg_addr;
+
+	mutex_lock(&car_mutex);
+
+	/* set up primary or secondary codec space */
+	if ((cpu_is_pxa21x() || cpu_is_pxa25x()) && reg == AC97_GPIO_STATUS)
+		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+	else
+		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+	reg_addr += (reg >> 1);
+
+	/* start read access across the ac97 link */
+	GSR = GSR_CDONE | GSR_SDONE;
+	gsr_bits = 0;
+	val = *reg_addr;
+	if (reg == AC97_GPIO_STATUS)
+		goto out;
+	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
+	    !((GSR | gsr_bits) & GSR_SDONE)) {
+		printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
+				__func__, reg, GSR | gsr_bits);
+		val = -1;
+		goto out;
+	}
+
+	/* valid data now */
+	GSR = GSR_CDONE | GSR_SDONE;
+	gsr_bits = 0;
+	val = *reg_addr;
+	/* but we've just started another cycle... */
+	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+
+out:	mutex_unlock(&car_mutex);
+	return val;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
+
+void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+			unsigned short val)
+{
+	volatile u32 *reg_addr;
+
+	mutex_lock(&car_mutex);
+
+	/* set up primary or secondary codec space */
+	if ((cpu_is_pxa21x() || cpu_is_pxa25x()) && reg == AC97_GPIO_STATUS)
+		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+	else
+		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+	reg_addr += (reg >> 1);
+
+	GSR = GSR_CDONE | GSR_SDONE;
+	gsr_bits = 0;
+	*reg_addr = val;
+	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
+	    !((GSR | gsr_bits) & GSR_CDONE))
+		printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
+				__func__, reg, GSR | gsr_bits);
+
+	mutex_unlock(&car_mutex);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
+
+#ifdef CONFIG_PXA25x
+static inline void pxa_ac97_warm_pxa25x(void)
+{
+	gsr_bits = 0;
+
+	GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
+	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+}
+
+static inline void pxa_ac97_cold_pxa25x(void)
+{
+	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
+	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
+
+	gsr_bits = 0;
+
+	GCR = GCR_COLD_RST;
+	GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
+	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+}
+#endif
+
+#ifdef CONFIG_PXA27x
+static inline void pxa_ac97_warm_pxa27x(void)
+{
+	gsr_bits = 0;
+
+	/* warm reset broken on Bulverde,
+	   so manually keep AC97 reset high */
+	pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
+	udelay(10);
+	GCR |= GCR_WARM_RST;
+	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+	udelay(500);
+}
+
+static inline void pxa_ac97_cold_pxa27x(void)
+{
+	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
+	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
+
+	gsr_bits = 0;
+
+	/* PXA27x Developers Manual section 13.5.2.2.1 */
+	clk_enable(ac97conf_clk);
+	udelay(5);
+	clk_disable(ac97conf_clk);
+	GCR = GCR_COLD_RST;
+	udelay(50);
+}
+#endif
+
+#ifdef CONFIG_PXA3xx
+static inline void pxa_ac97_warm_pxa3xx(void)
+{
+	int timeout = 100;
+
+	gsr_bits = 0;
+
+	/* Can't use interrupts */
+	GCR |= GCR_WARM_RST;
+	while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+		mdelay(1);
+}
+
+static inline void pxa_ac97_cold_pxa3xx(void)
+{
+	int timeout = 1000;
+
+	/* Hold CLKBPB for 100us */
+	GCR = 0;
+	GCR = GCR_CLKBPB;
+	udelay(100);
+	GCR = 0;
+
+	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
+	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
+
+	gsr_bits = 0;
+
+	/* Can't use interrupts on PXA3xx */
+	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+
+	GCR = GCR_WARM_RST | GCR_COLD_RST;
+	while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
+		mdelay(10);
+}
+#endif
+
+bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
+{
+#ifdef CONFIG_PXA25x
+	if (cpu_is_pxa21x() || cpu_is_pxa25x())
+		pxa_ac97_warm_pxa25x();
+	else
+#endif
+#ifdef CONFIG_PXA27x
+	if (cpu_is_pxa27x())
+		pxa_ac97_warm_pxa27x();
+	else
+#endif
+#ifdef CONFIG_PXA3xx
+	if (cpu_is_pxa3xx())
+		pxa_ac97_warm_pxa3xx();
+	else
+#endif
+		BUG();
+
+	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
+		printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
+				 __func__, gsr_bits);
+
+		return false;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
+
+bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
+{
+#ifdef CONFIG_PXA25x
+	if (cpu_is_pxa21x() || cpu_is_pxa25x())
+		pxa_ac97_cold_pxa25x();
+	else
+#endif
+#ifdef CONFIG_PXA27x
+	if (cpu_is_pxa27x())
+		pxa_ac97_cold_pxa27x();
+	else
+#endif
+#ifdef CONFIG_PXA3xx
+	if (cpu_is_pxa3xx())
+		pxa_ac97_cold_pxa3xx();
+	else
+#endif
+		BUG();
+
+	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
+		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
+				 __func__, gsr_bits);
+
+		return false;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
+
+
+void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97)
+{
+	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+	GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset);
+
+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
+{
+	long status;
+
+	status = GSR;
+	if (status) {
+		GSR = status;
+		gsr_bits |= status;
+		wake_up(&gsr_wq);
+
+		/* Although we don't use those we still need to clear them
+		   since they tend to spuriously trigger when MMC is used
+		   (hardware bug? go figure)... */
+		if (cpu_is_pxa27x()) {
+			MISR = MISR_EOC;
+			PISR = PISR_EOC;
+			MCSR = MCSR_EOC;
+		}
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+#ifdef CONFIG_PM
+int pxa2xx_ac97_hw_suspend(void)
+{
+	GCR |= GCR_ACLINK_OFF;
+	clk_disable(ac97_clk);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend);
+
+int pxa2xx_ac97_hw_resume(void)
+{
+	if (cpu_is_pxa21x() || cpu_is_pxa25x() || cpu_is_pxa27x()) {
+		pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+		pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+		pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+		pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+	}
+	if (cpu_is_pxa27x()) {
+		/* Use GPIO 113 as AC97 Reset on Bulverde */
+		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+	}
+	clk_enable(ac97_clk);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
+#endif
+
+int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
+{
+	int ret;
+
+	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
+	if (ret < 0)
+		goto err;
+
+	if (cpu_is_pxa21x() || cpu_is_pxa25x() || cpu_is_pxa27x()) {
+		pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+		pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+		pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+		pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+	}
+
+	if (cpu_is_pxa27x()) {
+		/* Use GPIO 113 as AC97 Reset on Bulverde */
+		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+		ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
+		if (IS_ERR(ac97conf_clk)) {
+			ret = PTR_ERR(ac97conf_clk);
+			ac97conf_clk = NULL;
+			goto err_irq;
+		}
+	}
+
+	ac97_clk = clk_get(&dev->dev, "AC97CLK");
+	if (IS_ERR(ac97_clk)) {
+		ret = PTR_ERR(ac97_clk);
+		ac97_clk = NULL;
+		goto err_irq;
+	}
+
+	return clk_enable(ac97_clk);
+
+err_irq:
+	GCR |= GCR_ACLINK_OFF;
+	if (ac97conf_clk) {
+		clk_put(ac97conf_clk);
+		ac97conf_clk = NULL;
+	}
+	free_irq(IRQ_AC97, NULL);
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
+
+void pxa2xx_ac97_hw_remove(struct platform_device *dev)
+{
+	GCR |= GCR_ACLINK_OFF;
+	free_irq(IRQ_AC97, NULL);
+	if (ac97conf_clk) {
+		clk_put(ac97conf_clk);
+		ac97conf_clk = NULL;
+	}
+	clk_disable(ac97_clk);
+	clk_put(ac97_clk);
+	ac97_clk = NULL;
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel/Marvell PXA sound library");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 199cca3..cba71d8 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -12,198 +12,27 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
+#include <sound/pxa2xx-lib.h>
 
-#include <asm/irq.h>
-#include <linux/mutex.h>
 #include <mach/hardware.h>
 #include <mach/pxa-regs.h>
-#include <mach/pxa2xx-gpio.h>
 #include <mach/audio.h>
 
 #include "pxa2xx-pcm.h"
 
-
-static DEFINE_MUTEX(car_mutex);
-static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
-static volatile long gsr_bits;
-static struct clk *ac97_clk;
-#ifdef CONFIG_PXA27x
-static struct clk *ac97conf_clk;
-#endif
-
-/*
- * Beware PXA27x bugs:
- *
- *   o Slot 12 read from modem space will hang controller.
- *   o CDONE, SDONE interrupt fails after any slot 12 IO.
- *
- * We therefore have an hybrid approach for waiting on SDONE (interrupt or
- * 1 jiffy timeout if interrupt never comes).
- */ 
-
-static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
-	unsigned short val = -1;
-	volatile u32 *reg_addr;
-
-	mutex_lock(&car_mutex);
-
-	/* set up primary or secondary codec space */
-	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
-	reg_addr += (reg >> 1);
-
-	/* start read access across the ac97 link */
-	GSR = GSR_CDONE | GSR_SDONE;
-	gsr_bits = 0;
-	val = *reg_addr;
-	if (reg == AC97_GPIO_STATUS)
-		goto out;
-	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
-	    !((GSR | gsr_bits) & GSR_SDONE)) {
-		printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
-				__func__, reg, GSR | gsr_bits);
-		val = -1;
-		goto out;
-	}
-
-	/* valid data now */
-	GSR = GSR_CDONE | GSR_SDONE;
-	gsr_bits = 0;
-	val = *reg_addr;			
-	/* but we've just started another cycle... */
-	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out:	mutex_unlock(&car_mutex);
-	return val;
-}
-
-static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
-	volatile u32 *reg_addr;
-
-	mutex_lock(&car_mutex);
-
-	/* set up primary or secondary codec space */
-	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
-	reg_addr += (reg >> 1);
-
-	GSR = GSR_CDONE | GSR_SDONE;
-	gsr_bits = 0;
-	*reg_addr = val;
-	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
-	    !((GSR | gsr_bits) & GSR_CDONE))
-		printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
-				__func__, reg, GSR | gsr_bits);
-
-	mutex_unlock(&car_mutex);
-}
-
 static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
 {
-	/* First, try cold reset */
-#ifdef CONFIG_PXA3xx
-	int timeout;
-
-	/* Hold CLKBPB for 100us */
-	GCR = 0;
-	GCR = GCR_CLKBPB;
-	udelay(100);
-	GCR = 0;
-#endif
-
-	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
-	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
-
-	gsr_bits = 0;
-#ifdef CONFIG_PXA27x
-	/* PXA27x Developers Manual section 13.5.2.2.1 */
-	clk_enable(ac97conf_clk);
-	udelay(5);
-	clk_disable(ac97conf_clk);
-	GCR = GCR_COLD_RST;
-	udelay(50);
-#elif defined(CONFIG_PXA3xx)
-	timeout = 1000;
-	/* Can't use interrupts on PXA3xx */
-	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-
-	GCR = GCR_WARM_RST | GCR_COLD_RST;
-	while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
-		mdelay(10);
-#else
-	GCR = GCR_COLD_RST;
-	GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
-	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
-	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
-		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
-				 __func__, gsr_bits);
-
-		/* let's try warm reset */
-		gsr_bits = 0;
-#ifdef CONFIG_PXA27x
-		/* warm reset broken on Bulverde,
-		   so manually keep AC97 reset high */
-		pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); 
-		udelay(10);
-		GCR |= GCR_WARM_RST;
-		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-		udelay(500);
-#elif defined(CONFIG_PXA3xx)
-		timeout = 100;
-		/* Can't use interrupts */
-		GCR |= GCR_WARM_RST;
-		while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
-			mdelay(1);
-#else
-		GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;
-		wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif			
-
-		if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
-			printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
-					 __func__, gsr_bits);
+	if (!pxa2xx_ac97_try_cold_reset(ac97)) {
+		pxa2xx_ac97_try_warm_reset(ac97);
 	}
 
-	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-	GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
-}
-
-static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
-{
-	long status;
-
-	status = GSR;
-	if (status) {
-		GSR = status;
-		gsr_bits |= status;
-		wake_up(&gsr_wq);
-
-#ifdef CONFIG_PXA27x
-		/* Although we don't use those we still need to clear them
-		   since they tend to spuriously trigger when MMC is used
-		   (hardware bug? go figure)... */
-		MISR = MISR_EOC;
-		PISR = PISR_EOC;
-		MCSR = MCSR_EOC;
-#endif
-
-		return IRQ_HANDLED;
-	}
-
-	return IRQ_NONE;
+	pxa2xx_ac97_finish_reset(ac97);
 }
 
 static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
@@ -288,17 +117,19 @@
 	snd_ac97_suspend(pxa2xx_ac97_ac97);
 	if (platform_ops && platform_ops->suspend)
 		platform_ops->suspend(platform_ops->priv);
-	GCR |= GCR_ACLINK_OFF;
-	clk_disable(ac97_clk);
 
-	return 0;
+	return pxa2xx_ac97_hw_suspend();
 }
 
 static int pxa2xx_ac97_do_resume(struct snd_card *card)
 {
 	pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+	int rc;
 
-	clk_enable(ac97_clk);
+	rc = pxa2xx_ac97_hw_resume();
+	if (rc)
+		return rc;
+
 	if (platform_ops && platform_ops->resume)
 		platform_ops->resume(platform_ops->priv);
 	snd_ac97_resume(pxa2xx_ac97_ac97);
@@ -354,40 +185,17 @@
 	if (ret)
 		goto err;
 
-	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
-	if (ret < 0)
+	ret = pxa2xx_ac97_hw_probe(dev);
+	if (ret)
 		goto err;
 
-	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
-	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
-	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
-	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
-#ifdef CONFIG_PXA27x
-	/* Use GPIO 113 as AC97 Reset on Bulverde */
-	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-	ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
-	if (IS_ERR(ac97conf_clk)) {
-		ret = PTR_ERR(ac97conf_clk);
-		ac97conf_clk = NULL;
-		goto err;
-	}
-#endif
-
-	ac97_clk = clk_get(&dev->dev, "AC97CLK");
-	if (IS_ERR(ac97_clk)) {
-		ret = PTR_ERR(ac97_clk);
-		ac97_clk = NULL;
-		goto err;
-	}
-	clk_enable(ac97_clk);
-
 	ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
 	if (ret)
-		goto err;
+		goto err_remove;
 	memset(&ac97_template, 0, sizeof(ac97_template));
 	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
 	if (ret)
-		goto err;
+		goto err_remove;
 
 	snprintf(card->shortname, sizeof(card->shortname),
 		 "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
@@ -401,22 +209,11 @@
 		return 0;
 	}
 
- err:
+err_remove:
+	pxa2xx_ac97_hw_remove(dev);
+err:
 	if (card)
 		snd_card_free(card);
-	if (ac97_clk) {
-		GCR |= GCR_ACLINK_OFF;
-		free_irq(IRQ_AC97, NULL);
-		clk_disable(ac97_clk);
-		clk_put(ac97_clk);
-		ac97_clk = NULL;
-	}
-#ifdef CONFIG_PXA27x
-	if (ac97conf_clk) {
-		clk_put(ac97conf_clk);
-		ac97conf_clk = NULL;
-	}
-#endif
 	return ret;
 }
 
@@ -427,15 +224,7 @@
 	if (card) {
 		snd_card_free(card);
 		platform_set_drvdata(dev, NULL);
-		GCR |= GCR_ACLINK_OFF;
-		free_irq(IRQ_AC97, NULL);
-		clk_disable(ac97_clk);
-		clk_put(ac97_clk);
-		ac97_clk = NULL;
-#ifdef CONFIG_PXA27x
-		clk_put(ac97conf_clk);
-		ac97conf_clk = NULL;
-#endif
+		pxa2xx_ac97_hw_remove(dev);
 	}
 
 	return 0;
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
new file mode 100644
index 0000000..1c93eb7
--- /dev/null
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -0,0 +1,278 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <asm/dma.h>
+#include <mach/pxa-regs.h>
+
+#include "pxa2xx-pcm.h"
+
+static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE |
+				  SNDRV_PCM_INFO_RESUME,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
+					SNDRV_PCM_FMTBIT_S24_LE |
+					SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 8192 - 32,
+	.periods_min		= 1,
+	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc),
+	.buffer_bytes_max	= 128 * 1024,
+	.fifo_size		= 32,
+};
+
+int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+	size_t totsize = params_buffer_bytes(params);
+	size_t period = params_period_bytes(params);
+	pxa_dma_desc *dma_desc;
+	dma_addr_t dma_buff_phys, next_desc_phys;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = totsize;
+
+	dma_desc = rtd->dma_desc_array;
+	next_desc_phys = rtd->dma_desc_array_phys;
+	dma_buff_phys = runtime->dma_addr;
+	do {
+		next_desc_phys += sizeof(pxa_dma_desc);
+		dma_desc->ddadr = next_desc_phys;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			dma_desc->dsadr = dma_buff_phys;
+			dma_desc->dtadr = rtd->params->dev_addr;
+		} else {
+			dma_desc->dsadr = rtd->params->dev_addr;
+			dma_desc->dtadr = dma_buff_phys;
+		}
+		if (period > totsize)
+			period = totsize;
+		dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
+		dma_desc++;
+		dma_buff_phys += period;
+	} while (totsize -= period);
+	dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+
+	return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
+
+int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+
+	if (rtd && rtd->params)
+		*rtd->params->drcmr = 0;
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
+
+int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+		DCSR(prtd->dma_ch) = DCSR_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+		DCSR(prtd->dma_ch) |= DCSR_RUN;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
+		DCSR(prtd->dma_ch) |= DCSR_RUN;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_trigger);
+
+snd_pcm_uframes_t
+pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *prtd = runtime->private_data;
+
+	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+			 DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
+	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+
+	if (x == runtime->buffer_size)
+		x = 0;
+	return x;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_pointer);
+
+int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+
+	DCSR(prtd->dma_ch) &= ~DCSR_RUN;
+	DCSR(prtd->dma_ch) = 0;
+	DCMD(prtd->dma_ch) = 0;
+	*prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
+
+	return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
+
+void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
+{
+	struct snd_pcm_substream *substream = dev_id;
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+	int dcsr;
+
+	dcsr = DCSR(dma_ch);
+	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
+
+	if (dcsr & DCSR_ENDINTR) {
+		snd_pcm_period_elapsed(substream);
+	} else {
+		printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+			rtd->params->name, dma_ch, dcsr);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+	}
+}
+EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
+
+int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd;
+	int ret;
+
+	runtime->hw = pxa2xx_pcm_hardware;
+
+	/*
+	 * For mysterious reasons (and despite what the manual says)
+	 * playback samples are lost if the DMA count is not a multiple
+	 * of the DMA burst size.  Let's add a rule to enforce that.
+	 */
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (ret)
+		goto out;
+
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (ret)
+		goto out;
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	ret = -ENOMEM;
+	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+	if (!rtd)
+		goto out;
+	rtd->dma_desc_array =
+		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+				       &rtd->dma_desc_array_phys, GFP_KERNEL);
+	if (!rtd->dma_desc_array)
+		goto err1;
+
+	runtime->private_data = rtd;
+	return 0;
+
+ err1:
+	kfree(rtd);
+ out:
+	return ret;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_open);
+
+int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+
+	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+			      rtd->dma_desc_array, rtd->dma_desc_array_phys);
+	kfree(rtd);
+	return 0;
+}
+EXPORT_SYMBOL(__pxa2xx_pcm_close);
+
+int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
+	struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+}
+EXPORT_SYMBOL(pxa2xx_pcm_mmap);
+
+int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+					   &buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->bytes = size;
+	return 0;
+}
+EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
+
+void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_writecombine(pcm->card->dev, buf->bytes,
+				      buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx sound library");
+MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
index 381094a..535704f 100644
--- a/sound/arm/pxa2xx-pcm.c
+++ b/sound/arm/pxa2xx-pcm.c
@@ -10,183 +10,20 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-
 #include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include <asm/dma.h>
-#include <mach/hardware.h>
-#include <mach/pxa-regs.h>
+#include <sound/pxa2xx-lib.h>
 
 #include "pxa2xx-pcm.h"
 
-
-static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
-	.info			= SNDRV_PCM_INFO_MMAP |
-				  SNDRV_PCM_INFO_MMAP_VALID |
-				  SNDRV_PCM_INFO_INTERLEAVED |
-				  SNDRV_PCM_INFO_PAUSE,
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
-	.period_bytes_min	= 32,
-	.period_bytes_max	= 8192 - 32,
-	.periods_min		= 1,
-	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc),
-	.buffer_bytes_max	= 128 * 1024,
-	.fifo_size		= 32,
-};
-
-struct pxa2xx_runtime_data {
-	int dma_ch;
-	struct pxa2xx_pcm_dma_params *params;
-	pxa_dma_desc *dma_desc_array;
-	dma_addr_t dma_desc_array_phys;
-};
-
-static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct pxa2xx_runtime_data *rtd = runtime->private_data;
-	size_t totsize = params_buffer_bytes(params);
-	size_t period = params_period_bytes(params);
-	pxa_dma_desc *dma_desc;
-	dma_addr_t dma_buff_phys, next_desc_phys;
-
-	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = totsize;
-
-	dma_desc = rtd->dma_desc_array;
-	next_desc_phys = rtd->dma_desc_array_phys;
-	dma_buff_phys = runtime->dma_addr;
-	do {
-		next_desc_phys += sizeof(pxa_dma_desc);
-		dma_desc->ddadr = next_desc_phys;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			dma_desc->dsadr = dma_buff_phys;
-			dma_desc->dtadr = rtd->params->dev_addr;
-		} else {
-			dma_desc->dsadr = rtd->params->dev_addr;
-			dma_desc->dtadr = dma_buff_phys;
-		}
-		if (period > totsize)
-			period = totsize;
-		dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
-		dma_desc++;
-		dma_buff_phys += period;
-	} while (totsize -= period);
-	dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
-
-	return 0;
-}
-
-static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
-	*rtd->params->drcmr = 0;
-	snd_pcm_set_runtime_buffer(substream, NULL);
-	return 0;
-}
-
 static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
 {
 	struct pxa2xx_pcm_client *client = substream->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct pxa2xx_runtime_data *rtd = runtime->private_data;
 
-	DCSR(rtd->dma_ch) &= ~DCSR_RUN;
-	DCSR(rtd->dma_ch) = 0;
-	DCMD(rtd->dma_ch) = 0;
-	*rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD;
+	__pxa2xx_pcm_prepare(substream);
 
 	return client->prepare(substream);
 }
 
-static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-	int ret = 0;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys;
-		DCSR(rtd->dma_ch) = DCSR_RUN;
-		break;
-
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		DCSR(rtd->dma_ch) &= ~DCSR_RUN;
-		break;
-
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		DCSR(rtd->dma_ch) |= DCSR_RUN;
-		break;
-
-	default:
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
-	struct snd_pcm_substream *substream = dev_id;
-	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-	int dcsr;
-
-	dcsr = DCSR(dma_ch);
-	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
-	if (dcsr & DCSR_ENDINTR) {
-		snd_pcm_period_elapsed(substream);
-	} else {
-		printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
-			rtd->params->name, dma_ch, dcsr );
-		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-	}
-}
-
-static snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct pxa2xx_runtime_data *rtd = runtime->private_data;
-	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-			 DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch);
-	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-	if (x == runtime->buffer_size)
-		x = 0;
-	return x;
-}
-
-static int
-pxa2xx_pcm_hw_rule_mult32(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
-{
-	struct snd_interval *i = hw_param_interval(params, rule->var);
-	int changed = 0;
-
-	if (i->min & 31) {
-		i->min = (i->min & ~31) + 32;
-		i->openmin = 0;
-		changed = 1;
-	}
-
-	if (i->max & 31) {
-		i->max &= ~31;
-		i->openmax = 0;
-		changed = 1;
-	}
-
-	return changed;
-}
-
 static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
 {
 	struct pxa2xx_pcm_client *client = substream->private_data;
@@ -194,33 +31,11 @@
 	struct pxa2xx_runtime_data *rtd;
 	int ret;
 
-	runtime->hw = pxa2xx_pcm_hardware;
-
-	/*
-	 * For mysterious reasons (and despite what the manual says)
-	 * playback samples are lost if the DMA count is not a multiple
-	 * of the DMA burst size.  Let's add a rule to enforce that.
-	 */
-	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-				  pxa2xx_pcm_hw_rule_mult32, NULL,
-				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
-	if (ret)
-		goto out;
-	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-				  pxa2xx_pcm_hw_rule_mult32, NULL,
-				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+	ret = __pxa2xx_pcm_open(substream);
 	if (ret)
 		goto out;
 
-	ret = -ENOMEM;
-	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
-	if (!rtd)
-		goto out;
-	rtd->dma_desc_array =
-		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-				       &rtd->dma_desc_array_phys, GFP_KERNEL);
-	if (!rtd->dma_desc_array)
-		goto err1;
+	rtd = runtime->private_data;
 
 	rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
 		      client->playback_params : client->capture_params;
@@ -230,17 +45,13 @@
 		goto err2;
 	rtd->dma_ch = ret;
 
-	runtime->private_data = rtd;
 	ret = client->startup(substream);
 	if (!ret)
 		goto out;
 
 	pxa_free_dma(rtd->dma_ch);
  err2:
-	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-			      rtd->dma_desc_array, rtd->dma_desc_array_phys);
- err1:
-	kfree(rtd);
+	__pxa2xx_pcm_close(substream);
  out:
 	return ret;
 }
@@ -252,69 +63,22 @@
 
 	pxa_free_dma(rtd->dma_ch);
 	client->shutdown(substream);
-	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-			      rtd->dma_desc_array, rtd->dma_desc_array_phys);
-	kfree(rtd);
-	return 0;
-}
 
-static int
-pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-				     runtime->dma_area,
-				     runtime->dma_addr,
-				     runtime->dma_bytes);
+	return __pxa2xx_pcm_close(substream);
 }
 
 static struct snd_pcm_ops pxa2xx_pcm_ops = {
 	.open		= pxa2xx_pcm_open,
 	.close		= pxa2xx_pcm_close,
 	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= pxa2xx_pcm_hw_params,
-	.hw_free	= pxa2xx_pcm_hw_free,
+	.hw_params	= __pxa2xx_pcm_hw_params,
+	.hw_free	= __pxa2xx_pcm_hw_free,
 	.prepare	= pxa2xx_pcm_prepare,
 	.trigger	= pxa2xx_pcm_trigger,
 	.pointer	= pxa2xx_pcm_pointer,
 	.mmap		= pxa2xx_pcm_mmap,
 };
 
-static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
-	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-	struct snd_dma_buffer *buf = &substream->dma_buffer;
-	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
-	buf->dev.type = SNDRV_DMA_TYPE_DEV;
-	buf->dev.dev = pcm->card->dev;
-	buf->private_data = NULL;
-	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-					   &buf->addr, GFP_KERNEL);
-	if (!buf->area)
-		return -ENOMEM;
-	buf->bytes = size;
-	return 0;
-}
-
-static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
-	struct snd_pcm_substream *substream;
-	struct snd_dma_buffer *buf;
-	int stream;
-
-	for (stream = 0; stream < 2; stream++) {
-		substream = pcm->streams[stream].substream;
-		if (!substream)
-			continue;
-		buf = &substream->dma_buffer;
-		if (!buf->area)
-			continue;
-		dma_free_writecombine(pcm->card->dev, buf->bytes,
-				      buf->area, buf->addr);
-		buf->area = NULL;
-	}
-}
-
 static u64 pxa2xx_pcm_dmamask = 0xffffffff;
 
 int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client,
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
index b79f1e8..5c4a4d3 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -9,14 +9,15 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <asm/dma.h>
 
-struct pxa2xx_pcm_dma_params {
-	char *name;			/* stream identifier */
-	u32 dcmd;			/* DMA descriptor dcmd field */
-	volatile u32 *drcmr;		/* the DMA request channel to use */
-	u32 dev_addr;			/* device physical address for DMA */
+struct pxa2xx_runtime_data {
+	int dma_ch;
+	struct pxa2xx_pcm_dma_params *params;
+	pxa_dma_desc *dma_desc_array;
+	dma_addr_t dma_desc_array_phys;
 };
-	
+
 struct pxa2xx_pcm_client {
 	struct pxa2xx_pcm_dma_params *playback_params;
 	struct pxa2xx_pcm_dma_params *capture_params;
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
index b9c51bf..1dcd51d 100644
--- a/sound/arm/sa11xx-uda1341.c
+++ b/sound/arm/sa11xx-uda1341.c
@@ -442,7 +442,8 @@
                 
 	/* we are requested to process synchronization DMA transfer */
 	if (s->tx_spin) {
-		snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return);
+		if (snd_BUG_ON(s->stream_id != SNDRV_PCM_STREAM_PLAYBACK))
+			return;
 		/* fill the xmit dma buffers and return */
 #ifdef HH_VERSION
 		sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
@@ -472,7 +473,7 @@
 				continue;		/* special case */
 		} else {
 			offset = dma_size * s->period;
-			snd_assert(dma_size <= DMA_BUF_SIZE, );
+			snd_BUG_ON(dma_size > DMA_BUF_SIZE);
 		}
 #ifdef HH_VERSION
 		ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
@@ -879,7 +880,7 @@
 	audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
 }
 
-static int __init sa11xx_uda1341_probe(struct platform_device *devptr)
+static int __devinit sa11xx_uda1341_probe(struct platform_device *devptr)
 {
 	int err;
 	struct snd_card *card;
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 335d45e..66348c9 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -12,6 +12,12 @@
 config SND_RAWMIDI
 	tristate
 
+# To be effective this also requires INPUT - users should say:
+#    select SND_JACK if INPUT=y || INPUT=SND
+# to avoid having to force INPUT on.
+config SND_JACK
+	bool
+
 config SND_SEQUENCER
 	tristate "Sequencer support"
 	select SND_TIMER
@@ -38,6 +44,7 @@
 	  will be called snd-seq-dummy.
 
 config SND_OSSEMUL
+	select SOUND_OSS_CORE
 	bool
 
 config SND_MIXER_OSS
@@ -101,6 +108,9 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-rtctimer.
 
+	  Note that this option is exclusive with the new RTC drivers
+	  (CONFIG_RTC_CLASS) since this requires the old API.
+
 config SND_SEQ_RTCTIMER_DEFAULT
 	bool "Use RTC as default sequencer timer"
 	depends on SND_RTCTIMER && SND_SEQUENCER
diff --git a/sound/core/Makefile b/sound/core/Makefile
index da8e685..d57125a 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -7,6 +7,7 @@
 snd-$(CONFIG_ISA_DMA_API) += isadma.o
 snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
 snd-$(CONFIG_SND_VMASTER) += vmaster.o
+snd-$(CONFIG_SND_JACK)	  += jack.o
 
 snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
 		pcm_memory.o
diff --git a/sound/core/control.c b/sound/core/control.c
index 281b2e2..6d71f9a 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -139,7 +139,8 @@
 	struct snd_ctl_file *ctl;
 	struct snd_kctl_event *ev;
 	
-	snd_assert(card != NULL && id != NULL, return);
+	if (snd_BUG_ON(!card || !id))
+		return;
 	read_lock(&card->ctl_files_rwlock);
 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
 	card->mixer_oss_change_count++;
@@ -188,8 +189,8 @@
 	struct snd_kcontrol *kctl;
 	unsigned int idx;
 	
-	snd_assert(control != NULL, return NULL);
-	snd_assert(control->count > 0, return NULL);
+	if (snd_BUG_ON(!control || !control->count))
+		return NULL;
 	kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
 	if (kctl == NULL) {
 		snd_printk(KERN_ERR "Cannot allocate control instance\n");
@@ -218,8 +219,8 @@
 	struct snd_kcontrol kctl;
 	unsigned int access;
 	
-	snd_assert(ncontrol != NULL, return NULL);
-	snd_assert(ncontrol->info != NULL, return NULL);
+	if (snd_BUG_ON(!ncontrol || !ncontrol->info))
+		return NULL;
 	memset(&kctl, 0, sizeof(kctl));
 	kctl.id.iface = ncontrol->iface;
 	kctl.id.device = ncontrol->device;
@@ -315,8 +316,8 @@
 
 	if (! kcontrol)
 		return err;
-	snd_assert(card != NULL, goto error);
-	snd_assert(kcontrol->info != NULL, goto error);
+	if (snd_BUG_ON(!card || !kcontrol->info))
+		goto error;
 	id = kcontrol->id;
 	down_write(&card->controls_rwsem);
 	if (snd_ctl_find_id(card, &id)) {
@@ -367,7 +368,8 @@
 	struct snd_ctl_elem_id id;
 	unsigned int idx;
 
-	snd_assert(card != NULL && kcontrol != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card || !kcontrol))
+		return -EINVAL;
 	list_del(&kcontrol->list);
 	card->controls_count -= kcontrol->count;
 	id = kcontrol->id;
@@ -487,7 +489,8 @@
 {
 	struct snd_kcontrol *kctl;
 
-	snd_assert(card != NULL && numid != 0, return NULL);
+	if (snd_BUG_ON(!card || !numid))
+		return NULL;
 	list_for_each_entry(kctl, &card->controls, list) {
 		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
 			return kctl;
@@ -514,7 +517,8 @@
 {
 	struct snd_kcontrol *kctl;
 
-	snd_assert(card != NULL && id != NULL, return NULL);
+	if (snd_BUG_ON(!card || !id))
+		return NULL;
 	if (id->numid != 0)
 		return snd_ctl_find_numid(card, id->numid);
 	list_for_each_entry(kctl, &card->controls, list) {
@@ -647,7 +651,7 @@
 #endif
 	result = kctl->info(kctl, info);
 	if (result >= 0) {
-		snd_assert(info->access == 0, );
+		snd_BUG_ON(info->access);
 		index_offset = snd_ctl_get_ioff(kctl, &info->id);
 		vd = &kctl->vd[index_offset];
 		snd_ctl_build_ioff(&info->id, kctl, index_offset);
@@ -1160,7 +1164,8 @@
 
 	ctl = file->private_data;
 	card = ctl->card;
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	switch (cmd) {
 	case SNDRV_CTL_IOCTL_PVERSION:
 		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
@@ -1222,7 +1227,8 @@
 	ssize_t result = 0;
 
 	ctl = file->private_data;
-	snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!ctl || !ctl->card))
+		return -ENXIO;
 	if (!ctl->subscribed)
 		return -EBADFD;
 	if (count < sizeof(struct snd_ctl_event))
@@ -1328,7 +1334,8 @@
 {
 	struct snd_kctl_ioctl *p;
 
-	snd_assert(fcn != NULL, return -EINVAL);
+	if (snd_BUG_ON(!fcn))
+		return -EINVAL;
 	down_write(&snd_ioctl_rwsem);
 	list_for_each_entry(p, lists, list) {
 		if (p->fioctl == fcn) {
@@ -1404,9 +1411,11 @@
 	int err, cardnum;
 	char name[16];
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	cardnum = card->number;
-	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
+		return -ENXIO;
 	sprintf(name, "controlC%i", cardnum);
 	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
 				       &snd_ctl_f_ops, card, name)) < 0)
@@ -1423,16 +1432,18 @@
 	struct snd_ctl_file *ctl;
 	int err, cardnum;
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	cardnum = card->number;
-	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
+	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
+		return -ENXIO;
 
-	down_read(&card->controls_rwsem);
+	read_lock(&card->ctl_files_rwlock);
 	list_for_each_entry(ctl, &card->ctl_files, list) {
 		wake_up(&ctl->change_sleep);
 		kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
 	}
-	up_read(&card->controls_rwsem);
+	read_unlock(&card->ctl_files_rwlock);
 
 	if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
 					 card, -1)) < 0)
@@ -1469,7 +1480,8 @@
 		.dev_disconnect = snd_ctl_dev_disconnect,
 	};
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
 }
 
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 6101259..368dc9c 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -398,7 +398,8 @@
 	int err;
 
 	ctl = file->private_data;
-	snd_assert(ctl && ctl->card, return -ENXIO);
+	if (snd_BUG_ON(!ctl || !ctl->card))
+		return -ENXIO;
 
 	switch (cmd) {
 	case SNDRV_CTL_IOCTL_PVERSION:
diff --git a/sound/core/device.c b/sound/core/device.c
index 202dac0..c58d822 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -45,9 +45,8 @@
 {
 	struct snd_device *dev;
 
-	snd_assert(card != NULL, return -ENXIO);
-	snd_assert(device_data != NULL, return -ENXIO);
-	snd_assert(ops != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card || !device_data || !ops))
+		return -ENXIO;
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (dev == NULL) {
 		snd_printk(KERN_ERR "Cannot allocate device\n");
@@ -80,8 +79,8 @@
 {
 	struct snd_device *dev;
 	
-	snd_assert(card != NULL, return -ENXIO);
-	snd_assert(device_data != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card || !device_data))
+		return -ENXIO;
 	list_for_each_entry(dev, &card->devices, list) {
 		if (dev->device_data != device_data)
 			continue;
@@ -123,8 +122,8 @@
 {
 	struct snd_device *dev;
 
-	snd_assert(card != NULL, return -ENXIO);
-	snd_assert(device_data != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card || !device_data))
+		return -ENXIO;
 	list_for_each_entry(dev, &card->devices, list) {
 		if (dev->device_data != device_data)
 			continue;
@@ -159,8 +158,8 @@
 	struct snd_device *dev;
 	int err;
 
-	snd_assert(card != NULL, return -ENXIO);
-	snd_assert(device_data != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card || !device_data))
+		return -ENXIO;
 	list_for_each_entry(dev, &card->devices, list) {
 		if (dev->device_data != device_data)
 			continue;
@@ -188,7 +187,8 @@
 	struct snd_device *dev;
 	int err;
 	
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	list_for_each_entry(dev, &card->devices, list) {
 		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
 			if ((err = dev->ops->dev_register(dev)) < 0)
@@ -208,7 +208,8 @@
 	struct snd_device *dev;
 	int err = 0;
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	list_for_each_entry(dev, &card->devices, list) {
 		if (snd_device_disconnect(card, dev->device_data) < 0)
 			err = -ENXIO;
@@ -226,7 +227,8 @@
 	int err;
 	unsigned int range_low, range_high;
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
 	range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
       __again:
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 6d6589f..195cafc 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -353,9 +353,10 @@
 		.dev_disconnect = snd_hwdep_dev_disconnect,
 	};
 
-	snd_assert(rhwdep != NULL, return -EINVAL);
-	*rhwdep = NULL;
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
+	if (rhwdep)
+		*rhwdep = NULL;
 	hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
 	if (hwdep == NULL) {
 		snd_printk(KERN_ERR "hwdep: cannot allocate\n");
@@ -374,13 +375,15 @@
 	}
 	init_waitqueue_head(&hwdep->open_wait);
 	mutex_init(&hwdep->open_mutex);
-	*rhwdep = hwdep;
+	if (rhwdep)
+		*rhwdep = hwdep;
 	return 0;
 }
 
 static int snd_hwdep_free(struct snd_hwdep *hwdep)
 {
-	snd_assert(hwdep != NULL, return -ENXIO);
+	if (!hwdep)
+		return 0;
 	if (hwdep->private_free)
 		hwdep->private_free(hwdep);
 	kfree(hwdep);
@@ -440,7 +443,8 @@
 {
 	struct snd_hwdep *hwdep = device->device_data;
 
-	snd_assert(hwdep != NULL, return -ENXIO);
+	if (snd_BUG_ON(!hwdep))
+		return -ENXIO;
 	mutex_lock(&register_mutex);
 	if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
 		mutex_unlock(&register_mutex);
diff --git a/sound/core/info.c b/sound/core/info.c
index c67773a..527b207 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -217,7 +217,8 @@
 	loff_t pos;
 
 	data = file->private_data;
-	snd_assert(data != NULL, return -ENXIO);
+	if (snd_BUG_ON(!data))
+		return -ENXIO;
 	pos = *offset;
 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
 		return -EIO;
@@ -258,7 +259,8 @@
 	loff_t pos;
 
 	data = file->private_data;
-	snd_assert(data != NULL, return -ENXIO);
+	if (snd_BUG_ON(!data))
+		return -ENXIO;
 	entry = data->entry;
 	pos = *offset;
 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
@@ -614,7 +616,8 @@
 	char str[8];
 	struct snd_info_entry *entry;
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 
 	sprintf(str, "card%i", card->number);
 	if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
@@ -636,7 +639,8 @@
 {
 	struct proc_dir_entry *p;
 
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 
 	if (!strcmp(card->id, card->proc_root->name))
 		return 0;
@@ -654,7 +658,8 @@
  */
 void snd_info_card_disconnect(struct snd_card *card)
 {
-	snd_assert(card != NULL, return);
+	if (!card)
+		return;
 	mutex_lock(&info_mutex);
 	if (card->proc_root_link) {
 		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
@@ -671,7 +676,8 @@
  */
 int snd_info_card_free(struct snd_card *card)
 {
-	snd_assert(card != NULL, return -ENXIO);
+	if (!card)
+		return 0;
 	snd_info_free_entry(card->proc_root);
 	card->proc_root = NULL;
 	return 0;
@@ -849,7 +855,7 @@
 		return;
 	list_del_init(&entry->list);
 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
-	snd_assert(root, return);
+	snd_BUG_ON(!root);
 	snd_remove_proc_entry(root, entry->p);
 	entry->p = NULL;
 }
@@ -947,7 +953,8 @@
 {
 	struct proc_dir_entry *root, *p = NULL;
 
-	snd_assert(entry != NULL, return -ENXIO);
+	if (snd_BUG_ON(!entry))
+		return -ENXIO;
 	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
 	mutex_lock(&info_mutex);
 	p = snd_create_proc_entry(entry->name, entry->mode, root);
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index e35789a..e4af138 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -43,8 +43,10 @@
 {
 	char *x;
 
-	snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO);
-	snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO);
+	if (snd_BUG_ON(dev < 0 || dev >= SNDRV_OSS_INFO_DEV_COUNT))
+		return -ENXIO;
+	if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS))
+		return -ENXIO;
 	mutex_lock(&strings);
 	if (string == NULL) {
 		if ((x = snd_sndstat_strings[num][dev]) != NULL) {
diff --git a/sound/core/init.c b/sound/core/init.c
index df46bbc..8af467d 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -545,7 +545,8 @@
 {
 	int err;
 
-	snd_assert(card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card))
+		return -EINVAL;
 #ifndef CONFIG_SYSFS_DEPRECATED
 	if (!card->card_dev) {
 		card->card_dev = device_create_drvdata(sound_class, card->dev,
diff --git a/sound/core/jack.c b/sound/core/jack.c
new file mode 100644
index 0000000..8133a2b
--- /dev/null
+++ b/sound/core/jack.c
@@ -0,0 +1,163 @@
+/*
+ *  Jack abstraction layer
+ *
+ *  Copyright 2008 Wolfson Microelectronics
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/input.h>
+#include <sound/jack.h>
+#include <sound/core.h>
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+	struct snd_jack *jack = device->device_data;
+
+	/* If the input device is registered with the input subsystem
+	 * then we need to use a different deallocator. */
+	if (jack->registered)
+		input_unregister_device(jack->input_dev);
+	else
+		input_free_device(jack->input_dev);
+
+	kfree(jack);
+
+	return 0;
+}
+
+static int snd_jack_dev_register(struct snd_device *device)
+{
+	struct snd_jack *jack = device->device_data;
+	struct snd_card *card = device->card;
+	int err;
+
+	snprintf(jack->name, sizeof(jack->name), "%s %s",
+		 card->longname, jack->id);
+	jack->input_dev->name = jack->name;
+
+	/* Default to the sound card device. */
+	if (!jack->input_dev->dev.parent)
+		jack->input_dev->dev.parent = card->dev;
+
+	err = input_register_device(jack->input_dev);
+	if (err == 0)
+		jack->registered = 1;
+
+	return err;
+}
+
+/**
+ * snd_jack_new - Create a new jack
+ * @card:  the card instance
+ * @id:    an identifying string for this jack
+ * @type:  a bitmask of enum snd_jack_type values that can be detected by
+ *         this jack
+ * @jjack: Used to provide the allocated jack object to the caller.
+ *
+ * Creates a new jack object.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jjack will be initialised.
+ */
+int snd_jack_new(struct snd_card *card, const char *id, int type,
+		 struct snd_jack **jjack)
+{
+	struct snd_jack *jack;
+	int err;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_jack_dev_free,
+		.dev_register = snd_jack_dev_register,
+	};
+
+	jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
+	if (jack == NULL)
+		return -ENOMEM;
+
+	jack->id = id;
+
+	jack->input_dev = input_allocate_device();
+	if (jack->input_dev == NULL) {
+		err = -ENOMEM;
+		goto fail_input;
+	}
+
+	jack->input_dev->phys = "ALSA";
+
+	jack->type = type;
+
+	if (type & SND_JACK_HEADPHONE)
+		input_set_capability(jack->input_dev, EV_SW,
+				     SW_HEADPHONE_INSERT);
+	if (type & SND_JACK_MICROPHONE)
+		input_set_capability(jack->input_dev, EV_SW,
+				     SW_MICROPHONE_INSERT);
+
+	err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
+	if (err < 0)
+		goto fail_input;
+
+	*jjack = jack;
+
+	return 0;
+
+fail_input:
+	input_free_device(jack->input_dev);
+	kfree(jack);
+	return err;
+}
+EXPORT_SYMBOL(snd_jack_new);
+
+/**
+ * snd_jack_set_parent - Set the parent device for a jack
+ *
+ * @jack:   The jack to configure
+ * @parent: The device to set as parent for the jack.
+ *
+ * Set the parent for the jack input device in the device tree.  This
+ * function is only valid prior to registration of the jack.  If no
+ * parent is configured then the parent device will be the sound card.
+ */
+void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
+{
+	WARN_ON(jack->registered);
+
+	jack->input_dev->dev.parent = parent;
+}
+EXPORT_SYMBOL(snd_jack_set_parent);
+
+/**
+ * snd_jack_report - Report the current status of a jack
+ *
+ * @jack:   The jack to report status for
+ * @status: The current status of the jack
+ */
+void snd_jack_report(struct snd_jack *jack, int status)
+{
+	if (jack->type & SND_JACK_HEADPHONE)
+		input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
+				    status & SND_JACK_HEADPHONE);
+	if (jack->type & SND_JACK_MICROPHONE)
+		input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
+				    status & SND_JACK_MICROPHONE);
+
+	input_sync(jack->input_dev);
+}
+EXPORT_SYMBOL(snd_jack_report);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("Jack detection support for ALSA");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index f5d6d8d..a7b46ec 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -46,14 +46,6 @@
 /*
  */
 
-void *snd_malloc_sgbuf_pages(struct device *device,
-                             size_t size, struct snd_dma_buffer *dmab,
-			     size_t *res_size);
-int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
-
-/*
- */
-
 static DEFINE_MUTEX(list_mutex);
 static LIST_HEAD(mem_list_head);
 
@@ -67,18 +59,6 @@
 /* id for pre-allocated buffers */
 #define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
 
-#ifdef CONFIG_SND_DEBUG
-#define __ASTRING__(x) #x
-#define snd_assert(expr, args...) do {\
-	if (!(expr)) {\
-		printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
-		args;\
-	}\
-} while (0)
-#else
-#define snd_assert(expr, args...) /**/
-#endif
-
 /*
  *
  *  Generic memory allocators
@@ -111,8 +91,10 @@
 	int pg;
 	void *res;
 
-	snd_assert(size > 0, return NULL);
-	snd_assert(gfp_flags != 0, return NULL);
+	if (WARN_ON(!size))
+		return NULL;
+	if (WARN_ON(!gfp_flags))
+		return NULL;
 	gfp_flags |= __GFP_COMP;	/* compound page lets parts be mapped */
 	pg = get_order(size);
 	if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL)
@@ -152,8 +134,8 @@
 	void *res;
 	gfp_t gfp_flags;
 
-	snd_assert(size > 0, return NULL);
-	snd_assert(dma != NULL, return NULL);
+	if (WARN_ON(!dma))
+		return NULL;
 	pg = get_order(size);
 	gfp_flags = GFP_KERNEL
 		| __GFP_COMP	/* compound page lets parts be mapped */
@@ -189,8 +171,8 @@
 	int pg;
 	void *res;
 
-	snd_assert(size > 0, return NULL);
-	snd_assert(dma_addr != NULL, return NULL);
+	if (WARN_ON(!dma_addr))
+		return NULL;
 	pg = get_order(size);
 	res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
 	if (res != NULL)
@@ -236,8 +218,10 @@
 int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 			struct snd_dma_buffer *dmab)
 {
-	snd_assert(size > 0, return -ENXIO);
-	snd_assert(dmab != NULL, return -ENXIO);
+	if (WARN_ON(!size))
+		return -ENXIO;
+	if (WARN_ON(!dmab))
+		return -ENXIO;
 
 	dmab->dev.type = type;
 	dmab->dev.dev = device;
@@ -292,15 +276,17 @@
 {
 	int err;
 
-	snd_assert(size > 0, return -ENXIO);
-	snd_assert(dmab != NULL, return -ENXIO);
-
 	while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
+		size_t aligned_size;
 		if (err != -ENOMEM)
 			return err;
-		size >>= 1;
 		if (size <= PAGE_SIZE)
 			return -ENOMEM;
+		aligned_size = PAGE_SIZE << get_order(size);
+		if (size != aligned_size)
+			size = aligned_size;
+		else
+			size >>= 1;
 	}
 	if (! dmab->area)
 		return -ENOMEM;
@@ -353,7 +339,8 @@
 {
 	struct snd_mem_list *mem;
 
-	snd_assert(dmab, return 0);
+	if (WARN_ON(!dmab))
+		return 0;
 
 	mutex_lock(&list_mutex);
 	list_for_each_entry(mem, &mem_list_head, list) {
@@ -387,7 +374,8 @@
 {
 	struct snd_mem_list *mem;
 
-	snd_assert(dmab, return -EINVAL);
+	if (WARN_ON(!dmab))
+		return -EINVAL;
 	mem = kmalloc(sizeof(*mem), GFP_KERNEL);
 	if (! mem)
 		return -ENOMEM;
diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c
index 9ded30d..05b58d4 100644
--- a/sound/core/oss/copy.c
+++ b/sound/core/oss/copy.c
@@ -32,17 +32,18 @@
 	unsigned int channel;
 	unsigned int nchannels;
 
-	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+		return -ENXIO;
 	if (frames == 0)
 		return 0;
 	nchannels = plugin->src_format.channels;
 	for (channel = 0; channel < nchannels; channel++) {
-		snd_assert(src_channels->area.first % 8 == 0 &&
-			   src_channels->area.step % 8 == 0,
-			   return -ENXIO);
-		snd_assert(dst_channels->area.first % 8 == 0 &&
-			   dst_channels->area.step % 8 == 0,
-			   return -ENXIO);
+		if (snd_BUG_ON(src_channels->area.first % 8 ||
+			       src_channels->area.step % 8))
+			return -ENXIO;
+		if (snd_BUG_ON(dst_channels->area.first % 8 ||
+			       dst_channels->area.step % 8))
+			return -ENXIO;
 		if (!src_channels->enabled) {
 			if (dst_channels->wanted)
 				snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
@@ -66,15 +67,20 @@
 	struct snd_pcm_plugin *plugin;
 	int width;
 
-	snd_assert(r_plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!r_plugin))
+		return -ENXIO;
 	*r_plugin = NULL;
 
-	snd_assert(src_format->format == dst_format->format, return -ENXIO);
-	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
-	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+	if (snd_BUG_ON(src_format->format != dst_format->format))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->rate != dst_format->rate))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->channels != dst_format->channels))
+		return -ENXIO;
 
 	width = snd_pcm_format_physical_width(src_format->format);
-	snd_assert(width > 0, return -ENXIO);
+	if (snd_BUG_ON(width <= 0))
+		return -ENXIO;
 
 	err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
 				   0, &plugin);
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
index f874f6c..6faa1d7 100644
--- a/sound/core/oss/io.c
+++ b/sound/core/oss/io.c
@@ -39,14 +39,17 @@
 				    struct snd_pcm_plugin_channel *dst_channels,
 				    snd_pcm_uframes_t frames)
 {
-	snd_assert(plugin != NULL, return -ENXIO);
-	snd_assert(src_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin))
+		return -ENXIO;
+	if (snd_BUG_ON(!src_channels))
+		return -ENXIO;
 	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
 		return pcm_write(plugin->plug, src_channels->area.addr, frames);
 	} else {
 		int channel, channels = plugin->dst_format.channels;
 		void **bufs = (void**)plugin->extra_data;
-		snd_assert(bufs != NULL, return -ENXIO);
+		if (snd_BUG_ON(!bufs))
+			return -ENXIO;
 		for (channel = 0; channel < channels; channel++) {
 			if (src_channels[channel].enabled)
 				bufs[channel] = src_channels[channel].area.addr;
@@ -62,14 +65,17 @@
 				   struct snd_pcm_plugin_channel *dst_channels,
 				   snd_pcm_uframes_t frames)
 {
-	snd_assert(plugin != NULL, return -ENXIO);
-	snd_assert(dst_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin))
+		return -ENXIO;
+	if (snd_BUG_ON(!dst_channels))
+		return -ENXIO;
 	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
 		return pcm_read(plugin->plug, dst_channels->area.addr, frames);
 	} else {
 		int channel, channels = plugin->dst_format.channels;
 		void **bufs = (void**)plugin->extra_data;
-		snd_assert(bufs != NULL, return -ENXIO);
+		if (snd_BUG_ON(!bufs))
+			return -ENXIO;
 		for (channel = 0; channel < channels; channel++) {
 			if (dst_channels[channel].enabled)
 				bufs[channel] = dst_channels[channel].area.addr;
@@ -107,9 +113,11 @@
 	struct snd_pcm_plugin_format format;
 	struct snd_pcm_plugin *plugin;
 
-	snd_assert(r_plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!r_plugin))
+		return -ENXIO;
 	*r_plugin = NULL;
-	snd_assert(plug != NULL && params != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plug || !params))
+		return -ENXIO;
 	format.format = params_format(params);
 	format.rate = params_rate(params);
 	format.channels = params_channels(params);
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index da3dbd4..4c1d168 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -92,7 +92,8 @@
 {
 	struct linear_priv *data;
 
-	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+		return -ENXIO;
 	data = (struct linear_priv *)plugin->extra_data;
 	if (frames == 0)
 		return 0;
@@ -100,12 +101,12 @@
 	{
 		unsigned int channel;
 		for (channel = 0; channel < plugin->src_format.channels; channel++) {
-			snd_assert(src_channels[channel].area.first % 8 == 0 &&
-				   src_channels[channel].area.step % 8 == 0,
-				   return -ENXIO);
-			snd_assert(dst_channels[channel].area.first % 8 == 0 &&
-				   dst_channels[channel].area.step % 8 == 0,
-				   return -ENXIO);
+			if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+				       src_channels[channel].area.step % 8))
+				return -ENXIO;
+			if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+				       dst_channels[channel].area.step % 8))
+				return -ENXIO;
 		}
 	}
 #endif
@@ -154,13 +155,17 @@
 	struct linear_priv *data;
 	struct snd_pcm_plugin *plugin;
 
-	snd_assert(r_plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!r_plugin))
+		return -ENXIO;
 	*r_plugin = NULL;
 
-	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
-	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
-	snd_assert(snd_pcm_format_linear(src_format->format) &&
-		   snd_pcm_format_linear(dst_format->format), return -ENXIO);
+	if (snd_BUG_ON(src_format->rate != dst_format->rate))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->channels != dst_format->channels))
+		return -ENXIO;
+	if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||
+		       !snd_pcm_format_linear(dst_format->format)))
+		return -ENXIO;
 
 	err = snd_pcm_plugin_build(plug, "linear format conversion",
 				   src_format, dst_format,
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 581aa2c..4690b8b 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -257,8 +257,10 @@
 		result = pslot->get_volume(fmixer, pslot, &left, &right);
 	if (!pslot->stereo)
 		right = left;
-	snd_assert(left >= 0 && left <= 100, return -EIO);
-	snd_assert(right >= 0 && right <= 100, return -EIO);
+	if (snd_BUG_ON(left < 0 || left > 100))
+		return -EIO;
+	if (snd_BUG_ON(right < 0 || right > 100))
+		return -EIO;
 	if (result >= 0) {
 		pslot->volume[0] = left;
 		pslot->volume[1] = right;
@@ -298,7 +300,8 @@
 	int __user *p = argp;
 	int tmp;
 
-	snd_assert(fmixer != NULL, return -ENXIO);
+	if (snd_BUG_ON(!fmixer))
+		return -ENXIO;
 	if (((cmd >> 8) & 0xff) == 'M') {
 		switch (cmd) {
 		case SOUND_MIXER_INFO:
@@ -368,7 +371,8 @@
 {
 	struct snd_mixer_oss_file fmixer;
 	
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
 	if (card->mixer_oss == NULL)
 		return -ENXIO;
 	memset(&fmixer, 0, sizeof(fmixer));
@@ -1284,9 +1288,11 @@
 	struct snd_card *card;
 	int idx;
  
-	snd_assert(mixer != NULL, return -ENXIO);
+	if (!mixer)
+		return 0;
 	card = mixer->card;
-	snd_assert(mixer == card->mixer_oss, return -ENXIO);
+	if (snd_BUG_ON(mixer != card->mixer_oss))
+		return -ENXIO;
 	card->mixer_oss = NULL;
 	for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
 		struct snd_mixer_oss_slot *chn = &mixer->slots[idx];
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index 77f9619..f7649d4 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -252,19 +252,20 @@
 {
 	struct mulaw_priv *data;
 
-	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+		return -ENXIO;
 	if (frames == 0)
 		return 0;
 #ifdef CONFIG_SND_DEBUG
 	{
 		unsigned int channel;
 		for (channel = 0; channel < plugin->src_format.channels; channel++) {
-			snd_assert(src_channels[channel].area.first % 8 == 0 &&
-				   src_channels[channel].area.step % 8 == 0,
-				   return -ENXIO);
-			snd_assert(dst_channels[channel].area.first % 8 == 0 &&
-				   dst_channels[channel].area.step % 8 == 0,
-				   return -ENXIO);
+			if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+				       src_channels[channel].area.step % 8))
+				return -ENXIO;
+			if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+				       dst_channels[channel].area.step % 8))
+				return -ENXIO;
 		}
 	}
 #endif
@@ -305,11 +306,14 @@
 	struct snd_pcm_plugin_format *format;
 	mulaw_f func;
 
-	snd_assert(r_plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!r_plugin))
+		return -ENXIO;
 	*r_plugin = NULL;
 
-	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
-	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
+	if (snd_BUG_ON(src_format->rate != dst_format->rate))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->channels != dst_format->channels))
+		return -ENXIO;
 
 	if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
 		format = src_format;
@@ -323,7 +327,8 @@
 		snd_BUG();
 		return -EINVAL;
 	}
-	snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
+	if (snd_BUG_ON(!snd_pcm_format_linear(format->format)))
+		return -ENXIO;
 
 	err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
 				   src_format, dst_format,
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 4c601b1..1af62b8 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -452,7 +452,8 @@
 	} else {
 		*params = *save;
 		max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
-		snd_assert(max >= 0, return -EINVAL);
+		if (max < 0)
+			return max;
 		last = 1;
 	}
  _end:
@@ -461,7 +462,7 @@
 		v = snd_pcm_hw_param_last(pcm, params, var, dir);
 	else
 		v = snd_pcm_hw_param_first(pcm, params, var, dir);
-	snd_assert(v >= 0, return -EINVAL);
+	snd_BUG_ON(v < 0);
 	return v;
 }
 
@@ -778,7 +779,8 @@
 	while (oss_period_size * oss_periods > oss_buffer_size)
 		oss_period_size /= 2;
 
-	snd_assert(oss_period_size >= 16, return -EINVAL);
+	if (oss_period_size < 16)
+		return -EINVAL;
 	runtime->oss.period_bytes = oss_period_size;
 	runtime->oss.period_frames = 1;
 	runtime->oss.periods = oss_periods;
@@ -895,7 +897,8 @@
 		}
 	}
 	err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
-	snd_assert(err >= 0, goto failure);
+	if (err < 0)
+		goto failure;
 
 	if (direct) {
 		memcpy(params, sparams, sizeof(*params));
@@ -958,11 +961,13 @@
 
 	n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
 	err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
-	snd_assert(err >= 0, goto failure);
+	if (err < 0)
+		goto failure;
 
 	err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
 				     runtime->oss.periods, NULL);
-	snd_assert(err >= 0, goto failure);
+	if (err < 0)
+		goto failure;
 
 	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
 
@@ -1006,7 +1011,10 @@
 
 	runtime->oss.periods = params_periods(sparams);
 	oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
-	snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure);
+	if (oss_period_size < 0) {
+		err = -EINVAL;
+		goto failure;
+	}
 #ifdef CONFIG_SND_PCM_OSS_PLUGINS
 	if (runtime->oss.plugin_first) {
 		err = snd_pcm_plug_alloc(substream, oss_period_size);
@@ -1017,7 +1025,10 @@
 	oss_period_size *= oss_frame_size;
 
 	oss_buffer_size = oss_period_size * runtime->oss.periods;
-	snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure);
+	if (oss_buffer_size < 0) {
+		err = -EINVAL;
+		goto failure;
+	}
 
 	runtime->oss.period_bytes = oss_period_size;
 	runtime->oss.buffer_bytes = oss_buffer_size;
@@ -1069,7 +1080,8 @@
 				return err;
 		}
 	}
-	snd_assert(asubstream != NULL, return -EIO);
+	if (!asubstream)
+		return -EIO;
 	if (r_substream)
 		*r_substream = asubstream;
 	return 0;
@@ -1764,7 +1776,8 @@
 	err = snd_pcm_hw_refine(substream, params);
 	format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 
 	kfree(params);
-	snd_assert(err >= 0, return err);
+	if (err < 0)
+		return err;
 	for (fmt = 0; fmt < 32; ++fmt) {
 		if (snd_mask_test(&format_mask, fmt)) {
 			int f = snd_pcm_oss_format_to(fmt);
@@ -2250,7 +2263,8 @@
 static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
 {
 	int cidx;
-	snd_assert(pcm_oss_file != NULL, return -ENXIO);
+	if (!pcm_oss_file)
+		return 0;
 	for (cidx = 0; cidx < 2; ++cidx) {
 		struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];
 		if (substream)
@@ -2271,8 +2285,8 @@
 	struct snd_pcm_substream *substream;
 	unsigned int f_mode = file->f_mode;
 
-	snd_assert(rpcm_oss_file != NULL, return -EINVAL);
-	*rpcm_oss_file = NULL;
+	if (rpcm_oss_file)
+		*rpcm_oss_file = NULL;
 
 	pcm_oss_file = kzalloc(sizeof(*pcm_oss_file), GFP_KERNEL);
 	if (pcm_oss_file == NULL)
@@ -2312,7 +2326,8 @@
 	}
 
 	file->private_data = pcm_oss_file;
-	*rpcm_oss_file = pcm_oss_file;
+	if (rpcm_oss_file)
+		*rpcm_oss_file = pcm_oss_file;
 	return 0;
 }
 
@@ -2321,7 +2336,8 @@
 {
 	unsigned int idx;
 
-	snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
+	if (snd_BUG_ON(!task || !name || size < 2))
+		return -EINVAL;
 	for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
 		name[idx] = task->comm[idx];
 	name[idx] = '\0';
@@ -2415,7 +2431,8 @@
 	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
 	if (substream == NULL)
 		substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
-	snd_assert(substream != NULL, return -ENXIO);
+	if (snd_BUG_ON(!substream))
+		return -ENXIO;
 	pcm = substream->pcm;
 	if (!pcm->card->shutdown)
 		snd_pcm_oss_sync(pcm_oss_file);
@@ -2448,7 +2465,8 @@
 			if (substream != NULL)
 				break;
 		}
-		snd_assert(substream != NULL, return -ENXIO);
+		if (snd_BUG_ON(idx >= 2))
+			return -ENXIO;
 		return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);
 	}
 #endif
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index bec9413..6751daa 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -62,7 +62,8 @@
 	if ((width = snd_pcm_format_physical_width(format->format)) < 0)
 		return width;
 	size = frames * format->channels * width;
-	snd_assert((size % 8) == 0, return -ENXIO);
+	if (snd_BUG_ON(size % 8))
+		return -ENXIO;
 	size /= 8;
 	if (plugin->buf_frames < frames) {
 		vfree(plugin->buf);
@@ -84,7 +85,8 @@
 			c->area.step = format->channels * width;
 		}
 	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
-		snd_assert((size % format->channels) == 0,);
+		if (snd_BUG_ON(size % format->channels))
+			return -EINVAL;
 		size /= format->channels;
 		for (channel = 0; channel < format->channels; channel++, c++) {
 			c->frames = frames;
@@ -102,13 +104,15 @@
 int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
 {
 	int err;
-	snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
+	if (snd_BUG_ON(!snd_pcm_plug_first(plug)))
+		return -ENXIO;
 	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
 		struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);
 		while (plugin->next) {
 			if (plugin->dst_frames)
 				frames = plugin->dst_frames(plugin, frames);
-			snd_assert(frames > 0, return -ENXIO);
+			if (snd_BUG_ON(frames <= 0))
+				return -ENXIO;
 			plugin = plugin->next;
 			err = snd_pcm_plugin_alloc(plugin, frames);
 			if (err < 0)
@@ -119,7 +123,8 @@
 		while (plugin->prev) {
 			if (plugin->src_frames)
 				frames = plugin->src_frames(plugin, frames);
-			snd_assert(frames > 0, return -ENXIO);
+			if (snd_BUG_ON(frames <= 0))
+				return -ENXIO;
 			plugin = plugin->prev;
 			err = snd_pcm_plugin_alloc(plugin, frames);
 			if (err < 0)
@@ -148,8 +153,10 @@
 	struct snd_pcm_plugin *plugin;
 	unsigned int channels;
 	
-	snd_assert(plug != NULL, return -ENXIO);
-	snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plug))
+		return -ENXIO;
+	if (snd_BUG_ON(!src_format || !dst_format))
+		return -ENXIO;
 	plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);
 	if (plugin == NULL)
 		return -ENOMEM;
@@ -159,10 +166,10 @@
 	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
 	plugin->src_format = *src_format;
 	plugin->src_width = snd_pcm_format_physical_width(src_format->format);
-	snd_assert(plugin->src_width > 0, );
+	snd_BUG_ON(plugin->src_width <= 0);
 	plugin->dst_format = *dst_format;
 	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
-	snd_assert(plugin->dst_width > 0, );
+	snd_BUG_ON(plugin->dst_width <= 0);
 	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		channels = src_format->channels;
 	else
@@ -194,7 +201,8 @@
 	struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
 	int stream = snd_pcm_plug_stream(plug);
 
-	snd_assert(plug != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plug))
+		return -ENXIO;
 	if (drv_frames == 0)
 		return 0;
 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -224,7 +232,8 @@
 	snd_pcm_sframes_t frames;
 	int stream = snd_pcm_plug_stream(plug);
 	
-	snd_assert(plug != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plug))
+		return -ENXIO;
 	if (clt_frames == 0)
 		return 0;
 	frames = clt_frames;
@@ -540,7 +549,8 @@
 	int width, nchannels, channel;
 	int stream = snd_pcm_plug_stream(plug);
 
-	snd_assert(buf != NULL, return -ENXIO);
+	if (snd_BUG_ON(!buf))
+		return -ENXIO;
 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		plugin = snd_pcm_plug_first(plug);
 		format = &plugin->src_format;
@@ -553,7 +563,9 @@
 	if ((width = snd_pcm_format_physical_width(format->format)) < 0)
 		return width;
 	nchannels = format->channels;
-	snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
+	if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
+		       format->channels > 1))
+		return -ENXIO;
 	for (channel = 0; channel < nchannels; channel++, v++) {
 		v->frames = count;
 		v->enabled = 1;
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 14dfb31..a466443 100644
--- a/sound/core/oss/rate.c
+++ b/sound/core/oss/rate.c
@@ -185,7 +185,8 @@
 	struct rate_priv *data;
 	snd_pcm_sframes_t res;
 
-	snd_assert(plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin))
+		return -ENXIO;
 	if (frames == 0)
 		return 0;
 	data = (struct rate_priv *)plugin->extra_data;
@@ -217,7 +218,8 @@
 	struct rate_priv *data;
 	snd_pcm_sframes_t res;
 
-	snd_assert(plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin))
+		return -ENXIO;
 	if (frames == 0)
 		return 0;
 	data = (struct rate_priv *)plugin->extra_data;
@@ -252,19 +254,20 @@
 	snd_pcm_uframes_t dst_frames;
 	struct rate_priv *data;
 
-	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+		return -ENXIO;
 	if (frames == 0)
 		return 0;
 #ifdef CONFIG_SND_DEBUG
 	{
 		unsigned int channel;
 		for (channel = 0; channel < plugin->src_format.channels; channel++) {
-			snd_assert(src_channels[channel].area.first % 8 == 0 &&
-				   src_channels[channel].area.step % 8 == 0,
-				   return -ENXIO);
-			snd_assert(dst_channels[channel].area.first % 8 == 0 &&
-				   dst_channels[channel].area.step % 8 == 0,
-				   return -ENXIO);
+			if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
+				       src_channels[channel].area.step % 8))
+				return -ENXIO;
+			if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
+				       dst_channels[channel].area.step % 8))
+				return -ENXIO;
 		}
 	}
 #endif
@@ -281,7 +284,8 @@
 		       enum snd_pcm_plugin_action action,
 		       unsigned long udata)
 {
-	snd_assert(plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin))
+		return -ENXIO;
 	switch (action) {
 	case INIT:
 	case PREPARE:
@@ -302,14 +306,20 @@
 	struct rate_priv *data;
 	struct snd_pcm_plugin *plugin;
 
-	snd_assert(r_plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!r_plugin))
+		return -ENXIO;
 	*r_plugin = NULL;
 
-	snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
-	snd_assert(src_format->channels > 0, return -ENXIO);
-	snd_assert(src_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO);
-	snd_assert(dst_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO);
-	snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
+	if (snd_BUG_ON(src_format->channels != dst_format->channels))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->channels <= 0))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->format != SNDRV_PCM_FORMAT_S16))
+		return -ENXIO;
+	if (snd_BUG_ON(dst_format->format != SNDRV_PCM_FORMAT_S16))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->rate == dst_format->rate))
+		return -ENXIO;
 
 	err = snd_pcm_plugin_build(plug, "rate conversion",
 				   src_format, dst_format,
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index da7ab7a..0dcc287 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -54,7 +54,8 @@
 	struct snd_pcm_plugin_channel *dvp;
 	int format;
 
-	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
+	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
+		return -ENXIO;
 	if (frames == 0)
 		return 0;
 
@@ -90,10 +91,13 @@
 	struct snd_pcm_plugin *plugin;
 	int err;
 
-	snd_assert(r_plugin != NULL, return -ENXIO);
+	if (snd_BUG_ON(!r_plugin))
+		return -ENXIO;
 	*r_plugin = NULL;
-	snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
-	snd_assert(src_format->format == dst_format->format, return -ENXIO);
+	if (snd_BUG_ON(src_format->rate != dst_format->rate))
+		return -ENXIO;
+	if (snd_BUG_ON(src_format->format != dst_format->format))
+		return -ENXIO;
 
 	err = snd_pcm_plugin_build(plug, "route conversion",
 				   src_format, dst_format, 0, &plugin);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index ece25c7..192a433 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -42,7 +42,7 @@
 static int snd_pcm_dev_register(struct snd_device *device);
 static int snd_pcm_dev_disconnect(struct snd_device *device);
 
-static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
+static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
 {
 	struct snd_pcm *pcm;
 
@@ -53,6 +53,37 @@
 	return NULL;
 }
 
+static int snd_pcm_next(struct snd_card *card, int device)
+{
+	struct snd_pcm *pcm;
+
+	list_for_each_entry(pcm, &snd_pcm_devices, list) {
+		if (pcm->card == card && pcm->device > device)
+			return pcm->device;
+		else if (pcm->card->number > card->number)
+			return -1;
+	}
+	return -1;
+}
+
+static int snd_pcm_add(struct snd_pcm *newpcm)
+{
+	struct snd_pcm *pcm;
+
+	list_for_each_entry(pcm, &snd_pcm_devices, list) {
+		if (pcm->card == newpcm->card && pcm->device == newpcm->device)
+			return -EBUSY;
+		if (pcm->card->number > newpcm->card->number ||
+				(pcm->card == newpcm->card &&
+				pcm->device > newpcm->device)) {
+			list_add(&newpcm->list, pcm->list.prev);
+			return 0;
+		}
+	}
+	list_add_tail(&newpcm->list, &snd_pcm_devices);
+	return 0;
+}
+
 static int snd_pcm_control_ioctl(struct snd_card *card,
 				 struct snd_ctl_file *control,
 				 unsigned int cmd, unsigned long arg)
@@ -65,14 +96,7 @@
 			if (get_user(device, (int __user *)arg))
 				return -EFAULT;
 			mutex_lock(&register_mutex);
-			device = device < 0 ? 0 : device + 1;
-			while (device < SNDRV_PCM_DEVICES) {
-				if (snd_pcm_search(card, device))
-					break;
-				device++;
-			}
-			if (device == SNDRV_PCM_DEVICES)
-				device = -1;
+			device = snd_pcm_next(card, device);
 			mutex_unlock(&register_mutex);
 			if (put_user(device, (int __user *)arg))
 				return -EFAULT;
@@ -98,7 +122,7 @@
 			if (get_user(subdevice, &info->subdevice))
 				return -EFAULT;
 			mutex_lock(&register_mutex);
-			pcm = snd_pcm_search(card, device);
+			pcm = snd_pcm_get(card, device);
 			if (pcm == NULL) {
 				err = -ENXIO;
 				goto _error;
@@ -232,7 +256,6 @@
 
 static const char *snd_pcm_stream_name(int stream)
 {
-	snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL);
 	return snd_pcm_stream_names[stream];
 }
 
@@ -248,7 +271,6 @@
 
 static const char *snd_pcm_tstamp_mode_name(int mode)
 {
-	snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL);
 	return snd_pcm_tstamp_mode_names[mode];
 }
 
@@ -682,9 +704,10 @@
 		.dev_disconnect = snd_pcm_dev_disconnect,
 	};
 
-	snd_assert(rpcm != NULL, return -EINVAL);
-	*rpcm = NULL;
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
+	if (rpcm)
+		*rpcm = NULL;
 	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 	if (pcm == NULL) {
 		snd_printk(KERN_ERR "Cannot allocate PCM\n");
@@ -708,7 +731,8 @@
 		snd_pcm_free(pcm);
 		return err;
 	}
-	*rpcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
@@ -742,7 +766,8 @@
 {
 	struct snd_pcm_notify *notify;
 
-	snd_assert(pcm != NULL, return -ENXIO);
+	if (!pcm)
+		return 0;
 	list_for_each_entry(notify, &snd_pcm_notify_list, list) {
 		notify->n_unregister(pcm);
 	}
@@ -773,9 +798,9 @@
 	int prefer_subdevice = -1;
 	size_t size;
 
-	snd_assert(rsubstream != NULL, return -EINVAL);
+	if (snd_BUG_ON(!pcm || !rsubstream))
+		return -ENXIO;
 	*rsubstream = NULL;
-	snd_assert(pcm != NULL, return -ENXIO);
 	pstr = &pcm->streams[stream];
 	if (pstr->substream == NULL || pstr->substream_count == 0)
 		return -ENODEV;
@@ -883,8 +908,9 @@
 {
 	struct snd_pcm_runtime *runtime;
 
+	if (PCM_RUNTIME_CHECK(substream))
+		return;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return);
 	if (runtime->private_free != NULL)
 		runtime->private_free(runtime);
 	snd_free_pages((void*)runtime->status,
@@ -929,13 +955,14 @@
 	struct snd_pcm *pcm = device->device_data;
 	struct device *dev;
 
-	snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+	if (snd_BUG_ON(!pcm || !device))
+		return -ENXIO;
 	mutex_lock(&register_mutex);
-	if (snd_pcm_search(pcm->card, pcm->device)) {
+	err = snd_pcm_add(pcm);
+	if (err) {
 		mutex_unlock(&register_mutex);
-		return -EBUSY;
+		return err;
 	}
-	list_add_tail(&pcm->list, &snd_pcm_devices);
 	for (cidx = 0; cidx < 2; cidx++) {
 		int devtype = -1;
 		if (pcm->streams[cidx].substream == NULL)
@@ -1019,10 +1046,11 @@
 {
 	struct snd_pcm *pcm;
 
-	snd_assert(notify != NULL &&
-		   notify->n_register != NULL &&
-		   notify->n_unregister != NULL &&
-		   notify->n_disconnect, return -EINVAL);
+	if (snd_BUG_ON(!notify ||
+		       !notify->n_register ||
+		       !notify->n_unregister ||
+		       !notify->n_disconnect))
+		return -EINVAL;
 	mutex_lock(&register_mutex);
 	if (nfree) {
 		list_del(&notify->list);
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 49aa693..36d7a59 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -397,7 +397,8 @@
 	snd_pcm_uframes_t boundary;
 	int err;
 
-	snd_assert(runtime, return -EINVAL);
+	if (snd_BUG_ON(!runtime))
+		return -EINVAL;
 
 	if (get_user(sflags, &src->flags) ||
 	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 1533f03..6ea5cfb 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -85,7 +85,8 @@
 		}
 		frames = runtime->buffer_size - runtime->silence_filled;
 	}
-	snd_assert(frames <= runtime->buffer_size, return);
+	if (snd_BUG_ON(frames > runtime->buffer_size))
+		return;
 	if (frames == 0)
 		return;
 	ofs = runtime->silence_start % runtime->buffer_size;
@@ -96,7 +97,7 @@
 			if (substream->ops->silence) {
 				int err;
 				err = substream->ops->silence(substream, -1, ofs, transfer);
-				snd_assert(err >= 0, );
+				snd_BUG_ON(err < 0);
 			} else {
 				char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
 				snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
@@ -108,7 +109,7 @@
 				for (c = 0; c < channels; ++c) {
 					int err;
 					err = substream->ops->silence(substream, c, ofs, transfer);
-					snd_assert(err >= 0, );
+					snd_BUG_ON(err < 0);
 				}
 			} else {
 				size_t dma_csize = runtime->dma_bytes / channels;
@@ -354,7 +355,7 @@
 {
 	u_int64_t n = (u_int64_t) a * b;
 	if (c == 0) {
-		snd_assert(n > 0, );
+		snd_BUG_ON(!n);
 		*r = 0;
 		return UINT_MAX;
 	}
@@ -380,7 +381,8 @@
 int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
 {
 	int changed = 0;
-	snd_assert(!snd_interval_empty(i), return -EINVAL);
+	if (snd_BUG_ON(snd_interval_empty(i)))
+		return -EINVAL;
 	if (i->min < v->min) {
 		i->min = v->min;
 		i->openmin = v->openmin;
@@ -423,7 +425,8 @@
 
 static int snd_interval_refine_first(struct snd_interval *i)
 {
-	snd_assert(!snd_interval_empty(i), return -EINVAL);
+	if (snd_BUG_ON(snd_interval_empty(i)))
+		return -EINVAL;
 	if (snd_interval_single(i))
 		return 0;
 	i->max = i->min;
@@ -435,7 +438,8 @@
 
 static int snd_interval_refine_last(struct snd_interval *i)
 {
-	snd_assert(!snd_interval_empty(i), return -EINVAL);
+	if (snd_BUG_ON(snd_interval_empty(i)))
+		return -EINVAL;
 	if (snd_interval_single(i))
 		return 0;
 	i->min = i->max;
@@ -889,7 +893,8 @@
 	c->private = private;
 	k = 0;
 	while (1) {
-		snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL);
+		if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps)))
+			return -EINVAL;
 		c->deps[k++] = dep;
 		if (dep < 0)
 			break;
@@ -1285,7 +1290,8 @@
 		return changed;
 	if (params->rmask) {
 		int err = snd_pcm_hw_refine(pcm, params);
-		snd_assert(err >= 0, return err);
+		if (snd_BUG_ON(err < 0))
+			return err;
 	}
 	return snd_pcm_hw_param_value(params, var, dir);
 }
@@ -1330,7 +1336,8 @@
 		return changed;
 	if (params->rmask) {
 		int err = snd_pcm_hw_refine(pcm, params);
-		snd_assert(err >= 0, return err);
+		if (snd_BUG_ON(err < 0))
+			return err;
 	}
 	return snd_pcm_hw_param_value(params, var, dir);
 }
@@ -1368,7 +1375,8 @@
 			err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
 		else
 			err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
-		snd_assert(err >= 0, return err);
+		if (snd_BUG_ON(err < 0))
+			return err;
 	}
 	return 0;
 }
@@ -1466,9 +1474,9 @@
 	struct snd_pcm_runtime *runtime;
 	unsigned long flags;
 
-	snd_assert(substream != NULL, return);
+	if (PCM_RUNTIME_CHECK(substream))
+		return;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return);
 
 	if (runtime->transfer_ack_begin)
 		runtime->transfer_ack_begin(substream);
@@ -1567,7 +1575,6 @@
 			return err;
 	} else {
 		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
-		snd_assert(runtime->dma_area, return -EFAULT);
 		if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
 			return -EFAULT;
 	}
@@ -1629,7 +1636,10 @@
 		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
 		if (frames > cont)
 			frames = cont;
-		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+		if (snd_BUG_ON(!frames)) {
+			snd_pcm_stream_unlock_irq(substream);
+			return -EINVAL;
+		}
 		appl_ptr = runtime->control->appl_ptr;
 		appl_ofs = appl_ptr % runtime->buffer_size;
 		snd_pcm_stream_unlock_irq(substream);
@@ -1669,18 +1679,30 @@
 	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
 }
 
+/* sanity-check for read/write methods */
+static int pcm_sanity_check(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
+	runtime = substream->runtime;
+	if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
+		return -EINVAL;
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	return 0;
+}
+
 snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
 {
 	struct snd_pcm_runtime *runtime;
 	int nonblock;
+	int err;
 
-	snd_assert(substream != NULL, return -ENXIO);
+	err = pcm_sanity_check(substream);
+	if (err < 0)
+		return err;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
-	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
-	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		return -EBADFD;
-
 	nonblock = !!(substream->f_flags & O_NONBLOCK);
 
 	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
@@ -1703,7 +1725,8 @@
 	int channels = runtime->channels;
 	int c;
 	if (substream->ops->copy) {
-		snd_assert(substream->ops->silence != NULL, return -EINVAL);
+		if (snd_BUG_ON(!substream->ops->silence))
+			return -EINVAL;
 		for (c = 0; c < channels; ++c, ++bufs) {
 			if (*bufs == NULL) {
 				if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
@@ -1717,7 +1740,6 @@
 	} else {
 		/* default transfer behaviour */
 		size_t dma_csize = runtime->dma_bytes / channels;
-		snd_assert(runtime->dma_area, return -EFAULT);
 		for (c = 0; c < channels; ++c, ++bufs) {
 			char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
 			if (*bufs == NULL) {
@@ -1738,14 +1760,12 @@
 {
 	struct snd_pcm_runtime *runtime;
 	int nonblock;
+	int err;
 
-	snd_assert(substream != NULL, return -ENXIO);
+	err = pcm_sanity_check(substream);
+	if (err < 0)
+		return err;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
-	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
-	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		return -EBADFD;
-
 	nonblock = !!(substream->f_flags & O_NONBLOCK);
 
 	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
@@ -1769,7 +1789,6 @@
 			return err;
 	} else {
 		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
-		snd_assert(runtime->dma_area, return -EFAULT);
 		if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
 			return -EFAULT;
 	}
@@ -1841,7 +1860,10 @@
 		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
 		if (frames > cont)
 			frames = cont;
-		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
+		if (snd_BUG_ON(!frames)) {
+			snd_pcm_stream_unlock_irq(substream);
+			return -EINVAL;
+		}
 		appl_ptr = runtime->control->appl_ptr;
 		appl_ofs = appl_ptr % runtime->buffer_size;
 		snd_pcm_stream_unlock_irq(substream);
@@ -1879,14 +1901,12 @@
 {
 	struct snd_pcm_runtime *runtime;
 	int nonblock;
+	int err;
 	
-	snd_assert(substream != NULL, return -ENXIO);
+	err = pcm_sanity_check(substream);
+	if (err < 0)
+		return err;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
-	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
-	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
-		return -EBADFD;
-
 	nonblock = !!(substream->f_flags & O_NONBLOCK);
 	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
 		return -EINVAL;
@@ -1916,7 +1936,6 @@
 		}
 	} else {
 		snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
-		snd_assert(runtime->dma_area, return -EFAULT);
 		for (c = 0; c < channels; ++c, ++bufs) {
 			char *hwbuf;
 			char __user *buf;
@@ -1938,11 +1957,12 @@
 {
 	struct snd_pcm_runtime *runtime;
 	int nonblock;
+	int err;
 
-	snd_assert(substream != NULL, return -ENXIO);
+	err = pcm_sanity_check(substream);
+	if (err < 0)
+		return err;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
-	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
 
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index ff07b4a..a6d4280 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -50,8 +50,6 @@
 	struct snd_dma_buffer *dmab = &substream->dma_buffer;
 	int err;
 
-	snd_assert(size > 0, return -EINVAL);
-
 	/* already reserved? */
 	if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
 		if (dmab->bytes >= size)
@@ -326,6 +324,32 @@
 
 EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
 
+/*
+ * compute the max chunk size with continuous pages on sg-buffer
+ */
+unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
+					  unsigned int ofs, unsigned int size)
+{
+	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
+	unsigned int start, end, pg;
+
+	start = ofs >> PAGE_SHIFT;
+	end = (ofs + size - 1) >> PAGE_SHIFT;
+	/* check page continuity */
+	pg = sg->table[start].addr >> PAGE_SHIFT;
+	for (;;) {
+		start++;
+		if (start > end)
+			break;
+		pg++;
+		if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
+			return (start << PAGE_SHIFT) - ofs;
+	}
+	/* ok, all on continuous pages */
+	return size;
+}
+EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
+
 /**
  * snd_pcm_lib_malloc_pages - allocate the DMA buffer
  * @substream: the substream to allocate the DMA buffer to
@@ -342,10 +366,12 @@
 	struct snd_pcm_runtime *runtime;
 	struct snd_dma_buffer *dmab = NULL;
 
-	snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL);
-	snd_assert(substream != NULL, return -EINVAL);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -EINVAL;
+	if (snd_BUG_ON(substream->dma_buffer.dev.type ==
+		       SNDRV_DMA_TYPE_UNKNOWN))
+		return -EINVAL;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -EINVAL);
 
 	if (runtime->dma_buffer_p) {
 		/* perphaps, we might free the large DMA memory region
@@ -391,9 +417,9 @@
 {
 	struct snd_pcm_runtime *runtime;
 
-	snd_assert(substream != NULL, return -EINVAL);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -EINVAL;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -EINVAL);
 	if (runtime->dma_area == NULL)
 		return 0;
 	if (runtime->dma_buffer_p != &substream->dma_buffer) {
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c487025..e61e125 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -95,7 +95,6 @@
 	struct snd_pcm *pcm = substream->pcm;
 	struct snd_pcm_str *pstr = substream->pstr;
 
-	snd_assert(substream != NULL, return -ENXIO);
 	memset(info, 0, sizeof(*info));
 	info->card = pcm->card->number;
 	info->device = pcm->device;
@@ -370,9 +369,9 @@
 	unsigned int bits;
 	snd_pcm_uframes_t frames;
 
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
 	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_OPEN:
@@ -490,9 +489,9 @@
 	struct snd_pcm_runtime *runtime;
 	int result = 0;
 
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
 	snd_pcm_stream_lock_irq(substream);
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_SETUP:
@@ -518,9 +517,9 @@
 {
 	struct snd_pcm_runtime *runtime;
 
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -ENXIO);
 	snd_pcm_stream_lock_irq(substream);
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 		snd_pcm_stream_unlock_irq(substream);
@@ -622,11 +621,8 @@
 			       struct snd_pcm_status __user * _status)
 {
 	struct snd_pcm_status status;
-	struct snd_pcm_runtime *runtime;
 	int res;
 	
-	snd_assert(substream != NULL, return -ENXIO);
-	runtime = substream->runtime;
 	memset(&status, 0, sizeof(status));
 	res = snd_pcm_status(substream, &status);
 	if (res < 0)
@@ -642,7 +638,6 @@
 	struct snd_pcm_runtime *runtime;
 	unsigned int channel;
 	
-	snd_assert(substream != NULL, return -ENXIO);
 	channel = info->channel;
 	runtime = substream->runtime;
 	snd_pcm_stream_lock_irq(substream);
@@ -1250,7 +1245,6 @@
 	int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
 	if (err < 0)
 		return err;
-	// snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );
 	runtime->hw_ptr_base = 0;
 	runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
 		runtime->status->hw_ptr % runtime->period_size;
@@ -1421,7 +1415,6 @@
 	int i, num_drecs;
 	struct drain_rec *drec, drec_tmp, *d;
 
-	snd_assert(substream != NULL, return -ENXIO);
 	card = substream->pcm->card;
 	runtime = substream->runtime;
 
@@ -1541,7 +1534,8 @@
 	struct snd_card *card;
 	int result = 0;
 	
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
 	card = substream->pcm->card;
 
@@ -1934,33 +1928,41 @@
 			mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
 	}
 	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
-	snd_assert(err >= 0, return -EINVAL);
+	if (err < 0)
+		return err;
 
 	err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
-	snd_assert(err >= 0, return -EINVAL);
+	if (err < 0)
+		return err;
 
 	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
-	snd_assert(err >= 0, return -EINVAL);
+	if (err < 0)
+		return err;
 
 	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
 					   hw->channels_min, hw->channels_max);
-	snd_assert(err >= 0, return -EINVAL);
+	if (err < 0)
+		return err;
 
 	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
 					   hw->rate_min, hw->rate_max);
-	snd_assert(err >= 0, return -EINVAL);
+	 if (err < 0)
+		 return err;
 
 	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 					   hw->period_bytes_min, hw->period_bytes_max);
-	snd_assert(err >= 0, return -EINVAL);
+	 if (err < 0)
+		 return err;
 
 	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
 					   hw->periods_min, hw->periods_max);
-	snd_assert(err >= 0, return -EINVAL);
+	if (err < 0)
+		return err;
 
 	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 					   hw->period_bytes_min, hw->buffer_bytes_max);
-	snd_assert(err >= 0, return -EINVAL);
+	if (err < 0)
+		return err;
 
 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 
 				  snd_pcm_hw_rule_buffer_bytes_max, substream,
@@ -1971,7 +1973,8 @@
 	/* FIXME: remove */
 	if (runtime->dma_bytes) {
 		err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
-		snd_assert(err >= 0, return -EINVAL);
+		if (err < 0)
+			return -EINVAL;
 	}
 
 	if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
@@ -2067,8 +2070,8 @@
 	struct snd_pcm_str *str;
 	int err;
 
-	snd_assert(rpcm_file != NULL, return -EINVAL);
-	*rpcm_file = NULL;
+	if (rpcm_file)
+		*rpcm_file = NULL;
 
 	err = snd_pcm_open_substream(pcm, stream, file, &substream);
 	if (err < 0)
@@ -2086,7 +2089,8 @@
 		substream->pcm_release = pcm_release_private;
 	}
 	file->private_data = pcm_file;
-	*rpcm_file = pcm_file;
+	if (rpcm_file)
+		*rpcm_file = pcm_file;
 	return 0;
 }
 
@@ -2170,7 +2174,8 @@
 
 	pcm_file = file->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, return -ENXIO);
+	if (snd_BUG_ON(!substream))
+		return -ENXIO;
 	pcm = substream->pcm;
 	fasync_helper(-1, file, 0, &substream->runtime->fasync);
 	mutex_lock(&pcm->open_mutex);
@@ -2493,8 +2498,6 @@
 				 struct snd_pcm_substream *substream,
 				 unsigned int cmd, void __user *arg)
 {
-	snd_assert(substream != NULL, return -ENXIO);
-
 	switch (cmd) {
 	case SNDRV_PCM_IOCTL_PVERSION:
 		return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
@@ -2563,8 +2566,10 @@
 				   struct snd_pcm_substream *substream,
 				   unsigned int cmd, void __user *arg)
 {
-	snd_assert(substream != NULL, return -ENXIO);
-	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
+	if (snd_BUG_ON(!substream))
+		return -ENXIO;
+	if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
+		return -EINVAL;
 	switch (cmd) {
 	case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
 	{
@@ -2643,8 +2648,10 @@
 				  struct snd_pcm_substream *substream,
 				  unsigned int cmd, void __user *arg)
 {
-	snd_assert(substream != NULL, return -ENXIO);
-	snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL);
+	if (snd_BUG_ON(!substream))
+		return -ENXIO;
+	if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE))
+		return -EINVAL;
 	switch (cmd) {
 	case SNDRV_PCM_IOCTL_READI_FRAMES:
 	{
@@ -2783,7 +2790,8 @@
 
 	pcm_file = file->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
@@ -2806,21 +2814,17 @@
 
 	pcm_file = file->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, result = -ENXIO; goto end);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
-	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-		result = -EBADFD;
-		goto end;
-	}
-	if (!frame_aligned(runtime, count)) {
-		result = -EINVAL;
-		goto end;
-	}
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
+	if (!frame_aligned(runtime, count))
+		return -EINVAL;
 	count = bytes_to_frames(runtime, count);
 	result = snd_pcm_lib_write(substream, buf, count);
 	if (result > 0)
 		result = frames_to_bytes(runtime, result);
- end:
 	return result;
 }
 
@@ -2838,7 +2842,8 @@
 
 	pcm_file = iocb->ki_filp->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
@@ -2872,17 +2877,14 @@
 
 	pcm_file = iocb->ki_filp->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, result = -ENXIO; goto end);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
-	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-		result = -EBADFD;
-		goto end;
-	}
+	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		return -EBADFD;
 	if (nr_segs > 128 || nr_segs != runtime->channels ||
-	    !frame_aligned(runtime, iov->iov_len)) {
-		result = -EINVAL;
-		goto end;
-	}
+	    !frame_aligned(runtime, iov->iov_len))
+		return -EINVAL;
 	frames = bytes_to_samples(runtime, iov->iov_len);
 	bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
 	if (bufs == NULL)
@@ -2893,7 +2895,6 @@
 	if (result > 0)
 		result = frames_to_bytes(runtime, result);
 	kfree(bufs);
- end:
 	return result;
 }
 
@@ -2908,7 +2909,8 @@
 	pcm_file = file->private_data;
 
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
 
 	poll_wait(file, &runtime->sleep, wait);
@@ -2946,7 +2948,8 @@
 	pcm_file = file->private_data;
 
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 	runtime = substream->runtime;
 
 	poll_wait(file, &runtime->sleep, wait);
@@ -3016,7 +3019,6 @@
 	if (!(area->vm_flags & VM_READ))
 		return -EINVAL;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -EAGAIN);
 	size = area->vm_end - area->vm_start;
 	if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
 		return -EINVAL;
@@ -3056,7 +3058,6 @@
 	if (!(area->vm_flags & VM_READ))
 		return -EINVAL;
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -EAGAIN);
 	size = area->vm_end - area->vm_start;
 	if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
 		return -EINVAL;
@@ -3188,7 +3189,6 @@
 			return -EINVAL;
 	}
 	runtime = substream->runtime;
-	snd_assert(runtime != NULL, return -EAGAIN);
 	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
 		return -EBADFD;
 	if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
@@ -3220,7 +3220,8 @@
 	
 	pcm_file = file->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, return -ENXIO);
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
 
 	offset = area->vm_pgoff << PAGE_SHIFT;
 	switch (offset) {
@@ -3248,9 +3249,9 @@
 	lock_kernel();
 	pcm_file = file->private_data;
 	substream = pcm_file->substream;
-	snd_assert(substream != NULL, goto out);
+	if (PCM_RUNTIME_CHECK(substream))
+		goto out;
 	runtime = substream->runtime;
-
 	err = fasync_helper(fd, file, on, &runtime->fasync);
 out:
 	unlock_kernel();
@@ -3384,6 +3385,17 @@
 }
 #endif /* CONFIG_SND_SUPPORT_OLD_API */
 
+#ifndef CONFIG_MMU
+unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr,
+				      unsigned long len, unsigned long pgoff,
+				      unsigned long flags)
+{
+	return 0;
+}
+#else
+# define dummy_get_unmapped_area NULL
+#endif
+
 /*
  *  Register section
  */
@@ -3400,6 +3412,7 @@
 		.compat_ioctl = 	snd_pcm_ioctl_compat,
 		.mmap =			snd_pcm_mmap,
 		.fasync =		snd_pcm_fasync,
+		.get_unmapped_area =	dummy_get_unmapped_area,
 	},
 	{
 		.owner =		THIS_MODULE,
@@ -3412,5 +3425,6 @@
 		.compat_ioctl = 	snd_pcm_ioctl_compat,
 		.mmap =			snd_pcm_mmap,
 		.fasync =		snd_pcm_fasync,
+		.get_unmapped_area =	dummy_get_unmapped_area,
 	}
 };
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 033a024..2c89c04 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -51,12 +51,14 @@
 	
         mult = 1000000000;
 	rate = runtime->rate;
-	snd_assert(rate != 0, return);
+	if (snd_BUG_ON(!rate))
+		return;
 	l = gcd(mult, rate);
 	mult /= l;
 	rate /= l;
 	fsize = runtime->period_size;
-	snd_assert(fsize != 0, return);
+	if (snd_BUG_ON(!fsize))
+		return;
 	l = gcd(rate, fsize);
 	rate /= l;
 	fsize /= l;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index b917a9f..c4995c9 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -470,8 +470,8 @@
 	struct snd_rawmidi_substream *substream;
 	struct snd_rawmidi_runtime *runtime;
 
-	snd_assert(rfile != NULL, return -ENXIO);
-	snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO);
+	if (snd_BUG_ON(!rfile))
+		return -ENXIO;
 	rmidi = rfile->rmidi;
 	mutex_lock(&rmidi->open_mutex);
 	if (rfile->input != NULL) {
@@ -1100,7 +1100,7 @@
 		return -EINVAL;
 	}
 	spin_lock_irqsave(&runtime->lock, flags);
-	snd_assert(runtime->avail + count <= runtime->buffer_size, );
+	snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
 	runtime->hw_ptr += count;
 	runtime->hw_ptr %= runtime->buffer_size;
 	runtime->avail += count;
@@ -1141,8 +1141,10 @@
 	long count1, result;
 	struct snd_rawmidi_runtime *runtime = substream->runtime;
 
-	snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL);
-	snd_assert(runtime->buffer != NULL, return -EINVAL);
+	if (snd_BUG_ON(!kernelbuf && !userbuf))
+		return -EINVAL;
+	if (snd_BUG_ON(!runtime->buffer))
+		return -EINVAL;
 
 	result = 0;
 	spin_lock_irqsave(&runtime->lock, flags);
@@ -1420,9 +1422,10 @@
 		.dev_disconnect = snd_rawmidi_dev_disconnect,
 	};
 
-	snd_assert(rrawmidi != NULL, return -EINVAL);
-	*rrawmidi = NULL;
-	snd_assert(card != NULL, return -ENXIO);
+	if (snd_BUG_ON(!card))
+		return -ENXIO;
+	if (rrawmidi)
+		*rrawmidi = NULL;
 	rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
 	if (rmidi == NULL) {
 		snd_printk(KERN_ERR "rawmidi: cannot allocate\n");
@@ -1455,7 +1458,8 @@
 		snd_rawmidi_free(rmidi);
 		return err;
 	}
-	*rrawmidi = rmidi;
+	if (rrawmidi)
+		*rrawmidi = rmidi;
 	return 0;
 }
 
@@ -1472,7 +1476,8 @@
 
 static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
 {
-	snd_assert(rmidi != NULL, return -ENXIO);	
+	if (!rmidi)
+		return 0;
 
 	snd_info_free_entry(rmidi->proc_entry);
 	rmidi->proc_entry = NULL;
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
index 97b30fb..51e64e3 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -91,7 +91,8 @@
 rtctimer_start(struct snd_timer *timer)
 {
 	rtc_task_t *rtc = timer->private_data;
-	snd_assert(rtc != NULL, return -EINVAL);
+	if (snd_BUG_ON(!rtc))
+		return -EINVAL;
 	rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
 	rtc_control(rtc, RTC_PIE_ON, 0);
 	return 0;
@@ -101,7 +102,8 @@
 rtctimer_stop(struct snd_timer *timer)
 {
 	rtc_task_t *rtc = timer->private_data;
-	snd_assert(rtc != NULL, return -EINVAL);
+	if (snd_BUG_ON(!rtc))
+		return -EINVAL;
 	rtc_control(rtc, RTC_PIE_OFF, 0);
 	return 0;
 }
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 777796e..f25e3cc 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -164,7 +164,8 @@
 {
 	struct seq_oss_devinfo *dp;
 	dp = file->private_data;
-	snd_assert(dp != NULL, return -EIO);
+	if (snd_BUG_ON(!dp))
+		return -ENXIO;
 	return snd_seq_oss_read(dp, buf, count);
 }
 
@@ -174,7 +175,8 @@
 {
 	struct seq_oss_devinfo *dp;
 	dp = file->private_data;
-	snd_assert(dp != NULL, return -EIO);
+	if (snd_BUG_ON(!dp))
+		return -ENXIO;
 	return snd_seq_oss_write(dp, buf, count, file);
 }
 
@@ -183,7 +185,8 @@
 {
 	struct seq_oss_devinfo *dp;
 	dp = file->private_data;
-	snd_assert(dp != NULL, return -EIO);
+	if (snd_BUG_ON(!dp))
+		return -ENXIO;
 	return snd_seq_oss_ioctl(dp, cmd, arg);
 }
 
@@ -198,7 +201,8 @@
 {
 	struct seq_oss_devinfo *dp;
 	dp = file->private_data;
-	snd_assert(dp != NULL, return 0);
+	if (snd_BUG_ON(!dp))
+		return -ENXIO;
 	return snd_seq_oss_poll(dp, file, wait);
 }
 
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index e024e45..945a27c 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -308,7 +308,8 @@
 	struct seq_oss_synth *rec;
 	struct seq_oss_synthinfo *info;
 
-	snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
+	if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+		return;
 	for (i = 0; i < dp->max_synthdev; i++) {
 		info = &dp->synths[i];
 		if (! info->opened)
@@ -402,7 +403,8 @@
 	struct seq_oss_synth *rec;
 	struct seq_oss_synthinfo *info;
 
-	snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
+	if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
+		return;
 	info = &dp->synths[dev];
 	if (! info->opened)
 		return;
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 7a1545d..8ca2be3 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -266,7 +266,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(client != NULL, return -EINVAL);
+	if (!client)
+		return 0;
 	snd_seq_delete_all_ports(client);
 	snd_seq_queue_client_leave(client->number);
 	spin_lock_irqsave(&clients_lock, flags);
@@ -403,7 +404,8 @@
 		return -EFAULT;
 
 	/* check client structures are in place */
-	snd_assert(client != NULL, return -ENXIO);
+	if (snd_BUG_ON(!client))
+		return -ENXIO;
 
 	if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
 		return -ENXIO;
@@ -825,7 +827,8 @@
 	struct snd_seq_client *client;
 	int result;
 
-	snd_assert(cell != NULL, return -EINVAL);
+	if (snd_BUG_ON(!cell))
+		return -EINVAL;
 
 	client = snd_seq_client_use_ptr(cell->event.source.client);
 	if (client == NULL) {
@@ -994,7 +997,8 @@
 		return -ENXIO;
 
 	/* check client structures are in place */
-	snd_assert(client != NULL, return -ENXIO);
+	if (snd_BUG_ON(!client))
+		return -ENXIO;
 		
 	if (!client->accept_output || client->pool == NULL)
 		return -ENXIO;
@@ -1076,7 +1080,8 @@
 	unsigned int mask = 0;
 
 	/* check client structures are in place */
-	snd_assert(client != NULL, return -ENXIO);
+	if (snd_BUG_ON(!client))
+		return -ENXIO;
 
 	if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
 	    client->data.user.fifo) {
@@ -2195,7 +2200,8 @@
 {
 	struct snd_seq_client *client = file->private_data;
 
-	snd_assert(client != NULL, return -ENXIO);
+	if (snd_BUG_ON(!client))
+		return -ENXIO;
 		
 	return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
 }
@@ -2216,7 +2222,8 @@
 	struct snd_seq_client *client;
 	va_list args;
 
-	snd_assert(! in_interrupt(), return -EBUSY);
+	if (snd_BUG_ON(in_interrupt()))
+		return -EBUSY;
 
 	if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD)
 		return -EINVAL;
@@ -2265,7 +2272,8 @@
 {
 	struct snd_seq_client *ptr;
 
-	snd_assert(! in_interrupt(), return -EBUSY);
+	if (snd_BUG_ON(in_interrupt()))
+		return -EBUSY;
 
 	ptr = clientptr(client);
 	if (ptr == NULL)
@@ -2288,7 +2296,8 @@
 	struct snd_seq_client *cptr;
 	int result;
 
-	snd_assert(ev != NULL, return -EINVAL);
+	if (snd_BUG_ON(!ev))
+		return -EINVAL;
 
 	if (ev->type == SNDRV_SEQ_EVENT_NONE)
 		return 0; /* ignore this */
@@ -2354,7 +2363,8 @@
 	struct snd_seq_client *cptr;
 	int result;
 
-	snd_assert(ev != NULL, return -EINVAL);
+	if (snd_BUG_ON(!ev))
+		return -EINVAL;
 
 	/* fill in client number */
 	ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 9628c06..38693f4 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -92,7 +92,8 @@
 	struct snd_seq_client *client = file->private_data;
 	void __user *argp = compat_ptr(arg);
 
-	snd_assert(client != NULL, return -ENXIO);
+	if (snd_BUG_ON(!client))
+		return -ENXIO;
 
 	switch (cmd) {
 	case SNDRV_SEQ_IOCTL_PVERSION:
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 05410e5..1f99767 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -187,7 +187,8 @@
 	if (result)
 		*result = NULL;
 
-	snd_assert(id != NULL, return -EINVAL);
+	if (snd_BUG_ON(!id))
+		return -EINVAL;
 
 	ops = find_driver(id, 1);
 	if (ops == NULL)
@@ -232,7 +233,8 @@
 {
 	struct ops_list *ops;
 
-	snd_assert(dev != NULL, return -EINVAL);
+	if (snd_BUG_ON(!dev))
+		return -EINVAL;
 
 	ops = find_driver(dev->id, 0);
 	if (ops == NULL)
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 3a94ed0..0d75afa 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -65,9 +65,11 @@
 {
 	struct snd_seq_fifo *f;
 
-	snd_assert(fifo != NULL, return);
+	if (snd_BUG_ON(!fifo))
+		return;
 	f = *fifo;
-	snd_assert(f != NULL, return);
+	if (snd_BUG_ON(!f))
+		return;
 	*fifo = NULL;
 
 	snd_seq_fifo_clear(f);
@@ -116,7 +118,8 @@
 	unsigned long flags;
 	int err;
 
-	snd_assert(f != NULL, return -EINVAL);
+	if (snd_BUG_ON(!f))
+		return -EINVAL;
 
 	snd_use_lock_use(&f->use_lock);
 	err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
@@ -174,7 +177,8 @@
 	unsigned long flags;
 	wait_queue_t wait;
 
-	snd_assert(f != NULL, return -EINVAL);
+	if (snd_BUG_ON(!f))
+		return -EINVAL;
 
 	*cellp = NULL;
 	init_waitqueue_entry(&wait, current);
@@ -233,7 +237,8 @@
 	struct snd_seq_pool *newpool, *oldpool;
 	struct snd_seq_event_cell *cell, *next, *oldhead;
 
-	snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
+	if (snd_BUG_ON(!f || !f->pool))
+		return -EINVAL;
 
 	/* allocate new pool */
 	newpool = snd_seq_pool_new(poolsize);
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 0cf6ac4..7fb5543 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -187,9 +187,11 @@
 	unsigned long flags;
 	struct snd_seq_pool *pool;
 
-	snd_assert(cell != NULL, return);
+	if (snd_BUG_ON(!cell))
+		return;
 	pool = cell->pool;
-	snd_assert(pool != NULL, return);
+	if (snd_BUG_ON(!pool))
+		return;
 
 	spin_lock_irqsave(&pool->lock, flags);
 	free_cell(pool, cell);
@@ -378,7 +380,8 @@
 	struct snd_seq_event_cell *cellptr;
 	unsigned long flags;
 
-	snd_assert(pool != NULL, return -EINVAL);
+	if (snd_BUG_ON(!pool))
+		return -EINVAL;
 	if (pool->ptr)			/* should be atomic? */
 		return 0;
 
@@ -414,7 +417,8 @@
 	struct snd_seq_event_cell *ptr;
 	int max_count = 5 * HZ;
 
-	snd_assert(pool != NULL, return -EINVAL);
+	if (snd_BUG_ON(!pool))
+		return -EINVAL;
 
 	/* wait for closing all threads */
 	spin_lock_irqsave(&pool->lock, flags);
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 99b3536..4d26146 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -116,7 +116,8 @@
 	struct snd_rawmidi_runtime *runtime;
 	int tmp;
 
-	snd_assert(substream != NULL || buf != NULL, return -EINVAL);
+	if (snd_BUG_ON(!substream || !buf))
+		return -EINVAL;
 	runtime = substream->runtime;
 	if ((tmp = runtime->avail) < count) {
 		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
@@ -135,7 +136,8 @@
 	struct snd_rawmidi_substream *substream;
 	int len;
 
-	snd_assert(msynth != NULL, return -EINVAL);
+	if (snd_BUG_ON(!msynth))
+		return -EINVAL;
 	substream = msynth->output_rfile.output;
 	if (substream == NULL)
 		return -ENODEV;
@@ -210,7 +212,8 @@
 	int err;
 	struct seq_midisynth *msynth = private_data;
 
-	snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
+	if (snd_BUG_ON(!msynth->input_rfile.input))
+		return -EINVAL;
 	err = snd_rawmidi_kernel_release(&msynth->input_rfile);
 	return err;
 }
@@ -247,7 +250,8 @@
 	struct seq_midisynth *msynth = private_data;
 	unsigned char buf = 0xff; /* MIDI reset */
 
-	snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
+	if (snd_BUG_ON(!msynth->output_rfile.output))
+		return -EINVAL;
 	/* sending single MIDI reset message to shut the device up */
 	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
 	snd_rawmidi_drain_output(msynth->output_rfile.output);
@@ -285,7 +289,8 @@
 	int device = dev->device;
 	unsigned int input_count = 0, output_count = 0;
 
-	snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
+	if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
+		return -EINVAL;
 	info = kmalloc(sizeof(*info), GFP_KERNEL);
 	if (! info)
 		return -ENOMEM;
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 1c32a53..3bf7d73 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -130,7 +130,8 @@
 	int num = -1;
 	
 	/* sanity check */
-	snd_assert(client, return NULL);
+	if (snd_BUG_ON(!client))
+		return NULL;
 
 	if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
 		snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
@@ -268,8 +269,8 @@
 	if (port->private_free)
 		port->private_free(port->private_data);
 
-	snd_assert(port->c_src.count == 0,);
-	snd_assert(port->c_dest.count == 0,);
+	snd_BUG_ON(port->c_src.count != 0);
+	snd_BUG_ON(port->c_dest.count != 0);
 
 	kfree(port);
 	return 0;
@@ -336,7 +337,8 @@
 int snd_seq_set_port_info(struct snd_seq_client_port * port,
 			  struct snd_seq_port_info * info)
 {
-	snd_assert(port && info, return -EINVAL);
+	if (snd_BUG_ON(!port || !info))
+		return -EINVAL;
 
 	/* set port name */
 	if (info->name[0])
@@ -365,7 +367,8 @@
 int snd_seq_get_port_info(struct snd_seq_client_port * port,
 			  struct snd_seq_port_info * info)
 {
-	snd_assert(port && info, return -EINVAL);
+	if (snd_BUG_ON(!port || !info))
+		return -EINVAL;
 
 	/* get port name */
 	strlcpy(info->name, port->name, sizeof(info->name));
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 85969db..0101a8b 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -153,8 +153,8 @@
 	int count;
 	int prior;
 
-	snd_assert(f, return -EINVAL);
-	snd_assert(cell, return -EINVAL);
+	if (snd_BUG_ON(!f || !cell))
+		return -EINVAL;
 	
 	/* check flags */
 	prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 4a48c6e..e7a8e9e 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -315,7 +315,8 @@
 	int dest, err;
 	struct snd_seq_queue *q;
 
-	snd_assert(cell != NULL, return -EINVAL);
+	if (snd_BUG_ON(!cell))
+		return -EINVAL;
 	dest = cell->event.queue;	/* destination queue */
 	q = queueptr(dest);
 	if (q == NULL)
@@ -734,7 +735,8 @@
 {
 	struct snd_seq_queue *q;
 
-	snd_assert(ev != NULL, return -EINVAL);
+	if (snd_BUG_ON(!ev))
+		return -EINVAL;
 	q = queueptr(ev->data.queue.queue);
 
 	if (q == NULL)
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index d8fcd62..f745c31 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -173,7 +173,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(tmr, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 	if (tempo <= 0)
 		return -EINVAL;
 	spin_lock_irqsave(&tmr->lock, flags);
@@ -190,7 +191,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(tmr, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 	if (ppq <= 0)
 		return -EINVAL;
 	spin_lock_irqsave(&tmr->lock, flags);
@@ -214,7 +216,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(tmr, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 
 	spin_lock_irqsave(&tmr->lock, flags);
 	tmr->tick.cur_tick = position;
@@ -229,7 +232,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(tmr, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 
 	snd_seq_sanity_real_time(&position);
 	spin_lock_irqsave(&tmr->lock, flags);
@@ -244,7 +248,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(tmr, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 
 	/* FIXME */
 	if (base != SKEW_BASE) {
@@ -265,7 +270,8 @@
 	int err;
 
 	tmr = q->timer;
-	snd_assert(tmr != NULL, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 	if (tmr->timeri)
 		return -EBUSY;
 	sprintf(str, "sequencer queue %i", q->queue);
@@ -302,7 +308,8 @@
 	struct snd_seq_timer *tmr;
 	
 	tmr = q->timer;
-	snd_assert(tmr != NULL, return -EINVAL);
+	if (snd_BUG_ON(!tmr))
+		return -EINVAL;
 	if (tmr->timeri) {
 		snd_timer_stop(tmr->timeri);
 		snd_timer_close(tmr->timeri);
@@ -328,7 +335,8 @@
 	unsigned long freq;
 
 	t = tmr->timeri->timer;
-	snd_assert(t, return -EINVAL);
+	if (snd_BUG_ON(!t))
+		return -EINVAL;
 
 	freq = tmr->preferred_resolution;
 	if (!freq)
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index cefd228..d4564ed 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -41,9 +41,11 @@
 	tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
 	tmpb.dev.dev = sgbuf->dev;
 	for (i = 0; i < sgbuf->pages; i++) {
+		if (!(sgbuf->table[i].addr & ~PAGE_MASK))
+			continue; /* continuous pages */
 		tmpb.area = sgbuf->table[i].buf;
-		tmpb.addr = sgbuf->table[i].addr;
-		tmpb.bytes = PAGE_SIZE;
+		tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
+		tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
 		snd_dma_free_pages(&tmpb);
 	}
 	if (dmab->area)
@@ -58,13 +60,17 @@
 	return 0;
 }
 
+#define MAX_ALLOC_PAGES		32
+
 void *snd_malloc_sgbuf_pages(struct device *device,
 			     size_t size, struct snd_dma_buffer *dmab,
 			     size_t *res_size)
 {
 	struct snd_sg_buf *sgbuf;
-	unsigned int i, pages;
+	unsigned int i, pages, chunk, maxpages;
 	struct snd_dma_buffer tmpb;
+	struct snd_sg_page *table;
+	struct page **pgtable;
 
 	dmab->area = NULL;
 	dmab->addr = 0;
@@ -74,31 +80,55 @@
 	sgbuf->dev = device;
 	pages = snd_sgbuf_aligned_pages(size);
 	sgbuf->tblsize = sgbuf_align_table(pages);
-	sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL);
-	if (! sgbuf->table)
+	table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
+	if (!table)
 		goto _failed;
-	sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL);
-	if (! sgbuf->page_table)
+	sgbuf->table = table;
+	pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
+	if (!pgtable)
 		goto _failed;
+	sgbuf->page_table = pgtable;
 
-	/* allocate each page */
-	for (i = 0; i < pages; i++) {
-		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) {
-			if (res_size == NULL)
+	/* allocate pages */
+	maxpages = MAX_ALLOC_PAGES;
+	while (pages > 0) {
+		chunk = pages;
+		/* don't be too eager to take a huge chunk */
+		if (chunk > maxpages)
+			chunk = maxpages;
+		chunk <<= PAGE_SHIFT;
+		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
+						 chunk, &tmpb) < 0) {
+			if (!sgbuf->pages)
+				return NULL;
+			if (!res_size)
 				goto _failed;
-			*res_size = size = sgbuf->pages * PAGE_SIZE;
+			size = sgbuf->pages * PAGE_SIZE;
 			break;
 		}
-		sgbuf->table[i].buf = tmpb.area;
-		sgbuf->table[i].addr = tmpb.addr;
-		sgbuf->page_table[i] = virt_to_page(tmpb.area);
-		sgbuf->pages++;
+		chunk = tmpb.bytes >> PAGE_SHIFT;
+		for (i = 0; i < chunk; i++) {
+			table->buf = tmpb.area;
+			table->addr = tmpb.addr;
+			if (!i)
+				table->addr |= chunk; /* mark head */
+			table++;
+			*pgtable++ = virt_to_page(tmpb.area);
+			tmpb.area += PAGE_SIZE;
+			tmpb.addr += PAGE_SIZE;
+		}
+		sgbuf->pages += chunk;
+		pages -= chunk;
+		if (chunk < maxpages)
+			maxpages = chunk;
 	}
 
 	sgbuf->size = size;
 	dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
 	if (! dmab->area)
 		goto _failed;
+	if (res_size)
+		*res_size = sgbuf->size;
 	return dmab->area;
 
  _failed:
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 1003ae3..c0685e2 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -34,8 +34,6 @@
 #include <linux/kmod.h>
 #include <linux/mutex.h>
 
-#define SNDRV_OS_MINORS 256
-
 static int major = CONFIG_SND_MAJOR;
 int snd_major;
 EXPORT_SYMBOL(snd_major);
@@ -208,20 +206,23 @@
 		minor = type;
 		break;
 	case SNDRV_DEVICE_TYPE_CONTROL:
-		snd_assert(card != NULL, return -EINVAL);
+		if (snd_BUG_ON(!card))
+			return -EINVAL;
 		minor = SNDRV_MINOR(card->number, type);
 		break;
 	case SNDRV_DEVICE_TYPE_HWDEP:
 	case SNDRV_DEVICE_TYPE_RAWMIDI:
 	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
 	case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
-		snd_assert(card != NULL, return -EINVAL);
+		if (snd_BUG_ON(!card))
+			return -EINVAL;
 		minor = SNDRV_MINOR(card->number, type + dev);
 		break;
 	default:
 		return -EINVAL;
 	}
-	snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
+	if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
+		return -EINVAL;
 	return minor;
 }
 #endif
@@ -249,7 +250,8 @@
 	int minor;
 	struct snd_minor *preg;
 
-	snd_assert(name, return -EINVAL);
+	if (snd_BUG_ON(!name))
+		return -EINVAL;
 	preg = kmalloc(sizeof *preg, GFP_KERNEL);
 	if (preg == NULL)
 		return -ENOMEM;
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 7be5154..7fe1226 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -64,7 +64,8 @@
 
 	switch (type) {
 	case SNDRV_OSS_DEVICE_TYPE_MIXER:
-		snd_assert(card != NULL && dev <= 1, return -EINVAL);
+		if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+			return -EINVAL;
 		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
 		break;
 	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
@@ -74,11 +75,13 @@
 		minor = SNDRV_MINOR_OSS_MUSIC;
 		break;
 	case SNDRV_OSS_DEVICE_TYPE_PCM:
-		snd_assert(card != NULL && dev <= 1, return -EINVAL);
+		if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+			return -EINVAL;
 		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
 		break;
 	case SNDRV_OSS_DEVICE_TYPE_MIDI:
-		snd_assert(card != NULL && dev <= 1, return -EINVAL);
+		if (snd_BUG_ON(!card || dev < 0 || dev > 1))
+			return -EINVAL;
 		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
 		break;
 	case SNDRV_OSS_DEVICE_TYPE_DMFM:
@@ -90,7 +93,8 @@
 	default:
 		return -EINVAL;
 	}
-	snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL);
+	if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OSS_MINORS))
+		return -EINVAL;
 	return minor;
 }
 
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 0af337e..e582fac 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -306,7 +306,8 @@
 	struct snd_timer *timer = NULL;
 	struct snd_timer_instance *slave, *tmp;
 
-	snd_assert(timeri != NULL, return -ENXIO);
+	if (snd_BUG_ON(!timeri))
+		return -ENXIO;
 
 	/* force to stop the timer */
 	snd_timer_stop(timeri);
@@ -385,8 +386,9 @@
 		do_posix_clock_monotonic_gettime(&tstamp);
 	else
 		getnstimeofday(&tstamp);
-	snd_assert(event >= SNDRV_TIMER_EVENT_START &&
-		   event <= SNDRV_TIMER_EVENT_PAUSE, return);
+	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
+		       event > SNDRV_TIMER_EVENT_PAUSE))
+		return;
 	if (event == SNDRV_TIMER_EVENT_START ||
 	    event == SNDRV_TIMER_EVENT_CONTINUE)
 		resolution = snd_timer_resolution(ti);
@@ -474,7 +476,8 @@
 	struct snd_timer *timer;
 	unsigned long flags;
 
-	snd_assert(timeri != NULL, return -ENXIO);
+	if (snd_BUG_ON(!timeri))
+		return -ENXIO;
 
 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
 		if (!keep_flag) {
@@ -758,9 +761,10 @@
 		.dev_disconnect = snd_timer_dev_disconnect,
 	};
 
-	snd_assert(tid != NULL, return -EINVAL);
-	snd_assert(rtimer != NULL, return -EINVAL);
-	*rtimer = NULL;
+	if (snd_BUG_ON(!tid))
+		return -EINVAL;
+	if (rtimer)
+		*rtimer = NULL;
 	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
 	if (timer == NULL) {
 		snd_printk(KERN_ERR "timer: cannot allocate\n");
@@ -788,13 +792,15 @@
 			return err;
 		}
 	}
-	*rtimer = timer;
+	if (rtimer)
+		*rtimer = timer;
 	return 0;
 }
 
 static int snd_timer_free(struct snd_timer *timer)
 {
-	snd_assert(timer != NULL, return -ENXIO);
+	if (!timer)
+		return 0;
 
 	mutex_lock(&register_mutex);
 	if (! list_empty(&timer->open_list_head)) {
@@ -827,8 +833,8 @@
 	struct snd_timer *timer = dev->device_data;
 	struct snd_timer *timer1;
 
-	snd_assert(timer != NULL && timer->hw.start != NULL &&
-		   timer->hw.stop != NULL, return -ENXIO);
+	if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
+		return -ENXIO;
 	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
 	    !timer->hw.resolution && timer->hw.c_resolution == NULL)
 	    	return -EINVAL;
@@ -879,8 +885,9 @@
 
 	if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
 		return;
-	snd_assert(event >= SNDRV_TIMER_EVENT_MSTART &&
-		   event <= SNDRV_TIMER_EVENT_MRESUME, return);
+	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
+		       event > SNDRV_TIMER_EVENT_MRESUME))
+		return;
 	spin_lock_irqsave(&timer->lock, flags);
 	if (event == SNDRV_TIMER_EVENT_MSTART ||
 	    event == SNDRV_TIMER_EVENT_MCONTINUE ||
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index 5512f53..e05802a 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -40,9 +40,11 @@
 	struct snd_timer *t;
 
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (snd_BUG_ON(!tu->timeri))
+		return -ENXIO;
 	t = tu->timeri->timer;
-	snd_assert(t != NULL, return -ENXIO);
+	if (snd_BUG_ON(!t))
+		return -ENXIO;
 	memset(&info, 0, sizeof(info));
 	info.card = t->card ? t->card->number : -1;
 	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
@@ -71,7 +73,8 @@
 	struct snd_timer_status status;
 	
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (snd_BUG_ON(!tu->timeri))
+		return -ENXIO;
 	memset(&status, 0, sizeof(status));
 	status.tstamp = tu->tstamp;
 	status.resolution = snd_timer_resolution(tu->timeri);
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 4e4c69e..e5e749f 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -47,9 +47,11 @@
 static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
 {
 	int err;
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0)
 		return err;
-	if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0)
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
+	if (err) < 0)
 		return err;
 	return 0;
 }
@@ -354,6 +356,7 @@
 	if ((dpcm = new_pcm_stream(substream)) == NULL)
 		return -ENOMEM;
 	runtime->private_data = dpcm;
+	/* makes the infrastructure responsible for freeing dpcm */
 	runtime->private_free = snd_card_dummy_runtime_free;
 	runtime->hw = snd_card_dummy_playback;
 	if (substream->pcm->device & 1) {
@@ -362,10 +365,9 @@
 	}
 	if (substream->pcm->device & 2)
 		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
-	if ((err = add_playback_constraints(runtime)) < 0) {
-		kfree(dpcm);
+	err = add_playback_constraints(runtime);
+	if (err < 0)
 		return err;
-	}
 
 	return 0;
 }
@@ -379,6 +381,7 @@
 	if ((dpcm = new_pcm_stream(substream)) == NULL)
 		return -ENOMEM;
 	runtime->private_data = dpcm;
+	/* makes the infrastructure responsible for freeing dpcm */
 	runtime->private_free = snd_card_dummy_runtime_free;
 	runtime->hw = snd_card_dummy_capture;
 	if (substream->pcm->device == 1) {
@@ -387,10 +390,9 @@
 	}
 	if (substream->pcm->device & 2)
 		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
-	if ((err = add_capture_constraints(runtime)) < 0) {
-		kfree(dpcm);
+	err = add_capture_constraints(runtime);
+	if (err < 0)
 		return err;
-	}
 
 	return 0;
 }
@@ -433,8 +435,9 @@
 	struct snd_pcm *pcm;
 	int err;
 
-	if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device,
-			       substreams, substreams, &pcm)) < 0)
+	err = snd_pcm_new(dummy->card, "Dummy PCM", device,
+			       substreams, substreams, &pcm);
+	if (err < 0)
 		return err;
 	dummy->pcm = pcm;
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
@@ -565,12 +568,14 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(dummy != NULL, return -EINVAL);
+	if (snd_BUG_ON(!dummy))
+		return -EINVAL;
 	spin_lock_init(&dummy->mixer_lock);
 	strcpy(card->mixername, "Dummy Mixer");
 
 	for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
-		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0)
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy));
+		if (err < 0)
 			return err;
 	}
 	return 0;
@@ -594,10 +599,12 @@
 			pcm_substreams[dev] = 1;
 		if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
 			pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
-		if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0)
+		err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]);
+		if (err < 0)
 			goto __nodev;
 	}
-	if ((err = snd_card_dummy_new_mixer(dummy)) < 0)
+	err = snd_card_dummy_new_mixer(dummy);
+	if (err < 0)
 		goto __nodev;
 	strcpy(card->driver, "Dummy");
 	strcpy(card->shortname, "Dummy");
@@ -605,7 +612,8 @@
 
 	snd_card_set_dev(card, &devptr->dev);
 
-	if ((err = snd_card_register(card)) == 0) {
+	err = snd_card_register(card);
+	if (err == 0) {
 		platform_set_drvdata(devptr, card);
 		return 0;
 	}
@@ -668,7 +676,8 @@
 {
 	int i, cards, err;
 
-	if ((err = platform_driver_register(&snd_dummy_driver)) < 0)
+	err = platform_driver_register(&snd_dummy_driver);
+	if (err < 0)
 		return err;
 
 	cards = 0;
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index b5e1a71..5b89c08 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -715,6 +715,10 @@
 
 	card->private_free = snd_mtpav_free;
 
+	err = snd_mtpav_get_RAWMIDI(mtp_card);
+	if (err < 0)
+		goto __error;
+
 	err = snd_mtpav_get_ISA(mtp_card);
 	if (err < 0)
 		goto __error;
@@ -724,10 +728,6 @@
 	snprintf(card->longname, sizeof(card->longname),
 		 "MTPAV on parallel port at 0x%lx", port);
 
-	err = snd_mtpav_get_RAWMIDI(mtp_card);
-	if (err < 0)
-		goto __error;
-
 	snd_mtpav_portscan(mtp_card);
 
 	snd_card_set_dev(card, &dev->dev);
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index ebe4359..7805823 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -139,7 +139,8 @@
 		 * If we had an OPL4 chip, opl3->hardware would have been set
 		 * by the OPL4 driver; so we can assume OPL3 here.
 		 */
-		snd_assert(opl3->r_port != 0, return -ENODEV);
+		if (snd_BUG_ON(!opl3->r_port))
+			return -ENODEV;
 		opl3->hardware = OPL3_HW_OPL3;
 	}
 	return 0;
@@ -324,7 +325,8 @@
 
 static int snd_opl3_free(struct snd_opl3 *opl3)
 {
-	snd_assert(opl3 != NULL, return -ENXIO);
+	if (snd_BUG_ON(!opl3))
+		return -ENXIO;
 	if (opl3->private_free)
 		opl3->private_free(opl3);
 	snd_opl3_clear_patches(opl3);
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index cebcb8b..16feafa 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -617,7 +617,8 @@
 
 	struct snd_opl3_voice *vp, *vp2;
 
-	snd_assert(voice < MAX_OPL3_VOICES, return);
+	if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
+		return;
 
 	vp = &opl3->voices[voice];
 	if (voice < MAX_OPL2_VOICES) {
@@ -737,7 +738,8 @@
 
 	struct snd_opl3_voice *vp;
 
-	snd_assert(voice < MAX_OPL3_VOICES, return);
+	if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
+		return;
 
 	vp = &opl3->voices[voice];
 	if (vp->chan == NULL)
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index 239347f..9a2271d 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -162,7 +162,8 @@
 	struct snd_opl3 *opl3 = closure;
 	int err;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 
 	if ((err = snd_opl3_synth_setup(opl3)) < 0)
 		return err;
@@ -184,7 +185,8 @@
 {
 	struct snd_opl3 *opl3;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	opl3 = arg->private_data;
 
 	snd_opl3_synth_cleanup(opl3);
@@ -206,7 +208,8 @@
 	char name[32];
 	int err, type;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	opl3 = arg->private_data;
 
 	if (format == FM_PATCH)
@@ -246,7 +249,8 @@
 {
 	struct snd_opl3 *opl3;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	opl3 = arg->private_data;
 	switch (cmd) {
 		case SNDCTL_FM_LOAD_INSTR:
@@ -271,7 +275,8 @@
 {
 	struct snd_opl3 *opl3;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	opl3 = arg->private_data;
 
 	return 0;
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index fb64c89..962bb9c 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -92,7 +92,8 @@
 	struct snd_opl3 *opl3 = hw->private_data;
 	void __user *argp = (void __user *)arg;
 
-	snd_assert(opl3 != NULL, return -EINVAL);
+	if (snd_BUG_ON(!opl3))
+		return -EINVAL;
 
 	switch (cmd) {
 		/* get information */
diff --git a/sound/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c
index 74f6e53..49b9e24 100644
--- a/sound/drivers/opl4/opl4_synth.c
+++ b/sound/drivers/opl4/opl4_synth.c
@@ -467,7 +467,7 @@
 	if (!list_empty(&opl4->off_voices))
 		return list_entry(opl4->off_voices.next, struct opl4_voice, list);
 	/* then get the oldest key-on voice */
-	snd_assert(!list_empty(&opl4->on_voices), );
+	snd_BUG_ON(list_empty(&opl4->on_voices));
 	return list_entry(opl4->on_voices.next, struct opl4_voice, list);
 }
 
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c
index 9529e3b..23f4857 100644
--- a/sound/drivers/vx/vx_cmd.c
+++ b/sound/drivers/vx/vx_cmd.c
@@ -99,7 +99,8 @@
  */
 void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd)
 {
-	snd_assert(cmd < CMD_LAST_INDEX, return);
+	if (snd_BUG_ON(cmd >= CMD_LAST_INDEX))
+		return;
 	rmh->LgCmd = vx_dsp_cmds[cmd].length;
 	rmh->LgStat = vx_dsp_cmds[cmd].st_length;
 	rmh->DspStat = vx_dsp_cmds[cmd].st_type;
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 585af2e..473b07f 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -205,7 +205,8 @@
 
 	if (size < 1)
 		return 0;
-	snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL);
+	if (snd_BUG_ON(size > SIZE_MAX_STATUS))
+		return -EINVAL;
 
 	for (i = 1; i <= size; i++) {
 		/* trigger an irq MESS_WRITE_NEXT */
@@ -425,13 +426,16 @@
 	int no_fillup = vx_has_new_dsp(chip);
 
 	/* check the length of boot image */
-	snd_assert(boot->size > 0, return -EINVAL);
-	snd_assert(boot->size % 3 == 0, return -EINVAL);
+	if (boot->size <= 0)
+		return -EINVAL;
+	if (boot->size % 3)
+		return -EINVAL;
 #if 0
 	{
 		/* more strict check */
 		unsigned int c = ((u32)boot->data[0] << 16) | ((u32)boot->data[1] << 8) | boot->data[2];
-		snd_assert(boot->size == (c + 2) * 3, return -EINVAL);
+		if (boot->size != (c + 2) * 3)
+			return -EINVAL;
 	}
 #endif
 
@@ -554,7 +558,8 @@
  */
 static void vx_reset_board(struct vx_core *chip, int cold_reset)
 {
-	snd_assert(chip->ops->reset_board, return);
+	if (snd_BUG_ON(!chip->ops->reset_board))
+		return;
 
 	/* current source, later sync'ed with target */
 	chip->audio_source = VX_AUDIO_SRC_LINE;
@@ -673,7 +678,8 @@
 	unsigned int csum = 0;
 	const unsigned char *image, *cptr;
 
-	snd_assert(dsp->size % 3 == 0, return -EINVAL);
+	if (dsp->size % 3)
+		return -EINVAL;
 
 	vx_toggle_dac_mute(chip, 1);
 
@@ -775,7 +781,8 @@
 {
 	struct vx_core *chip;
 
-	snd_assert(card && hw && ops, return NULL);
+	if (snd_BUG_ON(!card || !hw || !ops))
+		return NULL;
 
 	chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);
 	if (! chip) {
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index efd22e9..8d6362e 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -141,7 +141,8 @@
 	};
 	struct vx_core *vx = hw->private_data;
 
-	snd_assert(type_ids[vx->type], return -EINVAL);
+	if (snd_BUG_ON(!type_ids[vx->type]))
+		return -EINVAL;
 	strcpy(info->id, type_ids[vx->type]);
 	if (vx_is_pcmcia(vx))
 		info->num_dsps = 4;
@@ -168,7 +169,8 @@
 	int index, err;
 	struct firmware *fw;
 
-	snd_assert(vx->ops->load_dsp, return -ENXIO);
+	if (snd_BUG_ON(!vx->ops->load_dsp))
+		return -ENXIO;
 
 	fw = kmalloc(sizeof(*fw), GFP_KERNEL);
 	if (! fw) {
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index 5a34732..c71b8d1 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -34,7 +34,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(chip->ops->write_codec, return);
+	if (snd_BUG_ON(!chip->ops->write_codec))
+		return;
 
 	if (chip->chip_status & VX_STAT_IS_STALE)
 		return;
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index fdbf865..27de574 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -587,7 +587,8 @@
 		return -EBUSY;
 
 	audio = subs->pcm->device * 2;
-	snd_assert(audio < chip->audio_outs, return -EINVAL);
+	if (snd_BUG_ON(audio >= chip->audio_outs))
+		return -EINVAL;
 	
 	/* playback pipe may have been already allocated for monitoring */
 	pipe = chip->playback_pipes[audio];
@@ -996,7 +997,8 @@
 		return -EBUSY;
 
 	audio = subs->pcm->device * 2;
-	snd_assert(audio < chip->audio_ins, return -EINVAL);
+	if (snd_BUG_ON(audio >= chip->audio_ins))
+		return -EINVAL;
 	err = vx_alloc_pipe(chip, 1, audio, 2, &pipe);
 	if (err < 0)
 		return err;
@@ -1214,7 +1216,8 @@
 			}
 			if (capture)
 				continue;
-			snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,);
+			if (snd_BUG_ON(p < 0 || p >= chip->audio_outs))
+				continue;
 			pipe = chip->playback_pipes[p];
 			if (pipe && pipe->substream) {
 				vx_pcm_playback_update(chip, pipe->substream, pipe);
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index fb8932a..0e1ba9b 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -163,13 +163,15 @@
 {
 	int hexfreq;
 
-	snd_assert(freq > 0, return 0);
+	if (snd_BUG_ON(freq <= 0))
+		return 0;
 
 	hexfreq = (28224000 * 10) / freq;
 	hexfreq = (hexfreq + 5) / 10;
 
 	/* max freq = 55125 Hz */
-	snd_assert(hexfreq > 0x00000200, return 0);
+	if (snd_BUG_ON(hexfreq <= 0x00000200))
+		return 0;
 
 	if (hexfreq <= 0x03ff)
 		return hexfreq - 0x00000201;
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 9c3d361..020a5d5 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -314,7 +314,8 @@
 	unsigned long end_time;
 	int data, aes3input = 0;
 
-	snd_assert(cs8427, return);
+	if (snd_BUG_ON(!cs8427))
+		return;
 	chip = cs8427->private_data;
 	snd_i2c_lock(cs8427->bus);
 	if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) ==
@@ -526,7 +527,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(play_substream && cap_substream, return -EINVAL);
+	if (snd_BUG_ON(!play_substream || !cap_substream))
+		return -EINVAL;
 	for (idx = 0; idx < ARRAY_SIZE(snd_cs8427_iec958_controls); idx++) {
 		kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427);
 		if (kctl == NULL)
@@ -543,7 +545,8 @@
 
 	chip->playback.substream = play_substream;
 	chip->capture.substream = cap_substream;
-	snd_assert(chip->playback.pcm_ctl, return -EIO);
+	if (snd_BUG_ON(!chip->playback.pcm_ctl))
+		return -EIO;
 	return 0;
 }
 
@@ -553,7 +556,8 @@
 {
 	struct cs8427 *chip;
 
-	snd_assert(cs8427, return -ENXIO);
+	if (snd_BUG_ON(!cs8427))
+		return -ENXIO;
 	chip = cs8427->private_data;
 	if (active)
 		memcpy(chip->playback.pcm_status,
@@ -573,7 +577,8 @@
 	char *status;
 	int err, reset;
 
-	snd_assert(cs8427, return -ENXIO);
+	if (snd_BUG_ON(!cs8427))
+		return -ENXIO;
 	chip = cs8427->private_data;
 	status = chip->playback.pcm_status;
 	snd_i2c_lock(cs8427->bus);
diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
index b1e74e4..5c0c77d 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -49,7 +49,8 @@
 	struct snd_i2c_bus *slave;
 	struct snd_i2c_device *device;
 
-	snd_assert(bus != NULL, return -EINVAL);
+	if (snd_BUG_ON(!bus))
+		return -EINVAL;
 	while (!list_empty(&bus->devices)) {
 		device = snd_i2c_device(bus->devices.next);
 		snd_i2c_device_free(device);
@@ -113,7 +114,8 @@
 	struct snd_i2c_device *device;
 
 	*rdevice = NULL;
-	snd_assert(bus != NULL, return -EINVAL);
+	if (snd_BUG_ON(!bus))
+		return -EINVAL;
 	device = kzalloc(sizeof(*device), GFP_KERNEL);
 	if (device == NULL)
 		return -ENOMEM;
diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
index 1f4942e..9840eb4 100644
--- a/sound/i2c/l3/uda1341.c
+++ b/sound/i2c/l3/uda1341.c
@@ -771,7 +771,8 @@
 	struct l3_client *clnt;
 	int idx, err;
 
-	snd_assert(card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card))
+		return -EINVAL;
 
 	clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
 	if (clnt == NULL)
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index d20d893..0341451 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -475,7 +475,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(cap_substream, return -EINVAL);
+	if (snd_BUG_ON(!cap_substream))
+		return -EINVAL;
 	ak4114->playback_substream = ply_substream;
 	ak4114->capture_substream = cap_substream;
 	for (idx = 0; idx < AK4114_CONTROLS; idx++) {
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index f350835..2cad2d6 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -431,7 +431,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(cap_substream, return -EINVAL);
+	if (snd_BUG_ON(!cap_substream))
+		return -EINVAL;
 	ak4117->substream = cap_substream;
 	for (idx = 0; idx < AK4117_CONTROLS; idx++) {
 		kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 288926d..ee47aba 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -233,8 +233,8 @@
 		0x01, 0x02, /* 1: reset and soft-mute */
 		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
 			     * disable DZF, sharp roll-off, RSTN#=0 */
-		0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
-		// 0x02, 0x2e, /* quad speed */
+		0x02, 0x4e, /* 2: DA's power up, normal speed, RSTN#=0 */
+		/* 0x02, 0x6e,*/ /* quad speed */
 		0x03, 0x01, /* 3: de-emphasis off */
 		0x04, 0x00, /* 4: LOUT1 volume muted */
 		0x05, 0x00, /* 5: ROUT1 volume muted */
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 5769a13..660beb4 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -1,10 +1,6 @@
 # ALSA ISA drivers
 
-config SND_AD1848_LIB
-        tristate
-        select SND_PCM
-
-config SND_CS4231_LIB
+config SND_WSS_LIB
         tristate
         select SND_PCM
 
@@ -55,7 +51,7 @@
 
 config SND_AD1848
 	tristate "Generic AD1848/CS4248 driver"
-	select SND_AD1848_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for AD1848 (Analog Devices) or
 	  CS4248 (Cirrus Logic - Crystal Semiconductors) chips.
@@ -86,7 +82,7 @@
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for soundcards based on the
 	  Aztech Systems AZT2320 chip.
@@ -96,7 +92,7 @@
 
 config SND_CMI8330
 	tristate "C-Media CMI8330"
-	select SND_AD1848_LIB
+	select SND_WSS_LIB
 	select SND_SB16_DSP
 	help
 	  Say Y here to include support for soundcards based on the
@@ -108,7 +104,7 @@
 config SND_CS4231
 	tristate "Generic Cirrus Logic CS4231 driver"
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for CS4231 chips from Cirrus
 	  Logic - Crystal Semiconductors.
@@ -120,7 +116,7 @@
 	tristate "Generic Cirrus Logic CS4232 driver"
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for CS4232 chips from Cirrus
 	  Logic - Crystal Semiconductors.
@@ -132,7 +128,7 @@
 	tristate "Generic Cirrus Logic CS4236+ driver"
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y to include support for CS4235,CS4236,CS4237B,CS4238B,
 	  CS4239 chips from Cirrus Logic - Crystal Semiconductors.
@@ -192,7 +188,7 @@
 config SND_SC6000
 	tristate "Gallant SC-6000, Audio Excel DSP 16"
 	depends on HAS_IOPORT
-	select SND_AD1848_LIB
+	select SND_WSS_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	help
@@ -228,7 +224,7 @@
 config SND_GUSMAX
 	tristate "Gravis UltraSound MAX"
 	select SND_RAWMIDI
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for Gravis UltraSound MAX
 	  soundcards.
@@ -240,7 +236,7 @@
 	tristate "AMD InterWave, Gravis UltraSound PnP"
 	depends on PNP
 	select SND_RAWMIDI
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards (Gravis UltraSound Plug & Play, STB SoundRage32,
@@ -253,7 +249,7 @@
 	tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)"
 	depends on PNP
 	select SND_RAWMIDI
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards with a TEA6330T bass and treble regulator
@@ -266,7 +262,7 @@
 	tristate "Yamaha OPL3-SA2/SA3"
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for Yamaha OPL3-SA2 and OPL3-SA3
 	  chips.
@@ -279,7 +275,7 @@
 	select SND_OPL3_LIB
 	select SND_OPL4_LIB
 	select SND_MPU401_UART
-	select SND_AD1848_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for soundcards based on Opti
 	  82C92x or OTI-601 chips and using an AD1848 codec.
@@ -292,7 +288,7 @@
 	select SND_OPL3_LIB
 	select SND_OPL4_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for soundcards based on Opti
 	  82C92x chips and using a CS4231 codec.
@@ -304,7 +300,7 @@
 	tristate "OPTi 82C93x"
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for soundcards based on Opti
 	  82C93x chips.
@@ -315,7 +311,7 @@
 config SND_MIRO
 	tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
 	select SND_OPL4_LIB
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	select SND_MPU401_UART
 	select SND_PCM
 	help
@@ -364,7 +360,7 @@
 config SND_SB16_CSP
 	bool "Sound Blaster 16/AWE CSP support"
 	depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC)
-	select FW_LOADER if !SND_SB16_CSP_FIRMWARE_IN_KERNEL
+	select FW_LOADER
 	help
 	  Say Y here to include support for the CSP core.  This special
 	  coprocessor can do variable tasks like various compression and
@@ -372,7 +368,7 @@
 
 config SND_SGALAXY
 	tristate "Aztech Sound Galaxy"
-	select SND_AD1848_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for Aztech Sound Galaxy
 	  soundcards.
@@ -384,7 +380,7 @@
 	tristate "Ensoniq SoundScape PnP driver"
 	select SND_HWDEP
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for Ensoniq SoundScape PnP
 	  soundcards.
@@ -397,7 +393,7 @@
 	select FW_LOADER
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_CS4231_LIB
+	select SND_WSS_LIB
 	help
 	  Say Y here to include support for Turtle Beach Maui, Tropez
 	  and Tropez+ soundcards based on the Wavefront chip.
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index c0ce7db..63af13d 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -27,4 +27,4 @@
 obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
 
 obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \
-		     sb/ wavefront/
+		     sb/ wavefront/ wss/
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 68f1260..7752424 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -83,8 +83,10 @@
 	{ .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
 	/* Shark Predator ISA - added by Ken Arromdee */
 	{ .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
-	/* Analog Devices AD1816A - Terratec AudioSystem EWS64S */
+	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
 	{ .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
+	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */
+	{ .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } },
 	/* Analog Devices AD1816A - Terratec Base 64 */
 	{ .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
 	/* end */
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index 4b8dfe2..3bfca7c 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -394,7 +394,8 @@
 
 static unsigned long snd_ad1816a_timer_resolution(struct snd_timer *timer)
 {
-	snd_assert(timer != NULL, return 0);
+	if (snd_BUG_ON(!timer))
+		return 0;
 
 	return 10000;
 }
@@ -961,7 +962,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip || !chip->card))
+		return -EINVAL;
 
 	card = chip->card;
 
diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile
index ae23331..3d6dea3 100644
--- a/sound/isa/ad1848/Makefile
+++ b/sound/isa/ad1848/Makefile
@@ -3,10 +3,8 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
 #
 
-snd-ad1848-lib-objs := ad1848_lib.o
 snd-ad1848-objs := ad1848.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AD1848) += snd-ad1848.o
-obj-$(CONFIG_SND_AD1848_LIB) += snd-ad1848-lib.o
 
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index 5f5271e..b68d20e 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -28,7 +28,7 @@
 #include <linux/wait.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
 #include <sound/initval.h>
 
 #define CRD_NAME "Generic AD1848/AD1847/CS4248"
@@ -87,7 +87,7 @@
 static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
 {
 	struct snd_card *card;
-	struct snd_ad1848 *chip;
+	struct snd_wss *chip;
 	struct snd_pcm *pcm;
 	int error;
 
@@ -95,18 +95,19 @@
 	if (!card)
 		return -EINVAL;
 
-	error = snd_ad1848_create(card, port[n], irq[n], dma1[n],
-			thinkpad[n] ? AD1848_HW_THINKPAD : AD1848_HW_DETECT, &chip);
+	error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], -1,
+			thinkpad[n] ? WSS_HW_THINKPAD : WSS_HW_DETECT,
+			0, &chip);
 	if (error < 0)
 		goto out;
 
 	card->private_data = chip;
 
-	error = snd_ad1848_pcm(chip, 0, &pcm);
+	error = snd_wss_pcm(chip, 0, &pcm);
 	if (error < 0)
 		goto out;
 
-	error = snd_ad1848_mixer(chip);
+	error = snd_wss_mixer(chip);
 	if (error < 0)
 		goto out;
 
@@ -142,7 +143,7 @@
 static int snd_ad1848_suspend(struct device *dev, unsigned int n, pm_message_t state)
 {
 	struct snd_card *card = dev_get_drvdata(dev);
-	struct snd_ad1848 *chip = card->private_data;
+	struct snd_wss *chip = card->private_data;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	chip->suspend(chip);
@@ -152,7 +153,7 @@
 static int snd_ad1848_resume(struct device *dev, unsigned int n)
 {
 	struct snd_card *card = dev_get_drvdata(dev);
-	struct snd_ad1848 *chip = card->private_data;
+	struct snd_wss *chip = card->private_data;
 
 	chip->resume(chip);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
deleted file mode 100644
index 630c90f..0000000
--- a/sound/isa/ad1848/ad1848_lib.c
+++ /dev/null
@@ -1,1267 +0,0 @@
-/*
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *  Routines for control of AD1848/AD1847/CS4248
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#define SNDRV_MAIN_OBJECT_FILE
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <sound/core.h>
-#include <sound/ad1848.h>
-#include <sound/control.h>
-#include <sound/tlv.h>
-#include <sound/pcm_params.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248");
-MODULE_LICENSE("GPL");
-
-#if 0
-#define SNDRV_DEBUG_MCE
-#endif
-
-/*
- *  Some variables
- */
-
-static unsigned char freq_bits[14] = {
-	/* 5510 */	0x00 | AD1848_XTAL2,
-	/* 6620 */	0x0E | AD1848_XTAL2,
-	/* 8000 */	0x00 | AD1848_XTAL1,
-	/* 9600 */	0x0E | AD1848_XTAL1,
-	/* 11025 */	0x02 | AD1848_XTAL2,
-	/* 16000 */	0x02 | AD1848_XTAL1,
-	/* 18900 */	0x04 | AD1848_XTAL2,
-	/* 22050 */	0x06 | AD1848_XTAL2,
-	/* 27042 */	0x04 | AD1848_XTAL1,
-	/* 32000 */	0x06 | AD1848_XTAL1,
-	/* 33075 */	0x0C | AD1848_XTAL2,
-	/* 37800 */	0x08 | AD1848_XTAL2,
-	/* 44100 */	0x0A | AD1848_XTAL2,
-	/* 48000 */	0x0C | AD1848_XTAL1
-};
-
-static unsigned int rates[14] = {
-	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
-	27042, 32000, 33075, 37800, 44100, 48000
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
-	.count = ARRAY_SIZE(rates),
-	.list = rates,
-	.mask = 0,
-};
-
-static unsigned char snd_ad1848_original_image[16] =
-{
-	0x00,			/* 00 - lic */
-	0x00,			/* 01 - ric */
-	0x9f,			/* 02 - la1ic */
-	0x9f,			/* 03 - ra1ic */
-	0x9f,			/* 04 - la2ic */
-	0x9f,			/* 05 - ra2ic */
-	0xbf,			/* 06 - loc */
-	0xbf,			/* 07 - roc */
-	0x20,			/* 08 - dfr */
-	AD1848_AUTOCALIB,	/* 09 - ic */
-	0x00,			/* 0a - pc */
-	0x00,			/* 0b - ti */
-	0x00,			/* 0c - mi */
-	0x00,			/* 0d - lbc */
-	0x00,			/* 0e - dru */
-	0x00,			/* 0f - drl */
-};
-
-/*
- *  Basic I/O functions
- */
-
-static void snd_ad1848_wait(struct snd_ad1848 *chip)
-{
-	int timeout;
-
-	for (timeout = 250; timeout > 0; timeout--) {
-		if ((inb(AD1848P(chip, REGSEL)) & AD1848_INIT) == 0)
-			break;
-		udelay(100);
-	}
-}
-
-void snd_ad1848_out(struct snd_ad1848 *chip,
-			   unsigned char reg,
-			   unsigned char value)
-{
-	snd_ad1848_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
-		snd_printk(KERN_WARNING "auto calibration time out - "
-			   "reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
-	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
-	outb(chip->image[reg] = value, AD1848P(chip, REG));
-	mb();
-	snd_printdd("codec out - reg 0x%x = 0x%x\n",
-			chip->mce_bit | reg, value);
-}
-
-EXPORT_SYMBOL(snd_ad1848_out);
-
-static void snd_ad1848_dout(struct snd_ad1848 *chip,
-			    unsigned char reg, unsigned char value)
-{
-	snd_ad1848_wait(chip);
-	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
-	outb(value, AD1848P(chip, REG));
-	mb();
-}
-
-static unsigned char snd_ad1848_in(struct snd_ad1848 *chip, unsigned char reg)
-{
-	snd_ad1848_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
-		snd_printk(KERN_WARNING "auto calibration time out - "
-			   "reg = 0x%x\n", reg);
-#endif
-	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
-	mb();
-	return inb(AD1848P(chip, REG));
-}
-
-#if 0
-
-static void snd_ad1848_debug(struct snd_ad1848 *chip)
-{
-	printk("AD1848 REGS:      INDEX = 0x%02x  ", inb(AD1848P(chip, REGSEL)));
-	printk("                 STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS)));
-	printk("  0x00: left input      = 0x%02x  ", snd_ad1848_in(chip, 0x00));
-	printk("  0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));
-	printk("  0x01: right input     = 0x%02x  ", snd_ad1848_in(chip, 0x01));
-	printk("  0x09: iface (CFIG 1)  = 0x%02x\n", snd_ad1848_in(chip, 0x09));
-	printk("  0x02: AUXA left       = 0x%02x  ", snd_ad1848_in(chip, 0x02));
-	printk("  0x0a: pin control     = 0x%02x\n", snd_ad1848_in(chip, 0x0a));
-	printk("  0x03: AUXA right      = 0x%02x  ", snd_ad1848_in(chip, 0x03));
-	printk("  0x0b: init & status   = 0x%02x\n", snd_ad1848_in(chip, 0x0b));
-	printk("  0x04: AUXB left       = 0x%02x  ", snd_ad1848_in(chip, 0x04));
-	printk("  0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));
-	printk("  0x05: AUXB right      = 0x%02x  ", snd_ad1848_in(chip, 0x05));
-	printk("  0x0d: loopback        = 0x%02x\n", snd_ad1848_in(chip, 0x0d));
-	printk("  0x06: left output     = 0x%02x  ", snd_ad1848_in(chip, 0x06));
-	printk("  0x0e: data upr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0e));
-	printk("  0x07: right output    = 0x%02x  ", snd_ad1848_in(chip, 0x07));
-	printk("  0x0f: data lwr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0f));
-}
-
-#endif
-
-/*
- *  AD1848 detection / MCE routines
- */
-
-static void snd_ad1848_mce_up(struct snd_ad1848 *chip)
-{
-	unsigned long flags;
-	int timeout;
-
-	snd_ad1848_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
-		snd_printk(KERN_WARNING "mce_up - auto calibration time out (0)\n");
-#endif
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->mce_bit |= AD1848_MCE;
-	timeout = inb(AD1848P(chip, REGSEL));
-	if (timeout == 0x80)
-		snd_printk(KERN_WARNING "mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
-	if (!(timeout & AD1848_MCE))
-		outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
-{
-	unsigned long flags, timeout;
-	int reg;
-
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	for (timeout = 5; timeout > 0; timeout--)
-		inb(AD1848P(chip, REGSEL));
-	/* end of cleanup sequence */
-	for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
-		udelay(100);
-
-	snd_printdd("(1) timeout = %ld\n", timeout);
-
-#ifdef CONFIG_SND_DEBUG
-	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
-		snd_printk(KERN_WARNING "mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL));
-#endif
-
-	chip->mce_bit &= ~AD1848_MCE;
-	reg = inb(AD1848P(chip, REGSEL));
-	outb(chip->mce_bit | (reg & 0x1f), AD1848P(chip, REGSEL));
-	if (reg == 0x80)
-		snd_printk(KERN_WARNING "mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
-	if ((reg & AD1848_MCE) == 0) {
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		return;
-	}
-
-	/*
-	 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.
-	 * It may take up to 5 sample periods (at most 907 us @ 5.5125 kHz) for
-	 * the process to _start_, so it is important to wait at least that long
-	 * before checking.  Otherwise we might think AC has finished when it
-	 * has in fact not begun.  It could take 128 (no AC) or 384 (AC) cycles
-	 * for ACI to drop.  This gives a wait of at most 70 ms with a more
-	 * typical value of 3-9 ms.
-	 */
-	timeout = jiffies + msecs_to_jiffies(250);
-	do {
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		msleep(1);
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		reg = snd_ad1848_in(chip, AD1848_TEST_INIT) &
-		      AD1848_CALIB_IN_PROGRESS;
-	} while (reg && time_before(jiffies, timeout));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (reg)
-		snd_printk(KERN_ERR
-			   "mce_down - auto calibration time out (2)\n");
-
-	snd_printdd("(4) jiffies = %lu\n", jiffies);
-	snd_printd("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL)));
-}
-
-static unsigned int snd_ad1848_get_count(unsigned char format,
-				         unsigned int size)
-{
-	switch (format & 0xe0) {
-	case AD1848_LINEAR_16:
-		size >>= 1;
-		break;
-	}
-	if (format & AD1848_STEREO)
-		size >>= 1;
-	return size;
-}
-
-static int snd_ad1848_trigger(struct snd_ad1848 *chip, unsigned char what,
-			      int channel, int cmd)
-{
-	int result = 0;
-
-#if 0
-	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS)));
-#endif
-	spin_lock(&chip->reg_lock);
-	if (cmd == SNDRV_PCM_TRIGGER_START) {
-		if (chip->image[AD1848_IFACE_CTRL] & what) {
-			spin_unlock(&chip->reg_lock);
-			return 0;
-		}
-		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what);
-		chip->mode |= AD1848_MODE_RUNNING;
-	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
-		if (!(chip->image[AD1848_IFACE_CTRL] & what)) {
-			spin_unlock(&chip->reg_lock);
-			return 0;
-		}
-		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what);
-		chip->mode &= ~AD1848_MODE_RUNNING;
-	} else {
-		result = -EINVAL;
-	}
-	spin_unlock(&chip->reg_lock);
-	return result;
-}
-
-/*
- *  CODEC I/O
- */
-
-static unsigned char snd_ad1848_get_rate(unsigned int rate)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(rates); i++)
-		if (rate == rates[i])
-			return freq_bits[i];
-	snd_BUG();
-	return freq_bits[ARRAY_SIZE(rates) - 1];
-}
-
-static int snd_ad1848_ioctl(struct snd_pcm_substream *substream,
-			    unsigned int cmd, void *arg)
-{
-	return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
-static unsigned char snd_ad1848_get_format(int format, int channels)
-{
-	unsigned char rformat;
-
-	rformat = AD1848_LINEAR_8;
-	switch (format) {
-	case SNDRV_PCM_FORMAT_A_LAW:	rformat = AD1848_ALAW_8; break;
-	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = AD1848_ULAW_8; break;
-	case SNDRV_PCM_FORMAT_S16_LE:	rformat = AD1848_LINEAR_16; break;
-	}
-	if (channels > 1)
-		rformat |= AD1848_STEREO;
-#if 0
-	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
-#endif
-	return rformat;
-}
-
-static void snd_ad1848_calibrate_mute(struct snd_ad1848 *chip, int mute)
-{
-	unsigned long flags;
-	
-	mute = mute ? 1 : 0;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	if (chip->calibrate_mute == mute) {
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		return;
-	}
-	if (!mute) {
-		snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]);
-		snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]);
-	}
-	snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]);
-	snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]);
-	snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]);
-	snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]);
-	snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]);
-	snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]);
-	chip->calibrate_mute = mute;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static void snd_ad1848_set_data_format(struct snd_ad1848 *chip, struct snd_pcm_hw_params *hw_params)
-{
-	if (hw_params == NULL) {
-		chip->image[AD1848_DATA_FORMAT] = 0x20;
-	} else {
-		chip->image[AD1848_DATA_FORMAT] =
-		    snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) |
-		    snd_ad1848_get_rate(params_rate(hw_params));
-	}
-	// snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]);
-}
-
-static int snd_ad1848_open(struct snd_ad1848 *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	if (chip->mode & AD1848_MODE_OPEN)
-		return -EAGAIN;
-
-	snd_ad1848_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("open: (1)\n");
-#endif
-	snd_ad1848_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
-			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO |
-			     AD1848_CALIB_MODE);
-	chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;
-	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_ad1848_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("open: (2)\n");
-#endif
-
-	snd_ad1848_set_data_format(chip, NULL);
-
-	snd_ad1848_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_ad1848_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("open: (3)\n");
-#endif
-
-	/* ok. now enable and ack CODEC IRQ */
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
-	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
-	chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE;
-	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	chip->mode = mode;
-
-	return 0;
-}
-
-static void snd_ad1848_close(struct snd_ad1848 *chip)
-{
-	unsigned long flags;
-
-	if (!chip->mode)
-		return;
-	/* disable IRQ */
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
-	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
-	chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE;
-	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	/* now disable capture & playback */
-
-	snd_ad1848_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
-			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
-	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_ad1848_mce_down(chip);
-
-	/* clear IRQ again */
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
-	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	chip->mode = 0;
-}
-
-/*
- *  ok.. exported functions..
- */
-
-static int snd_ad1848_playback_trigger(struct snd_pcm_substream *substream,
-				       int cmd)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd);
-}
-
-static int snd_ad1848_capture_trigger(struct snd_pcm_substream *substream,
-				      int cmd)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd);
-}
-
-static int snd_ad1848_playback_hw_params(struct snd_pcm_substream *substream,
-					 struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	unsigned long flags;
-	int err;
-
-	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
-		return err;
-	snd_ad1848_calibrate_mute(chip, 1);
-	snd_ad1848_set_data_format(chip, hw_params);
-	snd_ad1848_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_ad1848_mce_down(chip);
-	snd_ad1848_calibrate_mute(chip, 0);
-	return 0;
-}
-
-static int snd_ad1848_playback_hw_free(struct snd_pcm_substream *substream)
-{
-	return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_ad1848_playback_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-
-	chip->dma_size = size;
-	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO);
-	snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
-	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
-	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ad1848_capture_hw_params(struct snd_pcm_substream *substream,
-					struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	unsigned long flags;
-	int err;
-
-	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
-		return err;
-	snd_ad1848_calibrate_mute(chip, 1);
-	snd_ad1848_set_data_format(chip, hw_params);
-	snd_ad1848_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_ad1848_mce_down(chip);
-	snd_ad1848_calibrate_mute(chip, 0);
-	return 0;
-}
-
-static int snd_ad1848_capture_hw_free(struct snd_pcm_substream *substream)
-{
-	return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_ad1848_capture_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-
-	chip->dma_size = size;
-	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
-	snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
-	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
-	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id)
-{
-	struct snd_ad1848 *chip = dev_id;
-
-	if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream &&
-	    (chip->mode & AD1848_MODE_RUNNING))
-		snd_pcm_period_elapsed(chip->playback_substream);
-	if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream &&
-	    (chip->mode & AD1848_MODE_RUNNING))
-		snd_pcm_period_elapsed(chip->capture_substream);
-	outb(0, AD1848P(chip, STATUS));	/* clear global interrupt bit */
-	return IRQ_HANDLED;
-}
-
-static snd_pcm_uframes_t snd_ad1848_playback_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-	
-	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE))
-		return 0;
-	ptr = snd_dma_pointer(chip->dma, chip->dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_ad1848_capture_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE))
-		return 0;
-	ptr = snd_dma_pointer(chip->dma, chip->dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-/*
-
- */
-
-static void snd_ad1848_thinkpad_twiddle(struct snd_ad1848 *chip, int on) {
-
-	int tmp;
-
-	if (!chip->thinkpad_flag) return;
-
-	outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
-	tmp = inb(AD1848_THINKPAD_CTL_PORT2);
-
-	if (on)
-		/* turn it on */
-		tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
-	else
-		/* turn it off */
-		tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
-	
-	outb(tmp, AD1848_THINKPAD_CTL_PORT2);
-
-}
-
-#ifdef CONFIG_PM
-static void snd_ad1848_suspend(struct snd_ad1848 *chip)
-{
-	snd_pcm_suspend_all(chip->pcm);
-	if (chip->thinkpad_flag)
-		snd_ad1848_thinkpad_twiddle(chip, 0);
-}
-
-static void snd_ad1848_resume(struct snd_ad1848 *chip)
-{
-	int i;
-
-	if (chip->thinkpad_flag)
-		snd_ad1848_thinkpad_twiddle(chip, 1);
-
-	/* clear any pendings IRQ */
-	inb(AD1848P(chip, STATUS));
-	outb(0, AD1848P(chip, STATUS));
-	mb();
-
-	snd_ad1848_mce_down(chip);
-	for (i = 0; i < 16; i++)
-		snd_ad1848_out(chip, i, chip->image[i]);
-	snd_ad1848_mce_up(chip);
-	snd_ad1848_mce_down(chip);
-}
-#endif /* CONFIG_PM */
-
-static int snd_ad1848_probe(struct snd_ad1848 * chip)
-{
-	unsigned long flags;
-	int i, id, rev, ad1847;
-	unsigned char *ptr;
-
-#if 0
-	snd_ad1848_debug(chip);
-#endif
-	id = ad1847 = 0;
-	for (i = 0; i < 1000; i++) {
-		mb();
-		if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
-			udelay(500);
-		else {
-			spin_lock_irqsave(&chip->reg_lock, flags);
-			snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
-			snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa);
-			snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45);
-			rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT);
-			if (rev == 0x65) {
-				spin_unlock_irqrestore(&chip->reg_lock, flags);
-				id = 1;
-				ad1847 = 1;
-				break;
-			}
-			if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) {
-				spin_unlock_irqrestore(&chip->reg_lock, flags);
-				id = 1;
-				break;
-			}
-			spin_unlock_irqrestore(&chip->reg_lock, flags);
-		}
-	}
-	if (id != 1)
-		return -ENODEV;	/* no valid device found */
-	if (chip->hardware == AD1848_HW_DETECT) {
-		if (ad1847) {
-			chip->hardware = AD1848_HW_AD1847;
-		} else {
-			chip->hardware = AD1848_HW_AD1848;
-			rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
-			if (rev & 0x80) {
-				chip->hardware = AD1848_HW_CS4248;
-			} else if ((rev & 0x0f) == 0x0a) {
-				snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40);
-				for (i = 0; i < 16; ++i) {
-					if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) {
-						chip->hardware = AD1848_HW_CMI8330;
-						break;
-					}
-				}
-				snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
-			}
-		}
-	}
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	inb(AD1848P(chip, STATUS));	/* clear any pendings IRQ */
-	outb(0, AD1848P(chip, STATUS));
-	mb();
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	chip->image[AD1848_MISC_INFO] = 0x00;
-	chip->image[AD1848_IFACE_CTRL] =
-	    (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA;
-	ptr = (unsigned char *) &chip->image;
-	snd_ad1848_mce_down(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	for (i = 0; i < 16; i++)	/* ok.. fill all AD1848 registers */
-		snd_ad1848_out(chip, i, *ptr++);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_ad1848_mce_up(chip);
-	snd_ad1848_mce_down(chip);
-	return 0;		/* all things are ok.. */
-}
-
-/*
-
- */
-
-static struct snd_pcm_hardware snd_ad1848_playback =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
-				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
-	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	.rate_min =		5510,
-	.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 struct snd_pcm_hardware snd_ad1848_capture =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
-				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
-	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	.rate_min =		5510,
-	.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 int snd_ad1848_playback_open(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int err;
-
-	if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0)
-		return err;
-	chip->playback_substream = substream;
-	runtime->hw = snd_ad1848_playback;
-	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
-	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return 0;
-}
-
-static int snd_ad1848_capture_open(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int err;
-
-	if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0)
-		return err;
-	chip->capture_substream = substream;
-	runtime->hw = snd_ad1848_capture;
-	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
-	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return 0;
-}
-
-static int snd_ad1848_playback_close(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-
-	chip->mode &= ~AD1848_MODE_PLAY;
-	chip->playback_substream = NULL;
-	snd_ad1848_close(chip);
-	return 0;
-}
-
-static int snd_ad1848_capture_close(struct snd_pcm_substream *substream)
-{
-	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
-
-	chip->mode &= ~AD1848_MODE_CAPTURE;
-	chip->capture_substream = NULL;
-	snd_ad1848_close(chip);
-	return 0;
-}
-
-static int snd_ad1848_free(struct snd_ad1848 *chip)
-{
-	release_and_free_resource(chip->res_port);
-	if (chip->irq >= 0)
-		free_irq(chip->irq, (void *) chip);
-	if (chip->dma >= 0) {
-		snd_dma_disable(chip->dma);
-		free_dma(chip->dma);
-	}
-	kfree(chip);
-	return 0;
-}
-
-static int snd_ad1848_dev_free(struct snd_device *device)
-{
-	struct snd_ad1848 *chip = device->device_data;
-	return snd_ad1848_free(chip);
-}
-
-static const char *snd_ad1848_chip_id(struct snd_ad1848 *chip)
-{
-	switch (chip->hardware) {
-	case AD1848_HW_AD1847:	return "AD1847";
-	case AD1848_HW_AD1848:	return "AD1848";
-	case AD1848_HW_CS4248:	return "CS4248";
-	case AD1848_HW_CMI8330: return "CMI8330/C3D";
-	default:		return "???";
-	}
-}
-
-int snd_ad1848_create(struct snd_card *card,
-		      unsigned long port,
-		      int irq, int dma,
-		      unsigned short hardware,
-		      struct snd_ad1848 ** rchip)
-{
-	static struct snd_device_ops ops = {
-		.dev_free =	snd_ad1848_dev_free,
-	};
-	struct snd_ad1848 *chip;
-	int err;
-
-	*rchip = NULL;
-	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-	if (chip == NULL)
-		return -ENOMEM;
-	spin_lock_init(&chip->reg_lock);
-	chip->card = card;
-	chip->port = port;
-	chip->irq = -1;
-	chip->dma = -1;
-	chip->hardware = hardware;
-	memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image));
-	
-	if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) {
-		snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port);
-		snd_ad1848_free(chip);
-		return -EBUSY;
-	}
-	if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) {
-		snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq);
-		snd_ad1848_free(chip);
-		return -EBUSY;
-	}
-	chip->irq = irq;
-	if (request_dma(dma, "AD1848")) {
-		snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma);
-		snd_ad1848_free(chip);
-		return -EBUSY;
-	}
-	chip->dma = dma;
-
-	if (hardware == AD1848_HW_THINKPAD) {
-		chip->thinkpad_flag = 1;
-		chip->hardware = AD1848_HW_DETECT; /* reset */
-		snd_ad1848_thinkpad_twiddle(chip, 1);
-	}
-
-	if (snd_ad1848_probe(chip) < 0) {
-		snd_ad1848_free(chip);
-		return -ENODEV;
-	}
-
-	/* Register device */
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_ad1848_free(chip);
-		return err;
-	}
-
-#ifdef CONFIG_PM
-	chip->suspend = snd_ad1848_suspend;
-	chip->resume = snd_ad1848_resume;
-#endif
-
-	*rchip = chip;
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_create);
-
-static struct snd_pcm_ops snd_ad1848_playback_ops = {
-	.open =		snd_ad1848_playback_open,
-	.close =	snd_ad1848_playback_close,
-	.ioctl =	snd_ad1848_ioctl,
-	.hw_params =	snd_ad1848_playback_hw_params,
-	.hw_free =	snd_ad1848_playback_hw_free,
-	.prepare =	snd_ad1848_playback_prepare,
-	.trigger =	snd_ad1848_playback_trigger,
-	.pointer =	snd_ad1848_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_ad1848_capture_ops = {
-	.open =		snd_ad1848_capture_open,
-	.close =	snd_ad1848_capture_close,
-	.ioctl =	snd_ad1848_ioctl,
-	.hw_params =	snd_ad1848_capture_hw_params,
-	.hw_free =	snd_ad1848_capture_hw_free,
-	.prepare =	snd_ad1848_capture_prepare,
-	.trigger =	snd_ad1848_capture_trigger,
-	.pointer =	snd_ad1848_capture_pointer,
-};
-
-int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm)
-{
-	struct snd_pcm *pcm;
-	int err;
-
-	if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops);
-
-	pcm->private_data = chip;
-	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
-	strcpy(pcm->name, snd_ad1848_chip_id(chip));
-
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-					      snd_dma_isa_data(),
-					      64*1024, chip->dma > 3 ? 128*1024 : 64*1024);
-
-	chip->pcm = pcm;
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_pcm);
-
-const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction)
-{
-	return direction == SNDRV_PCM_STREAM_PLAYBACK ?
-		&snd_ad1848_playback_ops : &snd_ad1848_capture_ops;
-}
-
-EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
-
-/*
- *  MIXER part
- */
-
-static int snd_ad1848_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	static char *texts[4] = {
-		"Line", "Aux", "Mic", "Mix"
-	};
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 2;
-	uinfo->value.enumerated.items = 4;
-	if (uinfo->value.enumerated.item > 3)
-		uinfo->value.enumerated.item = 3;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
-}
-
-static int snd_ad1848_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6;
-	ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ad1848_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	unsigned short left, right;
-	int change;
-	
-	if (ucontrol->value.enumerated.item[0] > 3 ||
-	    ucontrol->value.enumerated.item[1] > 3)
-		return -EINVAL;
-	left = ucontrol->value.enumerated.item[0] << 6;
-	right = ucontrol->value.enumerated.item[1] << 6;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left;
-	right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right;
-	change = left != chip->image[AD1848_LEFT_INPUT] ||
-	         right != chip->image[AD1848_RIGHT_INPUT];
-	snd_ad1848_out(chip, AD1848_LEFT_INPUT, left);
-	snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return change;
-}
-
-static int snd_ad1848_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-static int snd_ad1848_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0xff;
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0xff;
-	
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (invert)
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-	return 0;
-}
-
-static int snd_ad1848_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0xff;
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0xff;
-	int change;
-	unsigned short val;
-	
-	val = (ucontrol->value.integer.value[0] & mask);
-	if (invert)
-		val = mask - val;
-	val <<= shift;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	val = (chip->image[reg] & ~(mask << shift)) | val;
-	change = val != chip->image[reg];
-	snd_ad1848_out(chip, reg, val);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return change;
-}
-
-static int snd_ad1848_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-static int snd_ad1848_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int left_reg = kcontrol->private_value & 0xff;
-	int right_reg = (kcontrol->private_value >> 8) & 0xff;
-	int shift_left = (kcontrol->private_value >> 16) & 0x07;
-	int shift_right = (kcontrol->private_value >> 19) & 0x07;
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-	int invert = (kcontrol->private_value >> 22) & 1;
-	
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
-	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (invert) {
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
-	}
-	return 0;
-}
-
-static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int left_reg = kcontrol->private_value & 0xff;
-	int right_reg = (kcontrol->private_value >> 8) & 0xff;
-	int shift_left = (kcontrol->private_value >> 16) & 0x07;
-	int shift_right = (kcontrol->private_value >> 19) & 0x07;
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-	int invert = (kcontrol->private_value >> 22) & 1;
-	int change;
-	unsigned short val1, val2;
-	
-	val1 = ucontrol->value.integer.value[0] & mask;
-	val2 = ucontrol->value.integer.value[1] & mask;
-	if (invert) {
-		val1 = mask - val1;
-		val2 = mask - val2;
-	}
-	val1 <<= shift_left;
-	val2 <<= shift_right;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	if (left_reg != right_reg) {
-		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
-		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
-		change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
-		snd_ad1848_out(chip, left_reg, val1);
-		snd_ad1848_out(chip, right_reg, val2);
-	} else {
-		val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
-		change = val1 != chip->image[left_reg];
-		snd_ad1848_out(chip, left_reg, val1);		
-	}
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return change;
-}
-
-/*
- */
-int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
-			    const struct ad1848_mix_elem *c)
-{
-	static struct snd_kcontrol_new newctls[] = {
-		[AD1848_MIX_SINGLE] = {
-			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-			.info = snd_ad1848_info_single,
-			.get = snd_ad1848_get_single,
-			.put = snd_ad1848_put_single,
-		},
-		[AD1848_MIX_DOUBLE] = {
-			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-			.info = snd_ad1848_info_double,
-			.get = snd_ad1848_get_double,
-			.put = snd_ad1848_put_double,
-		},
-		[AD1848_MIX_CAPTURE] = {
-			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-			.info = snd_ad1848_info_mux,
-			.get = snd_ad1848_get_mux,
-			.put = snd_ad1848_put_mux,
-		},
-	};
-	struct snd_kcontrol *ctl;
-	int err;
-
-	ctl = snd_ctl_new1(&newctls[c->type], chip);
-	if (! ctl)
-		return -ENOMEM;
-	strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name));
-	ctl->id.index = c->index;
-	ctl->private_value = c->private_value;
-	if (c->tlv) {
-		ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
-		ctl->tlv.p = c->tlv;
-	}
-	if ((err = snd_ctl_add(chip->card, ctl)) < 0)
-		return err;
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
-
-static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-
-static struct ad1848_mix_elem snd_ad1848_controls[] = {
-AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1,
-		  db_scale_6bit),
-AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
-		  db_scale_5bit_12db_max),
-AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
-		  db_scale_5bit_12db_max),
-AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0,
-		  db_scale_rec_gain),
-{
-	.name = "Capture Source",
-	.type = AD1848_MIX_CAPTURE,
-},
-AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
-AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0,
-		  db_scale_6bit),
-};
-                                        
-int snd_ad1848_mixer(struct snd_ad1848 *chip)
-{
-	struct snd_card *card;
-	struct snd_pcm *pcm;
-	unsigned int idx;
-	int err;
-
-	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
-
-	pcm = chip->pcm;
-	card = chip->card;
-
-	strcpy(card->mixername, pcm->name);
-
-	for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++)
-		if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0)
-			return err;
-
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_ad1848_mixer);
-
-/*
- *  INIT part
- */
-
-static int __init alsa_ad1848_init(void)
-{
-	return 0;
-}
-
-static void __exit alsa_ad1848_exit(void)
-{
-}
-
-module_init(alsa_ad1848_init)
-module_exit(alsa_ad1848_exit)
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index 154e728..3e74d1a 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -38,7 +38,7 @@
 #include <linux/moduleparam.h>
 #include <sound/core.h>
 #include <sound/initval.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
 
@@ -76,7 +76,7 @@
 	int dev_no;
 	struct pnp_dev *dev;
 	struct pnp_dev *devmpu;
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 };
 
 static struct pnp_card_device_id snd_azt2320_pnpids[] = {
@@ -181,7 +181,7 @@
 	int error;
 	struct snd_card *card;
 	struct snd_card_azt2320 *acard;
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	struct snd_opl3 *opl3;
 
 	if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
@@ -200,11 +200,11 @@
 		return error;
 	}
 
-	if ((error = snd_cs4231_create(card, wss_port[dev], -1,
-				       irq[dev],
-				       dma1[dev],
-				       dma2[dev],
-				       CS4231_HW_DETECT, 0, &chip)) < 0) {
+	error = snd_wss_create(card, wss_port[dev], -1,
+			       irq[dev],
+			       dma1[dev], dma2[dev],
+			       WSS_HW_DETECT, 0, &chip);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
@@ -214,15 +214,18 @@
 	sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
 		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
 
-	if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
+	error = snd_wss_pcm(chip, 0, NULL);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
-	if ((error = snd_cs4231_mixer(chip)) < 0) {
+	error = snd_wss_mixer(chip);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
-	if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+	error = snd_wss_timer(chip, 0, NULL);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
@@ -293,7 +296,7 @@
 {
 	struct snd_card *card = pnp_get_card_drvdata(pcard);
 	struct snd_card_azt2320 *acard = card->private_data;
-	struct snd_cs4231 *chip = acard->chip;
+	struct snd_wss *chip = acard->chip;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	chip->suspend(chip);
@@ -304,7 +307,7 @@
 {
 	struct snd_card *card = pnp_get_card_drvdata(pcard);
 	struct snd_card_azt2320 *acard = card->private_data;
-	struct snd_cs4231 *chip = acard->chip;
+	struct snd_wss *chip = acard->chip;
 
 	chip->resume(chip);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index 4d198ec..e49aec7 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -50,7 +50,7 @@
 #include <linux/pnp.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
 #include <sound/sb.h>
 #include <sound/initval.h>
 
@@ -151,7 +151,7 @@
 	struct pnp_dev *play;
 #endif
 	struct snd_card *card;
-	struct snd_ad1848 *wss;
+	struct snd_wss *wss;
 	struct snd_sb *sb;
 
 	struct snd_pcm *pcm;
@@ -174,32 +174,57 @@
 #endif
 
 
-static struct ad1848_mix_elem snd_cmi8330_controls[] __devinitdata = {
-AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
-AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1),
-AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
-AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
-AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
-AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
-AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0),
-AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0),
-AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0),
-AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0),
-AD1848_DOUBLE("CD Capture Volume", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0),
-AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0),
-AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0),
-AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0),
-AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0),
-AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0),
-AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0),
-AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0),
-AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
-AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1),
-AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0),
-AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1),
-AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",CAPTURE,SWITCH), 0, CMI8330_RMUX3D, 7, 1, 1),
-AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",PLAYBACK,SWITCH), 0, CMI8330_MUTEMUX, 7, 1, 1),
+static struct snd_kcontrol_new snd_cmi8330_controls[] __devinitdata = {
+WSS_DOUBLE("Master Playback Volume", 0,
+		CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
+WSS_SINGLE("Loud Playback Switch", 0,
+		CMI8330_MUTEMUX, 6, 1, 1),
+WSS_DOUBLE("PCM Playback Switch", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+		CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
+WSS_DOUBLE("Line Playback Volume", 0,
+		CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
+WSS_DOUBLE("Line Capture Switch", 0,
+		CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
+WSS_DOUBLE("Line Capture Volume", 0,
+		CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0),
+WSS_DOUBLE("CD Playback Switch", 0,
+		CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0),
+WSS_DOUBLE("CD Capture Switch", 0,
+		CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0),
+WSS_DOUBLE("CD Playback Volume", 0,
+		CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0),
+WSS_DOUBLE("CD Capture Volume", 0,
+		CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0),
+WSS_SINGLE("Mic Playback Switch", 0,
+		CMI8330_MUTEMUX, 0, 1, 0),
+WSS_SINGLE("Mic Playback Volume", 0,
+		CMI8330_OUTPUTVOL, 0, 7, 0),
+WSS_SINGLE("Mic Capture Switch", 0,
+		CMI8330_RMUX3D, 0, 1, 0),
+WSS_SINGLE("Mic Capture Volume", 0,
+		CMI8330_OUTPUTVOL, 5, 7, 0),
+WSS_DOUBLE("Wavetable Playback Switch", 0,
+		CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0),
+WSS_DOUBLE("Wavetable Playback Volume", 0,
+		CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0),
+WSS_DOUBLE("Wavetable Capture Switch", 0,
+		CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0),
+WSS_DOUBLE("Wavetable Capture Volume", 0,
+		CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0),
+WSS_SINGLE("3D Control - Switch", 0,
+		CMI8330_RMUX3D, 5, 1, 1),
+WSS_SINGLE("PC Speaker Playback Volume", 0,
+		CMI8330_OUTPUTVOL, 3, 3, 0),
+WSS_SINGLE("FM Playback Switch", 0,
+		CMI8330_RECMUX, 3, 1, 1),
+WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0,
+		CMI8330_RMUX3D, 7, 1, 1),
+WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0,
+		CMI8330_MUTEMUX, 7, 1, 1),
 };
 
 #ifdef ENABLE_SB_MIXER
@@ -268,7 +293,10 @@
 	strcpy(card->mixername, "CMI8330/C3D");
 
 	for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
-		if ((err = snd_ad1848_add_ctl_elem(acard->wss, &snd_cmi8330_controls[idx])) < 0)
+		err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_cmi8330_controls[idx],
+					     acard->wss));
+		if (err < 0)
 			return err;
 	}
 
@@ -385,7 +413,7 @@
 	chip->streams[CMI_SB_STREAM].private_data = chip->sb;
 
 	/* AD1848 */
-	ops = snd_ad1848_get_pcm_ops(CMI_AD_STREAM);
+	ops = snd_wss_get_pcm_ops(CMI_AD_STREAM);
 	chip->streams[CMI_AD_STREAM].ops = *ops;
 	chip->streams[CMI_AD_STREAM].open = ops->open;
 	chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM];
@@ -461,16 +489,15 @@
 	int i, err;
 
 	acard = card->private_data;
-	if ((err = snd_ad1848_create(card,
-				     wssport[dev] + 4,
-				     wssirq[dev],
-				     wssdma[dev],
-				     AD1848_HW_DETECT,
-				     &acard->wss)) < 0) {
+	err = snd_wss_create(card, wssport[dev] + 4, -1,
+			     wssirq[dev],
+			     wssdma[dev], -1,
+			     WSS_HW_DETECT, 0, &acard->wss);
+	if (err < 0) {
 		snd_printk(KERN_ERR PFX "(AD1848) device busy??\n");
 		return err;
 	}
-	if (acard->wss->hardware != AD1848_HW_CMI8330) {
+	if (acard->wss->hardware != WSS_HW_CMI8330) {
 		snd_printk(KERN_ERR PFX "(AD1848) not found during probe\n");
 		return -ENODEV;
 	}
@@ -489,9 +516,10 @@
 		return err;
 	}
 
-	snd_ad1848_out(acard->wss, AD1848_MISC_INFO, 0x40); /* switch on MODE2 */
+	snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */
 	for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++)
-		snd_ad1848_out(acard->wss, i, snd_cmi8330_image[i - CMI8330_RMUX3D]);
+		snd_wss_out(acard->wss, i,
+			    snd_cmi8330_image[i - CMI8330_RMUX3D]);
 
 	if ((err = snd_cmi8330_mixer(card, acard)) < 0) {
 		snd_printk(KERN_ERR PFX "failed to create mixers\n");
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index 5067ee0..5870ca2 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -3,14 +3,12 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
 #
 
-snd-cs4231-lib-objs := cs4231_lib.o
 snd-cs4236-lib-objs := cs4236_lib.o
 snd-cs4231-objs := cs4231.o
 snd-cs4232-objs := cs4232.o
 snd-cs4236-objs := cs4236.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_CS4231_LIB) += snd-cs4231-lib.o
 obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
 obj-$(CONFIG_SND_CS4232) += snd-cs4232.o
 obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index e9462b9..ddd2891 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -27,7 +27,7 @@
 #include <linux/wait.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/initval.h>
 
@@ -91,7 +91,7 @@
 static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
 {
 	struct snd_card *card;
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	struct snd_pcm *pcm;
 	int error;
 
@@ -99,14 +99,14 @@
 	if (!card)
 		return -EINVAL;
 
-	error = snd_cs4231_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
-			CS4231_HW_DETECT, 0, &chip);
+	error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
+			WSS_HW_DETECT, 0, &chip);
 	if (error < 0)
 		goto out;
 
 	card->private_data = chip;
 
-	error = snd_cs4231_pcm(chip, 0, &pcm);
+	error = snd_wss_pcm(chip, 0, &pcm);
 	if (error < 0)
 		goto out;
 
@@ -118,11 +118,11 @@
 	if (dma2[n] >= 0)
 		sprintf(card->longname + strlen(card->longname), "&%d", dma2[n]);
 
-	error = snd_cs4231_mixer(chip);
+	error = snd_wss_mixer(chip);
 	if (error < 0)
 		goto out;
 
-	error = snd_cs4231_timer(chip, 0, NULL);
+	error = snd_wss_timer(chip, 0, NULL);
 	if (error < 0)
 		goto out;
 
@@ -160,7 +160,7 @@
 static int snd_cs4231_suspend(struct device *dev, unsigned int n, pm_message_t state)
 {
 	struct snd_card *card = dev_get_drvdata(dev);
-	struct snd_cs4231 *chip = card->private_data;
+	struct snd_wss *chip = card->private_data;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	chip->suspend(chip);
@@ -170,7 +170,7 @@
 static int snd_cs4231_resume(struct device *dev, unsigned int n)
 {
 	struct snd_card *card = dev_get_drvdata(dev);
-	struct snd_cs4231 *chip = card->private_data;
+	struct snd_wss *chip = card->private_data;
 
 	chip->resume(chip);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
deleted file mode 100644
index 521db70..0000000
--- a/sound/isa/cs423x/cs4231_lib.c
+++ /dev/null
@@ -1,1945 +0,0 @@
-/*
- *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
- *
- *  Bugs:
- *     - sometimes record brokes playback with WSS portion of 
- *       Yamaha OPL3-SA3 chip
- *     - CS4231 (GUS MAX) - still trouble with occasional noises
- *                        - broken initialization?
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <sound/core.h>
-#include <sound/cs4231.h>
-#include <sound/pcm_params.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
-MODULE_LICENSE("GPL");
-
-#if 0
-#define SNDRV_DEBUG_MCE
-#endif
-
-/*
- *  Some variables
- */
-
-static unsigned char freq_bits[14] = {
-	/* 5510 */	0x00 | CS4231_XTAL2,
-	/* 6620 */	0x0E | CS4231_XTAL2,
-	/* 8000 */	0x00 | CS4231_XTAL1,
-	/* 9600 */	0x0E | CS4231_XTAL1,
-	/* 11025 */	0x02 | CS4231_XTAL2,
-	/* 16000 */	0x02 | CS4231_XTAL1,
-	/* 18900 */	0x04 | CS4231_XTAL2,
-	/* 22050 */	0x06 | CS4231_XTAL2,
-	/* 27042 */	0x04 | CS4231_XTAL1,
-	/* 32000 */	0x06 | CS4231_XTAL1,
-	/* 33075 */	0x0C | CS4231_XTAL2,
-	/* 37800 */	0x08 | CS4231_XTAL2,
-	/* 44100 */	0x0A | CS4231_XTAL2,
-	/* 48000 */	0x0C | CS4231_XTAL1
-};
-
-static unsigned int rates[14] = {
-	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
-	27042, 32000, 33075, 37800, 44100, 48000
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
-	.count = ARRAY_SIZE(rates),
-	.list = rates,
-	.mask = 0,
-};
-
-static int snd_cs4231_xrate(struct snd_pcm_runtime *runtime)
-{
-	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-}
-
-static unsigned char snd_cs4231_original_image[32] =
-{
-	0x00,			/* 00/00 - lic */
-	0x00,			/* 01/01 - ric */
-	0x9f,			/* 02/02 - la1ic */
-	0x9f,			/* 03/03 - ra1ic */
-	0x9f,			/* 04/04 - la2ic */
-	0x9f,			/* 05/05 - ra2ic */
-	0xbf,			/* 06/06 - loc */
-	0xbf,			/* 07/07 - roc */
-	0x20,			/* 08/08 - pdfr */
-	CS4231_AUTOCALIB,	/* 09/09 - ic */
-	0x00,			/* 0a/10 - pc */
-	0x00,			/* 0b/11 - ti */
-	CS4231_MODE2,		/* 0c/12 - mi */
-	0xfc,			/* 0d/13 - lbc */
-	0x00,			/* 0e/14 - pbru */
-	0x00,			/* 0f/15 - pbrl */
-	0x80,			/* 10/16 - afei */
-	0x01,			/* 11/17 - afeii */
-	0x9f,			/* 12/18 - llic */
-	0x9f,			/* 13/19 - rlic */
-	0x00,			/* 14/20 - tlb */
-	0x00,			/* 15/21 - thb */
-	0x00,			/* 16/22 - la3mic/reserved */
-	0x00,			/* 17/23 - ra3mic/reserved */
-	0x00,			/* 18/24 - afs */
-	0x00,			/* 19/25 - lamoc/version */
-	0xcf,			/* 1a/26 - mioc */
-	0x00,			/* 1b/27 - ramoc/reserved */
-	0x20,			/* 1c/28 - cdfr */
-	0x00,			/* 1d/29 - res4 */
-	0x00,			/* 1e/30 - cbru */
-	0x00,			/* 1f/31 - cbrl */
-};
-
-static unsigned char snd_opti93x_original_image[32] =
-{
-	0x00,		/* 00/00 - l_mixout_outctrl */
-	0x00,		/* 01/01 - r_mixout_outctrl */
-	0x88,		/* 02/02 - l_cd_inctrl */
-	0x88,		/* 03/03 - r_cd_inctrl */
-	0x88,		/* 04/04 - l_a1/fm_inctrl */
-	0x88,		/* 05/05 - r_a1/fm_inctrl */
-	0x80,		/* 06/06 - l_dac_inctrl */
-	0x80,		/* 07/07 - r_dac_inctrl */
-	0x00,		/* 08/08 - ply_dataform_reg */
-	0x00,		/* 09/09 - if_conf */
-	0x00,		/* 0a/10 - pin_ctrl */
-	0x00,		/* 0b/11 - err_init_reg */
-	0x0a,		/* 0c/12 - id_reg */
-	0x00,		/* 0d/13 - reserved */
-	0x00,		/* 0e/14 - ply_upcount_reg */
-	0x00,		/* 0f/15 - ply_lowcount_reg */
-	0x88,		/* 10/16 - reserved/l_a1_inctrl */
-	0x88,		/* 11/17 - reserved/r_a1_inctrl */
-	0x88,		/* 12/18 - l_line_inctrl */
-	0x88,		/* 13/19 - r_line_inctrl */
-	0x88,		/* 14/20 - l_mic_inctrl */
-	0x88,		/* 15/21 - r_mic_inctrl */
-	0x80,		/* 16/22 - l_out_outctrl */
-	0x80,		/* 17/23 - r_out_outctrl */
-	0x00,		/* 18/24 - reserved */
-	0x00,		/* 19/25 - reserved */
-	0x00,		/* 1a/26 - reserved */
-	0x00,		/* 1b/27 - reserved */
-	0x00,		/* 1c/28 - cap_dataform_reg */
-	0x00,		/* 1d/29 - reserved */
-	0x00,		/* 1e/30 - cap_upcount_reg */
-	0x00		/* 1f/31 - cap_lowcount_reg */
-};
-
-/*
- *  Basic I/O functions
- */
-
-static inline void cs4231_outb(struct snd_cs4231 *chip, u8 offset, u8 val)
-{
-	outb(val, chip->port + offset);
-}
-
-static inline u8 cs4231_inb(struct snd_cs4231 *chip, u8 offset)
-{
-	return inb(chip->port + offset);
-}
-
-static void snd_cs4231_wait(struct snd_cs4231 *chip)
-{
-	int timeout;
-
-	for (timeout = 250;
-	     timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
-	     timeout--)
-	     	udelay(100);
-}
-
-static void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg,
-			    unsigned char mask, unsigned char value)
-{
-	unsigned char tmp = (chip->image[reg] & mask) | value;
-
-	snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
-		snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
-	chip->image[reg] = tmp;
-	if (!chip->calibrate_mute) {
-		cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
-		wmb();
-		cs4231_outb(chip, CS4231P(REG), tmp);
-		mb();
-	}
-}
-
-static void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg, unsigned char value)
-{
-	int timeout;
-
-	for (timeout = 250;
-	     timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
-	     timeout--)
-	     	udelay(10);
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
-	cs4231_outb(chip, CS4231P(REG), value);
-	mb();
-}
-
-void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char value)
-{
-	snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
-		snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
-	cs4231_outb(chip, CS4231P(REG), value);
-	chip->image[reg] = value;
-	mb();
-	snd_printdd("codec out - reg 0x%x = 0x%x\n",
-			chip->mce_bit | reg, value);
-}
-
-unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg)
-{
-	snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
-		snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
-#endif
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
-	mb();
-	return cs4231_inb(chip, CS4231P(REG));
-}
-
-void snd_cs4236_ext_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val)
-{
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
-	cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01));
-	cs4231_outb(chip, CS4231P(REG), val);
-	chip->eimage[CS4236_REG(reg)] = val;
-#if 0
-	printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
-#endif
-}
-
-unsigned char snd_cs4236_ext_in(struct snd_cs4231 *chip, unsigned char reg)
-{
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
-	cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01));
-#if 1
-	return cs4231_inb(chip, CS4231P(REG));
-#else
-	{
-		unsigned char res;
-		res = cs4231_inb(chip, CS4231P(REG));
-		printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
-		return res;
-	}
-#endif
-}
-
-#if 0
-
-static void snd_cs4231_debug(struct snd_cs4231 *chip)
-{
-	printk("CS4231 REGS:      INDEX = 0x%02x  ", cs4231_inb(chip, CS4231P(REGSEL)));
-	printk("                 STATUS = 0x%02x\n", cs4231_inb(chip, CS4231P(STATUS)));
-	printk("  0x00: left input      = 0x%02x  ", snd_cs4231_in(chip, 0x00));
-	printk("  0x10: alt 1 (CFIG 2)  = 0x%02x\n", snd_cs4231_in(chip, 0x10));
-	printk("  0x01: right input     = 0x%02x  ", snd_cs4231_in(chip, 0x01));
-	printk("  0x11: alt 2 (CFIG 3)  = 0x%02x\n", snd_cs4231_in(chip, 0x11));
-	printk("  0x02: GF1 left input  = 0x%02x  ", snd_cs4231_in(chip, 0x02));
-	printk("  0x12: left line in    = 0x%02x\n", snd_cs4231_in(chip, 0x12));
-	printk("  0x03: GF1 right input = 0x%02x  ", snd_cs4231_in(chip, 0x03));
-	printk("  0x13: right line in   = 0x%02x\n", snd_cs4231_in(chip, 0x13));
-	printk("  0x04: CD left input   = 0x%02x  ", snd_cs4231_in(chip, 0x04));
-	printk("  0x14: timer low       = 0x%02x\n", snd_cs4231_in(chip, 0x14));
-	printk("  0x05: CD right input  = 0x%02x  ", snd_cs4231_in(chip, 0x05));
-	printk("  0x15: timer high      = 0x%02x\n", snd_cs4231_in(chip, 0x15));
-	printk("  0x06: left output     = 0x%02x  ", snd_cs4231_in(chip, 0x06));
-	printk("  0x16: left MIC (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x16));
-	printk("  0x07: right output    = 0x%02x  ", snd_cs4231_in(chip, 0x07));
-	printk("  0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17));
-	printk("  0x08: playback format = 0x%02x  ", snd_cs4231_in(chip, 0x08));
-	printk("  0x18: IRQ status      = 0x%02x\n", snd_cs4231_in(chip, 0x18));
-	printk("  0x09: iface (CFIG 1)  = 0x%02x  ", snd_cs4231_in(chip, 0x09));
-	printk("  0x19: left line out   = 0x%02x\n", snd_cs4231_in(chip, 0x19));
-	printk("  0x0a: pin control     = 0x%02x  ", snd_cs4231_in(chip, 0x0a));
-	printk("  0x1a: mono control    = 0x%02x\n", snd_cs4231_in(chip, 0x1a));
-	printk("  0x0b: init & status   = 0x%02x  ", snd_cs4231_in(chip, 0x0b));
-	printk("  0x1b: right line out  = 0x%02x\n", snd_cs4231_in(chip, 0x1b));
-	printk("  0x0c: revision & mode = 0x%02x  ", snd_cs4231_in(chip, 0x0c));
-	printk("  0x1c: record format   = 0x%02x\n", snd_cs4231_in(chip, 0x1c));
-	printk("  0x0d: loopback        = 0x%02x  ", snd_cs4231_in(chip, 0x0d));
-	printk("  0x1d: var freq (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x1d));
-	printk("  0x0e: ply upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0e));
-	printk("  0x1e: ply lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1e));
-	printk("  0x0f: rec upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0f));
-	printk("  0x1f: rec lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1f));
-}
-
-#endif
-
-/*
- *  CS4231 detection / MCE routines
- */
-
-static void snd_cs4231_busy_wait(struct snd_cs4231 *chip)
-{
-	int timeout;
-
-	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
-	for (timeout = 5; timeout > 0; timeout--)
-		cs4231_inb(chip, CS4231P(REGSEL));
-	/* end of cleanup sequence */
-	for (timeout = 250;
-	     timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
-	     timeout--)
-	     	udelay(10);
-}
-
-void snd_cs4231_mce_up(struct snd_cs4231 *chip)
-{
-	unsigned long flags;
-	int timeout;
-
-	snd_cs4231_wait(chip);
-#ifdef CONFIG_SND_DEBUG
-	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
-		snd_printk("mce_up - auto calibration time out (0)\n");
-#endif
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->mce_bit |= CS4231_MCE;
-	timeout = cs4231_inb(chip, CS4231P(REGSEL));
-	if (timeout == 0x80)
-		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
-	if (!(timeout & CS4231_MCE))
-		cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-void snd_cs4231_mce_down(struct snd_cs4231 *chip)
-{
-	unsigned long flags;
-	unsigned long end_time;
-	int timeout;
-
-	snd_cs4231_busy_wait(chip);
-
-#ifdef CONFIG_SND_DEBUG
-	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
-		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL));
-#endif
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->mce_bit &= ~CS4231_MCE;
-	timeout = cs4231_inb(chip, CS4231P(REGSEL));
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (timeout == 0x80)
-		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
-	if ((timeout & CS4231_MCE) == 0 ||
-	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
-		return;
-	}
-
-	/*
-	 * Wait for (possible -- during init auto-calibration may not be set)
-	 * calibration process to start. Needs upto 5 sample periods on AD1848
-	 * which at the slowest possible rate of 5.5125 kHz means 907 us.
-	 */
-	msleep(1);
-
-	snd_printdd("(1) jiffies = %lu\n", jiffies);
-
-	/* check condition up to 250 ms */
-	end_time = jiffies + msecs_to_jiffies(250);
-	while (snd_cs4231_in(chip, CS4231_TEST_INIT) &
-		CS4231_CALIB_IN_PROGRESS) {
-
-		if (time_after(jiffies, end_time)) {
-			snd_printk(KERN_ERR "mce_down - "
-					"auto calibration time out (2)\n");
-			return;
-		}
-		msleep(1);
-	}
-
-	snd_printdd("(2) jiffies = %lu\n", jiffies);
-
-	/* check condition up to 100 ms */
-	end_time = jiffies + msecs_to_jiffies(100);
-	while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
-		if (time_after(jiffies, end_time)) {
-			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
-			return;
-		}
-		msleep(1);
-	}
-
-	snd_printdd("(3) jiffies = %lu\n", jiffies);
-	snd_printd("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL)));
-}
-
-static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size)
-{
-	switch (format & 0xe0) {
-	case CS4231_LINEAR_16:
-	case CS4231_LINEAR_16_BIG:
-		size >>= 1;
-		break;
-	case CS4231_ADPCM_16:
-		return size >> 2;
-	}
-	if (format & CS4231_STEREO)
-		size >>= 1;
-	return size;
-}
-
-static int snd_cs4231_trigger(struct snd_pcm_substream *substream,
-			      int cmd)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	int result = 0;
-	unsigned int what;
-	struct snd_pcm_substream *s;
-	int do_start;
-
-#if 0
-	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, cs4231_inb(chip, CS4231P(STATUS)));
-#endif
-
-	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 = 0;
-	snd_pcm_group_for_each_entry(s, substream) {
-		if (s == chip->playback_substream) {
-			what |= CS4231_PLAYBACK_ENABLE;
-			snd_pcm_trigger_done(s, substream);
-		} else if (s == chip->capture_substream) {
-			what |= CS4231_RECORD_ENABLE;
-			snd_pcm_trigger_done(s, substream);
-		}
-	}
-	spin_lock(&chip->reg_lock);
-	if (do_start) {
-		chip->image[CS4231_IFACE_CTRL] |= what;
-		if (chip->trigger)
-			chip->trigger(chip, what, 1);
-	} else {
-		chip->image[CS4231_IFACE_CTRL] &= ~what;
-		if (chip->trigger)
-			chip->trigger(chip, what, 0);
-	}
-	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
-	spin_unlock(&chip->reg_lock);
-#if 0
-	snd_cs4231_debug(chip);
-#endif
-	return result;
-}
-
-/*
- *  CODEC I/O
- */
-
-static unsigned char snd_cs4231_get_rate(unsigned int rate)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(rates); i++)
-		if (rate == rates[i])
-			return freq_bits[i];
-	// snd_BUG();
-	return freq_bits[ARRAY_SIZE(rates) - 1];
-}
-
-static unsigned char snd_cs4231_get_format(struct snd_cs4231 *chip,
-				           int format,
-                                           int channels)
-{
-	unsigned char rformat;
-
-	rformat = CS4231_LINEAR_8;
-	switch (format) {
-	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break;
-	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break;
-	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break;
-	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break;
-	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break;
-	}
-	if (channels > 1)
-		rformat |= CS4231_STEREO;
-#if 0
-	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
-#endif
-	return rformat;
-}
-
-static void snd_cs4231_calibrate_mute(struct snd_cs4231 *chip, int mute)
-{
-	unsigned long flags;
-
-	mute = mute ? 1 : 0;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	if (chip->calibrate_mute == mute) {
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		return;
-	}
-	if (!mute) {
-		snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]);
-		snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]);
-		snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]);
-	}
-	snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
-	snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
-	snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
-	snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
-	snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
-	snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
-	snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
-	snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
-	snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
-	if (chip->hardware == CS4231_HW_INTERWAVE) {
-		snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]);
-		snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]);		
-		snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]);
-		snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]);
-	}
-	chip->calibrate_mute = mute;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static void snd_cs4231_playback_format(struct snd_cs4231 *chip,
-				       struct snd_pcm_hw_params *params,
-				       unsigned char pdfr)
-{
-	unsigned long flags;
-	int full_calib = 1;
-
-	mutex_lock(&chip->mce_mutex);
-	snd_cs4231_calibrate_mute(chip, 1);
-	if (chip->hardware == CS4231_HW_CS4231A ||
-	    (chip->hardware & CS4231_HW_CS4232_MASK)) {
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */
-			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
-			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
-			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
-			udelay(100); /* Fixes audible clicks at least on GUS MAX */
-			full_calib = 0;
-		}
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-	}
-	if (full_calib) {
-		snd_cs4231_mce_up(chip);
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) {
-			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
-					(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
-					(pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
-				        pdfr);
-		} else {
-			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr);
-		}
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		if (chip->hardware == CS4231_HW_OPL3SA2)
-			udelay(100);	/* this seems to help */
-		snd_cs4231_mce_down(chip);
-	}
-	snd_cs4231_calibrate_mute(chip, 0);
-	mutex_unlock(&chip->mce_mutex);
-}
-
-static void snd_cs4231_capture_format(struct snd_cs4231 *chip,
-				      struct snd_pcm_hw_params *params,
-                                      unsigned char cdfr)
-{
-	unsigned long flags;
-	int full_calib = 1;
-
-	mutex_lock(&chip->mce_mutex);
-	snd_cs4231_calibrate_mute(chip, 1);
-	if (chip->hardware == CS4231_HW_CS4231A ||
-	    (chip->hardware & CS4231_HW_CS4232_MASK)) {
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */
-		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
-			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
-			snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr);
-			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
-			full_calib = 0;
-		}
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-	}
-	if (full_calib) {
-		snd_cs4231_mce_up(chip);
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		if (chip->hardware != CS4231_HW_INTERWAVE) {
-			if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
-				snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
-					       ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
-					       (cdfr & 0x0f));
-				spin_unlock_irqrestore(&chip->reg_lock, flags);
-				snd_cs4231_mce_down(chip);
-				snd_cs4231_mce_up(chip);
-				spin_lock_irqsave(&chip->reg_lock, flags);
-			}
-		}
-		snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		snd_cs4231_mce_down(chip);
-	}
-	snd_cs4231_calibrate_mute(chip, 0);
-	mutex_unlock(&chip->mce_mutex);
-}
-
-/*
- *  Timer interface
- */
-
-static unsigned long snd_cs4231_timer_resolution(struct snd_timer * timer)
-{
-	struct snd_cs4231 *chip = snd_timer_chip(timer);
-	if (chip->hardware & CS4231_HW_CS4236B_MASK)
-		return 14467;
-	else
-		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
-}
-
-static int snd_cs4231_timer_start(struct snd_timer * timer)
-{
-	unsigned long flags;
-	unsigned int ticks;
-	struct snd_cs4231 *chip = snd_timer_chip(timer);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ticks = timer->sticks;
-	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
-	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
-	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
-		snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8));
-		snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks);
-		snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE);
-	}
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-static int snd_cs4231_timer_stop(struct snd_timer * timer)
-{
-	unsigned long flags;
-	struct snd_cs4231 *chip = snd_timer_chip(timer);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-static void snd_cs4231_init(struct snd_cs4231 *chip)
-{
-	unsigned long flags;
-
-	snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("init: (1)\n");
-#endif
-	snd_cs4231_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
-			     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO |
-			     CS4231_CALIB_MODE);
-	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
-	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("init: (2)\n");
-#endif
-
-	snd_cs4231_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]);
-#endif
-
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	snd_cs4231_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("init: (4)\n");
-#endif
-
-	snd_cs4231_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_down(chip);
-
-#ifdef SNDRV_DEBUG_MCE
-	snd_printk("init: (5)\n");
-#endif
-}
-
-static int snd_cs4231_open(struct snd_cs4231 *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	mutex_lock(&chip->open_mutex);
-	if ((chip->mode & mode) ||
-	    ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) {
-		mutex_unlock(&chip->open_mutex);
-		return -EAGAIN;
-	}
-	if (chip->mode & CS4231_MODE_OPEN) {
-		chip->mode |= mode;
-		mutex_unlock(&chip->open_mutex);
-		return 0;
-	}
-	/* ok. now enable and ack CODEC IRQ */
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
-		       CS4231_RECORD_IRQ |
-		       CS4231_TIMER_IRQ);
-	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
-	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
-	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
-	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
-	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
-	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
-		       CS4231_RECORD_IRQ |
-		       CS4231_TIMER_IRQ);
-	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	chip->mode = mode;
-	mutex_unlock(&chip->open_mutex);
-	return 0;
-}
-
-static void snd_cs4231_close(struct snd_cs4231 *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	mutex_lock(&chip->open_mutex);
-	chip->mode &= ~mode;
-	if (chip->mode & CS4231_MODE_OPEN) {
-		mutex_unlock(&chip->open_mutex);
-		return;
-	}
-	snd_cs4231_calibrate_mute(chip, 1);
-
-	/* disable IRQ */
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
-	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
-	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
-	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
-	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
-
-	/* now disable record & playback */
-
-	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
-					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		snd_cs4231_mce_up(chip);
-		spin_lock_irqsave(&chip->reg_lock, flags);
-		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
-						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
-		snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
-		spin_unlock_irqrestore(&chip->reg_lock, flags);
-		snd_cs4231_mce_down(chip);
-		spin_lock_irqsave(&chip->reg_lock, flags);
-	}
-
-	/* clear IRQ again */
-	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
-	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
-	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	snd_cs4231_calibrate_mute(chip, 0);
-
-	chip->mode = 0;
-	mutex_unlock(&chip->open_mutex);
-}
-
-/*
- *  timer open/close
- */
-
-static int snd_cs4231_timer_open(struct snd_timer * timer)
-{
-	struct snd_cs4231 *chip = snd_timer_chip(timer);
-	snd_cs4231_open(chip, CS4231_MODE_TIMER);
-	return 0;
-}
-
-static int snd_cs4231_timer_close(struct snd_timer * timer)
-{
-	struct snd_cs4231 *chip = snd_timer_chip(timer);
-	snd_cs4231_close(chip, CS4231_MODE_TIMER);
-	return 0;
-}
-
-static struct snd_timer_hardware snd_cs4231_timer_table =
-{
-	.flags =	SNDRV_TIMER_HW_AUTO,
-	.resolution =	9945,
-	.ticks =	65535,
-	.open =		snd_cs4231_timer_open,
-	.close =	snd_cs4231_timer_close,
-	.c_resolution = snd_cs4231_timer_resolution,
-	.start =	snd_cs4231_timer_start,
-	.stop =		snd_cs4231_timer_stop,
-};
-
-/*
- *  ok.. exported functions..
- */
-
-static int snd_cs4231_playback_hw_params(struct snd_pcm_substream *substream,
-					 struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	unsigned char new_pdfr;
-	int err;
-
-	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
-		return err;
-	new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
-		   snd_cs4231_get_rate(params_rate(hw_params));
-	chip->set_playback_format(chip, hw_params, new_pdfr);
-	return 0;
-}
-
-static int snd_cs4231_playback_hw_free(struct snd_pcm_substream *substream)
-{
-	return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->p_dma_size = size;
-	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
-	snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
-	count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
-	snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
-	snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-#if 0
-	snd_cs4231_debug(chip);
-#endif
-	return 0;
-}
-
-static int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream,
-					struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	unsigned char new_cdfr;
-	int err;
-
-	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
-		return err;
-	new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) |
-		   snd_cs4231_get_rate(params_rate(hw_params));
-	chip->set_capture_format(chip, hw_params, new_cdfr);
-	return 0;
-}
-
-static int snd_cs4231_capture_hw_free(struct snd_pcm_substream *substream)
-{
-	return snd_pcm_lib_free_pages(substream);
-}
-
-static int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->c_dma_size = size;
-	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
-	snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
-	count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1;
-	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
-		snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
-		snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
-	} else {
-		snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
-		snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8));
-	}
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-void snd_cs4231_overrange(struct snd_cs4231 *chip)
-{
-	unsigned long flags;
-	unsigned char res;
-
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	res = snd_cs4231_in(chip, CS4231_TEST_INIT);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */
-		chip->capture_substream->runtime->overrange++;
-}
-
-irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id)
-{
-	struct snd_cs4231 *chip = dev_id;
-	unsigned char status;
-
-	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
-	if (status & CS4231_TIMER_IRQ) {
-		if (chip->timer)
-			snd_timer_interrupt(chip->timer, chip->timer->sticks);
-	}		
-	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) {
-		if (status & CS4231_PLAYBACK_IRQ) {
-			if (chip->mode & CS4231_MODE_PLAY) {
-				if (chip->playback_substream)
-					snd_pcm_period_elapsed(chip->playback_substream);
-			}
-			if (chip->mode & CS4231_MODE_RECORD) {
-				if (chip->capture_substream) {
-					snd_cs4231_overrange(chip);
-					snd_pcm_period_elapsed(chip->capture_substream);
-				}
-			}
-		}
-	} else {
-		if (status & CS4231_PLAYBACK_IRQ) {
-			if (chip->playback_substream)
-				snd_pcm_period_elapsed(chip->playback_substream);
-		}
-		if (status & CS4231_RECORD_IRQ) {
-			if (chip->capture_substream) {
-				snd_cs4231_overrange(chip);
-				snd_pcm_period_elapsed(chip->capture_substream);
-			}
-		}
-	}
-
-	spin_lock(&chip->reg_lock);
-	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
-	spin_unlock(&chip->reg_lock);
-	return IRQ_HANDLED;
-}
-
-static snd_pcm_uframes_t snd_cs4231_playback_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
-		return 0;
-	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_cs4231_capture_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-	
-	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
-		return 0;
-	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-/*
-
- */
-
-static int snd_cs4231_probe(struct snd_cs4231 *chip)
-{
-	unsigned long flags;
-	int i, id, rev;
-	unsigned char *ptr;
-	unsigned int hw;
-
-#if 0
-	snd_cs4231_debug(chip);
-#endif
-	id = 0;
-	for (i = 0; i < 50; i++) {
-		mb();
-		if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
-			udelay(2000);
-		else {
-			spin_lock_irqsave(&chip->reg_lock, flags);
-			snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
-			id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
-			spin_unlock_irqrestore(&chip->reg_lock, flags);
-			if (id == 0x0a)
-				break;	/* this is valid value */
-		}
-	}
-	snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id);
-	if (id != 0x0a)
-		return -ENODEV;	/* no valid device found */
-
-	if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
-		rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7;
-		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
-		if (rev == 0x80) {
-			unsigned char tmp = snd_cs4231_in(chip, 23);
-			snd_cs4231_out(chip, 23, ~tmp);
-			if (snd_cs4231_in(chip, 23) != tmp)
-				chip->hardware = CS4231_HW_AD1845;
-			else
-				chip->hardware = CS4231_HW_CS4231;
-		} else if (rev == 0xa0) {
-			chip->hardware = CS4231_HW_CS4231A;
-		} else if (rev == 0xa2) {
-			chip->hardware = CS4231_HW_CS4232;
-		} else if (rev == 0xb2) {
-			chip->hardware = CS4231_HW_CS4232A;
-		} else if (rev == 0x83) {
-			chip->hardware = CS4231_HW_CS4236;
-		} else if (rev == 0x03) {
-			chip->hardware = CS4231_HW_CS4236B;
-		} else {
-			snd_printk("unknown CS chip with version 0x%x\n", rev);
-			return -ENODEV;		/* unknown CS4231 chip? */
-		}
-	}
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	cs4231_inb(chip, CS4231P(STATUS));	/* clear any pendings IRQ */
-	cs4231_outb(chip, CS4231P(STATUS), 0);
-	mb();
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-	chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
-	switch (chip->hardware) {
-	case CS4231_HW_INTERWAVE:
-		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
-		break;
-	case CS4231_HW_CS4235:
-	case CS4231_HW_CS4236B:
-	case CS4231_HW_CS4237B:
-	case CS4231_HW_CS4238B:
-	case CS4231_HW_CS4239:
-		if (hw == CS4231_HW_DETECT3)
-			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
-		else
-			chip->hardware = CS4231_HW_CS4236;
-		break;
-	}
-
-	chip->image[CS4231_IFACE_CTRL] =
-	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
-	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
-	if (chip->hardware != CS4231_HW_OPTI93X) {
-		chip->image[CS4231_ALT_FEATURE_1] = 0x80;
-		chip->image[CS4231_ALT_FEATURE_2] =
-			chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
-	}
-	ptr = (unsigned char *) &chip->image;
-	snd_cs4231_mce_down(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	for (i = 0; i < 32; i++)	/* ok.. fill all CS4231 registers */
-		snd_cs4231_out(chip, i, *ptr++);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_up(chip);
-	snd_cs4231_mce_down(chip);
-
-	mdelay(2);
-
-	/* ok.. try check hardware version for CS4236+ chips */
-	if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) {
-		if (chip->hardware == CS4231_HW_CS4236B) {
-			rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
-			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
-			id = snd_cs4236_ext_in(chip, CS4236_VERSION);
-			snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
-			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
-			if ((id & 0x1f) == 0x1d) {	/* CS4235 */
-				chip->hardware = CS4231_HW_CS4235;
-				switch (id >> 5) {
-				case 4:
-				case 5:
-				case 6:
-					break;
-				default:
-					snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
-				}
-			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */
-				switch (id >> 5) {
-				case 4:
-				case 5:
-				case 6:
-				case 7:
-					chip->hardware = CS4231_HW_CS4236B;
-					break;
-				default:
-					snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
-				}
-			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */
-				chip->hardware = CS4231_HW_CS4237B;
-				switch (id >> 5) {
-				case 4:
-				case 5:
-				case 6:
-				case 7:
-					break;
-				default:
-					snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
-				}
-			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */
-				chip->hardware = CS4231_HW_CS4238B;
-				switch (id >> 5) {
-				case 5:
-				case 6:
-				case 7:
-					break;
-				default:
-					snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
-				}
-			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */
-				chip->hardware = CS4231_HW_CS4239;
-				switch (id >> 5) {
-				case 4:
-				case 5:
-				case 6:
-					break;
-				default:
-					snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
-				}
-			} else {
-				snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
-			}
-		}
-	}
-	return 0;		/* all things are ok.. */
-}
-
-/*
-
- */
-
-static struct snd_pcm_hardware snd_cs4231_playback =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_RESUME |
-				 SNDRV_PCM_INFO_SYNC_START),
-	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
-				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	.rate_min =		5510,
-	.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 struct snd_pcm_hardware snd_cs4231_capture =
-{
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_RESUME |
-				 SNDRV_PCM_INFO_SYNC_START),
-	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
-				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	.rate_min =		5510,
-	.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 int snd_cs4231_playback_open(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int err;
-
-	runtime->hw = snd_cs4231_playback;
-
-	/* hardware bug in InterWave chipset */
-	if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3)
-	    	runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
-	
-	/* hardware limitation of cheap chips */
-	if (chip->hardware == CS4231_HW_CS4235 ||
-	    chip->hardware == CS4231_HW_CS4239)
-		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
-
-	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
-	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
-
-	if (chip->claim_dma) {
-		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
-			return err;
-	}
-
-	if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) {
-		if (chip->release_dma)
-			chip->release_dma(chip, chip->dma_private_data, chip->dma1);
-		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
-		return err;
-	}
-	chip->playback_substream = substream;
-	snd_pcm_set_sync(substream);
-	chip->rate_constraint(runtime);
-	return 0;
-}
-
-static int snd_cs4231_capture_open(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int err;
-
-	runtime->hw = snd_cs4231_capture;
-
-	/* hardware limitation of cheap chips */
-	if (chip->hardware == CS4231_HW_CS4235 ||
-	    chip->hardware == CS4231_HW_CS4239)
-		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
-
-	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
-	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
-
-	if (chip->claim_dma) {
-		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
-			return err;
-	}
-
-	if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) {
-		if (chip->release_dma)
-			chip->release_dma(chip, chip->dma_private_data, chip->dma2);
-		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
-		return err;
-	}
-	chip->capture_substream = substream;
-	snd_pcm_set_sync(substream);
-	chip->rate_constraint(runtime);
-	return 0;
-}
-
-static int snd_cs4231_playback_close(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-
-	chip->playback_substream = NULL;
-	snd_cs4231_close(chip, CS4231_MODE_PLAY);
-	return 0;
-}
-
-static int snd_cs4231_capture_close(struct snd_pcm_substream *substream)
-{
-	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
-
-	chip->capture_substream = NULL;
-	snd_cs4231_close(chip, CS4231_MODE_RECORD);
-	return 0;
-}
-
-#ifdef CONFIG_PM
-
-/* lowlevel suspend callback for CS4231 */
-static void snd_cs4231_suspend(struct snd_cs4231 *chip)
-{
-	int reg;
-	unsigned long flags;
-	
-	snd_pcm_suspend_all(chip->pcm);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	for (reg = 0; reg < 32; reg++)
-		chip->image[reg] = snd_cs4231_in(chip, reg);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-/* lowlevel resume callback for CS4231 */
-static void snd_cs4231_resume(struct snd_cs4231 *chip)
-{
-	int reg;
-	unsigned long flags;
-	/* int timeout; */
-	
-	snd_cs4231_mce_up(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	for (reg = 0; reg < 32; reg++) {
-		switch (reg) {
-		case CS4231_VERSION:
-			break;
-		default:
-			snd_cs4231_out(chip, reg, chip->image[reg]);
-			break;
-		}
-	}
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-#if 1
-	snd_cs4231_mce_down(chip);
-#else
-	/* The following is a workaround to avoid freeze after resume on TP600E.
-	   This is the first half of copy of snd_cs4231_mce_down(), but doesn't
-	   include rescheduling.  -- iwai
-	   */
-	snd_cs4231_busy_wait(chip);
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	chip->mce_bit &= ~CS4231_MCE;
-	timeout = cs4231_inb(chip, CS4231P(REGSEL));
-	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (timeout == 0x80)
-		snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
-	if ((timeout & CS4231_MCE) == 0 ||
-	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) {
-		return;
-	}
-	snd_cs4231_busy_wait(chip);
-#endif
-}
-#endif /* CONFIG_PM */
-
-static int snd_cs4231_free(struct snd_cs4231 *chip)
-{
-	release_and_free_resource(chip->res_port);
-	release_and_free_resource(chip->res_cport);
-	if (chip->irq >= 0) {
-		disable_irq(chip->irq);
-		if (!(chip->hwshare & CS4231_HWSHARE_IRQ))
-			free_irq(chip->irq, (void *) chip);
-	}
-	if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) {
-		snd_dma_disable(chip->dma1);
-		free_dma(chip->dma1);
-	}
-	if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
-		snd_dma_disable(chip->dma2);
-		free_dma(chip->dma2);
-	}
-	if (chip->timer)
-		snd_device_free(chip->card, chip->timer);
-	kfree(chip);
-	return 0;
-}
-
-static int snd_cs4231_dev_free(struct snd_device *device)
-{
-	struct snd_cs4231 *chip = device->device_data;
-	return snd_cs4231_free(chip);	
-}
-
-const char *snd_cs4231_chip_id(struct snd_cs4231 *chip)
-{
-	switch (chip->hardware) {
-	case CS4231_HW_CS4231:	return "CS4231";
-	case CS4231_HW_CS4231A: return "CS4231A";
-	case CS4231_HW_CS4232:	return "CS4232";
-	case CS4231_HW_CS4232A:	return "CS4232A";
-	case CS4231_HW_CS4235:	return "CS4235";
-	case CS4231_HW_CS4236:  return "CS4236";
-	case CS4231_HW_CS4236B: return "CS4236B";
-	case CS4231_HW_CS4237B: return "CS4237B";
-	case CS4231_HW_CS4238B: return "CS4238B";
-	case CS4231_HW_CS4239:	return "CS4239";
-	case CS4231_HW_INTERWAVE: return "AMD InterWave";
-	case CS4231_HW_OPL3SA2: return chip->card->shortname;
-	case CS4231_HW_AD1845: return "AD1845";
-	case CS4231_HW_OPTI93X: return "OPTi 93x";
-	default: return "???";
-	}
-}
-
-static int snd_cs4231_new(struct snd_card *card,
-			  unsigned short hardware,
-			  unsigned short hwshare,
-			  struct snd_cs4231 ** rchip)
-{
-	struct snd_cs4231 *chip;
-
-	*rchip = NULL;
-	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-	if (chip == NULL)
-		return -ENOMEM;
-	chip->hardware = hardware;
-	chip->hwshare = hwshare;
-
-	spin_lock_init(&chip->reg_lock);
-	mutex_init(&chip->mce_mutex);
-	mutex_init(&chip->open_mutex);
-	chip->card = card;
-	chip->rate_constraint = snd_cs4231_xrate;
-	chip->set_playback_format = snd_cs4231_playback_format;
-	chip->set_capture_format = snd_cs4231_capture_format;
-	if (chip->hardware == CS4231_HW_OPTI93X)
-		memcpy(&chip->image, &snd_opti93x_original_image,
-		       sizeof(snd_opti93x_original_image));
-	else
-		memcpy(&chip->image, &snd_cs4231_original_image,
-		       sizeof(snd_cs4231_original_image));
-
-        *rchip = chip;
-        return 0;
-}
-
-int snd_cs4231_create(struct snd_card *card,
-	              unsigned long port,
-	              unsigned long cport,
-		      int irq, int dma1, int dma2,
-		      unsigned short hardware,
-		      unsigned short hwshare,
-		      struct snd_cs4231 ** rchip)
-{
-	static struct snd_device_ops ops = {
-		.dev_free =	snd_cs4231_dev_free,
-	};
-	struct snd_cs4231 *chip;
-	int err;
-
-	err = snd_cs4231_new(card, hardware, hwshare, &chip);
-	if (err < 0)
-		return err;
-	
-	chip->irq = -1;
-	chip->dma1 = -1;
-	chip->dma2 = -1;
-
-	if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) {
-		snd_printk(KERN_ERR "cs4231: can't grab port 0x%lx\n", port);
-		snd_cs4231_free(chip);
-		return -EBUSY;
-	}
-	chip->port = port;
-	if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) {
-		snd_printk(KERN_ERR "cs4231: can't grab control port 0x%lx\n", cport);
-		snd_cs4231_free(chip);
-		return -ENODEV;
-	}
-	chip->cport = cport;
-	if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, IRQF_DISABLED, "CS4231", (void *) chip)) {
-		snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq);
-		snd_cs4231_free(chip);
-		return -EBUSY;
-	}
-	chip->irq = irq;
-	if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) {
-		snd_printk(KERN_ERR "cs4231: can't grab DMA1 %d\n", dma1);
-		snd_cs4231_free(chip);
-		return -EBUSY;
-	}
-	chip->dma1 = dma1;
-	if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) {
-		snd_printk(KERN_ERR "cs4231: can't grab DMA2 %d\n", dma2);
-		snd_cs4231_free(chip);
-		return -EBUSY;
-	}
-	if (dma1 == dma2 || dma2 < 0) {
-		chip->single_dma = 1;
-		chip->dma2 = chip->dma1;
-	} else
-		chip->dma2 = dma2;
-
-	/* global setup */
-	if (snd_cs4231_probe(chip) < 0) {
-		snd_cs4231_free(chip);
-		return -ENODEV;
-	}
-	snd_cs4231_init(chip);
-
-#if 0
-	if (chip->hardware & CS4231_HW_CS4232_MASK) {
-		if (chip->res_cport == NULL)
-			snd_printk("CS4232 control port features are not accessible\n");
-	}
-#endif
-
-	/* Register device */
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_cs4231_free(chip);
-		return err;
-	}
-
-#ifdef CONFIG_PM
-	/* Power Management */
-	chip->suspend = snd_cs4231_suspend;
-	chip->resume = snd_cs4231_resume;
-#endif
-
-	*rchip = chip;
-	return 0;
-}
-
-static struct snd_pcm_ops snd_cs4231_playback_ops = {
-	.open =		snd_cs4231_playback_open,
-	.close =	snd_cs4231_playback_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_cs4231_playback_hw_params,
-	.hw_free =	snd_cs4231_playback_hw_free,
-	.prepare =	snd_cs4231_playback_prepare,
-	.trigger =	snd_cs4231_trigger,
-	.pointer =	snd_cs4231_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_cs4231_capture_ops = {
-	.open =		snd_cs4231_capture_open,
-	.close =	snd_cs4231_capture_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_cs4231_capture_hw_params,
-	.hw_free =	snd_cs4231_capture_hw_free,
-	.prepare =	snd_cs4231_capture_prepare,
-	.trigger =	snd_cs4231_trigger,
-	.pointer =	snd_cs4231_capture_pointer,
-};
-
-int snd_cs4231_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm)
-{
-	struct snd_pcm *pcm;
-	int err;
-
-	if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0)
-		return err;
-
-	spin_lock_init(&chip->reg_lock);
-	mutex_init(&chip->mce_mutex);
-	mutex_init(&chip->open_mutex);
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops);
-	
-	/* global setup */
-	pcm->private_data = chip;
-	pcm->info_flags = 0;
-	if (chip->single_dma)
-		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
-	if (chip->hardware != CS4231_HW_INTERWAVE)
-		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
-	strcpy(pcm->name, snd_cs4231_chip_id(chip));
-
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-					      snd_dma_isa_data(),
-					      64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
-
-	chip->pcm = pcm;
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-static void snd_cs4231_timer_free(struct snd_timer *timer)
-{
-	struct snd_cs4231 *chip = timer->private_data;
-	chip->timer = NULL;
-}
-
-int snd_cs4231_timer(struct snd_cs4231 *chip, int device, struct snd_timer **rtimer)
-{
-	struct snd_timer *timer;
-	struct snd_timer_id tid;
-	int err;
-
-	/* Timer initialization */
-	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
-	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
-	tid.card = chip->card->number;
-	tid.device = device;
-	tid.subdevice = 0;
-	if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
-		return err;
-	strcpy(timer->name, snd_cs4231_chip_id(chip));
-	timer->private_data = chip;
-	timer->private_free = snd_cs4231_timer_free;
-	timer->hw = snd_cs4231_timer_table;
-	chip->timer = timer;
-	if (rtimer)
-		*rtimer = timer;
-	return 0;
-}
-	
-/*
- *  MIXER part
- */
-
-static int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	static char *texts[4] = {
-		"Line", "Aux", "Mic", "Mix"
-	};
-	static char *opl3sa_texts[4] = {
-		"Line", "CD", "Mic", "Mix"
-	};
-	static char *gusmax_texts[4] = {
-		"Line", "Synth", "Mic", "Mix"
-	};
-	char **ptexts = texts;
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-
-	snd_assert(chip->card != NULL, return -EINVAL);
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 2;
-	uinfo->value.enumerated.items = 4;
-	if (uinfo->value.enumerated.item > 3)
-		uinfo->value.enumerated.item = 3;
-	if (!strcmp(chip->card->driver, "GUS MAX"))
-		ptexts = gusmax_texts;
-	switch (chip->hardware) {
-	case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break;
-	case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break;
-	}
-	strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
-	return 0;
-}
-
-static int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
-	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return 0;
-}
-
-static int snd_cs4231_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	unsigned short left, right;
-	int change;
-	
-	if (ucontrol->value.enumerated.item[0] > 3 ||
-	    ucontrol->value.enumerated.item[1] > 3)
-		return -EINVAL;
-	left = ucontrol->value.enumerated.item[0] << 6;
-	right = ucontrol->value.enumerated.item[1] << 6;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
-	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
-	change = left != chip->image[CS4231_LEFT_INPUT] ||
-	         right != chip->image[CS4231_RIGHT_INPUT];
-	snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
-	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return change;
-}
-
-int snd_cs4231_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-int snd_cs4231_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0xff;
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0xff;
-	
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (invert)
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-	return 0;
-}
-
-int snd_cs4231_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0xff;
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0xff;
-	int change;
-	unsigned short val;
-	
-	val = (ucontrol->value.integer.value[0] & mask);
-	if (invert)
-		val = mask - val;
-	val <<= shift;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	val = (chip->image[reg] & ~(mask << shift)) | val;
-	change = val != chip->image[reg];
-	snd_cs4231_out(chip, reg, val);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return change;
-}
-
-int snd_cs4231_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-int snd_cs4231_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int left_reg = kcontrol->private_value & 0xff;
-	int right_reg = (kcontrol->private_value >> 8) & 0xff;
-	int shift_left = (kcontrol->private_value >> 16) & 0x07;
-	int shift_right = (kcontrol->private_value >> 19) & 0x07;
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-	int invert = (kcontrol->private_value >> 22) & 1;
-	
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
-	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	if (invert) {
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
-	}
-	return 0;
-}
-
-int snd_cs4231_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int left_reg = kcontrol->private_value & 0xff;
-	int right_reg = (kcontrol->private_value >> 8) & 0xff;
-	int shift_left = (kcontrol->private_value >> 16) & 0x07;
-	int shift_right = (kcontrol->private_value >> 19) & 0x07;
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-	int invert = (kcontrol->private_value >> 22) & 1;
-	int change;
-	unsigned short val1, val2;
-	
-	val1 = ucontrol->value.integer.value[0] & mask;
-	val2 = ucontrol->value.integer.value[1] & mask;
-	if (invert) {
-		val1 = mask - val1;
-		val2 = mask - val2;
-	}
-	val1 <<= shift_left;
-	val2 <<= shift_right;
-	spin_lock_irqsave(&chip->reg_lock, flags);
-	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
-	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
-	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
-	snd_cs4231_out(chip, left_reg, val1);
-	snd_cs4231_out(chip, right_reg, val2);
-	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	return change;
-}
-
-static struct snd_kcontrol_new snd_cs4231_controls[] = {
-CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
-CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
-CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
-CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
-CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Source",
-	.info = snd_cs4231_info_mux,
-	.get = snd_cs4231_get_mux,
-	.put = snd_cs4231_put_mux,
-},
-CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1)
-};
-                                        
-static struct snd_kcontrol_new snd_opti93x_controls[] = {
-CS4231_DOUBLE("Master Playback Switch", 0,
-	      OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Playback Volume", 0,
-	      OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
-CS4231_DOUBLE("PCM Playback Switch", 0,
-	      CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Playback Volume", 0,
-	      CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("FM Playback Switch", 0,
-	      CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("FM Playback Volume", 0,
-	      CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1),
-CS4231_DOUBLE("Line Playback Switch", 0,
-	      CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Line Playback Volume", 0,
-	      CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1),
-CS4231_DOUBLE("Mic Playback Switch", 0,
-	      OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Mic Playback Volume", 0,
-	      OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
-CS4231_DOUBLE("Mic Boost", 0,
-	      CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-CS4231_DOUBLE("CD Playback Switch", 0,
-	      CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("CD Playback Volume", 0,
-	      CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1),
-CS4231_DOUBLE("Aux Playback Switch", 0,
-	      OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Playback Volume", 0,
-	      OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
-CS4231_DOUBLE("Capture Volume", 0,
-	      CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Source",
-	.info = snd_cs4231_info_mux,
-	.get = snd_cs4231_get_mux,
-	.put = snd_cs4231_put_mux,
-}
-};
-
-int snd_cs4231_mixer(struct snd_cs4231 *chip)
-{
-	struct snd_card *card;
-	unsigned int idx;
-	int err;
-
-	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
-
-	card = chip->card;
-
-	strcpy(card->mixername, chip->pcm->name);
-
-	if (chip->hardware == CS4231_HW_OPTI93X)
-		for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
-			err = snd_ctl_add(card,
-					snd_ctl_new1(&snd_opti93x_controls[idx],
-						     chip));
-			if (err < 0)
-				return err;
-		}
-	else
-		for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
-			err = snd_ctl_add(card,
-					snd_ctl_new1(&snd_cs4231_controls[idx],
-						     chip));
-			if (err < 0)
-				return err;
-		}
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_cs4231_out);
-EXPORT_SYMBOL(snd_cs4231_in);
-EXPORT_SYMBOL(snd_cs4236_ext_out);
-EXPORT_SYMBOL(snd_cs4236_ext_in);
-EXPORT_SYMBOL(snd_cs4231_mce_up);
-EXPORT_SYMBOL(snd_cs4231_mce_down);
-EXPORT_SYMBOL(snd_cs4231_overrange);
-EXPORT_SYMBOL(snd_cs4231_interrupt);
-EXPORT_SYMBOL(snd_cs4231_chip_id);
-EXPORT_SYMBOL(snd_cs4231_create);
-EXPORT_SYMBOL(snd_cs4231_pcm);
-EXPORT_SYMBOL(snd_cs4231_mixer);
-EXPORT_SYMBOL(snd_cs4231_timer);
-EXPORT_SYMBOL(snd_cs4231_info_single);
-EXPORT_SYMBOL(snd_cs4231_get_single);
-EXPORT_SYMBOL(snd_cs4231_put_single);
-EXPORT_SYMBOL(snd_cs4231_info_double);
-EXPORT_SYMBOL(snd_cs4231_get_double);
-EXPORT_SYMBOL(snd_cs4231_put_double);
-
-/*
- *  INIT part
- */
-
-static int __init alsa_cs4231_init(void)
-{
-	return 0;
-}
-
-static void __exit alsa_cs4231_exit(void)
-{
-}
-
-module_init(alsa_cs4231_init)
-module_exit(alsa_cs4231_exit)
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 4d4b8dd..91f9c15 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -26,7 +26,7 @@
 #include <linux/pnp.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
 #include <sound/initval.h>
@@ -134,7 +134,7 @@
 #endif /* CONFIG_PNP */
 
 struct snd_card_cs4236 {
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	struct resource *res_sb_port;
 #ifdef CONFIG_PNP
 	struct pnp_dev *wss;
@@ -239,6 +239,8 @@
 	{ .id = "CSC9836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
 	/* Gallant SC-70P */
 	{ .id = "CSC9837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
+	/* Techmakers MF-4236PW */
+	{ .id = "CSCa736", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
 	/* TerraTec AudioSystem EWS64XL - CS4236B */
 	{ .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" }, { "CSCa803" } } },
 	/* TerraTec AudioSystem EWS64XL - CS4236B */
@@ -396,7 +398,7 @@
 {
 	struct snd_card_cs4236 *acard;
 	struct snd_pcm *pcm;
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	struct snd_opl3 *opl3;
 	int err;
 
@@ -408,41 +410,37 @@
 		}
 
 #ifdef CS4232
-	if ((err = snd_cs4231_create(card,
-				     port[dev],
-				     cport[dev],
-				     irq[dev],
-				     dma1[dev],
-				     dma2[dev],
-				     CS4231_HW_DETECT,
-				     0,
-				     &chip)) < 0)
+	err = snd_wss_create(card, port[dev], cport[dev],
+			     irq[dev],
+			     dma1[dev], dma2[dev],
+			     WSS_HW_DETECT, 0, &chip);
+	if (err < 0)
 		return err;
 	acard->chip = chip;
 
-	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0)
+	err = snd_wss_pcm(chip, 0, &pcm);
+	if (err < 0)
 		return err;
 
-	if ((err = snd_cs4231_mixer(chip)) < 0)
+	err = snd_wss_mixer(chip);
+	if (err < 0)
 		return err;
 
 #else /* CS4236 */
-	if ((err = snd_cs4236_create(card,
-				     port[dev],
-				     cport[dev],
-				     irq[dev],
-				     dma1[dev],
-				     dma2[dev],
-				     CS4231_HW_DETECT,
-				     0,
-				     &chip)) < 0)
+	err = snd_cs4236_create(card,
+				port[dev], cport[dev],
+				irq[dev], dma1[dev], dma2[dev],
+				WSS_HW_DETECT, 0, &chip);
+	if (err < 0)
 		return err;
 	acard->chip = chip;
 
-	if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0)
+	err = snd_cs4236_pcm(chip, 0, &pcm);
+	if (err < 0)
 		return err;
 
-	if ((err = snd_cs4236_mixer(chip)) < 0)
+	err = snd_cs4236_mixer(chip);
+	if (err < 0)
 		return err;
 #endif
 	strcpy(card->driver, pcm->name);
@@ -455,7 +453,8 @@
 	if (dma2[dev] >= 0)
 		sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]);
 
-	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0)
+	err = snd_wss_timer(chip, 0, NULL);
+	if (err < 0)
 		return err;
 
 	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index de71910..6a85fdc 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -85,7 +85,7 @@
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/asoundef.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -121,13 +121,14 @@
  *
  */
 
-static void snd_cs4236_ctrl_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val)
+static void snd_cs4236_ctrl_out(struct snd_wss *chip,
+				unsigned char reg, unsigned char val)
 {
 	outb(reg, chip->cport + 3);
 	outb(chip->cimage[reg] = val, chip->cport + 4);
 }
 
-static unsigned char snd_cs4236_ctrl_in(struct snd_cs4231 *chip, unsigned char reg)
+static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg)
 {
 	outb(reg, chip->cport + 3);
 	return inb(chip->cport + 4);
@@ -180,44 +181,52 @@
 	}
 }
 
-static void snd_cs4236_playback_format(struct snd_cs4231 *chip, struct snd_pcm_hw_params *params, unsigned char pdfr)
+static void snd_cs4236_playback_format(struct snd_wss *chip,
+				       struct snd_pcm_hw_params *params,
+				       unsigned char pdfr)
 {
 	unsigned long flags;
 	unsigned char rate = divisor_to_rate_register(params->rate_den);
 	
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* set fast playback format change and clean playback FIFO */
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10);
-	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
+	snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+		    chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+	snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
+	snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+		    chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
 	snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
 
-static void snd_cs4236_capture_format(struct snd_cs4231 *chip, struct snd_pcm_hw_params *params, unsigned char cdfr)
+static void snd_cs4236_capture_format(struct snd_wss *chip,
+				      struct snd_pcm_hw_params *params,
+				      unsigned char cdfr)
 {
 	unsigned long flags;
 	unsigned char rate = divisor_to_rate_register(params->rate_den);
 	
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* set fast capture format change and clean capture FIFO */
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20);
-	snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
+	snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+		    chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+	snd_wss_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
+	snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+		    chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
 	snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
 
 #ifdef CONFIG_PM
 
-static void snd_cs4236_suspend(struct snd_cs4231 *chip)
+static void snd_cs4236_suspend(struct snd_wss *chip)
 {
 	int reg;
 	unsigned long flags;
 	
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	for (reg = 0; reg < 32; reg++)
-		chip->image[reg] = snd_cs4231_in(chip, reg);
+		chip->image[reg] = snd_wss_in(chip, reg);
 	for (reg = 0; reg < 18; reg++)
 		chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
 	for (reg = 2; reg < 9; reg++)
@@ -225,12 +234,12 @@
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
 
-static void snd_cs4236_resume(struct snd_cs4231 *chip)
+static void snd_cs4236_resume(struct snd_wss *chip)
 {
 	int reg;
 	unsigned long flags;
 	
-	snd_cs4231_mce_up(chip);
+	snd_wss_mce_up(chip);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	for (reg = 0; reg < 32; reg++) {
 		switch (reg) {
@@ -240,7 +249,7 @@
 		case 29:	/* why? CS4235 - master right */
 			break;
 		default:
-			snd_cs4231_out(chip, reg, chip->image[reg]);
+			snd_wss_out(chip, reg, chip->image[reg]);
 			break;
 		}
 	}
@@ -255,7 +264,7 @@
 		}
 	}
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_down(chip);
+	snd_wss_mce_down(chip);
 }
 
 #endif /* CONFIG_PM */
@@ -266,24 +275,26 @@
 		      int irq, int dma1, int dma2,
 		      unsigned short hardware,
 		      unsigned short hwshare,
-		      struct snd_cs4231 ** rchip)
+		      struct snd_wss **rchip)
 {
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	unsigned char ver1, ver2;
 	unsigned int reg;
 	int err;
 
 	*rchip = NULL;
-	if (hardware == CS4231_HW_DETECT)
-		hardware = CS4231_HW_DETECT3;
+	if (hardware == WSS_HW_DETECT)
+		hardware = WSS_HW_DETECT3;
 	if (cport < 0x100) {
 		snd_printk("please, specify control port for CS4236+ chips\n");
 		return -ENODEV;
 	}
-	if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0)
+	err = snd_wss_create(card, port, cport,
+			     irq, dma1, dma2, hardware, hwshare, &chip);
+	if (err < 0)
 		return err;
 
-	if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) {
+	if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
 	        snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%x\n",chip->hardware);
 		snd_device_free(card, chip);
 		return -ENODEV;
@@ -330,20 +341,20 @@
 		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
 
         /* initialize compatible but more featured registers */
-	snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40);
-	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40);
-	snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
-	snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
-	snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
-	snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
-	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
-	snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff);
-	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+	snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
+	snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
+	snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
+	snd_wss_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
+	snd_wss_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
+	snd_wss_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
+	snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
+	snd_wss_out(chip, CS4231_LEFT_LINE_IN, 0xff);
+	snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
 	switch (chip->hardware) {
-	case CS4231_HW_CS4235:
-	case CS4231_HW_CS4239:
-		snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff);
-		snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff);
+	case WSS_HW_CS4235:
+	case WSS_HW_CS4239:
+		snd_wss_out(chip, CS4235_LEFT_MASTER, 0xff);
+		snd_wss_out(chip, CS4235_RIGHT_MASTER, 0xff);
 		break;
 	}
 
@@ -351,12 +362,13 @@
 	return 0;
 }
 
-int snd_cs4236_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm)
+int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
 {
 	struct snd_pcm *pcm;
 	int err;
 	
-	if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0)
+	err = snd_wss_pcm(chip, device, &pcm);
+	if (err < 0)
 		return err;
 	pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
 	if (rpcm)
@@ -387,7 +399,7 @@
 
 static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int reg = kcontrol->private_value & 0xff;
 	int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -404,7 +416,7 @@
 
 static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int reg = kcontrol->private_value & 0xff;
 	int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -433,7 +445,7 @@
 
 static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int reg = kcontrol->private_value & 0xff;
 	int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -450,7 +462,7 @@
 
 static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int reg = kcontrol->private_value & 0xff;
 	int shift = (kcontrol->private_value >> 8) & 0xff;
@@ -490,7 +502,7 @@
 
 static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int left_reg = kcontrol->private_value & 0xff;
 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -512,7 +524,7 @@
 
 static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int left_reg = kcontrol->private_value & 0xff;
 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -555,7 +567,7 @@
 
 static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int left_reg = kcontrol->private_value & 0xff;
 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -577,7 +589,7 @@
 
 static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int left_reg = kcontrol->private_value & 0xff;
 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
@@ -600,7 +612,7 @@
 	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
 	val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
 	change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
-	snd_cs4231_out(chip, left_reg, val1);
+	snd_wss_out(chip, left_reg, val1);
 	snd_cs4236_ext_out(chip, right_reg, val2);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	return change;
@@ -619,7 +631,7 @@
 
 static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	
 	spin_lock_irqsave(&chip->reg_lock, flags);
@@ -631,7 +643,7 @@
 
 static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int change;
 	unsigned short val1, val2;
@@ -678,7 +690,7 @@
 
 static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	
 	spin_lock_irqsave(&chip->reg_lock, flags);
@@ -690,7 +702,7 @@
 
 static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int change;
 	unsigned short val1, val2;
@@ -701,108 +713,160 @@
 	val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
 	val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
 	change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
-	snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1);
-	snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2);
+	snd_wss_out(chip, CS4235_LEFT_MASTER, val1);
+	snd_wss_out(chip, CS4235_RIGHT_MASTER, val2);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	return change;
 }
 
 static struct snd_kcontrol_new snd_cs4236_controls[] = {
 
-CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_DOUBLE("Master Digital Playback Switch", 0,
+		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0,
+		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
 CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
 
-CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE("Capture Boost Volume", 0,
+		CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
 
-CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("PCM Playback Switch", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
 
-CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
-CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
+CS4236_DOUBLE("DSP Playback Switch", 0,
+		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
+CS4236_DOUBLE("DSP Playback Volume", 0,
+		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
 
-CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
-CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
+CS4236_DOUBLE("FM Playback Switch", 0,
+		CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
+CS4236_DOUBLE("FM Playback Volume", 0,
+		CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
 
-CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
-CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
+CS4236_DOUBLE("Wavetable Playback Switch", 0,
+		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+CS4236_DOUBLE("Wavetable Playback Volume", 0,
+		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
 
-CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
-CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
+WSS_DOUBLE("Synth Playback Switch", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Synth Volume", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE("Synth Capture Switch", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+WSS_DOUBLE("Synth Capture Bypass", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
 
-CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
-CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Playback Switch", 0,
+		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_DOUBLE("Mic Capture Switch", 0,
+		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
 CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
-CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
+CS4236_DOUBLE("Mic Playback Boost", 0,
+		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
 
-CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Line Volume", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Line Capture Switch", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("Line Capture Bypass", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
 
-CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("CD Playback Switch", 0,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("CD Volume", 0,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("CD Capture Switch", 0,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
 
-CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
-CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
-CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
+CS4236_DOUBLE1("Mono Output Playback Switch", 0,
+		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+CS4236_DOUBLE1("Mono Playback Switch", 0,
+		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
 
-CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+WSS_DOUBLE("Capture Volume", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+WSS_DOUBLE("Analog Loopback Capture Switch", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 
-CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
-CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
+WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+CS4236_DOUBLE1("Digital Loopback Playback Volume", 0,
+		CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
 };
 
 static struct snd_kcontrol_new snd_cs4235_controls[] = {
 
-CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
+WSS_DOUBLE("Master Switch", 0,
+		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
+WSS_DOUBLE("Master Volume", 0,
+		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
 
 CS4235_OUTPUT_ACCU("Playback Volume", 0),
 
-CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
-CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
+CS4236_DOUBLE("Master Digital Playback Switch", 0,
+		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
+CS4236_DOUBLE("Master Digital Capture Switch", 0,
+		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
 CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
 
-CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
-CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE("Master Digital Playback Switch", 1,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Master Digital Capture Switch", 1,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
+WSS_DOUBLE("Master Digital Volume", 1,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
 
-CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
+CS4236_DOUBLE("Capture Volume", 0,
+		CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
 
-CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("PCM Switch", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
 
 CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
 
 CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
 
-CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
+CS4236_DOUBLE("Wavetable Switch", 0,
+		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
 
-CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
-CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
+CS4236_DOUBLE("Mic Capture Switch", 0,
+		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
+CS4236_DOUBLE("Mic Playback Switch", 0,
+		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
 CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
 CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
 
-CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
-CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Aux Playback Switch", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Capture Switch", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("Aux Volume", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
 
-CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
-CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Aux Playback Switch", 1,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Capture Switch", 1,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
+WSS_DOUBLE("Aux Volume", 1,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
 
-CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
+CS4236_DOUBLE1("Master Mono Switch", 0,
+		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
 
-CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
-CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
+CS4236_DOUBLE1("Mono Switch", 0,
+		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
+WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
 
-CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
+WSS_DOUBLE("Analog Loopback Switch", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 };
 
 #define CS4236_IEC958_ENABLE(xname, xindex) \
@@ -813,14 +877,14 @@
 
 static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
 #if 0
 	printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
-			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+			snd_wss_in(chip, CS4231_ALT_FEATURE_1),
 			snd_cs4236_ctrl_in(chip, 3),
 			snd_cs4236_ctrl_in(chip, 4),
 			snd_cs4236_ctrl_in(chip, 5),
@@ -833,7 +897,7 @@
 
 static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 	unsigned long flags;
 	int change;
 	unsigned short enable, val;
@@ -841,23 +905,23 @@
 	enable = ucontrol->value.integer.value[0] & 1;
 
 	mutex_lock(&chip->mce_mutex);
-	snd_cs4231_mce_up(chip);
+	snd_wss_mce_up(chip);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
 	change = val != chip->image[CS4231_ALT_FEATURE_1];
-	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val);
+	snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
 	val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
 	snd_cs4236_ctrl_out(chip, 4, val);
 	udelay(100);
 	val &= ~0x40;
 	snd_cs4236_ctrl_out(chip, 4, val);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
-	snd_cs4231_mce_down(chip);
+	snd_wss_mce_down(chip);
 	mutex_unlock(&chip->mce_mutex);
 
 #if 0
 	printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
-			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1),
+			snd_wss_in(chip, CS4231_ALT_FEATURE_1),
 			snd_cs4236_ctrl_in(chip, 3),
 			snd_cs4236_ctrl_in(chip, 4),
 			snd_cs4236_ctrl_in(chip, 5),
@@ -896,19 +960,20 @@
 CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
 };
 
-int snd_cs4236_mixer(struct snd_cs4231 *chip)
+int snd_cs4236_mixer(struct snd_wss *chip)
 {
 	struct snd_card *card;
 	unsigned int idx, count;
 	int err;
 	struct snd_kcontrol_new *kcontrol;
 
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip || !chip->card))
+		return -EINVAL;
 	card = chip->card;
-	strcpy(card->mixername, snd_cs4231_chip_id(chip));
+	strcpy(card->mixername, snd_wss_chip_id(chip));
 
-	if (chip->hardware == CS4231_HW_CS4235 ||
-	    chip->hardware == CS4231_HW_CS4239) {
+	if (chip->hardware == WSS_HW_CS4235 ||
+	    chip->hardware == WSS_HW_CS4239) {
 		for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) {
 			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)
 				return err;
@@ -920,16 +985,16 @@
 		}
 	}
 	switch (chip->hardware) {
-	case CS4231_HW_CS4235:
-	case CS4231_HW_CS4239:
+	case WSS_HW_CS4235:
+	case WSS_HW_CS4239:
 		count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235);
 		kcontrol = snd_cs4236_3d_controls_cs4235;
 		break;
-	case CS4231_HW_CS4237B:
+	case WSS_HW_CS4237B:
 		count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237);
 		kcontrol = snd_cs4236_3d_controls_cs4237;
 		break;
-	case CS4231_HW_CS4238B:
+	case WSS_HW_CS4238B:
 		count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238);
 		kcontrol = snd_cs4236_3d_controls_cs4238;
 		break;
@@ -941,8 +1006,8 @@
 		if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
 			return err;
 	}
-	if (chip->hardware == CS4231_HW_CS4237B ||
-	    chip->hardware == CS4231_HW_CS4238B) {
+	if (chip->hardware == WSS_HW_CS4237B ||
+	    chip->hardware == WSS_HW_CS4238B) {
 		for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) {
 			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)
 				return err;
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 1e1e575..4fbb508 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -1009,7 +1009,8 @@
 	int err;
 	unsigned char reg, val;
 
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip || !chip->card))
+		return -EINVAL;
 
 	card = chip->card;
 
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index cccc16c..12eb98f 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -276,9 +276,11 @@
 	static unsigned char dmas[8] =
 		{6, 1, 0, 2, 0, 3, 4, 5};
 
-	snd_assert(gus != NULL, return -EINVAL);
+	if (snd_BUG_ON(!gus))
+		return -EINVAL;
 	card = gus->card;
-	snd_assert(card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card))
+		return -EINVAL;
 
 	gus->mix_cntrl_reg &= 0xf8;
 	gus->mix_cntrl_reg |= 0x01;	/* disable MIC, LINE IN, enable LINE OUT */
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
index ebdb334..0dd4341 100644
--- a/sound/isa/gus/gus_mixer.c
+++ b/sound/isa/gus/gus_mixer.c
@@ -161,9 +161,11 @@
 	unsigned int idx, max;
 	int err;
 
-	snd_assert(gus != NULL, return -EINVAL);
+	if (snd_BUG_ON(!gus))
+		return -EINVAL;
 	card = gus->card;
-	snd_assert(card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card))
+		return -EINVAL;
 
 	if (gus->ics_flag)
 		snd_component_add(card, "ICS2101");
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index 99731dc..38510ae 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -352,8 +352,10 @@
 	
 	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
 	len = samples_to_bytes(runtime, count);
-	snd_assert(bpos <= pcmp->dma_size, return -EIO);
-	snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+	if (snd_BUG_ON(bpos > pcmp->dma_size))
+		return -EIO;
+	if (snd_BUG_ON(bpos + len > pcmp->dma_size))
+		return -EIO;
 	if (copy_from_user(runtime->dma_area + bpos, src, len))
 		return -EFAULT;
 	if (snd_gf1_pcm_use_dma && len > 32) {
@@ -381,8 +383,10 @@
 	
 	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
 	len = samples_to_bytes(runtime, count);
-	snd_assert(bpos <= pcmp->dma_size, return -EIO);
-	snd_assert(bpos + len <= pcmp->dma_size, return -EIO);
+	if (snd_BUG_ON(bpos > pcmp->dma_size))
+		return -EIO;
+	if (snd_BUG_ON(bpos + len > pcmp->dma_size))
+		return -EIO;
 	snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count);
 	if (snd_gf1_pcm_use_dma && len > 32) {
 		return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index f87c623..f94c197 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -28,7 +28,7 @@
 #include <asm/dma.h>
 #include <sound/core.h>
 #include <sound/gus.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #define SNDRV_LEGACY_FIND_FREE_IRQ
 #define SNDRV_LEGACY_FIND_FREE_DMA
 #include <sound/initval.h>
@@ -75,7 +75,7 @@
 	int irq;
 	struct snd_card *card;
 	struct snd_gus_card *gus;
-	struct snd_cs4231 *cs4231;
+	struct snd_wss *wss;
 	unsigned short gus_status_reg;
 	unsigned short pcm_status_reg;
 };
@@ -117,7 +117,7 @@
 		}
 		if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */
 			handled = 1;
-			snd_cs4231_interrupt(irq, maxcard->cs4231);
+			snd_wss_interrupt(irq, maxcard->wss);
 			loop++;
 		}
 	} while (loop && --max > 0);
@@ -140,10 +140,7 @@
 	outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
 }
 
-#define CS4231_PRIVATE( left, right, shift, mute ) \
-			((left << 24)|(right << 16)|(shift<<8)|mute)
-
-static int __devinit snd_gusmax_mixer(struct snd_cs4231 *chip)
+static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
 {
 	struct snd_card *card = chip->card;
 	struct snd_ctl_elem_id id1, id2;
@@ -214,7 +211,7 @@
 	int xirq, xdma1, xdma2, err;
 	struct snd_card *card;
 	struct snd_gus_card *gus = NULL;
-	struct snd_cs4231 *cs4231;
+	struct snd_wss *wss;
 	struct snd_gusmax *maxcard;
 
 	card = snd_card_new(index[dev], id[dev], THIS_MODULE,
@@ -301,33 +298,39 @@
 	}
 	maxcard->irq = xirq;
 	
-	if ((err = snd_cs4231_create(card,
-				     gus->gf1.port + 0x10c, -1, xirq,
-				     xdma2 < 0 ? xdma1 : xdma2, xdma1,
-				     CS4231_HW_DETECT,
-				     CS4231_HWSHARE_IRQ |
-				     CS4231_HWSHARE_DMA1 |
-				     CS4231_HWSHARE_DMA2,
-				     &cs4231)) < 0)
+	err = snd_wss_create(card,
+			     gus->gf1.port + 0x10c, -1, xirq,
+			     xdma2 < 0 ? xdma1 : xdma2, xdma1,
+			     WSS_HW_DETECT,
+			     WSS_HWSHARE_IRQ |
+			     WSS_HWSHARE_DMA1 |
+			     WSS_HWSHARE_DMA2,
+			     &wss);
+	if (err < 0)
 		goto _err;
 
-	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0)
+	err = snd_wss_pcm(wss, 0, NULL);
+	if (err < 0)
 		goto _err;
 
-	if ((err = snd_cs4231_mixer(cs4231)) < 0)
+	err = snd_wss_mixer(wss);
+	if (err < 0)
 		goto _err;
 
-	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0)
+	err = snd_wss_timer(wss, 2, NULL);
+	if (err < 0)
 		goto _err;
 
 	if (pcm_channels[dev] > 0) {
 		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)
 			goto _err;
 	}
-	if ((err = snd_gusmax_mixer(cs4231)) < 0)
+	err = snd_gusmax_mixer(wss);
+	if (err < 0)
 		goto _err;
 
-	if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0)
+	err = snd_gf1_rawmidi_new(gus, 0, NULL);
+	if (err < 0)
 		goto _err;
 
 	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1);
@@ -336,11 +339,12 @@
 
 	snd_card_set_dev(card, pdev);
 
-	if ((err = snd_card_register(card)) < 0)
+	err = snd_card_register(card);
+	if (err < 0)
 		goto _err;
 		
 	maxcard->gus = gus;
-	maxcard->cs4231 = cs4231;
+	maxcard->wss = wss;
 
 	dev_set_drvdata(pdev, card);
 	return 0;
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index ca0d7ac..5faecfb 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -32,7 +32,7 @@
 #include <asm/dma.h>
 #include <sound/core.h>
 #include <sound/gus.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #ifdef SNDRV_STB
 #include <sound/tea6330t.h>
 #endif
@@ -118,7 +118,7 @@
 	int irq;
 	struct snd_card *card;
 	struct snd_gus_card *gus;
-	struct snd_cs4231 *cs4231;
+	struct snd_wss *wss;
 #ifdef SNDRV_STB
 	struct resource *i2c_res;
 #endif
@@ -312,7 +312,7 @@
 		}
 		if (inb(iwcard->pcm_status_reg) & 0x01) {	/* IRQ bit is set? */
 			handled = 1;
-			snd_cs4231_interrupt(irq, iwcard->cs4231);
+			snd_wss_interrupt(irq, iwcard->wss);
 			loop++;
 		}
 	} while (loop && --max > 0);
@@ -498,13 +498,17 @@
 }
 
 static struct snd_kcontrol_new snd_interwave_controls[] = {
-CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
-CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
-CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
+WSS_DOUBLE("Master Playback Switch", 0,
+		CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Master Playback Volume", 0,
+		CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Mic Playback Switch", 0,
+		CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Mic Playback Volume", 0,
+		CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
 };
 
-static int __devinit snd_interwave_mixer(struct snd_cs4231 *chip)
+static int __devinit snd_interwave_mixer(struct snd_wss *chip)
 {
 	struct snd_card *card = chip->card;
 	struct snd_ctl_elem_id id1, id2;
@@ -527,10 +531,10 @@
 	for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++)
 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0)
 			return err;
-	snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
-	snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
-	snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
-	snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
+	snd_wss_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
+	snd_wss_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
+	snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
+	snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
 	/* reassign AUXA to SYNTHESIZER */
 	strcpy(id1.name, "Aux Playback Switch");
 	strcpy(id2.name, "Synth Playback Switch");
@@ -642,7 +646,7 @@
 {
 	int xirq, xdma1, xdma2;
 	struct snd_interwave *iwcard = card->private_data;
-	struct snd_cs4231 *cs4231;
+	struct snd_wss *wss;
 	struct snd_gus_card *gus;
 #ifdef SNDRV_STB
 	struct snd_i2c_bus *i2c_bus;
@@ -684,33 +688,39 @@
 	}
 	iwcard->irq = xirq;
 
-	if ((err = snd_cs4231_create(card,
-				     gus->gf1.port + 0x10c, -1, xirq,
-				     xdma2 < 0 ? xdma1 : xdma2, xdma1,
-				     CS4231_HW_INTERWAVE,
-				     CS4231_HWSHARE_IRQ |
-				     CS4231_HWSHARE_DMA1 |
-				     CS4231_HWSHARE_DMA2,
-				     &cs4231)) < 0)
+	err = snd_wss_create(card,
+			     gus->gf1.port + 0x10c, -1, xirq,
+			     xdma2 < 0 ? xdma1 : xdma2, xdma1,
+			     WSS_HW_INTERWAVE,
+			     WSS_HWSHARE_IRQ |
+			     WSS_HWSHARE_DMA1 |
+			     WSS_HWSHARE_DMA2,
+			     &wss);
+	if (err < 0)
 		return err;
 
-	if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0)
+	err = snd_wss_pcm(wss, 0, &pcm);
+	if (err < 0)
 		return err;
 
 	sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
 	strcat(pcm->name, " (codec)");
 
-	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0)
+	err = snd_wss_timer(wss, 2, NULL);
+	if (err < 0)
 		return err;
 
-	if ((err = snd_cs4231_mixer(cs4231)) < 0)
+	err = snd_wss_mixer(wss);
+	if (err < 0)
 		return err;
 
 	if (pcm_channels[dev] > 0) {
-		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)
+		err = snd_gf1_pcm_new(gus, 1, 1, NULL);
+		if (err < 0)
 			return err;
 	}
-	if ((err = snd_interwave_mixer(cs4231)) < 0)
+	err = snd_interwave_mixer(wss);
+	if (err < 0)
 		return err;
 
 #ifdef SNDRV_STB
@@ -754,10 +764,11 @@
 	if (xdma2 >= 0)
 		sprintf(card->longname + strlen(card->longname), "&%d", xdma2);
 
-	if ((err = snd_card_register(card)) < 0)
+	err = snd_card_register(card);
+	if (err < 0)
 		return err;
 	
-	iwcard->cs4231 = cs4231;
+	iwcard->wss = wss;
 	iwcard->gus = gus;
 	return 0;
 }
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 854a9f7..58c972b 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -28,7 +28,7 @@
 #include <linux/pnp.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
 #include <sound/initval.h>
@@ -124,7 +124,6 @@
 #define OPL3SA2_PM_D3	(OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX)
 
 struct snd_opl3sa2 {
-	struct snd_card *card;
 	int version;		/* 2 or 3 */
 	unsigned long port;	/* control port */
 	struct resource *res_port; /* control port resource */
@@ -133,7 +132,7 @@
 	spinlock_t reg_lock;
 	struct snd_hwdep *synth;
 	struct snd_rawmidi *rmidi;
-	struct snd_cs4231 *cs4231;
+	struct snd_wss *wss;
 	unsigned char ctlregs[0x20];
 	int ymode;		/* SL added */
 	struct snd_kcontrol *master_switch;
@@ -222,14 +221,13 @@
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
 
-static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip)
+static int __devinit snd_opl3sa2_detect(struct snd_card *card)
 {
-	struct snd_card *card;
+	struct snd_opl3sa2 *chip = card->private_data;
 	unsigned long port;
 	unsigned char tmp, tmp1;
 	char str[2];
 
-	card = chip->card;
 	port = chip->port;
 	if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) {
 		snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
@@ -298,12 +296,14 @@
 static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
 {
 	unsigned short status;
-	struct snd_opl3sa2 *chip = dev_id;
+	struct snd_card *card = dev_id;
+	struct snd_opl3sa2 *chip;
 	int handled = 0;
 
-	if (chip == NULL || chip->card == NULL)
+	if (card == NULL)
 		return IRQ_NONE;
 
+	chip = card->private_data;
 	status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS);
 
 	if (status & 0x20) {
@@ -318,7 +318,7 @@
 
 	if (status & 0x07) {	/* TI,CI,PI */
 		handled = 1;
-		snd_cs4231_interrupt(irq, chip->cs4231);
+		snd_wss_interrupt(irq, chip->wss);
 	}
 
 	if (status & 0x40) { /* hardware volume change */
@@ -327,8 +327,10 @@
 		snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT);
 		snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT);
 		if (chip->master_switch && chip->master_volume) {
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+					&chip->master_switch->id);
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+					&chip->master_volume->id);
 		}
 	}
 	return IRQ_RETVAL(handled);
@@ -336,29 +338,18 @@
 
 #define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_opl3sa2_info_single, \
+  .info = snd_wss_info_single, \
   .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 #define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
   .name = xname, .index = xindex, \
-  .info = snd_opl3sa2_info_single, \
+  .info = snd_wss_info_single, \
   .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
   .tlv = { .p = (xtlv) } }
 
-static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
 static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
@@ -402,29 +393,18 @@
 
 #define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_opl3sa2_info_double, \
+  .info = snd_wss_info_double, \
   .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 #define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
   .name = xname, .index = xindex, \
-  .info = snd_opl3sa2_info_double, \
+  .info = snd_wss_info_double, \
   .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
   .tlv = { .p = (xtlv) } }
 
-static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
 static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
@@ -512,9 +492,9 @@
 	chip->master_volume = NULL;
 }
 
-static int __devinit snd_opl3sa2_mixer(struct snd_opl3sa2 *chip)
+static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
 {
-	struct snd_card *card = chip->card;
+	struct snd_opl3sa2 *chip = card->private_data;
 	struct snd_ctl_elem_id id1, id2;
 	struct snd_kcontrol *kctl;
 	unsigned int idx;
@@ -573,7 +553,7 @@
 	struct snd_opl3sa2 *chip = card->private_data;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-	chip->cs4231->suspend(chip->cs4231);
+	chip->wss->suspend(chip->wss);
 	/* power down */
 	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3);
 
@@ -597,8 +577,8 @@
 		for (i = 0x12; i <= 0x16; i++)
 			snd_opl3sa2_write(chip, i, chip->ctlregs[i]);
 	}
-	/* restore cs4231 */
-	chip->cs4231->resume(chip->cs4231);
+	/* restore wss */
+	chip->wss->resume(chip->wss);
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
@@ -650,7 +630,6 @@
 	chip = card->private_data;
 	spin_lock_init(&chip->reg_lock);
 	chip->irq = -1;
-	chip->card = card;
 	card->private_free = snd_opl3sa2_free;
 	return card;
 }
@@ -659,7 +638,7 @@
 {
 	int xirq, xdma1, xdma2;
 	struct snd_opl3sa2 *chip;
-	struct snd_cs4231 *cs4231;
+	struct snd_wss *wss;
 	struct snd_opl3 *opl3;
 	int err;
 
@@ -672,30 +651,36 @@
 	xdma2 = dma2[dev];
 	if (xdma2 < 0)
 		chip->single_dma = 1;
-	if ((err = snd_opl3sa2_detect(chip)) < 0)
+	err = snd_opl3sa2_detect(card);
+	if (err < 0)
 		return err;
-	if (request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED, "OPL3-SA2", chip)) {
+	err = request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED,
+			  "OPL3-SA2", card);
+	if (err) {
 		snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
 		return -ENODEV;
 	}
 	chip->irq = xirq;
-	if ((err = snd_cs4231_create(card,
-				     wss_port[dev] + 4, -1,
-				     xirq, xdma1, xdma2,
-				     CS4231_HW_OPL3SA2,
-				     CS4231_HWSHARE_IRQ,
-				     &cs4231)) < 0) {
+	err = snd_wss_create(card,
+			     wss_port[dev] + 4, -1,
+			     xirq, xdma1, xdma2,
+			     WSS_HW_OPL3SA2, WSS_HWSHARE_IRQ, &wss);
+	if (err < 0) {
 		snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
 		return err;
 	}
-	chip->cs4231 = cs4231;
-	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0)
+	chip->wss = wss;
+	err = snd_wss_pcm(wss, 0, NULL);
+	if (err < 0)
 		return err;
-	if ((err = snd_cs4231_mixer(cs4231)) < 0)
+	err = snd_wss_mixer(wss);
+	if (err < 0)
 		return err;
-	if ((err = snd_opl3sa2_mixer(chip)) < 0)
+	err = snd_opl3sa2_mixer(card);
+	if (err < 0)
 		return err;
-	if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0)
+	err = snd_wss_timer(wss, 0, NULL);
+	if (err < 0)
 		return err;
 	if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) {
 		if ((err = snd_opl3_create(card, fm_port[dev],
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 2a1e2f5..440755c 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -32,7 +32,7 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <sound/core.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/opl4.h>
 #include <sound/control.h>
@@ -675,7 +675,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(miro != NULL && miro->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!miro || !miro->card))
+		return -EINVAL;
 
 	card = miro->card;
 
@@ -1221,7 +1222,7 @@
 
 	int error;
 	struct snd_miro *miro;
-	struct snd_cs4231 *codec;
+	struct snd_wss *codec;
 	struct snd_timer *timer;
 	struct snd_card *card;
 	struct snd_pcm *pcm;
@@ -1310,29 +1311,32 @@
 		}
 	}
 
-	if ((error = snd_miro_configure(miro))) {
+	error = snd_miro_configure(miro);
+	if (error) {
 		snd_card_free(card);
 		return error;
 	}
 
-	if ((error = snd_cs4231_create(card, miro->wss_base + 4, -1,
-				       miro->irq, miro->dma1, miro->dma2,
-				       CS4231_HW_AD1845,
-				       0,
-				       &codec)) < 0) {
+	error = snd_wss_create(card, miro->wss_base + 4, -1,
+				miro->irq, miro->dma1, miro->dma2,
+				WSS_HW_AD1845, 0, &codec);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
 
-	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) {
+	error = snd_wss_pcm(codec, 0, &pcm);
+	if (error < 0)  {
 		snd_card_free(card);
 		return error;
 	}
-	if ((error = snd_cs4231_mixer(codec)) < 0) {
+	error = snd_wss_mixer(codec);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
-	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) {
+	error = snd_wss_timer(codec, 0, &timer);
+	if (error < 0) {
 		snd_card_free(card);
 		return error;
 	}
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 0797ca4..19706b0 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -33,11 +33,7 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <sound/core.h>
-#if defined(CS4231) || defined(OPTi93X)
-#include <sound/cs4231.h>
-#else
-#include <sound/ad1848.h>
-#endif	/* CS4231 */
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
 #ifndef OPTi93X
@@ -139,7 +135,7 @@
 	unsigned long mc_base_size;
 #ifdef OPTi93X
 	unsigned long mc_indir_index;
-	struct snd_cs4231 *codec;
+	struct snd_wss *codec;
 #endif	/* OPTi93X */
 	unsigned long pwd_reg;
 
@@ -148,9 +144,7 @@
 	long wss_base;
 	int irq;
 	int dma1;
-#if defined(CS4231) || defined(OPTi93X)
 	int dma2;
-#endif	/* CS4231 || OPTi93X */
 
 	long fm_port;
 
@@ -225,9 +219,7 @@
 	chip->wss_base = -1;
 	chip->irq = -1;
 	chip->dma1 = -1;
-#if defined(CS4231) || defined (OPTi93X)
 	chip->dma2 = -1;
-#endif 	/* CS4231 || OPTi93X */
 	chip->fm_port = -1;
 	chip->mpu_port = -1;
 	chip->mpu_irq = -1;
@@ -562,7 +554,7 @@
 
 static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
 {
-	struct snd_cs4231 *codec = dev_id;
+	struct snd_wss *codec = dev_id;
 	struct snd_opti9xx *chip = codec->card->private_data;
 	unsigned char status;
 
@@ -570,7 +562,7 @@
 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
 		snd_pcm_period_elapsed(codec->playback_substream);
 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
-		snd_cs4231_overrange(codec);
+		snd_wss_overrange(codec);
 		snd_pcm_period_elapsed(codec->capture_substream);
 	}
 	outb(0x00, OPTi93X_PORT(codec, STATUS));
@@ -691,7 +683,7 @@
         
 	if (chip) {
 #ifdef OPTi93X
-		struct snd_cs4231 *codec = chip->codec;
+		struct snd_wss *codec = chip->codec;
 		if (codec && codec->irq > 0) {
 			disable_irq(codec->irq);
 			free_irq(codec->irq, codec);
@@ -706,14 +698,10 @@
 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
 	int error;
 	struct snd_opti9xx *chip = card->private_data;
-#if defined(CS4231) || defined(OPTi93X)
-	struct snd_cs4231 *codec;
+	struct snd_wss *codec;
 #ifdef CS4231
 	struct snd_timer *timer;
 #endif
-#else
-	struct snd_ad1848 *codec;
-#endif
 	struct snd_pcm *pcm;
 	struct snd_rawmidi *rmidi;
 	struct snd_hwdep *synth;
@@ -731,38 +719,46 @@
 	chip->dma1 = dma1;
 #if defined(CS4231) || defined(OPTi93X)
 	chip->dma2 = dma2;
+#else
+	chip->dma2 = -1;
 #endif
 
 	if (chip->wss_base == SNDRV_AUTO_PORT) {
-		if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
+		chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
+		if (chip->wss_base < 0) {
 			snd_printk("unable to find a free WSS port\n");
 			return -EBUSY;
 		}
 	}
-	if ((error = snd_opti9xx_configure(chip)))
+	error = snd_opti9xx_configure(chip);
+	if (error)
 		return error;
 
-#if defined(CS4231) || defined(OPTi93X)
-	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
-				       chip->irq, chip->dma1, chip->dma2,
-#ifdef CS4231
-				       CS4231_HW_DETECT, 0,
-#else /* OPTi93x */
-				       CS4231_HW_OPTI93X, CS4231_HWSHARE_IRQ,
+	error = snd_wss_create(card, chip->wss_base + 4, -1,
+			       chip->irq, chip->dma1, chip->dma2,
+#ifdef OPTi93X
+			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
+#else
+			       WSS_HW_DETECT, 0,
 #endif
-				       &codec)) < 0)
+			       &codec);
+	if (error < 0)
 		return error;
 #ifdef OPTi93X
 	chip->codec = codec;
 #endif
-	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0)
+	error = snd_wss_pcm(codec, 0, &pcm);
+	if (error < 0)
 		return error;
-	if ((error = snd_cs4231_mixer(codec)) < 0)
+	error = snd_wss_mixer(codec);
+	if (error < 0)
 		return error;
 #ifdef CS4231
-	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0)
+	error = snd_wss_timer(codec, 0, &timer);
+	if (error < 0)
 		return error;
-#else /* OPTI93X */
+#endif
+#ifdef OPTi93X
 	error = request_irq(chip->irq, snd_opti93x_interrupt,
 			    IRQF_DISABLED, DEV_NAME" - WSS", codec);
 	if (error < 0) {
@@ -770,16 +766,6 @@
 		return error;
 	}
 #endif
-#else
-	if ((error = snd_ad1848_create(card, chip->wss_base + 4,
-				       chip->irq, chip->dma1,
-				       AD1848_HW_DETECT, &codec)) < 0)
-		return error;
-	if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0)
-		return error;
-	if ((error = snd_ad1848_mixer(codec)) < 0)
-		return error;
-#endif
 	strcpy(card->driver, chip->name);
 	sprintf(card->shortname, "OPTi %s", card->driver);
 #if defined(CS4231) || defined(OPTi93X)
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index b35be7d..96678d5 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1023,7 +1023,8 @@
 {
 	int i, err = 0;
 
-	snd_assert(emu != NULL && card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!emu || !card))
+		return -EINVAL;
 
 	spin_lock_init(&emu->control_lock);
 
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index 1be16c9..c99c607 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -156,7 +156,8 @@
 	struct snd_emu8000 *emu;
 
 	emu = rec->hw;
-	snd_assert(sp != NULL, return -EINVAL);
+	if (snd_BUG_ON(!sp))
+		return -EINVAL;
 
 	if (sp->v.size == 0)
 		return 0;
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index 35f3d7b..49037d0 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -198,7 +198,8 @@
 	struct snd_sb_csp_start start_info;
 	int err;
 
-	snd_assert(p != NULL, return -EINVAL);
+	if (snd_BUG_ON(!p))
+		return -EINVAL;
 
 	if (snd_sb_csp_check_version(p))
 		return -ENODEV;
@@ -1046,7 +1047,8 @@
 	struct snd_card *card;
 	int err;
 
-	snd_assert(p != NULL, return -EINVAL);
+	if (snd_BUG_ON(!p))
+		return -EINVAL;
 
 	card = p->chip->card;
 	p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2;
@@ -1071,7 +1073,8 @@
 	struct snd_card *card;
 	unsigned long flags;
 
-	snd_assert(p != NULL, return);
+	if (snd_BUG_ON(!p))
+		return;
 
 	card = p->chip->card;	
 	
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
index f7e8192..2a6cc1c 100644
--- a/sound/isa/sb/sb16_main.c
+++ b/sound/isa/sb/sb16_main.c
@@ -669,7 +669,8 @@
 static int snd_sb16_set_dma_mode(struct snd_sb *chip, int what)
 {
 	if (chip->dma8 < 0 || chip->dma16 < 0) {
-		snd_assert(what == 0, return -EINVAL);
+		if (snd_BUG_ON(what))
+			return -EINVAL;
 		return 0;
 	}
 	if (what == 0) {
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
index fe03bb8..658d557 100644
--- a/sound/isa/sb/sb8_main.c
+++ b/sound/isa/sb/sb8_main.c
@@ -111,7 +111,9 @@
 	switch (chip->hardware) {
 	case SB_HW_PRO:
 		if (runtime->channels > 1) {
-			snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+			if (snd_BUG_ON(rate != SB8_RATE(11025) &&
+				       rate != SB8_RATE(22050)))
+				return -EINVAL;
 			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
 			break;
 		}
@@ -237,7 +239,9 @@
 	switch (chip->hardware) {
 	case SB_HW_PRO:
 		if (runtime->channels > 1) {
-			snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+			if (snd_BUG_ON(rate != SB8_RATE(11025) &&
+				       rate != SB8_RATE(22050)))
+				return -EINVAL;
 			chip->capture_format = SB_DSP_HI_INPUT_AUTO;
 			break;
 		}
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index b432d9a..27a6515 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -219,7 +219,8 @@
 		.dev_free =	snd_sbdsp_dev_free,
 	};
 
-	snd_assert(r_chip != NULL, return -EINVAL);
+	if (snd_BUG_ON(!r_chip))
+		return -EINVAL;
 	*r_chip = NULL;
 	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
 	if (chip == NULL)
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 73d4572..406a431 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -792,7 +792,8 @@
 	struct snd_card *card;
 	int err;
 
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip || !chip->card))
+		return -EINVAL;
 
 	card = chip->card;
 
@@ -925,7 +926,8 @@
 static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
 {
 	unsigned char *val = chip->saved_regs;
-	snd_assert(num_regs <= ARRAY_SIZE(chip->saved_regs), return);
+	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
+		return;
 	for (; num_regs; num_regs--)
 		*val++ = snd_sbmixer_read(chip, *regs++);
 }
@@ -933,7 +935,8 @@
 static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
 {
 	unsigned char *val = chip->saved_regs;
-	snd_assert(num_regs <= ARRAY_SIZE(chip->saved_regs), return);
+	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
+		return;
 	for (; num_regs; num_regs--)
 		snd_sbmixer_write(chip, *regs++, *val++);
 }
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index da3d152..ca35924 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -29,7 +29,7 @@
 #include <linux/io.h>
 #include <asm/dma.h>
 #include <sound/core.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
 #include <sound/opl3.h>
 #include <sound/mpu401.h>
 #include <sound/control.h>
@@ -397,7 +397,7 @@
 	return 0;
 }
 
-static int __devinit snd_sc6000_mixer(struct snd_ad1848 *chip)
+static int __devinit snd_sc6000_mixer(struct snd_wss *chip)
 {
 	struct snd_card *card = chip->card;
 	struct snd_ctl_elem_id id1, id2;
@@ -483,7 +483,7 @@
 	int xirq = irq[dev];
 	int xdma = dma[dev];
 	struct snd_card *card;
-	struct snd_ad1848 *chip;
+	struct snd_wss *chip;
 	struct snd_opl3 *opl3;
 	char __iomem *vport;
 	char __iomem *vmss_port;
@@ -548,21 +548,21 @@
 	if (err < 0)
 		goto err_unmap2;
 
-	err = snd_ad1848_create(card, mss_port[dev] + 4, xirq, xdma,
-				AD1848_HW_DETECT, &chip);
+	err = snd_wss_create(card, mss_port[dev] + 4,  -1, xirq, xdma, -1,
+			     WSS_HW_DETECT, 0, &chip);
 	if (err < 0)
 		goto err_unmap2;
 	card->private_data = chip;
 
-	err = snd_ad1848_pcm(chip, 0, NULL);
+	err = snd_wss_pcm(chip, 0, NULL);
 	if (err < 0) {
 		snd_printk(KERN_ERR PFX
-			   "error creating new ad1848 PCM device\n");
+			   "error creating new WSS PCM device\n");
 		goto err_unmap2;
 	}
-	err = snd_ad1848_mixer(chip);
+	err = snd_wss_mixer(chip);
 	if (err < 0) {
-		snd_printk(KERN_ERR PFX "error creating new ad1848 mixer\n");
+		snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
 		goto err_unmap2;
 	}
 	err = snd_sc6000_mixer(chip);
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
index a07274e..2c7503b 100644
--- a/sound/isa/sgalaxy.c
+++ b/sound/isa/sgalaxy.c
@@ -31,7 +31,7 @@
 #include <asm/dma.h>
 #include <sound/core.h>
 #include <sound/sb.h>
-#include <sound/ad1848.h>
+#include <sound/wss.h>
 #include <sound/control.h>
 #define SNDRV_LEGACY_FIND_FREE_IRQ
 #define SNDRV_LEGACY_FIND_FREE_DMA
@@ -175,12 +175,14 @@
 	return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
 }
 
-static struct ad1848_mix_elem snd_sgalaxy_controls[] = {
-AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
-AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
+static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
+WSS_DOUBLE("Aux Playback Switch", 0,
+		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 0,
+		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
 };
 
-static int __devinit snd_sgalaxy_mixer(struct snd_ad1848 *chip)
+static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
 {
 	struct snd_card *card = chip->card;
 	struct snd_ctl_elem_id id1, id2;
@@ -210,7 +212,9 @@
 		return err;
 	/* build AUX2 input */
 	for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
-		if ((err = snd_ad1848_add_ctl_elem(chip, &snd_sgalaxy_controls[idx])) < 0)
+		err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
+		if (err < 0)
 			return err;
 	}
 	return 0;
@@ -237,7 +241,7 @@
 	static int possible_dmas[] = {1, 3, 0, -1};
 	int err, xirq, xdma1;
 	struct snd_card *card;
-	struct snd_ad1848 *chip;
+	struct snd_wss *chip;
 
 	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
 	if (card == NULL)
@@ -263,18 +267,21 @@
 	if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
 		goto _err;
 
-	if ((err = snd_ad1848_create(card, wssport[dev] + 4,
-				     xirq, xdma1,
-				     AD1848_HW_DETECT, &chip)) < 0)
+	err = snd_wss_create(card, wssport[dev] + 4, -1,
+			     xirq, xdma1, -1,
+			     WSS_HW_DETECT, 0, &chip);
+	if (err < 0)
 		goto _err;
 	card->private_data = chip;
 
-	if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) {
-		snd_printdd(PFX "error creating new ad1848 PCM device\n");
+	err = snd_wss_pcm(chip, 0, NULL);
+	if (err < 0) {
+		snd_printdd(PFX "error creating new WSS PCM device\n");
 		goto _err;
 	}
-	if ((err = snd_ad1848_mixer(chip)) < 0) {
-		snd_printdd(PFX "error creating new ad1848 mixer\n");
+	err = snd_wss_mixer(chip);
+	if (err < 0) {
+		snd_printdd(PFX "error creating new WSS mixer\n");
 		goto _err;
 	}
 	if ((err = snd_sgalaxy_mixer(chip)) < 0) {
@@ -312,7 +319,7 @@
 			       pm_message_t state)
 {
 	struct snd_card *card = dev_get_drvdata(pdev);
-	struct snd_ad1848 *chip = card->private_data;
+	struct snd_wss *chip = card->private_data;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	chip->suspend(chip);
@@ -322,11 +329,11 @@
 static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
 {
 	struct snd_card *card = dev_get_drvdata(pdev);
-	struct snd_ad1848 *chip = card->private_data;
+	struct snd_wss *chip = card->private_data;
 
 	chip->resume(chip);
-	snd_ad1848_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
-	snd_ad1848_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
+	snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
+	snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 06ad786..48a16d8 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -31,7 +31,7 @@
 #include <asm/dma.h>
 #include <sound/core.h>
 #include <sound/hwdep.h>
-#include <sound/cs4231.h>
+#include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/initval.h>
 
@@ -147,7 +147,7 @@
 	enum card_type type;
 	struct resource *io_res;
 	struct resource *wss_res;
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	struct snd_mpu401 *mpu;
 	struct snd_hwdep *hw;
 
@@ -726,7 +726,7 @@
 static int sscape_midi_get(struct snd_kcontrol *kctl,
                            struct snd_ctl_elem_value *uctl)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kctl);
+	struct snd_wss *chip = snd_kcontrol_chip(kctl);
 	struct snd_card *card = chip->card;
 	register struct soundscape *s = get_card_soundscape(card);
 	unsigned long flags;
@@ -746,7 +746,7 @@
 static int sscape_midi_put(struct snd_kcontrol *kctl,
                            struct snd_ctl_elem_value *uctl)
 {
-	struct snd_cs4231 *chip = snd_kcontrol_chip(kctl);
+	struct snd_wss *chip = snd_kcontrol_chip(kctl);
 	struct snd_card *card = chip->card;
 	register struct soundscape *s = get_card_soundscape(card);
 	unsigned long flags;
@@ -958,7 +958,9 @@
  * Override for the CS4231 playback format function.
  * The AD1845 has much simpler format and rate selection.
  */
-static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_params *params, unsigned char format)
+static void ad1845_playback_format(struct snd_wss *chip,
+				   struct snd_pcm_hw_params *params,
+				   unsigned char format)
 {
 	unsigned long flags;
 	unsigned rate = params_rate(params);
@@ -983,9 +985,9 @@
 	 * NOTE: We seem to need to write to the MSB before the LSB
 	 *       to get the correct sample frequency.
 	 */
-	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0));
-	snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
-	snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
+	snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0));
+	snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
+	snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
 
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
@@ -994,7 +996,9 @@
  * Override for the CS4231 capture format function. 
  * The AD1845 has much simpler format and rate selection.
  */
-static void ad1845_capture_format(struct snd_cs4231 * chip, struct snd_pcm_hw_params *params, unsigned char format)
+static void ad1845_capture_format(struct snd_wss *chip,
+				  struct snd_pcm_hw_params *params,
+				  unsigned char format)
 {
 	unsigned long flags;
 	unsigned rate = params_rate(params);
@@ -1019,9 +1023,9 @@
 	 * NOTE: We seem to need to write to the MSB before the LSB
 	 *       to get the correct sample frequency.
 	 */
-	snd_cs4231_out(chip, CS4231_REC_FORMAT, (format & 0xf0));
-	snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
-	snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
+	snd_wss_out(chip, CS4231_REC_FORMAT, (format & 0xf0));
+	snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
+	snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
 
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 }
@@ -1036,7 +1040,7 @@
 				   int irq, int dma1, int dma2)
 {
 	register struct soundscape *sscape = get_card_soundscape(card);
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	int err;
 
 	if (sscape->type == SSCAPE_VIVO)
@@ -1045,9 +1049,8 @@
 	if (dma1 == dma2)
 		dma2 = -1;
 
-	err = snd_cs4231_create(card,
-				port, -1, irq, dma1, dma2,
-				CS4231_HW_DETECT, CS4231_HWSHARE_DMA1, &chip);
+	err = snd_wss_create(card, port, -1, irq, dma1, dma2,
+			     WSS_HW_DETECT, WSS_HWSHARE_DMA1, &chip);
 	if (!err) {
 		unsigned long flags;
 		struct snd_pcm *pcm;
@@ -1063,11 +1066,11 @@
  *
 #define AD1845_IFACE_CONFIG  \
            (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE)
-    snd_cs4231_mce_up(chip);
+    snd_wss_mce_up(chip);
     spin_lock_irqsave(&chip->reg_lock, flags);
-    snd_cs4231_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
+    snd_wss_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);
     spin_unlock_irqrestore(&chip->reg_lock, flags);
-    snd_cs4231_mce_down(chip);
+    snd_wss_mce_down(chip);
  */
 
 		if (sscape->type != SSCAPE_VIVO) {
@@ -1077,11 +1080,11 @@
 			 * be 14.31818 MHz, because we must set this register
 			 * to get the playback to sound correct ...
 			 */
-			snd_cs4231_mce_up(chip);
+			snd_wss_mce_up(chip);
 			spin_lock_irqsave(&chip->reg_lock, flags);
-			snd_cs4231_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);
+			snd_wss_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);
 			spin_unlock_irqrestore(&chip->reg_lock, flags);
-			snd_cs4231_mce_down(chip);
+			snd_wss_mce_down(chip);
 
 			/*
 			 * More custom configuration:
@@ -1089,28 +1092,28 @@
 			 * b) enable frequency selection (for capture/playback)
 			 */
 			spin_lock_irqsave(&chip->reg_lock, flags);
-			snd_cs4231_out(chip, CS4231_MISC_INFO,
-					CS4231_MODE2 | 0x10);
-			val = snd_cs4231_in(chip, AD1845_PWR_DOWN_CTRL);
-			snd_cs4231_out(chip, AD1845_PWR_DOWN_CTRL,
-					val | AD1845_FREQ_SEL_ENABLE);
+			snd_wss_out(chip, CS4231_MISC_INFO,
+				    CS4231_MODE2 | 0x10);
+			val = snd_wss_in(chip, AD1845_PWR_DOWN_CTRL);
+			snd_wss_out(chip, AD1845_PWR_DOWN_CTRL,
+				    val | AD1845_FREQ_SEL_ENABLE);
 			spin_unlock_irqrestore(&chip->reg_lock, flags);
 		}
 
-		err = snd_cs4231_pcm(chip, 0, &pcm);
+		err = snd_wss_pcm(chip, 0, &pcm);
 		if (err < 0) {
 			snd_printk(KERN_ERR "sscape: No PCM device "
 					    "for AD1845 chip\n");
 			goto _error;
 		}
 
-		err = snd_cs4231_mixer(chip);
+		err = snd_wss_mixer(chip);
 		if (err < 0) {
 			snd_printk(KERN_ERR "sscape: No mixer device "
 					    "for AD1845 chip\n");
 			goto _error;
 		}
-		err = snd_cs4231_timer(chip, 0, NULL);
+		err = snd_wss_timer(chip, 0, NULL);
 		if (err < 0) {
 			snd_printk(KERN_ERR "sscape: No timer device "
 					    "for AD1845 chip\n");
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 3a6c6fe..4c095bc 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -1,6 +1,6 @@
 /*
  *  ALSA card-level driver for Turtle Beach Wavefront cards 
- *                                              (Maui,Tropez,Tropez+)
+ *						(Maui,Tropez,Tropez+)
  *
  *  Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>
  *
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/opl3.h>
+#include <sound/wss.h>
 #include <sound/snd_wavefront.h>
 
 MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
@@ -319,8 +320,8 @@
 	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input);
 
 	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
-	                     SNDRV_RAWMIDI_INFO_INPUT |
-	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+			     SNDRV_RAWMIDI_INFO_INPUT |
+			     SNDRV_RAWMIDI_INFO_DUPLEX;
 
 	return rmidi;
 }
@@ -363,7 +364,7 @@
 snd_wavefront_probe (struct snd_card *card, int dev)
 {
 	snd_wavefront_card_t *acard = card->private_data;
-	struct snd_cs4231 *chip;
+	struct snd_wss *chip;
 	struct snd_hwdep *wavefront_synth;
 	struct snd_rawmidi *ics2115_internal_rmidi = NULL;
 	struct snd_rawmidi *ics2115_external_rmidi = NULL;
@@ -372,21 +373,20 @@
 
 	/* --------- PCM --------------- */
 
-	if ((err = snd_cs4231_create (card,
-				      cs4232_pcm_port[dev],
-				      -1,
-				      cs4232_pcm_irq[dev],
-				      dma1[dev],
-				      dma2[dev],
-				      CS4231_HW_DETECT, 0, &chip)) < 0) {
-		snd_printk (KERN_ERR "can't allocate CS4231 device\n");
+	err = snd_wss_create(card, cs4232_pcm_port[dev], -1,
+			     cs4232_pcm_irq[dev], dma1[dev], dma2[dev],
+			     WSS_HW_DETECT, 0, &chip);
+	if (err < 0) {
+		snd_printk(KERN_ERR "can't allocate WSS device\n");
 		return err;
 	}
 
-	if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0)
+	err = snd_wss_pcm(chip, 0, NULL);
+	if (err < 0)
 		return err;
 
-	if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0)
+	err = snd_wss_timer(chip, 0, NULL);
+	if (err < 0)
 		return err;
 
 	/* ---------- OPL3 synth --------- */
@@ -394,24 +394,24 @@
 	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
 		struct snd_opl3 *opl3;
 
-	        if ((err = snd_opl3_create(card,
-					   fm_port[dev],
-					   fm_port[dev] + 2,
-					   OPL3_HW_OPL3_CS,
-					   0, &opl3)) < 0) {
+		err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+				      OPL3_HW_OPL3_CS, 0, &opl3);
+		if (err < 0) {
 			snd_printk (KERN_ERR "can't allocate or detect OPL3 synth\n");
 			return err;
 		}
 
-		if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0)
+		err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL);
+		if (err < 0)
 			return err;
 		hw_dev++;
 	}
 
 	/* ------- ICS2115 Wavetable synth ------- */
 
-	if ((acard->wavefront.res_base = request_region(ics2115_port[dev], 16,
-							"ICS2115")) == NULL) {
+	acard->wavefront.res_base = request_region(ics2115_port[dev], 16,
+						   "ICS2115");
+	if (acard->wavefront.res_base == NULL) {
 		snd_printk(KERN_ERR "unable to grab ICS2115 i/o region 0x%lx-0x%lx\n",
 			   ics2115_port[dev], ics2115_port[dev] + 16 - 1);
 		return -EBUSY;
@@ -425,7 +425,8 @@
 	acard->wavefront.irq = ics2115_irq[dev];
 	acard->wavefront.base = ics2115_port[dev];
 
-	if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) {
+	wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard);
+	if (wavefront_synth == NULL) {
 		snd_printk (KERN_ERR "can't create WaveFront synth device\n");
 		return -ENOMEM;
 	}
@@ -436,7 +437,8 @@
 
 	/* --------- Mixer ------------ */
 
-	if ((err = snd_cs4231_mixer(chip)) < 0) {
+	err = snd_wss_mixer(chip);
+	if (err < 0) {
 		snd_printk (KERN_ERR "can't allocate mixer device\n");
 		return err;
 	}
@@ -444,11 +446,11 @@
 	/* -------- CS4232 MPU-401 interface -------- */
 
 	if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
-		if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
-					       cs4232_mpu_port[dev], 0,
-					       cs4232_mpu_irq[dev],
-					       IRQF_DISABLED,
-					       NULL)) < 0) {
+		err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
+					  cs4232_mpu_port[dev], 0,
+					  cs4232_mpu_irq[dev], IRQF_DISABLED,
+					  NULL);
+		if (err < 0) {
 			snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
 			return err;
 		}
@@ -601,7 +603,7 @@
 
 #ifdef CONFIG_PNP
 static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
-                                              const struct pnp_card_device_id *pid)
+					const struct pnp_card_device_id *pid)
 {
 	static int dev;
 	struct snd_card *card;
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 2efaa7f..dfc449a 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -180,11 +180,11 @@
 	unsigned short *pd;
 	int err = 0;
 
-	snd_assert(sdev->card != NULL, return -ENODEV);
-	
 	card = sdev->card;
-
-	snd_assert(card->private_data != NULL, return -ENODEV);
+	if (snd_BUG_ON(!card))
+		return -ENODEV;
+	if (snd_BUG_ON(!card->private_data))
+		return -ENODEV;
 
 	acard = card->private_data;
 	dev = &acard->wavefront;
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index a33384a..f14a7c0 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -235,8 +235,10 @@
 	snd_wavefront_midi_t *midi;
 	snd_wavefront_mpu_id mpu;
 
-	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
-	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+	if (snd_BUG_ON(!substream || !substream->rmidi))
+		return -ENXIO;
+	if (snd_BUG_ON(!substream->rmidi->private_data))
+		return -ENXIO;
 
 	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 
@@ -257,8 +259,10 @@
 	snd_wavefront_midi_t *midi;
 	snd_wavefront_mpu_id mpu;
 
-	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
-	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+	if (snd_BUG_ON(!substream || !substream->rmidi))
+		return -ENXIO;
+	if (snd_BUG_ON(!substream->rmidi->private_data))
+		return -ENXIO;
 
 	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 
@@ -279,8 +283,10 @@
 	snd_wavefront_midi_t *midi;
 	snd_wavefront_mpu_id mpu;
 
-	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
-	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+	if (snd_BUG_ON(!substream || !substream->rmidi))
+		return -ENXIO;
+	if (snd_BUG_ON(!substream->rmidi->private_data))
+		return -ENXIO;
 
 	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 
@@ -300,8 +306,10 @@
 	snd_wavefront_midi_t *midi;
 	snd_wavefront_mpu_id mpu;
 
-	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO);
-	snd_assert(substream->rmidi->private_data != NULL, return -EIO);
+	if (snd_BUG_ON(!substream || !substream->rmidi))
+		return -ENXIO;
+	if (snd_BUG_ON(!substream->rmidi->private_data))
+		return -ENXIO;
 
 	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
 
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 0bb9b92..4c41082 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1648,9 +1648,10 @@
 
 	card = (struct snd_card *) hw->card;
 
-	snd_assert(card != NULL, return -ENODEV);
-
-	snd_assert(card->private_data != NULL, return -ENODEV);
+	if (snd_BUG_ON(!card))
+		return -ENODEV;
+	if (snd_BUG_ON(!card->private_data))
+		return -ENODEV;
 
 	acard = card->private_data;
 	dev = &acard->wavefront;
diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile
new file mode 100644
index 0000000..454fee7
--- /dev/null
+++ b/sound/isa/wss/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz>
+#
+
+snd-wss-lib-objs := wss_lib.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_WSS_LIB) += snd-wss-lib.o
+
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
new file mode 100644
index 0000000..3d6c5f2
--- /dev/null
+++ b/sound/isa/wss/wss_lib.c
@@ -0,0 +1,2322 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
+ *
+ *  Bugs:
+ *     - sometimes record brokes playback with WSS portion of
+ *       Yamaha OPL3-SA3 chip
+ *     - CS4231 (GUS MAX) - still trouble with occasional noises
+ *			  - broken initialization?
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/wss.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
+MODULE_LICENSE("GPL");
+
+#if 0
+#define SNDRV_DEBUG_MCE
+#endif
+
+/*
+ *  Some variables
+ */
+
+static unsigned char freq_bits[14] = {
+	/* 5510 */	0x00 | CS4231_XTAL2,
+	/* 6620 */	0x0E | CS4231_XTAL2,
+	/* 8000 */	0x00 | CS4231_XTAL1,
+	/* 9600 */	0x0E | CS4231_XTAL1,
+	/* 11025 */	0x02 | CS4231_XTAL2,
+	/* 16000 */	0x02 | CS4231_XTAL1,
+	/* 18900 */	0x04 | CS4231_XTAL2,
+	/* 22050 */	0x06 | CS4231_XTAL2,
+	/* 27042 */	0x04 | CS4231_XTAL1,
+	/* 32000 */	0x06 | CS4231_XTAL1,
+	/* 33075 */	0x0C | CS4231_XTAL2,
+	/* 37800 */	0x08 | CS4231_XTAL2,
+	/* 44100 */	0x0A | CS4231_XTAL2,
+	/* 48000 */	0x0C | CS4231_XTAL1
+};
+
+static unsigned int rates[14] = {
+	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
+	27042, 32000, 33075, 37800, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list = rates,
+	.mask = 0,
+};
+
+static int snd_wss_xrate(struct snd_pcm_runtime *runtime)
+{
+	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					  &hw_constraints_rates);
+}
+
+static unsigned char snd_wss_original_image[32] =
+{
+	0x00,			/* 00/00 - lic */
+	0x00,			/* 01/01 - ric */
+	0x9f,			/* 02/02 - la1ic */
+	0x9f,			/* 03/03 - ra1ic */
+	0x9f,			/* 04/04 - la2ic */
+	0x9f,			/* 05/05 - ra2ic */
+	0xbf,			/* 06/06 - loc */
+	0xbf,			/* 07/07 - roc */
+	0x20,			/* 08/08 - pdfr */
+	CS4231_AUTOCALIB,	/* 09/09 - ic */
+	0x00,			/* 0a/10 - pc */
+	0x00,			/* 0b/11 - ti */
+	CS4231_MODE2,		/* 0c/12 - mi */
+	0xfc,			/* 0d/13 - lbc */
+	0x00,			/* 0e/14 - pbru */
+	0x00,			/* 0f/15 - pbrl */
+	0x80,			/* 10/16 - afei */
+	0x01,			/* 11/17 - afeii */
+	0x9f,			/* 12/18 - llic */
+	0x9f,			/* 13/19 - rlic */
+	0x00,			/* 14/20 - tlb */
+	0x00,			/* 15/21 - thb */
+	0x00,			/* 16/22 - la3mic/reserved */
+	0x00,			/* 17/23 - ra3mic/reserved */
+	0x00,			/* 18/24 - afs */
+	0x00,			/* 19/25 - lamoc/version */
+	0xcf,			/* 1a/26 - mioc */
+	0x00,			/* 1b/27 - ramoc/reserved */
+	0x20,			/* 1c/28 - cdfr */
+	0x00,			/* 1d/29 - res4 */
+	0x00,			/* 1e/30 - cbru */
+	0x00,			/* 1f/31 - cbrl */
+};
+
+static unsigned char snd_opti93x_original_image[32] =
+{
+	0x00,		/* 00/00 - l_mixout_outctrl */
+	0x00,		/* 01/01 - r_mixout_outctrl */
+	0x88,		/* 02/02 - l_cd_inctrl */
+	0x88,		/* 03/03 - r_cd_inctrl */
+	0x88,		/* 04/04 - l_a1/fm_inctrl */
+	0x88,		/* 05/05 - r_a1/fm_inctrl */
+	0x80,		/* 06/06 - l_dac_inctrl */
+	0x80,		/* 07/07 - r_dac_inctrl */
+	0x00,		/* 08/08 - ply_dataform_reg */
+	0x00,		/* 09/09 - if_conf */
+	0x00,		/* 0a/10 - pin_ctrl */
+	0x00,		/* 0b/11 - err_init_reg */
+	0x0a,		/* 0c/12 - id_reg */
+	0x00,		/* 0d/13 - reserved */
+	0x00,		/* 0e/14 - ply_upcount_reg */
+	0x00,		/* 0f/15 - ply_lowcount_reg */
+	0x88,		/* 10/16 - reserved/l_a1_inctrl */
+	0x88,		/* 11/17 - reserved/r_a1_inctrl */
+	0x88,		/* 12/18 - l_line_inctrl */
+	0x88,		/* 13/19 - r_line_inctrl */
+	0x88,		/* 14/20 - l_mic_inctrl */
+	0x88,		/* 15/21 - r_mic_inctrl */
+	0x80,		/* 16/22 - l_out_outctrl */
+	0x80,		/* 17/23 - r_out_outctrl */
+	0x00,		/* 18/24 - reserved */
+	0x00,		/* 19/25 - reserved */
+	0x00,		/* 1a/26 - reserved */
+	0x00,		/* 1b/27 - reserved */
+	0x00,		/* 1c/28 - cap_dataform_reg */
+	0x00,		/* 1d/29 - reserved */
+	0x00,		/* 1e/30 - cap_upcount_reg */
+	0x00		/* 1f/31 - cap_lowcount_reg */
+};
+
+/*
+ *  Basic I/O functions
+ */
+
+static inline void wss_outb(struct snd_wss *chip, u8 offset, u8 val)
+{
+	outb(val, chip->port + offset);
+}
+
+static inline u8 wss_inb(struct snd_wss *chip, u8 offset)
+{
+	return inb(chip->port + offset);
+}
+
+static void snd_wss_wait(struct snd_wss *chip)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
+	     timeout--)
+		udelay(100);
+}
+
+static void snd_wss_outm(struct snd_wss *chip, unsigned char reg,
+			    unsigned char mask, unsigned char value)
+{
+	unsigned char tmp = (chip->image[reg] & mask) | value;
+
+	snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+		snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	chip->image[reg] = tmp;
+	if (!chip->calibrate_mute) {
+		wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+		wmb();
+		wss_outb(chip, CS4231P(REG), tmp);
+		mb();
+	}
+}
+
+static void snd_wss_dout(struct snd_wss *chip, unsigned char reg,
+			 unsigned char value)
+{
+	int timeout;
+
+	for (timeout = 250;
+	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
+	     timeout--)
+		udelay(10);
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+	wss_outb(chip, CS4231P(REG), value);
+	mb();
+}
+
+void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char value)
+{
+	snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+		snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+#endif
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+	wss_outb(chip, CS4231P(REG), value);
+	chip->image[reg] = value;
+	mb();
+	snd_printdd("codec out - reg 0x%x = 0x%x\n",
+			chip->mce_bit | reg, value);
+}
+EXPORT_SYMBOL(snd_wss_out);
+
+unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg)
+{
+	snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+		snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
+#endif
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
+	mb();
+	return wss_inb(chip, CS4231P(REG));
+}
+EXPORT_SYMBOL(snd_wss_in);
+
+void snd_cs4236_ext_out(struct snd_wss *chip, unsigned char reg,
+			unsigned char val)
+{
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
+	wss_outb(chip, CS4231P(REG),
+		 reg | (chip->image[CS4236_EXT_REG] & 0x01));
+	wss_outb(chip, CS4231P(REG), val);
+	chip->eimage[CS4236_REG(reg)] = val;
+#if 0
+	printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+#endif
+}
+EXPORT_SYMBOL(snd_cs4236_ext_out);
+
+unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg)
+{
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
+	wss_outb(chip, CS4231P(REG),
+		 reg | (chip->image[CS4236_EXT_REG] & 0x01));
+#if 1
+	return wss_inb(chip, CS4231P(REG));
+#else
+	{
+		unsigned char res;
+		res = wss_inb(chip, CS4231P(REG));
+		printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
+		return res;
+	}
+#endif
+}
+EXPORT_SYMBOL(snd_cs4236_ext_in);
+
+#if 0
+
+static void snd_wss_debug(struct snd_wss *chip)
+{
+	printk(KERN_DEBUG
+		"CS4231 REGS:      INDEX = 0x%02x  "
+		"                 STATUS = 0x%02x\n",
+					wss_inb(chip, CS4231P(REGSEL)),
+					wss_inb(chip, CS4231P(STATUS)));
+	printk(KERN_DEBUG
+		"  0x00: left input      = 0x%02x  "
+		"  0x10: alt 1 (CFIG 2)  = 0x%02x\n",
+					snd_wss_in(chip, 0x00),
+					snd_wss_in(chip, 0x10));
+	printk(KERN_DEBUG
+		"  0x01: right input     = 0x%02x  "
+		"  0x11: alt 2 (CFIG 3)  = 0x%02x\n",
+					snd_wss_in(chip, 0x01),
+					snd_wss_in(chip, 0x11));
+	printk(KERN_DEBUG
+		"  0x02: GF1 left input  = 0x%02x  "
+		"  0x12: left line in    = 0x%02x\n",
+					snd_wss_in(chip, 0x02),
+					snd_wss_in(chip, 0x12));
+	printk(KERN_DEBUG
+		"  0x03: GF1 right input = 0x%02x  "
+		"  0x13: right line in   = 0x%02x\n",
+					snd_wss_in(chip, 0x03),
+					snd_wss_in(chip, 0x13));
+	printk(KERN_DEBUG
+		"  0x04: CD left input   = 0x%02x  "
+		"  0x14: timer low       = 0x%02x\n",
+					snd_wss_in(chip, 0x04),
+					snd_wss_in(chip, 0x14));
+	printk(KERN_DEBUG
+		"  0x05: CD right input  = 0x%02x  "
+		"  0x15: timer high      = 0x%02x\n",
+					snd_wss_in(chip, 0x05),
+					snd_wss_in(chip, 0x15));
+	printk(KERN_DEBUG
+		"  0x06: left output     = 0x%02x  "
+		"  0x16: left MIC (PnP)  = 0x%02x\n",
+					snd_wss_in(chip, 0x06),
+					snd_wss_in(chip, 0x16));
+	printk(KERN_DEBUG
+		"  0x07: right output    = 0x%02x  "
+		"  0x17: right MIC (PnP) = 0x%02x\n",
+					snd_wss_in(chip, 0x07),
+					snd_wss_in(chip, 0x17));
+	printk(KERN_DEBUG
+		"  0x08: playback format = 0x%02x  "
+		"  0x18: IRQ status      = 0x%02x\n",
+					snd_wss_in(chip, 0x08),
+					snd_wss_in(chip, 0x18));
+	printk(KERN_DEBUG
+		"  0x09: iface (CFIG 1)  = 0x%02x  "
+		"  0x19: left line out   = 0x%02x\n",
+					snd_wss_in(chip, 0x09),
+					snd_wss_in(chip, 0x19));
+	printk(KERN_DEBUG
+		"  0x0a: pin control     = 0x%02x  "
+		"  0x1a: mono control    = 0x%02x\n",
+					snd_wss_in(chip, 0x0a),
+					snd_wss_in(chip, 0x1a));
+	printk(KERN_DEBUG
+		"  0x0b: init & status   = 0x%02x  "
+		"  0x1b: right line out  = 0x%02x\n",
+					snd_wss_in(chip, 0x0b),
+					snd_wss_in(chip, 0x1b));
+	printk(KERN_DEBUG
+		"  0x0c: revision & mode = 0x%02x  "
+		"  0x1c: record format   = 0x%02x\n",
+					snd_wss_in(chip, 0x0c),
+					snd_wss_in(chip, 0x1c));
+	printk(KERN_DEBUG
+		"  0x0d: loopback        = 0x%02x  "
+		"  0x1d: var freq (PnP)  = 0x%02x\n",
+					snd_wss_in(chip, 0x0d),
+					snd_wss_in(chip, 0x1d));
+	printk(KERN_DEBUG
+		"  0x0e: ply upr count   = 0x%02x  "
+		"  0x1e: ply lwr count   = 0x%02x\n",
+					snd_wss_in(chip, 0x0e),
+					snd_wss_in(chip, 0x1e));
+	printk(KERN_DEBUG
+		"  0x0f: rec upr count   = 0x%02x  "
+		"  0x1f: rec lwr count   = 0x%02x\n",
+					snd_wss_in(chip, 0x0f),
+					snd_wss_in(chip, 0x1f));
+}
+
+#endif
+
+/*
+ *  CS4231 detection / MCE routines
+ */
+
+static void snd_wss_busy_wait(struct snd_wss *chip)
+{
+	int timeout;
+
+	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
+	for (timeout = 5; timeout > 0; timeout--)
+		wss_inb(chip, CS4231P(REGSEL));
+	/* end of cleanup sequence */
+	for (timeout = 25000;
+	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
+	     timeout--)
+		udelay(10);
+}
+
+void snd_wss_mce_up(struct snd_wss *chip)
+{
+	unsigned long flags;
+	int timeout;
+
+	snd_wss_wait(chip);
+#ifdef CONFIG_SND_DEBUG
+	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+		snd_printk("mce_up - auto calibration time out (0)\n");
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->mce_bit |= CS4231_MCE;
+	timeout = wss_inb(chip, CS4231P(REGSEL));
+	if (timeout == 0x80)
+		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if (!(timeout & CS4231_MCE))
+		wss_outb(chip, CS4231P(REGSEL),
+			 chip->mce_bit | (timeout & 0x1f));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+EXPORT_SYMBOL(snd_wss_mce_up);
+
+void snd_wss_mce_down(struct snd_wss *chip)
+{
+	unsigned long flags;
+	unsigned long end_time;
+	int timeout;
+	int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848;
+
+	snd_wss_busy_wait(chip);
+
+#ifdef CONFIG_SND_DEBUG
+	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL));
+#endif
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->mce_bit &= ~CS4231_MCE;
+	timeout = wss_inb(chip, CS4231P(REGSEL));
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (timeout == 0x80)
+		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask))
+		return;
+
+	/*
+	 * Wait for (possible -- during init auto-calibration may not be set)
+	 * calibration process to start. Needs upto 5 sample periods on AD1848
+	 * which at the slowest possible rate of 5.5125 kHz means 907 us.
+	 */
+	msleep(1);
+
+	snd_printdd("(1) jiffies = %lu\n", jiffies);
+
+	/* check condition up to 250 ms */
+	end_time = jiffies + msecs_to_jiffies(250);
+	while (snd_wss_in(chip, CS4231_TEST_INIT) &
+		CS4231_CALIB_IN_PROGRESS) {
+
+		if (time_after(jiffies, end_time)) {
+			snd_printk(KERN_ERR "mce_down - "
+					"auto calibration time out (2)\n");
+			return;
+		}
+		msleep(1);
+	}
+
+	snd_printdd("(2) jiffies = %lu\n", jiffies);
+
+	/* check condition up to 100 ms */
+	end_time = jiffies + msecs_to_jiffies(100);
+	while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
+		if (time_after(jiffies, end_time)) {
+			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
+			return;
+		}
+		msleep(1);
+	}
+
+	snd_printdd("(3) jiffies = %lu\n", jiffies);
+	snd_printd("mce_down - exit = 0x%x\n", wss_inb(chip, CS4231P(REGSEL)));
+}
+EXPORT_SYMBOL(snd_wss_mce_down);
+
+static unsigned int snd_wss_get_count(unsigned char format, unsigned int size)
+{
+	switch (format & 0xe0) {
+	case CS4231_LINEAR_16:
+	case CS4231_LINEAR_16_BIG:
+		size >>= 1;
+		break;
+	case CS4231_ADPCM_16:
+		return size >> 2;
+	}
+	if (format & CS4231_STEREO)
+		size >>= 1;
+	return size;
+}
+
+static int snd_wss_trigger(struct snd_pcm_substream *substream,
+			   int cmd)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	int result = 0;
+	unsigned int what;
+	struct snd_pcm_substream *s;
+	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 = 0;
+	snd_pcm_group_for_each_entry(s, substream) {
+		if (s == chip->playback_substream) {
+			what |= CS4231_PLAYBACK_ENABLE;
+			snd_pcm_trigger_done(s, substream);
+		} else if (s == chip->capture_substream) {
+			what |= CS4231_RECORD_ENABLE;
+			snd_pcm_trigger_done(s, substream);
+		}
+	}
+	spin_lock(&chip->reg_lock);
+	if (do_start) {
+		chip->image[CS4231_IFACE_CTRL] |= what;
+		if (chip->trigger)
+			chip->trigger(chip, what, 1);
+	} else {
+		chip->image[CS4231_IFACE_CTRL] &= ~what;
+		if (chip->trigger)
+			chip->trigger(chip, what, 0);
+	}
+	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+	spin_unlock(&chip->reg_lock);
+#if 0
+	snd_wss_debug(chip);
+#endif
+	return result;
+}
+
+/*
+ *  CODEC I/O
+ */
+
+static unsigned char snd_wss_get_rate(unsigned int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rates); i++)
+		if (rate == rates[i])
+			return freq_bits[i];
+	// snd_BUG();
+	return freq_bits[ARRAY_SIZE(rates) - 1];
+}
+
+static unsigned char snd_wss_get_format(struct snd_wss *chip,
+					int format,
+					int channels)
+{
+	unsigned char rformat;
+
+	rformat = CS4231_LINEAR_8;
+	switch (format) {
+	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break;
+	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break;
+	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break;
+	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break;
+	}
+	if (channels > 1)
+		rformat |= CS4231_STEREO;
+#if 0
+	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+#endif
+	return rformat;
+}
+
+static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
+{
+	unsigned long flags;
+
+	mute = mute ? 0x80 : 0;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (chip->calibrate_mute == mute) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		return;
+	}
+	if (!mute) {
+		snd_wss_dout(chip, CS4231_LEFT_INPUT,
+			     chip->image[CS4231_LEFT_INPUT]);
+		snd_wss_dout(chip, CS4231_RIGHT_INPUT,
+			     chip->image[CS4231_RIGHT_INPUT]);
+		snd_wss_dout(chip, CS4231_LOOPBACK,
+			     chip->image[CS4231_LOOPBACK]);
+	}
+	snd_wss_dout(chip, CS4231_AUX1_LEFT_INPUT,
+		     mute | chip->image[CS4231_AUX1_LEFT_INPUT]);
+	snd_wss_dout(chip, CS4231_AUX1_RIGHT_INPUT,
+		     mute | chip->image[CS4231_AUX1_RIGHT_INPUT]);
+	snd_wss_dout(chip, CS4231_AUX2_LEFT_INPUT,
+		     mute | chip->image[CS4231_AUX2_LEFT_INPUT]);
+	snd_wss_dout(chip, CS4231_AUX2_RIGHT_INPUT,
+		     mute | chip->image[CS4231_AUX2_RIGHT_INPUT]);
+	snd_wss_dout(chip, CS4231_LEFT_OUTPUT,
+		     mute | chip->image[CS4231_LEFT_OUTPUT]);
+	snd_wss_dout(chip, CS4231_RIGHT_OUTPUT,
+		     mute | chip->image[CS4231_RIGHT_OUTPUT]);
+	if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
+		snd_wss_dout(chip, CS4231_LEFT_LINE_IN,
+			     mute | chip->image[CS4231_LEFT_LINE_IN]);
+		snd_wss_dout(chip, CS4231_RIGHT_LINE_IN,
+			     mute | chip->image[CS4231_RIGHT_LINE_IN]);
+		snd_wss_dout(chip, CS4231_MONO_CTRL,
+			     mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
+	}
+	if (chip->hardware == WSS_HW_INTERWAVE) {
+		snd_wss_dout(chip, CS4231_LEFT_MIC_INPUT,
+			     mute | chip->image[CS4231_LEFT_MIC_INPUT]);
+		snd_wss_dout(chip, CS4231_RIGHT_MIC_INPUT,
+			     mute | chip->image[CS4231_RIGHT_MIC_INPUT]);
+		snd_wss_dout(chip, CS4231_LINE_LEFT_OUTPUT,
+			     mute | chip->image[CS4231_LINE_LEFT_OUTPUT]);
+		snd_wss_dout(chip, CS4231_LINE_RIGHT_OUTPUT,
+			     mute | chip->image[CS4231_LINE_RIGHT_OUTPUT]);
+	}
+	chip->calibrate_mute = mute;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static void snd_wss_playback_format(struct snd_wss *chip,
+				       struct snd_pcm_hw_params *params,
+				       unsigned char pdfr)
+{
+	unsigned long flags;
+	int full_calib = 1;
+
+	mutex_lock(&chip->mce_mutex);
+	snd_wss_calibrate_mute(chip, 1);
+	if (chip->hardware == WSS_HW_CS4231A ||
+	    (chip->hardware & WSS_HW_CS4232_MASK)) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */
+			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+				    chip->image[CS4231_ALT_FEATURE_1] | 0x10);
+			chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+			snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+				    chip->image[CS4231_PLAYBK_FORMAT]);
+			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+				    chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
+			udelay(100); /* Fixes audible clicks at least on GUS MAX */
+			full_calib = 0;
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	if (full_calib) {
+		snd_wss_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
+			if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
+				pdfr = (pdfr & 0xf0) |
+				       (chip->image[CS4231_REC_FORMAT] & 0x0f);
+		} else {
+			chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+		}
+		snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		if (chip->hardware == WSS_HW_OPL3SA2)
+			udelay(100);	/* this seems to help */
+		snd_wss_mce_down(chip);
+	}
+	snd_wss_calibrate_mute(chip, 0);
+	mutex_unlock(&chip->mce_mutex);
+}
+
+static void snd_wss_capture_format(struct snd_wss *chip,
+				   struct snd_pcm_hw_params *params,
+				   unsigned char cdfr)
+{
+	unsigned long flags;
+	int full_calib = 1;
+
+	mutex_lock(&chip->mce_mutex);
+	snd_wss_calibrate_mute(chip, 1);
+	if (chip->hardware == WSS_HW_CS4231A ||
+	    (chip->hardware & WSS_HW_CS4232_MASK)) {
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */
+		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+				chip->image[CS4231_ALT_FEATURE_1] | 0x20);
+			snd_wss_out(chip, CS4231_REC_FORMAT,
+				chip->image[CS4231_REC_FORMAT] = cdfr);
+			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+				chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
+			full_calib = 0;
+		}
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+	}
+	if (full_calib) {
+		snd_wss_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		if (chip->hardware != WSS_HW_INTERWAVE &&
+		    !(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
+			if (chip->single_dma)
+				snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr);
+			else
+				snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+				   (chip->image[CS4231_PLAYBK_FORMAT] & 0xf0) |
+				   (cdfr & 0x0f));
+			spin_unlock_irqrestore(&chip->reg_lock, flags);
+			snd_wss_mce_down(chip);
+			snd_wss_mce_up(chip);
+			spin_lock_irqsave(&chip->reg_lock, flags);
+		}
+		if (chip->hardware & WSS_HW_AD1848_MASK)
+			snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr);
+		else
+			snd_wss_out(chip, CS4231_REC_FORMAT, cdfr);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_wss_mce_down(chip);
+	}
+	snd_wss_calibrate_mute(chip, 0);
+	mutex_unlock(&chip->mce_mutex);
+}
+
+/*
+ *  Timer interface
+ */
+
+static unsigned long snd_wss_timer_resolution(struct snd_timer *timer)
+{
+	struct snd_wss *chip = snd_timer_chip(timer);
+	if (chip->hardware & WSS_HW_CS4236B_MASK)
+		return 14467;
+	else
+		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
+}
+
+static int snd_wss_timer_start(struct snd_timer *timer)
+{
+	unsigned long flags;
+	unsigned int ticks;
+	struct snd_wss *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ticks = timer->sticks;
+	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
+	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
+	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
+		chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8);
+		snd_wss_out(chip, CS4231_TIMER_HIGH,
+			    chip->image[CS4231_TIMER_HIGH]);
+		chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks;
+		snd_wss_out(chip, CS4231_TIMER_LOW,
+			    chip->image[CS4231_TIMER_LOW]);
+		snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+			    chip->image[CS4231_ALT_FEATURE_1] |
+			    CS4231_TIMER_ENABLE);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_wss_timer_stop(struct snd_timer *timer)
+{
+	unsigned long flags;
+	struct snd_wss *chip = snd_timer_chip(timer);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
+	snd_wss_out(chip, CS4231_ALT_FEATURE_1,
+		    chip->image[CS4231_ALT_FEATURE_1]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static void snd_wss_init(struct snd_wss *chip)
+{
+	unsigned long flags;
+
+	snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (1)\n");
+#endif
+	snd_wss_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
+					    CS4231_PLAYBACK_PIO |
+					    CS4231_RECORD_ENABLE |
+					    CS4231_RECORD_PIO |
+					    CS4231_CALIB_MODE);
+	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
+	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (2)\n");
+#endif
+
+	snd_wss_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_wss_out(chip,
+		    CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (3) - afei = 0x%x\n",
+		   chip->image[CS4231_ALT_FEATURE_1]);
+#endif
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_wss_out(chip, CS4231_ALT_FEATURE_2,
+		    chip->image[CS4231_ALT_FEATURE_2]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_wss_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+		    chip->image[CS4231_PLAYBK_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (4)\n");
+#endif
+
+	snd_wss_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (!(chip->hardware & WSS_HW_AD1848_MASK))
+		snd_wss_out(chip, CS4231_REC_FORMAT,
+			    chip->image[CS4231_REC_FORMAT]);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_wss_mce_down(chip);
+
+#ifdef SNDRV_DEBUG_MCE
+	snd_printk("init: (5)\n");
+#endif
+}
+
+static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	mutex_lock(&chip->open_mutex);
+	if ((chip->mode & mode) ||
+	    ((chip->mode & WSS_MODE_OPEN) && chip->single_dma)) {
+		mutex_unlock(&chip->open_mutex);
+		return -EAGAIN;
+	}
+	if (chip->mode & WSS_MODE_OPEN) {
+		chip->mode |= mode;
+		mutex_unlock(&chip->open_mutex);
+		return 0;
+	}
+	/* ok. now enable and ack CODEC IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
+		snd_wss_out(chip, CS4231_IRQ_STATUS,
+			    CS4231_PLAYBACK_IRQ |
+			    CS4231_RECORD_IRQ |
+			    CS4231_TIMER_IRQ);
+		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+	}
+	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
+	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
+	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
+	snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+	if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
+		snd_wss_out(chip, CS4231_IRQ_STATUS,
+			    CS4231_PLAYBACK_IRQ |
+			    CS4231_RECORD_IRQ |
+			    CS4231_TIMER_IRQ);
+		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	chip->mode = mode;
+	mutex_unlock(&chip->open_mutex);
+	return 0;
+}
+
+static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
+{
+	unsigned long flags;
+
+	mutex_lock(&chip->open_mutex);
+	chip->mode &= ~mode;
+	if (chip->mode & WSS_MODE_OPEN) {
+		mutex_unlock(&chip->open_mutex);
+		return;
+	}
+	snd_wss_calibrate_mute(chip, 1);
+
+	/* disable IRQ */
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (!(chip->hardware & WSS_HW_AD1848_MASK))
+		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
+	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
+	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
+	snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
+
+	/* now disable record & playback */
+
+	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_wss_mce_up(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
+						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+		snd_wss_out(chip, CS4231_IFACE_CTRL,
+			    chip->image[CS4231_IFACE_CTRL]);
+		spin_unlock_irqrestore(&chip->reg_lock, flags);
+		snd_wss_mce_down(chip);
+		spin_lock_irqsave(&chip->reg_lock, flags);
+	}
+
+	/* clear IRQ again */
+	if (!(chip->hardware & WSS_HW_AD1848_MASK))
+		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
+	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
+	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	snd_wss_calibrate_mute(chip, 0);
+
+	chip->mode = 0;
+	mutex_unlock(&chip->open_mutex);
+}
+
+/*
+ *  timer open/close
+ */
+
+static int snd_wss_timer_open(struct snd_timer *timer)
+{
+	struct snd_wss *chip = snd_timer_chip(timer);
+	snd_wss_open(chip, WSS_MODE_TIMER);
+	return 0;
+}
+
+static int snd_wss_timer_close(struct snd_timer *timer)
+{
+	struct snd_wss *chip = snd_timer_chip(timer);
+	snd_wss_close(chip, WSS_MODE_TIMER);
+	return 0;
+}
+
+static struct snd_timer_hardware snd_wss_timer_table =
+{
+	.flags =	SNDRV_TIMER_HW_AUTO,
+	.resolution =	9945,
+	.ticks =	65535,
+	.open =		snd_wss_timer_open,
+	.close =	snd_wss_timer_close,
+	.c_resolution = snd_wss_timer_resolution,
+	.start =	snd_wss_timer_start,
+	.stop =		snd_wss_timer_stop,
+};
+
+/*
+ *  ok.. exported functions..
+ */
+
+static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
+					 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	unsigned char new_pdfr;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	new_pdfr = snd_wss_get_format(chip, params_format(hw_params),
+				params_channels(hw_params)) |
+				snd_wss_get_rate(params_rate(hw_params));
+	chip->set_playback_format(chip, hw_params, new_pdfr);
+	return 0;
+}
+
+static int snd_wss_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_wss_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->p_dma_size = size;
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
+	snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+	count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
+	snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+	snd_wss_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 0
+	snd_wss_debug(chip);
+#endif
+	return 0;
+}
+
+static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	unsigned char new_cdfr;
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	new_cdfr = snd_wss_get_format(chip, params_format(hw_params),
+			   params_channels(hw_params)) |
+			   snd_wss_get_rate(params_rate(hw_params));
+	chip->set_capture_format(chip, hw_params, new_cdfr);
+	return 0;
+}
+
+static int snd_wss_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_wss_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned long flags;
+	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
+	unsigned int count = snd_pcm_lib_period_bytes(substream);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->c_dma_size = size;
+	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
+	snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+	if (chip->hardware & WSS_HW_AD1848_MASK)
+		count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT],
+					  count);
+	else
+		count = snd_wss_get_count(chip->image[CS4231_REC_FORMAT],
+					  count);
+	count--;
+	if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) {
+		snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
+		snd_wss_out(chip, CS4231_PLY_UPR_CNT,
+			    (unsigned char) (count >> 8));
+	} else {
+		snd_wss_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
+		snd_wss_out(chip, CS4231_REC_UPR_CNT,
+			    (unsigned char) (count >> 8));
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+void snd_wss_overrange(struct snd_wss *chip)
+{
+	unsigned long flags;
+	unsigned char res;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	res = snd_wss_in(chip, CS4231_TEST_INIT);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */
+		chip->capture_substream->runtime->overrange++;
+}
+EXPORT_SYMBOL(snd_wss_overrange);
+
+irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
+{
+	struct snd_wss *chip = dev_id;
+	unsigned char status;
+
+	if (chip->hardware & WSS_HW_AD1848_MASK)
+		/* pretend it was the only possible irq for AD1848 */
+		status = CS4231_PLAYBACK_IRQ;
+	else
+		status = snd_wss_in(chip, CS4231_IRQ_STATUS);
+	if (status & CS4231_TIMER_IRQ) {
+		if (chip->timer)
+			snd_timer_interrupt(chip->timer, chip->timer->sticks);
+	}
+	if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) {
+		if (status & CS4231_PLAYBACK_IRQ) {
+			if (chip->mode & WSS_MODE_PLAY) {
+				if (chip->playback_substream)
+					snd_pcm_period_elapsed(chip->playback_substream);
+			}
+			if (chip->mode & WSS_MODE_RECORD) {
+				if (chip->capture_substream) {
+					snd_wss_overrange(chip);
+					snd_pcm_period_elapsed(chip->capture_substream);
+				}
+			}
+		}
+	} else {
+		if (status & CS4231_PLAYBACK_IRQ) {
+			if (chip->playback_substream)
+				snd_pcm_period_elapsed(chip->playback_substream);
+		}
+		if (status & CS4231_RECORD_IRQ) {
+			if (chip->capture_substream) {
+				snd_wss_overrange(chip);
+				snd_pcm_period_elapsed(chip->capture_substream);
+			}
+		}
+	}
+
+	spin_lock(&chip->reg_lock);
+	status = ~CS4231_ALL_IRQS | ~status;
+	if (chip->hardware & WSS_HW_AD1848_MASK)
+		wss_outb(chip, CS4231P(STATUS), 0);
+	else
+		snd_wss_outm(chip, CS4231_IRQ_STATUS, status, 0);
+	spin_unlock(&chip->reg_lock);
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(snd_wss_interrupt);
+
+static snd_pcm_uframes_t snd_wss_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
+		return 0;
+	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
+		return 0;
+	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static int snd_ad1848_probe(struct snd_wss *chip)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+	unsigned long flags;
+	unsigned char r;
+	unsigned short hardware = 0;
+	int err = 0;
+	int i;
+
+	while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
+		if (time_after(jiffies, timeout))
+			return -ENODEV;
+		cond_resched();
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+
+	/* set CS423x MODE 1 */
+	snd_wss_dout(chip, CS4231_MISC_INFO, 0);
+
+	snd_wss_dout(chip, CS4231_RIGHT_INPUT, 0x45); /* 0x55 & ~0x10 */
+	r = snd_wss_in(chip, CS4231_RIGHT_INPUT);
+	if (r != 0x45) {
+		/* RMGE always high on AD1847 */
+		if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45) {
+			err = -ENODEV;
+			goto out;
+		}
+		hardware = WSS_HW_AD1847;
+	} else {
+		snd_wss_dout(chip, CS4231_LEFT_INPUT,  0xaa);
+		r = snd_wss_in(chip, CS4231_LEFT_INPUT);
+		/* L/RMGE always low on AT2320 */
+		if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa) {
+			err = -ENODEV;
+			goto out;
+		}
+	}
+
+	/* clear pending IRQ */
+	wss_inb(chip, CS4231P(STATUS));
+	wss_outb(chip, CS4231P(STATUS), 0);
+	mb();
+
+	if ((chip->hardware & WSS_HW_TYPE_MASK) != WSS_HW_DETECT)
+		goto out;
+
+	if (hardware) {
+		chip->hardware = hardware;
+		goto out;
+	}
+
+	r = snd_wss_in(chip, CS4231_MISC_INFO);
+
+	/* set CS423x MODE 2 */
+	snd_wss_dout(chip, CS4231_MISC_INFO, CS4231_MODE2);
+	for (i = 0; i < 16; i++) {
+		if (snd_wss_in(chip, i) != snd_wss_in(chip, 16 + i)) {
+			/* we have more than 16 registers: check ID */
+			if ((r & 0xf) != 0xa)
+				goto out_mode;
+			/*
+			 * on CMI8330, CS4231_VERSION is volume control and
+			 * can be set to 0
+			 */
+			snd_wss_dout(chip, CS4231_VERSION, 0);
+			r = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
+			if (!r)
+				chip->hardware = WSS_HW_CMI8330;
+			goto out_mode;
+		}
+	}
+	if (r & 0x80)
+		chip->hardware = WSS_HW_CS4248;
+	else
+		chip->hardware = WSS_HW_AD1848;
+out_mode:
+	snd_wss_dout(chip, CS4231_MISC_INFO, 0);
+out:
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return err;
+}
+
+static int snd_wss_probe(struct snd_wss *chip)
+{
+	unsigned long flags;
+	int i, id, rev, regnum;
+	unsigned char *ptr;
+	unsigned int hw;
+
+	id = snd_ad1848_probe(chip);
+	if (id < 0)
+		return id;
+
+	hw = chip->hardware;
+	if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
+		for (i = 0; i < 50; i++) {
+			mb();
+			if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
+				msleep(2);
+			else {
+				spin_lock_irqsave(&chip->reg_lock, flags);
+				snd_wss_out(chip, CS4231_MISC_INFO,
+					    CS4231_MODE2);
+				id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
+				spin_unlock_irqrestore(&chip->reg_lock, flags);
+				if (id == 0x0a)
+					break;	/* this is valid value */
+			}
+		}
+		snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
+		if (id != 0x0a)
+			return -ENODEV;	/* no valid device found */
+
+		rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
+		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
+		if (rev == 0x80) {
+			unsigned char tmp = snd_wss_in(chip, 23);
+			snd_wss_out(chip, 23, ~tmp);
+			if (snd_wss_in(chip, 23) != tmp)
+				chip->hardware = WSS_HW_AD1845;
+			else
+				chip->hardware = WSS_HW_CS4231;
+		} else if (rev == 0xa0) {
+			chip->hardware = WSS_HW_CS4231A;
+		} else if (rev == 0xa2) {
+			chip->hardware = WSS_HW_CS4232;
+		} else if (rev == 0xb2) {
+			chip->hardware = WSS_HW_CS4232A;
+		} else if (rev == 0x83) {
+			chip->hardware = WSS_HW_CS4236;
+		} else if (rev == 0x03) {
+			chip->hardware = WSS_HW_CS4236B;
+		} else {
+			snd_printk("unknown CS chip with version 0x%x\n", rev);
+			return -ENODEV;		/* unknown CS4231 chip? */
+		}
+	}
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	wss_inb(chip, CS4231P(STATUS));	/* clear any pendings IRQ */
+	wss_outb(chip, CS4231P(STATUS), 0);
+	mb();
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	if (!(chip->hardware & WSS_HW_AD1848_MASK))
+		chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
+	switch (chip->hardware) {
+	case WSS_HW_INTERWAVE:
+		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
+		break;
+	case WSS_HW_CS4235:
+	case WSS_HW_CS4236B:
+	case WSS_HW_CS4237B:
+	case WSS_HW_CS4238B:
+	case WSS_HW_CS4239:
+		if (hw == WSS_HW_DETECT3)
+			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
+		else
+			chip->hardware = WSS_HW_CS4236;
+		break;
+	}
+
+	chip->image[CS4231_IFACE_CTRL] =
+	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
+	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
+	if (chip->hardware != WSS_HW_OPTI93X) {
+		chip->image[CS4231_ALT_FEATURE_1] = 0x80;
+		chip->image[CS4231_ALT_FEATURE_2] =
+			chip->hardware == WSS_HW_INTERWAVE ? 0xc2 : 0x01;
+	}
+	ptr = (unsigned char *) &chip->image;
+	regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
+	snd_wss_mce_down(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (i = 0; i < regnum; i++)	/* ok.. fill all registers */
+		snd_wss_out(chip, i, *ptr++);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	snd_wss_mce_up(chip);
+	snd_wss_mce_down(chip);
+
+	mdelay(2);
+
+	/* ok.. try check hardware version for CS4236+ chips */
+	if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
+		if (chip->hardware == WSS_HW_CS4236B) {
+			rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
+			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
+			id = snd_cs4236_ext_in(chip, CS4236_VERSION);
+			snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
+			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
+			if ((id & 0x1f) == 0x1d) {	/* CS4235 */
+				chip->hardware = WSS_HW_CS4235;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+					break;
+				default:
+					snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					chip->hardware = WSS_HW_CS4236B;
+					break;
+				default:
+					snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */
+				chip->hardware = WSS_HW_CS4237B;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					break;
+				default:
+					snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */
+				chip->hardware = WSS_HW_CS4238B;
+				switch (id >> 5) {
+				case 5:
+				case 6:
+				case 7:
+					break;
+				default:
+					snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
+				}
+			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */
+				chip->hardware = WSS_HW_CS4239;
+				switch (id >> 5) {
+				case 4:
+				case 5:
+				case 6:
+					break;
+				default:
+					snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
+				}
+			} else {
+				snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
+			}
+		}
+	}
+	return 0;		/* all things are ok.. */
+}
+
+/*
+
+ */
+
+static struct snd_pcm_hardware snd_wss_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_SYNC_START),
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.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 struct snd_pcm_hardware snd_wss_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_SYNC_START),
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.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 int snd_wss_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	runtime->hw = snd_wss_playback;
+
+	/* hardware limitation of older chipsets */
+	if (chip->hardware & WSS_HW_AD1848_MASK)
+		runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM |
+					 SNDRV_PCM_FMTBIT_S16_BE);
+
+	/* hardware bug in InterWave chipset */
+	if (chip->hardware == WSS_HW_INTERWAVE && chip->dma1 > 3)
+		runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
+
+	/* hardware limitation of cheap chips */
+	if (chip->hardware == WSS_HW_CS4235 ||
+	    chip->hardware == WSS_HW_CS4239)
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
+
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
+
+	if (chip->claim_dma) {
+		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
+			return err;
+	}
+
+	err = snd_wss_open(chip, WSS_MODE_PLAY);
+	if (err < 0) {
+		if (chip->release_dma)
+			chip->release_dma(chip, chip->dma_private_data, chip->dma1);
+		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+		return err;
+	}
+	chip->playback_substream = substream;
+	snd_pcm_set_sync(substream);
+	chip->rate_constraint(runtime);
+	return 0;
+}
+
+static int snd_wss_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	runtime->hw = snd_wss_capture;
+
+	/* hardware limitation of older chipsets */
+	if (chip->hardware & WSS_HW_AD1848_MASK)
+		runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM |
+					 SNDRV_PCM_FMTBIT_S16_BE);
+
+	/* hardware limitation of cheap chips */
+	if (chip->hardware == WSS_HW_CS4235 ||
+	    chip->hardware == WSS_HW_CS4239 ||
+	    chip->hardware == WSS_HW_OPTI93X)
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 |
+				      SNDRV_PCM_FMTBIT_S16_LE;
+
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
+	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
+
+	if (chip->claim_dma) {
+		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
+			return err;
+	}
+
+	err = snd_wss_open(chip, WSS_MODE_RECORD);
+	if (err < 0) {
+		if (chip->release_dma)
+			chip->release_dma(chip, chip->dma_private_data, chip->dma2);
+		snd_free_pages(runtime->dma_area, runtime->dma_bytes);
+		return err;
+	}
+	chip->capture_substream = substream;
+	snd_pcm_set_sync(substream);
+	chip->rate_constraint(runtime);
+	return 0;
+}
+
+static int snd_wss_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+
+	chip->playback_substream = NULL;
+	snd_wss_close(chip, WSS_MODE_PLAY);
+	return 0;
+}
+
+static int snd_wss_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_wss *chip = snd_pcm_substream_chip(substream);
+
+	chip->capture_substream = NULL;
+	snd_wss_close(chip, WSS_MODE_RECORD);
+	return 0;
+}
+
+static void snd_wss_thinkpad_twiddle(struct snd_wss *chip, int on)
+{
+	int tmp;
+
+	if (!chip->thinkpad_flag)
+		return;
+
+	outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
+	tmp = inb(AD1848_THINKPAD_CTL_PORT2);
+
+	if (on)
+		/* turn it on */
+		tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
+	else
+		/* turn it off */
+		tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
+
+	outb(tmp, AD1848_THINKPAD_CTL_PORT2);
+}
+
+#ifdef CONFIG_PM
+
+/* lowlevel suspend callback for CS4231 */
+static void snd_wss_suspend(struct snd_wss *chip)
+{
+	int reg;
+	unsigned long flags;
+
+	snd_pcm_suspend_all(chip->pcm);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++)
+		chip->image[reg] = snd_wss_in(chip, reg);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (chip->thinkpad_flag)
+		snd_wss_thinkpad_twiddle(chip, 0);
+}
+
+/* lowlevel resume callback for CS4231 */
+static void snd_wss_resume(struct snd_wss *chip)
+{
+	int reg;
+	unsigned long flags;
+	/* int timeout; */
+
+	if (chip->thinkpad_flag)
+		snd_wss_thinkpad_twiddle(chip, 1);
+	snd_wss_mce_up(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	for (reg = 0; reg < 32; reg++) {
+		switch (reg) {
+		case CS4231_VERSION:
+			break;
+		default:
+			snd_wss_out(chip, reg, chip->image[reg]);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+#if 1
+	snd_wss_mce_down(chip);
+#else
+	/* The following is a workaround to avoid freeze after resume on TP600E.
+	   This is the first half of copy of snd_wss_mce_down(), but doesn't
+	   include rescheduling.  -- iwai
+	   */
+	snd_wss_busy_wait(chip);
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	chip->mce_bit &= ~CS4231_MCE;
+	timeout = wss_inb(chip, CS4231P(REGSEL));
+	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (timeout == 0x80)
+		snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+	if ((timeout & CS4231_MCE) == 0 ||
+	    !(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) {
+		return;
+	}
+	snd_wss_busy_wait(chip);
+#endif
+}
+#endif /* CONFIG_PM */
+
+static int snd_wss_free(struct snd_wss *chip)
+{
+	release_and_free_resource(chip->res_port);
+	release_and_free_resource(chip->res_cport);
+	if (chip->irq >= 0) {
+		disable_irq(chip->irq);
+		if (!(chip->hwshare & WSS_HWSHARE_IRQ))
+			free_irq(chip->irq, (void *) chip);
+	}
+	if (!(chip->hwshare & WSS_HWSHARE_DMA1) && chip->dma1 >= 0) {
+		snd_dma_disable(chip->dma1);
+		free_dma(chip->dma1);
+	}
+	if (!(chip->hwshare & WSS_HWSHARE_DMA2) &&
+	    chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
+		snd_dma_disable(chip->dma2);
+		free_dma(chip->dma2);
+	}
+	if (chip->timer)
+		snd_device_free(chip->card, chip->timer);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_wss_dev_free(struct snd_device *device)
+{
+	struct snd_wss *chip = device->device_data;
+	return snd_wss_free(chip);
+}
+
+const char *snd_wss_chip_id(struct snd_wss *chip)
+{
+	switch (chip->hardware) {
+	case WSS_HW_CS4231:
+		return "CS4231";
+	case WSS_HW_CS4231A:
+		return "CS4231A";
+	case WSS_HW_CS4232:
+		return "CS4232";
+	case WSS_HW_CS4232A:
+		return "CS4232A";
+	case WSS_HW_CS4235:
+		return "CS4235";
+	case WSS_HW_CS4236:
+		return "CS4236";
+	case WSS_HW_CS4236B:
+		return "CS4236B";
+	case WSS_HW_CS4237B:
+		return "CS4237B";
+	case WSS_HW_CS4238B:
+		return "CS4238B";
+	case WSS_HW_CS4239:
+		return "CS4239";
+	case WSS_HW_INTERWAVE:
+		return "AMD InterWave";
+	case WSS_HW_OPL3SA2:
+		return chip->card->shortname;
+	case WSS_HW_AD1845:
+		return "AD1845";
+	case WSS_HW_OPTI93X:
+		return "OPTi 93x";
+	case WSS_HW_AD1847:
+		return "AD1847";
+	case WSS_HW_AD1848:
+		return "AD1848";
+	case WSS_HW_CS4248:
+		return "CS4248";
+	case WSS_HW_CMI8330:
+		return "CMI8330/C3D";
+	default:
+		return "???";
+	}
+}
+EXPORT_SYMBOL(snd_wss_chip_id);
+
+static int snd_wss_new(struct snd_card *card,
+			  unsigned short hardware,
+			  unsigned short hwshare,
+			  struct snd_wss **rchip)
+{
+	struct snd_wss *chip;
+
+	*rchip = NULL;
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->hardware = hardware;
+	chip->hwshare = hwshare;
+
+	spin_lock_init(&chip->reg_lock);
+	mutex_init(&chip->mce_mutex);
+	mutex_init(&chip->open_mutex);
+	chip->card = card;
+	chip->rate_constraint = snd_wss_xrate;
+	chip->set_playback_format = snd_wss_playback_format;
+	chip->set_capture_format = snd_wss_capture_format;
+	if (chip->hardware == WSS_HW_OPTI93X)
+		memcpy(&chip->image, &snd_opti93x_original_image,
+		       sizeof(snd_opti93x_original_image));
+	else
+		memcpy(&chip->image, &snd_wss_original_image,
+		       sizeof(snd_wss_original_image));
+	if (chip->hardware & WSS_HW_AD1848_MASK) {
+		chip->image[CS4231_PIN_CTRL] = 0;
+		chip->image[CS4231_TEST_INIT] = 0;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+int snd_wss_create(struct snd_card *card,
+		      unsigned long port,
+		      unsigned long cport,
+		      int irq, int dma1, int dma2,
+		      unsigned short hardware,
+		      unsigned short hwshare,
+		      struct snd_wss **rchip)
+{
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_wss_dev_free,
+	};
+	struct snd_wss *chip;
+	int err;
+
+	err = snd_wss_new(card, hardware, hwshare, &chip);
+	if (err < 0)
+		return err;
+
+	chip->irq = -1;
+	chip->dma1 = -1;
+	chip->dma2 = -1;
+
+	chip->res_port = request_region(port, 4, "WSS");
+	if (!chip->res_port) {
+		snd_printk(KERN_ERR "wss: can't grab port 0x%lx\n", port);
+		snd_wss_free(chip);
+		return -EBUSY;
+	}
+	chip->port = port;
+	if ((long)cport >= 0) {
+		chip->res_cport = request_region(cport, 8, "CS4232 Control");
+		if (!chip->res_cport) {
+			snd_printk(KERN_ERR
+				"wss: can't grab control port 0x%lx\n", cport);
+			snd_wss_free(chip);
+			return -ENODEV;
+		}
+	}
+	chip->cport = cport;
+	if (!(hwshare & WSS_HWSHARE_IRQ))
+		if (request_irq(irq, snd_wss_interrupt, IRQF_DISABLED,
+				"WSS", (void *) chip)) {
+			snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
+			snd_wss_free(chip);
+			return -EBUSY;
+		}
+	chip->irq = irq;
+	if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "WSS - 1")) {
+		snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1);
+		snd_wss_free(chip);
+		return -EBUSY;
+	}
+	chip->dma1 = dma1;
+	if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 &&
+	      dma2 >= 0 && request_dma(dma2, "WSS - 2")) {
+		snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2);
+		snd_wss_free(chip);
+		return -EBUSY;
+	}
+	if (dma1 == dma2 || dma2 < 0) {
+		chip->single_dma = 1;
+		chip->dma2 = chip->dma1;
+	} else
+		chip->dma2 = dma2;
+
+	if (hardware == WSS_HW_THINKPAD) {
+		chip->thinkpad_flag = 1;
+		chip->hardware = WSS_HW_DETECT; /* reset */
+		snd_wss_thinkpad_twiddle(chip, 1);
+	}
+
+	/* global setup */
+	if (snd_wss_probe(chip) < 0) {
+		snd_wss_free(chip);
+		return -ENODEV;
+	}
+	snd_wss_init(chip);
+
+#if 0
+	if (chip->hardware & WSS_HW_CS4232_MASK) {
+		if (chip->res_cport == NULL)
+			snd_printk("CS4232 control port features are not accessible\n");
+	}
+#endif
+
+	/* Register device */
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_wss_free(chip);
+		return err;
+	}
+
+#ifdef CONFIG_PM
+	/* Power Management */
+	chip->suspend = snd_wss_suspend;
+	chip->resume = snd_wss_resume;
+#endif
+
+	*rchip = chip;
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_create);
+
+static struct snd_pcm_ops snd_wss_playback_ops = {
+	.open =		snd_wss_playback_open,
+	.close =	snd_wss_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_wss_playback_hw_params,
+	.hw_free =	snd_wss_playback_hw_free,
+	.prepare =	snd_wss_playback_prepare,
+	.trigger =	snd_wss_trigger,
+	.pointer =	snd_wss_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_wss_capture_ops = {
+	.open =		snd_wss_capture_open,
+	.close =	snd_wss_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_wss_capture_hw_params,
+	.hw_free =	snd_wss_capture_hw_free,
+	.prepare =	snd_wss_capture_prepare,
+	.trigger =	snd_wss_trigger,
+	.pointer =	snd_wss_capture_pointer,
+};
+
+int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(chip->card, "WSS", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_wss_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_wss_capture_ops);
+
+	/* global setup */
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	if (chip->single_dma)
+		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
+	if (chip->hardware != WSS_HW_INTERWAVE)
+		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+	strcpy(pcm->name, snd_wss_chip_id(chip));
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_isa_data(),
+					      64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+
+	chip->pcm = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_pcm);
+
+static void snd_wss_timer_free(struct snd_timer *timer)
+{
+	struct snd_wss *chip = timer->private_data;
+	chip->timer = NULL;
+}
+
+int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer)
+{
+	struct snd_timer *timer;
+	struct snd_timer_id tid;
+	int err;
+
+	/* Timer initialization */
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = chip->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
+		return err;
+	strcpy(timer->name, snd_wss_chip_id(chip));
+	timer->private_data = chip;
+	timer->private_free = snd_wss_timer_free;
+	timer->hw = snd_wss_timer_table;
+	chip->timer = timer;
+	if (rtimer)
+		*rtimer = timer;
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_timer);
+
+/*
+ *  MIXER part
+ */
+
+static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[4] = {
+		"Line", "Aux", "Mic", "Mix"
+	};
+	static char *opl3sa_texts[4] = {
+		"Line", "CD", "Mic", "Mix"
+	};
+	static char *gusmax_texts[4] = {
+		"Line", "Synth", "Mic", "Mix"
+	};
+	char **ptexts = texts;
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+
+	if (snd_BUG_ON(!chip->card))
+		return -EINVAL;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 2;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 3)
+		uinfo->value.enumerated.item = 3;
+	if (!strcmp(chip->card->driver, "GUS MAX"))
+		ptexts = gusmax_texts;
+	switch (chip->hardware) {
+	case WSS_HW_INTERWAVE:
+		ptexts = gusmax_texts;
+		break;
+	case WSS_HW_OPL3SA2:
+		ptexts = opl3sa_texts;
+		break;
+	}
+	strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
+	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return 0;
+}
+
+static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned short left, right;
+	int change;
+
+	if (ucontrol->value.enumerated.item[0] > 3 ||
+	    ucontrol->value.enumerated.item[1] > 3)
+		return -EINVAL;
+	left = ucontrol->value.enumerated.item[0] << 6;
+	right = ucontrol->value.enumerated.item[1] << 6;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
+	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
+	change = left != chip->image[CS4231_LEFT_INPUT] ||
+		 right != chip->image[CS4231_RIGHT_INPUT];
+	snd_wss_out(chip, CS4231_LEFT_INPUT, left);
+	snd_wss_out(chip, CS4231_RIGHT_INPUT, right);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+
+int snd_wss_info_single(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_info_single);
+
+int snd_wss_get_single(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_get_single);
+
+int snd_wss_put_single(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0xff;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0xff;
+	int change;
+	unsigned short val;
+
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val <<= shift;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = (chip->image[reg] & ~(mask << shift)) | val;
+	change = val != chip->image[reg];
+	snd_wss_out(chip, reg, val);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+EXPORT_SYMBOL(snd_wss_put_single);
+
+int snd_wss_info_double(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_info_double);
+
+int snd_wss_get_double(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
+	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_get_double);
+
+int snd_wss_put_double(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int left_reg = kcontrol->private_value & 0xff;
+	int right_reg = (kcontrol->private_value >> 8) & 0xff;
+	int shift_left = (kcontrol->private_value >> 16) & 0x07;
+	int shift_right = (kcontrol->private_value >> 19) & 0x07;
+	int mask = (kcontrol->private_value >> 24) & 0xff;
+	int invert = (kcontrol->private_value >> 22) & 1;
+	int change;
+	unsigned short val1, val2;
+
+	val1 = ucontrol->value.integer.value[0] & mask;
+	val2 = ucontrol->value.integer.value[1] & mask;
+	if (invert) {
+		val1 = mask - val1;
+		val2 = mask - val2;
+	}
+	val1 <<= shift_left;
+	val2 <<= shift_right;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (left_reg != right_reg) {
+		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
+		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
+		change = val1 != chip->image[left_reg] ||
+			 val2 != chip->image[right_reg];
+		snd_wss_out(chip, left_reg, val1);
+		snd_wss_out(chip, right_reg, val2);
+	} else {
+		mask = (mask << shift_left) | (mask << shift_right);
+		val1 = (chip->image[left_reg] & ~mask) | val1 | val2;
+		change = val1 != chip->image[left_reg];
+		snd_wss_out(chip, left_reg, val1);
+	}
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return change;
+}
+EXPORT_SYMBOL(snd_wss_put_double);
+
+static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
+static struct snd_kcontrol_new snd_ad1848_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT,
+	   7, 7, 1, 1),
+WSS_DOUBLE_TLV("PCM Playback Volume", 0,
+	       CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
+	       db_scale_6bit),
+WSS_DOUBLE("Aux Playback Switch", 0,
+	   CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Aux Playback Volume", 0,
+	       CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+	       db_scale_5bit_12db_max),
+WSS_DOUBLE("Aux Playback Switch", 1,
+	   CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Aux Playback Volume", 1,
+	       CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+	       db_scale_5bit_12db_max),
+WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
+		0, 0, 15, 0, db_scale_rec_gain),
+{
+	.name = "Capture Source",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_wss_info_mux,
+	.get = snd_wss_get_mux,
+	.put = snd_wss_put_mux,
+},
+WSS_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 1, 63, 0,
+	       db_scale_6bit),
+};
+
+static struct snd_kcontrol_new snd_wss_controls[] = {
+WSS_DOUBLE("PCM Playback Switch", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Line Playback Volume", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
+WSS_DOUBLE("Aux Playback Switch", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_DOUBLE("Aux Playback Switch", 1,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 1,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
+WSS_SINGLE("Mono Playback Switch", 0,
+		CS4231_MONO_CTRL, 7, 1, 1),
+WSS_SINGLE("Mono Playback Volume", 0,
+		CS4231_MONO_CTRL, 0, 15, 1),
+WSS_SINGLE("Mono Output Playback Switch", 0,
+		CS4231_MONO_CTRL, 6, 1, 1),
+WSS_SINGLE("Mono Output Playback Bypass", 0,
+		CS4231_MONO_CTRL, 5, 1, 0),
+WSS_DOUBLE("Capture Volume", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_wss_info_mux,
+	.get = snd_wss_get_mux,
+	.put = snd_wss_put_mux,
+},
+WSS_DOUBLE("Mic Boost", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_SINGLE("Loopback Capture Switch", 0,
+		CS4231_LOOPBACK, 0, 1, 0),
+WSS_SINGLE("Loopback Capture Volume", 0,
+		CS4231_LOOPBACK, 2, 63, 1)
+};
+
+static struct snd_kcontrol_new snd_opti93x_controls[] = {
+WSS_DOUBLE("Master Playback Switch", 0,
+		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
+WSS_DOUBLE("Master Playback Volume", 0,
+		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
+WSS_DOUBLE("PCM Playback Switch", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+WSS_DOUBLE("PCM Playback Volume", 0,
+		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1),
+WSS_DOUBLE("FM Playback Switch", 0,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("FM Playback Volume", 0,
+		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Line Playback Switch", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE("Line Playback Volume", 0,
+		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1),
+WSS_DOUBLE("Mic Playback Switch", 0,
+		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Mic Playback Volume", 0,
+		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Mic Boost", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+WSS_DOUBLE("CD Playback Switch", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("CD Playback Volume", 0,
+		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Aux Playback Switch", 0,
+		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("Aux Playback Volume", 0,
+		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
+WSS_DOUBLE("Capture Volume", 0,
+		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_wss_info_mux,
+	.get = snd_wss_get_mux,
+	.put = snd_wss_put_mux,
+}
+};
+
+int snd_wss_mixer(struct snd_wss *chip)
+{
+	struct snd_card *card;
+	unsigned int idx;
+	int err;
+
+	if (snd_BUG_ON(!chip || !chip->pcm))
+		return -EINVAL;
+
+	card = chip->card;
+
+	strcpy(card->mixername, chip->pcm->name);
+
+	if (chip->hardware == WSS_HW_OPTI93X)
+		for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
+			err = snd_ctl_add(card,
+					snd_ctl_new1(&snd_opti93x_controls[idx],
+						     chip));
+			if (err < 0)
+				return err;
+		}
+	else if (chip->hardware & WSS_HW_AD1848_MASK)
+		for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) {
+			err = snd_ctl_add(card,
+					snd_ctl_new1(&snd_ad1848_controls[idx],
+						     chip));
+			if (err < 0)
+				return err;
+		}
+	else
+		for (idx = 0; idx < ARRAY_SIZE(snd_wss_controls); idx++) {
+			err = snd_ctl_add(card,
+					snd_ctl_new1(&snd_wss_controls[idx],
+						     chip));
+			if (err < 0)
+				return err;
+		}
+	return 0;
+}
+EXPORT_SYMBOL(snd_wss_mixer);
+
+const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction)
+{
+	return direction == SNDRV_PCM_STREAM_PLAYBACK ?
+		&snd_wss_playback_ops : &snd_wss_capture_ops;
+}
+EXPORT_SYMBOL(snd_wss_get_pcm_ops);
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_wss_init(void)
+{
+	return 0;
+}
+
+static void __exit alsa_wss_exit(void)
+{
+}
+
+module_init(alsa_wss_init);
+module_exit(alsa_wss_exit);
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
index fbef38a..1881cec 100644
--- a/sound/mips/au1x00.c
+++ b/sound/mips/au1x00.c
@@ -190,14 +190,16 @@
 static void
 au1000_dma_stop(struct audio_stream *stream)
 {
-	snd_assert(stream->buffer, return);
+	if (snd_BUG_ON(!stream->buffer))
+		return;
 	disable_dma(stream->dma);
 }
 
 static void
 au1000_dma_start(struct audio_stream *stream)
 {
-	snd_assert(stream->buffer, return);
+	if (snd_BUG_ON(!stream->buffer))
+		return;
 
 	init_dma(stream->dma);
 	if (get_dma_active_buffer(stream->dma) == 0) {
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index d4fafb6..1ca7427 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -24,13 +24,6 @@
 	  <file:Documentation/sound/oss/vwsnd> for more info on this driver's
 	  capabilities.
 
-config SOUND_HAL2
-	tristate "SGI HAL2 sound (EXPERIMENTAL)"
-	depends on SGI_IP22 && EXPERIMENTAL
-	help
-	  Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to
-	  use its on-board A2 audio system.
-
 config SOUND_AU1550_AC97
 	tristate "Au1550/Au1200 AC97 Sound"
 	depends on SOC_AU1550 || SOC_AU1200
@@ -546,34 +539,6 @@
 	  Base I/O port address for the CD-ROM interface of the Audio Excel
 	  DSP 16 card.
 
-choice
-	prompt "Audio Excel DSP 16"
-	optional
-	depends on SOUND_AEDSP16
-
-config AEDSP16_MSS
-	bool "MSS emulation"
-	depends on SOUND_MSS
-	help
-	  Answer Y if you want your audio card to emulate Microsoft Sound
-	  System. You should then say Y to "Microsoft Sound System support"
-	  and say N to "Audio Excel DSP 16 (SBPro emulation)".
-
-config AEDSP16_SBPRO
-	bool "SBPro emulation"
-	depends on SOUND_SB
-	help
-	  Answer Y if you want your audio card to emulate Sound Blaster Pro.
-	  You should then say Y to "100% Sound Blaster compatibles
-	  (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS
-	  emulation)".
-
-	  If you compile the driver into the kernel, you have to add
-	  "aedsp16=<io>,<irq>,<dma>,<mssio>,<mpuio>,<mouirq>" to the kernel
-	  command line.
-
-endchoice
-
 config SOUND_VIDC
 	tristate "VIDC 16-bit sound"
 	depends on ARM && (ARCH_ACORN || ARCH_CLPS7500)
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index c611514..e0ae4d4 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -10,7 +10,6 @@
 # Please leave it as is, cause the link order is significant !
 
 obj-$(CONFIG_SOUND_SH_DAC_AUDIO)	+= sh_dac_audio.o
-obj-$(CONFIG_SOUND_HAL2)	+= hal2.o
 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
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
index 51e1fde..a0274f3 100644
--- a/sound/oss/aedsp16.c
+++ b/sound/oss/aedsp16.c
@@ -29,14 +29,6 @@
 #include "sound_config.h"
 
 /*
- * Sanity checks
- */
-
-#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS)
-#error You have to enable only one of the MSS and SBPRO emulations.
-#endif
-
-/*
 
    READ THIS
 
diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig
index 3eb7827..f456574 100644
--- a/sound/oss/dmasound/Kconfig
+++ b/sound/oss/dmasound/Kconfig
@@ -42,3 +42,4 @@
 
 config DMASOUND
 	tristate
+	select SOUND_OSS_CORE
diff --git a/sound/oss/hal2.c b/sound/oss/hal2.c
deleted file mode 100644
index a94b9df..0000000
--- a/sound/oss/hal2.c
+++ /dev/null
@@ -1,1558 +0,0 @@
-/*
- *  Driver for A2 audio system used in SGI machines
- *  Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
- *  
- *  Based on Ulf Carlsson's code.
- *
- *  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.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Supported devices:
- *  /dev/dsp    standard dsp device, (mostly) OSS compatible
- *  /dev/mixer	standard mixer device, (mostly) OSS compatible
- *
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/sound.h>
-#include <linux/soundcard.h>
-#include <linux/mutex.h>
-
-
-#include <asm/io.h>
-#include <asm/sgi/hpc3.h>
-#include <asm/sgi/ip22.h>
-
-#include "hal2.h"
-
-#if 0
-#define DEBUG(args...)		printk(args)
-#else
-#define DEBUG(args...)
-#endif
-
-#if 0 
-#define DEBUG_MIX(args...)	printk(args)
-#else
-#define DEBUG_MIX(args...)
-#endif
-
-/*
- * Before touching these look how it works. It is a bit unusual I know,
- * but it helps to keep things simple. This driver is considered complete
- * and I won't add any new features although hardware has many cool
- * capabilities.
- * (Historical note: HAL2 driver was first written by Ulf Carlsson - ALSA
- * 0.3 running with 2.2.x kernel. Then ALSA changed completely and it
- * seemed easier to me to write OSS driver from scratch - this one. Now
- * when ALSA is official part of 2.6 kernel it's time to write ALSA driver
- * using (hopefully) final version of ALSA interface)
- */
-#define H2_BLOCK_SIZE	1024
-#define H2_ADC_BUFSIZE	8192
-#define H2_DAC_BUFSIZE	16834
-
-struct hal2_pbus {
-	struct hpc3_pbus_dmacregs *pbus;
-	int pbusnr;
-	unsigned int ctrl;		/* Current state of pbus->pbdma_ctrl */
-};
-
-struct hal2_desc {
-	struct hpc_dma_desc desc;
-	u32 cnt;			/* don't touch, it is also padding */
-};
-
-struct hal2_codec {
-	unsigned char *buffer;
-	struct hal2_desc *desc;
-	int desc_count;
-	int tail, head;			/* tail index, head index */
-	struct hal2_pbus pbus;
-	unsigned int format;		/* Audio data format */
-	int voices;			/* mono/stereo */
-	unsigned int sample_rate;
-	unsigned int master;		/* Master frequency */
-	unsigned short mod;		/* MOD value */
-	unsigned short inc;		/* INC value */
-
-	wait_queue_head_t dma_wait;
-	spinlock_t lock;
-	struct mutex sem;
-
-	int usecount;			/* recording and playback are
-					 * independent */
-};
-
-#define H2_MIX_OUTPUT_ATT	0
-#define H2_MIX_INPUT_GAIN	1
-#define H2_MIXERS		2
-struct hal2_mixer {
-	int modcnt;
-	unsigned int master;
-	unsigned int volume[H2_MIXERS];
-};
-
-struct hal2_card {
-	int dev_dsp;			/* audio device */
-	int dev_mixer;			/* mixer device */
-	int dev_midi;			/* midi device */
-
-	struct hal2_ctl_regs *ctl_regs;	/* HAL2 ctl registers */
-	struct hal2_aes_regs *aes_regs;	/* HAL2 aes registers */
-	struct hal2_vol_regs *vol_regs;	/* HAL2 vol registers */
-	struct hal2_syn_regs *syn_regs;	/* HAL2 syn registers */
-
-	struct hal2_codec dac;
-	struct hal2_codec adc;
-	struct hal2_mixer mixer;
-};
-
-#define H2_INDIRECT_WAIT(regs)	while (regs->isr & H2_ISR_TSTATUS);
-
-#define H2_READ_ADDR(addr)	(addr | (1<<7))
-#define H2_WRITE_ADDR(addr)	(addr)
-
-static char *hal2str = "HAL2";
-
-/*
- * I doubt anyone has a machine with two HAL2 cards. It's possible to
- * have two HPC's, so it is probably possible to have two HAL2 cards.
- * Try to deal with it, but note that it is not tested.
- */
-#define MAXCARDS	2
-static struct hal2_card* hal2_card[MAXCARDS];
-
-static const struct {
-	unsigned char idx:4, avail:1;
-} mixtable[SOUND_MIXER_NRDEVICES] = {
-	[SOUND_MIXER_PCM]	= { H2_MIX_OUTPUT_ATT, 1 },	/* voice */
-	[SOUND_MIXER_MIC]	= { H2_MIX_INPUT_GAIN, 1 },	/* mic */
-};
-
-#define H2_SUPPORTED_FORMATS	(AFMT_S16_LE | AFMT_S16_BE)
-
-static inline void hal2_isr_write(struct hal2_card *hal2, u16 val)
-{
-	hal2->ctl_regs->isr = val;
-}
-
-static inline u16 hal2_isr_look(struct hal2_card *hal2)
-{
-	return hal2->ctl_regs->isr;
-}
-
-static inline u16 hal2_rev_look(struct hal2_card *hal2)
-{
-	return hal2->ctl_regs->rev;
-}
-
-#ifdef HAL2_DUMP_REGS
-static u16 hal2_i_look16(struct hal2_card *hal2, u16 addr)
-{
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->iar = H2_READ_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-	return regs->idr0;
-}
-#endif
-
-static u32 hal2_i_look32(struct hal2_card *hal2, u16 addr)
-{
-	u32 ret;
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->iar = H2_READ_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-	ret = regs->idr0 & 0xffff;
-	regs->iar = H2_READ_ADDR(addr | 0x1);
-	H2_INDIRECT_WAIT(regs);
-	ret |= (regs->idr0 & 0xffff) << 16;
-	return ret;
-}
-
-static void hal2_i_write16(struct hal2_card *hal2, u16 addr, u16 val)
-{
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->idr0 = val;
-	regs->idr1 = 0;
-	regs->idr2 = 0;
-	regs->idr3 = 0;
-	regs->iar = H2_WRITE_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_write32(struct hal2_card *hal2, u16 addr, u32 val)
-{
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->idr0 = val & 0xffff;
-	regs->idr1 = val >> 16;
-	regs->idr2 = 0;
-	regs->idr3 = 0;
-	regs->iar = H2_WRITE_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_setbit16(struct hal2_card *hal2, u16 addr, u16 bit)
-{
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->iar = H2_READ_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-	regs->idr0 = (regs->idr0 & 0xffff) | bit;
-	regs->idr1 = 0;
-	regs->idr2 = 0;
-	regs->idr3 = 0;
-	regs->iar = H2_WRITE_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_setbit32(struct hal2_card *hal2, u16 addr, u32 bit)
-{
-	u32 tmp;
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->iar = H2_READ_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-	tmp = (regs->idr0 & 0xffff) | (regs->idr1 << 16) | bit;
-	regs->idr0 = tmp & 0xffff;
-	regs->idr1 = tmp >> 16;
-	regs->idr2 = 0;
-	regs->idr3 = 0;
-	regs->iar = H2_WRITE_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-}
-
-static void hal2_i_clearbit16(struct hal2_card *hal2, u16 addr, u16 bit)
-{
-	struct hal2_ctl_regs *regs = hal2->ctl_regs;
-
-	regs->iar = H2_READ_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-	regs->idr0 = (regs->idr0 & 0xffff) & ~bit;
-	regs->idr1 = 0;
-	regs->idr2 = 0;
-	regs->idr3 = 0;
-	regs->iar = H2_WRITE_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-}
-
-#if 0
-static void hal2_i_clearbit32(struct hal2_card *hal2, u16 addr, u32 bit)
-{
-	u32 tmp;
-	hal2_ctl_regs_t *regs = hal2->ctl_regs;
-
-	regs->iar = H2_READ_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-	tmp = ((regs->idr0 & 0xffff) | (regs->idr1 << 16)) & ~bit;
-	regs->idr0 = tmp & 0xffff;
-	regs->idr1 = tmp >> 16;
-	regs->idr2 = 0;
-	regs->idr3 = 0;
-	regs->iar = H2_WRITE_ADDR(addr);
-	H2_INDIRECT_WAIT(regs);
-}
-#endif
-
-#ifdef HAL2_DUMP_REGS
-static void hal2_dump_regs(struct hal2_card *hal2)
-{
-	DEBUG("isr: %08hx ", hal2_isr_look(hal2));
-	DEBUG("rev: %08hx\n", hal2_rev_look(hal2));
-	DEBUG("relay: %04hx\n", hal2_i_look16(hal2, H2I_RELAY_C));
-	DEBUG("port en: %04hx ", hal2_i_look16(hal2, H2I_DMA_PORT_EN));
-	DEBUG("dma end: %04hx ", hal2_i_look16(hal2, H2I_DMA_END));
-	DEBUG("dma drv: %04hx\n", hal2_i_look16(hal2, H2I_DMA_DRV));
-	DEBUG("syn ctl: %04hx ", hal2_i_look16(hal2, H2I_SYNTH_C));
-	DEBUG("aesrx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESRX_C));
-	DEBUG("aestx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESTX_C));
-	DEBUG("dac ctl1: %04hx ", hal2_i_look16(hal2, H2I_ADC_C1));
-	DEBUG("dac ctl2: %08x ", hal2_i_look32(hal2, H2I_ADC_C2));
-	DEBUG("adc ctl1: %04hx ", hal2_i_look16(hal2, H2I_DAC_C1));
-	DEBUG("adc ctl2: %08x ", hal2_i_look32(hal2, H2I_DAC_C2));
-	DEBUG("syn map: %04hx\n", hal2_i_look16(hal2, H2I_SYNTH_MAP_C));
-	DEBUG("bres1 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES1_C1));
-	DEBUG("bres1 ctl2: %04x ", hal2_i_look32(hal2, H2I_BRES1_C2));
-	DEBUG("bres2 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES2_C1));
-	DEBUG("bres2 ctl2: %04x ", hal2_i_look32(hal2, H2I_BRES2_C2));
-	DEBUG("bres3 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES3_C1));
-	DEBUG("bres3 ctl2: %04x\n", hal2_i_look32(hal2, H2I_BRES3_C2));
-}
-#endif
-
-static struct hal2_card* hal2_dsp_find_card(int minor)
-{
-	int i;
-
-	for (i = 0; i < MAXCARDS; i++)
-		if (hal2_card[i] != NULL && hal2_card[i]->dev_dsp == minor)
-			return hal2_card[i];
-	return NULL;
-}
-
-static struct hal2_card* hal2_mixer_find_card(int minor)
-{
-	int i;
-
-	for (i = 0; i < MAXCARDS; i++)
-		if (hal2_card[i] != NULL && hal2_card[i]->dev_mixer == minor)
-			return hal2_card[i];
-	return NULL;
-}
-
-static void hal2_inc_head(struct hal2_codec *codec)
-{
-	codec->head++;
-	if (codec->head == codec->desc_count)
-		codec->head = 0;
-}
-
-static void hal2_inc_tail(struct hal2_codec *codec)
-{
-	codec->tail++;
-	if (codec->tail == codec->desc_count)
-		codec->tail = 0;
-}
-
-static void hal2_dac_interrupt(struct hal2_codec *dac)
-{
-	int running;
-
-	spin_lock(&dac->lock);
-	/* if tail buffer contains zero samples DMA stream was already
-	 * stopped */
-	running = dac->desc[dac->tail].cnt;
-	dac->desc[dac->tail].cnt = 0;
-	dac->desc[dac->tail].desc.cntinfo = HPCDMA_XIE | HPCDMA_EOX;
-	/* we just proccessed empty buffer, don't update tail pointer */
-	if (running)
-		hal2_inc_tail(dac);
-	spin_unlock(&dac->lock);
-
-	wake_up(&dac->dma_wait);
-}
-
-static void hal2_adc_interrupt(struct hal2_codec *adc)
-{
-	int running;
-
-	spin_lock(&adc->lock);
-	/* if head buffer contains nonzero samples DMA stream was already
-	 * stopped */
-	running = !adc->desc[adc->head].cnt;
-	adc->desc[adc->head].cnt = H2_BLOCK_SIZE;
-	adc->desc[adc->head].desc.cntinfo = HPCDMA_XIE | HPCDMA_EOR;
-	/* we just proccessed empty buffer, don't update head pointer */
-	if (running)
-		hal2_inc_head(adc);
-	spin_unlock(&adc->lock);
-
-	wake_up(&adc->dma_wait);
-}
-
-static irqreturn_t hal2_interrupt(int irq, void *dev_id)
-{
-	struct hal2_card *hal2 = dev_id;
-	irqreturn_t ret = IRQ_NONE;
-
-	/* decide what caused this interrupt */
-	if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
-		hal2_dac_interrupt(&hal2->dac);
-		ret = IRQ_HANDLED;
-	}
-	if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
-		hal2_adc_interrupt(&hal2->adc);
-		ret = IRQ_HANDLED;
-	}
-	return ret;
-}
-
-static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
-{
-	unsigned short mod;
-	
-	DEBUG("rate: %d\n", rate);
-	
-	if (rate < 4000) rate = 4000;
-	else if (rate > 48000) rate = 48000;
-
-	if (44100 % rate < 48000 % rate) {
-		mod = 4 * 44100 / rate;
-		codec->master = 44100;
-	} else {
-		mod = 4 * 48000 / rate;
-		codec->master = 48000;
-	}
-
-	codec->inc = 4;
-	codec->mod = mod;
-	rate = 4 * codec->master / mod;
-
-	DEBUG("real_rate: %d\n", rate);
-
-	return rate;
-}
-
-static void hal2_set_dac_rate(struct hal2_card *hal2)
-{
-	unsigned int master = hal2->dac.master;
-	int inc = hal2->dac.inc;
-	int mod = hal2->dac.mod;
-
-	DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod);
-	
-	hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
-	hal2_i_write32(hal2, H2I_BRES1_C2, ((0xffff & (inc - mod - 1)) << 16) | inc);
-}
-
-static void hal2_set_adc_rate(struct hal2_card *hal2)
-{
-	unsigned int master = hal2->adc.master;
-	int inc = hal2->adc.inc;
-	int mod = hal2->adc.mod;
-
-	DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod);
-	
-	hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0);
-	hal2_i_write32(hal2, H2I_BRES2_C2, ((0xffff & (inc - mod - 1)) << 16) | inc);
-}
-
-static void hal2_setup_dac(struct hal2_card *hal2)
-{
-	unsigned int fifobeg, fifoend, highwater, sample_size;
-	struct hal2_pbus *pbus = &hal2->dac.pbus;
-
-	DEBUG("hal2_setup_dac\n");
-	
-	/* Now we set up some PBUS information. The PBUS needs information about
-	 * what portion of the fifo it will use. If it's receiving or
-	 * transmitting, and finally whether the stream is little endian or big
-	 * endian. The information is written later, on the start call.
-	 */
-	sample_size = 2 * hal2->dac.voices;
-	/* Fifo should be set to hold exactly four samples. Highwater mark
-	 * should be set to two samples. */
-	highwater = (sample_size * 2) >> 1;	/* halfwords */
-	fifobeg = 0;				/* playback is first */
-	fifoend = (sample_size * 4) >> 3;	/* doublewords */
-	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD |
-		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24) |
-		     (hal2->dac.format & AFMT_S16_LE ? HPC3_PDMACTRL_SEL : 0);
-	/* We disable everything before we do anything at all */
-	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
-	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
-	/* Setup the HAL2 for playback */
-	hal2_set_dac_rate(hal2);
-	/* Set endianess */
-	if (hal2->dac.format & AFMT_S16_LE)
-		hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
-	else
-		hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
-	/* Set DMA bus */
-	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
-	/* We are using 1st Bresenham clock generator for playback */
-	hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
-			| (1 << H2I_C1_CLKID_SHIFT)
-			| (hal2->dac.voices << H2I_C1_DATAT_SHIFT));
-}
-
-static void hal2_setup_adc(struct hal2_card *hal2)
-{
-	unsigned int fifobeg, fifoend, highwater, sample_size;
-	struct hal2_pbus *pbus = &hal2->adc.pbus;
-
-	DEBUG("hal2_setup_adc\n");
-
-	sample_size = 2 * hal2->adc.voices;
-	highwater = (sample_size * 2) >> 1;		/* halfwords */
-	fifobeg = (4 * 4) >> 3;				/* record is second */
-	fifoend = (4 * 4 + sample_size * 4) >> 3;	/* doublewords */
-	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD | 
-		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24) |
-		     (hal2->adc.format & AFMT_S16_LE ? HPC3_PDMACTRL_SEL : 0);
-	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
-	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
-	/* Setup the HAL2 for record */
-	hal2_set_adc_rate(hal2);
-	/* Set endianess */
-	if (hal2->adc.format & AFMT_S16_LE)
-		hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
-	else
-		hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
-	/* Set DMA bus */
-	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
-	/* We are using 2nd Bresenham clock generator for record */
-	hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
-			| (2 << H2I_C1_CLKID_SHIFT)
-			| (hal2->adc.voices << H2I_C1_DATAT_SHIFT));
-}
-
-static dma_addr_t hal2_desc_addr(struct hal2_codec *codec, int i)
-{
-	if (--i < 0)
-		i = codec->desc_count - 1;
-	return codec->desc[i].desc.pnext;
-}
-
-static void hal2_start_dac(struct hal2_card *hal2)
-{
-	struct hal2_codec *dac = &hal2->dac;
-	struct hal2_pbus *pbus = &dac->pbus;
-
-	pbus->pbus->pbdma_dptr = hal2_desc_addr(dac, dac->tail);
-	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
-	/* enable DAC */
-	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
-}
-
-static void hal2_start_adc(struct hal2_card *hal2)
-{
-	struct hal2_codec *adc = &hal2->adc;
-	struct hal2_pbus *pbus = &adc->pbus;
-
-	pbus->pbus->pbdma_dptr = hal2_desc_addr(adc, adc->head);
-	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
-	/* enable ADC */
-	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
-}
-
-static inline void hal2_stop_dac(struct hal2_card *hal2)
-{
-	hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
-	/* The HAL2 itself may remain enabled safely */
-}
-
-static inline void hal2_stop_adc(struct hal2_card *hal2)
-{
-	hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
-}
-
-static int hal2_alloc_dmabuf(struct hal2_codec *codec, int size,
-			     int count, int cntinfo, int dir)
-{
-	struct hal2_desc *desc, *dma_addr;
-	int i;
-
-	DEBUG("allocating %dk DMA buffer.\n", size / 1024);
-
-	codec->buffer = (unsigned char *)__get_free_pages(GFP_KERNEL | GFP_DMA,
-							  get_order(size));
-	if (!codec->buffer)
-		return -ENOMEM;
-	desc = dma_alloc_coherent(NULL, count * sizeof(struct hal2_desc),
-				  (dma_addr_t *)&dma_addr, GFP_KERNEL);
-	if (!desc) {
-		free_pages((unsigned long)codec->buffer, get_order(size));
-		return -ENOMEM;
-	}
-	codec->desc = desc;
-	for (i = 0; i < count; i++) {
-		desc->desc.pbuf = dma_map_single(NULL,
-			(void *)(codec->buffer + i * H2_BLOCK_SIZE),
-			H2_BLOCK_SIZE, dir);
-		desc->desc.cntinfo = cntinfo;
-		desc->desc.pnext = (i == count - 1) ?
-				   (u32)dma_addr : (u32)(dma_addr + i + 1);
-		desc->cnt = 0;
-		desc++;
-	}
-	codec->desc_count = count;
-	codec->head = codec->tail = 0;
-	return 0;
-}
-
-static int hal2_alloc_dac_dmabuf(struct hal2_codec *codec)
-{
-	return hal2_alloc_dmabuf(codec, H2_DAC_BUFSIZE,
-				 H2_DAC_BUFSIZE / H2_BLOCK_SIZE,
-				 HPCDMA_XIE | HPCDMA_EOX,
-				 DMA_TO_DEVICE);
-}
-
-static int hal2_alloc_adc_dmabuf(struct hal2_codec *codec)
-{
-	return hal2_alloc_dmabuf(codec, H2_ADC_BUFSIZE,
-				 H2_ADC_BUFSIZE / H2_BLOCK_SIZE,
-				 HPCDMA_XIE | H2_BLOCK_SIZE,
-				 DMA_TO_DEVICE);
-}
-
-static void hal2_free_dmabuf(struct hal2_codec *codec, int size, int dir)
-{
-	dma_addr_t dma_addr;
-	int i;
-
-	dma_addr = codec->desc[codec->desc_count - 1].desc.pnext;
-	for (i = 0; i < codec->desc_count; i++)
-		dma_unmap_single(NULL, codec->desc[i].desc.pbuf,
-				 H2_BLOCK_SIZE, dir);
-	dma_free_coherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
-			  (void *)codec->desc, dma_addr);
-	free_pages((unsigned long)codec->buffer, get_order(size));
-}
-
-static void hal2_free_dac_dmabuf(struct hal2_codec *codec)
-{
-	return hal2_free_dmabuf(codec, H2_DAC_BUFSIZE, DMA_TO_DEVICE);
-}
-
-static void hal2_free_adc_dmabuf(struct hal2_codec *codec)
-{
-	return hal2_free_dmabuf(codec, H2_ADC_BUFSIZE, DMA_FROM_DEVICE);
-}
-
-/* 
- * Add 'count' bytes to 'buffer' from DMA ring buffers. Return number of
- * bytes added or -EFAULT if copy_from_user failed.
- */
-static int hal2_get_buffer(struct hal2_card *hal2, char *buffer, int count)
-{
-	unsigned long flags;
-	int size, ret = 0;
-	unsigned char *buf;
-	struct hal2_desc *tail;
-	struct hal2_codec *adc = &hal2->adc;
-
-	DEBUG("getting %d bytes ", count);
-
-	spin_lock_irqsave(&adc->lock, flags);
-	tail = &adc->desc[adc->tail];
-	/* enable DMA stream if there are no data */
-	if (!tail->cnt && !(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT))
-		hal2_start_adc(hal2);
-	while (tail->cnt > 0 && count > 0) {
-		size = min((int)tail->cnt, count);
-		buf = &adc->buffer[(adc->tail + 1) * H2_BLOCK_SIZE - tail->cnt];
-		spin_unlock_irqrestore(&adc->lock, flags);
-		dma_sync_single(NULL, tail->desc.pbuf, size, DMA_FROM_DEVICE);
-		if (copy_to_user(buffer, buf, size)) {
-			ret = -EFAULT;
-			goto out;
-		}
-		spin_lock_irqsave(&adc->lock, flags);
-		tail->cnt -= size;
-		/* buffer is empty, update tail pointer */
-		if (tail->cnt == 0) {
-			tail->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
-			hal2_inc_tail(adc);
-			tail = &adc->desc[adc->tail];
-			/* enable DMA stream again if needed */
-			if (!(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT))
-				hal2_start_adc(hal2);
-		}
-		buffer += size;
-		ret += size;
-		count -= size;
-
-		DEBUG("(%d) ", size);
-	}
-	spin_unlock_irqrestore(&adc->lock, flags);
-out:
-	DEBUG("\n");
-
-	return ret;
-} 
-
-/* 
- * Add 'count' bytes from 'buffer' to DMA ring buffers. Return number of
- * bytes added or -EFAULT if copy_from_user failed.
- */
-static int hal2_add_buffer(struct hal2_card *hal2, char *buffer, int count)
-{
-	unsigned long flags;
-	unsigned char *buf;
-	int size, ret = 0;
-	struct hal2_desc *head;
-	struct hal2_codec *dac = &hal2->dac;
-
-	DEBUG("adding %d bytes ", count);
-
-	spin_lock_irqsave(&dac->lock, flags);
-	head = &dac->desc[dac->head];
-	while (head->cnt == 0 && count > 0) {
-		size = min((int)H2_BLOCK_SIZE, count);
-		buf = &dac->buffer[dac->head * H2_BLOCK_SIZE];
-		spin_unlock_irqrestore(&dac->lock, flags);
-		if (copy_from_user(buf, buffer, size)) {
-			ret = -EFAULT;
-			goto out;
-		}
-		dma_sync_single(NULL, head->desc.pbuf, size, DMA_TO_DEVICE);
-		spin_lock_irqsave(&dac->lock, flags);
-		head->desc.cntinfo = size | HPCDMA_XIE;
-		head->cnt = size;
-		buffer += size;
-		ret += size;
-		count -= size;
-		hal2_inc_head(dac);
-		head = &dac->desc[dac->head];
-
-		DEBUG("(%d) ", size);
-	}
-	if (!(dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) && ret > 0)
-		hal2_start_dac(hal2);
-	spin_unlock_irqrestore(&dac->lock, flags);
-out:
-	DEBUG("\n");
-
-	return ret;
-}
-
-#define hal2_reset_dac_pointer(hal2)	hal2_reset_pointer(hal2, 1)
-#define hal2_reset_adc_pointer(hal2)	hal2_reset_pointer(hal2, 0)
-static void hal2_reset_pointer(struct hal2_card *hal2, int is_dac)
-{
-	int i;
-	struct hal2_codec *codec = (is_dac) ? &hal2->dac : &hal2->adc;
-
-	DEBUG("hal2_reset_pointer\n");
-
-	for (i = 0; i < codec->desc_count; i++) {
-		codec->desc[i].cnt = 0;
-		codec->desc[i].desc.cntinfo = HPCDMA_XIE | (is_dac) ?
-					      HPCDMA_EOX : H2_BLOCK_SIZE;
-	}
-	codec->head = codec->tail = 0;
-}
-
-static int hal2_sync_dac(struct hal2_card *hal2)
-{
-	DECLARE_WAITQUEUE(wait, current);
-	struct hal2_codec *dac = &hal2->dac;
-	int ret = 0;
-	unsigned long flags;
-	signed long timeout = 1000 * H2_BLOCK_SIZE * 2 * dac->voices *
-			      HZ / dac->sample_rate / 900;
-
-	while (dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) {
-		add_wait_queue(&dac->dma_wait, &wait);
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(timeout);
-		spin_lock_irqsave(&dac->lock, flags);
-		if (dac->desc[dac->tail].cnt)
-			ret = -ETIME;
-		spin_unlock_irqrestore(&dac->lock, flags);
-		if (signal_pending(current))
-			ret = -ERESTARTSYS;
-		if (ret) {
-			hal2_stop_dac(hal2);
-			hal2_reset_dac_pointer(hal2);
-		}
-		remove_wait_queue(&dac->dma_wait, &wait);
-	}
-
-	return ret;
-}
-
-static int hal2_write_mixer(struct hal2_card *hal2, int index, int vol)
-{
-	unsigned int l, r, tmp;
-
-	DEBUG_MIX("mixer %d write\n", index);
-
-	if (index >= SOUND_MIXER_NRDEVICES || !mixtable[index].avail)
-		return -EINVAL;
-
-	r = (vol >> 8) & 0xff;
-	if (r > 100)
-		r = 100;
-	l = vol & 0xff;
-	if (l > 100)
-		l = 100;
-
-	hal2->mixer.volume[mixtable[index].idx] = l | (r << 8);
-
-	switch (mixtable[index].idx) {
-	case H2_MIX_OUTPUT_ATT:
-
-		DEBUG_MIX("output attenuator %d,%d\n", l, r);
-
-		if (r | l) {
-			tmp = hal2_i_look32(hal2, H2I_DAC_C2);
-			tmp &= ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
-
-			/* Attenuator has five bits */
-			l = 31 * (100 - l) / 99;
-			r = 31 * (100 - r) / 99;
-
-			DEBUG_MIX("left: %d, right %d\n", l, r);
-
-			tmp |= (l << H2I_C2_L_ATT_SHIFT) & H2I_C2_L_ATT_M;
-			tmp |= (r << H2I_C2_R_ATT_SHIFT) & H2I_C2_R_ATT_M;
-			hal2_i_write32(hal2, H2I_DAC_C2, tmp);
-		} else 
-			hal2_i_setbit32(hal2, H2I_DAC_C2, H2I_C2_MUTE);
-		break;
-	case H2_MIX_INPUT_GAIN:
-
-		DEBUG_MIX("input gain %d,%d\n", l, r);
-
-		tmp = hal2_i_look32(hal2, H2I_ADC_C2);
-		tmp &= ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
-
-		/* Gain control has four bits */
-		l = 16 * l / 100;
-		r = 16 * r / 100;
-
-		DEBUG_MIX("left: %d, right %d\n", l, r);
-
-		tmp |= (l << H2I_C2_L_GAIN_SHIFT) & H2I_C2_L_GAIN_M;
-		tmp |= (r << H2I_C2_R_GAIN_SHIFT) & H2I_C2_R_GAIN_M;
-		hal2_i_write32(hal2, H2I_ADC_C2, tmp);
-
-		break;
-	}
-
-	return 0;
-}
-
-static void hal2_init_mixer(struct hal2_card *hal2)
-{
-	int i;
-
-	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
-		if (mixtable[i].avail)
-			hal2->mixer.volume[mixtable[i].idx] = 100 | (100 << 8);
-
-	/* disable attenuator */
-	hal2_i_write32(hal2, H2I_DAC_C2, 0);
-	/* set max input gain */
-	hal2_i_write32(hal2, H2I_ADC_C2, H2I_C2_MUTE |
-			(H2I_C2_L_GAIN_M << H2I_C2_L_GAIN_SHIFT) |
-			(H2I_C2_R_GAIN_M << H2I_C2_R_GAIN_SHIFT));
-	/* set max volume */
-	hal2->mixer.master = 0xff;
-	hal2->vol_regs->left = 0xff;
-	hal2->vol_regs->right = 0xff;
-}
-
-/*
- * XXX: later i'll implement mixer for main volume which will be disabled
- * by default. enabling it users will be allowed to have master volume level
- * control on panel in their favourite X desktop
- */
-static void hal2_volume_control(int direction)
-{
-	unsigned int master = hal2_card[0]->mixer.master;
-	struct hal2_vol_regs *vol = hal2_card[0]->vol_regs;
-
-	/* volume up */
-	if (direction > 0 && master < 0xff)
-		master++;
-	/* volume down */
-	else if (direction < 0 && master > 0)
-		master--;
-	/* TODO: mute/unmute */
-	vol->left = master;
-	vol->right = master;
-	hal2_card[0]->mixer.master = master;
-}
-
-static int hal2_mixer_ioctl(struct hal2_card *hal2, unsigned int cmd,
-			    unsigned long arg)
-{
-	int val;
-
-        if (cmd == SOUND_MIXER_INFO) {
-		mixer_info info;
-
-		memset(&info, 0, sizeof(info));
-		strlcpy(info.id, hal2str, sizeof(info.id));
-		strlcpy(info.name, hal2str, sizeof(info.name));
-		info.modify_counter = hal2->mixer.modcnt;
-		if (copy_to_user((void *)arg, &info, sizeof(info)))
-			return -EFAULT;
-		return 0;
-	}
-	if (cmd == SOUND_OLD_MIXER_INFO) {
-		_old_mixer_info info;
-
-		memset(&info, 0, sizeof(info));
-		strlcpy(info.id, hal2str, sizeof(info.id));
-		strlcpy(info.name, hal2str, sizeof(info.name));
-		if (copy_to_user((void *)arg, &info, sizeof(info)))
-			return -EFAULT;
-		return 0;
-	}
-	if (cmd == OSS_GETVERSION)
-		return put_user(SOUND_VERSION, (int *)arg);
-
-	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
-                return -EINVAL;
-
-        if (_IOC_DIR(cmd) == _IOC_READ) {
-                switch (_IOC_NR(cmd)) {
-		/* Give the current record source */
-		case SOUND_MIXER_RECSRC:
-			val = 0;	/* FIXME */
-			break;
-		/* Give the supported mixers, all of them support stereo */
-                case SOUND_MIXER_DEVMASK:
-                case SOUND_MIXER_STEREODEVS: {
-			int i;
-
-			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
-				if (mixtable[i].avail)
-					val |= 1 << i;
-			break;
-			}
-		/* Arg contains a bit for each supported recording source */
-                case SOUND_MIXER_RECMASK:
-			val = 0;
-			break;
-                case SOUND_MIXER_CAPS:
-			val = 0;
-			break;
-		/* Read a specific mixer */
-		default: {
-			int i = _IOC_NR(cmd);
-
-			if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
-				return -EINVAL;
-			val = hal2->mixer.volume[mixtable[i].idx];
-			break;
-			}
-		}
-		return put_user(val, (int *)arg);
-	}
-
-        if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ))
-		return -EINVAL;
-
-	hal2->mixer.modcnt++;
-
-	if (get_user(val, (int *)arg))
-		return -EFAULT;
-
-	switch (_IOC_NR(cmd)) {
-	/* Arg contains a bit for each recording source */
-	case SOUND_MIXER_RECSRC:
-		return 0;	/* FIXME */
-	default:
-		return hal2_write_mixer(hal2, _IOC_NR(cmd), val);
-	}
-
-	return 0;
-}
-
-static int hal2_open_mixdev(struct inode *inode, struct file *file)
-{
-	struct hal2_card *hal2 = hal2_mixer_find_card(iminor(inode));
-
-	if (hal2) {
-		file->private_data = hal2;
-		return nonseekable_open(inode, file);
-	}
-	return -ENODEV;
-}
-
-static int hal2_release_mixdev(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int hal2_ioctl_mixdev(struct inode *inode, struct file *file,
-			     unsigned int cmd, unsigned long arg)
-{
-	return hal2_mixer_ioctl((struct hal2_card *)file->private_data, cmd, arg);
-}
-
-static int hal2_ioctl(struct inode *inode, struct file *file, 
-		      unsigned int cmd, unsigned long arg)
-{
-	int val;
-	struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-
-	switch (cmd) {
-	case OSS_GETVERSION:
-		return put_user(SOUND_VERSION, (int *)arg);
-
-	case SNDCTL_DSP_SYNC:
-		if (file->f_mode & FMODE_WRITE)
-			return hal2_sync_dac(hal2);
-		return 0;
-
-	case SNDCTL_DSP_SETDUPLEX:
-		return 0;
-
-	case SNDCTL_DSP_GETCAPS:
-		return put_user(DSP_CAP_DUPLEX | DSP_CAP_MULTI, (int *)arg);
-
-	case SNDCTL_DSP_RESET:
-		if (file->f_mode & FMODE_READ) {
-			hal2_stop_adc(hal2);
-			hal2_reset_adc_pointer(hal2);
-		}
-		if (file->f_mode & FMODE_WRITE) {
-			hal2_stop_dac(hal2);
-			hal2_reset_dac_pointer(hal2);
-		}
-		return 0;
-
- 	case SNDCTL_DSP_SPEED:
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (file->f_mode & FMODE_READ) {
-			hal2_stop_adc(hal2);
-			val = hal2_compute_rate(&hal2->adc, val);
-			hal2->adc.sample_rate = val;
-			hal2_set_adc_rate(hal2);
-		}
-		if (file->f_mode & FMODE_WRITE) {
-			hal2_stop_dac(hal2);
-			val = hal2_compute_rate(&hal2->dac, val);
-			hal2->dac.sample_rate = val;
-			hal2_set_dac_rate(hal2);
-		}
-		return put_user(val, (int *)arg);
-
-	case SNDCTL_DSP_STEREO:
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (file->f_mode & FMODE_READ) {
-			hal2_stop_adc(hal2);
-			hal2->adc.voices = (val) ? 2 : 1;
-			hal2_setup_adc(hal2);
-		}
-		if (file->f_mode & FMODE_WRITE) {
-			hal2_stop_dac(hal2);
-			hal2->dac.voices = (val) ? 2 : 1;
-			hal2_setup_dac(hal2);
-                }
-		return 0;
-
-	case SNDCTL_DSP_CHANNELS:
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (val != 0) {
-			if (file->f_mode & FMODE_READ) {
-				hal2_stop_adc(hal2);
-				hal2->adc.voices = (val == 1) ? 1 : 2;
-				hal2_setup_adc(hal2);
-			}
-			if (file->f_mode & FMODE_WRITE) {
-				hal2_stop_dac(hal2);
-				hal2->dac.voices = (val == 1) ? 1 : 2;
-				hal2_setup_dac(hal2);
-			}
-		}
-		val = -EINVAL;
-		if (file->f_mode & FMODE_READ)
-			val = hal2->adc.voices;
-		if (file->f_mode & FMODE_WRITE)
-			val = hal2->dac.voices;
-		return put_user(val, (int *)arg);
-
-	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
-                return put_user(H2_SUPPORTED_FORMATS, (int *)arg);
-
-	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-		if (val != AFMT_QUERY) {
-			if (!(val & H2_SUPPORTED_FORMATS))
-				return -EINVAL;
-			if (file->f_mode & FMODE_READ) {
-				hal2_stop_adc(hal2);
-				hal2->adc.format = val;
-				hal2_setup_adc(hal2);
-			}
-			if (file->f_mode & FMODE_WRITE) {
-				hal2_stop_dac(hal2);
-				hal2->dac.format = val;
-				hal2_setup_dac(hal2);
-			}
-		} else {
-			val = -EINVAL;
-			if (file->f_mode & FMODE_READ)
-				val = hal2->adc.format;
-			if (file->f_mode & FMODE_WRITE)
-				val = hal2->dac.format;
-		}
-		return put_user(val, (int *)arg);
-
-	case SNDCTL_DSP_POST:
-		return 0;
-
-	case SNDCTL_DSP_GETOSPACE: {
-		audio_buf_info info;
-		int i;
-		unsigned long flags;
-		struct hal2_codec *dac = &hal2->dac;
-
-		if (!(file->f_mode & FMODE_WRITE))
-			return -EINVAL;
-		info.fragments = 0;
-		spin_lock_irqsave(&dac->lock, flags);
-		for (i = 0; i < dac->desc_count; i++)
-			if (dac->desc[i].cnt == 0)
-				info.fragments++;
-		spin_unlock_irqrestore(&dac->lock, flags);
-		info.fragstotal = dac->desc_count;
-		info.fragsize = H2_BLOCK_SIZE;
-                info.bytes = info.fragsize * info.fragments;
-
-		return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0;
-	}
-
-	case SNDCTL_DSP_GETISPACE: {
-		audio_buf_info info;
-		int i;
-		unsigned long flags;
-		struct hal2_codec *adc = &hal2->adc;
-
-		if (!(file->f_mode & FMODE_READ))
-			return -EINVAL;
-		info.fragments = 0;
-		info.bytes = 0;
-		spin_lock_irqsave(&adc->lock, flags);
-		for (i = 0; i < adc->desc_count; i++)
-			if (adc->desc[i].cnt > 0) {
-				info.fragments++;
-				info.bytes += adc->desc[i].cnt;
-			}
-		spin_unlock_irqrestore(&adc->lock, flags);
-		info.fragstotal = adc->desc_count;
-		info.fragsize = H2_BLOCK_SIZE;
-
-		return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0;
-	}
-
-	case SNDCTL_DSP_NONBLOCK:
-		file->f_flags |= O_NONBLOCK;
-		return 0;
-
-	case SNDCTL_DSP_GETBLKSIZE:
-		return put_user(H2_BLOCK_SIZE, (int *)arg);
-
-	case SNDCTL_DSP_SETFRAGMENT:
-		return 0;
-
-	case SOUND_PCM_READ_RATE:
-		val = -EINVAL;
-		if (file->f_mode & FMODE_READ)
-			val = hal2->adc.sample_rate;
-		if (file->f_mode & FMODE_WRITE)
-			val = hal2->dac.sample_rate;
-		return put_user(val, (int *)arg);
-
-	case SOUND_PCM_READ_CHANNELS:
-		val = -EINVAL;
-		if (file->f_mode & FMODE_READ)
-			val = hal2->adc.voices;
-		if (file->f_mode & FMODE_WRITE)
-			val = hal2->dac.voices;
-		return put_user(val, (int *)arg);
-
-	case SOUND_PCM_READ_BITS:
-		return put_user(16, (int *)arg);
-	}
-
-	return hal2_mixer_ioctl(hal2, cmd, arg);
-}
-
-static ssize_t hal2_read(struct file *file, char *buffer,
-			 size_t count, loff_t *ppos)
-{
-	ssize_t err;
-	struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-	struct hal2_codec *adc = &hal2->adc;
-
-	if (!count)
-		return 0;
-	if (mutex_lock_interruptible(&adc->sem))
-		return -EINTR;
-	if (file->f_flags & O_NONBLOCK) {
-		err = hal2_get_buffer(hal2, buffer, count);
-		err = err == 0 ? -EAGAIN : err;
-	} else {
-		do {
-			/* ~10% longer */
-			signed long timeout = 1000 * H2_BLOCK_SIZE *
-				2 * adc->voices * HZ / adc->sample_rate / 900;
-			unsigned long flags;
-			DECLARE_WAITQUEUE(wait, current);
-			ssize_t cnt = 0;
-
-			err = hal2_get_buffer(hal2, buffer, count);
-			if (err > 0) {
-				count -= err;
-				cnt += err;
-				buffer += err;
-				err = cnt;
-			}
-			if (count > 0 && err >= 0) {
-				add_wait_queue(&adc->dma_wait, &wait);
-				set_current_state(TASK_INTERRUPTIBLE);
-				schedule_timeout(timeout);
-				spin_lock_irqsave(&adc->lock, flags);
-				if (!adc->desc[adc->tail].cnt)
-					err = -EAGAIN;
-				spin_unlock_irqrestore(&adc->lock, flags);
-				if (signal_pending(current))
-					err = -ERESTARTSYS;
-				remove_wait_queue(&adc->dma_wait, &wait);
-				if (err < 0) {
-					hal2_stop_adc(hal2);
-					hal2_reset_adc_pointer(hal2);
-				}
-			}
-		} while (count > 0 && err >= 0);
-	}
-	mutex_unlock(&adc->sem);
-
-	return err;
-}
-
-static ssize_t hal2_write(struct file *file, const char *buffer,
-			  size_t count, loff_t *ppos)
-{
-	ssize_t err;
-	char *buf = (char*) buffer;
-	struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-	struct hal2_codec *dac = &hal2->dac;
-
-	if (!count)
-		return 0;
-	if (mutex_lock_interruptible(&dac->sem))
-		return -EINTR;
-	if (file->f_flags & O_NONBLOCK) {
-		err = hal2_add_buffer(hal2, buf, count);
-		err = err == 0 ? -EAGAIN : err;
-	} else {
-		do {
-			/* ~10% longer */
-			signed long timeout = 1000 * H2_BLOCK_SIZE *
-				2 * dac->voices * HZ / dac->sample_rate / 900;
-			unsigned long flags;
-			DECLARE_WAITQUEUE(wait, current);
-			ssize_t cnt = 0;
-
-			err = hal2_add_buffer(hal2, buf, count);
-			if (err > 0) {
-				count -= err;
-				cnt += err;
-				buf += err;
-				err = cnt;
-			}
-			if (count > 0 && err >= 0) {
-				add_wait_queue(&dac->dma_wait, &wait);
-				set_current_state(TASK_INTERRUPTIBLE);
-				schedule_timeout(timeout);
-				spin_lock_irqsave(&dac->lock, flags);
-				if (dac->desc[dac->head].cnt)
-					err = -EAGAIN;
-				spin_unlock_irqrestore(&dac->lock, flags);
-				if (signal_pending(current))
-					err = -ERESTARTSYS;
-				remove_wait_queue(&dac->dma_wait, &wait);
-				if (err < 0) {
-					hal2_stop_dac(hal2);
-					hal2_reset_dac_pointer(hal2);
-				}
-			}
-		} while (count > 0 && err >= 0);
-	}
-	mutex_unlock(&dac->sem);
-
-	return err;
-}
-
-static unsigned int hal2_poll(struct file *file, struct poll_table_struct *wait)
-{
-	unsigned long flags;
-	unsigned int mask = 0;
-	struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-
-	if (file->f_mode & FMODE_READ) {
-		struct hal2_codec *adc = &hal2->adc;
-
-		poll_wait(file, &adc->dma_wait, wait);
-		spin_lock_irqsave(&adc->lock, flags);
-		if (adc->desc[adc->tail].cnt > 0)
-			mask |= POLLIN;
-		spin_unlock_irqrestore(&adc->lock, flags);
-	}
-
-	if (file->f_mode & FMODE_WRITE) {
-		struct hal2_codec *dac = &hal2->dac;
-
-		poll_wait(file, &dac->dma_wait, wait);
-		spin_lock_irqsave(&dac->lock, flags);
-		if (dac->desc[dac->head].cnt == 0)
-			mask |= POLLOUT;
-		spin_unlock_irqrestore(&dac->lock, flags);
-	}
-
-	return mask;
-}
-
-static int hal2_open(struct inode *inode, struct file *file)
-{
-	int err;
-	struct hal2_card *hal2 = hal2_dsp_find_card(iminor(inode));
-
-	if (!hal2)
-		return -ENODEV;
-	file->private_data = hal2;
-	if (file->f_mode & FMODE_READ) {
-		struct hal2_codec *adc = &hal2->adc;
-
-		if (adc->usecount)
-			return -EBUSY;
-		/* OSS spec wanted us to use 8 bit, 8 kHz mono by default,
-		 * but HAL2 can't do 8bit audio */
-		adc->format = AFMT_S16_BE;
-		adc->voices = 1;
-		adc->sample_rate = hal2_compute_rate(adc, 8000);
-		hal2_set_adc_rate(hal2);
-		err = hal2_alloc_adc_dmabuf(adc);
-		if (err)
-			return err;
-		hal2_setup_adc(hal2);
-		adc->usecount++;
-	}
-	if (file->f_mode & FMODE_WRITE) {
-		struct hal2_codec *dac = &hal2->dac;
-
-		if (dac->usecount)
-			return -EBUSY;
-		dac->format = AFMT_S16_BE;
-		dac->voices = 1;
-		dac->sample_rate = hal2_compute_rate(dac, 8000);
-		hal2_set_dac_rate(hal2);
-		err = hal2_alloc_dac_dmabuf(dac);
-		if (err)
-			return err;
-		hal2_setup_dac(hal2);
-		dac->usecount++;
-	}
-
-	return nonseekable_open(inode, file);
-}
-
-static int hal2_release(struct inode *inode, struct file *file)
-{
-	struct hal2_card *hal2 = (struct hal2_card *) file->private_data;
-
-	if (file->f_mode & FMODE_READ) {
-		struct hal2_codec *adc = &hal2->adc;
-
-		mutex_lock(&adc->sem);
-		hal2_stop_adc(hal2);
-		hal2_free_adc_dmabuf(adc);
-		adc->usecount--;
-		mutex_unlock(&adc->sem);
-	}
-	if (file->f_mode & FMODE_WRITE) {
-		struct hal2_codec *dac = &hal2->dac;
-
-		mutex_lock(&dac->sem);
-		hal2_sync_dac(hal2);
-		hal2_free_dac_dmabuf(dac);
-		dac->usecount--;
-		mutex_unlock(&dac->sem);
-	}
-
-	return 0;
-}
-
-static const struct file_operations hal2_audio_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.read		= hal2_read,
-	.write		= hal2_write,
-	.poll		= hal2_poll,
-	.ioctl		= hal2_ioctl,
-	.open		= hal2_open,
-	.release	= hal2_release,
-};
-
-static const struct file_operations hal2_mixer_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.ioctl		= hal2_ioctl_mixdev,
-	.open		= hal2_open_mixdev,
-	.release	= hal2_release_mixdev,
-};
-
-static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3,
-			    int index)
-{
-	codec->pbus.pbusnr = index;
-	codec->pbus.pbus = &hpc3->pbdma[index];
-	init_waitqueue_head(&codec->dma_wait);
-	mutex_init(&codec->sem);
-	spin_lock_init(&codec->lock);
-}
-
-static int hal2_detect(struct hal2_card *hal2)
-{
-	unsigned short board, major, minor;
-	unsigned short rev;
-
-	/* reset HAL2 */
-	hal2_isr_write(hal2, 0);
-	/* release reset */
-	hal2_isr_write(hal2, H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N);
-
-	hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE); 
-	if ((rev = hal2_rev_look(hal2)) & H2_REV_AUDIO_PRESENT)
-		return -ENODEV;
-
-	board = (rev & H2_REV_BOARD_M) >> 12;
-	major = (rev & H2_REV_MAJOR_CHIP_M) >> 4;
-	minor = (rev & H2_REV_MINOR_CHIP_M);
-
-	printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n",
-	       board, major, minor);
-
-	return 0;
-}
-
-static int hal2_init_card(struct hal2_card **phal2, struct hpc3_regs *hpc3)
-{
-	int ret = 0;
-	struct hal2_card *hal2;
-
-	hal2 = kzalloc(sizeof(struct hal2_card), GFP_KERNEL);
-	if (!hal2)
-		return -ENOMEM;
-
-	hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0];
-	hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1];
-	hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2];
-	hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3];
-
-	if (hal2_detect(hal2) < 0) {
-		ret = -ENODEV;
-		goto free_card;
-	}
-
-	hal2_init_codec(&hal2->dac, hpc3, 0);
-	hal2_init_codec(&hal2->adc, hpc3, 1);
-
-	/*
-	 * All DMA channel interfaces in HAL2 are designed to operate with
-	 * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles
-	 * in D5. HAL2 is a 16-bit device which can accept both big and little
-	 * endian format. It assumes that even address bytes are on high
-	 * portion of PBUS (15:8) and assumes that HPC3 is programmed to
-	 * accept a live (unsynchronized) version of P_DREQ_N from HAL2.
-	 */
-#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \
-			  (2 << HPC3_DMACFG_D4R_SHIFT) | \
-			  (2 << HPC3_DMACFG_D5R_SHIFT) | \
-			  (0 << HPC3_DMACFG_D3W_SHIFT) | \
-			  (2 << HPC3_DMACFG_D4W_SHIFT) | \
-			  (2 << HPC3_DMACFG_D5W_SHIFT) | \
-				HPC3_DMACFG_DS16 | \
-				HPC3_DMACFG_EVENHI | \
-				HPC3_DMACFG_RTIME | \
-			  (8 << HPC3_DMACFG_BURST_SHIFT) | \
-				HPC3_DMACFG_DRQLIVE)
-	/*
-	 * Ignore what's mentioned in the specification and write value which
-	 * works in The Real World (TM)
-	 */
-	hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
-	hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
-
-	if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
-			hal2str, hal2)) {
-		printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
-		ret = -EAGAIN;
-		goto free_card;
-	}
-
-	hal2->dev_dsp = register_sound_dsp(&hal2_audio_fops, -1);
-	if (hal2->dev_dsp < 0) {
-		ret = hal2->dev_dsp;
-		goto free_irq;
-	}
-
-	hal2->dev_mixer = register_sound_mixer(&hal2_mixer_fops, -1);
-	if (hal2->dev_mixer < 0) {
-		ret = hal2->dev_mixer;
-		goto unregister_dsp;
-	}
-
-	hal2_init_mixer(hal2);
-
-	*phal2 = hal2;
-	return 0;
-unregister_dsp:
-	unregister_sound_dsp(hal2->dev_dsp);
-free_irq:
-	free_irq(SGI_HPCDMA_IRQ, hal2);
-free_card:
-	kfree(hal2);
-
-	return ret;
-}
-
-extern void (*indy_volume_button)(int);
-
-/* 
- * Assuming only one HAL2 card. Mail me if you ever meet machine with
- * more than one.
- */
-static int __init init_hal2(void)
-{
-	int i, error;
-
-	for (i = 0; i < MAXCARDS; i++)
-		hal2_card[i] = NULL;
-
-	error = hal2_init_card(&hal2_card[0], hpc3c0);
-
-	/* let Indy's volume buttons work */
-	if (!error && !ip22_is_fullhouse())
-		indy_volume_button = hal2_volume_control;
-
-	return error;
-
-}
-
-static void __exit exit_hal2(void)
-{
-	int i;
-
-	/* unregister volume butons callback function */
-	indy_volume_button = NULL;
-	
-	for (i = 0; i < MAXCARDS; i++)
-		if (hal2_card[i]) {
-			free_irq(SGI_HPCDMA_IRQ, hal2_card[i]);
-			unregister_sound_dsp(hal2_card[i]->dev_dsp);
-			unregister_sound_mixer(hal2_card[i]->dev_mixer);
-			kfree(hal2_card[i]);
-	}
-}
-
-module_init(init_hal2);
-module_exit(exit_hal2);
-
-MODULE_DESCRIPTION("OSS compatible driver for SGI HAL2 audio");
-MODULE_AUTHOR("Ladislav Michl");
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/hal2.h b/sound/oss/hal2.h
deleted file mode 100644
index 2bd3b52..0000000
--- a/sound/oss/hal2.h
+++ /dev/null
@@ -1,248 +0,0 @@
-#ifndef __HAL2_H
-#define __HAL2_H
-
-/*
- *  Driver for HAL2 sound processors
- *  Copyright (c) 1999 Ulf Carlsson <ulfc@bun.falkenberg.se>
- *  Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
- *
- *  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.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <asm/addrspace.h>
-#include <asm/sgi/hpc3.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-/* Indirect status register */
-
-#define H2_ISR_TSTATUS		0x01	/* RO: transaction status 1=busy */
-#define H2_ISR_USTATUS		0x02	/* RO: utime status bit 1=armed */
-#define H2_ISR_QUAD_MODE	0x04	/* codec mode 0=indigo 1=quad */
-#define H2_ISR_GLOBAL_RESET_N	0x08	/* chip global reset 0=reset */
-#define H2_ISR_CODEC_RESET_N	0x10	/* codec/synth reset 0=reset  */
-
-/* Revision register */
-
-#define H2_REV_AUDIO_PRESENT	0x8000	/* RO: audio present 0=present */
-#define H2_REV_BOARD_M		0x7000	/* RO: bits 14:12, board revision */
-#define H2_REV_MAJOR_CHIP_M	0x00F0	/* RO: bits 7:4, major chip revision */
-#define H2_REV_MINOR_CHIP_M	0x000F	/* RO: bits 3:0, minor chip revision */
-
-/* Indirect address register */
-
-/*
- * Address of indirect internal register to be accessed. A write to this
- * register initiates read or write access to the indirect registers in the
- * HAL2. Note that there af four indirect data registers for write access to
- * registers larger than 16 byte.
- */
-
-#define H2_IAR_TYPE_M		0xF000	/* bits 15:12, type of functional */
-					/* block the register resides in */
-					/* 1=DMA Port */
-					/* 9=Global DMA Control */
-					/* 2=Bresenham */
-					/* 3=Unix Timer */
-#define H2_IAR_NUM_M		0x0F00	/* bits 11:8 instance of the */
-					/* blockin which the indirect */
-					/* register resides */
-					/* If IAR_TYPE_M=DMA Port: */
-					/* 1=Synth In */
-					/* 2=AES In */
-					/* 3=AES Out */
-					/* 4=DAC Out */
-					/* 5=ADC Out */
-					/* 6=Synth Control */
-					/* If IAR_TYPE_M=Global DMA Control: */
-					/* 1=Control */
-					/* If IAR_TYPE_M=Bresenham: */
-					/* 1=Bresenham Clock Gen 1 */
-					/* 2=Bresenham Clock Gen 2 */
-					/* 3=Bresenham Clock Gen 3 */
-					/* If IAR_TYPE_M=Unix Timer: */
-					/* 1=Unix Timer */
-#define H2_IAR_ACCESS_SELECT	0x0080	/* 1=read 0=write */
-#define H2_IAR_PARAM		0x000C	/* Parameter Select */
-#define H2_IAR_RB_INDEX_M	0x0003	/* Read Back Index */
-					/* 00:word0 */
-					/* 01:word1 */
-					/* 10:word2 */
-					/* 11:word3 */
-/*
- * HAL2 internal addressing
- *
- * The HAL2 has "indirect registers" (idr) which are accessed by writing to the
- * Indirect Data registers. Write the address to the Indirect Address register
- * to transfer the data.
- *
- * We define the H2IR_* to the read address and H2IW_* to the write address and
- * H2I_* to be fields in whatever register is referred to.
- *
- * When we write to indirect registers which are larger than one word (16 bit)
- * we have to fill more than one indirect register before writing. When we read
- * back however we have to read several times, each time with different Read
- * Back Indexes (there are defs for doing this easily).
- */
-
-/*
- * Relay Control
- */
-#define H2I_RELAY_C		0x9100
-#define H2I_RELAY_C_STATE	0x01		/* state of RELAY pin signal */
-
-/* DMA port enable */
-
-#define H2I_DMA_PORT_EN		0x9104
-#define H2I_DMA_PORT_EN_SY_IN	0x01		/* Synth_in DMA port */
-#define H2I_DMA_PORT_EN_AESRX	0x02		/* AES receiver DMA port */
-#define H2I_DMA_PORT_EN_AESTX	0x04		/* AES transmitter DMA port */
-#define H2I_DMA_PORT_EN_CODECTX	0x08		/* CODEC transmit DMA port */
-#define H2I_DMA_PORT_EN_CODECR	0x10		/* CODEC receive DMA port */
-
-#define H2I_DMA_END		0x9108 		/* global dma endian select */
-#define H2I_DMA_END_SY_IN	0x01		/* Synth_in DMA port */
-#define H2I_DMA_END_AESRX	0x02		/* AES receiver DMA port */
-#define H2I_DMA_END_AESTX	0x04		/* AES transmitter DMA port */
-#define H2I_DMA_END_CODECTX	0x08		/* CODEC transmit DMA port */
-#define H2I_DMA_END_CODECR	0x10		/* CODEC receive DMA port */
-						/* 0=b_end 1=l_end */
-
-#define H2I_DMA_DRV		0x910C  	/* global PBUS DMA enable */
-
-#define H2I_SYNTH_C		0x1104		/* Synth DMA control */
-
-#define H2I_AESRX_C		0x1204	 	/* AES RX dma control */
-
-#define H2I_C_TS_EN		0x20		/* Timestamp enable */
-#define H2I_C_TS_FRMT		0x40		/* Timestamp format */
-#define H2I_C_NAUDIO		0x80		/* Sign extend */
-
-/* AESRX CTL, 16 bit */
-
-#define H2I_AESTX_C		0x1304		/* AES TX DMA control */
-#define H2I_AESTX_C_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
-#define H2I_AESTX_C_CLKID_M	0x18
-#define H2I_AESTX_C_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
-#define H2I_AESTX_C_DATAT_M	0x300
-
-/* CODEC registers */
-
-#define H2I_DAC_C1		0x1404 		/* DAC DMA control, 16 bit */
-#define H2I_DAC_C2		0x1408		/* DAC DMA control, 32 bit */
-#define H2I_ADC_C1		0x1504 		/* ADC DMA control, 16 bit */
-#define H2I_ADC_C2		0x1508		/* ADC DMA control, 32 bit */
-
-/* Bits in CTL1 register */
-
-#define H2I_C1_DMA_SHIFT	0		/* DMA channel */
-#define H2I_C1_DMA_M		0x7
-#define H2I_C1_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
-#define H2I_C1_CLKID_M		0x18
-#define H2I_C1_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
-#define H2I_C1_DATAT_M		0x300
-
-/* Bits in CTL2 register */
-
-#define H2I_C2_R_GAIN_SHIFT	0		/* right a/d input gain */	
-#define H2I_C2_R_GAIN_M		0xf	
-#define H2I_C2_L_GAIN_SHIFT	4		/* left a/d input gain */
-#define H2I_C2_L_GAIN_M		0xf0
-#define H2I_C2_R_SEL		0x100		/* right input select */
-#define H2I_C2_L_SEL		0x200		/* left input select */
-#define H2I_C2_MUTE		0x400		/* mute */
-#define H2I_C2_DO1		0x00010000	/* digital output port bit 0 */
-#define H2I_C2_DO2		0x00020000	/* digital output port bit 1 */
-#define H2I_C2_R_ATT_SHIFT	18		/* right d/a output - */
-#define H2I_C2_R_ATT_M		0x007c0000	/* attenuation */
-#define H2I_C2_L_ATT_SHIFT	23		/* left d/a output - */
-#define H2I_C2_L_ATT_M		0x0f800000	/* attenuation */
-
-#define H2I_SYNTH_MAP_C		0x1104		/* synth dma handshake ctrl */
-
-/* Clock generator CTL 1, 16 bit */
-
-#define H2I_BRES1_C1		0x2104
-#define H2I_BRES2_C1		0x2204
-#define H2I_BRES3_C1		0x2304
-
-#define H2I_BRES_C1_SHIFT	0		/* 0=48.0 1=44.1 2=aes_rx */
-#define H2I_BRES_C1_M		0x03
-				
-/* Clock generator CTL 2, 32 bit */
-
-#define H2I_BRES1_C2		0x2108
-#define H2I_BRES2_C2		0x2208
-#define H2I_BRES3_C2		0x2308
-
-#define H2I_BRES_C2_INC_SHIFT	0		/* increment value */
-#define H2I_BRES_C2_INC_M	0xffff
-#define H2I_BRES_C2_MOD_SHIFT	16		/* modcontrol value */
-#define H2I_BRES_C2_MOD_M	0xffff0000	/* modctrl=0xffff&(modinc-1) */
-
-/* Unix timer, 64 bit */
-
-#define H2I_UTIME		0x3104
-#define H2I_UTIME_0_LD		0xffff		/* microseconds, LSB's */
-#define H2I_UTIME_1_LD0		0x0f		/* microseconds, MSB's */
-#define H2I_UTIME_1_LD1		0xf0		/* tenths of microseconds */
-#define H2I_UTIME_2_LD		0xffff		/* seconds, LSB's */
-#define H2I_UTIME_3_LD		0xffff		/* seconds, MSB's */
-
-struct hal2_ctl_regs {
-	u32 _unused0[4];
-	volatile u32 isr;		/* 0x10 Status Register */
-	u32 _unused1[3];
-	volatile u32 rev;		/* 0x20 Revision Register */
-	u32 _unused2[3];
-	volatile u32 iar;		/* 0x30 Indirect Address Register */
-	u32 _unused3[3];
-	volatile u32 idr0;		/* 0x40 Indirect Data Register 0 */
-	u32 _unused4[3];
-	volatile u32 idr1;		/* 0x50 Indirect Data Register 1 */
-	u32 _unused5[3];
-	volatile u32 idr2;		/* 0x60 Indirect Data Register 2 */
-	u32 _unused6[3];
-	volatile u32 idr3;		/* 0x70 Indirect Data Register 3 */
-};
-
-struct hal2_aes_regs {
-	volatile u32 rx_stat[2];	/* Status registers */
-	volatile u32 rx_cr[2];		/* Control registers */
-	volatile u32 rx_ud[4];		/* User data window */
-	volatile u32 rx_st[24];		/* Channel status data */
-	
-	volatile u32 tx_stat[1];	/* Status register */
-	volatile u32 tx_cr[3];		/* Control registers */
-	volatile u32 tx_ud[4];		/* User data window */
-	volatile u32 tx_st[24];		/* Channel status data */
-};
-
-struct hal2_vol_regs {
-	volatile u32 right;		/* Right volume */
-	volatile u32 left;		/* Left volume */
-};
-
-struct hal2_syn_regs {
-	u32 _unused0[2];
-	volatile u32 page;		/* DOC Page register */
-	volatile u32 regsel;		/* DOC Register selection */
-	volatile u32 dlow;		/* DOC Data low */
-	volatile u32 dhigh;		/* DOC Data high */
-	volatile u32 irq;		/* IRQ Status */
-	volatile u32 dram;		/* DRAM Access */
-};
-
-#endif	/* __HAL2_H */
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
index a690ca5..6c0a770 100644
--- a/sound/oss/mpu401.c
+++ b/sound/oss/mpu401.c
@@ -1015,7 +1015,7 @@
 		mpu401_chk_version(m, devc);
 		if (devc->version == 0)
 			mpu401_chk_version(m, devc);
-			spin_unlock_irqrestore(&devc->lock,flags);
+		spin_unlock_irqrestore(&devc->lock, flags);
 	}
 
 	if (devc->version != 0)
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index 99f5483..41f870f 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -868,7 +868,8 @@
 	struct snd_card *card = h->card;
 	int idx, err;
 
-	snd_assert(h != NULL, return -EINVAL);
+	if (snd_BUG_ON(!h))
+		return -EINVAL;
 	strcpy(card->mixername, "Harmony Gain control interface");
 
 	for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 31f52d3..7003711 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -517,6 +517,14 @@
 	  This interface can be used for out-of-band communication
 	  with codecs for debugging purposes.
 
+config SND_HDA_INPUT_BEEP
+	bool "Support digital beep via input layer"
+	depends on SND_HDA_INTEL
+	depends on INPUT=y || INPUT=SND_HDA_INTEL
+	help
+	  Say Y here to build a digital beep interface for HD-audio
+	  driver. This interface is used to generate digital beeps.
+
 config SND_HDA_CODEC_REALTEK
 	bool "Build Realtek HD-audio codec support"
 	depends on SND_HDA_INTEL
@@ -557,6 +565,14 @@
 	  Say Y here to include ATI HDMI HD-audio codec support in
 	  snd-hda-intel driver, such as ATI RS600 HDMI.
 
+config SND_HDA_CODEC_NVHDMI
+	bool "Build NVIDIA HDMI HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include NVIDIA HDMI HD-audio codec support in
+	  snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
+
 config SND_HDA_CODEC_CONEXANT
 	bool "Build Conexant HD-audio codec support"
 	depends on SND_HDA_INTEL
@@ -649,8 +665,9 @@
 
 	  Currently supported hardware is: M-Audio Delta 1010(LT),
 	  DiO 2496, 66, 44, 410, Audiophile 24/96; Digigram VX442;
-	  TerraTec EWX 24/96, EWS 88MT, 88D, DMX 6Fire, Phase 88;
-	  Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8.
+	  TerraTec EWX 24/96, EWS 88MT/D, DMX 6Fire, Phase 88;
+	  Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8;
+	  Lionstracs Mediastation, Terrasoniq TS 88.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-ice1712.
@@ -665,9 +682,12 @@
 	  ICE/VT1724/1720 (Envy24HT/PT) chips.
 
 	  Currently supported hardware is: AMP AUDIO2000; M-Audio
-	  Revolution 7.1; TerraTec Aureon 5.1 Sky, 7.1 Space/Universe;
-	  AudioTrak Prodigy 7.1; Pontis MS300; Albatron K8X800 Pro II;
-	  Chaintech ZNF3-150/250.
+	  Revolution 5.1, 7.1, Audiophile 192; TerraTec Aureon 5.1 Sky,
+	  7.1 Space/Universe, Phase 22/28; Onkyo SE-90PCI, SE-200PCI;
+	  AudioTrak Prodigy 192, 7.1 (HIFI/LT/XT), HD2; Hercules
+	  Fortissimo IV; ESI Juli@; Pontis MS300; EGO-SYS WaveTerminal
+	  192M; Albatron K8X800 Pro II; Chaintech ZNF3-150/250, 9CJS,
+	  AV-710; Shuttle SN25P.
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-ice1724.
@@ -845,7 +865,8 @@
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
-	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X.
+	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X and
+	  HDAV1.3 (Deluxe).
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-virtuoso.
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 8c49a00..6704acb 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -67,8 +67,8 @@
 };
 
 static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
-{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },
 { 0x41445300, 0xffffff00, "Analog Devices",	NULL,	NULL },
+{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },
 { 0x414c4300, 0xffffff00, "Realtek",		NULL,	NULL },
 { 0x414c4700, 0xffffff00, "Realtek",		NULL,	NULL },
 { 0x434d4900, 0xffffff00, "C-Media Electronics", NULL,	NULL },
@@ -94,11 +94,6 @@
 };
 
 static const struct ac97_codec_id snd_ac97_codec_ids[] = {
-{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL },
-{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL },
-{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL },
-{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL },
-{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },
 { 0x41445303, 0xffffffff, "AD1819",		patch_ad1819,	NULL },
 { 0x41445340, 0xffffffff, "AD1881",		patch_ad1881,	NULL },
 { 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881,	NULL },
@@ -112,20 +107,25 @@
 { 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b,	NULL },
 { 0x41445375, 0xffffffff, "AD1985",		patch_ad1985,	NULL },
 { 0x41445378, 0xffffffff, "AD1986",		patch_ad1986,	NULL },
+{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL },
+{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL },
+{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL },
+{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL },
+{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },
 { 0x414c4300, 0xffffff00, "ALC100,100P", 	NULL,		NULL },
 { 0x414c4710, 0xfffffff0, "ALC200,200P",	NULL,		NULL },
 { 0x414c4721, 0xffffffff, "ALC650D",		NULL,	NULL }, /* already patched */
 { 0x414c4722, 0xffffffff, "ALC650E",		NULL,	NULL }, /* already patched */
 { 0x414c4723, 0xffffffff, "ALC650F",		NULL,	NULL }, /* already patched */
 { 0x414c4720, 0xfffffff0, "ALC650",		patch_alc650,	NULL },
-{ 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655,	NULL },
-{ 0x414c4781, 0xffffffff, "ALC658D",		NULL,	NULL }, /* already patched */
-{ 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655,	NULL },
-{ 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850,	NULL },
 { 0x414c4730, 0xffffffff, "ALC101",		NULL,		NULL },
 { 0x414c4740, 0xfffffff0, "ALC202",		NULL,		NULL },
 { 0x414c4750, 0xfffffff0, "ALC250",		NULL,		NULL },
-{ 0x414c4770, 0xfffffff0, "ALC203",		NULL,		NULL },
+{ 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655,	NULL },
+{ 0x414c4770, 0xfffffff0, "ALC203",		patch_alc203,	NULL },
+{ 0x414c4781, 0xffffffff, "ALC658D",		NULL,	NULL }, /* already patched */
+{ 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655,	NULL },
+{ 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850,	NULL },
 { 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738,	NULL },
 { 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739,	NULL },
 { 0x434d4969, 0xffffffff, "CMI9780",		patch_cm9780,	NULL },
@@ -168,7 +168,7 @@
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL,		NULL },
 { 0x56494161, 0xffffffff, "VIA1612A",		NULL,		NULL }, // modified ICE1232 with S/PDIF
 { 0x56494170, 0xffffffff, "VIA1617A",		patch_vt1617a,	NULL }, // modified VT1616 with S/PDIF
-{ 0x56494182, 0xffffffff, "VIA1618",		NULL,		NULL },
+{ 0x56494182, 0xffffffff, "VIA1618",		patch_vt1618,   NULL },
 { 0x57454301, 0xffffffff, "W83971D",		NULL,		NULL },
 { 0x574d4c00, 0xffffffff, "WM9701,WM9701A",	NULL,		NULL },
 { 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
@@ -1890,8 +1890,8 @@
 		.dev_free =	snd_ac97_bus_dev_free,
 	};
 
-	snd_assert(card != NULL, return -EINVAL);
-	snd_assert(rbus != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card))
+		return -EINVAL;
 	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
 	if (bus == NULL)
 		return -ENOMEM;
@@ -1906,7 +1906,8 @@
 		snd_ac97_bus_free(bus);
 		return err;
 	}
-	*rbus = bus;
+	if (rbus)
+		*rbus = bus;
 	return 0;
 }
 
@@ -1991,10 +1992,14 @@
 		.dev_disconnect =	snd_ac97_dev_disconnect,
 	};
 
-	snd_assert(rac97 != NULL, return -EINVAL);
-	*rac97 = NULL;
-	snd_assert(bus != NULL && template != NULL, return -EINVAL);
-	snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL);
+	if (rac97)
+		*rac97 = NULL;
+	if (snd_BUG_ON(!bus || !template))
+		return -EINVAL;
+	if (snd_BUG_ON(template->num >= 4))
+		return -EINVAL;
+	if (bus->codec[template->num])
+		return -EBUSY;
 
 	card = bus->card;
 	ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index f4fbc79..6ce3cbe 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -2560,6 +2560,14 @@
 	return 0;
 }
 
+/*
+ * realtek ALC203: use mono-out for pin 37
+ */
+static int patch_alc203(struct snd_ac97 *ac97)
+{
+	snd_ac97_update_bits(ac97, 0x7a, 0x400, 0x400);
+	return 0;
+}
 
 /*
  * realtek ALC65x/850 codecs
@@ -3457,7 +3465,7 @@
 
 /*
  * unfortunately, the vt1617a stashes the twiddlers required for
- * nooding the i/o jacks on 2 different regs. * thameans that we cant
+ * noodling the i/o jacks on 2 different regs. that means that we can't
  * use the easy way provided by AC97_ENUM_DOUBLE() we have to write
  * are own funcs.
  *
@@ -3490,7 +3498,7 @@
 	
 	pac97 = snd_kcontrol_chip(kcontrol); /* grab codec handle */
 
-	/* grab our desirec bits, then mash them together in a manner
+	/* grab our desired bits, then mash them together in a manner
 	 * consistent with Table 6 on page 17 in the 1617a docs */
  
 	usSM51 = snd_ac97_read(pac97, 0x7a) >> 14;
@@ -3540,7 +3548,7 @@
 	},
 };
 
-int patch_vt1617a(struct snd_ac97 * ac97)
+static int patch_vt1617a(struct snd_ac97 * ac97)
 {
 	int err = 0;
 	int val;
@@ -3568,6 +3576,200 @@
 	return err;
 }
 
+/* VIA VT1618 8 CHANNEL AC97 CODEC
+ *
+ * VIA implements 'Smart 5.1' completely differently on the 1618 than
+ * it does on the 1617a. awesome! They seem to have sourced this
+ * particular revision of the technology from somebody else, it's
+ * called Universal Audio Jack and it shows up on some other folk's chips
+ * as well.
+ *
+ * ordering in this list reflects vt1618 docs for Reg 60h and
+ * the block diagram, DACs are as follows:
+ *
+ *        OUT_O -> Front,
+ *	  OUT_1 -> Surround,
+ *	  OUT_2 -> C/LFE
+ *
+ * Unlike the 1617a, each OUT has a consistent set of mappings
+ * for all bitpatterns other than 00:
+ *
+ *        01       Unmixed Output
+ *        10       Line In
+ *        11       Mic  In
+ *
+ * Special Case of 00:
+ *
+ *        OUT_0    Mixed Output
+ *        OUT_1    Reserved
+ *        OUT_2    Reserved
+ *
+ * I have no idea what the hell Reserved does, but on an MSI
+ * CN700T, i have to set it to get 5.1 output - YMMV, bad
+ * shit may happen.
+ *
+ * If other chips use Universal Audio Jack, then this code might be applicable
+ * to them.
+ */
+
+struct vt1618_uaj_item {
+	unsigned short mask;
+	unsigned short shift;
+	const char *items[4];
+};
+
+/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */
+
+static struct vt1618_uaj_item vt1618_uaj[3] = {
+	{
+		/* speaker jack */
+		.mask  = 0x03,
+		.shift = 0,
+		.items = {
+			"Speaker Out", "DAC Unmixed Out", "Line In", "Mic In"
+		}
+	},
+	{
+		/* line jack */
+		.mask  = 0x0c,
+		.shift = 2,
+		.items = {
+			"Surround Out", "DAC Unmixed Out", "Line In", "Mic In"
+		}
+	},
+	{
+		/* mic jack */
+		.mask  = 0x30,
+		.shift = 4,
+		.items = {
+			"Center LFE Out", "DAC Unmixed Out", "Line In", "Mic In"
+		},
+	},
+};
+
+static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	return ac97_enum_text_info(kcontrol, uinfo,
+				   vt1618_uaj[kcontrol->private_value].items,
+				   4);
+}
+
+/* All of the vt1618 Universal Audio Jack twiddlers are on
+ * Vendor Defined Register 0x60, page 0. The bits, and thus
+ * the mask, are the only thing that changes
+ */
+static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned short datpag, uaj;
+	struct snd_ac97 *pac97 = snd_kcontrol_chip(kcontrol);
+
+	mutex_lock(&pac97->page_mutex);
+
+	datpag = snd_ac97_read(pac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
+	snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, 0);
+
+	uaj = snd_ac97_read(pac97, 0x60) &
+		vt1618_uaj[kcontrol->private_value].mask;
+
+	snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, datpag);
+	mutex_unlock(&pac97->page_mutex);
+
+	ucontrol->value.enumerated.item[0] = uaj >>
+		vt1618_uaj[kcontrol->private_value].shift;
+
+	return 0;
+}
+
+static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	return ac97_update_bits_page(snd_kcontrol_chip(kcontrol), 0x60,
+				     vt1618_uaj[kcontrol->private_value].mask,
+				     ucontrol->value.enumerated.item[0]<<
+				     vt1618_uaj[kcontrol->private_value].shift,
+				     0);
+}
+
+/* config aux in jack - not found on 3 jack motherboards or soundcards */
+
+static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	static const char *txt_aux[] = {"Aux In", "Back Surr Out"};
+
+	return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2);
+}
+
+static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.enumerated.item[0] =
+		(snd_ac97_read(snd_kcontrol_chip(kcontrol), 0x5c) & 0x0008)>>3;
+	return 0;
+}
+
+static int snd_ac97_vt1618_aux_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	/* toggle surround rear dac power */
+
+	snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x5c, 0x0008,
+			     ucontrol->value.enumerated.item[0] << 3);
+
+	/* toggle aux in surround rear out jack */
+
+	return snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x76, 0x0008,
+				    ucontrol->value.enumerated.item[0] << 3);
+}
+
+static const struct snd_kcontrol_new snd_ac97_controls_vt1618[] = {
+	AC97_SINGLE("Exchange Center/LFE", 0x5a,  8, 1,     0),
+	AC97_SINGLE("DC Offset",           0x5a, 10, 1,     0),
+	AC97_SINGLE("Soft Mute",           0x5c,  0, 1,     1),
+	AC97_SINGLE("Headphone Amp",       0x5c,  5, 1,     1),
+	AC97_DOUBLE("Back Surr Volume",    0x5e,  8, 0, 31, 1),
+	AC97_SINGLE("Back Surr Switch",    0x5e, 15, 1,     1),
+	{
+		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name          = "Speaker Jack Mode",
+		.info          = snd_ac97_vt1618_UAJ_info,
+		.get           = snd_ac97_vt1618_UAJ_get,
+		.put           = snd_ac97_vt1618_UAJ_put,
+		.private_value = 0
+	},
+	{
+		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name          = "Line Jack Mode",
+		.info          = snd_ac97_vt1618_UAJ_info,
+		.get           = snd_ac97_vt1618_UAJ_get,
+		.put           = snd_ac97_vt1618_UAJ_put,
+		.private_value = 1
+	},
+	{
+		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name          = "Mic Jack Mode",
+		.info          = snd_ac97_vt1618_UAJ_info,
+		.get           = snd_ac97_vt1618_UAJ_get,
+		.put           = snd_ac97_vt1618_UAJ_put,
+		.private_value = 2
+	},
+	{
+		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name          = "Aux Jack Mode",
+		.info          = snd_ac97_vt1618_aux_info,
+		.get           = snd_ac97_vt1618_aux_get,
+		.put           = snd_ac97_vt1618_aux_put,
+	}
+};
+
+static int patch_vt1618(struct snd_ac97 *ac97)
+{
+	return patch_build_controls(ac97, snd_ac97_controls_vt1618,
+				    ARRAY_SIZE(snd_ac97_controls_vt1618));
+}
+
 /*
  */
 static void it2646_update_jacks(struct snd_ac97 *ac97)
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 39ec55b..92f3a97 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -549,7 +549,8 @@
 	ptr = ad1889_readl(chip, AD_DMA_WAVCA);
 	ptr -= chip->wave.addr;
 	
-	snd_assert((ptr >= 0) && (ptr < chip->wave.size), return 0);
+	if (snd_BUG_ON(ptr >= chip->wave.size))
+		return 0;
 	
 	return bytes_to_frames(ss->runtime, ptr);
 }
@@ -567,7 +568,8 @@
 	ptr = ad1889_readl(chip, AD_DMA_ADCCA);
 	ptr -= chip->ramc.addr;
 
-	snd_assert((ptr >= 0) && (ptr < chip->ramc.size), return 0);
+	if (snd_BUG_ON(ptr >= chip->ramc.size))
+		return 0;
 	
 	return bytes_to_frames(ss->runtime, ptr);
 }
diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c
index 33d37b1..0f819dd 100644
--- a/sound/pci/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -392,9 +392,10 @@
 		.dev_free =	snd_ak4531_dev_free,
 	};
 
-	snd_assert(rak4531 != NULL, return -EINVAL);
-	*rak4531 = NULL;
-	snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card || !_ak4531))
+		return -EINVAL;
+	if (rak4531)
+		*rak4531 = NULL;
 	ak4531 = kzalloc(sizeof(*ak4531), GFP_KERNEL);
 	if (ak4531 == NULL)
 		return -ENOMEM;
@@ -428,7 +429,8 @@
 #if 0
 	snd_ak4531_dump(ak4531);
 #endif
-	*rak4531 = ak4531;
+	if (rak4531)
+		*rak4531 = ak4531;
 	return 0;
 }
 
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 27ce613..ba57005 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -2,7 +2,7 @@
  *  card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
  *  Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
  *			  Jaroslav Kysela <perex@perex.cz>
- *  Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
+ *  Copyright (C) 2002, 2008 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
  *
  *  Framework borrowed from Massimo Piccioni's card-als100.c.
  *
@@ -27,8 +27,10 @@
  *  bought an ALS4000 based soundcard, I was forced to base this driver
  *  on reverse engineering.
  *
- *  Note: this is no longer true. Pretty verbose chip docu (ALS4000a.PDF)
- *  can be found on the ALSA web site.
+ *  Note: this is no longer true (thank you!):
+ *  pretty verbose chip docu (ALS4000a.PDF) can be found on the ALSA web site.
+ *  Page numbers stated anywhere below with the "SPECS_PAGE:" tag
+ *  refer to: ALS4000a.PDF specs Ver 1.0, May 28th, 1998.
  *
  *  The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an
  *  ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport 
@@ -59,7 +61,7 @@
  * - value -> some port 0x0c0d
  *
  * ToDo:
- * - Proper shared IRQ handling?
+ * - by default, don't enable legacy game and use PCI game I/O
  * - power management? (card can do voice wakeup according to datasheet!!)
  */
 
@@ -78,7 +80,7 @@
 #include <sound/sb.h>
 #include <sound/initval.h>
 
-MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>");
+MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>, Andreas Mohr");
 MODULE_DESCRIPTION("Avance Logic ALS4000");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}");
@@ -107,7 +109,7 @@
 
 struct snd_card_als4000 {
 	/* most frequent access first */
-	unsigned long gcr;
+	unsigned long iobase;
 	struct pci_dev *pci;
 	struct snd_sb *chip;
 #ifdef SUPPORT_JOYSTICK
@@ -122,28 +124,168 @@
 
 MODULE_DEVICE_TABLE(pci, snd_als4000_ids);
 
-static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val)
+enum als4k_iobase_t {
+	/* IOx: B == Byte, W = Word, D = DWord; SPECS_PAGE: 37 */
+	ALS4K_IOD_00_AC97_ACCESS = 0x00,
+	ALS4K_IOW_04_AC97_READ = 0x04,
+	ALS4K_IOB_06_AC97_STATUS = 0x06,
+	ALS4K_IOB_07_IRQSTATUS = 0x07,
+	ALS4K_IOD_08_GCR_DATA = 0x08,
+	ALS4K_IOB_0C_GCR_INDEX = 0x0c,
+	ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU = 0x0e,
+	ALS4K_IOB_10_ADLIB_ADDR0 = 0x10,
+	ALS4K_IOB_11_ADLIB_ADDR1 = 0x11,
+	ALS4K_IOB_12_ADLIB_ADDR2 = 0x12,
+	ALS4K_IOB_13_ADLIB_ADDR3 = 0x13,
+	ALS4K_IOB_14_MIXER_INDEX = 0x14,
+	ALS4K_IOB_15_MIXER_DATA = 0x15,
+	ALS4K_IOB_16_ESP_RESET = 0x16,
+	ALS4K_IOB_16_ACK_FOR_CR1E = 0x16, /* 2nd function */
+	ALS4K_IOB_18_OPL_ADDR0 = 0x18,
+	ALS4K_IOB_19_OPL_ADDR1 = 0x19,
+	ALS4K_IOB_1A_ESP_RD_DATA = 0x1a,
+	ALS4K_IOB_1C_ESP_CMD_DATA = 0x1c,
+	ALS4K_IOB_1C_ESP_WR_STATUS = 0x1c, /* 2nd function */
+	ALS4K_IOB_1E_ESP_RD_STATUS8 = 0x1e,
+	ALS4K_IOB_1F_ESP_RD_STATUS16 = 0x1f,
+	ALS4K_IOB_20_ESP_GAMEPORT_200 = 0x20,
+	ALS4K_IOB_21_ESP_GAMEPORT_201 = 0x21,
+	ALS4K_IOB_30_MIDI_DATA = 0x30,
+	ALS4K_IOB_31_MIDI_STATUS = 0x31,
+	ALS4K_IOB_31_MIDI_COMMAND = 0x31, /* 2nd function */
+};
+
+enum als4k_iobase_0e_t {
+	ALS4K_IOB_0E_MPU_IRQ = 0x10,
+	ALS4K_IOB_0E_CR1E_IRQ = 0x40,
+	ALS4K_IOB_0E_SB_DMA_IRQ = 0x80,
+};
+
+enum als4k_gcr_t { /* all registers 32bit wide; SPECS_PAGE: 38 to 42 */
+	ALS4K_GCR8C_MISC_CTRL = 0x8c,
+	ALS4K_GCR90_TEST_MODE_REG = 0x90,
+	ALS4K_GCR91_DMA0_ADDR = 0x91,
+	ALS4K_GCR92_DMA0_MODE_COUNT = 0x92,
+	ALS4K_GCR93_DMA1_ADDR = 0x93,
+	ALS4K_GCR94_DMA1_MODE_COUNT = 0x94,
+	ALS4K_GCR95_DMA3_ADDR = 0x95,
+	ALS4K_GCR96_DMA3_MODE_COUNT = 0x96,
+	ALS4K_GCR99_DMA_EMULATION_CTRL = 0x99,
+	ALS4K_GCRA0_FIFO1_CURRENT_ADDR = 0xa0,
+	ALS4K_GCRA1_FIFO1_STATUS_BYTECOUNT = 0xa1,
+	ALS4K_GCRA2_FIFO2_PCIADDR = 0xa2,
+	ALS4K_GCRA3_FIFO2_COUNT = 0xa3,
+	ALS4K_GCRA4_FIFO2_CURRENT_ADDR = 0xa4,
+	ALS4K_GCRA5_FIFO1_STATUS_BYTECOUNT = 0xa5,
+	ALS4K_GCRA6_PM_CTRL = 0xa6,
+	ALS4K_GCRA7_PCI_ACCESS_STORAGE = 0xa7,
+	ALS4K_GCRA8_LEGACY_CFG1 = 0xa8,
+	ALS4K_GCRA9_LEGACY_CFG2 = 0xa9,
+	ALS4K_GCRFF_DUMMY_SCRATCH = 0xff,
+};
+
+enum als4k_gcr8c_t {
+	ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE = 0x8000,
+	ALS4K_GCR8C_CHIP_REV_MASK = 0xf0000
+};
+
+static inline void snd_als4k_iobase_writeb(unsigned long iobase,
+						enum als4k_iobase_t reg,
+						u8 val)
 {
-	outb(reg, port+0x0c);
-	outl(val, port+0x08);
+	outb(val, iobase + reg);
 }
 
-static inline void snd_als4000_gcr_write(struct snd_sb *sb, u32 reg, u32 val)
+static inline void snd_als4k_iobase_writel(unsigned long iobase,
+						enum als4k_iobase_t reg,
+						u32 val)
 {
-	snd_als4000_gcr_write_addr(sb->alt_port, reg, val);
+	outl(val, iobase + reg);
+}
+
+static inline u8 snd_als4k_iobase_readb(unsigned long iobase,
+						enum als4k_iobase_t reg)
+{
+	return inb(iobase + reg);
+}
+
+static inline u32 snd_als4k_iobase_readl(unsigned long iobase,
+						enum als4k_iobase_t reg)
+{
+	return inl(iobase + reg);
+}
+
+static inline void snd_als4k_gcr_write_addr(unsigned long iobase,
+						 enum als4k_gcr_t reg,
+						 u32 val)
+{
+	snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg);
+	snd_als4k_iobase_writel(iobase, ALS4K_IOD_08_GCR_DATA, val);
+}
+
+static inline void snd_als4k_gcr_write(struct snd_sb *sb,
+					 enum als4k_gcr_t reg,
+					 u32 val)
+{
+	snd_als4k_gcr_write_addr(sb->alt_port, reg, val);
 }	
 
-static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg)
+static inline u32 snd_als4k_gcr_read_addr(unsigned long iobase,
+						 enum als4k_gcr_t reg)
 {
-	outb(reg, port+0x0c);
-	return inl(port+0x08);
+	/* SPECS_PAGE: 37/38 */
+	snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg);
+	return snd_als4k_iobase_readl(iobase, ALS4K_IOD_08_GCR_DATA);
 }
 
-static inline u32 snd_als4000_gcr_read(struct snd_sb *sb, u32 reg)
+static inline u32 snd_als4k_gcr_read(struct snd_sb *sb, enum als4k_gcr_t reg)
 {
-	return snd_als4000_gcr_read_addr(sb->alt_port, reg);
+	return snd_als4k_gcr_read_addr(sb->alt_port, reg);
 }
 
+enum als4k_cr_t { /* all registers 8bit wide; SPECS_PAGE: 20 to 23 */
+	ALS4K_CR0_SB_CONFIG = 0x00,
+	ALS4K_CR2_MISC_CONTROL = 0x02,
+	ALS4K_CR3_CONFIGURATION = 0x03,
+	ALS4K_CR17_FIFO_STATUS = 0x17,
+	ALS4K_CR18_ESP_MAJOR_VERSION = 0x18,
+	ALS4K_CR19_ESP_MINOR_VERSION = 0x19,
+	ALS4K_CR1A_MPU401_UART_MODE_CONTROL = 0x1a,
+	ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO = 0x1c,
+	ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI = 0x1d,
+	ALS4K_CR1E_FIFO2_CONTROL = 0x1e, /* secondary PCM FIFO (recording) */
+	ALS4K_CR3A_MISC_CONTROL = 0x3a,
+	ALS4K_CR3B_CRC32_BYTE0 = 0x3b, /* for testing, activate via CR3A */
+	ALS4K_CR3C_CRC32_BYTE1 = 0x3c,
+	ALS4K_CR3D_CRC32_BYTE2 = 0x3d,
+	ALS4K_CR3E_CRC32_BYTE3 = 0x3e,
+};
+
+enum als4k_cr0_t {
+	ALS4K_CR0_DMA_CONTIN_MODE_CTRL = 0x02, /* IRQ/FIFO controlled for 0/1 */
+	ALS4K_CR0_DMA_90H_MODE_CTRL = 0x04, /* IRQ/FIFO controlled for 0/1 */
+	ALS4K_CR0_MX80_81_REG_WRITE_ENABLE = 0x80,
+};
+
+static inline void snd_als4_cr_write(struct snd_sb *chip,
+					enum als4k_cr_t reg,
+					u8 data)
+{
+	/* Control Register is reg | 0xc0 (bit 7, 6 set) on sbmixer_index
+	 * NOTE: assumes chip->mixer_lock to be locked externally already!
+	 * SPECS_PAGE: 6 */
+	snd_sbmixer_write(chip, reg | 0xc0, data);
+}
+
+static inline u8 snd_als4_cr_read(struct snd_sb *chip,
+					enum als4k_cr_t reg)
+{
+	/* NOTE: assumes chip->mixer_lock to be locked externally already! */
+	return snd_sbmixer_read(chip, reg | 0xc0);
+}
+
+
+
 static void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)
 {
 	if (!(chip->mode & SB_RATE_LOCK)) {
@@ -156,15 +298,19 @@
 static inline void snd_als4000_set_capture_dma(struct snd_sb *chip,
 					       dma_addr_t addr, unsigned size)
 {
-	snd_als4000_gcr_write(chip, 0xa2, addr);
-	snd_als4000_gcr_write(chip, 0xa3, (size-1));
+	/* SPECS_PAGE: 40 */
+	snd_als4k_gcr_write(chip, ALS4K_GCRA2_FIFO2_PCIADDR, addr);
+	snd_als4k_gcr_write(chip, ALS4K_GCRA3_FIFO2_COUNT, (size-1));
 }
 
 static inline void snd_als4000_set_playback_dma(struct snd_sb *chip,
-						dma_addr_t addr, unsigned size)
+						dma_addr_t addr,
+						unsigned size)
 {
-	snd_als4000_gcr_write(chip, 0x91, addr);
-	snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000);
+	/* SPECS_PAGE: 38 */
+	snd_als4k_gcr_write(chip, ALS4K_GCR91_DMA0_ADDR, addr);
+	snd_als4k_gcr_write(chip, ALS4K_GCR92_DMA0_MODE_COUNT,
+							(size-1)|0x180000);
 }
 
 #define ALS4000_FORMAT_SIGNED	(1<<0)
@@ -248,7 +394,7 @@
 	count = snd_pcm_lib_period_bytes(substream);
 	
 	if (chip->capture_format & ALS4000_FORMAT_16BIT)
-		count >>=1;
+		count >>= 1;
 	count--;
 
 	spin_lock_irq(&chip->reg_lock);
@@ -256,8 +402,8 @@
 	snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
 	spin_unlock_irq(&chip->reg_lock);
 	spin_lock_irq(&chip->mixer_lock);
-	snd_sbmixer_write(chip, 0xdc, count);
-	snd_sbmixer_write(chip, 0xdd, count>>8);
+	snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
+	snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
 	spin_unlock_irq(&chip->mixer_lock);
 	return 0;
 }
@@ -275,7 +421,7 @@
 	count = snd_pcm_lib_period_bytes(substream);
 	
 	if (chip->playback_format & ALS4000_FORMAT_16BIT)
-		count >>=1;
+		count >>= 1;
 	count--;
 	
 	/* FIXME: from second playback on, there's a lot more clicks and pops
@@ -292,8 +438,8 @@
 	/* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */
 	snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);
 	snd_sbdsp_command(chip, playback_cmd(chip).format);
-	snd_sbdsp_command(chip, count);
-	snd_sbdsp_command(chip, count>>8);
+	snd_sbdsp_command(chip, count & 0xff);
+	snd_sbdsp_command(chip, count >> 8);
 	snd_sbdsp_command(chip, playback_cmd(chip).dma_off);	
 	spin_unlock_irq(&chip->reg_lock);
 	
@@ -305,17 +451,25 @@
 	struct snd_sb *chip = snd_pcm_substream_chip(substream);
 	int result = 0;
 	
+	/* FIXME race condition in here!!!
+	   chip->mode non-atomic update gets consistently protected
+	   by reg_lock always, _except_ for this place!!
+	   Probably need to take reg_lock as outer (or inner??) lock, too.
+	   (or serialize both lock operations? probably not, though... - racy?)
+	*/
 	spin_lock(&chip->mixer_lock);
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 		chip->mode |= SB_RATE_LOCK_CAPTURE;
-		snd_sbmixer_write(chip, 0xde, capture_cmd(chip));
+		snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL,
+							 capture_cmd(chip));
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 		chip->mode &= ~SB_RATE_LOCK_CAPTURE;
-		snd_sbmixer_write(chip, 0xde, 0);
+		snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL,
+							 capture_cmd(chip));
 		break;
 	default:
 		result = -EINVAL;
@@ -356,8 +510,9 @@
 	unsigned int result;
 
 	spin_lock(&chip->reg_lock);	
-	result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff;
+	result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
 	spin_unlock(&chip->reg_lock);
+	result &= 0xffff;
 	return bytes_to_frames( substream->runtime, result );
 }
 
@@ -367,8 +522,9 @@
 	unsigned result;
 
 	spin_lock(&chip->reg_lock);	
-	result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff;
+	result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
 	spin_unlock(&chip->reg_lock);
+	result &= 0xffff;
 	return bytes_to_frames( substream->runtime, result );
 }
 
@@ -376,45 +532,63 @@
  * return IRQ_HANDLED no matter whether we actually had an IRQ flag or not).
  * ALS4000a.PDF writes that while ACKing IRQ in PCI block will *not* ACK
  * the IRQ in the SB core, ACKing IRQ in SB block *will* ACK the PCI IRQ
- * register (alt_port + 0x0e). Probably something could be optimized here to
- * query/write one register only...
+ * register (alt_port + ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU). Probably something
+ * could be optimized here to query/write one register only...
  * And even if both registers need to be queried, then there's still the
  * question of whether it's actually correct to ACK PCI IRQ before reading
  * SB IRQ like we do now, since ALS4000a.PDF mentions that PCI IRQ will *clear*
  * SB IRQ status.
+ * (hmm, SPECS_PAGE: 38 mentions it the other way around!)
  * And do we *really* need the lock here for *reading* SB_DSP4_IRQSTATUS??
  * */
 static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
 {
 	struct snd_sb *chip = dev_id;
-	unsigned gcr_status;
-	unsigned sb_status;
+	unsigned pci_irqstatus;
+	unsigned sb_irqstatus;
 
-	/* find out which bit of the ALS4000 produced the interrupt */
-	gcr_status = inb(chip->alt_port + 0xe);
-
-	if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */
+	/* find out which bit of the ALS4000 PCI block produced the interrupt,
+	   SPECS_PAGE: 38, 5 */
+	pci_irqstatus = snd_als4k_iobase_readb(chip->alt_port,
+				 ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU);
+	if ((pci_irqstatus & ALS4K_IOB_0E_SB_DMA_IRQ)
+	 && (chip->playback_substream)) /* playback */
 		snd_pcm_period_elapsed(chip->playback_substream);
-	if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
+	if ((pci_irqstatus & ALS4K_IOB_0E_CR1E_IRQ)
+	 && (chip->capture_substream)) /* capturing */
 		snd_pcm_period_elapsed(chip->capture_substream);
-	if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */
+	if ((pci_irqstatus & ALS4K_IOB_0E_MPU_IRQ)
+	 && (chip->rmidi)) /* MPU401 interrupt */
 		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
-	/* release the gcr */
-	outb(gcr_status, chip->alt_port + 0xe);
+	/* ACK the PCI block IRQ */
+	snd_als4k_iobase_writeb(chip->alt_port,
+			 ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU, pci_irqstatus);
 	
 	spin_lock(&chip->mixer_lock);
-	sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+	/* SPECS_PAGE: 20 */
+	sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
 	spin_unlock(&chip->mixer_lock);
 	
-	if (sb_status & SB_IRQTYPE_8BIT) 
+	if (sb_irqstatus & SB_IRQTYPE_8BIT)
 		snd_sb_ack_8bit(chip);
-	if (sb_status & SB_IRQTYPE_16BIT) 
+	if (sb_irqstatus & SB_IRQTYPE_16BIT)
 		snd_sb_ack_16bit(chip);
-	if (sb_status & SB_IRQTYPE_MPUIN)
+	if (sb_irqstatus & SB_IRQTYPE_MPUIN)
 		inb(chip->mpu_port);
-	if (sb_status & 0x20)
-		inb(SBP(chip, RESET));
-	return IRQ_HANDLED;
+	if (sb_irqstatus & ALS4K_IRQTYPE_CR1E_DMA)
+		snd_als4k_iobase_readb(chip->alt_port,
+					ALS4K_IOB_16_ACK_FOR_CR1E);
+
+	/* printk(KERN_INFO "als4000: irq 0x%04x 0x%04x\n",
+					 pci_irqstatus, sb_irqstatus); */
+
+	/* only ack the things we actually handled above */
+	return IRQ_RETVAL(
+	     (pci_irqstatus & (ALS4K_IOB_0E_SB_DMA_IRQ|ALS4K_IOB_0E_CR1E_IRQ|
+				ALS4K_IOB_0E_MPU_IRQ))
+	  || (sb_irqstatus & (SB_IRQTYPE_8BIT|SB_IRQTYPE_16BIT|
+				SB_IRQTYPE_MPUIN|ALS4K_IRQTYPE_CR1E_DMA))
+	);
 }
 
 /*****************************************************************/
@@ -526,7 +700,8 @@
 	struct snd_pcm *pcm;
 	int err;
 
-	if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0)
+	err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm);
+	if (err < 0)
 		return err;
 	pcm->private_data = chip;
 	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
@@ -543,48 +718,55 @@
 
 /******************************************************************/
 
-static void snd_als4000_set_addr(unsigned long gcr,
-					unsigned int sb,
-					unsigned int mpu,
-					unsigned int opl,
-					unsigned int game)
+static void snd_als4000_set_addr(unsigned long iobase,
+					unsigned int sb_io,
+					unsigned int mpu_io,
+					unsigned int opl_io,
+					unsigned int game_io)
 {
-	u32 confA = 0;
-	u32 confB = 0;
+	u32 cfg1 = 0;
+	u32 cfg2 = 0;
 
-	if (mpu > 0)
-		confB |= (mpu | 1) << 16;
-	if (sb > 0)
-		confB |= (sb | 1);
-	if (game > 0)
-		confA |= (game | 1) << 16;
-	if (opl > 0)	
-		confA |= (opl | 1);
-	snd_als4000_gcr_write_addr(gcr, 0xa8, confA);
-	snd_als4000_gcr_write_addr(gcr, 0xa9, confB);
+	if (mpu_io > 0)
+		cfg2 |= (mpu_io | 1) << 16;
+	if (sb_io > 0)
+		cfg2 |= (sb_io | 1);
+	if (game_io > 0)
+		cfg1 |= (game_io | 1) << 16;
+	if (opl_io > 0)
+		cfg1 |= (opl_io | 1);
+	snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA8_LEGACY_CFG1, cfg1);
+	snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA9_LEGACY_CFG2, cfg2);
 }
 
 static void snd_als4000_configure(struct snd_sb *chip)
 {
-	unsigned tmp;
+	u8 tmp;
 	int i;
 
 	/* do some more configuration */
 	spin_lock_irq(&chip->mixer_lock);
-	tmp = snd_sbmixer_read(chip, 0xc0);
-	snd_sbmixer_write(chip, 0xc0, tmp|0x80);
-	/* always select DMA channel 0, since we do not actually use DMA */
+	tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
+	snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+				tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
+	/* always select DMA channel 0, since we do not actually use DMA
+	 * SPECS_PAGE: 19/20 */
 	snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
-	snd_sbmixer_write(chip, 0xc0, tmp&0x7f);
+	snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+				 tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
 	spin_unlock_irq(&chip->mixer_lock);
 	
 	spin_lock_irq(&chip->reg_lock);
-	/* magic number. Enables interrupts(?) */
-	snd_als4000_gcr_write(chip, 0x8c, 0x28000);
-	for(i = 0x91; i <= 0x96; ++i)
-		snd_als4000_gcr_write(chip, i, 0);
+	/* enable interrupts */
+	snd_als4k_gcr_write(chip, ALS4K_GCR8C_MISC_CTRL,
+					ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE);
+
+	/* SPECS_PAGE: 39 */
+	for (i = ALS4K_GCR91_DMA0_ADDR; i <= ALS4K_GCR96_DMA3_MODE_COUNT; ++i)
+		snd_als4k_gcr_write(chip, i, 0);
 	
-	snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99));
+	snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL,
+		snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL));
 	spin_unlock_irq(&chip->reg_lock);
 }
 
@@ -628,7 +810,7 @@
 	gameport_set_port_data(gp, r);
 
 	/* Enable legacy joystick port */
-	snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1);
+	snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
 
 	gameport_register_port(acard->gameport);
 
@@ -643,7 +825,9 @@
 		gameport_unregister_port(acard->gameport);
 		acard->gameport = NULL;
 
-		snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0); /* disable joystick */
+		/* disable joystick */
+		snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
+
 		release_and_free_resource(r);
 	}
 }
@@ -654,10 +838,10 @@
 
 static void snd_card_als4000_free( struct snd_card *card )
 {
-	struct snd_card_als4000 * acard = (struct snd_card_als4000 *)card->private_data;
+	struct snd_card_als4000 *acard = card->private_data;
 
 	/* make sure that interrupts are disabled */
-	snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0);
+	snd_als4k_gcr_write_addr(acard->iobase, ALS4K_GCR8C_MISC_CTRL, 0);
 	/* free resources */
 	snd_als4000_free_gameport(acard);
 	pci_release_regions(acard->pci);
@@ -670,7 +854,7 @@
 	static int dev;
 	struct snd_card *card;
 	struct snd_card_als4000 *acard;
-	unsigned long gcr;
+	unsigned long iobase;
 	struct snd_sb *chip;
 	struct snd_opl3 *opl3;
 	unsigned short word;
@@ -699,31 +883,32 @@
 		pci_disable_device(pci);
 		return err;
 	}
-	gcr = pci_resource_start(pci, 0);
+	iobase = pci_resource_start(pci, 0);
 
 	pci_read_config_word(pci, PCI_COMMAND, &word);
 	pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
 	pci_set_master(pci);
 	
 	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 
-			    sizeof( struct snd_card_als4000 ) );
+			    sizeof(*acard) /* private_data: acard */);
 	if (card == NULL) {
 		pci_release_regions(pci);
 		pci_disable_device(pci);
 		return -ENOMEM;
 	}
 
-	acard = (struct snd_card_als4000 *)card->private_data;
+	acard = card->private_data;
 	acard->pci = pci;
-	acard->gcr = gcr;
+	acard->iobase = iobase;
 	card->private_free = snd_card_als4000_free;
 
 	/* disable all legacy ISA stuff */
-	snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0);
+	snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
 
 	if ((err = snd_sbdsp_create(card,
-				    gcr + 0x10,
+				    iobase + ALS4K_IOB_10_ADLIB_ADDR0,
 				    pci->irq,
+		/* internally registered as IRQF_SHARED in case of ALS4000 SB */
 				    snd_als4000_interrupt,
 				    -1,
 				    -1,
@@ -734,7 +919,7 @@
 	acard->chip = chip;
 
 	chip->pci = pci;
-	chip->alt_port = gcr;
+	chip->alt_port = iobase;
 	snd_card_set_dev(card, &pci->dev);
 
 	snd_als4000_configure(chip);
@@ -745,11 +930,18 @@
 		card->shortname, chip->alt_port, chip->irq);
 
 	if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
-				        gcr+0x30, MPU401_INFO_INTEGRATED,
+					iobase + ALS4K_IOB_30_MIDI_DATA,
+					MPU401_INFO_INTEGRATED,
 					pci->irq, 0, &chip->rmidi)) < 0) {
-		printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", gcr+0x30);
+		printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n",
+				iobase + ALS4K_IOB_30_MIDI_DATA);
 		goto out_err;
 	}
+	/* FIXME: ALS4000 has interesting MPU401 configuration features
+	 * at ALS4K_CR1A_MPU401_UART_MODE_CONTROL
+	 * (pass-thru / UART switching, fast MIDI clock, etc.),
+	 * however there doesn't seem to be an ALSA API for this...
+	 * SPECS_PAGE: 21 */
 
 	if ((err = snd_als4000_pcm(chip, 0)) < 0) {
 		goto out_err;
@@ -758,10 +950,13 @@
 		goto out_err;
 	}	    
 
-	if (snd_opl3_create(card, gcr+0x10, gcr+0x12,
+	if (snd_opl3_create(card,
+				iobase + ALS4K_IOB_10_ADLIB_ADDR0,
+				iobase + ALS4K_IOB_12_ADLIB_ADDR2,
 			    OPL3_HW_AUTO, 1, &opl3) < 0) {
 		printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx?\n",
-			   gcr+0x10, gcr+0x12 );
+			   iobase + ALS4K_IOB_10_ADLIB_ADDR0,
+			   iobase + ALS4K_IOB_12_ADLIB_ADDR2);
 	} else {
 		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
 			goto out_err;
@@ -831,13 +1026,13 @@
 
 #ifdef SUPPORT_JOYSTICK
 	if (acard->gameport)
-		snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1);
+		snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
 #endif
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 }
-#endif
+#endif /* CONFIG_PM */
 
 
 static struct pci_driver driver = {
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 457228f..085a52b 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -37,7 +37,7 @@
 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("ATI IXP AC97 controller");
 MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}");
+MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400/600}}");
 
 static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
 static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
@@ -290,6 +290,7 @@
 	{ 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */
 	{ 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */
 	{ 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */
+	{ 0x1002, 0x4382, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB600 */
 	{ 0, }
 };
 
@@ -722,7 +723,9 @@
 	struct atiixp_dma *dma = substream->runtime->private_data;
 	int err = 0;
 
-	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+	if (snd_BUG_ON(!dma->ops->enable_transfer ||
+		       !dma->ops->flush_dma))
+		return -EINVAL;
 
 	spin_lock(&chip->reg_lock);
 	switch (cmd) {
@@ -1032,7 +1035,8 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
-	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+		return -EINVAL;
 
 	if (dma->opened)
 		return -EBUSY;
@@ -1064,7 +1068,8 @@
 {
 	struct atiixp *chip = snd_pcm_substream_chip(substream);
 	/* disable DMA bits */
-	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+		return -EINVAL;
 	spin_lock_irq(&chip->reg_lock);
 	dma->ops->enable_dma(chip, 0);
 	spin_unlock_irq(&chip->reg_lock);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index d457a32..2f10630 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -674,7 +674,9 @@
 	struct atiixp_dma *dma = substream->runtime->private_data;
 	int err = 0;
 
-	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
+	if (snd_BUG_ON(!dma->ops->enable_transfer ||
+		       !dma->ops->flush_dma))
+		return -EINVAL;
 
 	spin_lock(&chip->reg_lock);
 	switch(cmd) {
@@ -865,7 +867,8 @@
 		.mask = 0,
 	};
 
-	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+		return -EINVAL;
 
 	if (dma->opened)
 		return -EBUSY;
@@ -895,7 +898,8 @@
 {
 	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
 	/* disable DMA bits */
-	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);
+	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
+		return -EINVAL;
 	spin_lock_irq(&chip->reg_lock);
 	dma->ops->enable_dma(chip, 0);
 	spin_unlock_irq(&chip->reg_lock);
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index 4aad35b..cf46bba 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -125,7 +125,6 @@
 	/* Virtual page extender stuff */
 	int nr_periods;
 	int period_bytes;
-	struct snd_sg_buf *sgbuf;	/* DMA Scatter Gather struct */
 	int period_real;
 	int period_virt;
 
@@ -195,16 +194,14 @@
 
 /* DMA Engines. */
 static void vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
-				     struct snd_sg_buf * sgbuf, int size,
-				     int count);
+				     int size, int count);
 static void vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie,
 				  int dir, int fmt, int d,
 				  u32 offset);
 static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb);
 #ifndef CHIP_AU8810
 static void vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
-				    struct snd_sg_buf * sgbuf, int size,
-				    int count);
+				    int size, int count);
 static void vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d,	/*int e, */
 				 u32 offset);
 static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb);
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 333c62d..b070e57 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -427,7 +427,7 @@
 
 	/* Set clipping ceiling (this may be all wrong). */
 	/*
-	for (x = 0; x > 0x80; x++) {
+	for (x = 0; x < 0x80; x++) {
 		hwwrite(vortex->mmio, VORTEX_MIXER_CLIP + (x << 2), 0x3ffff);
 	}
 	*/
@@ -1097,19 +1097,12 @@
 
 static void
 vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
-			 struct snd_sg_buf * sgbuf, int psize, int count)
+			 int psize, int count)
 {
 	stream_t *dma = &vortex->dma_adb[adbdma];
 
-	if (sgbuf == NULL) {
-		printk(KERN_INFO "vortex: FATAL: sgbuf is NULL!\n");
-		return;
-	}
-	//printk(KERN_INFO "vortex: page count = %d, tblcount = %d\n", count, sgbuf->tblsize);
-
 	dma->period_bytes = psize;
 	dma->nr_periods = count;
-	dma->sgbuf = sgbuf;
 
 	dma->cfg0 = 0;
 	dma->cfg1 = 0;
@@ -1120,26 +1113,26 @@
 		dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize - 1);
 		hwwrite(vortex->mmio,
 			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc,
-			snd_sgbuf_get_addr(sgbuf, psize * 3));
+			snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
 		/* 3 pages */
 	case 3:
 		dma->cfg0 |= 0x12000000;
 		dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
 		hwwrite(vortex->mmio,
 			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
-			snd_sgbuf_get_addr(sgbuf, psize * 2));
+			snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
 		/* 2 pages */
 	case 2:
 		dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
 		hwwrite(vortex->mmio,
 			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
-			snd_sgbuf_get_addr(sgbuf, psize));
+			snd_pcm_sgbuf_get_addr(dma->substream, psize));
 		/* 1 page */
 	case 1:
 		dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
 		hwwrite(vortex->mmio,
 			VORTEX_ADBDMA_BUFBASE + (adbdma << 4),
-			snd_sgbuf_get_addr(sgbuf, 0));
+			snd_pcm_sgbuf_get_addr(dma->substream, 0));
 		break;
 	}
 	//printk("vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1);
@@ -1205,7 +1198,7 @@
 			//hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), dma->table[p].addr);
 			hwwrite(vortex->mmio,
 				VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
-				snd_sgbuf_get_addr(dma->sgbuf,
+				snd_pcm_sgbuf_get_addr(dma->substream,
 				dma->period_bytes * p));
 			/* Force write thru cache. */
 			hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
@@ -1244,7 +1237,10 @@
 			if (pp >= 4)
 				pp -= 4;
 		}
-		hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+		hwwrite(vortex->mmio,
+			VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
+			snd_pcm_sgbuf_get_addr(dma->substream,
+					       dma->period_bytes * p));
 		/* Force write thru cache. */
 		hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
 	}
@@ -1367,13 +1363,12 @@
 
 static void
 vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
-			struct snd_sg_buf * sgbuf, int psize, int count)
+			int psize, int count)
 {
 	stream_t *dma = &vortex->dma_wt[wtdma];
 
 	dma->period_bytes = psize;
 	dma->nr_periods = count;
-	dma->sgbuf = sgbuf;
 
 	dma->cfg0 = 0;
 	dma->cfg1 = 0;
@@ -1383,23 +1378,23 @@
 	case 4:
 		dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
 		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
-			snd_sgbuf_get_addr(sgbuf, psize * 3));
+			snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
 		/* 3 pages */
 	case 3:
 		dma->cfg0 |= 0x12000000;
 		dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
 		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4)  + 0x8,
-			snd_sgbuf_get_addr(sgbuf, psize * 2));
+			snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
 		/* 2 pages */
 	case 2:
 		dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
 		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
-			snd_sgbuf_get_addr(sgbuf, psize));
+			snd_pcm_sgbuf_get_addr(dma->substream, psize));
 		/* 1 page */
 	case 1:
 		dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
 		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4),
-			snd_sgbuf_get_addr(sgbuf, 0));
+			snd_pcm_sgbuf_get_addr(dma->substream, 0));
 		break;
 	}
 	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + (wtdma << 3), dma->cfg0);
@@ -1465,7 +1460,8 @@
 			hwwrite(vortex->mmio,
 				VORTEX_WTDMA_BUFBASE +
 				(((wtdma << 2) + pp) << 2),
-				snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p));
+				snd_pcm_sgbuf_get_addr(dma->substream,
+						       dma->period_bytes * p));
 			/* Force write thru cache. */
 			hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
 			       (((wtdma << 2) + pp) << 2));
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index f9a58b4..b9d2f20 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -189,7 +189,6 @@
 {
 	vortex_t *chip = snd_pcm_substream_chip(substream);
 	stream_t *stream = (stream_t *) (substream->runtime->private_data);
-	struct snd_sg_buf *sgbuf;
 	int err;
 
 	// Alloc buffer memory.
@@ -199,8 +198,6 @@
 		printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
 		return err;
 	}
-	//sgbuf = (struct snd_sg_buf *) substream->runtime->dma_private;
-	sgbuf = snd_pcm_substream_sgbuf(substream);
 	/*
 	   printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
 	   params_period_bytes(hw_params), params_channels(hw_params));
@@ -226,7 +223,7 @@
 		stream = substream->runtime->private_data = &chip->dma_adb[dma];
 		stream->substream = substream;
 		/* Setup Buffers. */
-		vortex_adbdma_setbuffers(chip, dma, sgbuf,
+		vortex_adbdma_setbuffers(chip, dma,
 					 params_period_bytes(hw_params),
 					 params_periods(hw_params));
 	}
@@ -240,7 +237,7 @@
 		    &chip->dma_wt[substream->number];
 		stream->dma = substream->number;
 		stream->substream = substream;
-		vortex_wtdma_setbuffers(chip, substream->number, sgbuf,
+		vortex_wtdma_setbuffers(chip, substream->number,
 					params_period_bytes(hw_params),
 					params_periods(hw_params));
 	}
@@ -392,13 +389,6 @@
 	return (bytes_to_frames(substream->runtime, current_ptr));
 }
 
-/* Page callback. */
-/*
-static struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset) {
-	
-	
-}
-*/
 /* operators */
 static struct snd_pcm_ops snd_vortex_playback_ops = {
 	.open = snd_vortex_pcm_open,
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 22f18f3..333007c 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -816,7 +816,8 @@
 	int err;
 
 	snd_azf3328_dbgcallenter();
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip || !chip->card))
+		return -EINVAL;
 
 	card = chip->card;
 
@@ -1471,7 +1472,8 @@
 	u8 val;
 	unsigned long flags;
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 4ecdd63..3aa8d97 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -227,7 +227,6 @@
 static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substream *substream,
 			       	 unsigned int periods, unsigned int period_bytes)
 {
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 	unsigned int i, offset;
 	u32 *risc;
 
@@ -246,6 +245,7 @@
 		rest = period_bytes;
 		do {
 			u32 cmd, len;
+			unsigned int addr;
 
 			len = PAGE_SIZE - (offset % PAGE_SIZE);
 			if (len > rest)
@@ -260,7 +260,8 @@
 			if (len == rest)
 				cmd |= RISC_EOL | RISC_IRQ;
 			*risc++ = cpu_to_le32(cmd);
-			*risc++ = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, offset));
+			addr = snd_pcm_sgbuf_get_addr(substream, offset);
+			*risc++ = cpu_to_le32(addr);
 			offset += len;
 			rest -= len;
 		} while (rest > 0);
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 6abe8a3..a7d8966 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -254,7 +254,7 @@
 	   .name   = "MSI K8N Diamond MB",
 	   .gpio_type = 2,
 	   .i2c_adc = 1,
-	   .spi_dac = 2 },
+	   .spi_dac = 2 } ,
 	 /* Shuttle XPC SD31P which has an onboard Creative Labs
 	  * Sound Blaster Live! 24-bit EAX
 	  * high-definition 7.1 audio processor".
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index 893ee4f..c788511 100644
--- a/sound/pci/ca0106/ca_midi.c
+++ b/sound/pci/ca0106/ca_midi.c
@@ -125,7 +125,8 @@
 	struct snd_ca_midi *midi = substream->rmidi->private_data;
 	unsigned long flags;
 	
-	snd_assert(midi->dev_id, return -ENXIO);
+	if (snd_BUG_ON(!midi->dev_id))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->midi_mode |= CA_MIDI_MODE_INPUT;
 	midi->substream_input = substream;
@@ -144,7 +145,8 @@
 	struct snd_ca_midi *midi = substream->rmidi->private_data;
 	unsigned long flags;
 
-	snd_assert(midi->dev_id, return -ENXIO);
+	if (snd_BUG_ON(!midi->dev_id))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
 	midi->substream_output = substream;
@@ -163,7 +165,8 @@
 	struct snd_ca_midi *midi = substream->rmidi->private_data;
 	unsigned long flags;
 
-	snd_assert(midi->dev_id, return -ENXIO);
+	if (snd_BUG_ON(!midi->dev_id))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->interrupt_disable(midi,midi->rx_enable);
 	midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
@@ -181,7 +184,9 @@
 {
 	struct snd_ca_midi *midi = substream->rmidi->private_data;
 	unsigned long flags;
-	snd_assert(midi->dev_id, return -ENXIO);
+
+	if (snd_BUG_ON(!midi->dev_id))
+		return -ENXIO;
 	
 	spin_lock_irqsave(&midi->open_lock, flags);
 
@@ -201,7 +206,9 @@
 static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 {
 	struct snd_ca_midi *midi = substream->rmidi->private_data;
-	snd_assert(midi->dev_id, return);
+
+	if (snd_BUG_ON(!midi->dev_id))
+		return;
 
 	if (up) {
 		midi->interrupt_enable(midi,midi->rx_enable);
@@ -215,7 +222,8 @@
 	struct snd_ca_midi *midi = substream->rmidi->private_data;
 	unsigned long flags;
 
-	snd_assert(midi->dev_id, return);
+	if (snd_BUG_ON(!midi->dev_id))
+		return;
 
 	if (up) {
 		int max = 4;
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 9971b5b..1a74ca6 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2357,7 +2357,8 @@
 {
 	struct cmipci_switch_args *args;
 	args = (struct cmipci_switch_args *)kcontrol->private_value;
-	snd_assert(args != NULL, return -EINVAL);
+	if (snd_BUG_ON(!args))
+		return -EINVAL;
 	return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);
 }
 
@@ -2401,7 +2402,8 @@
 {
 	struct cmipci_switch_args *args;
 	args = (struct cmipci_switch_args *)kcontrol->private_value;
-	snd_assert(args != NULL, return -EINVAL);
+	if (snd_BUG_ON(!args))
+		return -EINVAL;
 	return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args);
 }
 
@@ -2662,7 +2664,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(cm != NULL && cm->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!cm || !cm->card))
+		return -EINVAL;
 
 	card = cm->card;
 
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 7556fd9..ef9308f 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -766,13 +766,13 @@
 	if (!capture) {
 		if (dma->left_slot == chip->src_left_play_slot) {
 			unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
-			snd_assert(dma->right_slot == chip->src_right_play_slot, );
+			snd_BUG_ON(dma->right_slot != chip->src_right_play_slot);
 			snd_cs4281_pokeBA0(chip, BA0_DACSR, val);
 		}
 	} else {
 		if (dma->left_slot == chip->src_left_rec_slot) {
 			unsigned int val = snd_cs4281_rate(runtime->rate, NULL);
-			snd_assert(dma->right_slot == chip->src_right_rec_slot, );
+			snd_BUG_ON(dma->right_slot != chip->src_right_rec_slot);
 			snd_cs4281_pokeBA0(chip, BA0_ADCSR, val);
 		}
 	}
@@ -1209,7 +1209,8 @@
 {
 	struct cs4281 *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return);
+	if (snd_BUG_ON(!chip))
+		return;
 	snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff);
 }
 
@@ -1217,7 +1218,8 @@
 {
 	struct cs4281 *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 	return snd_cs4281_peekBA0(chip, BA0_JSPT);
 }
 
@@ -1228,7 +1230,8 @@
 	struct cs4281 *chip = gameport_get_port_data(gameport);
 	unsigned js1, js2, jst;
 	
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 
 	js1 = snd_cs4281_peekBA0(chip, BA0_JSC1);
 	js2 = snd_cs4281_peekBA0(chip, BA0_JSC2);
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index e214e56..fb6dc39 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -90,9 +90,10 @@
 	int count;
 	unsigned short result,tmp;
 	u32 offset = 0;
-	snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
-		     (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
-		     return -EINVAL);
+
+	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+		return -EINVAL;
 
 	chip->active_ctrl(chip, 1);
 
@@ -212,9 +213,9 @@
 	unsigned short val;
 	int codec_index = ac97->num;
 
-	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
-		   codec_index == CS46XX_SECONDARY_CODEC_INDEX,
-		   return 0xffff);
+	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+		return 0xffff;
 
 	val = snd_cs46xx_codec_read(chip, reg, codec_index);
 
@@ -229,9 +230,9 @@
 {
 	int count;
 
-	snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||
-		    (codec_index == CS46XX_SECONDARY_CODEC_INDEX),
-		    return);
+	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+		return;
 
 	chip->active_ctrl(chip, 1);
 
@@ -294,9 +295,9 @@
 	struct snd_cs46xx *chip = ac97->private_data;
 	int codec_index = ac97->num;
 
-	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||
-		   codec_index == CS46XX_SECONDARY_CODEC_INDEX,
-		   return);
+	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
+		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
+		return;
 
 	snd_cs46xx_codec_write(chip, reg, val, codec_index);
 }
@@ -315,7 +316,8 @@
 	unsigned int bank = offset >> 16;
 	offset = offset & 0xffff;
 
-	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+	if (snd_BUG_ON((offset & 3) || (len & 3)))
+		return -EINVAL;
 	dst = chip->region.idx[bank+1].remap_addr + offset;
 	len /= sizeof(u32);
 
@@ -343,7 +345,8 @@
 	unsigned int bank = offset >> 16;
 	offset = offset & 0xffff;
 
-	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);
+	if (snd_BUG_ON((offset & 3) || (len & 3)))
+		return -EINVAL;
 	dst = chip->region.idx[bank+1].remap_addr + offset;
 	len /= sizeof(u32);
 
@@ -722,7 +725,9 @@
 	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
 	size_t ptr;
 	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
-	snd_assert (cpcm->pcm_channel,return -ENXIO);
+
+	if (snd_BUG_ON(!cpcm->pcm_channel))
+		return -ENXIO;
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
@@ -740,7 +745,8 @@
 	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	snd_assert (cpcm->pcm_channel,return -ENXIO);
+	if (snd_BUG_ON(!cpcm->pcm_channel))
+		return -ENXIO;
 	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
 #else
 	ptr = snd_cs46xx_peek(chip, BA1_PBA);
@@ -908,7 +914,8 @@
 	cpcm = runtime->private_data;
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	snd_assert (sample_rate != 0, return -ENXIO);
+	if (snd_BUG_ON(!sample_rate))
+		return -ENXIO;
 
 	mutex_lock(&chip->spos_mutex);
 
@@ -917,7 +924,7 @@
 		return -ENXIO;
 	}
 
-	snd_assert (cpcm->pcm_channel != NULL);
+	snd_BUG_ON(!cpcm->pcm_channel);
 	if (!cpcm->pcm_channel) {
 		mutex_unlock(&chip->spos_mutex);
 		return -ENXIO;
@@ -952,7 +959,7 @@
 		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
 			substream->ops = &snd_cs46xx_playback_iec958_ops;
 		} else {
-			snd_assert(0);
+			snd_BUG();
 		}
 #else
 		substream->ops = &snd_cs46xx_playback_ops;
@@ -981,7 +988,7 @@
 		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
 			substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;
 		} else {
-			snd_assert(0);
+			snd_BUG();
 		}
 #else
 		substream->ops = &snd_cs46xx_playback_indirect_ops;
@@ -1029,7 +1036,8 @@
 	cpcm = runtime->private_data;
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-    snd_assert (cpcm->pcm_channel != NULL, return -ENXIO);
+	if (snd_BUG_ON(!cpcm->pcm_channel))
+		return -ENXIO;
 
 	pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
 	pfie &= ~0x0000f03f;
@@ -1714,9 +1722,9 @@
 {
 	struct snd_cs46xx *chip = ac97->private_data;
 
-	snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) ||
-		    (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]),
-		    return);
+	if (snd_BUG_ON(ac97 != chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] &&
+		       ac97 != chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]))
+		return;
 
 	if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) {
 		chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL;
@@ -1864,7 +1872,7 @@
 		break;
 	default:
 		res = -EINVAL;
-		snd_assert(0, (void)0);
+		snd_BUG(); /* should never happen ... */
 	}
 
 	return res;
@@ -2236,7 +2244,7 @@
 		snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3);
 		snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);
 	} else {
-		snd_assert(0); /* should never happen ... */
+		snd_BUG(); /* should never happen ... */
 	}
 
 	udelay(50);
@@ -2553,7 +2561,8 @@
 {
 	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return);
+	if (snd_BUG_ON(!chip))
+		return;
 	snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF);  //outb(gameport->io, 0xFF);
 }
 
@@ -2561,7 +2570,8 @@
 {
 	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 	return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);
 }
 
@@ -2570,7 +2580,8 @@
 	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
 	unsigned js1, js2, jst;
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 
 	js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1);
 	js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2);
@@ -2754,7 +2765,8 @@
 {
 	int idx;
 
-	snd_assert(chip != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip))
+		return -EINVAL;
 
 	if (chip->active_ctrl)
 		chip->active_ctrl(chip, 1);
@@ -3489,8 +3501,9 @@
 		.name = "Mitac MI6020/21",
 		.amp = amp_voyetra,
 	},
+	/* Hercules Game Theatre XP */
 	{
-		.vendor = 0x14AF,
+		.vendor = 0x14af, /* Guillemot Corporation */
 		.id = 0x0050,
 		.name = "Hercules Game Theatre XP",
 		.amp = amp_hercules,
@@ -3532,9 +3545,25 @@
 		.amp = amp_hercules,
 		.mixer_init = hercules_mixer_init,
 	},
+	/* Herculess Fortissimo */
+	{
+		.vendor = 0x1681,
+		.id = 0xa010,
+		.name = "Hercules Gamesurround Fortissimo II",
+	},
+	{
+		.vendor = 0x1681,
+		.id = 0xa011,
+		.name = "Hercules Gamesurround Fortissimo III 7.1",
+	},
 	/* Teratec */
 	{
 		.vendor = 0x153b,
+		.id = 0x112e,
+		.name = "Terratec DMX XFire 1024",
+	},
+	{
+		.vendor = 0x153b,
 		.id = 0x1136,
 		.name = "Terratec SiXPack 5.1",
 	},
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index ccc8bed..f4f0c8f 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -63,7 +63,8 @@
 	u32 mop_operands,mop_type,wide_op;
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert( ((size % 2) == 0), return -EINVAL);
+	if (snd_BUG_ON(size %2))
+		return -EINVAL;
   
 	while (i < size) {
 		loval = data[i++];
@@ -289,7 +290,8 @@
 	int i;
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert(ins != NULL, return);
+	if (snd_BUG_ON(!ins))
+		return;
 
 	mutex_lock(&chip->spos_mutex);
 	for (i = 0; i < ins->nscb; ++i) {
@@ -404,7 +406,8 @@
 
 		/* if module has a code segment it must have
 		   symbol table */
-		snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM);
+		if (snd_BUG_ON(!module->symbol_table.symbols))
+			return -ENOMEM;
 		if (add_symbols(chip,module)) {
 			snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n");
 			return -ENOMEM;
@@ -1369,7 +1372,8 @@
 
 	valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
 
-	snd_assert (chip->nr_ac97_codecs == 1 || chip->nr_ac97_codecs == 2);
+	if (snd_BUG_ON(chip->nr_ac97_codecs != 1 && chip->nr_ac97_codecs != 2))
+		goto _fail_end;
 
 	if (chip->nr_ac97_codecs == 1) {
 		/* output on slot 5 and 11 
@@ -1609,11 +1613,14 @@
 
 		spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST);
 
-		snd_assert(spdifo_scb_desc, return -EIO);
+		if (snd_BUG_ON(!spdifo_scb_desc))
+			return -EIO;
 		spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST);
-		snd_assert(spdifi_scb_desc, return -EIO);
+		if (snd_BUG_ON(!spdifi_scb_desc))
+			return -EIO;
 		async_codec_scb_desc = cs46xx_dsp_create_scb(chip,"AsynCodecInputSCB",(u32 *)&async_codec_input_scb, HFG_TREE_SCB);
-		snd_assert(async_codec_scb_desc, return -EIO);
+		if (snd_BUG_ON(!async_codec_scb_desc))
+			return -EIO;
 
 		async_codec_scb_desc->parent_scb_ptr = NULL;
 		async_codec_scb_desc->next_scb_ptr = spdifi_scb_desc;
@@ -1698,8 +1705,10 @@
 	chip->active_ctrl(chip, 1);
 	chip->amplifier_ctrl(chip, 1);
 
-	snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
-	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+	if (snd_BUG_ON(ins->asynch_rx_scb))
+		return -EINVAL;
+	if (snd_BUG_ON(!ins->spdif_in_src))
+		return -EINVAL;
 
 	mutex_lock(&chip->spos_mutex);
 
@@ -1754,8 +1763,10 @@
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
-	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);	
+	if (snd_BUG_ON(!ins->asynch_rx_scb))
+		return -EINVAL;
+	if (snd_BUG_ON(!ins->spdif_in_src))
+		return -EINVAL;
 
 	mutex_lock(&chip->spos_mutex);
 
@@ -1780,8 +1791,10 @@
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert (ins->pcm_input == NULL,return -EINVAL);
-	snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
+	if (snd_BUG_ON(ins->pcm_input))
+		return -EINVAL;
+	if (snd_BUG_ON(!ins->ref_snoop_scb))
+		return -EINVAL;
 
 	mutex_lock(&chip->spos_mutex);
 	ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
@@ -1795,7 +1808,8 @@
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert (ins->pcm_input != NULL,return -EINVAL);
+	if (snd_BUG_ON(!ins->pcm_input))
+		return -EINVAL;
 
 	mutex_lock(&chip->spos_mutex);
 	cs46xx_dsp_remove_scb (chip,ins->pcm_input);
@@ -1809,8 +1823,10 @@
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert (ins->adc_input == NULL,return -EINVAL);
-	snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
+	if (snd_BUG_ON(ins->adc_input))
+		return -EINVAL;
+	if (snd_BUG_ON(!ins->codec_in_scb))
+		return -EINVAL;
 
 	mutex_lock(&chip->spos_mutex);
 	ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
@@ -1824,7 +1840,8 @@
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert (ins->adc_input != NULL,return -EINVAL);
+	if (snd_BUG_ON(!ins->adc_input))
+		return -EINVAL;
 
 	mutex_lock(&chip->spos_mutex);
 	cs46xx_dsp_remove_scb (chip,ins->adc_input);
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 2873cfe..dd7c41b 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -46,8 +46,11 @@
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	int symbol_index = (int)(symbol - ins->symbol_table.symbols);
 
-	snd_assert(ins->symbol_table.nsymbols > 0,return);
-	snd_assert(symbol_index >= 0 && symbol_index < ins->symbol_table.nsymbols, return);
+	if (snd_BUG_ON(ins->symbol_table.nsymbols <= 0))
+		return;
+	if (snd_BUG_ON(symbol_index < 0 ||
+		       symbol_index >= ins->symbol_table.nsymbols))
+		return;
 
 	ins->symbol_table.symbols[symbol_index].deleted = 1;
 
@@ -116,8 +119,9 @@
 
 	if ( scb->parent_scb_ptr ) {
 		/* unlink parent SCB */
-		snd_assert ((scb->parent_scb_ptr->sub_list_ptr == scb ||
-			     scb->parent_scb_ptr->next_scb_ptr == scb),return);
+		if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr != scb &&
+			       scb->parent_scb_ptr->next_scb_ptr != scb))
+			return;
   
 		if (scb->parent_scb_ptr->sub_list_ptr == scb) {
 
@@ -140,7 +144,6 @@
 				scb->next_scb_ptr = ins->the_null_scb;
 			}
 		} else {
-			/* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */
 			scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;
 
 			if (scb->next_scb_ptr != ins->the_null_scb) {
@@ -181,16 +184,17 @@
 	unsigned long flags;
 
 	/* check integrety */
-	snd_assert ( (scb->index >= 0 && 
-		      scb->index < ins->nscb && 
-		      (ins->scbs + scb->index) == scb), return );
+	if (snd_BUG_ON(scb->index < 0 ||
+		       scb->index >= ins->nscb ||
+		       (ins->scbs + scb->index) != scb))
+		return;
 
 #if 0
 	/* can't remove a SCB with childs before 
 	   removing childs first  */
-	snd_assert ( (scb->sub_list_ptr == ins->the_null_scb &&
-		      scb->next_scb_ptr == ins->the_null_scb),
-		     goto _end);
+	if (snd_BUG_ON(scb->sub_list_ptr != ins->the_null_scb ||
+		       scb->next_scb_ptr != ins->the_null_scb))
+		goto _end;
 #endif
 
 	spin_lock_irqsave(&scb->lock, flags);
@@ -198,7 +202,8 @@
 	spin_unlock_irqrestore(&scb->lock, flags);
 
 	cs46xx_dsp_proc_free_scb_desc(scb);
-	snd_assert (scb->scb_symbol != NULL, return );
+	if (snd_BUG_ON(!scb->scb_symbol))
+		return;
 	remove_symbol (chip,scb->scb_symbol);
 
 	ins->scbs[scb->index].deleted = 1;
@@ -234,7 +239,6 @@
 		snd_info_free_entry(scb->proc_info);
 		scb->proc_info = NULL;
 
-		snd_assert (scb_info != NULL, return);
 		kfree (scb_info);
 	}
 }
@@ -291,7 +295,8 @@
   
 	unsigned long flags;
 
-	snd_assert (ins->the_null_scb != NULL,return NULL);
+	if (snd_BUG_ON(!ins->the_null_scb))
+		return NULL;
 
 	/* fill the data that will be wroten to DSP */
 	scb_data[SCBsubListPtr] = 
@@ -321,18 +326,20 @@
 #endif
 		/* link to  parent SCB */
 		if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
-			snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
-				     return NULL);
+			if (snd_BUG_ON(scb->parent_scb_ptr->next_scb_ptr !=
+				       ins->the_null_scb))
+				return NULL;
 
 			scb->parent_scb_ptr->next_scb_ptr = scb;
 
 		} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
-			snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
-				     return NULL);
+			if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr !=
+				       ins->the_null_scb))
+				return NULL;
 
 			scb->parent_scb_ptr->sub_list_ptr = scb;
 		} else {
-			snd_assert (0,return NULL);
+			snd_BUG();
 		}
 
 		spin_lock_irqsave(&chip->reg_lock, flags);
@@ -675,7 +682,7 @@
 		if (pass_through) {
 			/* wont work with any other rate than
 			   the native DSP rate */
-			snd_assert (rate == 48000);
+			snd_BUG_ON(rate != 48000);
 
 			scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
 							    dest,"DMAREADER",parent_scb,
@@ -1142,7 +1149,8 @@
 	struct dsp_scb_descriptor * scb = from;
 
 	while (scb->next_scb_ptr != ins->the_null_scb) {
-		snd_assert (scb->next_scb_ptr != NULL, return NULL);
+		if (snd_BUG_ON(!scb->next_scb_ptr))
+			return NULL;
 
 		scb = scb->next_scb_ptr;
 	}
@@ -1246,10 +1254,11 @@
 		break;
 	case DSP_PCM_S71_CHANNEL:
 		/* TODO */
-		snd_assert(0);
+		snd_BUG();
 		break;
 	case DSP_IEC958_CHANNEL:
-		snd_assert (ins->asynch_tx_scb != NULL, return NULL);
+		if (snd_BUG_ON(!ins->asynch_tx_scb))
+			return NULL;
 		mixer_scb = ins->asynch_tx_scb;
 
 		/* if sample rate is set to 48khz we pass
@@ -1262,7 +1271,7 @@
 		}
 		break;
 	default:
-		snd_assert (0);
+		snd_BUG();
 		return NULL;
 	}
 	/* default sample rate is 44100 */
@@ -1308,7 +1317,8 @@
 				break;
 			}
 		}
-		snd_assert (src_index != -1,return NULL);
+		if (snd_BUG_ON(src_index == -1))
+			return NULL;
 
 		/* we need to create a new SRC SCB */
 		if (mixer_scb->sub_list_ptr == ins->the_null_scb) {
@@ -1462,9 +1472,10 @@
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	unsigned long flags;
 
-	snd_assert(pcm_channel->active, return );
-	snd_assert(ins->npcm_channels > 0, return );
-	snd_assert(pcm_channel->src_scb->ref_count > 0, return );
+	if (snd_BUG_ON(!pcm_channel->active ||
+		       ins->npcm_channels <= 0 ||
+		       pcm_channel->src_scb->ref_count <= 0))
+		return;
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	pcm_channel->unlinked = 1;
@@ -1479,8 +1490,9 @@
 	if (!pcm_channel->src_scb->ref_count) {
 		cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
 
-		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot < DSP_MAX_SRC_NR,
-			    return );
+		if (snd_BUG_ON(pcm_channel->src_slot < 0 ||
+			       pcm_channel->src_slot >= DSP_MAX_SRC_NR))
+			return;
 
 		ins->src_scb_slots[pcm_channel->src_slot] = 0;
 		ins->nsrc_scb --;
@@ -1490,11 +1502,11 @@
 int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
 			   struct dsp_pcm_channel_descriptor * pcm_channel)
 {
-	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	unsigned long flags;
 
-	snd_assert(pcm_channel->active,return -EIO);
-	snd_assert(ins->npcm_channels > 0,return -EIO);
+	if (snd_BUG_ON(!pcm_channel->active ||
+		       chip->dsp_spos_instance->npcm_channels <= 0))
+		return -EIO;
 
 	spin_lock(&pcm_channel->src_scb->lock);
 
@@ -1537,7 +1549,7 @@
 
 	src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
 
-	snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
+	snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);
 	pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
@@ -1564,7 +1576,8 @@
 	struct dsp_scb_descriptor * pcm_input;
 	int insert_point;
 
-	snd_assert (ins->record_mixer_scb != NULL,return NULL);
+	if (snd_BUG_ON(!ins->record_mixer_scb))
+		return NULL;
 
 	if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {
 		parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr);
@@ -1583,7 +1596,8 @@
 
 int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
 {
-	snd_assert (src->parent_scb_ptr != NULL,  return -EINVAL );
+	if (snd_BUG_ON(!src->parent_scb_ptr))
+		return -EINVAL;
 
 	/* mute SCB */
 	cs46xx_dsp_scb_set_volume (chip,src,0,0);
@@ -1598,8 +1612,10 @@
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	struct dsp_scb_descriptor * parent_scb;
 
-	snd_assert (src->parent_scb_ptr == NULL,   return -EINVAL );
-	snd_assert(ins->master_mix_scb !=NULL,   return -EINVAL );
+	if (snd_BUG_ON(src->parent_scb_ptr))
+		return -EINVAL;
+	if (snd_BUG_ON(!ins->master_mix_scb))
+		return -EINVAL;
 
 	if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
 		parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
@@ -1635,8 +1651,11 @@
 		return -EBUSY;
 	}
 
-	snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL);
-	snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL);
+	if (snd_BUG_ON(ins->asynch_tx_scb))
+		return -EINVAL;
+	if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr !=
+		       ins->the_null_scb))
+		return -EINVAL;
 
 	/* reset output snooper sample buffer pointer */
 	snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2,
@@ -1676,10 +1695,15 @@
 	}
 
 	/* check integrety */
-	snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
-	snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL);
-	snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL);
-	snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL);
+	if (snd_BUG_ON(!ins->asynch_tx_scb))
+		return -EINVAL;
+	if (snd_BUG_ON(!ins->spdif_pcm_input_scb))
+		return -EINVAL;
+	if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr != ins->asynch_tx_scb))
+		return -EINVAL;
+	if (snd_BUG_ON(ins->asynch_tx_scb->parent_scb_ptr !=
+		       ins->master_mix_scb))
+		return -EINVAL;
 
 	cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
 	cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
@@ -1734,7 +1758,8 @@
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 
-	snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL);
+	if (snd_BUG_ON(!ins->asynch_tx_scb))
+		return -EINVAL;
 
 	ins->spdif_status_out  &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN;
 
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
index 4159e3b..2904330 100644
--- a/sound/pci/echoaudio/darla20_dsp.c
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -34,7 +34,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Darla20\n"));
-	snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
index 79938ee..6022873 100644
--- a/sound/pci/echoaudio/darla24_dsp.c
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -34,7 +34,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Darla24\n"));
-	snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -148,8 +149,9 @@
 
 static int set_input_clock(struct echoaudio *chip, u16 clock)
 {
-	snd_assert(clock == ECHO_CLOCK_INTERNAL ||
-		   clock == ECHO_CLOCK_ESYNC, return -EINVAL);
+	if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL &&
+		       clock != ECHO_CLOCK_ESYNC))
+		return -EINVAL;
 	chip->input_clock = clock;
 	return set_sample_rate(chip, chip->sample_rate);
 }
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index 48eb7c5..417e25a 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -47,7 +47,8 @@
 
 	local_irq_enable();
 	DE_INIT(("init_hw() - Echo3G\n"));
-	snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -104,9 +105,11 @@
 	if ((err = init_line_levels(chip)) < 0)
 		return err;
 	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-	snd_assert(err >= 0, return err);
+	if (err < 0)
+		return err;
 	err = set_phantom_power(chip, 0);
-	snd_assert(err >= 0, return err);
+	if (err < 0)
+		return err;
 	err = set_professional_spdif(chip, TRUE);
 
 	DE_INIT(("init_hw done\n"));
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index e16dc92..8dbc5c4 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -490,7 +490,6 @@
 {
 	struct echoaudio *chip;
 	int err, per, rest, page, edge, offs;
-	struct snd_sg_buf *sgbuf;
 	struct audiopipe *pipe;
 
 	chip = snd_pcm_substream_chip(substream);
@@ -503,7 +502,7 @@
 	if (pipe->index >= 0) {
 		DE_HWP(("hwp_ie free(%d)\n", pipe->index));
 		err = free_pipes(chip, pipe);
-		snd_assert(!err);
+		snd_BUG_ON(err);
 		chip->substream[pipe->index] = NULL;
 	}
 
@@ -531,10 +530,6 @@
 		return err;
 	}
 
-	sgbuf = snd_pcm_substream_sgbuf(substream);
-
-	DE_HWP(("pcm_hw_params table size=%d pages=%d\n",
-		sgbuf->size, sgbuf->pages));
 	sglist_init(chip, pipe);
 	edge = PAGE_SIZE;
 	for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
@@ -543,16 +538,15 @@
 		if (offs + rest > params_buffer_bytes(hw_params))
 			rest = params_buffer_bytes(hw_params) - offs;
 		while (rest) {
+			dma_addr_t addr;
+			addr = snd_pcm_sgbuf_get_addr(substream, offs);
 			if (rest <= edge - offs) {
-				sglist_add_mapping(chip, pipe,
-						   snd_sgbuf_get_addr(sgbuf, offs),
-						   rest);
+				sglist_add_mapping(chip, pipe, addr, rest);
 				sglist_add_irq(chip, pipe);
 				offs += rest;
 				rest = 0;
 			} else {
-				sglist_add_mapping(chip, pipe,
-						   snd_sgbuf_get_addr(sgbuf, offs),
+				sglist_add_mapping(chip, pipe, addr,
 						   edge - offs);
 				rest -= edge - offs;
 				offs = edge;
@@ -690,8 +684,10 @@
 		return -EINVAL;
 	}
 
-	snd_assert(pipe_index < px_num(chip), return -EINVAL);
-	snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL);
+	if (snd_BUG_ON(pipe_index >= px_num(chip)))
+		return -EINVAL;
+	if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index)))
+		return -EINVAL;
 	set_audio_format(chip, pipe_index, &format);
 	return 0;
 }
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
index 52a9331..c3736bb 100644
--- a/sound/pci/echoaudio/echoaudio_3g.c
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -103,9 +103,11 @@
 	int err, i, o;
 
 	/* All audio channels must be closed before changing the digital mode */
-	snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+	if (snd_BUG_ON(chip->pipe_alloc_mask))
+		return -EAGAIN;
 
-	snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+	if (snd_BUG_ON(!(chip->digital_modes & (1 << mode))))
+		return -EINVAL;
 
 	previous_mode = chip->digital_mode;
 	err = dsp_set_digital_mode(chip, mode);
@@ -267,8 +269,9 @@
 		return 0;
 	}
 
-	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
-		   return -EINVAL);
+	if (snd_BUG_ON(rate >= 50000 &&
+		       chip->digital_mode == DIGITAL_MODE_ADAT))
+		return -EINVAL;
 
 	clock = 0;
 	control_reg = le32_to_cpu(chip->comm_page->control_register);
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index e6c1007..be0e181 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -474,7 +474,8 @@
 	const struct firmware *fw;
 	int box_type, err;
 
-	snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM);
+	if (snd_BUG_ON(!chip->dsp_code_to_load || !chip->comm_page))
+		return -EPERM;
 
 	/* See if the ASIC is present and working - only if the DSP is already loaded */
 	if (chip->dsp_code) {
@@ -512,8 +513,8 @@
 /* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */
 static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)
 {
-	snd_assert(index < num_busses_out(chip) + num_busses_in(chip),
-		   return -EINVAL);
+	if (snd_BUG_ON(index >= num_busses_out(chip) + num_busses_in(chip)))
+		return -EINVAL;
 
 	/* Wait for the handshake (OK even if ASIC is not loaded) */
 	if (wait_handshake(chip))
@@ -536,7 +537,8 @@
 /* Set the gain for a single physical output channel (dB). */
 static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)
 {
-	snd_assert(channel < num_busses_out(chip), return -EINVAL);
+	if (snd_BUG_ON(channel >= num_busses_out(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
@@ -554,8 +556,9 @@
 static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input,
 			    s8 gain)
 {
-	snd_assert(output < num_busses_out(chip) &&
-		   input < num_busses_in(chip), return -EINVAL);
+	if (snd_BUG_ON(output >= num_busses_out(chip) ||
+		    input >= num_busses_in(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
@@ -1065,8 +1068,10 @@
 	int i;
 
 	DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
-	snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL);
-	snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL);
+	if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index)))
+		return -EINVAL;
+	if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED))
+		return -EINVAL;
 
 	for (channel_mask = i = 0; i < pipe->interleave; i++)
 		channel_mask |= 1 << (pipe->index + i);
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
index 3aa37e7..afa2733 100644
--- a/sound/pci/echoaudio/echoaudio_gml.c
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -112,9 +112,11 @@
 		return -EIO;
 
 	/* All audio channels must be closed before changing the digital mode */
-	snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+	if (snd_BUG_ON(chip->pipe_alloc_mask))
+		return -EAGAIN;
 
-	snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+	if (snd_BUG_ON(!(chip->digital_modes & (1 << mode))))
+		return -EINVAL;
 
 	previous_mode = chip->digital_mode;
 	err = dsp_set_digital_mode(chip, mode);
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
index 2757c89..db6c952 100644
--- a/sound/pci/echoaudio/gina20_dsp.c
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -38,7 +38,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Gina20\n"));
-	snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -177,7 +178,8 @@
 /* Set input bus gain (one unit is 0.5dB !) */
 static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
 {
-	snd_assert(input < num_busses_in(chip), return -EINVAL);
+	if (snd_BUG_ON(input >= num_busses_in(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
index 144fc56..2fef37a 100644
--- a/sound/pci/echoaudio/gina24_dsp.c
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -43,7 +43,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Gina24\n"));
-	snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -84,7 +85,8 @@
 	if ((err = init_line_levels(chip)) < 0)
 		return err;
 	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-	snd_assert(err >= 0, return err);
+	if (err < 0)
+		return err;
 	err = set_professional_spdif(chip, TRUE);
 
 	DE_INIT(("init_hw done\n"));
@@ -163,8 +165,9 @@
 {
 	u32 control_reg, clock;
 
-	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
-		   return -EINVAL);
+	if (snd_BUG_ON(rate >= 50000 &&
+		       chip->digital_mode == DIGITAL_MODE_ADAT))
+		return -EINVAL;
 
 	/* Only set the clock for internal mode. */
 	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
index d6ac773..f05e39f 100644
--- a/sound/pci/echoaudio/indigo_dsp.c
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -39,7 +39,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Indigo\n"));
-	snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -143,8 +144,9 @@
 {
 	int index;
 
-	snd_assert(pipe < num_pipes_out(chip) &&
-		   output < num_busses_out(chip), return -EINVAL);
+	if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+		       output >= num_busses_out(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
index 500e150..90730a5 100644
--- a/sound/pci/echoaudio/indigodj_dsp.c
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -39,7 +39,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Indigo DJ\n"));
-	snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -143,8 +144,9 @@
 {
 	int index;
 
-	snd_assert(pipe < num_pipes_out(chip) &&
-		   output < num_busses_out(chip), return -EINVAL);
+	if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+		       output >= num_busses_out(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
index f3ad13d..a7e09ec 100644
--- a/sound/pci/echoaudio/indigoio_dsp.c
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -39,7 +39,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Indigo IO\n"));
-	snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -114,8 +115,9 @@
 {
 	int index;
 
-	snd_assert(pipe < num_pipes_out(chip) &&
-		   output < num_busses_out(chip), return -EINVAL);
+	if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+		       output >= num_busses_out(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
index 990c9a6..ede75c6 100644
--- a/sound/pci/echoaudio/layla20_dsp.c
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -42,7 +42,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Layla20\n"));
-	snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -155,7 +156,8 @@
 
 static int set_sample_rate(struct echoaudio *chip, u32 rate)
 {
-	snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL);
+	if (snd_BUG_ON(rate < 8000 || rate > 50000))
+		return -EINVAL;
 
 	/* Only set the clock for internal mode. Do not return failure,
 	   simply treat it as a non-event. */
@@ -252,7 +254,8 @@
 /* Set input bus gain (one unit is 0.5dB !) */
 static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
 {
-	snd_assert(input < num_busses_in(chip), return -EINVAL);
+	if (snd_BUG_ON(input >= num_busses_in(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
index 97e42e1..d61b5cb 100644
--- a/sound/pci/echoaudio/layla24_dsp.c
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -42,7 +42,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Layla24\n"));
-	snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -73,7 +74,8 @@
 		return err;
 
 	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-	snd_assert(err >= 0, return err);
+	if (err < 0)
+		return err;
 	err = set_professional_spdif(chip, TRUE);
 
 	DE_INIT(("init_hw done\n"));
@@ -158,8 +160,9 @@
 {
 	u32 control_reg, clock, base_rate;
 
-	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
-		   return -EINVAL);
+	if (snd_BUG_ON(rate >= 50000 &&
+		       chip->digital_mode == DIGITAL_MODE_ADAT))
+		return -EINVAL;
 
 	/* Only set the clock for internal mode. */
 	if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
index 891c705..2273866 100644
--- a/sound/pci/echoaudio/mia_dsp.c
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -42,7 +42,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Mia\n"));
-	snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -161,8 +162,9 @@
 static int set_input_clock(struct echoaudio *chip, u16 clock)
 {
 	DE_ACT(("set_input_clock(%d)\n", clock));
-	snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF,
-		   return -EINVAL);
+	if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL &&
+		       clock != ECHO_CLOCK_SPDIF))
+		return -EINVAL;
 
 	chip->input_clock = clock;
 	return set_sample_rate(chip, chip->sample_rate);
@@ -176,8 +178,9 @@
 {
 	int index;
 
-	snd_assert(pipe < num_pipes_out(chip) &&
-		   output < num_busses_out(chip), return -EINVAL);
+	if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+		       output >= num_busses_out(chip)))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index 91f5bff..77bf2a8 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -59,7 +59,8 @@
 Returns how many actually written or < 0 on error */
 static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
 {
-	snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL);
+	if (snd_BUG_ON(bytes <= 0 || bytes >= MIDI_OUT_BUFFER_SIZE))
+		return -EINVAL;
 
 	if (wait_handshake(chip))
 		return -EIO;
@@ -119,7 +120,8 @@
 	/* The count is at index 0, followed by actual data */
 	count = le16_to_cpu(chip->comm_page->midi_input[0]);
 
-	snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0);
+	if (snd_BUG_ON(count >= MIDI_IN_BUFFER_SIZE))
+		return 0;
 
 	/* Get the MIDI data from the comm page */
 	i = 1;
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
index c0b4bf0..eaa619b 100644
--- a/sound/pci/echoaudio/mona_dsp.c
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -43,7 +43,8 @@
 	int err;
 
 	DE_INIT(("init_hw() - Mona\n"));
-	snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV);
+	if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA))
+		return -ENODEV;
 
 	if ((err = init_dsp_comm_page(chip))) {
 		DE_INIT(("init_hw - could not initialize DSP comm page\n"));
@@ -79,7 +80,8 @@
 		return err;
 
 	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-	snd_assert(err >= 0, return err);
+	if (err < 0)
+		return err;
 	err = set_professional_spdif(chip, TRUE);
 
 	DE_INIT(("init_hw done\n"));
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 45088eb..0e649dc 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -145,7 +145,8 @@
 {
 	struct snd_emu10k1 *hw;
 	
-	snd_assert(vp, return);
+	if (snd_BUG_ON(!vp))
+		return;
 	hw = vp->hw;
 	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
 	if (vp->block) {
@@ -325,7 +326,8 @@
 	
 	hw = vp->hw;
 	ch = vp->ch;
-	snd_assert(ch >= 0, return -EINVAL);
+	if (snd_BUG_ON(ch < 0))
+		return -EINVAL;
 	chan = vp->chan;
 
 	emem = (struct snd_emu10k1_memblk *)vp->block;
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index 42bae6f..e10f027 100644
--- a/sound/pci/emu10k1/emu10k1_patch.c
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -46,8 +46,8 @@
 	struct snd_emu10k1 *emu;
 
 	emu = rec->hw;
-	snd_assert(sp != NULL, return -EINVAL);
-	snd_assert(hdr != NULL, return -EINVAL);
+	if (snd_BUG_ON(!sp || !hdr))
+		return -EINVAL;
 
 	if (sp->v.size == 0) {
 		snd_printd("emu: rom font for sample %d\n", sp->v.sample);
@@ -104,7 +104,8 @@
 	size = BLANK_HEAD_SIZE;
 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 		size *= 2;
-	snd_assert(offset + size <= blocksize, return -EINVAL);
+	if (offset + size > blocksize)
+		return -EINVAL;
 	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
 	offset += size;
 
@@ -112,7 +113,8 @@
 	size = loopend;
 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 		size *= 2;
-	snd_assert(offset + size <= blocksize, return -EINVAL);
+	if (offset + size > blocksize)
+		return -EINVAL;
 	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
 		snd_emu10k1_synth_free(emu, sp->block);
 		sp->block = NULL;
@@ -129,12 +131,14 @@
 			int woffset;
 			unsigned short *wblock = (unsigned short*)block;
 			woffset = offset / 2;
-			snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL);
+			if (offset + loopsize * 2 > blocksize)
+				return -EINVAL;
 			for (i = 0; i < loopsize; i++)
 				wblock[woffset + i] = wblock[woffset - i -1];
 			offset += loopsize * 2;
 		} else {
-			snd_assert(offset + loopsize <= blocksize, return -EINVAL);
+			if (offset + loopsize > blocksize)
+				return -EINVAL;
 			for (i = 0; i < loopsize; i++)
 				block[offset + i] = block[offset - i -1];
 			offset += loopsize;
@@ -154,7 +158,8 @@
 
 	/* loopend -> sample end */
 	size = sp->v.size - loopend;
-	snd_assert(size >= 0, return -EINVAL);
+	if (size < 0)
+		return -EINVAL;
 	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 		size *= 2;
 	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
@@ -212,8 +217,8 @@
 	struct snd_emu10k1 *emu;
 
 	emu = rec->hw;
-	snd_assert(sp != NULL, return -EINVAL);
-	snd_assert(hdr != NULL, return -EINVAL);
+	if (snd_BUG_ON(!sp || !hdr))
+		return -EINVAL;
 
 	if (sp->block) {
 		snd_emu10k1_synth_free(emu, sp->block);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 491a4a5..5ff4dbb 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1319,7 +1319,8 @@
 	unsigned long flags;
 	
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
 	midi->substream_input = substream;
@@ -1345,7 +1346,8 @@
 	unsigned long flags;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
 	midi->substream_output = substream;
@@ -1372,7 +1374,8 @@
 	int err = 0;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	snd_emu10k1x_intr_disable(emu, midi->rx_enable);
 	midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
@@ -1394,7 +1397,8 @@
 	int err = 0;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	snd_emu10k1x_intr_disable(emu, midi->tx_enable);
 	midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
@@ -1413,7 +1417,8 @@
 	struct emu10k1x *emu;
 	struct emu10k1x_midi *midi = substream->rmidi->private_data;
 	emu = midi->emu;
-	snd_assert(emu, return);
+	if (snd_BUG_ON(!emu))
+		return;
 
 	if (up)
 		snd_emu10k1x_intr_enable(emu, midi->rx_enable);
@@ -1428,7 +1433,8 @@
 	unsigned long flags;
 
 	emu = midi->emu;
-	snd_assert(emu, return);
+	if (snd_BUG_ON(!emu))
+		return;
 
 	if (up) {
 		int max = 4;
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 71dc4c8..7dba08f 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -487,7 +487,8 @@
 				 u32 op, u32 r, u32 a, u32 x, u32 y)
 {
 	u_int32_t *code;
-	snd_assert(*ptr < 512, return);
+	if (snd_BUG_ON(*ptr >= 512))
+		return;
 	code = (u_int32_t __force *)icode->code + (*ptr) * 2;
 	set_bit(*ptr, icode->code_valid);
 	code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
@@ -503,7 +504,8 @@
 					u32 op, u32 r, u32 a, u32 x, u32 y)
 {
 	u_int32_t *code;
-	snd_assert(*ptr < 1024, return);
+	if (snd_BUG_ON(*ptr >= 1024))
+		return;
 	code = (u_int32_t __force *)icode->code + (*ptr) * 2;
 	set_bit(*ptr, icode->code_valid);
 	code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index c4d76d1..8578c70 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -157,7 +157,8 @@
 	unsigned long flags;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
 	midi->substream_input = substream;
@@ -183,7 +184,8 @@
 	unsigned long flags;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
 	midi->substream_output = substream;
@@ -210,7 +212,8 @@
 	int err = 0;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	snd_emu10k1_intr_disable(emu, midi->rx_enable);
 	midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
@@ -232,7 +235,8 @@
 	int err = 0;
 
 	emu = midi->emu;
-	snd_assert(emu, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 	spin_lock_irqsave(&midi->open_lock, flags);
 	snd_emu10k1_intr_disable(emu, midi->tx_enable);
 	midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
@@ -251,7 +255,8 @@
 	struct snd_emu10k1 *emu;
 	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
 	emu = midi->emu;
-	snd_assert(emu, return);
+	if (snd_BUG_ON(!emu))
+		return;
 
 	if (up)
 		snd_emu10k1_intr_enable(emu, midi->rx_enable);
@@ -266,7 +271,8 @@
 	unsigned long flags;
 
 	emu = midi->emu;
-	snd_assert(emu, return);
+	if (snd_BUG_ON(!emu))
+		return;
 
 	if (up) {
 		int max = 4;
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 7d379f5..6a47672 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -107,7 +107,8 @@
 
 	list_for_each (pos, &emu->mapped_link_head) {
 		struct snd_emu10k1_memblk *blk = get_emu10k1_memblk(pos, mapped_link);
-		snd_assert(blk->mapped_page >= 0, continue);
+		if (blk->mapped_page < 0)
+			continue;
 		size = blk->mapped_page - page;
 		if (size == npages) {
 			*nextp = pos;
@@ -295,15 +296,18 @@
 snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 	struct snd_util_memhdr *hdr;
 	struct snd_emu10k1_memblk *blk;
 	int page, err, idx;
 
-	snd_assert(emu, return NULL);
-	snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL);
+	if (snd_BUG_ON(!emu))
+		return NULL;
+	if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+		       runtime->dma_bytes >= MAXPAGES * EMUPAGESIZE))
+		return NULL;
 	hdr = emu->memhdr;
-	snd_assert(hdr, return NULL);
+	if (snd_BUG_ON(!hdr))
+		return NULL;
 
 	mutex_lock(&hdr->block_mutex);
 	blk = search_empty(emu, runtime->dma_bytes);
@@ -316,16 +320,9 @@
 	 */
 	idx = 0;
 	for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
+		unsigned long ofs = idx << PAGE_SHIFT;
 		dma_addr_t addr;
-#ifdef CONFIG_SND_DEBUG
-		if (idx >= sgbuf->pages) {
-			printk(KERN_ERR "emu: pages overflow! (%d-%d) for %d\n",
-			       blk->first_page, blk->last_page, sgbuf->pages);
-			mutex_unlock(&hdr->block_mutex);
-			return NULL;
-		}
-#endif
-		addr = sgbuf->table[idx].addr;
+		addr = snd_pcm_sgbuf_get_addr(substream, ofs);
 		if (! is_valid_page(emu, addr)) {
 			printk(KERN_ERR "emu: failure page = %d\n", idx);
 			mutex_unlock(&hdr->block_mutex);
@@ -353,7 +350,8 @@
  */
 int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
 {
-	snd_assert(emu && blk, return -EINVAL);
+	if (snd_BUG_ON(!emu || !blk))
+		return -EINVAL;
 	return snd_emu10k1_synth_free(emu, blk);
 }
 
@@ -498,7 +496,8 @@
 static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
 {
 	char *ptr;
-	snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL);
+	if (snd_BUG_ON(page < 0 || page >= emu->max_cache_pages))
+		return NULL;
 	ptr = emu->page_ptr_table[page];
 	if (! ptr) {
 		printk(KERN_ERR "emu10k1: access to NULL ptr: page = %d\n", page);
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 958cb2a..d7300a1 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -111,8 +111,10 @@
 	unsigned long flags;
 	int result;
 
-	snd_assert(rvoice != NULL, return -EINVAL);
-	snd_assert(number, return -EINVAL);
+	if (snd_BUG_ON(!rvoice))
+		return -EINVAL;
+	if (snd_BUG_ON(!number))
+		return -EINVAL;
 
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	for (;;) {
@@ -145,7 +147,8 @@
 {
 	unsigned long flags;
 
-	snd_assert(pvoice != NULL, return -EINVAL);
+	if (snd_BUG_ON(!pvoice))
+		return -EINVAL;
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	pvoice->interrupt = NULL;
 	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 84fac1f..4cd9a1f 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -860,7 +860,8 @@
 	struct es1938 *chip = snd_pcm_substream_chip(substream);
 	pos <<= chip->dma1_shift;
 	count <<= chip->dma1_shift;
-	snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+	if (snd_BUG_ON(pos + count > chip->dma1_size))
+		return -EINVAL;
 	if (pos + count < chip->dma1_size) {
 		if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
 			return -EFAULT;
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 1bf298d..20ee759 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -692,7 +692,8 @@
 /* no spinlock */
 static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
 {
-	snd_assert(channel < NR_APUS, return);
+	if (snd_BUG_ON(channel >= NR_APUS))
+		return;
 #ifdef CONFIG_PM
 	chip->apu_map[channel][reg] = data;
 #endif
@@ -711,7 +712,8 @@
 
 static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
 {
-	snd_assert(channel < NR_APUS, return 0);
+	if (snd_BUG_ON(channel >= NR_APUS))
+		return 0;
 	reg |= (channel << 4);
 	apu_index_set(chip, reg);
 	return __maestro_read(chip, IDR0_DATA_PORT);
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ab0c726..1980c6d 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -5,6 +5,7 @@
 snd-hda-intel-y += hda_codec.o
 snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
 snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
+snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
 snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o
@@ -14,5 +15,6 @@
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
+snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
 
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
new file mode 100644
index 0000000..9b77b3e
--- /dev/null
+++ b/sound/pci/hda/hda_beep.c
@@ -0,0 +1,134 @@
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/pci.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include "hda_beep.h"
+
+enum {
+	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */
+	DIGBEEP_HZ_MIN = 93750,		/* 93.750 Hz */
+	DIGBEEP_HZ_MAX = 12000000,	/* 12 KHz */
+};
+
+static void snd_hda_generate_beep(struct work_struct *work)
+{
+	struct hda_beep *beep =
+		container_of(work, struct hda_beep, beep_work);
+	struct hda_codec *codec = beep->codec;
+
+	/* generate tone */
+	snd_hda_codec_write_cache(codec, beep->nid, 0,
+			AC_VERB_SET_BEEP_CONTROL, beep->tone);
+}
+
+static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
+				unsigned int code, int hz)
+{
+	struct hda_beep *beep = input_get_drvdata(dev);
+
+	switch (code) {
+	case SND_BELL:
+		if (hz)
+			hz = 1000;
+	case SND_TONE:
+		hz *= 1000; /* fixed point */
+		hz = hz - DIGBEEP_HZ_MIN;
+		if (hz < 0)
+			hz = 0; /* turn off PC beep*/
+		else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
+			hz = 0xff;
+		else {
+			hz /= DIGBEEP_HZ_STEP;
+			hz++;
+		}
+		break;
+	default:
+		return -1;
+	}
+	beep->tone = hz;
+
+	/* schedule beep event */
+	schedule_work(&beep->beep_work);
+	return 0;
+}
+
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+	struct input_dev *input_dev;
+	struct hda_beep *beep;
+	int err;
+
+	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+	if (beep == NULL)
+		return -ENOMEM;
+	snprintf(beep->phys, sizeof(beep->phys),
+		"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
+	input_dev = input_allocate_device();
+
+	/* setup digital beep device */
+	input_dev->name = "HDA Digital PCBeep";
+	input_dev->phys = beep->phys;
+	input_dev->id.bustype = BUS_PCI;
+
+	input_dev->id.vendor = codec->vendor_id >> 16;
+	input_dev->id.product = codec->vendor_id & 0xffff;
+	input_dev->id.version = 0x01;
+
+	input_dev->evbit[0] = BIT_MASK(EV_SND);
+	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+	input_dev->event = snd_hda_beep_event;
+	input_dev->dev.parent = &codec->bus->pci->dev;
+	input_set_drvdata(input_dev, beep);
+
+	err = input_register_device(input_dev);
+	if (err < 0) {
+		input_free_device(input_dev);
+		kfree(beep);
+		return err;
+	}
+
+	/* enable linear scale */
+	snd_hda_codec_write(codec, nid, 0,
+		AC_VERB_SET_DIGI_CONVERT_2, 0x01);
+
+	beep->nid = nid;
+	beep->dev = input_dev;
+	beep->codec = codec;
+	codec->beep = beep;
+
+	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+	return 0;
+}
+
+void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+	struct hda_beep *beep = codec->beep;
+	if (beep) {
+		cancel_work_sync(&beep->beep_work);
+		flush_scheduled_work();
+
+		input_unregister_device(beep->dev);
+		kfree(beep);
+	}
+}
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
new file mode 100644
index 0000000..de4036e
--- /dev/null
+++ b/sound/pci/hda/hda_beep.h
@@ -0,0 +1,44 @@
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matthew Ranostay <mranostay@embeddedalley.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __SOUND_HDA_BEEP_H
+#define __SOUND_HDA_BEEP_H
+
+#include "hda_codec.h"
+
+/* beep information */
+struct hda_beep {
+	struct input_dev *dev;
+	struct hda_codec *codec;
+	char phys[32];
+	int tone;
+	int nid;
+	struct work_struct beep_work; /* scheduled task for beep event */
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
+void snd_hda_detach_beep_device(struct hda_codec *codec);
+#else
+#define snd_hda_attach_beep_device(...)
+#define snd_hda_detach_beep_device(...)
+#endif
+#endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index d2e1093..6447754 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -94,6 +94,9 @@
 #ifdef CONFIG_SND_HDA_CODEC_VIA
 	snd_hda_preset_via,
 #endif
+#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
+	snd_hda_preset_nvhdmi,
+#endif
 	NULL
 };
 
@@ -211,7 +214,8 @@
 	unsigned int shift, num_elems, mask;
 	hda_nid_t prev_nid;
 
-	snd_assert(conn_list && max_conns > 0, return -EINVAL);
+	if (snd_BUG_ON(!conn_list || max_conns <= 0))
+		return -EINVAL;
 
 	parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
 	if (parm & AC_CLIST_LONG) {
@@ -313,7 +317,7 @@
 }
 
 /*
- * process queueud unsolicited events
+ * process queued unsolicited events
  */
 static void process_unsol_events(struct work_struct *work)
 {
@@ -407,8 +411,10 @@
 		.dev_free = snd_hda_bus_dev_free,
 	};
 
-	snd_assert(temp, return -EINVAL);
-	snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL);
+	if (snd_BUG_ON(!temp))
+		return -EINVAL;
+	if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response))
+		return -EINVAL;
 
 	if (busp)
 		*busp = NULL;
@@ -585,11 +591,13 @@
 				struct hda_codec **codecp)
 {
 	struct hda_codec *codec;
-	char component[13];
+	char component[31];
 	int err;
 
-	snd_assert(bus, return -EINVAL);
-	snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);
+	if (snd_BUG_ON(!bus))
+		return -EINVAL;
+	if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+		return -EINVAL;
 
 	if (bus->caddr_tbl[codec_addr]) {
 		snd_printk(KERN_ERR "hda_codec: "
@@ -688,7 +696,7 @@
 	snd_hda_create_hwdep(codec);
 #endif
 
-	sprintf(component, "HDA:%08x", codec->vendor_id);
+	sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
 	snd_component_add(codec->bus->card, component);
 
 	if (codecp)
@@ -956,15 +964,6 @@
 }
 #endif /* SND_HDA_NEEDS_RESUME */
 
-/*
- * AMP control callbacks
- */
-/* retrieve parameters from private_value */
-#define get_amp_nid(kc)		((kc)->private_value & 0xffff)
-#define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
-#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
-
 /* volume */
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_info *uinfo)
@@ -1430,6 +1429,29 @@
 	return sbits;
 }
 
+/* set digital convert verbs both for the given NID and its slaves */
+static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
+			int verb, int val)
+{
+	hda_nid_t *d;
+
+	snd_hda_codec_write(codec, nid, 0, verb, val);
+	d = codec->slave_dig_outs;
+	if (!d)
+		return;
+	for (; *d; d++)
+		snd_hda_codec_write(codec, *d, 0, verb, val);
+}
+
+static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
+				       int dig1, int dig2)
+{
+	if (dig1 != -1)
+		set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
+	if (dig2 != -1)
+		set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
+}
+
 static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
 				     struct snd_ctl_elem_value *ucontrol)
 {
@@ -1448,14 +1470,8 @@
 	change = codec->spdif_ctls != val;
 	codec->spdif_ctls = val;
 
-	if (change) {
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_DIGI_CONVERT_1,
-					  val & 0xff);
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_DIGI_CONVERT_2,
-					  val >> 8);
-	}
+	if (change)
+		set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
 
 	mutex_unlock(&codec->spdif_mutex);
 	return change;
@@ -1487,9 +1503,7 @@
 	change = codec->spdif_ctls != val;
 	if (change) {
 		codec->spdif_ctls = val;
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_DIGI_CONVERT_1,
-					  val & 0xff);
+		set_dig_out_convert(codec, nid, val & 0xff, -1);
 		/* unmute amp switch (if any) */
 		if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
 		    (val & AC_DIG1_ENABLE))
@@ -2236,11 +2250,13 @@
 	if (info->ops.close == NULL)
 		info->ops.close = hda_pcm_default_open_close;
 	if (info->ops.prepare == NULL) {
-		snd_assert(info->nid, return -EINVAL);
+		if (snd_BUG_ON(!info->nid))
+			return -EINVAL;
 		info->ops.prepare = hda_pcm_default_prepare;
 	}
 	if (info->ops.cleanup == NULL) {
-		snd_assert(info->nid, return -EINVAL);
+		if (snd_BUG_ON(!info->nid))
+			return -EINVAL;
 		info->ops.cleanup = hda_pcm_default_cleanup;
 	}
 	return 0;
@@ -2583,14 +2599,31 @@
 				 unsigned int stream_tag, unsigned int format)
 {
 	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-	if (codec->spdif_ctls & AC_DIG1_ENABLE)
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+		set_dig_out_convert(codec, nid, 
+				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+				    -1);
 	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+	if (codec->slave_dig_outs) {
+		hda_nid_t *d;
+		for (d = codec->slave_dig_outs; *d; d++)
+			snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
+						   format);
+	}
 	/* turn on again (if needed) */
-	if (codec->spdif_ctls & AC_DIG1_ENABLE)
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    codec->spdif_ctls & 0xff);
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+		set_dig_out_convert(codec, nid,
+				    codec->spdif_ctls & 0xff, -1);
+}
+
+static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+	snd_hda_codec_cleanup_stream(codec, nid);
+	if (codec->slave_dig_outs) {
+		hda_nid_t *d;
+		for (d = codec->slave_dig_outs; *d; d++)
+			snd_hda_codec_cleanup_stream(codec, *d);
+	}
 }
 
 /*
@@ -2602,7 +2635,7 @@
 	mutex_lock(&codec->spdif_mutex);
 	if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
 		/* already opened as analog dup; reset it once */
-		snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+		cleanup_dig_out_stream(codec, mout->dig_out_nid);
 	mout->dig_out_used = HDA_DIG_EXCLUSIVE;
 	mutex_unlock(&codec->spdif_mutex);
 	return 0;
@@ -2697,7 +2730,7 @@
 					     stream_tag, format);
 		} else {
 			mout->dig_out_used = 0;
-			snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+			cleanup_dig_out_stream(codec, mout->dig_out_nid);
 		}
 	}
 	mutex_unlock(&codec->spdif_mutex);
@@ -2748,7 +2781,7 @@
 						     mout->extra_out_nid[i]);
 	mutex_lock(&codec->spdif_mutex);
 	if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
-		snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+		cleanup_dig_out_stream(codec, mout->dig_out_nid);
 		mout->dig_out_used = 0;
 	}
 	mutex_unlock(&codec->spdif_mutex);
@@ -2756,7 +2789,7 @@
 }
 
 /*
- * Helper for automatic ping configuration
+ * Helper for automatic pin configuration
  */
 
 static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index efc6828..60468f5 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -90,6 +90,14 @@
 #define AC_VERB_GET_CONFIG_DEFAULT		0x0f1c
 /* f20: AFG/MFG */
 #define AC_VERB_GET_SUBSYSTEM_ID		0x0f20
+#define AC_VERB_GET_CVT_CHAN_COUNT		0x0f2d
+#define AC_VERB_GET_HDMI_DIP_SIZE		0x0f2e
+#define AC_VERB_GET_HDMI_ELDD			0x0f2f
+#define AC_VERB_GET_HDMI_DIP_INDEX		0x0f30
+#define AC_VERB_GET_HDMI_DIP_DATA		0x0f31
+#define AC_VERB_GET_HDMI_DIP_XMIT		0x0f32
+#define AC_VERB_GET_HDMI_CP_CTRL		0x0f33
+#define AC_VERB_GET_HDMI_CHAN_SLOT		0x0f34
 
 /*
  * SET verbs
@@ -121,7 +129,14 @@
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1	0x71d
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2	0x71e
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3	0x71f
+#define AC_VERB_SET_EAPD				0x788
 #define AC_VERB_SET_CODEC_RESET			0x7ff
+#define AC_VERB_SET_CVT_CHAN_COUNT		0x72d
+#define AC_VERB_SET_HDMI_DIP_INDEX		0x730
+#define AC_VERB_SET_HDMI_DIP_DATA		0x731
+#define AC_VERB_SET_HDMI_DIP_XMIT		0x732
+#define AC_VERB_SET_HDMI_CP_CTRL		0x733
+#define AC_VERB_SET_HDMI_CHAN_SLOT		0x734
 
 /*
  * Parameter IDs
@@ -143,6 +158,7 @@
 #define AC_PAR_GPIO_CAP			0x11
 #define AC_PAR_AMP_OUT_CAP		0x12
 #define AC_PAR_VOL_KNB_CAP		0x13
+#define AC_PAR_HDMI_LPCM_CAP		0x20
 
 /*
  * AC_VERB_PARAMETERS results (32bit)
@@ -171,6 +187,8 @@
 #define AC_WCAP_DIGITAL			(1<<9)	/* digital I/O */
 #define AC_WCAP_POWER			(1<<10)	/* power control */
 #define AC_WCAP_LR_SWAP			(1<<11)	/* L/R swap */
+#define AC_WCAP_CP_CAPS			(1<<12) /* content protection */
+#define AC_WCAP_CHAN_CNT_EXT		(7<<13)	/* channel count ext */
 #define AC_WCAP_DELAY			(0xf<<16)
 #define AC_WCAP_DELAY_SHIFT		16
 #define AC_WCAP_TYPE			(0xf<<20)
@@ -206,9 +224,20 @@
 /* Input converter SDI select */
 #define AC_SDI_SELECT			(0xf<<0)
 
-/* Unsolicited response */
+/* Unsolicited response control */
 #define AC_UNSOL_TAG			(0x3f<<0)
 #define AC_UNSOL_ENABLED		(1<<7)
+#define AC_USRSP_EN			AC_UNSOL_ENABLED
+
+/* Unsolicited responses */
+#define AC_UNSOL_RES_TAG		(0x3f<<26)
+#define AC_UNSOL_RES_TAG_SHIFT		26
+#define AC_UNSOL_RES_SUBTAG		(0x1f<<21)
+#define AC_UNSOL_RES_SUBTAG_SHIFT	21
+#define AC_UNSOL_RES_ELDV		(1<<1)	/* ELD Data valid (for HDMI) */
+#define AC_UNSOL_RES_PD			(1<<0)	/* pinsense detect */
+#define AC_UNSOL_RES_CP_STATE		(1<<1)	/* content protection */
+#define AC_UNSOL_RES_CP_READY		(1<<0)	/* content protection */
 
 /* Pin widget capabilies */
 #define AC_PINCAP_IMP_SENSE		(1<<0)	/* impedance sense capable */
@@ -222,6 +251,10 @@
  *       but is marked reserved in the Intel HDA specification.
  */
 #define AC_PINCAP_LR_SWAP		(1<<7)	/* L/R swap */
+/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
+ *       in HD-audio specification
+ */
+#define AC_PINCAP_HDMI			(1<<7)	/* HDMI pin */
 #define AC_PINCAP_VREF			(0x37<<8)
 #define AC_PINCAP_VREF_SHIFT		8
 #define AC_PINCAP_EAPD			(1<<16)	/* EAPD capable */
@@ -272,6 +305,22 @@
 #define AC_KNBCAP_NUM_STEPS		(0x7f<<0)
 #define AC_KNBCAP_DELTA			(1<<7)
 
+/* HDMI LPCM capabilities */
+#define AC_LPCMCAP_48K_CP_CHNS		(0x0f<<0) /* max channels w/ CP-on */	
+#define AC_LPCMCAP_48K_NO_CHNS		(0x0f<<4) /* max channels w/o CP-on */
+#define AC_LPCMCAP_48K_20BIT		(1<<8)	/* 20b bitrate supported */
+#define AC_LPCMCAP_48K_24BIT		(1<<9)	/* 24b bitrate supported */
+#define AC_LPCMCAP_96K_CP_CHNS		(0x0f<<10) /* max channels w/ CP-on */	
+#define AC_LPCMCAP_96K_NO_CHNS		(0x0f<<14) /* max channels w/o CP-on */
+#define AC_LPCMCAP_96K_20BIT		(1<<18)	/* 20b bitrate supported */
+#define AC_LPCMCAP_96K_24BIT		(1<<19)	/* 24b bitrate supported */
+#define AC_LPCMCAP_192K_CP_CHNS		(0x0f<<20) /* max channels w/ CP-on */	
+#define AC_LPCMCAP_192K_NO_CHNS		(0x0f<<24) /* max channels w/o CP-on */
+#define AC_LPCMCAP_192K_20BIT		(1<<28)	/* 20b bitrate supported */
+#define AC_LPCMCAP_192K_24BIT		(1<<29)	/* 24b bitrate supported */
+#define AC_LPCMCAP_44K			(1<<30)	/* 44.1kHz support */
+#define AC_LPCMCAP_44K_MS		(1<<31)	/* 44.1kHz-multiplies support */
+
 /*
  * Control Parameters
  */
@@ -317,18 +366,44 @@
 #define AC_PINCTL_OUT_EN		(1<<6)
 #define AC_PINCTL_HP_EN			(1<<7)
 
-/* Unsolicited response - 8bit */
-#define AC_USRSP_EN			(1<<7)
-
 /* Pin sense - 32bit */
 #define AC_PINSENSE_IMPEDANCE_MASK	(0x7fffffff)
 #define AC_PINSENSE_PRESENCE		(1<<31)
+#define AC_PINSENSE_ELDV		(1<<30)	/* ELD valid (HDMI) */
 
 /* EAPD/BTL enable - 32bit */
 #define AC_EAPDBTL_BALANCED		(1<<0)
 #define AC_EAPDBTL_EAPD			(1<<1)
 #define AC_EAPDBTL_LR_SWAP		(1<<2)
 
+/* HDMI ELD data */
+#define AC_ELDD_ELD_VALID		(1<<31)
+#define AC_ELDD_ELD_DATA		0xff
+
+/* HDMI DIP size */
+#define AC_DIPSIZE_ELD_BUF		(1<<3) /* ELD buf size of packet size */
+#define AC_DIPSIZE_PACK_IDX		(0x07<<0) /* packet index */
+
+/* HDMI DIP index */
+#define AC_DIPIDX_PACK_IDX		(0x07<<5) /* packet idnex */
+#define AC_DIPIDX_BYTE_IDX		(0x1f<<0) /* byte index */
+
+/* HDMI DIP xmit (transmit) control */
+#define AC_DIPXMIT_MASK			(0x3<<6)
+#define AC_DIPXMIT_DISABLE		(0x0<<6) /* disable xmit */
+#define AC_DIPXMIT_ONCE			(0x2<<6) /* xmit once then disable */
+#define AC_DIPXMIT_BEST			(0x3<<6) /* best effort */
+
+/* HDMI content protection (CP) control */
+#define AC_CPCTRL_CES			(1<<9) /* current encryption state */
+#define AC_CPCTRL_READY			(1<<8) /* ready bit */
+#define AC_CPCTRL_SUBTAG		(0x1f<<3) /* subtag for unsol-resp */
+#define AC_CPCTRL_STATE			(3<<0) /* current CP request state */
+
+/* Converter channel <-> HDMI slot mapping */
+#define AC_CVTMAP_HDMI_SLOT		(0xf<<0) /* HDMI slot number */
+#define AC_CVTMAP_CHAN			(0xf<<4) /* converter channel number */
+
 /* configuration default - 32bit */
 #define AC_DEFCFG_SEQUENCE		(0xf<<0)
 #define AC_DEFCFG_DEF_ASSOC		(0xf<<4)
@@ -449,6 +524,7 @@
  */
 
 struct hda_bus;
+struct hda_beep;
 struct hda_codec;
 struct hda_pcm;
 struct hda_pcm_stream;
@@ -634,6 +710,9 @@
 	/* codec specific info */
 	void *spec;
 
+	/* beep device */
+	struct hda_beep *beep;
+
 	/* widget capabilities cache */
 	unsigned int num_nodes;
 	hda_nid_t start_nid;
@@ -646,9 +725,15 @@
 	unsigned int spdif_status;	/* IEC958 status bits */
 	unsigned short spdif_ctls;	/* SPDIF control bits */
 	unsigned int spdif_in_enable;	/* SPDIF input enable? */
+	hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
 
 	struct snd_hwdep *hwdep;	/* assigned hwdep device */
 
+	/* misc flags */
+	unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
+					     * status change
+					     * (e.g. Realtek codecs)
+					     */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	unsigned int power_on :1;	/* current (global) power-state */
 	unsigned int power_transition :1; /* power-state in transition */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 59e4389..0ca3089 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -174,7 +174,8 @@
 	int i, nodes, err;
 	hda_nid_t nid;
 
-	snd_assert(spec, return -EINVAL);
+	if (snd_BUG_ON(!spec))
+		return -EINVAL;
 
 	spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
 	spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1c53e33..9f316c1 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -222,9 +222,9 @@
 #define RIRB_INT_OVERRUN	0x04
 #define RIRB_INT_MASK		0x05
 
-/* STATESTS int mask: SD2,SD1,SD0 */
-#define AZX_MAX_CODECS		3
-#define STATESTS_INT_MASK	0x07
+/* STATESTS int mask: S3,SD2,SD1,SD0 */
+#define AZX_MAX_CODECS		4
+#define STATESTS_INT_MASK	0x0f
 
 /* SD_CTL bits */
 #define SD_CTL_STREAM_RESET	0x01	/* stream reset bit */
@@ -286,6 +286,11 @@
 #define INTEL_SCH_HDA_DEVC      0x78
 #define INTEL_SCH_HDA_DEVC_NOSNOOP       (0x1<<11)
 
+/* Define IN stream 0 FIFO size offset in VIA controller */
+#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET	0x90
+/* Define VIA HD Audio Device ID*/
+#define VIA_HDAC_DEVICE_ID		0x3288
+
 
 /*
  */
@@ -317,6 +322,12 @@
 	unsigned int running :1;
 	unsigned int irq_pending :1;
 	unsigned int irq_ignore :1;
+	/*
+	 * For VIA:
+	 *  A flag to ensure DMA position is 0
+	 *  when link position is not greater than FIFO size
+	 */
+	unsigned int insufficient :1;
 };
 
 /* CORB/RIRB */
@@ -379,6 +390,7 @@
 	unsigned int polling_mode :1;
 	unsigned int msi :1;
 	unsigned int irq_pending_warned :1;
+	unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
 
 	/* for debugging */
 	unsigned int last_cmd;	/* last issued command (to sync) */
@@ -398,6 +410,7 @@
 	AZX_DRIVER_ULI,
 	AZX_DRIVER_NVIDIA,
 	AZX_DRIVER_TERA,
+	AZX_NUM_DRIVERS, /* keep this as last entry */
 };
 
 static char *driver_short_names[] __devinitdata = {
@@ -818,6 +831,11 @@
 /* start a stream */
 static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
 {
+	/*
+	 * Before stream start, initialize parameter
+	 */
+	azx_dev->insufficient = 1;
+
 	/* enable SIE */
 	azx_writeb(chip, INTCTL,
 		   azx_readb(chip, INTCTL) | (1 << azx_dev->index));
@@ -998,7 +1016,6 @@
 		      struct azx_dev *azx_dev, u32 **bdlp,
 		      int ofs, int size, int with_ioc)
 {
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 	u32 *bdl = *bdlp;
 
 	while (size > 0) {
@@ -1008,14 +1025,12 @@
 		if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
 			return -EINVAL;
 
-		addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
+		addr = snd_pcm_sgbuf_get_addr(substream, ofs);
 		/* program the address field of the BDL entry */
 		bdl[0] = cpu_to_le32((u32)addr);
 		bdl[1] = cpu_to_le32(upper_32_bits(addr));
 		/* program the size field of the BDL entry */
-		chunk = PAGE_SIZE - (ofs % PAGE_SIZE);
-		if (size < chunk)
-			chunk = size;
+		chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
 		bdl[2] = cpu_to_le32(chunk);
 		/* program the IOC to enable interrupt
 		 * only when the whole fragment is processed
@@ -1151,7 +1166,8 @@
 
 	/* enable the position buffer */
 	if (chip->position_fix == POS_FIX_POSBUF ||
-	    chip->position_fix == POS_FIX_AUTO) {
+	    chip->position_fix == POS_FIX_AUTO ||
+	    chip->via_dmapos_patch) {
 		if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
 			azx_writel(chip, DPLBASE,
 				(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@@ -1169,23 +1185,26 @@
  * Codec initialization
  */
 
-static unsigned int azx_max_codecs[] __devinitdata = {
-	[AZX_DRIVER_ICH] = 4,		/* Some ICH9 boards use SD3 */
-	[AZX_DRIVER_SCH] = 3,
-	[AZX_DRIVER_ATI] = 4,
-	[AZX_DRIVER_ATIHDMI] = 4,
-	[AZX_DRIVER_VIA] = 3,		/* FIXME: correct? */
-	[AZX_DRIVER_SIS] = 3,		/* FIXME: correct? */
-	[AZX_DRIVER_ULI] = 3,		/* FIXME: correct? */
-	[AZX_DRIVER_NVIDIA] = 3,	/* FIXME: correct? */
+/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
+static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
 	[AZX_DRIVER_TERA] = 1,
 };
 
+/* number of slots to probe as default
+ * this can be different from azx_max_codecs[] -- e.g. some boards
+ * report wrongly the non-existing 4th slot availability
+ */
+static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
+	[AZX_DRIVER_ICH] = 3,
+	[AZX_DRIVER_ATI] = 3,
+};
+
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
 				      unsigned int codec_probe_mask)
 {
 	struct hda_bus_template bus_temp;
 	int c, codecs, audio_codecs, err;
+	int def_slots, max_slots;
 
 	memset(&bus_temp, 0, sizeof(bus_temp));
 	bus_temp.private_data = chip;
@@ -1201,8 +1220,17 @@
 	if (err < 0)
 		return err;
 
+	if (chip->driver_type == AZX_DRIVER_NVIDIA)
+		chip->bus->needs_damn_long_delay = 1;
+
 	codecs = audio_codecs = 0;
-	for (c = 0; c < AZX_MAX_CODECS; c++) {
+	max_slots = azx_max_codecs[chip->driver_type];
+	if (!max_slots)
+		max_slots = AZX_MAX_CODECS;
+	def_slots = azx_default_codecs[chip->driver_type];
+	if (!def_slots)
+		def_slots = max_slots;
+	for (c = 0; c < def_slots; c++) {
 		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
 			struct hda_codec *codec;
 			err = snd_hda_codec_new(chip->bus, c, &codec);
@@ -1215,7 +1243,7 @@
 	}
 	if (!audio_codecs) {
 		/* probe additional slots if no codec is found */
-		for (; c < azx_max_codecs[chip->driver_type]; c++) {
+		for (; c < max_slots; c++) {
 			if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
 				err = snd_hda_codec_new(chip->bus, c, NULL);
 				if (err < 0)
@@ -1507,13 +1535,71 @@
 	return 0;
 }
 
+/* get the current DMA position with correction on VIA chips */
+static unsigned int azx_via_get_position(struct azx *chip,
+					 struct azx_dev *azx_dev)
+{
+	unsigned int link_pos, mini_pos, bound_pos;
+	unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
+	unsigned int fifo_size;
+
+	link_pos = azx_sd_readl(azx_dev, SD_LPIB);
+	if (azx_dev->index >= 4) {
+		/* Playback, no problem using link position */
+		return link_pos;
+	}
+
+	/* Capture */
+	/* For new chipset,
+	 * use mod to get the DMA position just like old chipset
+	 */
+	mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
+	mod_dma_pos %= azx_dev->period_bytes;
+
+	/* azx_dev->fifo_size can't get FIFO size of in stream.
+	 * Get from base address + offset.
+	 */
+	fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
+
+	if (azx_dev->insufficient) {
+		/* Link position never gather than FIFO size */
+		if (link_pos <= fifo_size)
+			return 0;
+
+		azx_dev->insufficient = 0;
+	}
+
+	if (link_pos <= fifo_size)
+		mini_pos = azx_dev->bufsize + link_pos - fifo_size;
+	else
+		mini_pos = link_pos - fifo_size;
+
+	/* Find nearest previous boudary */
+	mod_mini_pos = mini_pos % azx_dev->period_bytes;
+	mod_link_pos = link_pos % azx_dev->period_bytes;
+	if (mod_link_pos >= fifo_size)
+		bound_pos = link_pos - mod_link_pos;
+	else if (mod_dma_pos >= mod_mini_pos)
+		bound_pos = mini_pos - mod_mini_pos;
+	else {
+		bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
+		if (bound_pos >= azx_dev->bufsize)
+			bound_pos = 0;
+	}
+
+	/* Calculate real DMA position we want */
+	return bound_pos + mod_dma_pos;
+}
+
 static unsigned int azx_get_position(struct azx *chip,
 				     struct azx_dev *azx_dev)
 {
 	unsigned int pos;
 
-	if (chip->position_fix == POS_FIX_POSBUF ||
-	    chip->position_fix == POS_FIX_AUTO) {
+	if (chip->via_dmapos_patch)
+		pos = azx_via_get_position(chip, azx_dev);
+	else if (chip->position_fix == POS_FIX_POSBUF ||
+		 chip->position_fix == POS_FIX_AUTO) {
 		/* use the position buffer */
 		pos = le32_to_cpu(*azx_dev->posbuf);
 	} else {
@@ -1559,6 +1645,8 @@
 			chip->position_fix = POS_FIX_POSBUF;
 	}
 
+	if (!bdl_pos_adj[chip->dev_index])
+		return 1; /* no delayed ack */
 	if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
 		return 0; /* NG - it's below the period boundary */
 	return 1; /* OK, it's fine */
@@ -1646,7 +1734,8 @@
 	if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
 		return 0;
 
-	snd_assert(cpcm->name, return -EINVAL);
+	if (snd_BUG_ON(!cpcm->name))
+		return -EINVAL;
 
 	err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
 			  cpcm->stream[0].substreams,
@@ -1670,7 +1759,7 @@
 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
 					      snd_dma_pci_data(chip->pci),
-					      1024 * 64, 1024 * 1024);
+					      1024 * 64, 32 * 1024 * 1024);
 	chip->pcm[cpcm->device] = pcm;
 	return 0;
 }
@@ -1946,6 +2035,15 @@
 {
 	const struct snd_pci_quirk *q;
 
+	/* Check VIA HD Audio Controller exist */
+	if (chip->pci->vendor == PCI_VENDOR_ID_VIA &&
+	    chip->pci->device == VIA_HDAC_DEVICE_ID) {
+		chip->via_dmapos_patch = 1;
+		/* Use link position directly, avoid any transfer problem. */
+		return POS_FIX_LPIB;
+	}
+	chip->via_dmapos_patch = 0;
+
 	if (fix == POS_FIX_AUTO) {
 		q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
 		if (q) {
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5c9e578..7957fef 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -368,12 +368,15 @@
 #define AMP_OUT_UNMUTE	0xb000
 #define AMP_OUT_ZERO	0xb000
 /* pinctl values */
-#define PIN_IN		0x20
-#define PIN_VREF80	0x24
-#define PIN_VREF50	0x21
-#define PIN_OUT		0x40
-#define PIN_HP		0xc0
-#define PIN_HP_AMP	0x80
+#define PIN_IN			(AC_PINCTL_IN_EN)
+#define PIN_VREFHIZ	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREF50		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
+#define PIN_VREFGRD	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREF80		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
+#define PIN_VREF100	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT		(AC_PINCTL_OUT_EN)
+#define PIN_HP			(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
+#define PIN_HP_AMP		(AC_PINCTL_HP_EN)
 
 /*
  * get widget capabilities
@@ -418,4 +421,13 @@
 				 hda_nid_t nid);
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+/*
+ * AMP control callbacks
+ */
+/* retrieve parameters from private_value */
+#define get_amp_nid(kc)		((kc)->private_value & 0xffff)
+#define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
+#define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
+#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
+
 #endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
index 2fdf235..dfbcfa8 100644
--- a/sound/pci/hda/hda_patch.h
+++ b/sound/pci/hda/hda_patch.h
@@ -18,3 +18,5 @@
 extern struct hda_codec_preset snd_hda_preset_conexant[];
 /* VIA codecs */
 extern struct hda_codec_preset snd_hda_preset_via[];
+/* NVIDIA HDMI codecs */
+extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 1e5aff5..743d779 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -216,7 +216,7 @@
 	unsigned int caps, val;
 
 	caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-	snd_iprintf(buffer, "  Pincap 0x08%x:", caps);
+	snd_iprintf(buffer, "  Pincap 0x%08x:", caps);
 	if (caps & AC_PINCAP_IN)
 		snd_iprintf(buffer, " IN");
 	if (caps & AC_PINCAP_OUT)
@@ -229,8 +229,13 @@
 		snd_iprintf(buffer, " Detect");
 	if (caps & AC_PINCAP_BALANCE)
 		snd_iprintf(buffer, " Balanced");
-	if (caps & AC_PINCAP_LR_SWAP)
-		snd_iprintf(buffer, " R/L");
+	if (caps & AC_PINCAP_HDMI) {
+		/* Realtek uses this bit as a different meaning */
+		if ((codec->vendor_id >> 16) == 0x10ec)
+			snd_iprintf(buffer, " R/L");
+		else
+			snd_iprintf(buffer, " HDMI");
+	}
 	if (caps & AC_PINCAP_TRIG_REQ)
 		snd_iprintf(buffer, " Trigger");
 	if (caps & AC_PINCAP_IMP_SENSE)
@@ -552,9 +557,15 @@
 
 		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
 			    get_wid_type_name(wid_type), wid_caps);
-		if (wid_caps & AC_WCAP_STEREO)
-			snd_iprintf(buffer, " Stereo");
-		else
+		if (wid_caps & AC_WCAP_STEREO) {
+			unsigned int chans;
+			chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+			chans = ((chans << 1) | 1) + 1;
+			if (chans == 2)
+				snd_iprintf(buffer, " Stereo");
+			else
+				snd_iprintf(buffer, " %d-Channels", chans);
+		} else
 			snd_iprintf(buffer, " Mono");
 		if (wid_caps & AC_WCAP_DIGITAL)
 			snd_iprintf(buffer, " Digital");
@@ -566,6 +577,8 @@
 			snd_iprintf(buffer, " Stripe");
 		if (wid_caps & AC_WCAP_LR_SWAP)
 			snd_iprintf(buffer, " R/L");
+		if (wid_caps & AC_WCAP_CP_CAPS)
+			snd_iprintf(buffer, " CP");
 		snd_iprintf(buffer, "\n");
 
 		/* volume knob is a special widget that always have connection
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index e8003d9..2b00c4a 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1826,9 +1826,14 @@
 	0x0c, 0x0d, 0x0e
 };
 
-#define AD1988_SPDIF_OUT	0x02
+#define AD1988_SPDIF_OUT		0x02
+#define AD1988_SPDIF_OUT_HDMI	0x0b
 #define AD1988_SPDIF_IN		0x07
 
+static hda_nid_t ad1989b_slave_dig_outs[2] = {
+	AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI
+};
+
 static struct hda_input_mux ad1988_6stack_capture_source = {
 	.num_items = 5,
 	.items = {
@@ -2143,6 +2148,7 @@
 
 static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
 	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
 	{ } /* end */
 };
 
@@ -2207,6 +2213,8 @@
 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
 	/* Analog CD Input */
 	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 
 	{ }
 };
@@ -2247,8 +2255,12 @@
 
 /* AD1989 has no ADC -> SPDIF route */
 static struct hda_verb ad1989_spdif_init_verbs[] = {
-	/* SPDIF out pin */
+	/* SPDIF-1 out pin */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	/* SPDIF-2/HDMI out pin */
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
 	{ }
 };
 
@@ -2336,6 +2348,8 @@
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 	{ }
 };
 
@@ -2409,6 +2423,8 @@
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
 	{ }
 };
 
@@ -2868,6 +2884,7 @@
 	SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
 	SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
+	SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
 	{}
 };
 
@@ -2975,6 +2992,7 @@
 				ad1989_spdif_out_mixers;
 			spec->init_verbs[spec->num_init_verbs++] =
 				ad1989_spdif_init_verbs;
+			codec->slave_dig_outs = ad1989b_slave_dig_outs;
 		} else {
 			spec->mixers[spec->num_mixers++] =
 				ad1988_spdif_out_mixers;
@@ -3911,7 +3929,7 @@
 
 
 /*
- * AD1882
+ * AD1882 / AD1882A
  *
  * port-A - front hp-out
  * port-B - front mic-in
@@ -3948,6 +3966,18 @@
 	},
 };
 
+/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
+static struct hda_input_mux ad1882a_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Front Mic", 0x1 },
+		{ "Mic", 0x4},
+		{ "Line", 0x2 },
+		{ "Digital Mic", 0x06 },
+		{ "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),
@@ -3957,16 +3987,7 @@
 	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),
@@ -3999,6 +4020,35 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
+	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),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
+	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, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+	HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
+	{ } /* 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),
@@ -4168,9 +4218,16 @@
 	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;
+	if (codec->vendor_id == 0x11d1882)
+		spec->input_mux = &ad1882_capture_source;
+	else
+		spec->input_mux = &ad1882a_capture_source;
+	spec->num_mixers = 2;
 	spec->mixers[0] = ad1882_base_mixers;
+	if (codec->vendor_id == 0x11d1882)
+		spec->mixers[1] = ad1882_loopback_mixers;
+	else
+		spec->mixers[1] = ad1882a_loopback_mixers;
 	spec->num_init_verbs = 1;
 	spec->init_verbs[0] = ad1882_init_verbs;
 	spec->spdif_route = 0;
@@ -4187,8 +4244,8 @@
 	switch (board_config) {
 	default:
 	case AD1882_3STACK:
-		spec->num_mixers = 2;
-		spec->mixers[1] = ad1882_3stack_mixers;
+		spec->num_mixers = 3;
+		spec->mixers[2] = ad1882_3stack_mixers;
 		spec->channel_mode = ad1882_modes;
 		spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
 		spec->need_dac_fix = 1;
@@ -4196,8 +4253,8 @@
 		spec->multiout.num_dacs = 1;
 		break;
 	case AD1882_6STACK:
-		spec->num_mixers = 2;
-		spec->mixers[1] = ad1882_6stack_mixers;
+		spec->num_mixers = 3;
+		spec->mixers[2] = ad1882_6stack_mixers;
 		break;
 	}
 	return 0;
@@ -4220,6 +4277,7 @@
 	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
 	{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
 	{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
+	{ .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
 	{ .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
 	{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
 	{} /* terminator */
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index 1227250..ba61575 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -35,6 +35,9 @@
 	struct hda_pcm pcm_rec;
 };
 
+#define CVT_NID		0x02	/* audio converter */
+#define PIN_NID		0x03	/* HDMI output pin */
+
 static struct hda_verb atihdmi_basic_init[] = {
 	/* enable digital output on pin widget */
 	{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
@@ -60,8 +63,9 @@
 {
 	snd_hda_sequence_write(codec, atihdmi_basic_init);
 	/* SI codec requires to unmute the pin */
-	if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP)
-		snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+	if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+		snd_hda_codec_write(codec, PIN_NID, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
 				    AMP_OUT_UNMUTE);
 	return 0;
 }
@@ -92,15 +96,29 @@
 					    struct snd_pcm_substream *substream)
 {
 	struct atihdmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-					     format, substream);
+	int chans = substream->runtime->channels;
+	int i, err;
+
+	err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					    format, substream);
+	if (err < 0)
+		return err;
+	snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
+			    chans - 1);
+	/* FIXME: XXX */
+	for (i = 0; i < chans; i++) {
+		snd_hda_codec_write(codec, CVT_NID, 0,
+				    AC_VERB_SET_HDMI_CHAN_SLOT,
+				    (i << 4) | i);
+	}
+	return 0;
 }
 
 static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
 	.substreams = 1,
 	.channels_min = 2,
 	.channels_max = 2,
-	.nid = 0x2, /* NID to query formats and rates and setup streams */
+	.nid = CVT_NID, /* NID to query formats and rates and setup streams */
 	.ops = {
 		.open = atihdmi_dig_playback_pcm_open,
 		.close = atihdmi_dig_playback_pcm_close,
@@ -112,6 +130,7 @@
 {
 	struct atihdmi_spec *spec = codec->spec;
 	struct hda_pcm *info = &spec->pcm_rec;
+	unsigned int chans;
 
 	codec->num_pcms = 1;
 	codec->pcm_info = info;
@@ -120,6 +139,13 @@
 	info->pcm_type = HDA_PCM_TYPE_HDMI;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
 
+	/* FIXME: we must check ELD and change the PCM parameters dynamically
+	 */
+	chans = get_wcaps(codec, CVT_NID);
+	chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13;
+	chans = ((chans << 1) | 1) + 1;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+
 	return 0;
 }
 
@@ -147,9 +173,11 @@
 
 	spec->multiout.num_dacs = 0;	  /* no analog */
 	spec->multiout.max_channels = 2;
-	spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital,
-					   * seems to be unused in pure-digital
-					   * case. */
+	/* NID for copying analog to digital,
+	 * seems to be unused in pure-digital
+	 * case.
+	 */
+	spec->multiout.dig_out_nid = CVT_NID;
 
 	codec->patch_ops = atihdmi_patch_ops;
 
@@ -164,6 +192,7 @@
 	{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+	{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
 	{} /* terminator */
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
new file mode 100644
index 0000000..1a65775
--- /dev/null
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -0,0 +1,164 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for NVIDIA HDMI codecs
+ *
+ * Copyright (c) 2008 NVIDIA Corp.  All rights reserved.
+ * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct nvhdmi_spec {
+	struct hda_multi_out multiout;
+
+	struct hda_pcm pcm_rec;
+};
+
+static struct hda_verb nvhdmi_basic_init[] = {
+	/* enable digital output on pin widget */
+	{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{} /* terminator */
+};
+
+/*
+ * Controls
+ */
+static int nvhdmi_build_controls(struct hda_codec *codec)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int nvhdmi_init(struct hda_codec *codec)
+{
+	snd_hda_sequence_write(codec, nvhdmi_basic_init);
+	return 0;
+}
+
+/*
+ * Digital out
+ */
+static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				     struct hda_codec *codec,
+				     struct snd_pcm_substream *substream)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					    struct hda_codec *codec,
+					    unsigned int stream_tag,
+					    unsigned int format,
+					    struct snd_pcm_substream *substream)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x4, /* NID to query formats and rates and setup streams */
+	.rates = SNDRV_PCM_RATE_48000,
+	.maxbps = 16,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.ops = {
+		.open = nvhdmi_dig_playback_pcm_open,
+		.close = nvhdmi_dig_playback_pcm_close,
+		.prepare = nvhdmi_dig_playback_pcm_prepare
+	},
+};
+
+static int nvhdmi_build_pcms(struct hda_codec *codec)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = &spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "NVIDIA HDMI";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
+
+	return 0;
+}
+
+static void nvhdmi_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+static struct hda_codec_ops nvhdmi_patch_ops = {
+	.build_controls = nvhdmi_build_controls,
+	.build_pcms = nvhdmi_build_pcms,
+	.init = nvhdmi_init,
+	.free = nvhdmi_free,
+};
+
+static int patch_nvhdmi(struct hda_codec *codec)
+{
+	struct nvhdmi_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	spec->multiout.num_dacs = 0;	  /* no analog */
+	spec->multiout.max_channels = 2;
+	spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
+					   * seems to be unused in pure-digital
+					   * case. */
+
+	codec->patch_ops = nvhdmi_patch_ops;
+
+	return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
+	{ .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi },
+	{ .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi },
+	{} /* terminator */
+};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 6602516..0b6e682 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -72,6 +72,7 @@
 enum {
 	ALC260_BASIC,
 	ALC260_HP,
+	ALC260_HP_DC7600,
 	ALC260_HP_3013,
 	ALC260_FUJITSU_S702X,
 	ALC260_ACER,
@@ -100,6 +101,9 @@
 	ALC262_BENQ_T31,
 	ALC262_ULTRA,
 	ALC262_LENOVO_3000,
+	ALC262_NEC,
+	ALC262_TOSHIBA_S06,
+	ALC262_TOSHIBA_RX1,
 	ALC262_AUTO,
 	ALC262_MODEL_LAST /* last tag */
 };
@@ -110,6 +114,7 @@
 	ALC268_3ST,
 	ALC268_TOSHIBA,
 	ALC268_ACER,
+	ALC268_ACER_ASPIRE_ONE,
 	ALC268_DELL,
 	ALC268_ZEPTO,
 #ifdef CONFIG_SND_DEBUG
@@ -122,6 +127,7 @@
 /* ALC269 models */
 enum {
 	ALC269_BASIC,
+	ALC269_QUANTA_FL1,
 	ALC269_ASUS_EEEPC_P703,
 	ALC269_ASUS_EEEPC_P901,
 	ALC269_AUTO,
@@ -169,6 +175,13 @@
 	ALC663_ASUS_G71V,
 	ALC663_ASUS_H13,
 	ALC663_ASUS_G50V,
+	ALC662_ECS,
+	ALC663_ASUS_MODE1,
+	ALC662_ASUS_MODE2,
+	ALC663_ASUS_MODE3,
+	ALC663_ASUS_MODE4,
+	ALC663_ASUS_MODE5,
+	ALC663_ASUS_MODE6,
 	ALC662_AUTO,
 	ALC662_MODEL_LAST,
 };
@@ -200,18 +213,21 @@
 	ALC883_ACER,
 	ALC883_ACER_ASPIRE,
 	ALC883_MEDION,
-	ALC883_MEDION_MD2,	
+	ALC883_MEDION_MD2,
 	ALC883_LAPTOP_EAPD,
 	ALC883_LENOVO_101E_2ch,
 	ALC883_LENOVO_NB0763,
 	ALC888_LENOVO_MS7195_DIG,
-	ALC883_HAIER_W66,		
+	ALC888_LENOVO_SKY,
+	ALC883_HAIER_W66,
 	ALC888_3ST_HP,
 	ALC888_6ST_DELL,
 	ALC883_MITAC,
 	ALC883_CLEVO_M720,
 	ALC883_FUJITSU_PI2515,
 	ALC883_3ST_6ch_INTEL,
+	ALC888_ASUS_M90V,
+	ALC888_ASUS_EEE1601,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 };
@@ -398,7 +414,7 @@
 
 /*
  * Control the mode of pin widget settings via the mixer.  "pc" is used
- * instead of "%" to avoid consequences of accidently treating the % as 
+ * instead of "%" to avoid consequences of accidently treating the % as
  * being part of a format specifier.  Maximum allowed length of a value is
  * 63 characters plus NULL terminator.
  *
@@ -429,7 +445,7 @@
 #define ALC_PIN_DIR_IN_NOMICBIAS    0x03
 #define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
 
-/* Info about the pin modes supported by the different pin direction modes. 
+/* Info about the pin modes supported by the different pin direction modes.
  * For each direction the minimum and maximum values are given.
  */
 static signed char alc_pin_mode_dir_info[5][2] = {
@@ -502,7 +518,7 @@
 					  AC_VERB_SET_PIN_WIDGET_CONTROL,
 					  alc_pin_mode_values[val]);
 
-		/* Also enable the retasking pin's input/output as required 
+		/* Also enable the retasking pin's input/output as required
 		 * for the requested pin mode.  Enum values of 2 or less are
 		 * input modes.
 		 *
@@ -707,7 +723,7 @@
 	     i++)
 		spec->init_verbs[spec->num_init_verbs++] =
 			preset->init_verbs[i];
-	
+
 	spec->channel_mode = preset->channel_mode;
 	spec->num_channel_mode = preset->num_channel_mode;
 	spec->need_dac_fix = preset->need_dac_fix;
@@ -718,7 +734,7 @@
 	spec->multiout.dac_nids = preset->dac_nids;
 	spec->multiout.dig_out_nid = preset->dig_out_nid;
 	spec->multiout.hp_nid = preset->hp_nid;
-	
+
 	spec->num_mux_defs = preset->num_mux_defs;
 	if (!spec->num_mux_defs)
 		spec->num_mux_defs = 1;
@@ -855,7 +871,7 @@
 	if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
 		goto do_sku;
 
-	/*	
+	/*
 	 * 31~30	: port conetcivity
 	 * 29~21	: reserve
 	 * 20		: PCBEEP input
@@ -946,7 +962,7 @@
 			tmp = snd_hda_codec_read(codec, 0x20, 0,
 						 AC_VERB_GET_PROC_COEF, 0);
 			snd_hda_codec_write(codec, 0x20, 0,
-					    AC_VERB_SET_COEF_INDEX, 7);	
+					    AC_VERB_SET_COEF_INDEX, 7);
 			snd_hda_codec_write(codec, 0x20, 0,
 					    AC_VERB_SET_PROC_COEF,
 					    tmp | 0x2010);
@@ -961,7 +977,7 @@
 			tmp = snd_hda_codec_read(codec, 0x20, 0,
 						 AC_VERB_GET_PROC_COEF, 0);
 			snd_hda_codec_write(codec, 0x20, 0,
-					    AC_VERB_SET_COEF_INDEX, 7);	
+					    AC_VERB_SET_COEF_INDEX, 7);
 			snd_hda_codec_write(codec, 0x20, 0,
 					    AC_VERB_SET_PROC_COEF,
 					    tmp | 0x3000);
@@ -970,7 +986,7 @@
 	default:
 		break;
 	}
-	
+
 	/* is laptop or Desktop and enable the function "Mute internal speaker
 	 * when the external headphone out jack is plugged"
 	 */
@@ -1006,6 +1022,7 @@
 	snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,
 			    AC_VERB_SET_UNSOLICITED_ENABLE,
 			    AC_USRSP_EN | ALC880_HP_EVENT);
+
 	spec->unsol_event = alc_sku_unsol_event;
 }
 
@@ -1296,7 +1313,7 @@
  *
  * The system also has a pair of internal speakers, and a headphone jack.
  * These are both connected to Line2 on the codec, hence to DAC 02.
- * 
+ *
  * There is a variable resistor to control the speaker or headphone
  * volume. This is a hardware-only device without a software API.
  *
@@ -1824,7 +1841,7 @@
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-	
+
 	{ }
 };
 
@@ -1869,7 +1886,7 @@
 
 /*
 * Uniwill P53
-* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19, 
+* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
  */
 static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
 	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -1968,7 +1985,7 @@
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
 {
 	unsigned int present;
-	
+
 	present = snd_hda_codec_read(codec, 0x21, 0,
 				     AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
 	present &= HDA_AMP_VOLMASK;
@@ -2050,7 +2067,7 @@
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-	
+
 	{ }
 };
 
@@ -2632,12 +2649,14 @@
 
 	info->name = spec->stream_name_analog;
 	if (spec->stream_analog_playback) {
-		snd_assert(spec->multiout.dac_nids, return -EINVAL);
+		if (snd_BUG_ON(!spec->multiout.dac_nids))
+			return -EINVAL;
 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
 	}
 	if (spec->stream_analog_capture) {
-		snd_assert(spec->adc_nids, return -EINVAL);
+		if (snd_BUG_ON(!spec->adc_nids))
+			return -EINVAL;
 		info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
 		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
 	}
@@ -2667,6 +2686,8 @@
 			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
 		}
+		/* FIXME: do we need this for all Realtek codec models? */
+		codec->spdif_status_reset = 1;
 	}
 
 	/* If the use of more than one ADC is requested for the current
@@ -3683,7 +3704,7 @@
 {
 	struct alc_spec *spec = codec->spec;
 	int i;
-	
+
 	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
 	for (i = 0; i < spec->autocfg.line_outs; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
@@ -4124,6 +4145,33 @@
 	{ } /* end */
 };
 
+static struct hda_bind_ctls alc260_dc7600_bind_master_vol = {
+	.ops = &snd_hda_bind_vol,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct hda_bind_ctls alc260_dc7600_bind_switch = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol),
+	HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
 static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 	{},
@@ -4147,7 +4195,30 @@
 		alc260_hp_3013_automute(codec);
 }
 
-/* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12, 
+static void alc260_hp_3012_automute(struct hda_codec *codec)
+{
+	unsigned int present, bits;
+
+	present = snd_hda_codec_read(codec, 0x10, 0,
+			AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+
+	bits = present ? 0 : PIN_OUT;
+	snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+}
+
+static void alc260_hp_3012_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc260_hp_3012_automute(codec);
+}
+
+/* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12,
  * HP jack = 0x14, CD audio =  0x16, internal speaker = 0x10.
  */
 static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
@@ -4478,7 +4549,7 @@
 	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
 	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
 
-	/* Ensure Line1 pin widget takes its input from the OUT1 sum bus 
+	/* Ensure Line1 pin widget takes its input from the OUT1 sum bus
 	 * when acting as an output.
 	 */
 	{0x0d, AC_VERB_SET_CONNECT_SEL, 0},
@@ -4503,14 +4574,14 @@
 	 * stage.
 	 */
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	/* Unmute input buffer of pin widget used for Line-in (no equiv 
+	/* Unmute input buffer of pin widget used for Line-in (no equiv
 	 * mixer ctrl)
 	 */
 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 	/* Mute capture amp left and right */
 	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	/* Set ADC connection select to match default mixer setting - line 
+	/* Set ADC connection select to match default mixer setting - line
 	 * in (on mic1 pin)
 	 */
 	{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -4564,7 +4635,7 @@
 	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
 	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
 
-	/* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum 
+	/* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
 	 * bus when acting as outputs.
 	 */
 	{0x0b, AC_VERB_SET_CONNECT_SEL, 0},
@@ -4675,6 +4746,20 @@
                 alc260_replacer_672v_automute(codec);
 }
 
+static struct hda_verb alc260_hp_dc7600_verbs[] = {
+	{0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
 /* Test configuration for debugging, modelled after the ALC880 test
  * configuration.
  */
@@ -4686,7 +4771,7 @@
 	0x04, 0x05,
 };
 /* For testing the ALC260, each input MUX needs its own definition since
- * the signal assignments are different.  This assumes that the first ADC 
+ * the signal assignments are different.  This assumes that the first ADC
  * is NID 0x04.
  */
 static struct hda_input_mux alc260_test_capture_sources[2] = {
@@ -4769,7 +4854,7 @@
 
 	/* Switches to allow the digital IO pins to be enabled.  The datasheet
 	 * is ambigious as to which NID is which; testing on laptops which
-	 * make this output available should provide clarification. 
+	 * make this output available should provide clarification.
 	 */
 	ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
 	ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
@@ -4805,7 +4890,7 @@
 	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
 	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
 
-	/* Ensure mic1, mic2, line1 and line2 pin widgets take input from the 
+	/* Ensure mic1, mic2, line1 and line2 pin widgets take input from the
 	 * OUT1 sum bus when acting as an output.
 	 */
 	{0x0b, AC_VERB_SET_CONNECT_SEL, 0},
@@ -4897,7 +4982,7 @@
 		sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
 	} else
 		return 0; /* N/A */
-	
+
 	snprintf(name, sizeof(name), "%s Playback Volume", pfx);
 	err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);
 	if (err < 0)
@@ -5003,7 +5088,7 @@
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
 		alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0);
 	}
-	
+
 	nid = spec->autocfg.speaker_pins[0];
 	if (nid)
 		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
@@ -5045,7 +5130,7 @@
 	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	
+
 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
 	 * mixer widget
 	 * Note: PASD motherboards uses the Line In 2 as the input for
@@ -5074,7 +5159,7 @@
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	
+
 	{ }
 };
 
@@ -5155,6 +5240,7 @@
 	[ALC260_BASIC]		= "basic",
 	[ALC260_HP]		= "hp",
 	[ALC260_HP_3013]	= "hp-3013",
+	[ALC260_HP_DC7600]	= "hp-dc7600",
 	[ALC260_FUJITSU_S702X]	= "fujitsu",
 	[ALC260_ACER]		= "acer",
 	[ALC260_WILL]		= "will",
@@ -5172,7 +5258,7 @@
 	SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
-	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013),
+	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),
 	SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
 	SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP),
@@ -5218,6 +5304,22 @@
 		.unsol_event = alc260_hp_unsol_event,
 		.init_hook = alc260_hp_automute,
 	},
+	[ALC260_HP_DC7600] = {
+		.mixers = { alc260_hp_dc7600_mixer,
+			    alc260_input_mixer,
+			    alc260_capture_alt_mixer },
+		.init_verbs = { alc260_init_verbs,
+				alc260_hp_dc7600_verbs },
+		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
+		.dac_nids = alc260_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
+		.adc_nids = alc260_hp_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc260_modes),
+		.channel_mode = alc260_modes,
+		.input_mux = &alc260_capture_source,
+		.unsol_event = alc260_hp_3012_unsol_event,
+		.init_hook = alc260_hp_3012_automute,
+	},
 	[ALC260_HP_3013] = {
 		.mixers = { alc260_hp_3013_mixer,
 			    alc260_input_mixer,
@@ -5933,7 +6035,7 @@
 
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	
+
 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
 	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
 	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -5949,7 +6051,7 @@
 static void alc882_targa_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_stereo(codec, 0x1b, HDA_OUTPUT, 0,
@@ -5975,7 +6077,7 @@
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	
+
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
@@ -5993,7 +6095,7 @@
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-        
+
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
@@ -6319,7 +6421,7 @@
 		.channel_mode = alc882_3ST_6ch_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc882_capture_source,
-	},	
+	},
 	[ALC882_ASUS_A7M] = {
 		.mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
 		.init_verbs = { alc882_init_verbs, alc882_eapd_verbs,
@@ -6332,14 +6434,14 @@
 		.channel_mode = alc880_threestack_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc882_capture_source,
-	},	
+	},
 };
 
 
 /*
  * Pin config fixes
  */
-enum { 
+enum {
 	PINFIX_ABIT_AW9D_MAX
 };
 
@@ -6554,16 +6656,19 @@
 			board_config = ALC885_MACPRO;
 			break;
 		case 0x106b1000: /* iMac 24 */
+		case 0x106b2800: /* AppleTV */
 			board_config = ALC885_IMAC24;
 			break;
 		case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
+		case 0x106b00a4: /* MacbookPro4,1 */
 		case 0x106b2c00: /* Macbook Pro rev3 */
 		case 0x106b3600: /* Macbook 3.1 */
 			board_config = ALC885_MBP3;
 			break;
 		default:
 			/* ALC889A is handled better as ALC888-compatible */
-			if (codec->revision_id == 0x100103) {
+			if (codec->revision_id == 0x100101 ||
+			    codec->revision_id == 0x100103) {
 				alc_free(codec);
 				return patch_alc883(codec);
 			}
@@ -6718,6 +6823,23 @@
 	},
 };
 
+static struct hda_input_mux alc883_lenovo_sky_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x4 },
+	},
+};
+
+static struct hda_input_mux alc883_asus_eee1601_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Line", 0x2 },
+	},
+};
+
 #define alc883_mux_enum_info alc_mux_enum_info
 #define alc883_mux_enum_get alc_mux_enum_get
 /* ALC883 has the ALC882-type input selection */
@@ -7032,13 +7154,11 @@
 	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,
+		.count = 1,
 		.info = alc883_mux_enum_info,
 		.get = alc883_mux_enum_get,
 		.put = alc883_mux_enum_put,
@@ -7256,7 +7376,7 @@
 		.put = alc883_mux_enum_put,
 	},
 	{ } /* end */
-};	
+};
 
 static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -7283,6 +7403,87 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc888_lenovo_sky_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_MUTE("iSpeaker Playback Switch", 0x1a, 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("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 hda_bind_ctls alc883_bind_cap_vol = {
+	.ops = &snd_hda_bind_vol,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+		HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+		0
+	},
+};
+
+static struct hda_bind_ctls alc883_bind_cap_switch = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+		HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+		0
+	},
+};
+
+static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
+	HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.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,
@@ -7296,7 +7497,7 @@
 
 static struct hda_verb alc883_init_verbs[] = {
 	/* ADC1: mute amp left and right */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* ADC2: mute amp left and right */
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -7361,14 +7562,14 @@
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
 	/* Input mixer2 */
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	/* Input mixer3 */
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{ }
 };
 
@@ -7468,7 +7669,7 @@
 
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	
+
 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
 	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
 	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
@@ -7518,6 +7719,18 @@
 	{ } /* end */
 };
 
+static struct hda_verb alc888_lenovo_sky_verbs[] = {
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{ } /* end */
+};
+
 static struct hda_verb alc888_3st_hp_verbs[] = {
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */
 	{0x16, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Rear : output 1 (0x0d) */
@@ -7555,7 +7768,7 @@
 static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
 {
  	unsigned int present;
- 
+
  	present = snd_hda_codec_read(codec, 0x1b, 0,
 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
 	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -7568,7 +7781,7 @@
 static void alc888_lenovo_ms7195_rca_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_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -7598,7 +7811,7 @@
 static void alc883_medion_md2_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_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -7753,7 +7966,7 @@
 static void alc883_acer_aspire_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_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -7790,7 +8003,7 @@
 static void alc888_6st_dell_front_automute(struct hda_codec *codec)
 {
  	unsigned int present;
- 
+
  	present = snd_hda_codec_read(codec, 0x1b, 0,
 				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
 	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -7814,6 +8027,50 @@
 	}
 }
 
+static void alc888_lenovo_sky_front_automute(struct hda_codec *codec)
+{
+	unsigned int mute;
+	unsigned int present;
+
+	snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
+	present = snd_hda_codec_read(codec, 0x1b, 0,
+				     AC_VERB_GET_PIN_SENSE, 0);
+	present = (present & 0x80000000) != 0;
+	if (present) {
+		/* mute internal speaker */
+		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+		snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+		snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+	} else {
+		/* unmute internal speaker if necessary */
+		mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
+		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+		snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+		snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+	}
+}
+
+static void alc883_lenovo_sky_unsol_event(struct hda_codec *codec,
+					     unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc888_lenovo_sky_front_automute(codec);
+}
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -7898,6 +8155,105 @@
 	{ } /* end */
 };
 
+static struct hda_verb alc888_asus_m90v_verbs[] = {
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* enable unsolicited event */
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+	{ } /* end */
+};
+
+static void alc883_nb_mic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
+}
+
+static void alc883_M90V_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x1b, 0,
+				     AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+	bits = present ? 0 : PIN_OUT;
+	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+}
+
+static void alc883_mode2_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc883_M90V_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc883_nb_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc883_mode2_inithook(struct hda_codec *codec)
+{
+	alc883_M90V_speaker_automute(codec);
+	alc883_nb_mic_automute(codec);
+}
+
+static struct hda_verb alc888_asus_eee1601_verbs[] = {
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x0838},
+	/* enable unsolicited event */
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{ } /* end */
+};
+
+static void alc883_eee1601_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x14, 0,
+				     AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+	bits = present ? 0 : PIN_OUT;
+	snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    bits);
+}
+
+static void alc883_eee1601_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc883_eee1601_speaker_automute(codec);
+		break;
+	}
+}
+
+static void alc883_eee1601_inithook(struct hda_codec *codec)
+{
+	alc883_eee1601_speaker_automute(codec);
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc883_loopbacks	alc880_loopbacks
 #endif
@@ -7927,6 +8283,7 @@
 	[ALC883_LENOVO_101E_2ch] = "lenovo-101e",
 	[ALC883_LENOVO_NB0763]	= "lenovo-nb0763",
 	[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
+	[ALC888_LENOVO_SKY] = "lenovo-sky",
 	[ALC883_HAIER_W66] 	= "haier-w66",
 	[ALC888_3ST_HP]		= "3stack-hp",
 	[ALC888_6ST_DELL]	= "6stack-dell",
@@ -7942,7 +8299,7 @@
 	SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
-	SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), 
+	SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
 	SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
 	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
@@ -7950,10 +8307,13 @@
 	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x1043, 0x8317, "Asus M90V", ALC888_ASUS_M90V),
+	SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
 	SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
 	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
 	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
@@ -7989,6 +8349,7 @@
 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+	SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY),
 	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
 	SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
@@ -8128,7 +8489,7 @@
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc883_medion_md2_unsol_event,
 		.init_hook = alc883_medion_md2_automute,
-	},	
+	},
 	[ALC883_LAPTOP_EAPD] = {
 		.mixers = { alc883_base_mixer },
 		.init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
@@ -8245,6 +8606,49 @@
 		.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
 		.init_hook = alc883_2ch_fujitsu_pi2515_automute,
 	},
+	[ALC888_LENOVO_SKY] = {
+		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_lenovo_sky_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,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_lenovo_sky_capture_source,
+		.unsol_event = alc883_lenovo_sky_unsol_event,
+		.init_hook = alc888_lenovo_sky_front_automute,
+	},
+	[ALC888_ASUS_M90V] = {
+		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_fujitsu_pi2515_capture_source,
+		.unsol_event = alc883_mode2_unsol_event,
+		.init_hook = alc883_mode2_inithook,
+	},
+	[ALC888_ASUS_EEE1601] = {
+		.mixers = { alc883_asus_eee1601_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_asus_eee1601_capture_source,
+		.unsol_event = alc883_eee1601_unsol_event,
+		.init_hook = alc883_eee1601_inithook,
+	},
 };
 
 
@@ -8452,6 +8856,13 @@
 #define alc262_modes		alc260_modes
 #define alc262_capture_source	alc882_capture_source
 
+static hda_nid_t alc262_dmic_adc_nids[1] = {
+	/* ADC0 */
+	0x09
+};
+
+static hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 };
+
 static struct snd_kcontrol_new alc262_base_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -8833,10 +9244,10 @@
 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-	
+
 	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-	
+
 	/* FIXME: use matrix-type input source selection */
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
 	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
@@ -8858,6 +9269,12 @@
 	{ }
 };
 
+static struct hda_verb alc262_eapd_verbs[] = {
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{ }
+};
+
 static struct hda_verb alc262_hippo_unsol_verbs[] = {
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -8884,6 +9301,91 @@
 	{}
 };
 
+static struct hda_input_mux alc262_dmic_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Int DMic", 0x9 },
+		{ "Mic", 0x0 },
+	},
+};
+
+static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker 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("Capture Volume", 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_verb alc262_toshiba_s06_verbs[] = {
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x09},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static void alc262_dmic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x22, 0,
+				AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
+}
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc262_toshiba_s06_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x15, 0,
+					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	bits = present ? 0 : PIN_OUT;
+	snd_hda_codec_write(codec, 0x14, 0,
+					AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
+}
+
+
+
+/* unsolicited event for HP jack sensing */
+static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc262_toshiba_s06_speaker_automute(codec);
+	if ((res >> 26) == ALC880_MIC_EVENT)
+		alc262_dmic_automute(codec);
+
+}
+
+static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
+{
+	alc262_toshiba_s06_speaker_automute(codec);
+	alc262_dmic_automute(codec);
+}
+
 /* mute/unmute internal speaker according to the hp jack and mute state */
 static void alc262_hippo_automute(struct hda_codec *codec)
 {
@@ -8948,6 +9450,41 @@
 }
 
 /*
+ * nec model
+ *  0x15 = headphone
+ *  0x16 = internal speaker
+ *  0x18 = external mic
+ */
+
+static struct snd_kcontrol_new alc262_nec_mixer[] = {
+	HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+static struct hda_verb alc262_nec_verbs[] = {
+	/* Unmute Speaker */
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* Headphone */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+	/* External mic to headphone */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* External mic to speaker */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{}
+};
+
+/*
  * fujitsu model
  *  0x14 = headphone/spdif-out, 0x15 = internal speaker,
  *  0x1b = port replicator headphone out
@@ -9179,6 +9716,25 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = alc262_sony_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	{ } /* end */
+};
+
 /* additional init verbs for Benq laptops */
 static struct hda_verb alc262_EAPD_verbs[] = {
 	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
@@ -9427,7 +9983,7 @@
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	
+
 	/* set up input amps for analog loopback */
 	/* Amp Indices: DAC = 0, mixer = 1 */
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -9482,7 +10038,7 @@
 	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
         {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-	
+
 	/*
 	 * Set up output mixers (0x0c - 0x0e)
 	 */
@@ -9643,6 +10199,24 @@
 	{ }
 };
 
+static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },	/* Front Speaker */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },	/* MIC jack */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },	/* Front MIC */
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
+
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },	/* HP  jack */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc262_loopbacks	alc880_loopbacks
 #endif
@@ -9729,13 +10303,17 @@
 	[ALC262_BENQ_ED8]	= "benq",
 	[ALC262_BENQ_T31]	= "benq-t31",
 	[ALC262_SONY_ASSAMD]	= "sony-assamd",
+	[ALC262_TOSHIBA_S06]	= "toshiba-s06",
+	[ALC262_TOSHIBA_RX1]	= "toshiba-rx1",
 	[ALC262_ULTRA]		= "ultra",
 	[ALC262_LENOVO_3000]	= "lenovo-3000",
+	[ALC262_NEC]		= "nec",
 	[ALC262_AUTO]		= "auto",
 };
 
 static struct snd_pci_quirk alc262_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
+	SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC),
 	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
@@ -9764,7 +10342,8 @@
 	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
-		      ALC262_SONY_ASSAMD),
+		      ALC262_TOSHIBA_RX1),
+	SND_PCI_QUIRK(0x1179, 0x0268, "Toshiba S06", ALC262_TOSHIBA_S06),
 	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
@@ -9918,7 +10497,7 @@
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.init_hook = alc262_hippo_automute,
-	},	
+	},
 	[ALC262_ULTRA] = {
 		.mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
 		.init_verbs = { alc262_ultra_verbs },
@@ -9946,6 +10525,43 @@
 		.input_mux = &alc262_fujitsu_capture_source,
 		.unsol_event = alc262_lenovo_3000_unsol_event,
 	},
+	[ALC262_NEC] = {
+		.mixers = { alc262_nec_mixer },
+		.init_verbs = { alc262_nec_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,
+	},
+	[ALC262_TOSHIBA_S06] = {
+		.mixers = { alc262_toshiba_s06_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs,
+							alc262_eapd_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.capsrc_nids = alc262_dmic_capsrc_nids,
+		.dac_nids = alc262_dac_nids,
+		.adc_nids = alc262_dmic_adc_nids, /* ADC0 */
+		.dig_out_nid = ALC262_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_dmic_capture_source,
+		.unsol_event = alc262_toshiba_s06_unsol_event,
+		.init_hook = alc262_toshiba_s06_init_hook,
+	},
+	[ALC262_TOSHIBA_RX1] = {
+		.mixers = { alc262_toshiba_rx1_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_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,
+		.init_hook = alc262_hippo_automute,
+	},
 };
 
 static int patch_alc262(struct hda_codec *codec)
@@ -10004,7 +10620,7 @@
 	spec->stream_name_analog = "ALC262 Analog";
 	spec->stream_analog_playback = &alc262_pcm_analog_playback;
 	spec->stream_analog_capture = &alc262_pcm_analog_capture;
-		
+
 	spec->stream_name_digital = "ALC262 Digital";
 	spec->stream_digital_playback = &alc262_pcm_digital_playback;
 	spec->stream_digital_capture = &alc262_pcm_digital_capture;
@@ -10040,7 +10656,7 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc262_loopbacks;
 #endif
-		
+
 	return 0;
 }
 
@@ -10049,7 +10665,7 @@
  */
 #define ALC268_DIGOUT_NID	ALC880_DIGOUT_NID
 #define alc268_modes		alc260_modes
-	
+
 static hda_nid_t alc268_dac_nids[2] = {
 	/* front, hp */
 	0x02, 0x03
@@ -10109,6 +10725,14 @@
 	{ } /* end */
 };
 
+static struct hda_input_mux alc268_acer_lc_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "i-Mic", 0x6 },
+		{ "E-Mic", 0x0 },
+	},
+};
+
 /* Acer specific */
 /* bind volumes of both NID 0x02 and 0x03 */
 static struct hda_bind_ctls alc268_acer_bind_master_vol = {
@@ -10161,6 +10785,21 @@
 	return change;
 }
 
+static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
+	/* output mixer control */
+	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = alc268_acer_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT),
+	{ }
+};
+
 static struct snd_kcontrol_new alc268_acer_mixer[] = {
 	/* output mixer control */
 	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
@@ -10178,6 +10817,16 @@
 	{ }
 };
 
+static struct hda_verb alc268_acer_aspire_one_verbs[] = {
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x23, AC_VERB_SET_CONNECT_SEL, 0x06},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017},
+	{ }
+};
+
 static struct hda_verb alc268_acer_verbs[] = {
 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */
 	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -10185,7 +10834,6 @@
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 	{ }
 };
@@ -10212,6 +10860,47 @@
 	alc268_acer_automute(codec, 1);
 }
 
+/* toggle speaker-output according to the hp-jack state */
+static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x15, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	bits = present ? AMP_IN_MUTE(0) : 0;
+	snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+}
+
+
+static void alc268_acer_mic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
+			    present ? 0x0 : 0x6);
+}
+
+static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
+				    unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc268_aspire_one_speaker_automute(codec);
+	if ((res >> 26) == ALC880_MIC_EVENT)
+		alc268_acer_mic_automute(codec);
+}
+
+static void alc268_acer_lc_init_hook(struct hda_codec *codec)
+{
+	alc268_aspire_one_speaker_automute(codec);
+	alc268_acer_mic_automute(codec);
+}
+
 static struct snd_kcontrol_new alc268_dell_mixer[] = {
 	/* output mixer control */
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
@@ -10360,7 +11049,7 @@
 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 
 	/* Unmute Selector 23h,24h and set the default input to mic-in */
-	
+
 	{0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -10559,7 +11248,7 @@
 
 	nid = cfg->line_out_pins[0];
 	if (nid)
-		alc268_new_analog_output(spec, nid, "Front", 0);	
+		alc268_new_analog_output(spec, nid, "Front", 0);
 
 	nid = cfg->speaker_pins[0];
 	if (nid == 0x1d) {
@@ -10581,7 +11270,7 @@
 		if (err < 0)
 			return err;
 	}
-	return 0;	
+	return 0;
 }
 
 /* create playback/capture controls for input pins */
@@ -10602,7 +11291,7 @@
 		case 0x1a:
 			idx1 = 2;	/* Line In */
 			break;
-		case 0x1c:	
+		case 0x1c:
 			idx1 = 3;	/* CD */
 			break;
 		case 0x12:
@@ -10614,7 +11303,7 @@
 		}
 		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
 		imux->items[imux->num_items].index = idx1;
-		imux->num_items++;	
+		imux->num_items++;
 	}
 	return 0;
 }
@@ -10644,11 +11333,11 @@
 	}
 
 	dac_vol1 = dac_vol2 = 0xb000 | 0x40;	/* set max volume  */
-	if (line_nid == 0x14)	
+	if (line_nid == 0x14)
 		dac_vol2 = AMP_OUT_ZERO;
 	else if (line_nid == 0x15)
 		dac_vol1 = AMP_OUT_ZERO;
-	if (hp_nid == 0x14)	
+	if (hp_nid == 0x14)
 		dac_vol2 = AMP_OUT_ZERO;
 	else if (hp_nid == 0x15)
 		dac_vol1 = AMP_OUT_ZERO;
@@ -10739,6 +11428,7 @@
 	[ALC268_3ST]		= "3stack",
 	[ALC268_TOSHIBA]	= "toshiba",
 	[ALC268_ACER]		= "acer",
+	[ALC268_ACER_ASPIRE_ONE]	= "acer-aspire",
 	[ALC268_DELL]		= "dell",
 	[ALC268_ZEPTO]		= "zepto",
 #ifdef CONFIG_SND_DEBUG
@@ -10753,11 +11443,14 @@
 	SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
 	SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
 	SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
+	SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One",
+						ALC268_ACER_ASPIRE_ONE),
 	SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
 	SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
 	SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
+	SND_PCI_QUIRK(0x1179, 0xff64, "TOSHIBA L305", ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
 	SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
@@ -10830,6 +11523,23 @@
 		.unsol_event = alc268_acer_unsol_event,
 		.init_hook = alc268_acer_init_hook,
 	},
+	[ALC268_ACER_ASPIRE_ONE] = {
+		.mixers = { alc268_acer_aspire_one_mixer,
+				alc268_capture_alt_mixer },
+		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+				alc268_acer_aspire_one_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,
+		.capsrc_nids = alc268_capsrc_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc268_modes),
+		.channel_mode = alc268_modes,
+		.input_mux = &alc268_acer_lc_capture_source,
+		.unsol_event = alc268_acer_lc_unsol_event,
+		.init_hook = alc268_acer_lc_init_hook,
+	},
 	[ALC268_DELL] = {
 		.mixers = { alc268_dell_mixer, alc268_beep_mixer },
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
@@ -10974,7 +11684,7 @@
 	codec->patch_ops = alc_patch_ops;
 	if (board_config == ALC268_AUTO)
 		spec->init_hook = alc268_auto_init;
-		
+
 	return 0;
 }
 
@@ -10990,6 +11700,14 @@
 	0x08,
 };
 
+static hda_nid_t alc269_capsrc_nids[1] = {
+	0x23,
+};
+
+/* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24),
+ *       not a mux!
+ */
+
 static struct hda_input_mux alc269_eeepc_dmic_capture_source = {
 	.num_items = 2,
 	.items = {
@@ -11016,6 +11734,8 @@
 	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("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
@@ -11025,6 +11745,28 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
+	/* output mixer control */
+	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = alc268_acer_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	{ }
+};
+
 /* bind volumes of both NID 0x0c and 0x0d */
 static struct hda_bind_ctls alc269_epc_bind_vol = {
 	.ops = &snd_hda_bind_vol,
@@ -11068,6 +11810,165 @@
 	{ } /* end */
 };
 
+/* beep control */
+static struct snd_kcontrol_new alc269_beep_mixer[] = {
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct hda_verb alc269_quanta_fl1_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{ }
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x15, 0,
+			AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	bits = present ? AMP_IN_MUTE(0) : 0;
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+			AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+			AMP_IN_MUTE(0), bits);
+
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_COEF_INDEX, 0x0c);
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_PROC_COEF, 0x680);
+
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_COEF_INDEX, 0x0c);
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_PROC_COEF, 0x480);
+}
+
+static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x23, 0,
+			    AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
+}
+
+static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
+				    unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc269_quanta_fl1_speaker_automute(codec);
+	if ((res >> 26) == ALC880_MIC_EVENT)
+		alc269_quanta_fl1_mic_automute(codec);
+}
+
+static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
+{
+	alc269_quanta_fl1_speaker_automute(codec);
+	alc269_quanta_fl1_mic_automute(codec);
+}
+
+static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc269_eeepc_amic_init_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc269_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x15, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	bits = present ? AMP_IN_MUTE(0) : 0;
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+}
+
+static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x23, 0,
+				AC_VERB_SET_CONNECT_SEL,  (present ? 0 : 5));
+}
+
+static void alc269_eeepc_amic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
+				     unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc269_speaker_automute(codec);
+
+	if ((res >> 26) == ALC880_MIC_EVENT)
+		alc269_eeepc_dmic_automute(codec);
+}
+
+static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
+{
+	alc269_speaker_automute(codec);
+	alc269_eeepc_dmic_automute(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
+				     unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc269_speaker_automute(codec);
+
+	if ((res >> 26) == ALC880_MIC_EVENT)
+		alc269_eeepc_amic_automute(codec);
+}
+
+static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
+{
+	alc269_speaker_automute(codec);
+	alc269_eeepc_amic_automute(codec);
+}
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -11075,7 +11976,7 @@
 	/*
 	 * Unmute ADC0 and set the default input to mic-in
 	 */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 	/* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
 	 * analog-loopback mixer widget
@@ -11127,8 +12028,8 @@
 	/* FIXME: use matrix-type input source selection */
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
 	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 
@@ -11138,98 +12039,6 @@
 	{ }
 };
 
-static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
-	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
-	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
-	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-	{}
-};
-
-static struct hda_verb alc269_eeepc_amic_init_verbs[] = {
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))},
-	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
-	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-	{}
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_speaker_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-	unsigned int bits;
-
-	present = snd_hda_codec_read(codec, 0x15, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? AMP_IN_MUTE(0) : 0;
-	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-				 AMP_IN_MUTE(0), bits);
-	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-				 AMP_IN_MUTE(0), bits);
-}
-
-static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
-			    present ? 0 : 5);
-}
-
-static void alc269_eeepc_amic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    present ? AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0));
-	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    present ? AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1));
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
-					  unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc269_speaker_automute(codec);
-
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc269_eeepc_dmic_automute(codec);
-}
-
-static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
-{
-	alc269_speaker_automute(codec);
-	alc269_eeepc_dmic_automute(codec);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
-					  unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc269_speaker_automute(codec);
-
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc269_eeepc_amic_automute(codec);
-}
-
-static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
-{
-	alc269_speaker_automute(codec);
-	alc269_eeepc_amic_automute(codec);
-}
-
 /* add playback controls from the parsed DAC table */
 static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
 					     const struct auto_pin_cfg *cfg)
@@ -11330,7 +12139,7 @@
 static int alc269_parse_auto_config(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	int err;
+	int i, err;
 	static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
 
 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
@@ -11353,9 +12162,20 @@
 	if (spec->kctl_alloc)
 		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
+	/* create a beep mixer control if the pin 0x1d isn't assigned */
+	for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
+		if (spec->autocfg.input_pins[i] == 0x1d)
+			break;
+	if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
+		spec->mixers[spec->num_mixers++] = alc269_beep_mixer;
+
 	spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
+	/* set default input source */
+	snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0],
+				  0, AC_VERB_SET_CONNECT_SEL,
+				  spec->input_mux->items[0].index);
 
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
@@ -11387,14 +12207,20 @@
  * configuration and preset
  */
 static const char *alc269_models[ALC269_MODEL_LAST] = {
-	[ALC269_BASIC]		= "basic",
+	[ALC269_BASIC]			= "basic",
+	[ALC269_QUANTA_FL1]		= "quanta",
+	[ALC269_ASUS_EEEPC_P703]	= "eeepc-p703",
+	[ALC269_ASUS_EEEPC_P901]	= "eeepc-p901"
 };
 
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
 	SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
 		      ALC269_ASUS_EEEPC_P703),
 	SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
 		      ALC269_ASUS_EEEPC_P901),
+	SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
+		      ALC269_ASUS_EEEPC_P901),
 	{}
 };
 
@@ -11409,6 +12235,18 @@
 		.channel_mode = alc269_modes,
 		.input_mux = &alc269_capture_source,
 	},
+	[ALC269_QUANTA_FL1] = {
+		.mixers = { alc269_quanta_fl1_mixer },
+		.init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs },
+		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
+		.dac_nids = alc269_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc269_modes),
+		.channel_mode = alc269_modes,
+		.input_mux = &alc269_capture_source,
+		.unsol_event = alc269_quanta_fl1_unsol_event,
+		.init_hook = alc269_quanta_fl1_init_hook,
+	},
 	[ALC269_ASUS_EEEPC_P703] = {
 		.mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
 		.init_verbs = { alc269_init_verbs,
@@ -11488,6 +12326,7 @@
 
 	spec->adc_nids = alc269_adc_nids;
 	spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
+	spec->capsrc_nids = alc269_capsrc_nids;
 
 	codec->patch_ops = alc_patch_ops;
 	if (board_config == ALC269_AUTO)
@@ -11689,7 +12528,7 @@
 	HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
-	
+
         /*Capture mixer control */
 	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
@@ -11832,20 +12671,20 @@
 	/* route front mic to ADC1*/
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	
+
 	/* Unmute DAC0~3 & spdif out*/
 	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	
+
 	/* Unmute Mixer 14 (mic) 1c (Line in)*/
 	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	
+
 	/* Unmute Stereo Mixer 15 */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -11901,13 +12740,13 @@
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	
+
 	/* Unmute Mixer 14 (mic) 1c (Line in)*/
 	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	
+
 	/* Unmute Stereo Mixer 15 */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -11963,13 +12802,13 @@
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	
+
 	/* Unmute Mixer 14 (mic) 1c (Line in)*/
 	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	
+
 	/* Unmute Stereo Mixer 15 */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -12034,7 +12873,7 @@
         {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
         {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	
+
 	/* Unmute Stereo Mixer 15 */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -12071,20 +12910,20 @@
 	 */
 	/* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	
+
 	/* Unmute DAC0~3 & spdif out*/
 	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	
+
 	/* Unmute Mixer 14 (mic) 1c (Line in)*/
 	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	
+
 	/* Unmute Stereo Mixer 15 */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -12659,7 +13498,7 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc861_loopbacks;
 #endif
-		
+
 	return 0;
 }
 
@@ -12913,7 +13752,7 @@
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	
+
 	{ } /* end */
 };
 
@@ -13058,7 +13897,7 @@
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},	
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
 	{}
 };
 
@@ -13120,7 +13959,7 @@
 	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	
+
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
@@ -13145,7 +13984,7 @@
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},	
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 
 	{ } /* end */
@@ -13304,7 +14143,7 @@
 		.input_mux = &alc861vd_hp_capture_source,
 		.unsol_event = alc861vd_dallas_unsol_event,
 		.init_hook = alc861vd_dallas_automute,
-	},		
+	},
 };
 
 /*
@@ -13883,13 +14722,120 @@
 	{ } /* end */
 };
 
+static struct hda_bind_ctls alc663_asus_bind_master_vol = {
+	.ops = &snd_hda_bind_vol,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct hda_bind_ctls alc663_asus_one_bind_switch = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
 static struct snd_kcontrol_new alc663_m51va_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+	HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct hda_bind_ctls alc663_asus_tree_bind_switch = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+	HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+
+	{ } /* end */
+};
+
+static struct hda_bind_ctls alc663_asus_four_bind_switch = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+	HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc662_1bjd_mixer[] = {
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 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("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct hda_bind_ctls alc663_asus_two_bind_master_vol = {
+	.ops = &snd_hda_bind_vol,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct hda_bind_ctls alc663_asus_two_bind_switch = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+		HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT),
+		0
+	},
+};
+
+static struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume",
+				&alc663_asus_two_bind_master_vol),
+	HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 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_MUTE("DMic Playback Switch", 0x23, 0x9, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = {
+	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
+	HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 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),
 	{ } /* end */
 };
 
@@ -14074,17 +15020,84 @@
 };
 
 static struct hda_verb alc663_m51va_init_verbs[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x21, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */
-
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
-
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
 	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 	{}
 };
 
+static struct hda_verb alc663_21jd_amic_init_verbs[] = {
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc662_1bjd_amic_init_verbs[] = {
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc663_15jd_amic_init_verbs[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = {
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x0},	/* Headphone */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},	/* Headphone */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = {
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
 static struct hda_verb alc663_g71v_init_verbs[] = {
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	/* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
@@ -14110,6 +15123,14 @@
 	{}
 };
 
+static struct hda_verb alc662_ecs_init_verbs[] = {
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
 /* capture mixer elements */
 static struct snd_kcontrol_new alc662_capture_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
@@ -14129,6 +15150,12 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+	{ } /* end */
+};
+
 static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
 {
 	unsigned int present;
@@ -14209,12 +15236,12 @@
 	if (present) {
 		/* mute internal speaker */
 		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+					HDA_AMP_MUTE, HDA_AMP_MUTE);
 	} else {
 		/* unmute internal speaker if necessary */
 		mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
 		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
+					HDA_AMP_MUTE, mute);
 	}
 }
 
@@ -14237,11 +15264,108 @@
 	unsigned char bits;
 
 	present = snd_hda_codec_read(codec, 0x21, 0,
-				     AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
 	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+}
+
+static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x21, 0,
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
+	bits = present ? HDA_AMP_MUTE : 0;
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+}
+
+static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x15, 0,
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
+	bits = present ? HDA_AMP_MUTE : 0;
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), bits);
+}
+
+static void alc662_f5z_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x1b, 0,
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
+	bits = present ? 0 : PIN_OUT;
+	snd_hda_codec_write(codec, 0x14, 0,
+			 AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
+}
+
+static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present1, present2;
+
+	present1 = snd_hda_codec_read(codec, 0x21, 0,
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
+	present2 = snd_hda_codec_read(codec, 0x15, 0,
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
+
+	if (present1 || present2) {
+		snd_hda_codec_write_cache(codec, 0x14, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+	} else {
+		snd_hda_codec_write_cache(codec, 0x14, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+	}
+}
+
+static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present1, present2;
+
+	present1 = snd_hda_codec_read(codec, 0x1b, 0,
+				AC_VERB_GET_PIN_SENSE, 0)
+				& AC_PINSENSE_PRESENCE;
+	present2 = snd_hda_codec_read(codec, 0x15, 0,
+				AC_VERB_GET_PIN_SENSE, 0)
+				& AC_PINSENSE_PRESENCE;
+
+	if (present1 || present2) {
+		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), AMP_IN_MUTE(0));
+		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), AMP_IN_MUTE(0));
+	} else {
+		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+				AMP_IN_MUTE(0), 0);
+		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+				AMP_IN_MUTE(0), 0);
+	}
 }
 
 static void alc663_m51va_mic_automute(struct hda_codec *codec)
@@ -14249,16 +15373,16 @@
 	unsigned int present;
 
 	present = snd_hda_codec_read(codec, 0x18, 0,
-				     AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
+			AC_VERB_GET_PIN_SENSE, 0)
+			& AC_PINSENSE_PRESENCE;
 	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+			0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
 	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+			0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
 	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
+			0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
 	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
+			0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
 }
 
 static void alc663_m51va_unsol_event(struct hda_codec *codec,
@@ -14280,6 +15404,121 @@
 	alc663_m51va_mic_automute(codec);
 }
 
+/* ***************** Mode1 ******************************/
+static void alc663_mode1_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_m51va_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_mode1_inithook(struct hda_codec *codec)
+{
+	alc663_m51va_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode2 ******************************/
+static void alc662_mode2_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc662_f5z_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc662_mode2_inithook(struct hda_codec *codec)
+{
+	alc662_f5z_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode3 ******************************/
+static void alc663_mode3_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_two_hp_m1_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_mode3_inithook(struct hda_codec *codec)
+{
+	alc663_two_hp_m1_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode4 ******************************/
+static void alc663_mode4_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_21jd_two_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_mode4_inithook(struct hda_codec *codec)
+{
+	alc663_21jd_two_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode5 ******************************/
+static void alc663_mode5_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_15jd_two_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_mode5_inithook(struct hda_codec *codec)
+{
+	alc663_15jd_two_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+/* ***************** Mode6 ******************************/
+static void alc663_mode6_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_two_hp_m2_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_mode6_inithook(struct hda_codec *codec)
+{
+	alc663_two_hp_m2_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+
 static void alc663_g71v_hp_automute(struct hda_codec *codec)
 {
 	unsigned int present;
@@ -14350,6 +15589,46 @@
 	alc662_eeepc_mic_automute(codec);
 }
 
+/* bind hp and internal speaker mute (with plug check) */
+static int alc662_ecs_master_sw_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int change;
+
+	change = snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
+					  HDA_AMP_MUTE,
+					  valp[0] ? 0 : HDA_AMP_MUTE);
+	change |= snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
+					   HDA_AMP_MUTE,
+					   valp[1] ? 0 : HDA_AMP_MUTE);
+	if (change)
+		alc262_hippo1_automute(codec);
+	return change;
+}
+
+static struct snd_kcontrol_new alc662_ecs_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = alc662_ecs_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+	},
+
+	HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("e-Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("i-Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	{ } /* end */
+};
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc662_loopbacks	alc880_loopbacks
 #endif
@@ -14372,21 +15651,67 @@
 	[ALC662_LENOVO_101E]	= "lenovo-101e",
 	[ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
 	[ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
+	[ALC662_ECS] = "ecs",
 	[ALC663_ASUS_M51VA] = "m51va",
 	[ALC663_ASUS_G71V] = "g71v",
 	[ALC663_ASUS_H13] = "h13",
 	[ALC663_ASUS_G50V] = "g50v",
+	[ALC663_ASUS_MODE1] = "asus-mode1",
+	[ALC662_ASUS_MODE2] = "asus-mode2",
+	[ALC663_ASUS_MODE3] = "asus-mode3",
+	[ALC663_ASUS_MODE4] = "asus-mode4",
+	[ALC663_ASUS_MODE5] = "asus-mode5",
+	[ALC663_ASUS_MODE6] = "asus-mode6",
 	[ALC662_AUTO]		= "auto",
 };
 
 static struct snd_pci_quirk alc662_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS G71V", ALC663_ASUS_G71V),
 	SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
 	SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS M51VA", ALC663_ASUS_G50V),
 	SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
 	SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
+	SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
+	SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
+	SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
+	SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
+	SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
+	SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
+	SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
+	SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
+	SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
+	SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
+	SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
+	SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
+		      ALC662_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
+	SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
+	SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
+	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
+		      ALC662_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0",
+					ALC662_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13),
 	SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13),
 	SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13),
@@ -14477,6 +15802,18 @@
 		.unsol_event = alc662_eeepc_ep20_unsol_event,
 		.init_hook = alc662_eeepc_ep20_inithook,
 	},
+	[ALC662_ECS] = {
+		.mixers = { alc662_ecs_mixer, alc662_capture_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc662_ecs_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.dac_nids = alc662_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc662_eeepc_unsol_event,
+		.init_hook = alc662_eeepc_inithook,
+	},
 	[ALC663_ASUS_M51VA] = {
 		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
 		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
@@ -14524,6 +15861,91 @@
 		.unsol_event = alc663_g50v_unsol_event,
 		.init_hook = alc663_g50v_inithook,
 	},
+	[ALC663_ASUS_MODE1] = {
+		.mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc663_21jd_amic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.hp_nid = 0x03,
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc663_mode1_unsol_event,
+		.init_hook = alc663_mode1_inithook,
+	},
+	[ALC662_ASUS_MODE2] = {
+		.mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc662_1bjd_amic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc662_mode2_unsol_event,
+		.init_hook = alc662_mode2_inithook,
+	},
+	[ALC663_ASUS_MODE3] = {
+		.mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc663_two_hp_amic_m1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.hp_nid = 0x03,
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc663_mode3_unsol_event,
+		.init_hook = alc663_mode3_inithook,
+	},
+	[ALC663_ASUS_MODE4] = {
+		.mixers = { alc663_asus_21jd_clfe_mixer,
+				alc662_auto_capture_mixer},
+		.init_verbs = { alc662_init_verbs,
+				alc663_21jd_amic_init_verbs},
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.hp_nid = 0x03,
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc663_mode4_unsol_event,
+		.init_hook = alc663_mode4_inithook,
+	},
+	[ALC663_ASUS_MODE5] = {
+		.mixers = { alc663_asus_15jd_clfe_mixer,
+				alc662_auto_capture_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc663_15jd_amic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.hp_nid = 0x03,
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc663_mode5_unsol_event,
+		.init_hook = alc663_mode5_inithook,
+	},
+	[ALC663_ASUS_MODE6] = {
+		.mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc663_two_hp_amic_m2_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.hp_nid = 0x03,
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc663_mode6_unsol_event,
+		.init_hook = alc663_mode6_inithook,
+	},
 };
 
 
@@ -14560,15 +15982,15 @@
 							      HDA_OUTPUT));
 			if (err < 0)
 				return err;
-			err = add_control(spec, ALC_CTL_BIND_MUTE,
+			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
 					  "Center Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 1, 2,
+					  HDA_COMPOSE_AMP_VAL(0x0e, 1, 0,
 							      HDA_INPUT));
 			if (err < 0)
 				return err;
-			err = add_control(spec, ALC_CTL_BIND_MUTE,
+			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
 					  "LFE Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 2, 2,
+					  HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
 							      HDA_INPUT));
 			if (err < 0)
 				return err;
@@ -14580,9 +16002,9 @@
 			if (err < 0)
 				return err;
 			sprintf(name, "%s Playback Switch", chname[i]);
-			err = add_control(spec, ALC_CTL_BIND_MUTE, name,
-					  HDA_COMPOSE_AMP_VAL(nid, 3, 2,
-							      HDA_INPUT));
+			err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+				HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
+						    3, 0, HDA_INPUT));
 			if (err < 0)
 				return err;
 		}
@@ -14777,7 +16199,7 @@
 
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
-	
+
 	spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
 	if (codec->vendor_id == 0x10ec0663)
 		spec->init_verbs[spec->num_init_verbs++] =
@@ -14896,6 +16318,8 @@
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
 	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+	{ .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
+	  .patch = patch_alc882 }, /* should be patch_alc883() in future */
 	{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
 	  .patch = patch_alc882 }, /* should be patch_alc883() in future */
 	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index f3da621..c461baa 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -33,10 +33,12 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_patch.h"
+#include "hda_beep.h"
 
 #define NUM_CONTROL_ALLOC	32
 #define STAC_PWR_EVENT		0x20
 #define STAC_HP_EVENT		0x30
+#define STAC_VREF_EVENT		0x40
 
 enum {
 	STAC_REF,
@@ -71,9 +73,15 @@
 };
 
 enum {
+	STAC_92HD83XXX_REF,
+	STAC_92HD83XXX_MODELS
+};
+
+enum {
 	STAC_92HD71BXX_REF,
 	STAC_DELL_M4_1,
 	STAC_DELL_M4_2,
+	STAC_HP_M4,
 	STAC_92HD71BXX_MODELS
 };
 
@@ -104,6 +112,7 @@
 	STAC_MACBOOK_PRO_V2,
 	STAC_IMAC_INTEL,
 	STAC_IMAC_INTEL_20,
+	STAC_ECS_202,
 	STAC_922X_DELL_D81,
 	STAC_922X_DELL_D82,
 	STAC_922X_DELL_M81,
@@ -130,6 +139,7 @@
 	unsigned int mic_switch: 1;
 	unsigned int alt_switch: 1;
 	unsigned int hp_detect: 1;
+	unsigned int spdif_mute: 1;
 
 	/* gpio lines */
 	unsigned int eapd_mask;
@@ -138,17 +148,22 @@
 	unsigned int gpio_data;
 	unsigned int gpio_mute;
 
+	/* stream */
+	unsigned int stream_delay;
+
 	/* analog loopback */
 	unsigned char aloopback_mask;
 	unsigned char aloopback_shift;
 
 	/* power management */
 	unsigned int num_pwrs;
+	unsigned int *pwr_mapping;
 	hda_nid_t *pwr_nids;
 	hda_nid_t *dac_list;
 
 	/* playback */
 	struct hda_input_mux *mono_mux;
+	struct hda_input_mux *amp_mux;
 	unsigned int cur_mmux;
 	struct hda_multi_out multiout;
 	hda_nid_t dac_nids[5];
@@ -162,8 +177,14 @@
 	unsigned int num_dmics;
 	hda_nid_t *dmux_nids;
 	unsigned int num_dmuxes;
+	hda_nid_t *smux_nids;
+	unsigned int num_smuxes;
+	const char **spdif_labels;
+
 	hda_nid_t dig_in_nid;
 	hda_nid_t mono_nid;
+	hda_nid_t anabeep_nid;
+	hda_nid_t digbeep_nid;
 
 	/* pin widgets */
 	hda_nid_t *pin_nids;
@@ -180,6 +201,12 @@
 	unsigned int cur_dmux[2];
 	struct hda_input_mux *input_mux;
 	unsigned int cur_mux[3];
+	struct hda_input_mux *sinput_mux;
+	unsigned int cur_smux[2];
+	unsigned int cur_amux;
+	hda_nid_t *amp_nids;
+	unsigned int num_amps;
+	unsigned int powerdown_adcs;
 
 	/* i/o switches */
 	unsigned int io_switch[2];
@@ -195,6 +222,8 @@
 	struct snd_kcontrol_new *kctl_alloc;
 	struct hda_input_mux private_dimux;
 	struct hda_input_mux private_imux;
+	struct hda_input_mux private_smux;
+	struct hda_input_mux private_amp_mux;
 	struct hda_input_mux private_mono_mux;
 };
 
@@ -215,10 +244,19 @@
 	0x0f, 0x10, 0x11
 };
 
+static hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
+	0x26, 0,
+};
+
 static hda_nid_t stac92hd73xx_adc_nids[2] = {
 	0x1a, 0x1b
 };
 
+#define DELL_M6_AMP 2
+static hda_nid_t stac92hd73xx_amp_nids[3] = {
+	0x0b, 0x0c, 0x0e
+};
+
 #define STAC92HD73XX_NUM_DMICS	2
 static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
 	0x13, 0x14, 0
@@ -237,6 +275,41 @@
 	0x20, 0x21,
 };
 
+static hda_nid_t stac92hd73xx_smux_nids[2] = {
+	0x22, 0x23,
+};
+
+#define STAC92HD83XXX_NUM_DMICS	2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+	0x11, 0x12, 0
+};
+
+#define STAC92HD81_DAC_COUNT 2
+#define STAC92HD83_DAC_COUNT 3
+static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = {
+	0x13, 0x14, 0x22,
+};
+
+static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
+	0x17, 0x18,
+};
+
+static hda_nid_t stac92hd83xxx_adc_nids[2] = {
+	0x15, 0x16,
+};
+
+static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
+	0xa, 0xb, 0xd, 0xe,
+};
+
+static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
+	0x1e, 0,
+};
+
+static unsigned int stac92hd83xxx_pwr_mapping[4] = {
+	0x03, 0x0c, 0x10, 0x40,
+};
+
 static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
 	0x0a, 0x0d, 0x0f
 };
@@ -253,6 +326,10 @@
 	0x1c,
 };
 
+static hda_nid_t stac92hd71bxx_smux_nids[2] = {
+	0x24, 0x25,
+};
+
 static hda_nid_t stac92hd71bxx_dac_nids[1] = {
 	0x10, /*0x11, */
 };
@@ -262,6 +339,10 @@
 	0x18, 0x19, 0
 };
 
+static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
+	0x22, 0
+};
+
 static hda_nid_t stac925x_adc_nids[1] = {
         0x03,
 };
@@ -299,6 +380,10 @@
         0x15, 0x16, 0x17
 };
 
+static hda_nid_t stac927x_smux_nids[1] = {
+	0x21,
+};
+
 static hda_nid_t stac927x_dac_nids[6] = {
 	0x02, 0x03, 0x04, 0x05, 0x06, 0
 };
@@ -312,6 +397,11 @@
 	0x13, 0x14, 0
 };
 
+static const char *stac927x_spdif_labels[5] = {
+	"Digital Playback", "ADAT", "Analog Mux 1",
+	"Analog Mux 2", "Analog Mux 3"
+};
+
 static hda_nid_t stac9205_adc_nids[2] = {
         0x12, 0x13
 };
@@ -324,6 +414,10 @@
 	0x1d,
 };
 
+static hda_nid_t stac9205_smux_nids[1] = {
+	0x21,
+};
+
 #define STAC9205_NUM_DMICS	2
 static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
         0x17, 0x18, 0
@@ -347,12 +441,18 @@
 static hda_nid_t stac92hd73xx_pin_nids[13] = {
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
 	0x0f, 0x10, 0x11, 0x12, 0x13,
-	0x14, 0x1e, 0x22
+	0x14, 0x22, 0x23
 };
 
-static hda_nid_t stac92hd71bxx_pin_nids[10] = {
+static hda_nid_t stac92hd83xxx_pin_nids[14] = {
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+	0x0f, 0x10, 0x11, 0x12, 0x13,
+	0x1d, 0x1e, 0x1f, 0x20
+};
+static hda_nid_t stac92hd71bxx_pin_nids[11] = {
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
 	0x0f, 0x14, 0x18, 0x19, 0x1e,
+	0x1f,
 };
 
 static hda_nid_t stac927x_pin_nids[14] = {
@@ -367,6 +467,34 @@
 	0x21, 0x22,
 };
 
+#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info
+
+static int stac92xx_amp_volume_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid = spec->amp_nids[spec->cur_amux];
+
+	kcontrol->private_value ^= get_amp_nid(kcontrol);
+	kcontrol->private_value |= nid;
+
+	return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
+}
+
+static int stac92xx_amp_volume_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid = spec->amp_nids[spec->cur_amux];
+
+	kcontrol->private_value ^= get_amp_nid(kcontrol);
+	kcontrol->private_value |= nid;
+
+	return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+}
+
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_info *uinfo)
 {
@@ -397,6 +525,58 @@
 			spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
 }
 
+static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->sinput_mux, uinfo);
+}
+
+static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
+	return 0;
+}
+
+static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_input_mux *smux = &spec->private_smux;
+	unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	int err, val;
+	hda_nid_t nid;
+
+	err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol,
+			spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]);
+	if (err < 0)
+		return err;
+
+	if (spec->spdif_mute) {
+		if (smux_idx == 0)
+			nid = spec->multiout.dig_out_nid;
+		else
+			nid = codec->slave_dig_outs[smux_idx - 1];
+		if (spec->cur_smux[smux_idx] == smux->num_items - 1)
+			val = AMP_OUT_MUTE;
+		if (smux_idx == 0)
+			nid = spec->multiout.dig_out_nid;
+		else
+			nid = codec->slave_dig_outs[smux_idx - 1];
+		/* un/mute SPDIF out */
+		snd_hda_codec_write_cache(codec, nid, 0,
+			AC_VERB_SET_AMP_GAIN_MUTE, val);
+	}
+	return 0;
+}
+
 static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -452,6 +632,41 @@
 				     spec->mono_nid, &spec->cur_mmux);
 }
 
+static int stac92xx_amp_mux_enum_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->amp_mux, uinfo);
+}
+
+static int stac92xx_amp_mux_enum_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->cur_amux;
+	return 0;
+}
+
+static int stac92xx_amp_mux_enum_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	struct snd_kcontrol *ctl =
+		snd_hda_find_mixer_ctl(codec, "Amp Capture Volume");
+	if (!ctl)
+		return -EINVAL;
+
+	snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+
+	return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol,
+				     0, &spec->cur_amux);
+}
+
 #define stac92xx_aloopback_info snd_ctl_boolean_mono_info
 
 static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
@@ -546,8 +761,8 @@
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
 	/* setup audio connections */
 	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
+	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02},
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},
 	/* setup adcs to point to mixer */
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -628,6 +843,19 @@
 	{}
 };
 
+static struct hda_verb stac92hd83xxx_core_init[] = {
+	/* start of config #1 */
+	{ 0xe, AC_VERB_SET_CONNECT_SEL, 0x3},
+
+	/* start of config #2 */
+	{ 0xa, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{ 0xb, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{ 0xd, AC_VERB_SET_CONNECT_SEL, 0x1},
+
+	/* power state controls amps */
+	{ 0x01, AC_VERB_SET_EAPD, 1 << 2},
+};
+
 static struct hda_verb stac92hd71bxx_core_init[] = {
 	/* set master volume and direct control */
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -690,12 +918,16 @@
 static struct hda_verb stac927x_core_init[] = {
 	/* set master volume and direct control */	
 	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	/* enable analog pc beep path */
+	{ 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
 	{}
 };
 
 static struct hda_verb stac9205_core_init[] = {
 	/* set master volume and direct control */	
 	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+	/* enable analog pc beep path */
+	{ 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
 	{}
 };
 
@@ -709,6 +941,31 @@
 		.put = stac92xx_mono_mux_enum_put, \
 	}
 
+#define STAC_AMP_MUX \
+	{ \
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name = "Amp Selector Capture Switch", \
+		.count = 1, \
+		.info = stac92xx_amp_mux_enum_info, \
+		.get = stac92xx_amp_mux_enum_get, \
+		.put = stac92xx_amp_mux_enum_put, \
+	}
+
+#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \
+	{ \
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name = xname, \
+		.index = 0, \
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+		.info = stac92xx_amp_volume_info, \
+		.get = stac92xx_amp_volume_get, \
+		.put = stac92xx_amp_volume_put, \
+		.tlv = { .c = snd_hda_mixer_amp_tlv }, \
+		.private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
+	}
+
 #define STAC_INPUT_SOURCE(cnt) \
 	{ \
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -736,11 +993,28 @@
 	STAC_INPUT_SOURCE(1),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
 	{ } /* end */
 };
 
+#define DELL_M6_MIXER 6
 static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
+	/* start of config #1 */
+	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
+
+	/* start of config #2 */
+	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
@@ -749,20 +1023,6 @@
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
 
-	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
-	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
 	{ } /* end */
 };
 
@@ -818,19 +1078,46 @@
 	{ } /* end */
 };
 
+
+static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT),
+	HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT),
+
+	/*
+	HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
+	*/
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
 	STAC_INPUT_SOURCE(2),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
-
+	/* analog pc-beep replaced with digital beep support */
+	/*
 	HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
+	*/
 
 	HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
 	HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
@@ -843,11 +1130,9 @@
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 	{ } /* end */
 };
 
@@ -855,7 +1140,6 @@
 	STAC_INPUT_SOURCE(1),
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
 	{ } /* end */
 };
 
@@ -865,12 +1149,9 @@
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT),
-
 	{ } /* end */
 };
 
@@ -879,11 +1160,9 @@
 	STAC_INPUT_SOURCE(2),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT),
 	{ } /* end */
 };
 
@@ -894,15 +1173,12 @@
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT),
 
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT),
 	{ } /* end */
 };
 
@@ -915,6 +1191,15 @@
 	.put = stac92xx_dmux_enum_put,
 };
 
+static struct snd_kcontrol_new stac_smux_mixer = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC958 Playback Source",
+	/* count set later */
+	.info = stac92xx_smux_enum_info,
+	.get = stac92xx_smux_enum_get,
+	.put = stac92xx_smux_enum_put,
+};
+
 static const char *slave_vols[] = {
 	"Front Playback Volume",
 	"Surround Playback Volume",
@@ -966,6 +1251,22 @@
 		if (err < 0)
 			return err;
 	}
+	if (spec->num_smuxes > 0) {
+		int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid);
+		struct hda_input_mux *smux = &spec->private_smux;
+		/* check for mute support on SPDIF out */
+		if (wcaps & AC_WCAP_OUT_AMP) {
+			smux->items[smux->num_items].label = "Off";
+			smux->items[smux->num_items].index = 0;
+			smux->num_items++;
+			spec->spdif_mute = 1;
+		}
+		stac_smux_mixer.count = spec->num_smuxes;
+		err = snd_ctl_add(codec->bus->card,
+				  snd_ctl_new1(&stac_smux_mixer, codec));
+		if (err < 0)
+			return err;
+	}
 
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
@@ -977,7 +1278,7 @@
 			return err;
 		spec->multiout.share_spdif = 1;
 	}
-	if (spec->dig_in_nid) {
+	if (spec->dig_in_nid && (!spec->gpio_dir & 0x01)) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
 		if (err < 0)
 			return err;
@@ -1325,40 +1626,65 @@
 	{} /* terminator */
 };
 
-static unsigned int ref92hd71bxx_pin_configs[10] = {
+static unsigned int ref92hd83xxx_pin_configs[14] = {
+	0x02214030, 0x02211010, 0x02a19020, 0x02170130,
+	0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
+	0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0,
+	0x01451160, 0x98560170,
+};
+
+static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
+	[STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
+};
+
+static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
+	[STAC_92HD83XXX_REF] = "ref",
+};
+
+static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
+	/* SigmaTel reference board */
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+		      "DFI LanParty", STAC_92HD71BXX_REF),
+};
+
+static unsigned int ref92hd71bxx_pin_configs[11] = {
 	0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
 	0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
-	0x90a000f0, 0x01452050,
+	0x90a000f0, 0x01452050, 0x01452050,
 };
 
-static unsigned int dell_m4_1_pin_configs[10] = {
+static unsigned int dell_m4_1_pin_configs[11] = {
 	0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
 	0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
-	0x40f000f0, 0x4f0000f0,
+	0x40f000f0, 0x4f0000f0, 0x4f0000f0,
 };
 
-static unsigned int dell_m4_2_pin_configs[10] = {
+static unsigned int dell_m4_2_pin_configs[11] = {
 	0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
 	0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
-	0x40f000f0, 0x044413b0,
+	0x40f000f0, 0x044413b0, 0x044413b0,
 };
 
 static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
 	[STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
 	[STAC_DELL_M4_1]	= dell_m4_1_pin_configs,
 	[STAC_DELL_M4_2]	= dell_m4_2_pin_configs,
+	[STAC_HP_M4]		= NULL,
 };
 
 static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
 	[STAC_92HD71BXX_REF] = "ref",
 	[STAC_DELL_M4_1] = "dell-m4-1",
 	[STAC_DELL_M4_2] = "dell-m4-2",
+	[STAC_HP_M4] = "hp-m4",
 };
 
 static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
 	/* SigmaTel reference board */
 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
 		      "DFI LanParty", STAC_92HD71BXX_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
+				"unknown HP", STAC_HP_M4),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
 				"unknown Dell", STAC_DELL_M4_1),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -1477,6 +1803,11 @@
 	0x400000fc, 0x400000fb,
 };
 
+static unsigned int ecs202_pin_configs[10] = {
+	0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
+	0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
+	0x9037012e, 0x40e000f2,
+};
 
 static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
 	[STAC_D945_REF] = ref922x_pin_configs,
@@ -1495,6 +1826,7 @@
 	[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,
+	[STAC_ECS_202] = ecs202_pin_configs,
 	[STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
 	[STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,	
 	[STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
@@ -1518,6 +1850,7 @@
 	[STAC_MACBOOK_PRO_V2]	= "macbook-pro",
 	[STAC_IMAC_INTEL] = "imac-intel",
 	[STAC_IMAC_INTEL_20] = "imac-intel-20",
+	[STAC_ECS_202] = "ecs202",
 	[STAC_922X_DELL_D81] = "dell-d81",
 	[STAC_922X_DELL_D82] = "dell-d82",
 	[STAC_922X_DELL_M81] = "dell-m81",
@@ -1604,6 +1937,33 @@
 		      "unknown Dell", STAC_922X_DELL_D81),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
 		      "Dell XPS M1210", STAC_922X_DELL_M82),
+	/* ECS/PC Chips boards */
+	SND_PCI_QUIRK(0x1019, 0x2144,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2608,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2633,
+		      "ECS/PC chips P17G/1333", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2811,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2812,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2813,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2814,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2815,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2816,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2817,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2818,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2819,
+		      "ECS/PC chips", STAC_ECS_202),
+	SND_PCI_QUIRK(0x1019, 0x2820,
+		      "ECS/PC chips", STAC_ECS_202),
 	{} /* terminator */
 };
 
@@ -1867,6 +2227,8 @@
 				      struct snd_pcm_substream *substream)
 {
 	struct sigmatel_spec *spec = codec->spec;
+	if (spec->stream_delay)
+		msleep(spec->stream_delay);
 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
 					     hinfo);
 }
@@ -1930,9 +2292,14 @@
 					struct snd_pcm_substream *substream)
 {
 	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid = spec->adc_nids[substream->number];
 
-	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
-                                   stream_tag, 0, format);
+	if (spec->powerdown_adcs) {
+		msleep(40);
+		snd_hda_codec_write_cache(codec, nid, 0,
+			AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+	}
+	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
 	return 0;
 }
 
@@ -1941,8 +2308,12 @@
 					struct snd_pcm_substream *substream)
 {
 	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid = spec->adc_nids[substream->number];
 
-	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
+	snd_hda_codec_cleanup_stream(codec, nid);
+	if (spec->powerdown_adcs)
+		snd_hda_codec_write_cache(codec, nid, 0,
+			AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 	return 0;
 }
 
@@ -2193,6 +2564,8 @@
 	STAC_CTL_WIDGET_VOL,
 	STAC_CTL_WIDGET_MUTE,
 	STAC_CTL_WIDGET_MONO_MUX,
+	STAC_CTL_WIDGET_AMP_MUX,
+	STAC_CTL_WIDGET_AMP_VOL,
 	STAC_CTL_WIDGET_HP_SWITCH,
 	STAC_CTL_WIDGET_IO_SWITCH,
 	STAC_CTL_WIDGET_CLFE_SWITCH
@@ -2202,13 +2575,16 @@
 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
 	STAC_MONO_MUX,
+	STAC_AMP_MUX,
+	STAC_AMP_VOL(NULL, 0, 0, 0, 0),
 	STAC_CODEC_HP_SWITCH(NULL),
 	STAC_CODEC_IO_SWITCH(NULL, 0),
 	STAC_CODEC_CLFE_SWITCH(NULL, 0),
 };
 
 /* add dynamic controls */
-static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
+static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type,
+		int idx, const char *name, unsigned long val)
 {
 	struct snd_kcontrol_new *knew;
 
@@ -2228,6 +2604,7 @@
 
 	knew = &spec->kctl_alloc[spec->num_kctl_used];
 	*knew = stac92xx_control_templates[type];
+	knew->index = idx;
 	knew->name = kstrdup(name, GFP_KERNEL);
 	if (! knew->name)
 		return -ENOMEM;
@@ -2236,6 +2613,14 @@
 	return 0;
 }
 
+
+/* add dynamic controls */
+static int stac92xx_add_control(struct sigmatel_spec *spec, int type,
+		const char *name, unsigned long val)
+{
+	return stac92xx_add_control_idx(spec, type, 0, name, val);
+}
+
 /* flag inputs as additional dynamic lineouts */
 static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
 {
@@ -2467,6 +2852,10 @@
 		}
 	}
 
+	if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
+			cfg->hp_outs && !spec->multiout.hp_nid)
+		spec->multiout.hp_nid = nid;
+
 	if (cfg->hp_outs > 1) {
 		err = stac92xx_add_control(spec,
 			STAC_CTL_WIDGET_HP_SWITCH,
@@ -2579,8 +2968,8 @@
 }
 
 /* labels for mono mux outputs */
-static const char *stac92xx_mono_labels[3] = {
-	"DAC0", "DAC1", "Mixer"
+static const char *stac92xx_mono_labels[4] = {
+	"DAC0", "DAC1", "Mixer", "DAC2"
 };
 
 /* create mono mux for mono out on capable codecs */
@@ -2609,6 +2998,116 @@
 				"Mono Mux", spec->mono_nid);
 }
 
+/* labels for amp mux outputs */
+static const char *stac92xx_amp_labels[3] = {
+	"Front Microphone", "Microphone", "Line In"
+};
+
+/* create amp out controls mux on capable codecs */
+static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_input_mux *amp_mux = &spec->private_amp_mux;
+	int i, err;
+
+	for (i = 0; i < spec->num_amps; i++) {
+		amp_mux->items[amp_mux->num_items].label =
+					stac92xx_amp_labels[i];
+		amp_mux->items[amp_mux->num_items].index = i;
+		amp_mux->num_items++;
+	}
+
+	if (spec->num_amps > 1) {
+		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX,
+			"Amp Selector Capture Switch", 0);
+		if (err < 0)
+			return err;
+	}
+	return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL,
+		"Amp Capture Volume",
+		HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT));
+}
+
+
+/* create PC beep volume controls */
+static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
+						hda_nid_t nid)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+	int err;
+
+	/* check for mute support for the the amp */
+	if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
+		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+			"PC Beep Playback Switch",
+			HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
+			if (err < 0)
+				return err;
+	}
+
+	/* check to see if there is volume support for the amp */
+	if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
+			"PC Beep Playback Volume",
+			HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
+			if (err < 0)
+				return err;
+	}
+	return 0;
+}
+
+static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int wcaps, nid, i, err = 0;
+
+	for (i = 0; i < spec->num_muxes; i++) {
+		nid = spec->mux_nids[i];
+		wcaps = get_wcaps(codec, nid);
+
+		if (wcaps & AC_WCAP_OUT_AMP) {
+			err = stac92xx_add_control_idx(spec,
+				STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume",
+				HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+};
+
+static const char *stac92xx_spdif_labels[3] = {
+	"Digital Playback", "Analog Mux 1", "Analog Mux 2",
+};
+
+static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct hda_input_mux *spdif_mux = &spec->private_smux;
+	const char **labels = spec->spdif_labels;
+	int i, num_cons;
+	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+
+	num_cons = snd_hda_get_connections(codec,
+				spec->smux_nids[0],
+				con_lst,
+				HDA_MAX_NUM_INPUTS);
+	if (!num_cons)
+		return -EINVAL;
+
+	if (!labels)
+		labels = stac92xx_spdif_labels;
+
+	for (i = 0; i < num_cons; i++) {
+		spdif_mux->items[spdif_mux->num_items].label = labels[i];
+		spdif_mux->items[spdif_mux->num_items].index = i;
+		spdif_mux->num_items++;
+	}
+
+	return 0;
+}
+
 /* labels for dmic mux inputs */
 static const char *stac92xx_dmic_labels[5] = {
 	"Analog Inputs", "Digital Mic 1", "Digital Mic 2",
@@ -2656,16 +3155,19 @@
 			}
 		continue;
 found:
-		wcaps = get_wcaps(codec, nid);
+		wcaps = get_wcaps(codec, nid) &
+			(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
 
-		if (wcaps & AC_WCAP_OUT_AMP) {
+		if (wcaps) {
 			sprintf(name, "%s Capture Volume",
 				stac92xx_dmic_labels[dimux->num_items]);
 
 			err = stac92xx_add_control(spec,
 				STAC_CTL_WIDGET_VOL,
 				name,
-				HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+				HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+				(wcaps & AC_WCAP_OUT_AMP) ?
+				HDA_OUTPUT : HDA_INPUT));
 			if (err < 0)
 				return err;
 		}
@@ -2789,8 +3291,8 @@
 		hp_speaker_swap = 1;
 	}
 	if (spec->autocfg.mono_out_pin) {
-		int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
-				& AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
+		int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
+			(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
 		u32 caps = query_amp_caps(codec,
 				spec->autocfg.mono_out_pin, dir);
 		hda_nid_t conn_list[1];
@@ -2812,21 +3314,26 @@
 						!(wcaps & AC_WCAP_LR_SWAP))
 					spec->mono_nid = conn_list[0];
 		}
-		/* all mono outs have a least a mute/unmute switch */
-		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
-			"Mono Playback Switch",
-			HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
-					1, 0, dir));
-		if (err < 0)
-			return err;
-		/* check to see if there is volume support for the amp */
-		if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
-			err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
-				"Mono Playback Volume",
-				HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
-					1, 0, dir));
+		if (dir) {
+			hda_nid_t nid = spec->autocfg.mono_out_pin;
+
+			/* most mono outs have a least a mute/unmute switch */
+			dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
+			err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+				"Mono Playback Switch",
+				HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
 			if (err < 0)
 				return err;
+			/* check for volume support for the amp */
+			if ((caps & AC_AMPCAP_NUM_STEPS)
+					>> AC_AMPCAP_NUM_STEPS_SHIFT) {
+				err = stac92xx_add_control(spec,
+					STAC_CTL_WIDGET_VOL,
+					"Mono Playback Volume",
+				HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
+				if (err < 0)
+					return err;
+			}
 		}
 
 		stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
@@ -2844,6 +3351,28 @@
 	if (err < 0)
 		return err;
 
+	/* setup analog beep controls */
+	if (spec->anabeep_nid > 0) {
+		err = stac92xx_auto_create_beep_ctls(codec,
+			spec->anabeep_nid);
+		if (err < 0)
+			return err;
+	}
+
+	/* setup digital beep controls and input device */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+	if (spec->digbeep_nid > 0) {
+		hda_nid_t nid = spec->digbeep_nid;
+
+		err = stac92xx_auto_create_beep_ctls(codec, nid);
+		if (err < 0)
+			return err;
+		err = snd_hda_attach_beep_device(codec, nid);
+		if (err < 0)
+			return err;
+	}
+#endif
+
 	if (hp_speaker_swap == 1) {
 		/* Restore the hp_outs and line_outs */
 		memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
@@ -2872,11 +3401,25 @@
 		if (err < 0)
 			return err;
 	}
-
-	if (spec->num_dmics > 0)
+	if (spec->num_amps > 0) {
+		err = stac92xx_auto_create_amp_output_ctls(codec);
+		if (err < 0)
+			return err;
+	}
+	if (spec->num_dmics > 0 && !spec->dinput_mux)
 		if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
 						&spec->autocfg)) < 0)
 			return err;
+	if (spec->num_muxes > 0) {
+		err = stac92xx_auto_create_mux_input_ctls(codec);
+		if (err < 0)
+			return err;
+	}
+	if (spec->num_smuxes > 0) {
+		err = stac92xx_auto_create_spdif_mux_ctls(codec);
+		if (err < 0)
+			return err;
+	}
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 	if (spec->multiout.max_channels > 2)
@@ -2884,17 +3427,17 @@
 
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = dig_out;
-	if (spec->autocfg.dig_in_pin)
+	if (dig_in && spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = dig_in;
 
 	if (spec->kctl_alloc)
 		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
 	spec->input_mux = &spec->private_imux;
-	if (!spec->dinput_mux)
-		spec->dinput_mux = &spec->private_dimux;
+	spec->dinput_mux = &spec->private_dimux;
+	spec->sinput_mux = &spec->private_smux;
 	spec->mono_mux = &spec->private_mono_mux;
-
+	spec->amp_mux = &spec->private_amp_mux;
 	return 1;
 }
 
@@ -3074,6 +3617,12 @@
 
 	snd_hda_sequence_write(codec, spec->init);
 
+	/* power down adcs initially */
+	if (spec->powerdown_adcs)
+		for (i = 0; i < spec->num_adcs; i++)
+			snd_hda_codec_write_cache(codec,
+				spec->adc_nids[i], 0,
+				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 	/* set up pins */
 	if (spec->hp_detect) {
 		/* Enable unsolicited responses on the HP widget */
@@ -3095,7 +3644,12 @@
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = cfg->input_pins[i];
 		if (nid) {
-			unsigned int pinctl = AC_PINCTL_IN_EN;
+			unsigned int pinctl = snd_hda_codec_read(codec, nid,
+				0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+			/* if PINCTL already set then skip */
+			if (pinctl & AC_PINCAP_IN)
+				continue;
+			pinctl = AC_PINCTL_IN_EN;
 			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
 				pinctl |= stac92xx_get_vref(codec, nid);
 			stac92xx_auto_set_pinctl(codec, nid, pinctl);
@@ -3158,6 +3712,7 @@
 		kfree(spec->bios_pin_configs);
 
 	kfree(spec);
+	snd_hda_detach_beep_device(codec);
 }
 
 static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -3279,7 +3834,12 @@
 	val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
 							& 0x000000ff;
 	presence = get_hp_pin_presence(codec, nid);
-	idx = 1 << idx;
+
+	/* several codecs have two power down bits */
+	if (spec->pwr_mapping)
+		idx = spec->pwr_mapping[idx];
+	else
+		idx = 1 << idx;
 
 	if (presence)
 		val &= ~idx;
@@ -3295,13 +3855,22 @@
 	struct sigmatel_spec *spec = codec->spec;
 	int idx = res >> 26 & 0x0f;
 
-	switch ((res >> 26) & 0x30) {
+	switch ((res >> 26) & 0x70) {
 	case STAC_HP_EVENT:
 		stac92xx_hp_detect(codec, res);
 		/* fallthru */
 	case STAC_PWR_EVENT:
 		if (spec->num_pwrs > 0)
 			stac92xx_pin_sense(codec, idx);
+		break;
+	case STAC_VREF_EVENT: {
+		int data = snd_hda_codec_read(codec, codec->afg, 0,
+			AC_VERB_GET_GPIO_DATA, 0);
+		/* toggle VREF state based on GPIOx status */
+		snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
+			!!(data & (1 << idx)));
+		break;
+		}
 	}
 }
 
@@ -3478,9 +4047,9 @@
 	.num_items = 4,
 	.items = {
 		{ "Analog Inputs", 0x0b },
-		{ "CD", 0x08 },
 		{ "Digital Mic 1", 0x09 },
 		{ "Digital Mic 2", 0x0a },
+		{ "CD", 0x08 },
 	}
 };
 
@@ -3495,6 +4064,7 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
+	codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
 	spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
 	spec->pin_nids = stac92hd73xx_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec,
@@ -3527,17 +4097,14 @@
 
 	switch (spec->multiout.num_dacs) {
 	case 0x3: /* 6 Channel */
-		spec->multiout.hp_nid = 0x17;
 		spec->mixer = stac92hd73xx_6ch_mixer;
 		spec->init = stac92hd73xx_6ch_core_init;
 		break;
 	case 0x4: /* 8 Channel */
-		spec->multiout.hp_nid = 0x18;
 		spec->mixer = stac92hd73xx_8ch_mixer;
 		spec->init = stac92hd73xx_8ch_core_init;
 		break;
 	case 0x5: /* 10 Channel */
-		spec->multiout.hp_nid = 0x19;
 		spec->mixer = stac92hd73xx_10ch_mixer;
 		spec->init = stac92hd73xx_10ch_core_init;
 	};
@@ -3546,27 +4113,34 @@
 	spec->aloopback_mask = 0x01;
 	spec->aloopback_shift = 8;
 
+	spec->digbeep_nid = 0x1c;
 	spec->mux_nids = stac92hd73xx_mux_nids;
 	spec->adc_nids = stac92hd73xx_adc_nids;
 	spec->dmic_nids = stac92hd73xx_dmic_nids;
 	spec->dmux_nids = stac92hd73xx_dmux_nids;
+	spec->smux_nids = stac92hd73xx_smux_nids;
+	spec->amp_nids = stac92hd73xx_amp_nids;
+	spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids);
 
 	spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
 	spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
-	spec->dinput_mux = &stac92hd73xx_dmux;
-	/* GPIO0 High = Enable EAPD */
-	spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
-	spec->gpio_data = 0x01;
+	memcpy(&spec->private_dimux, &stac92hd73xx_dmux,
+			sizeof(stac92hd73xx_dmux));
 
 	switch (spec->board_config) {
 	case STAC_DELL_M6:
 		spec->init = dell_eq_core_init;
+		spec->num_smuxes = 0;
+		spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
+		spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
+		spec->num_amps = 1;
 		switch (codec->subsystem_id) {
 		case 0x1028025e: /* Analog Mics */
 		case 0x1028025f:
 			stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
 			spec->num_dmics = 0;
+			spec->private_dimux.num_items = 1;
 			break;
 		case 0x10280271: /* Digital Mics */
 		case 0x10280272:
@@ -3576,23 +4150,32 @@
 		case 0x10280255:
 			stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
 			spec->num_dmics = 1;
+			spec->private_dimux.num_items = 2;
 			break;
 		case 0x10280256: /* Both */
 		case 0x10280057:
 			stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
 			stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
 			spec->num_dmics = 1;
+			spec->private_dimux.num_items = 2;
 			break;
 		}
 		break;
 	default:
 		spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+		spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
 	}
+	if (spec->board_config > STAC_92HD73XX_REF) {
+		/* GPIO0 High = Enable EAPD */
+		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+		spec->gpio_data = 0x01;
+	}
+	spec->dinput_mux = &spec->private_dimux;
 
 	spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
 	spec->pwr_nids = stac92hd73xx_pwr_nids;
 
-	err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
+	err = stac92xx_parse_auto_config(codec, 0x25, 0x27);
 
 	if (!err) {
 		if (spec->board_config < 0) {
@@ -3614,6 +4197,136 @@
 	return 0;
 }
 
+static struct hda_input_mux stac92hd83xxx_dmux = {
+	.num_items = 3,
+	.items = {
+		{ "Analog Inputs", 0x03 },
+		{ "Digital Mic 1", 0x04 },
+		{ "Digital Mic 2", 0x05 },
+	}
+};
+
+static int patch_stac92hd83xxx(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec;
+	int err;
+
+	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
+	spec->mono_nid = 0x19;
+	spec->digbeep_nid = 0x21;
+	spec->dmic_nids = stac92hd83xxx_dmic_nids;
+	spec->dmux_nids = stac92hd83xxx_dmux_nids;
+	spec->adc_nids = stac92hd83xxx_adc_nids;
+	spec->pwr_nids = stac92hd83xxx_pwr_nids;
+	spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
+	spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
+	spec->multiout.dac_nids = stac92hd83xxx_dac_nids;
+
+	spec->init = stac92hd83xxx_core_init;
+	switch (codec->vendor_id) {
+	case 0x111d7605:
+		spec->multiout.num_dacs = STAC92HD81_DAC_COUNT;
+		break;
+	default:
+		spec->num_pwrs--;
+		spec->init++; /* switch to config #2 */
+		spec->multiout.num_dacs = STAC92HD83_DAC_COUNT;
+	}
+
+	spec->mixer = stac92hd83xxx_mixer;
+	spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
+	spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids);
+	spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
+	spec->num_dmics = STAC92HD83XXX_NUM_DMICS;
+	spec->dinput_mux = &stac92hd83xxx_dmux;
+	spec->pin_nids = stac92hd83xxx_pin_nids;
+	spec->board_config = snd_hda_check_board_config(codec,
+							STAC_92HD83XXX_MODELS,
+							stac92hd83xxx_models,
+							stac92hd83xxx_cfg_tbl);
+again:
+	if (spec->board_config < 0) {
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+			" STAC92HD83XXX, using BIOS defaults\n");
+		err = stac92xx_save_bios_config_regs(codec);
+		if (err < 0) {
+			stac92xx_free(codec);
+			return err;
+		}
+		spec->pin_configs = spec->bios_pin_configs;
+	} else {
+		spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config];
+		stac92xx_set_config_regs(codec);
+	}
+
+	err = stac92xx_parse_auto_config(codec, 0x1d, 0);
+	if (!err) {
+		if (spec->board_config < 0) {
+			printk(KERN_WARNING "hda_codec: No auto-config is "
+			       "available, default to model=ref\n");
+			spec->board_config = STAC_92HD83XXX_REF;
+			goto again;
+		}
+		err = -EINVAL;
+	}
+
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
+	}
+
+	codec->patch_ops = stac92xx_patch_ops;
+
+	return 0;
+}
+
+#ifdef SND_HDA_NEEDS_RESUME
+static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+	snd_hda_codec_write_cache(codec, codec->afg, 0,
+		AC_VERB_SET_POWER_STATE, pwr);
+
+	msleep(1);
+	for (i = 0; i < spec->num_adcs; i++) {
+		snd_hda_codec_write_cache(codec,
+			spec->adc_nids[i], 0,
+			AC_VERB_SET_POWER_STATE, pwr);
+	}
+};
+
+static int stac92hd71xx_resume(struct hda_codec *codec)
+{
+	stac92hd71xx_set_power_state(codec, AC_PWRST_D0);
+	return stac92xx_resume(codec);
+}
+
+static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+	stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
+	return 0;
+};
+
+#endif
+
+static struct hda_codec_ops stac92hd71bxx_patch_ops = {
+	.build_controls = stac92xx_build_controls,
+	.build_pcms = stac92xx_build_pcms,
+	.init = stac92xx_init,
+	.free = stac92xx_free,
+	.unsol_event = stac92xx_unsol_event,
+#ifdef SND_HDA_NEEDS_RESUME
+	.resume = stac92hd71xx_resume,
+	.suspend = stac92hd71xx_suspend,
+#endif
+};
+
 static int patch_stac92hd71bxx(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec;
@@ -3624,6 +4337,7 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
+	codec->patch_ops = stac92xx_patch_ops;
 	spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
 	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
 	spec->pin_nids = stac92hd71bxx_pin_nids;
@@ -3653,8 +4367,28 @@
 	case 0x111d76b5:
 		spec->mixer = stac92hd71bxx_mixer;
 		spec->init = stac92hd71bxx_core_init;
+		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
 		break;
 	case 0x111d7608: /* 5 Port with Analog Mixer */
+		switch (codec->subsystem_id) {
+		case 0x103c361a:
+			/* Enable VREF power saving on GPIO1 detect */
+			snd_hda_codec_write(codec, codec->afg, 0,
+				AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
+			snd_hda_codec_write_cache(codec, codec->afg, 0,
+					AC_VERB_SET_UNSOLICITED_ENABLE,
+					(AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+			spec->gpio_mask |= 0x02;
+			break;
+		}
+		if ((codec->revision_id & 0xf) == 0 ||
+				(codec->revision_id & 0xf) == 1) {
+#ifdef SND_HDA_NEEDS_RESUME
+			codec->patch_ops = stac92hd71bxx_patch_ops;
+#endif
+			spec->stream_delay = 40; /* 40 milliseconds */
+		}
+
 		/* no output amps */
 		spec->num_pwrs = 0;
 		spec->mixer = stac92hd71bxx_analog_mixer;
@@ -3664,32 +4398,60 @@
 		stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
 		break;
 	case 0x111d7603: /* 6 Port with Analog Mixer */
+		if ((codec->revision_id & 0xf) == 1) {
+#ifdef SND_HDA_NEEDS_RESUME
+			codec->patch_ops = stac92hd71bxx_patch_ops;
+#endif
+			spec->stream_delay = 40; /* 40 milliseconds */
+		}
+
 		/* no output amps */
 		spec->num_pwrs = 0;
 		/* fallthru */
 	default:
 		spec->mixer = stac92hd71bxx_analog_mixer;
 		spec->init = stac92hd71bxx_analog_core_init;
+		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
 	}
 
 	spec->aloopback_mask = 0x20;
 	spec->aloopback_shift = 0;
 
-	/* GPIO0 High = EAPD */
-	spec->gpio_mask = 0x01;
-	spec->gpio_dir = 0x01;
-	spec->gpio_data = 0x01;
+	if (spec->board_config > STAC_92HD71BXX_REF) {
+		/* GPIO0 = EAPD */
+		spec->gpio_mask = 0x01;
+		spec->gpio_dir = 0x01;
+		spec->gpio_data = 0x01;
+	}
 
+	spec->powerdown_adcs = 1;
+	spec->digbeep_nid = 0x26;
 	spec->mux_nids = stac92hd71bxx_mux_nids;
 	spec->adc_nids = stac92hd71bxx_adc_nids;
 	spec->dmic_nids = stac92hd71bxx_dmic_nids;
 	spec->dmux_nids = stac92hd71bxx_dmux_nids;
+	spec->smux_nids = stac92hd71bxx_smux_nids;
 	spec->pwr_nids = stac92hd71bxx_pwr_nids;
 
 	spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
-	spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
-	spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+
+	switch (spec->board_config) {
+	case STAC_HP_M4:
+		spec->num_dmics = 0;
+		spec->num_smuxes = 0;
+		spec->num_dmuxes = 0;
+
+		/* enable internal microphone */
+		stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+		stac92xx_auto_set_pinctl(codec, 0x0e,
+			AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
+		break;
+	default:
+		spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
+		spec->num_smuxes = ARRAY_SIZE(stac92hd71bxx_smux_nids);
+		spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+	};
 
 	spec->multiout.num_dacs = 1;
 	spec->multiout.hp_nid = 0x11;
@@ -3711,8 +4473,6 @@
 		return err;
 	}
 
-	codec->patch_ops = stac92xx_patch_ops;
-
 	return 0;
 };
 
@@ -3854,10 +4614,14 @@
 		stac92xx_set_config_regs(codec);
 	}
 
+	spec->digbeep_nid = 0x23;
 	spec->adc_nids = stac927x_adc_nids;
 	spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
 	spec->mux_nids = stac927x_mux_nids;
 	spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+	spec->smux_nids = stac927x_smux_nids;
+	spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids);
+	spec->spdif_labels = stac927x_spdif_labels;
 	spec->dac_list = stac927x_dac_nids;
 	spec->multiout.dac_nids = spec->dac_nids;
 
@@ -3900,9 +4664,11 @@
 		spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
 		break;
 	default:
-		/* GPIO0 High = Enable EAPD */
-		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
-		spec->gpio_data = 0x01;
+		if (spec->board_config > STAC_D965_REF) {
+			/* GPIO0 High = Enable EAPD */
+			spec->eapd_mask = spec->gpio_mask = 0x01;
+			spec->gpio_dir = spec->gpio_data = 0x01;
+		}
 		spec->num_dmics = 0;
 
 		spec->init = stac927x_core_init;
@@ -3974,10 +4740,13 @@
 		stac92xx_set_config_regs(codec);
 	}
 
+	spec->digbeep_nid = 0x23;
 	spec->adc_nids = stac9205_adc_nids;
 	spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
 	spec->mux_nids = stac9205_mux_nids;
 	spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
+	spec->smux_nids = stac9205_smux_nids;
+	spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);
 	spec->dmic_nids = stac9205_dmic_nids;
 	spec->num_dmics = STAC9205_NUM_DMICS;
 	spec->dmux_nids = stac9205_dmux_nids;
@@ -4013,6 +4782,9 @@
 		 */
 		spec->gpio_data = 0x01;
 		break;
+	case STAC_9205_REF:
+		/* SPDIF-In enabled */
+		break;
 	default:
 		/* GPIO0 High = EAPD */
 		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
@@ -4332,6 +5104,8 @@
  	{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
  	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
 	{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
+	{ .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
 	{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
 	{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index e7e4352..63e4871 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1,10 +1,10 @@
 /*
  * Universal Interface for Intel High Definition Audio Codec
  *
- * HD audio interface patch for VIA VT1708 codec
+ * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
  *
- * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com>
- *                    Takashi Iwai <tiwai@suse.de>
+ * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
+ *			   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
@@ -29,6 +29,13 @@
 /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
 /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization      */
 /* 2007-09-17  Lydia Wang  Add VT1708B codec support                        */
+/* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
+/* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
+/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support        */
+/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin             */
+/* 2008-04-09  Lydia Wang  Add Independent HP feature                        */
+/* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
+/* 2008-09-15  Logan Li    Add VT1708S Mic Boost workaround/backdoor	     */
 /*                                                                           */
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
@@ -37,6 +44,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <sound/core.h>
+#include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_patch.h"
@@ -53,6 +61,8 @@
 #define VT1708_DIGOUT_NID	0x14
 #define VT1708_DIGIN_NID	0x16
 #define VT1708_DIGIN_PIN	0x26
+#define VT1708_HP_PIN_NID	0x20
+#define VT1708_CD_PIN_NID	0x24
 
 #define VT1709_HP_DAC_NID	0x28
 #define VT1709_DIGOUT_NID	0x13
@@ -64,12 +74,64 @@
 #define VT1708B_DIGIN_NID	0x15
 #define VT1708B_DIGIN_PIN	0x21
 
+#define VT1708S_HP_NID		0x25
+#define VT1708S_DIGOUT_NID	0x12
+
+#define VT1702_HP_NID		0x17
+#define VT1702_DIGOUT_NID	0x11
+
 #define IS_VT1708_VENDORID(x)		((x) >= 0x11061708 && (x) <= 0x1106170b)
 #define IS_VT1709_10CH_VENDORID(x)	((x) >= 0x1106e710 && (x) <= 0x1106e713)
 #define IS_VT1709_6CH_VENDORID(x)	((x) >= 0x1106e714 && (x) <= 0x1106e717)
 #define IS_VT1708B_8CH_VENDORID(x)	((x) >= 0x1106e720 && (x) <= 0x1106e723)
 #define IS_VT1708B_4CH_VENDORID(x)	((x) >= 0x1106e724 && (x) <= 0x1106e727)
+#define IS_VT1708S_VENDORID(x)		((x) >= 0x11060397 && (x) <= 0x11067397)
+#define IS_VT1702_VENDORID(x)		((x) >= 0x11060398 && (x) <= 0x11067398)
 
+enum VIA_HDA_CODEC {
+	UNKNOWN = -1,
+	VT1708,
+	VT1709_10CH,
+	VT1709_6CH,
+	VT1708B_8CH,
+	VT1708B_4CH,
+	VT1708S,
+	VT1702,
+	CODEC_TYPES,
+};
+
+static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
+{
+	u16 ven_id = vendor_id >> 16;
+	u16 dev_id = vendor_id & 0xffff;
+	enum VIA_HDA_CODEC codec_type;
+
+	/* get codec type */
+	if (ven_id != 0x1106)
+		codec_type = UNKNOWN;
+	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
+		codec_type = VT1708;
+	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
+		codec_type = VT1709_10CH;
+	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
+		codec_type = VT1709_6CH;
+	else if (dev_id >= 0xe720 && dev_id <= 0xe723)
+		codec_type = VT1708B_8CH;
+	else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+		codec_type = VT1708B_4CH;
+	else if ((dev_id & 0xfff) == 0x397
+		 && (dev_id >> 12) < 8)
+		codec_type = VT1708S;
+	else if ((dev_id & 0xfff) == 0x398
+		 && (dev_id >> 12) < 8)
+		codec_type = VT1702;
+	else
+		codec_type = UNKNOWN;
+	return codec_type;
+};
+
+#define VIA_HP_EVENT		0x01
+#define VIA_GPIO_EVENT		0x02
 
 enum {
 	VIA_CTL_WIDGET_VOL,
@@ -77,12 +139,54 @@
 };
 
 enum {
-	AUTO_SEQ_FRONT,
+	AUTO_SEQ_FRONT = 0,
 	AUTO_SEQ_SURROUND,
 	AUTO_SEQ_CENLFE,
 	AUTO_SEQ_SIDE
 };
 
+#define get_amp_nid(kc)	((kc)->private_value & 0xffff)
+
+/* Some VT1708S based boards gets the micboost setting wrong, so we have
+ * to apply some brute-force and re-write the TLV's by software. */
+static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+			 unsigned int size, unsigned int __user *_tlv)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = get_amp_nid(kcontrol);
+
+	if (get_codec_type(codec->vendor_id) == VT1708S
+	    && (nid == 0x1a || nid == 0x1e)) {
+		if (size < 4 * sizeof(unsigned int))
+			return -ENOMEM;
+		if (put_user(1, _tlv))	/* SNDRV_CTL_TLVT_DB_SCALE */
+			return -EFAULT;
+		if (put_user(2 * sizeof(unsigned int), _tlv + 1))
+			return -EFAULT;
+		if (put_user(0, _tlv + 2)) /* offset = 0 */
+			return -EFAULT;
+		if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = get_amp_nid(kcontrol);
+
+	if (get_codec_type(codec->vendor_id) == VT1708S
+	    && (nid == 0x1a || nid == 0x1e)) {
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+		uinfo->count = 2;
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = 3;
+	}
+	return 0;
+}
+
 static struct snd_kcontrol_new vt1708_control_templates[] = {
 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
@@ -94,7 +198,8 @@
 	struct snd_kcontrol_new *mixers[3];
 	unsigned int num_mixers;
 
-	struct hda_verb *init_verbs;
+	struct hda_verb *init_verbs[5];
+	unsigned int num_iverbs;
 
 	char *stream_name_analog;
 	struct hda_pcm_stream *stream_analog_playback;
@@ -106,6 +211,7 @@
 
 	/* playback */
 	struct hda_multi_out multiout;
+	hda_nid_t extra_dig_out_nid;
 
 	/* capture */
 	unsigned int num_adc_nids;
@@ -117,15 +223,19 @@
 	unsigned int cur_mux[3];
 
 	/* PCM information */
-	struct hda_pcm pcm_rec[2];
+	struct hda_pcm pcm_rec[3];
 
 	/* dynamic controls, init_verbs and input_mux */
 	struct auto_pin_cfg autocfg;
 	unsigned int num_kctl_alloc, num_kctl_used;
 	struct snd_kcontrol_new *kctl_alloc;
-	struct hda_input_mux private_imux;
+	struct hda_input_mux private_imux[2];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
+	/* HP mode source */
+	const struct hda_input_mux *hp_mux;
+	unsigned int hp_independent_mode;
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	struct hda_loopback_check loopback;
 #endif
@@ -146,6 +256,16 @@
 	0x13, 0x14
 };
 
+static hda_nid_t vt1708S_adc_nids[2] = {
+	/* ADC1-2 */
+	0x13, 0x14
+};
+
+static hda_nid_t vt1702_adc_nids[3] = {
+	/* ADC1-2 */
+	0x12, 0x20, 0x1F
+};
+
 /* add dynamic controls */
 static int via_add_control(struct via_spec *spec, int type, const char *name,
 			   unsigned long val)
@@ -283,19 +403,108 @@
 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
 					     0x18, &spec->cur_mux[adc_idx]);
 	else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
-		  IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0)
+		  IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0))
 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
 					     0x19, &spec->cur_mux[adc_idx]);
 	else if ((IS_VT1708B_8CH_VENDORID(vendor_id) ||
-		  IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0)
+		  IS_VT1708B_4CH_VENDORID(vendor_id)) && (adc_idx == 0))
 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
 					     0x17, &spec->cur_mux[adc_idx]);
+	else if (IS_VT1702_VENDORID(vendor_id) && (adc_idx == 0))
+		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+					     0x13, &spec->cur_mux[adc_idx]);
 	else
 		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
 					     spec->adc_nids[adc_idx],
 					     &spec->cur_mux[adc_idx]);
 }
 
+static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct via_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
+}
+
+static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct via_spec *spec = codec->spec;
+	hda_nid_t nid = spec->autocfg.hp_pins[0];
+	unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
+						 AC_VERB_GET_CONNECT_SEL,
+						 0x00);
+
+	ucontrol->value.enumerated.item[0] = pinsel;
+
+	return 0;
+}
+
+static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct via_spec *spec = codec->spec;
+	hda_nid_t nid = spec->autocfg.hp_pins[0];
+	unsigned int pinsel = ucontrol->value.enumerated.item[0];
+	unsigned int con_nid = snd_hda_codec_read(codec, nid, 0,
+					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+
+	if (con_nid == spec->multiout.hp_nid) {
+		if (pinsel == 0) {
+			if (!spec->hp_independent_mode) {
+				if (spec->multiout.num_dacs > 1)
+					spec->multiout.num_dacs -= 1;
+				spec->hp_independent_mode = 1;
+			}
+		} else if (pinsel == 1) {
+		       if (spec->hp_independent_mode) {
+				if (spec->multiout.num_dacs > 1)
+					spec->multiout.num_dacs += 1;
+				spec->hp_independent_mode = 0;
+		       }
+		}
+	} else {
+		if (pinsel == 0) {
+			if (spec->hp_independent_mode) {
+				if (spec->multiout.num_dacs > 1)
+					spec->multiout.num_dacs += 1;
+				spec->hp_independent_mode = 0;
+			}
+		} else if (pinsel == 1) {
+		       if (!spec->hp_independent_mode) {
+				if (spec->multiout.num_dacs > 1)
+					spec->multiout.num_dacs -= 1;
+				spec->hp_independent_mode = 1;
+		       }
+		}
+	}
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+			    pinsel);
+
+	if (spec->multiout.hp_nid &&
+	    spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT])
+			snd_hda_codec_setup_stream(codec,
+						   spec->multiout.hp_nid,
+						   0, 0, 0);
+
+	return 0;
+}
+
+static struct snd_kcontrol_new via_hp_mixer[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Independent HP",
+		.count = 1,
+		.info = via_independent_hp_info,
+		.get = via_independent_hp_get,
+		.put = via_independent_hp_put,
+	},
+	{ } /* end */
+};
+
 /* capture mixer elements */
 static struct snd_kcontrol_new vt1708_capture_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -380,6 +589,138 @@
 	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
 }
 
+
+static void playback_multi_pcm_prep_0(struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      struct snd_pcm_substream *substream)
+{
+	struct via_spec *spec = codec->spec;
+	struct hda_multi_out *mout = &spec->multiout;
+	hda_nid_t *nids = mout->dac_nids;
+	int chs = substream->runtime->channels;
+	int i;
+
+	mutex_lock(&codec->spdif_mutex);
+	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+		if (chs == 2 &&
+		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
+						format) &&
+		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+			/* turn off SPDIF once; otherwise the IEC958 bits won't
+			 * be updated */
+			if (codec->spdif_ctls & AC_DIG1_ENABLE)
+				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
+						    AC_VERB_SET_DIGI_CONVERT_1,
+						    codec->spdif_ctls &
+							~AC_DIG1_ENABLE & 0xff);
+			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+						   stream_tag, 0, format);
+			/* turn on again (if needed) */
+			if (codec->spdif_ctls & AC_DIG1_ENABLE)
+				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
+						    AC_VERB_SET_DIGI_CONVERT_1,
+						    codec->spdif_ctls & 0xff);
+		} else {
+			mout->dig_out_used = 0;
+			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+						   0, 0, 0);
+		}
+	}
+	mutex_unlock(&codec->spdif_mutex);
+
+	/* front */
+	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
+				   0, format);
+
+	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
+	    !spec->hp_independent_mode)
+		/* headphone out will just decode front left/right (stereo) */
+		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
+					   0, format);
+
+	/* extra outputs copied from front */
+	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+		if (mout->extra_out_nid[i])
+			snd_hda_codec_setup_stream(codec,
+						   mout->extra_out_nid[i],
+						   stream_tag, 0, format);
+
+	/* surrounds */
+	for (i = 1; i < mout->num_dacs; i++) {
+		if (chs >= (i + 1) * 2) /* independent out */
+			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+						   i * 2, format);
+		else /* copy front */
+			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+						   0, format);
+	}
+}
+
+static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
+					  struct hda_codec *codec,
+					  unsigned int stream_tag,
+					  unsigned int format,
+					  struct snd_pcm_substream *substream)
+{
+	struct via_spec *spec = codec->spec;
+	struct hda_multi_out *mout = &spec->multiout;
+	hda_nid_t *nids = mout->dac_nids;
+
+	if (substream->number == 0)
+		playback_multi_pcm_prep_0(codec, stream_tag, format,
+					  substream);
+	else {
+		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
+		    spec->hp_independent_mode)
+			snd_hda_codec_setup_stream(codec, mout->hp_nid,
+						   stream_tag, 0, format);
+	}
+
+	return 0;
+}
+
+static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct via_spec *spec = codec->spec;
+	struct hda_multi_out *mout = &spec->multiout;
+	hda_nid_t *nids = mout->dac_nids;
+	int i;
+
+	if (substream->number == 0) {
+		for (i = 0; i < mout->num_dacs; i++)
+			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+
+		if (mout->hp_nid && !spec->hp_independent_mode)
+			snd_hda_codec_setup_stream(codec, mout->hp_nid,
+						   0, 0, 0);
+
+		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+			if (mout->extra_out_nid[i])
+				snd_hda_codec_setup_stream(codec,
+							mout->extra_out_nid[i],
+							0, 0, 0);
+		mutex_lock(&codec->spdif_mutex);
+		if (mout->dig_out_nid &&
+		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+						   0, 0, 0);
+			mout->dig_out_used = 0;
+		}
+		mutex_unlock(&codec->spdif_mutex);
+	} else {
+		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
+		    spec->hp_independent_mode)
+			snd_hda_codec_setup_stream(codec, mout->hp_nid,
+						   0, 0, 0);
+	}
+
+	return 0;
+}
+
 /*
  * Digital out
  */
@@ -399,6 +740,21 @@
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+/* setup SPDIF output stream */
+static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
+				 unsigned int stream_tag, unsigned int format)
+{
+	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+	if (codec->spdif_ctls & AC_DIG1_ENABLE)
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+	/* turn on again (if needed) */
+	if (codec->spdif_ctls & AC_DIG1_ENABLE)
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+				    codec->spdif_ctls & 0xff);
+}
+
 static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 					struct hda_codec *codec,
 					unsigned int stream_tag,
@@ -406,8 +762,20 @@
 					struct snd_pcm_substream *substream)
 {
 	struct via_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
-					     stream_tag, format, substream);
+	hda_nid_t nid;
+
+	/* 1st or 2nd S/PDIF */
+	if (substream->number == 0)
+		nid = spec->multiout.dig_out_nid;
+	else if (substream->number == 1)
+		nid = spec->extra_dig_out_nid;
+	else
+		return -1;
+
+	mutex_lock(&codec->spdif_mutex);
+	setup_dig_playback_stream(codec, nid, stream_tag, format);
+	mutex_unlock(&codec->spdif_mutex);
+	return 0;
 }
 
 /*
@@ -436,14 +804,14 @@
 }
 
 static struct hda_pcm_stream vt1708_pcm_analog_playback = {
-	.substreams = 1,
+	.substreams = 2,
 	.channels_min = 2,
 	.channels_max = 8,
 	.nid = 0x10, /* NID to query formats and rates */
 	.ops = {
 		.open = via_playback_pcm_open,
-		.prepare = via_playback_pcm_prepare,
-		.cleanup = via_playback_pcm_cleanup
+		.prepare = via_playback_multi_pcm_prepare,
+		.cleanup = via_playback_multi_pcm_cleanup
 	},
 };
 
@@ -515,6 +883,13 @@
 		if (err < 0)
 			return err;
 		spec->multiout.share_spdif = 1;
+
+		if (spec->extra_dig_out_nid) {
+			err = snd_hda_create_spdif_out_ctls(codec,
+						    spec->extra_dig_out_nid);
+			if (err < 0)
+				return err;
+		}
 	}
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -580,10 +955,89 @@
 	kfree(codec->spec);
 }
 
+/* mute internal speaker if HP is plugged */
+static void via_hp_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	struct via_spec *spec = codec->spec;
+
+	present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
+				 HDA_OUTPUT, 0, HDA_AMP_MUTE,
+				 present ? HDA_AMP_MUTE : 0);
+}
+
+static void via_gpio_control(struct hda_codec *codec)
+{
+	unsigned int gpio_data;
+	unsigned int vol_counter;
+	unsigned int vol;
+	unsigned int master_vol;
+
+	struct via_spec *spec = codec->spec;
+
+	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
+				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
+
+	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
+					  0xF84, 0) & 0x3F0000) >> 16;
+
+	vol = vol_counter & 0x1F;
+	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
+					AC_VERB_GET_AMP_GAIN_MUTE,
+					AC_AMP_GET_INPUT);
+
+	if (gpio_data == 0x02) {
+		/* unmute line out */
+		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
+					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+
+		if (vol_counter & 0x20) {
+			/* decrease volume */
+			if (vol > master_vol)
+				vol = master_vol;
+			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
+						 0, HDA_AMP_VOLMASK,
+						 master_vol-vol);
+		} else {
+			/* increase volume */
+			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
+					 HDA_AMP_VOLMASK,
+					 ((master_vol+vol) > 0x2A) ? 0x2A :
+					  (master_vol+vol));
+		}
+	} else if (!(gpio_data & 0x02)) {
+		/* mute line out */
+		snd_hda_codec_amp_stereo(codec,
+					 spec->autocfg.line_out_pins[0],
+					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
+					 HDA_AMP_MUTE);
+	}
+}
+
+/* unsolicited event for jack sensing */
+static void via_unsol_event(struct hda_codec *codec,
+				  unsigned int res)
+{
+	res >>= 26;
+	if (res == VIA_HP_EVENT)
+		via_hp_automute(codec);
+	else if (res == VIA_GPIO_EVENT)
+		via_gpio_control(codec);
+}
+
+static hda_nid_t slave_dig_outs[] = {
+	0,
+};
+
 static int via_init(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
-	snd_hda_sequence_write(codec, spec->init_verbs);
+	int i;
+	for (i = 0; i < spec->num_iverbs; i++)
+		snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
 	/* Lydia Add for EAPD enable */
 	if (!spec->dig_in_nid) { /* No Digital In connection */
 		if (IS_VT1708_VENDORID(codec->vendor_id)) {
@@ -611,6 +1065,9 @@
 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
 
+	/* no slave outs */
+	codec->slave_dig_outs = slave_dig_outs;
+
  	return 0;
 }
 
@@ -657,10 +1114,10 @@
 				spec->multiout.dac_nids[i] = 0x12;
 				break;
 			case AUTO_SEQ_SURROUND:
-				spec->multiout.dac_nids[i] = 0x13;
+				spec->multiout.dac_nids[i] = 0x11;
 				break;
 			case AUTO_SEQ_SIDE:
-				spec->multiout.dac_nids[i] = 0x11;
+				spec->multiout.dac_nids[i] = 0x13;
 				break;
 			}
 		}
@@ -685,7 +1142,7 @@
 			continue;
 		
 		if (i != AUTO_SEQ_FRONT)
-			nid_vol = 0x1b - i + 1;
+			nid_vol = 0x18 + i;
 
 		if (i == AUTO_SEQ_CENLFE) {
 			/* Center/LFE */
@@ -760,6 +1217,24 @@
 	return 0;
 }
 
+static void create_hp_imux(struct via_spec *spec)
+{
+	int i;
+	struct hda_input_mux *imux = &spec->private_imux[1];
+	static const char *texts[] = { "OFF", "ON", NULL};
+
+	/* for hp mode select */
+	i = 0;
+	while (texts[i] != NULL) {
+		imux->items[imux->num_items].label =  texts[i];
+		imux->items[imux->num_items].index = i;
+		imux->num_items++;
+		i++;
+	}
+
+	spec->hp_mux = &spec->private_imux[1];
+}
+
 static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 {
 	int err;
@@ -780,6 +1255,8 @@
 	if (err < 0)
 		return err;
 
+	create_hp_imux(spec);
+
 	return 0;
 }
 
@@ -790,7 +1267,7 @@
 	static char *labels[] = {
 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
 	};
-	struct hda_input_mux *imux = &spec->private_imux;
+	struct hda_input_mux *imux = &spec->private_imux[0];
 	int i, err, idx = 0;
 
 	/* for internal loopback recording select */
@@ -840,11 +1317,36 @@
 };
 #endif
 
+static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int def_conf;
+	unsigned char seqassoc;
+
+	def_conf = snd_hda_codec_read(codec, nid, 0,
+				      AC_VERB_GET_CONFIG_DEFAULT, 0);
+	seqassoc = (unsigned char) get_defcfg_association(def_conf);
+	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
+	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
+		if (seqassoc == 0xff) {
+			def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+					    def_conf >> 24);
+		}
+	}
+
+	return;
+}
+
 static int vt1708_parse_auto_config(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
 	int err;
 
+	/* Add HP and CD pin config connect bit re-config action */
+	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
+	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
+
 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
 	if (err < 0)
 		return err;
@@ -874,9 +1376,12 @@
 	if (spec->kctl_alloc)
 		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
-	spec->init_verbs = vt1708_volume_init_verbs;	
+	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
 
-	spec->input_mux = &spec->private_imux;
+	spec->input_mux = &spec->private_imux[0];
+
+	if (spec->hp_mux)
+		spec->mixers[spec->num_mixers++] = via_hp_mixer;
 
 	return 1;
 }
@@ -897,7 +1402,7 @@
 	int err;
 
 	/* create a codec specific record */
-	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 		return -ENOMEM;
 
@@ -966,6 +1471,11 @@
 	{ } /* end */
 };
 
+static struct hda_verb vt1709_uniwill_init_verbs[] = {
+	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+	{ }
+};
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -1090,11 +1600,11 @@
 					break;
 				case AUTO_SEQ_SURROUND:
 					/* AOW3 */
-					spec->multiout.dac_nids[i] = 0x27;
+					spec->multiout.dac_nids[i] = 0x11;
 					break;
 				case AUTO_SEQ_SIDE:
 					/* AOW1 */
-					spec->multiout.dac_nids[i] = 0x11;
+					spec->multiout.dac_nids[i] = 0x27;
 					break;
 				default:
 					break;
@@ -1203,26 +1713,26 @@
 		} else if (i == AUTO_SEQ_SURROUND) {
 			sprintf(name, "%s Playback Volume", chname[i]);
 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
 								  HDA_OUTPUT));
 			if (err < 0)
 				return err;
 			sprintf(name, "%s Playback Switch", chname[i]);
 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
 								  HDA_OUTPUT));
 			if (err < 0)
 				return err;
 		} else if (i == AUTO_SEQ_SIDE) {
 			sprintf(name, "%s Playback Volume", chname[i]);
 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
 								  HDA_OUTPUT));
 			if (err < 0)
 				return err;
 			sprintf(name, "%s Playback Switch", chname[i]);
 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
 								  HDA_OUTPUT));
 			if (err < 0)
 				return err;
@@ -1265,7 +1775,7 @@
 	static char *labels[] = {
 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
 	};
-	struct hda_input_mux *imux = &spec->private_imux;
+	struct hda_input_mux *imux = &spec->private_imux[0];
 	int i, err, idx = 0;
 
 	/* for internal loopback recording select */
@@ -1339,7 +1849,10 @@
 	if (spec->kctl_alloc)
 		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
-	spec->input_mux = &spec->private_imux;
+	spec->input_mux = &spec->private_imux[0];
+
+	if (spec->hp_mux)
+		spec->mixers[spec->num_mixers++] = via_hp_mixer;
 
 	return 1;
 }
@@ -1360,7 +1873,7 @@
 	int err;
 
 	/* create a codec specific record */
-	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 		return -ENOMEM;
 
@@ -1375,7 +1888,8 @@
 		       "Using genenic mode...\n");
 	}
 
-	spec->init_verbs = vt1709_10ch_volume_init_verbs;	
+	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
 
 	spec->stream_name_analog = "VT1709 Analog";
 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
@@ -1396,6 +1910,7 @@
 	codec->patch_ops = via_patch_ops;
 
 	codec->patch_ops.init = via_auto_init;
+	codec->patch_ops.unsol_event = via_unsol_event;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	spec->loopback.amplist = vt1709_loopbacks;
 #endif
@@ -1451,7 +1966,7 @@
 	int err;
 
 	/* create a codec specific record */
-	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 		return -ENOMEM;
 
@@ -1466,7 +1981,8 @@
 		       "Using genenic mode...\n");
 	}
 
-	spec->init_verbs = vt1709_6ch_volume_init_verbs;	
+	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
 
 	spec->stream_name_analog = "VT1709 Analog";
 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
@@ -1487,6 +2003,7 @@
 	codec->patch_ops = via_patch_ops;
 
 	codec->patch_ops.init = via_auto_init;
+	codec->patch_ops.unsol_event = via_unsol_event;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	spec->loopback.amplist = vt1709_loopbacks;
 #endif
@@ -1586,27 +2103,32 @@
 	{ }
 };
 
+static struct hda_verb vt1708B_uniwill_init_verbs[] = {
+	{0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+	{ }
+};
+
 static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
-	.substreams = 1,
+	.substreams = 2,
 	.channels_min = 2,
 	.channels_max = 8,
 	.nid = 0x10, /* NID to query formats and rates */
 	.ops = {
 		.open = via_playback_pcm_open,
-		.prepare = via_playback_pcm_prepare,
-		.cleanup = via_playback_pcm_cleanup
+		.prepare = via_playback_multi_pcm_prepare,
+		.cleanup = via_playback_multi_pcm_cleanup
 	},
 };
 
 static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
-	.substreams = 1,
+	.substreams = 2,
 	.channels_min = 2,
 	.channels_max = 4,
 	.nid = 0x10, /* NID to query formats and rates */
 	.ops = {
 		.open = via_playback_pcm_open,
-		.prepare = via_playback_pcm_prepare,
-		.cleanup = via_playback_pcm_cleanup
+		.prepare = via_playback_multi_pcm_prepare,
+		.cleanup = via_playback_multi_pcm_cleanup
 	},
 };
 
@@ -1662,10 +2184,10 @@
 				spec->multiout.dac_nids[i] = 0x24;
 				break;
 			case AUTO_SEQ_SURROUND:
-				spec->multiout.dac_nids[i] = 0x25;
+				spec->multiout.dac_nids[i] = 0x11;
 				break;
 			case AUTO_SEQ_SIDE:
-				spec->multiout.dac_nids[i] = 0x11;
+				spec->multiout.dac_nids[i] = 0x25;
 				break;
 			}
 		}
@@ -1680,7 +2202,7 @@
 {
 	char name[32];
 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
-	hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18};
+	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
 	hda_nid_t nid, nid_vol = 0;
 	int i, err;
 
@@ -1785,6 +2307,8 @@
 	if (err < 0)
 		return err;
 
+	create_hp_imux(spec);
+
 	return 0;
 }
 
@@ -1795,7 +2319,7 @@
 	static char *labels[] = {
 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
 	};
-	struct hda_input_mux *imux = &spec->private_imux;
+	struct hda_input_mux *imux = &spec->private_imux[0];
 	int i, err, idx = 0;
 
 	/* for internal loopback recording select */
@@ -1869,7 +2393,10 @@
 	if (spec->kctl_alloc)
 		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
-	spec->input_mux = &spec->private_imux;
+	spec->input_mux = &spec->private_imux[0];
+
+	if (spec->hp_mux)
+		spec->mixers[spec->num_mixers++] = via_hp_mixer;
 
 	return 1;
 }
@@ -1890,7 +2417,7 @@
 	int err;
 
 	/* create a codec specific record */
-	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 		return -ENOMEM;
 
@@ -1906,7 +2433,8 @@
 		       "from BIOS.  Using genenic mode...\n");
 	}
 
-	spec->init_verbs = vt1708B_8ch_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
 
 	spec->stream_name_analog = "VT1708B Analog";
 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
@@ -1926,6 +2454,7 @@
 	codec->patch_ops = via_patch_ops;
 
 	codec->patch_ops.init = via_auto_init;
+	codec->patch_ops.unsol_event = via_unsol_event;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	spec->loopback.amplist = vt1708B_loopbacks;
 #endif
@@ -1939,7 +2468,7 @@
 	int err;
 
 	/* create a codec specific record */
-	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 		return -ENOMEM;
 
@@ -1955,7 +2484,8 @@
 		       "from BIOS.  Using genenic mode...\n");
 	}
 
-	spec->init_verbs = vt1708B_4ch_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
 
 	spec->stream_name_analog = "VT1708B Analog";
 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
@@ -1975,6 +2505,7 @@
 	codec->patch_ops = via_patch_ops;
 
 	codec->patch_ops.init = via_auto_init;
+	codec->patch_ops.unsol_event = via_unsol_event;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	spec->loopback.amplist = vt1708B_loopbacks;
 #endif
@@ -1982,6 +2513,752 @@
 	return 0;
 }
 
+/* Patch for VT1708S */
+
+/* VT1708S software backdoor based override for buggy hardware micboost
+ * setting */
+#define MIC_BOOST_VOLUME(xname, nid) {				\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
+	.name = xname,					\
+	.index = 0,					\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |	\
+	SNDRV_CTL_ELEM_ACCESS_TLV_READ |		\
+	SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,		\
+	.info = mic_boost_volume_info,			\
+	.get = snd_hda_mixer_amp_volume_get,		\
+	.put = snd_hda_mixer_amp_volume_put,		\
+	.tlv = { .c = mic_boost_tlv },			\
+	.private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) }
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
+	MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A),
+	MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = via_mux_enum_info,
+		.get = via_mux_enum_get,
+		.put = via_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_verb vt1708S_volume_init_verbs[] = {
+	/* Unmute ADC0-1 and set the default input to mic-in */
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
+	 * analog-loopback mixer widget */
+	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/* Setup default input of PW4 to MW0 */
+	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
+	/* PW9, PW10  Output enable */
+	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	/* Enable Mic Boost Volume backdoor */
+	{0x1, 0xf98, 0x1},
+	{ }
+};
+
+static struct hda_verb vt1708S_uniwill_init_verbs[] = {
+	{0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+	{ }
+};
+
+static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 8,
+	.nid = 0x10, /* NID to query formats and rates */
+	.ops = {
+		.open = via_playback_pcm_open,
+		.prepare = via_playback_pcm_prepare,
+		.cleanup = via_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x13, /* NID to query formats and rates */
+	.ops = {
+		.prepare = via_capture_pcm_prepare,
+		.cleanup = via_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in via_build_pcms */
+	.ops = {
+		.open = via_dig_playback_pcm_open,
+		.close = via_dig_playback_pcm_close,
+		.prepare = via_dig_playback_pcm_prepare
+	},
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
+				     const struct auto_pin_cfg *cfg)
+{
+	int i;
+	hda_nid_t nid;
+
+	spec->multiout.num_dacs = cfg->line_outs;
+
+	spec->multiout.dac_nids = spec->private_dac_nids;
+
+	for (i = 0; i < 4; i++) {
+		nid = cfg->line_out_pins[i];
+		if (nid) {
+			/* config dac list */
+			switch (i) {
+			case AUTO_SEQ_FRONT:
+				spec->multiout.dac_nids[i] = 0x10;
+				break;
+			case AUTO_SEQ_CENLFE:
+				spec->multiout.dac_nids[i] = 0x24;
+				break;
+			case AUTO_SEQ_SURROUND:
+				spec->multiout.dac_nids[i] = 0x11;
+				break;
+			case AUTO_SEQ_SIDE:
+				spec->multiout.dac_nids[i] = 0x25;
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	char name[32];
+	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+	hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
+	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
+	hda_nid_t nid, nid_vol, nid_mute;
+	int i, err;
+
+	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+		nid = cfg->line_out_pins[i];
+
+		if (!nid)
+			continue;
+
+		nid_vol = nid_vols[i];
+		nid_mute = nid_mutes[i];
+
+		if (i == AUTO_SEQ_CENLFE) {
+			/* Center/LFE */
+			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+					      "Center Playback Volume",
+					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+					      "LFE Playback Volume",
+					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+					      "Center Playback Switch",
+					      HDA_COMPOSE_AMP_VAL(nid_mute,
+								  1, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+					      "LFE Playback Switch",
+					      HDA_COMPOSE_AMP_VAL(nid_mute,
+								  2, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+		} else if (i == AUTO_SEQ_FRONT) {
+			/* add control to mixer index 0 */
+			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+					      "Master Front Playback Volume",
+					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
+								  HDA_INPUT));
+			if (err < 0)
+				return err;
+			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+					      "Master Front Playback Switch",
+					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
+								  HDA_INPUT));
+			if (err < 0)
+				return err;
+
+			/* Front */
+			sprintf(name, "%s Playback Volume", chname[i]);
+			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			sprintf(name, "%s Playback Switch", chname[i]);
+			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+					      HDA_COMPOSE_AMP_VAL(nid_mute,
+								  3, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+		} else {
+			sprintf(name, "%s Playback Volume", chname[i]);
+			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+			sprintf(name, "%s Playback Switch", chname[i]);
+			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+					      HDA_COMPOSE_AMP_VAL(nid_mute,
+								  3, 0,
+								  HDA_OUTPUT));
+			if (err < 0)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
+static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+	int err;
+
+	if (!pin)
+		return 0;
+
+	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
+
+	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+			      "Headphone Playback Volume",
+			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+
+	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+			      "Headphone Playback Switch",
+			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+
+	create_hp_imux(spec);
+
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	static char *labels[] = {
+		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+	};
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	int i, err, idx = 0;
+
+	/* for internal loopback recording select */
+	imux->items[imux->num_items].label = "Stereo Mixer";
+	imux->items[imux->num_items].index = 5;
+	imux->num_items++;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		if (!cfg->input_pins[i])
+			continue;
+
+		switch (cfg->input_pins[i]) {
+		case 0x1a: /* Mic */
+			idx = 2;
+			break;
+
+		case 0x1b: /* Line In */
+			idx = 3;
+			break;
+
+		case 0x1e: /* Front Mic */
+			idx = 4;
+			break;
+
+		case 0x1f: /* CD */
+			idx = 1;
+			break;
+		}
+		err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
+					   idx, 0x16);
+		if (err < 0)
+			return err;
+		imux->items[imux->num_items].label = labels[i];
+		imux->items[imux->num_items].index = idx-1;
+		imux->num_items++;
+	}
+	return 0;
+}
+
+static int vt1708S_parse_auto_config(struct hda_codec *codec)
+{
+	struct via_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t vt1708s_ignore[] = {0x21, 0};
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+					   vt1708s_ignore);
+	if (err < 0)
+		return err;
+	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+		return 0; /* can't find valid BIOS pin config */
+
+	err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+	if (err < 0)
+		return err;
+	err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
+
+	spec->extra_dig_out_nid = 0x15;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->input_mux = &spec->private_imux[0];
+
+	if (spec->hp_mux)
+		spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+	return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1708S_loopbacks[] = {
+	{ 0x16, HDA_INPUT, 1 },
+	{ 0x16, HDA_INPUT, 2 },
+	{ 0x16, HDA_INPUT, 3 },
+	{ 0x16, HDA_INPUT, 4 },
+	{ } /* end */
+};
+#endif
+
+static int patch_vt1708S(struct hda_codec *codec)
+{
+	struct via_spec *spec;
+	int err;
+
+	/* create a codec specific record */
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	/* automatic parse from the BIOS config */
+	err = vt1708S_parse_auto_config(codec);
+	if (err < 0) {
+		via_free(codec);
+		return err;
+	} else if (!err) {
+		printk(KERN_INFO "hda_codec: Cannot set up configuration "
+		       "from BIOS.  Using genenic mode...\n");
+	}
+
+	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
+
+	spec->stream_name_analog = "VT1708S Analog";
+	spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
+	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
+
+	spec->stream_name_digital = "VT1708S Digital";
+	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
+
+	if (!spec->adc_nids && spec->input_mux) {
+		spec->adc_nids = vt1708S_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
+		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
+		spec->num_mixers++;
+	}
+
+	codec->patch_ops = via_patch_ops;
+
+	codec->patch_ops.init = via_auto_init;
+	codec->patch_ops.unsol_event = via_unsol_event;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	spec->loopback.amplist = vt1708S_loopbacks;
+#endif
+
+	return 0;
+}
+
+/* Patch for VT1702 */
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1702_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
+			 HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = via_mux_enum_info,
+		.get = via_mux_enum_get,
+		.put = via_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_verb vt1702_volume_init_verbs[] = {
+	/*
+	 * Unmute ADC0-1 and set the default input to mic-in
+	 */
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 */
+	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
+	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+	/* Setup default input of PW4 to MW0 */
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* PW6 PW7 Output enable */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{ }
+};
+
+static struct hda_verb vt1702_uniwill_init_verbs[] = {
+	{0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT},
+	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+	{ }
+};
+
+static struct hda_pcm_stream vt1702_pcm_analog_playback = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x10, /* NID to query formats and rates */
+	.ops = {
+		.open = via_playback_pcm_open,
+		.prepare = via_playback_multi_pcm_prepare,
+		.cleanup = via_playback_multi_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream vt1702_pcm_analog_capture = {
+	.substreams = 3,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x12, /* NID to query formats and rates */
+	.ops = {
+		.prepare = via_capture_pcm_prepare,
+		.cleanup = via_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream vt1702_pcm_digital_playback = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in via_build_pcms */
+	.ops = {
+		.open = via_dig_playback_pcm_open,
+		.close = via_dig_playback_pcm_close,
+		.prepare = via_dig_playback_pcm_prepare
+	},
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
+				     const struct auto_pin_cfg *cfg)
+{
+	spec->multiout.num_dacs = 1;
+	spec->multiout.dac_nids = spec->private_dac_nids;
+
+	if (cfg->line_out_pins[0]) {
+		/* config dac list */
+		spec->multiout.dac_nids[0] = 0x10;
+	}
+
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	int err;
+
+	if (!cfg->line_out_pins[0])
+		return -1;
+
+	/* add control to mixer index 0 */
+	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+			      "Master Front Playback Volume",
+			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
+	if (err < 0)
+		return err;
+	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+			      "Master Front Playback Switch",
+			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
+	if (err < 0)
+		return err;
+
+	/* Front */
+	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+			      "Front Playback Volume",
+			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+			      "Front Playback Switch",
+			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+	int err;
+
+	if (!pin)
+		return 0;
+
+	spec->multiout.hp_nid = 0x1D;
+
+	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+			      "Headphone Playback Volume",
+			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+
+	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+			      "Headphone Playback Switch",
+			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+
+	create_hp_imux(spec);
+
+	return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	static char *labels[] = {
+		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+	};
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	int i, err, idx = 0;
+
+	/* for internal loopback recording select */
+	imux->items[imux->num_items].label = "Stereo Mixer";
+	imux->items[imux->num_items].index = 3;
+	imux->num_items++;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		if (!cfg->input_pins[i])
+			continue;
+
+		switch (cfg->input_pins[i]) {
+		case 0x14: /* Mic */
+			idx = 1;
+			break;
+
+		case 0x15: /* Line In */
+			idx = 2;
+			break;
+
+		case 0x18: /* Front Mic */
+			idx = 3;
+			break;
+		}
+		err = via_new_analog_input(spec, cfg->input_pins[i],
+					   labels[i], idx, 0x1A);
+		if (err < 0)
+			return err;
+		imux->items[imux->num_items].label = labels[i];
+		imux->items[imux->num_items].index = idx-1;
+		imux->num_items++;
+	}
+	return 0;
+}
+
+static int vt1702_parse_auto_config(struct hda_codec *codec)
+{
+	struct via_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t vt1702_ignore[] = {0x1C, 0};
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+					   vt1702_ignore);
+	if (err < 0)
+		return err;
+	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+		return 0; /* can't find valid BIOS pin config */
+
+	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+	if (err < 0)
+		return err;
+	err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
+
+	spec->extra_dig_out_nid = 0x1B;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->input_mux = &spec->private_imux[0];
+
+	if (spec->hp_mux)
+		spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+	return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1702_loopbacks[] = {
+	{ 0x1A, HDA_INPUT, 1 },
+	{ 0x1A, HDA_INPUT, 2 },
+	{ 0x1A, HDA_INPUT, 3 },
+	{ 0x1A, HDA_INPUT, 4 },
+	{ } /* end */
+};
+#endif
+
+static int patch_vt1702(struct hda_codec *codec)
+{
+	struct via_spec *spec;
+	int err;
+	unsigned int response;
+	unsigned char control;
+
+	/* create a codec specific record */
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	/* automatic parse from the BIOS config */
+	err = vt1702_parse_auto_config(codec);
+	if (err < 0) {
+		via_free(codec);
+		return err;
+	} else if (!err) {
+		printk(KERN_INFO "hda_codec: Cannot set up configuration "
+		       "from BIOS.  Using genenic mode...\n");
+	}
+
+	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
+	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
+
+	spec->stream_name_analog = "VT1702 Analog";
+	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
+	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
+
+	spec->stream_name_digital = "VT1702 Digital";
+	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
+
+	if (!spec->adc_nids && spec->input_mux) {
+		spec->adc_nids = vt1702_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
+		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
+		spec->num_mixers++;
+	}
+
+	codec->patch_ops = via_patch_ops;
+
+	codec->patch_ops.init = via_auto_init;
+	codec->patch_ops.unsol_event = via_unsol_event;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	spec->loopback.amplist = vt1702_loopbacks;
+#endif
+
+	/* Open backdoor */
+	response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0);
+	control = (unsigned char)(response & 0xff);
+	control |= 0x3;
+	snd_hda_codec_write(codec,  codec->afg, 0, 0xF88, control);
+
+	/* Enable GPIO 0&1 for volume&mute control */
+	/* Enable GPIO 2 for DMIC-DATA */
+	response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0);
+	control = (unsigned char)((response >> 16) & 0x3f);
+	snd_hda_codec_write(codec,  codec->afg, 0, 0xF82, control);
+
+	return 0;
+}
+
 /*
  * patch entries
  */
@@ -2022,5 +3299,37 @@
 	  .patch = patch_vt1708B_4ch},
 	{ .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
 	  .patch = patch_vt1708B_4ch},
+	{ .id = 0x11060397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11061397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11062397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11063397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11064397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11065397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11066397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11067397, .name = "VIA VT1708S",
+	  .patch = patch_vt1708S},
+	{ .id = 0x11060398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11061398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11062398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11063398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11064398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11065398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11066398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
+	{ .id = 0x11067398, .name = "VIA VT1702",
+	  .patch = patch_vt1702},
 	{} /* terminator */
 };
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
index dab31b2..03391da 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -59,7 +59,8 @@
 	struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
 	struct snd_ice1712 *ice = ak->private_data[0];
 
-	snd_assert(chip >= 0 && chip < 4, return);
+	if (snd_BUG_ON(chip < 0 || chip >= 4))
+		return;
 
 	tmp = snd_ice1712_gpio_read(ice);
 	tmp |= priv->add_flags;
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index 868ae29..110d16e 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -44,10 +44,9 @@
  *       not working: prety much everything else, at least i could verify that
  *                    we have no digital output, no capture, pretty bad clicks and poops
  *                    on mixer switch and other coll stuff.
- *
- */      
+ */
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -131,7 +130,7 @@
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(50);
 
-	/* 
+	/*
 	 * send i2c stop condition and start condition
 	 * to obtain sane state
 	 */
@@ -152,10 +151,16 @@
 	 * skipping ack cycles inbetween
 	 */
 	for (j = 0; j < 3; j++) {
-		switch(j) {
-		case 0: val = dev; break;
-		case 1: val = reg; break;
-		case 2: val = data; break;
+		switch (j) {
+		case 0:
+			val = dev;
+			break;
+		case 1:
+			val = reg;
+			break;
+		case 2:
+			val = data;
+			break;
 		}
 		for (i = 7; i >= 0; i--) {
 			tmp &= ~AUREON_SPI_CLK;
@@ -171,7 +176,7 @@
 			snd_ice1712_gpio_write(ice, tmp);
 			udelay(40);
 		}
-                tmp &= ~AUREON_SPI_CLK;
+		tmp &= ~AUREON_SPI_CLK;
 		snd_ice1712_gpio_write(ice, tmp);
 		udelay(40);
 		tmp |= AUREON_SPI_CLK;
@@ -203,7 +208,7 @@
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items = 3;
-	if(uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
 	return 0;
@@ -231,12 +236,12 @@
 		return -EINVAL;
 	snd_ice1712_save_gpio_status(ice);
 	oval = spec->pca9554_out;
-	if ((change = (oval != nval))) {
+	change = (oval != nval);
+	if (change) {
 		aureon_pca9554_write(ice, PCA9554_OUT, nval);
 		spec->pca9554_out = nval;
 	}
 	snd_ice1712_restore_gpio_status(ice);
-	
 	return change;
 }
 
@@ -256,7 +261,7 @@
 	udelay(10);
 	tmp &= ~AUREON_AC97_ADDR;
 	snd_ice1712_gpio_write(ice, tmp);
-	udelay(10);	
+	udelay(10);
 
 	/* Send low-order byte to XILINX chip */
 	tmp &= ~AUREON_AC97_DATA_MASK;
@@ -269,7 +274,7 @@
 	tmp &= ~AUREON_AC97_DATA_LOW;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(10);
-	
+
 	/* Send high-order byte to XILINX chip */
 	tmp &= ~AUREON_AC97_DATA_MASK;
 	tmp |= (val >> 8) & AUREON_AC97_DATA_MASK;
@@ -282,7 +287,7 @@
 	tmp &= ~AUREON_AC97_DATA_HIGH;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(10);
-	
+
 	/* Instruct XILINX chip to parse the data to the STAC9744 chip */
 	tmp |= AUREON_AC97_COMMIT;
 	snd_ice1712_gpio_write(ice, tmp);
@@ -290,7 +295,7 @@
 	tmp &= ~AUREON_AC97_COMMIT;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(10);
-	
+
 	/* Store the data in out private buffer */
 	spec->stac9744[(reg & 0x7F) >> 1] = val;
 }
@@ -304,7 +309,7 @@
 /*
  * Initialize STAC9744 chip
  */
-static int aureon_ac97_init (struct snd_ice1712 *ice)
+static int aureon_ac97_init(struct snd_ice1712 *ice)
 {
 	struct aureon_spec *spec = ice->spec;
 	int i;
@@ -335,20 +340,21 @@
 	tmp = (snd_ice1712_gpio_read(ice) | AUREON_AC97_RESET) & ~AUREON_AC97_DATA_MASK;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(3);
-	
+
 	tmp &= ~AUREON_AC97_RESET;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(3);
-	
+
 	tmp |= AUREON_AC97_RESET;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(3);
-	
+
 	memset(&spec->stac9744, 0, sizeof(spec->stac9744));
-	for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2)
+	for (i = 0; ac97_defaults[i] != (unsigned short)-1; i += 2)
 		spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1];
-		
-	aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770
+
+	/* Unmute AC'97 master volume permanently - muting is done by WM8770 */
+	aureon_ac97_write(ice, AC97_MASTER, 0x0000);
 
 	return 0;
 }
@@ -388,7 +394,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned short ovol, nvol;
 	int change;
-	
+
 	snd_ice1712_save_gpio_status(ice);
 
 	ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
@@ -396,13 +402,14 @@
 	if (kcontrol->private_value & AUREON_AC97_STEREO)
 		nvol |= ((0x1F - ucontrol->value.integer.value[1]) << 8) & 0x1F00;
 	nvol |= ovol & ~0x1F1F;
-	
-	if ((change = (ovol != nvol)))
+
+	change = (ovol != nvol);
+	if (change)
 		aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
 
 	snd_ice1712_restore_gpio_status(ice);
 
-	return change;		
+	return change;
 }
 
 /*
@@ -416,7 +423,8 @@
 
 	mutex_lock(&ice->gpio_mutex);
 
-	ucontrol->value.integer.value[0] = aureon_ac97_read(ice, kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
+	ucontrol->value.integer.value[0] = aureon_ac97_read(ice,
+			kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
 
 	mutex_unlock(&ice->gpio_mutex);
 	return 0;
@@ -429,13 +437,14 @@
 	int change;
 
 	snd_ice1712_save_gpio_status(ice);
-	
+
 	ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
-	nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~	0x8000);
-	
-	if ((change = (ovol != nvol)))
+	nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~0x8000);
+
+	change = (ovol != nvol);
+	if (change)
 		aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);
-		
+
 	snd_ice1712_restore_gpio_status(ice);
 
 	return change;
@@ -465,13 +474,14 @@
 	int change;
 
 	snd_ice1712_save_gpio_status(ice);
-	
+
 	ovol = aureon_ac97_read(ice, AC97_MIC);
 	nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x0020) | (ovol & ~0x0020);
-	
-	if ((change = (ovol != nvol)))
+
+	change = (ovol != nvol);
+	if (change)
 		aureon_ac97_write(ice, AC97_MIC, nvol);
-		
+
 	snd_ice1712_restore_gpio_status(ice);
 
 	return change;
@@ -493,16 +503,15 @@
 		snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_SPI_MOSI|PRODIGY_SPI_CLK|PRODIGY_WM_CS));
 		mosi = PRODIGY_SPI_MOSI;
 		clk = PRODIGY_SPI_CLK;
-	}
-	else {
+	} else {
 		snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RW|AUREON_SPI_MOSI|AUREON_SPI_CLK|
 						 AUREON_WM_CS|AUREON_CS8415_CS));
 		mosi = AUREON_SPI_MOSI;
 		clk = AUREON_SPI_CLK;
-		
+
 		tmp |= AUREON_WM_RW;
 	}
-	
+
 	tmp &= ~cs;
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(1);
@@ -534,7 +543,9 @@
 /*
  * Read data in SPI mode
  */
-static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits, unsigned char *buffer, int size) {
+static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs,
+		unsigned int data, int bits, unsigned char *buffer, int size)
+{
 	int i, j;
 	unsigned int tmp;
 
@@ -544,7 +555,7 @@
 	snd_ice1712_gpio_write(ice, tmp);
 	udelay(1);
 
-	for (i=bits-1; i>=0; i--) {
+	for (i = bits-1; i >= 0; i--) {
 		if (data & (1 << i))
 			tmp |= AUREON_SPI_MOSI;
 		else
@@ -561,9 +572,9 @@
 		udelay(1);
 	}
 
-	for (j=0; j<size; j++) {
+	for (j = 0; j < size; j++) {
 		unsigned char outdata = 0;
-		for (i=7; i>=0; i--) {
+		for (i = 7; i >= 0; i--) {
 			tmp = snd_ice1712_gpio_read(ice);
 			outdata <<= 1;
 			outdata |= (tmp & AUREON_SPI_MISO) ? 1 : 0;
@@ -584,19 +595,24 @@
 	snd_ice1712_gpio_write(ice, tmp);
 }
 
-static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg) {
+static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg)
+{
 	unsigned char val;
 	aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
 	aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, &val, 1);
 	return val;
 }
 
-static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg, unsigned char *buffer, int size) {
+static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg,
+				unsigned char *buffer, int size)
+{
 	aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);
 	aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, buffer, size);
 }
 
-static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg, unsigned char val) {
+static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg,
+						unsigned char val)
+{
 	aureon_spi_write(ice, AUREON_CS8415_CS, 0x200000 | (reg << 8) | val, 24);
 }
 
@@ -654,18 +670,20 @@
 	return 0;
 }
 
-static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned short ovol, nvol;
 	int change;
-	
+
 	snd_ice1712_save_gpio_status(ice);
-	
+
 	ovol = wm_get(ice, WM_OUT_MUX1);
 	nvol = (ovol & ~0x02) | (ucontrol->value.integer.value[0] ? 0x02 : 0x00);
-	if ((change = (ovol != nvol)))
+	change = (ovol != nvol);
+	if (change)
 		wm_put(ice, WM_OUT_MUX1, nvol);
-		
+
 	snd_ice1712_restore_gpio_status(ice);
 
 	return change;
@@ -702,12 +720,12 @@
 static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
 {
 	unsigned char nvol;
-	
+
 	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
 		nvol = 0;
 	else
 		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
-	
+
 	wm_put(ice, index, nvol);
 	wm_put_nocache(ice, index, 0x180 | nvol);
 }
@@ -736,7 +754,8 @@
 	snd_ice1712_save_gpio_status(ice);
 	oval = wm_get(ice, WM_MUTE);
 	nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
-	if ((change = (nval != oval)))
+	change = (oval != nval);
+	if (change)
 		wm_put(ice, WM_MUTE, nval);
 	snd_ice1712_restore_gpio_status(ice);
 
@@ -760,7 +779,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct aureon_spec *spec = ice->spec;
 	int i;
-	for (i=0; i<2; i++)
+	for (i = 0; i < 2; i++)
 		ucontrol->value.integer.value[i] =
 			spec->master[i] & ~WM_VOL_MUTE;
 	return 0;
@@ -849,7 +868,8 @@
 /*
  * WM8770 mute control
  */
-static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	uinfo->count = kcontrol->private_value >> 8;
 	uinfo->value.integer.min = 0;
@@ -862,7 +882,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct aureon_spec *spec = ice->spec;
 	int voices, ofs, i;
-	
+
 	voices = kcontrol->private_value >> 8;
 	ofs = kcontrol->private_value & 0xFF;
 
@@ -907,7 +927,7 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct aureon_spec *spec = ice->spec;
-	
+
 	ucontrol->value.integer.value[0] =
 		(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;
 	ucontrol->value.integer.value[1] =
@@ -1083,21 +1103,21 @@
 static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 	static const char * const texts[] = {
-		"CD",		//AIN1
-		"Aux",		//AIN2
-		"Line",		//AIN3
-		"Mic",		//AIN4
-		"AC97"		//AIN5
+		"CD",		/* AIN1 */
+		"Aux",		/* AIN2 */
+		"Line",		/* AIN3 */
+		"Mic",		/* AIN4 */
+		"AC97"		/* AIN5 */
 	};
 	static const char * const universe_texts[] = {
-		"Aux1",		//AIN1
-		"CD",		//AIN2
-		"Phono",	//AIN3
-		"Line",		//AIN4
-		"Aux2",		//AIN5
-		"Mic",		//AIN6
-		"Aux3",		//AIN7
-		"AC97"		//AIN8
+		"Aux1",		/* AIN1 */
+		"CD",		/* AIN2 */
+		"Phono",	/* AIN3 */
+		"Line",		/* AIN4 */
+		"Aux2",		/* AIN5 */
+		"Mic",		/* AIN6 */
+		"Aux3",		/* AIN7 */
+		"AC97"		/* AIN8 */
 	};
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 
@@ -1108,8 +1128,7 @@
 		if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
 			uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
 		strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]);
-	}
-	else {
+	} else {
 		uinfo->value.enumerated.items = 5;
 		if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
 			uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
@@ -1156,8 +1175,8 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	static const char * const aureon_texts[] = {
-		"CD",		//RXP0
-		"Optical"	//RXP1
+		"CD",		/* RXP0 */
+		"Optical"	/* RXP1 */
 	};
 	static const char * const prodigy_texts[] = {
 		"CD",
@@ -1180,10 +1199,10 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct aureon_spec *spec = ice->spec;
 
-	//snd_ice1712_save_gpio_status(ice);
-	//val = aureon_cs8415_get(ice, CS8415_CTRL2);
+	/* snd_ice1712_save_gpio_status(ice); */
+	/* val = aureon_cs8415_get(ice, CS8415_CTRL2); */
 	ucontrol->value.enumerated.item[0] = spec->cs8415_mux;
-	//snd_ice1712_restore_gpio_status(ice);
+	/* snd_ice1712_restore_gpio_status(ice); */
 	return 0;
 }
 
@@ -1206,7 +1225,7 @@
 	return change;
 }
 
-static int aureon_cs8415_rate_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int aureon_cs8415_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
@@ -1215,7 +1234,7 @@
 	return 0;
 }
 
-static int aureon_cs8415_rate_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int aureon_cs8415_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char ratio;
@@ -1229,7 +1248,7 @@
  */
 #define aureon_cs8415_mute_info		snd_ctl_boolean_mono_info
 
-static int aureon_cs8415_mute_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int aureon_cs8415_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	snd_ice1712_save_gpio_status(ice);
@@ -1238,7 +1257,7 @@
 	return 0;
 }
 
-static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int aureon_cs8415_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char oval, nval;
@@ -1249,7 +1268,8 @@
 		nval = oval & ~0x20;
 	else
 		nval = oval | 0x20;
-	if ((change = (oval != nval)))
+	change = (oval != nval);
+	if (change)
 		aureon_cs8415_put(ice, CS8415_CTRL1, nval);
 	snd_ice1712_restore_gpio_status(ice);
 	return change;
@@ -1258,15 +1278,17 @@
 /*
  * CS8415A Q-Sub info
  */
-static int aureon_cs8415_qsub_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int aureon_cs8415_qsub_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 	uinfo->count = 10;
 	return 0;
 }
 
-static int aureon_cs8415_qsub_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_cs8415_qsub_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	
+
 	snd_ice1712_save_gpio_status(ice);
 	aureon_cs8415_read(ice, CS8415_QSUB, ucontrol->value.bytes.data, 10);
 	snd_ice1712_restore_gpio_status(ice);
@@ -1274,18 +1296,21 @@
 	return 0;
 }
 
-static int aureon_cs8415_spdif_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int aureon_cs8415_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
 	uinfo->count = 1;
 	return 0;
 }
 
-static int aureon_cs8415_mask_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_cs8415_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
 	memset(ucontrol->value.iec958.status, 0xFF, 24);
 	return 0;
 }
 
-static int aureon_cs8415_spdif_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {
+static int aureon_cs8415_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 
 	snd_ice1712_save_gpio_status(ice);
@@ -1311,9 +1336,9 @@
 	else
 		if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
 		    ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT)
-			tmp &= ~ AUREON_HP_SEL;
+			tmp &= ~AUREON_HP_SEL;
 		else
-			tmp &= ~ PRODIGY_HP_SEL;
+			tmp &= ~PRODIGY_HP_SEL;
 	if (tmp != tmp2) {
 		snd_ice1712_gpio_write(ice, tmp);
 		return 1;
@@ -1325,7 +1350,7 @@
 {
 	unsigned int tmp = snd_ice1712_gpio_read(ice);
 
-	return ( tmp & AUREON_HP_SEL )!= 0;
+	return (tmp & AUREON_HP_SEL) != 0;
 }
 
 #define aureon_hpamp_info	snd_ctl_boolean_mono_info
@@ -1343,7 +1368,7 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 
-	return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
+	return aureon_set_headphone_amp(ice, ucontrol->value.integer.value[0]);
 }
 
 /*
@@ -1390,7 +1415,7 @@
 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
 
-        return 0;
+	return 0;
 }
 
 static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1434,7 +1459,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Master Playback Volume",
 		.info = wm_master_vol_info,
 		.get = wm_master_vol_get,
@@ -1452,7 +1477,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Front Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
@@ -1471,7 +1496,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Rear Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
@@ -1490,7 +1515,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Center Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
@@ -1509,7 +1534,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "LFE Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
@@ -1528,7 +1553,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Side Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
@@ -1539,23 +1564,23 @@
 };
 
 static struct snd_kcontrol_new wm_controls[] __devinitdata = {
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "PCM Playback Switch",
 		.info = wm_pcm_mute_info,
 		.get = wm_pcm_mute_get,
 		.put = wm_pcm_mute_put
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "PCM Playback Volume",
 		.info = wm_pcm_vol_info,
 		.get = wm_pcm_vol_get,
 		.put = wm_pcm_vol_put,
 		.tlv = { .p = db_scale_wm_pcm }
- 	},
+	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Capture Switch",
@@ -1566,7 +1591,7 @@
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Capture Volume",
 		.info = wm_adc_vol_info,
 		.get = wm_adc_vol_get,
@@ -1605,232 +1630,232 @@
 };
 
 static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "AC97 Playback Switch",
 		.info = aureon_ac97_mmute_info,
 		.get = aureon_ac97_mmute_get,
 		.put = aureon_ac97_mmute_put,
 		.private_value = AC97_MASTER
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "AC97 Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MASTER|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "AC97 Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_MASTER|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_master }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "CD Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_CD
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "CD Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_CD
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "CD Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_CD|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "CD Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_CD|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Aux Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_AUX,
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Aux Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_AUX,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Aux Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_AUX|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Aux Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_AUX|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Line Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_LINE
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Line Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_LINE
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Line Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_LINE|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Line Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_LINE|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Mic Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_MIC
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Mic Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_MIC
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Mic Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MIC,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Mic Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_MIC,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Mic Boost (+20dB)",
- 		.info = aureon_ac97_micboost_info,
- 		.get = aureon_ac97_micboost_get,
- 		.put = aureon_ac97_micboost_put
- 	}
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Mic Boost (+20dB)",
+		.info = aureon_ac97_micboost_info,
+		.get = aureon_ac97_micboost_get,
+		.put = aureon_ac97_micboost_put
+	}
 };
 
 static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "AC97 Playback Switch",
 		.info = aureon_ac97_mmute_info,
 		.get = aureon_ac97_mmute_get,
 		.put = aureon_ac97_mmute_put,
 		.private_value = AC97_MASTER
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "AC97 Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MASTER|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "AC97 Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_MASTER|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_master }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "CD Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_AUX
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "CD Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_AUX
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "CD Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_AUX|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "CD Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_AUX|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Phono Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_CD
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Phono Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_CD
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Phono Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_CD|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Phono Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_CD|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Line Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_LINE
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Line Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_LINE
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Line Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_LINE|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Line Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_LINE|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Mic Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_MIC
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Mic Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_MIC
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Mic Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MIC,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Mic Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_MIC,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Mic Boost (+20dB)",
- 		.info = aureon_ac97_micboost_info,
- 		.get = aureon_ac97_micboost_get,
- 		.put = aureon_ac97_micboost_put
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- 		.name = "Aux Playback Switch",
- 		.info = aureon_ac97_mute_info,
- 		.get = aureon_ac97_mute_get,
- 		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_VIDEO,
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Mic Boost (+20dB)",
+		.info = aureon_ac97_micboost_info,
+		.get = aureon_ac97_micboost_get,
+		.put = aureon_ac97_micboost_put
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Aux Playback Switch",
+		.info = aureon_ac97_mute_info,
+		.get = aureon_ac97_mute_get,
+		.put = aureon_ac97_mute_put,
+		.private_value = AC97_VIDEO,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- 		.name = "Aux Playback Volume",
- 		.info = aureon_ac97_vol_info,
- 		.get = aureon_ac97_vol_get,
- 		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_VIDEO|AUREON_AC97_STEREO,
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+		.name = "Aux Playback Volume",
+		.info = aureon_ac97_vol_info,
+		.get = aureon_ac97_vol_get,
+		.put = aureon_ac97_vol_put,
+		.private_value = AC97_VIDEO|AUREON_AC97_STEREO,
 		.tlv = { .p = db_scale_ac97_gain }
- 	},
+	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Aux Source",
@@ -1844,43 +1869,43 @@
 static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
 		.info = aureon_cs8415_mute_info,
 		.get = aureon_cs8415_mute_get,
 		.put = aureon_cs8415_mute_put
 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Source",
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Source",
 		.info = aureon_cs8415_mux_info,
 		.get = aureon_cs8415_mux_get,
 		.put = aureon_cs8415_mux_put,
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("Q-subcode ",CAPTURE,DEFAULT),
+		.name = SNDRV_CTL_NAME_IEC958("Q-subcode ", CAPTURE, DEFAULT),
 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
 		.info = aureon_cs8415_qsub_info,
 		.get = aureon_cs8415_qsub_get,
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
 		.info = aureon_cs8415_spdif_info,
 		.get = aureon_cs8415_mask_get
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
 		.info = aureon_cs8415_spdif_info,
 		.get = aureon_cs8415_spdif_get
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Rate",
-		.access =SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
 		.info = aureon_cs8415_rate_info,
 		.get = aureon_cs8415_rate_get
 	}
@@ -1905,15 +1930,14 @@
 		if (err < 0)
 			return err;
 	}
-	
+
 	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
 		for (i = 0; i < ARRAY_SIZE(universe_ac97_controls); i++) {
 			err = snd_ctl_add(ice->card, snd_ctl_new1(&universe_ac97_controls[i], ice));
 			if (err < 0)
 				return err;
 		}
-	}
-	else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
+	} else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&
 		 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {
 		for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) {
 			err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice));
@@ -1932,7 +1956,7 @@
 		else if ((id & 0x0F) != 0x01)
 			snd_printk(KERN_INFO "Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));
 		else {
-			for (i = 0; i< ARRAY_SIZE(cs8415_controls); i++) {
+			for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
 				struct snd_kcontrol *kctl;
 				err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));
 				if (err < 0)
@@ -1943,7 +1967,7 @@
 		}
 		snd_ice1712_restore_gpio_status(ice);
 	}
-	
+
 	return 0;
 }
 
@@ -2059,11 +2083,12 @@
 
 	/* to remeber the register values of CS8415 */
 	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
-	if (! ice->akm)
+	if (!ice->akm)
 		return -ENOMEM;
 	ice->akm_codecs = 1;
-	
-	if ((err = aureon_ac97_init(ice)) != 0)
+
+	err = aureon_ac97_init(ice);
+	if (err != 0)
 		return err;
 
 	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
@@ -2086,7 +2111,7 @@
 	/* initialize WM8770 codec */
 	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71 ||
 		ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT ||
-	        ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)
+		ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)
 		p = wm_inits_prodigy;
 	else
 		p = wm_inits_aureon;
@@ -2105,10 +2130,10 @@
 
 	snd_ice1712_restore_gpio_status(ice);
 
-        /* initialize PCA9554 pin directions & set default input*/
+	/* initialize PCA9554 pin directions & set default input */
 	aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
 	aureon_pca9554_write(ice, PCA9554_OUT, 0x00);   /* internal AUX */
-	
+
 	spec->master[0] = WM_VOL_MUTE;
 	spec->master[1] = WM_VOL_MUTE;
 	for (i = 0; i < ice->num_total_dacs; i++) {
@@ -2158,6 +2183,24 @@
 };
 #define prodigy71_eeprom aureon71_eeprom
 
+static unsigned char aureon71_universe_eeprom[] __devinitdata = {
+	[ICE_EEP2_SYSCONF]     = 0x2b,	/* clock 512, mpu401, spdif-in/ADC,
+					 * 4DACs
+					 */
+	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
+	[ICE_EEP2_I2S]         = 0xfc,	/* vol, 96k, 24bit, 192k */
+	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */
+	[ICE_EEP2_GPIO_DIR]    = 0xff,
+	[ICE_EEP2_GPIO_DIR1]   = 0xff,
+	[ICE_EEP2_GPIO_DIR2]   = 0x5f,
+	[ICE_EEP2_GPIO_MASK]   = 0x00,
+	[ICE_EEP2_GPIO_MASK1]  = 0x00,
+	[ICE_EEP2_GPIO_MASK2]  = 0x00,
+	[ICE_EEP2_GPIO_STATE]  = 0x00,
+	[ICE_EEP2_GPIO_STATE1] = 0x00,
+	[ICE_EEP2_GPIO_STATE2] = 0x00,
+};
+
 static unsigned char prodigy71lt_eeprom[] __devinitdata = {
 	[ICE_EEP2_SYSCONF]     = 0x4b,	/* clock 384, spdif-in/ADC, 4DACs */
 	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
@@ -2197,14 +2240,14 @@
 		.eeprom_data = aureon71_eeprom,
 		.driver = "Aureon71",
 	},
- 	{
- 		.subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
- 		.name = "Terratec Aureon 7.1-Universe",
+	{
+		.subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE,
+		.name = "Terratec Aureon 7.1-Universe",
 		.model = "universe",
- 		.chip_init = aureon_init,
- 		.build_controls = aureon_add_controls,
- 		.eeprom_size = sizeof(aureon71_eeprom),
- 		.eeprom_data = aureon71_eeprom,
+		.chip_init = aureon_init,
+		.build_controls = aureon_add_controls,
+		.eeprom_size = sizeof(aureon71_universe_eeprom),
+		.eeprom_data = aureon71_universe_eeprom,
 		.driver = "Aureon71Univ", /* keep in 15 letters */
 	},
 	{
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 0ed96c1..d216362 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -400,7 +400,7 @@
 static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kcontrol,
 			 struct snd_ctl_elem_value *ucontrol)
 {
-	char reg = 0x10; // cs8427 receiver error register
+	char reg = 0x10; /* CS8427 receiver error register */
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 
 	if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index ea7116c..f7f14df 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -31,6 +31,7 @@
 		"{MidiMan M Audio,Delta DiO 2496},"\
 		"{MidiMan M Audio,Delta 66},"\
 		"{MidiMan M Audio,Delta 44},"\
+		"{MidiMan M Audio,Delta 410},"\
 		"{MidiMan M Audio,Audiophile 24/96},"\
 		"{Digigram,VX442},"\
 		"{Lionstracs,Mediastation},"
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 013fc4f..6fe35b8 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -149,7 +149,8 @@
 	struct ews_spec *spec = ice->spec;
 	unsigned char data, ndata;
 
-	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
+	if (snd_BUG_ON(chip_mask < 0 || chip_mask > 0x0f))
+		return -EINVAL;
 	snd_i2c_lock(ice->i2c);
 	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)
 		goto __error;
@@ -685,7 +686,8 @@
 	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned char data;
 
-	snd_assert(channel >= 0 && channel <= 7, return 0);
+	if (snd_BUG_ON(channel < 0 || channel > 7))
+		return 0;
 	snd_i2c_lock(ice->i2c);
 	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
 		snd_i2c_unlock(ice->i2c);
@@ -705,7 +707,8 @@
 	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned char data, ndata;
 
-	snd_assert(channel >= 0 && channel <= 7, return 0);
+	if (snd_BUG_ON(channel < 0 || channel > 7))
+		return 0;
 	snd_i2c_lock(ice->i2c);
 	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {
 		snd_i2c_unlock(ice->i2c);
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 29d449d..5b44238 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -17,7 +17,7 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
- */      
+ */
 
 /*
   NOTES:
@@ -35,7 +35,7 @@
  *
  *  2002.11.26	James Stafford <jstafford@ampltd.com>
  *	Added support for VT1724 (Envy24HT)
- *	I have left out support for 176.4 and 192 KHz for the moment. 
+ *	I have left out support for 176.4 and 192 KHz for the moment.
  *  I also haven't done anything with the internal S/PDIF transmitter or the MPU-401
  *
  *  2003.02.20  Taksahi Iwai <tiwai@suse.de>
@@ -47,7 +47,7 @@
  */
 
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -123,7 +123,7 @@
 /*
  *  Basic I/O
  */
- 
+
 /* check whether the clock mode is spdif-in */
 static inline int is_spdif_master(struct snd_ice1712 *ice)
 {
@@ -135,13 +135,13 @@
 	return is_spdif_master(ice) || PRO_RATE_LOCKED;
 }
 
-static inline void snd_ice1712_ds_write(struct snd_ice1712 * ice, u8 channel, u8 addr, u32 data)
+static inline void snd_ice1712_ds_write(struct snd_ice1712 *ice, u8 channel, u8 addr, u32 data)
 {
 	outb((channel << 4) | addr, ICEDS(ice, INDEX));
 	outl(data, ICEDS(ice, DATA));
 }
 
-static inline u32 snd_ice1712_ds_read(struct snd_ice1712 * ice, u8 channel, u8 addr)
+static inline u32 snd_ice1712_ds_read(struct snd_ice1712 *ice, u8 channel, u8 addr)
 {
 	outb((channel << 4) | addr, ICEDS(ice, INDEX));
 	return inl(ICEDS(ice, DATA));
@@ -260,7 +260,7 @@
 static int snd_ice1712_digmix_route_ac97_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	
+
 	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
 	return 0;
 }
@@ -269,11 +269,12 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char val, nval;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
 	nval = val & ~ICE1712_ROUTE_AC97;
-	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+	if (ucontrol->value.integer.value[0])
+		nval |= ICE1712_ROUTE_AC97;
 	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
 	spin_unlock_irq(&ice->reg_lock);
 	return val != nval;
@@ -329,7 +330,7 @@
 	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */
 	unsigned char val, nval;
 	int res = 0;
-	
+
 	snd_i2c_lock(ice->i2c);
 	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
 		snd_i2c_unlock(ice->i2c);
@@ -381,9 +382,9 @@
 {
 	int err;
 
-	if ((err = snd_cs8427_create(ice->i2c, addr,
-				     (ice->cs8427_timeout * HZ) / 1000,
-				     &ice->cs8427)) < 0) {
+	err = snd_cs8427_create(ice->i2c, addr,
+		(ice->cs8427_timeout * HZ) / 1000, &ice->cs8427);
+	if (err < 0) {
 		snd_printk(KERN_ERR "CS8427 initialization failed\n");
 		return err;
 	}
@@ -395,9 +396,9 @@
 
 static void snd_ice1712_set_input_clock_source(struct snd_ice1712 *ice, int spdif_is_master)
 {
-        /* change CS8427 clock source too */
-        if (ice->cs8427)
-                snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master);
+	/* change CS8427 clock source too */
+	if (ice->cs8427)
+		snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master);
 	/* notify ak4524 chip as well */
 	if (spdif_is_master) {
 		unsigned int i;
@@ -457,11 +458,12 @@
 			u16 pbkstatus;
 			struct snd_pcm_substream *substream;
 			pbkstatus = inw(ICEDS(ice, INTSTAT));
-			//printk("pbkstatus = 0x%x\n", pbkstatus);
+			/* printk("pbkstatus = 0x%x\n", pbkstatus); */
 			for (idx = 0; idx < 6; idx++) {
 				if ((pbkstatus & (3 << (idx * 2))) == 0)
 					continue;
-				if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+				substream = ice->playback_con_substream_ds[idx];
+				if (substream != NULL)
 					snd_pcm_period_elapsed(substream);
 				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
 			}
@@ -507,7 +509,7 @@
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
 	int result = 0;
 	u32 tmp;
-	
+
 	spin_lock(&ice->reg_lock);
 	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
 	if (cmd == SNDRV_PCM_TRIGGER_START) {
@@ -532,7 +534,7 @@
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
 	int result = 0;
 	u32 tmp;
-	
+
 	spin_lock(&ice->reg_lock);
 	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
 	if (cmd == SNDRV_PCM_TRIGGER_START) {
@@ -557,7 +559,7 @@
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
 	int result = 0;
 	u8 tmp;
-	
+
 	spin_lock(&ice->reg_lock);
 	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
 	if (cmd == SNDRV_PCM_TRIGGER_START) {
@@ -711,8 +713,7 @@
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
-static const struct snd_pcm_hardware snd_ice1712_playback =
-{
+static const struct snd_pcm_hardware snd_ice1712_playback = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -731,8 +732,7 @@
 	.fifo_size =		0,
 };
 
-static const struct snd_pcm_hardware snd_ice1712_playback_ds =
-{
+static const struct snd_pcm_hardware snd_ice1712_playback_ds = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -751,8 +751,7 @@
 	.fifo_size =		0,
 };
 
-static const struct snd_pcm_hardware snd_ice1712_capture =
-{
+static const struct snd_pcm_hardware snd_ice1712_capture = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
@@ -788,7 +787,7 @@
 
 	ice->playback_con_substream_ds[substream->number] = substream;
 	runtime->hw = snd_ice1712_playback_ds;
-	spin_lock_irq(&ice->reg_lock); 
+	spin_lock_irq(&ice->reg_lock);
 	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
 	outw(tmp, ICEDS(ice, INTMASK));
 	spin_unlock_irq(&ice->reg_lock);
@@ -821,7 +820,7 @@
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
 	u32 tmp;
 
-	spin_lock_irq(&ice->reg_lock); 
+	spin_lock_irq(&ice->reg_lock);
 	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
 	outw(tmp, ICEDS(ice, INTMASK));
 	spin_unlock_irq(&ice->reg_lock);
@@ -870,7 +869,7 @@
 	.pointer =	snd_ice1712_capture_pointer,
 };
 
-static int __devinit snd_ice1712_pcm(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
 {
 	struct snd_pcm *pcm;
 	int err;
@@ -900,7 +899,7 @@
 	return 0;
 }
 
-static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
 {
 	struct snd_pcm *pcm;
 	int err;
@@ -1029,14 +1028,14 @@
 	if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
 						 ICE1712_PLAYBACK_PAUSE|
 						 ICE1712_PLAYBACK_START)) {
-	      __out:
+__out:
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
 		return;
 	}
 	if (!force && is_pro_rate_locked(ice))
 		goto __out;
 
-        old = inb(ICEMT(ice, RATE));
+	old = inb(ICEMT(ice, RATE));
 	if (!force && old == val)
 		goto __out;
 	outb(val, ICEMT(ice, RATE));
@@ -1123,8 +1122,7 @@
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
-static const struct snd_pcm_hardware snd_ice1712_playback_pro =
-{
+static const struct snd_pcm_hardware snd_ice1712_playback_pro = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -1143,8 +1141,7 @@
 	.fifo_size =		0,
 };
 
-static const struct snd_pcm_hardware snd_ice1712_capture_pro =
-{
+static const struct snd_pcm_hardware snd_ice1712_capture_pro = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -1238,7 +1235,7 @@
 	.pointer =	snd_ice1712_capture_pro_pointer,
 };
 
-static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm)
+static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
 {
 	struct snd_pcm *pcm;
 	int err;
@@ -1262,7 +1259,7 @@
 	ice->pcm_pro = pcm;
 	if (rpcm)
 		*rpcm = pcm;
-	
+
 	if (ice->cs8427) {
 		/* assign channels to iec958 */
 		err = snd_cs8427_iec958_build(ice->cs8427,
@@ -1272,7 +1269,8 @@
 			return err;
 	}
 
-	if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
+	err = snd_ice1712_build_pro_mixer(ice);
+	if (err < 0)
 		return err;
 	return 0;
 }
@@ -1299,7 +1297,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
 		kcontrol->private_value;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	ucontrol->value.integer.value[0] =
 		!((ice->pro_volumes[priv_idx] >> 15) & 1);
@@ -1341,7 +1339,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
 		kcontrol->private_value;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	ucontrol->value.integer.value[0] =
 		(ice->pro_volumes[priv_idx] >> 0) & 127;
@@ -1406,7 +1404,7 @@
 
 static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,SWITCH),
+	.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
 	.info = snd_ice1712_pro_mixer_switch_info,
 	.get = snd_ice1712_pro_mixer_switch_get,
 	.put = snd_ice1712_pro_mixer_switch_put,
@@ -1428,7 +1426,7 @@
 
 static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,VOLUME),
+	.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
 	.info = snd_ice1712_pro_mixer_volume_info,
 	.get = snd_ice1712_pro_mixer_volume_get,
 	.put = snd_ice1712_pro_mixer_volume_put,
@@ -1448,7 +1446,7 @@
 		if (err < 0)
 			return err;
 	}
-	
+
 	if (ice->num_total_adcs > 0) {
 		struct snd_kcontrol_new tmp = snd_ice1712_multi_capture_analog_switch;
 		tmp.count = ice->num_total_adcs;
@@ -1495,7 +1493,7 @@
 	ice->ac97 = NULL;
 }
 
-static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 * ice)
+static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
 {
 	int err, bus_num = 0;
 	struct snd_ac97_template ac97;
@@ -1510,27 +1508,32 @@
 	};
 
 	if (ice_has_con_ac97(ice)) {
-		if ((err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus)) < 0)
+		err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus);
+		if (err < 0)
 			return err;
 		memset(&ac97, 0, sizeof(ac97));
 		ac97.private_data = ice;
 		ac97.private_free = snd_ice1712_mixer_free_ac97;
-		if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
+		if (err < 0)
 			printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
 		else {
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice));
+			if (err < 0)
 				return err;
 			return 0;
 		}
 	}
 
-	if (! (ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) {
-		if ((err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus)) < 0)
+	if (!(ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) {
+		err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus);
+		if (err < 0)
 			return err;
 		memset(&ac97, 0, sizeof(ac97));
 		ac97.private_data = ice;
 		ac97.private_free = snd_ice1712_mixer_free_ac97;
-		if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
+		if (err < 0)
 			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
 		else
 			return 0;
@@ -1549,7 +1552,7 @@
 	return (unsigned int)ice->eeprom.data[idx] | ((unsigned int)ice->eeprom.data[idx + 1] << 8);
 }
 
-static void snd_ice1712_proc_read(struct snd_info_entry *entry, 
+static void snd_ice1712_proc_read(struct snd_info_entry *entry,
 				  struct snd_info_buffer *buffer)
 {
 	struct snd_ice1712 *ice = entry->private_data;
@@ -1585,15 +1588,15 @@
 	snd_iprintf(buffer, "  SPDOUT           : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT)));
 	snd_iprintf(buffer, "  RATE             : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE)));
 	snd_iprintf(buffer, "  GPIO_DATA        : 0x%02x\n", (unsigned)snd_ice1712_get_gpio_data(ice));
-        snd_iprintf(buffer, "  GPIO_WRITE_MASK  : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK));
+	snd_iprintf(buffer, "  GPIO_WRITE_MASK  : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK));
 	snd_iprintf(buffer, "  GPIO_DIRECTION   : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION));
 }
 
-static void __devinit snd_ice1712_proc_init(struct snd_ice1712 * ice)
+static void __devinit snd_ice1712_proc_init(struct snd_ice1712 *ice)
 {
 	struct snd_info_entry *entry;
 
-	if (! snd_card_proc_new(ice->card, "ice1712", &entry))
+	if (!snd_card_proc_new(ice->card, "ice1712", &entry))
 		snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read);
 }
 
@@ -1613,7 +1616,7 @@
 				  struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	
+
 	memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
 	return 0;
 }
@@ -1641,7 +1644,7 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	if (ice->spdif.ops.default_get)
-		ice->spdif.ops.default_get(ice, ucontrol); 
+		ice->spdif.ops.default_get(ice, ucontrol);
 	return 0;
 }
 
@@ -1657,7 +1660,7 @@
 static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =
 {
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
 	.info =		snd_ice1712_spdif_info,
 	.get =		snd_ice1712_spdif_default_get,
 	.put =		snd_ice1712_spdif_default_put
@@ -1709,7 +1712,7 @@
 {
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
 	.info =		snd_ice1712_spdif_info,
 	.get =		snd_ice1712_spdif_maskc_get,
 };
@@ -1718,7 +1721,7 @@
 {
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
 	.info =		snd_ice1712_spdif_info,
 	.get =		snd_ice1712_spdif_maskp_get,
 };
@@ -1746,7 +1749,7 @@
 	.access =	(SNDRV_CTL_ELEM_ACCESS_READWRITE |
 			 SNDRV_CTL_ELEM_ACCESS_INACTIVE),
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
 	.info =		snd_ice1712_spdif_info,
 	.get =		snd_ice1712_spdif_stream_get,
 	.put =		snd_ice1712_spdif_stream_put
@@ -1758,7 +1761,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char mask = kcontrol->private_value & 0xff;
 	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
-	
+
 	snd_ice1712_save_gpio_status(ice);
 	ucontrol->value.integer.value[0] =
 		(snd_ice1712_gpio_read(ice) & mask ? 1 : 0) ^ invert;
@@ -1825,7 +1828,7 @@
 		9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10
 	};
 	unsigned char val;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	if (is_spdif_master(ice)) {
 		ucontrol->value.enumerated.item[0] = 13;
@@ -1867,7 +1870,7 @@
 
 	if ((oval & ICE1712_SPDIF_MASTER) !=
 	    (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER))
-	        snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice));
+		snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice));
 
 	return change;
 }
@@ -1897,7 +1900,7 @@
 		"64000",	/* 10: 15 */
 		"88200",	/* 11: 11 */
 		"96000",	/* 12: 7 */
-		// "IEC958 Input",	/* 13: -- */
+		/* "IEC958 Input",	13: -- */
 	};
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
@@ -2026,7 +2029,7 @@
 		"IEC958 In L", "IEC958 In R", /* 9-10 */
 		"Digital Mixer", /* 11 - optional */
 	};
-	
+
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items =
@@ -2070,7 +2073,7 @@
 	int change, shift;
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int val, old_val, nval;
-	
+
 	/* update PSDOUT */
 	if (ucontrol->value.enumerated.item[0] >= 11)
 		nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
@@ -2140,7 +2143,7 @@
 	int change, shift;
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int val, old_val, nval;
-	
+
 	/* update SPDOUT */
 	spin_lock_irq(&ice->reg_lock);
 	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
@@ -2182,7 +2185,7 @@
 
 static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+	.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
 	.info = snd_ice1712_pro_route_info,
 	.get = snd_ice1712_pro_route_spdif_get,
 	.put = snd_ice1712_pro_route_spdif_put,
@@ -2204,7 +2207,7 @@
 					   struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	
+
 	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
 	return 0;
 }
@@ -2245,7 +2248,7 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	for (idx = 0; idx < 22; idx++) {
 		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
@@ -2296,12 +2299,12 @@
 	unsigned int i, size;
 	struct snd_ice1712_card_info * const *tbl, *c;
 
-	if (! modelname || ! *modelname) {
+	if (!modelname || !*modelname) {
 		ice->eeprom.subvendor = 0;
 		if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) != 0)
 			ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
-				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | 
-				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) |
+				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |
 				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
 		if (ice->eeprom.subvendor == 0 ||
 		    ice->eeprom.subvendor == (unsigned int)-1) {
@@ -2318,12 +2321,12 @@
 	}
 	for (tbl = card_tables; *tbl; tbl++) {
 		for (c = *tbl; c->subvendor; c++) {
-			if (modelname && c->model && ! strcmp(modelname, c->model)) {
+			if (modelname && c->model && !strcmp(modelname, c->model)) {
 				printk(KERN_INFO "ice1712: Using board model %s\n", c->name);
 				ice->eeprom.subvendor = c->subvendor;
 			} else if (c->subvendor != ice->eeprom.subvendor)
 				continue;
-			if (! c->eeprom_size || ! c->eeprom_data)
+			if (!c->eeprom_size || !c->eeprom_data)
 				goto found;
 			/* if the EEPROM is given by the driver, use it */
 			snd_printdd("using the defined eeprom..\n");
@@ -2416,7 +2419,8 @@
 	int err;
 	struct snd_kcontrol *kctl;
 
-	snd_assert(ice->pcm_pro != NULL, return -EIO);
+	if (snd_BUG_ON(!ice->pcm_pro))
+		return -EIO;
 	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
 	if (err < 0)
 		return err;
@@ -2483,13 +2487,13 @@
 
 static int snd_ice1712_free(struct snd_ice1712 *ice)
 {
-	if (! ice->port)
+	if (!ice->port)
 		goto __hw_end;
 	/* mask all interrupts */
 	outb(0xc0, ICEMT(ice, IRQ));
 	outb(0xff, ICEREG(ice, IRQMASK));
 	/* --- */
-      __hw_end:
+__hw_end:
 	if (ice->irq >= 0)
 		free_irq(ice->irq, ice);
 
@@ -2514,7 +2518,7 @@
 					int omni,
 					int cs8427_timeout,
 					int dxr_enable,
-					struct snd_ice1712 ** r_ice1712)
+					struct snd_ice1712 **r_ice1712)
 {
 	struct snd_ice1712 *ice;
 	int err;
@@ -2524,8 +2528,9 @@
 
 	*r_ice1712 = NULL;
 
-        /* enable PCI device */
-	if ((err = pci_enable_device(pci)) < 0)
+	/* enable PCI device */
+	err = pci_enable_device(pci);
+	if (err < 0)
 		return err;
 	/* check, if we can restrict PCI DMA transfers to 28 bits */
 	if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
@@ -2569,7 +2574,8 @@
 	snd_ice1712_proc_init(ice);
 	synchronize_irq(pci->irq);
 
-	if ((err = pci_request_regions(pci, "ICE1712")) < 0) {
+	err = pci_request_regions(pci, "ICE1712");
+	if (err < 0) {
 		kfree(ice);
 		pci_disable_device(pci);
 		return err;
@@ -2585,7 +2591,7 @@
 		snd_ice1712_free(ice);
 		return -EIO;
 	}
-	
+
 	ice->irq = pci->irq;
 
 	if (snd_ice1712_read_eeprom(ice, modelname) < 0) {
@@ -2605,9 +2611,10 @@
 	     ICEREG(ice, IRQMASK));
 	outb(0x00, ICEMT(ice, IRQ));
 
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
+	if (err < 0) {
 		snd_ice1712_free(ice);
- 		return err;
+		return err;
 	}
 
 	snd_card_set_dev(card, &pci->dev);
@@ -2647,10 +2654,10 @@
 
 	strcpy(card->driver, "ICE1712");
 	strcpy(card->shortname, "ICEnsemble ICE1712");
-	
-	if ((err = snd_ice1712_create(card, pci, model[dev], omni[dev],
-				      cs8427_timeout[dev], dxr_enable[dev],
-				      &ice)) < 0) {
+
+	err = snd_ice1712_create(card, pci, model[dev], omni[dev],
+		cs8427_timeout[dev], dxr_enable[dev], &ice);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
@@ -2662,7 +2669,8 @@
 				if (c->driver) /* specific driver? */
 					strcpy(card->driver, c->driver);
 				if (c->chip_init) {
-					if ((err = c->chip_init(ice)) < 0) {
+					err = c->chip_init(ice);
+					if (err < 0) {
 						snd_card_free(card);
 						return err;
 					}
@@ -2674,47 +2682,52 @@
 	c = &no_matched;
  __found:
 
-	if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
+	err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
-	
+
 	if (ice_has_con_ac97(ice))
-		if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
+		err = snd_ice1712_pcm(ice, pcm_dev++, NULL);
+		if (err < 0) {
 			snd_card_free(card);
 			return err;
 		}
 
-	if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
+	err = snd_ice1712_ac97_mixer(ice);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
 
-	if ((err = snd_ice1712_build_controls(ice)) < 0) {
+	err = snd_ice1712_build_controls(ice);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
 
 	if (c->build_controls) {
-		if ((err = c->build_controls(ice)) < 0) {
+		err = c->build_controls(ice);
+		if (err < 0) {
 			snd_card_free(card);
 			return err;
 		}
 	}
 
 	if (ice_has_con_ac97(ice))
-		if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
+		err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL);
+		if (err < 0) {
 			snd_card_free(card);
 			return err;
 		}
 
-	if (! c->no_mpu401) {
-		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
-					       ICEREG(ice, MPU1_CTRL),
-					       (c->mpu401_1_info_flags |
-						MPU401_INFO_INTEGRATED),
-					       ice->irq, 0,
-					       &ice->rmidi[0])) < 0) {
+	if (!c->no_mpu401) {
+		err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+			ICEREG(ice, MPU1_CTRL),
+			(c->mpu401_1_info_flags | MPU401_INFO_INTEGRATED),
+			ice->irq, 0, &ice->rmidi[0]);
+		if (err < 0) {
 			snd_card_free(card);
 			return err;
 		}
@@ -2726,12 +2739,12 @@
 
 		if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {
 			/*  2nd port used  */
-			if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
-						       ICEREG(ice, MPU2_CTRL),
-						       (c->mpu401_2_info_flags |
-							MPU401_INFO_INTEGRATED),
-						       ice->irq, 0,
-						       &ice->rmidi[1])) < 0) {
+			err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
+				ICEREG(ice, MPU2_CTRL),
+				(c->mpu401_2_info_flags | MPU401_INFO_INTEGRATED),
+				ice->irq, 0, &ice->rmidi[1]);
+
+			if (err < 0) {
 				snd_card_free(card);
 				return err;
 			}
@@ -2749,7 +2762,8 @@
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
 		card->shortname, ice->port, ice->irq);
 
-	if ((err = snd_card_register(card)) < 0) {
+	err = snd_card_register(card);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 762fbd7..fdae6de 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -20,7 +20,7 @@
  *   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/control.h>
 #include <sound/ac97_codec.h>
@@ -112,7 +112,7 @@
  */
 
 #define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
- 
+
 #define ICE1712_DS_INTMASK		0x00	/* word - interrupt mask */
 #define ICE1712_DS_INTSTAT		0x02	/* word - interrupt status */
 #define ICE1712_DS_DATA			0x04	/* dword - channel data */
@@ -121,7 +121,7 @@
 /*
  *  Consumer section channel registers
  */
- 
+
 #define ICE1712_DSC_ADDR0		0x00	/* dword - base address 0 */
 #define ICE1712_DSC_COUNT0		0x01	/* word - count 0 */
 #define ICE1712_DSC_ADDR1		0x02	/* dword - base address 1 */
@@ -138,7 +138,7 @@
 #define ICE1712_DSC_RATE		0x05	/* dword - rate */
 #define ICE1712_DSC_VOLUME		0x06	/* word - volume control */
 
-/* 
+/*
  *  Professional multi-track direct control registers
  */
 
@@ -214,7 +214,7 @@
 
 
 /*
- *  
+ *
  */
 
 struct snd_ice1712;
@@ -253,12 +253,12 @@
 	ICE_EEP1_ADC_ID2,
 	ICE_EEP1_ADC_ID3
 };
-	
+
 #define ice_has_con_ac97(ice)	(!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97))
 
 
 struct snd_ak4xxx_private {
-	unsigned int cif: 1;		/* CIF mode */
+	unsigned int cif:1;		/* CIF mode */
 	unsigned char caddr;		/* C0 and C1 bits */
 	unsigned int data_mask;		/* DATA gpio bit */
 	unsigned int clk_mask;		/* CLK gpio bit */
@@ -306,11 +306,11 @@
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm_ds;
 	struct snd_pcm *pcm_pro;
-        struct snd_pcm_substream *playback_con_substream;
-        struct snd_pcm_substream *playback_con_substream_ds[6];
-        struct snd_pcm_substream *capture_con_substream;
-        struct snd_pcm_substream *playback_pro_substream;
-        struct snd_pcm_substream *capture_pro_substream;
+	struct snd_pcm_substream *playback_con_substream;
+	struct snd_pcm_substream *playback_con_substream_ds[6];
+	struct snd_pcm_substream *capture_con_substream;
+	struct snd_pcm_substream *playback_pro_substream;
+	struct snd_pcm_substream *capture_pro_substream;
 	unsigned int playback_pro_size;
 	unsigned int capture_pro_size;
 	unsigned int playback_con_virt_addr[6];
@@ -326,15 +326,15 @@
 	struct snd_ice1712_eeprom eeprom;
 
 	unsigned int pro_volumes[20];
-	unsigned int omni: 1;		/* Delta Omni I/O */
-	unsigned int dxr_enable: 1;	/* Terratec DXR enable for DMX6FIRE */
-	unsigned int vt1724: 1;
-	unsigned int vt1720: 1;
-	unsigned int has_spdif: 1;	/* VT1720/4 - has SPDIF I/O */
-	unsigned int force_pdma4: 1;	/* VT1720/4 - PDMA4 as non-spdif */
-	unsigned int force_rdma1: 1;	/* VT1720/4 - RDMA1 as non-spdif */
-	unsigned int midi_output: 1;	/* VT1720/4: MIDI output triggered */
-	unsigned int midi_input: 1;	/* VT1720/4: MIDI input triggered */
+	unsigned int omni:1;		/* Delta Omni I/O */
+	unsigned int dxr_enable:1;	/* Terratec DXR enable for DMX6FIRE */
+	unsigned int vt1724:1;
+	unsigned int vt1720:1;
+	unsigned int has_spdif:1;	/* VT1720/4 - has SPDIF I/O */
+	unsigned int force_pdma4:1;	/* VT1720/4 - PDMA4 as non-spdif */
+	unsigned int force_rdma1:1;	/* VT1720/4 - RDMA1 as non-spdif */
+	unsigned int midi_output:1;	/* VT1720/4: MIDI output triggered */
+	unsigned int midi_input:1;	/* VT1720/4: MIDI input triggered */
 	unsigned int num_total_dacs;	/* total DACs */
 	unsigned int num_total_adcs;	/* total ADCs */
 	unsigned int cur_rate;		/* current rate */
@@ -351,7 +351,7 @@
 	struct snd_i2c_bus *i2c;		/* I2C bus */
 	struct snd_i2c_device *cs8427;	/* CS8427 I2C device */
 	unsigned int cs8427_timeout;	/* CS8427 reset timeout in HZ/100 */
-	
+
 	struct ice1712_gpio {
 		unsigned int direction;		/* current direction bits */
 		unsigned int write_mask;	/* current mask bits */
@@ -455,7 +455,7 @@
 {
 	ice->gpio.direction &= ~mask;
 	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
-	return  (snd_ice1712_gpio_read(ice) & mask);
+	return  snd_ice1712_gpio_read(ice) & mask;
 }
 
 int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
@@ -467,13 +467,13 @@
 
 int snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr);
 
-static inline void snd_ice1712_write(struct snd_ice1712 * ice, u8 addr, u8 data)
+static inline void snd_ice1712_write(struct snd_ice1712 *ice, u8 addr, u8 data)
 {
 	outb(addr, ICEREG(ice, INDEX));
 	outb(data, ICEREG(ice, DATA));
 }
 
-static inline u8 snd_ice1712_read(struct snd_ice1712 * ice, u8 addr)
+static inline u8 snd_ice1712_read(struct snd_ice1712 *ice, u8 addr)
 {
 	outb(addr, ICEREG(ice, INDEX));
 	return inb(ICEREG(ice, DATA));
@@ -491,7 +491,7 @@
 	char *driver;
 	int (*chip_init)(struct snd_ice1712 *);
 	int (*build_controls)(struct snd_ice1712 *);
-	unsigned int no_mpu401: 1;
+	unsigned int no_mpu401:1;
 	unsigned int mpu401_1_info_flags;
 	unsigned int mpu401_2_info_flags;
 	const char *mpu401_1_name;
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index e596d77..1b3f117 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -20,9 +20,9 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
- */      
+ */
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -105,7 +105,7 @@
 /*
  *  Basic I/O
  */
- 
+
 /*
  *  default rates, default clock routines
  */
@@ -198,7 +198,7 @@
 static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
 {
 	outw(data, ICEREG1724(ice, GPIO_WRITE_MASK));
-	if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */
+	if (!ice->vt1720) /* VT1720 supports only 16 GPIO bits */
 		outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22));
 	inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
 }
@@ -206,7 +206,7 @@
 static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
 {
 	outw(data, ICEREG1724(ice, GPIO_DATA));
-	if (! ice->vt1720)
+	if (!ice->vt1720)
 		outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22));
 	inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */
 }
@@ -214,7 +214,7 @@
 static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
 {
 	unsigned int data;
-	if (! ice->vt1720)
+	if (!ice->vt1720)
 		data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22));
 	else
 		data = 0;
@@ -399,7 +399,7 @@
 			break;
 		}
 #endif
-		handled = 1;		
+		handled = 1;
 		if (status & VT1724_IRQ_MPU_TX) {
 			spin_lock(&ice->reg_lock);
 			if (ice->midi_output)
@@ -468,8 +468,8 @@
 			/* ought to really handle this properly */
 			if (mtstat & VT1724_MULTI_FIFO_ERR) {
 				unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR));
-				outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));	
-				outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));	
+				outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));
+				outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));
 				/* If I don't do this, I get machine lockup due to continual interrupts */
 			}
 
@@ -733,17 +733,17 @@
 	outl(substream->runtime->dma_addr, ICEMT1724(ice, PLAYBACK_ADDR));
 
 	size = (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1;
-	// outl(size, ICEMT1724(ice, PLAYBACK_SIZE));
+	/* outl(size, ICEMT1724(ice, PLAYBACK_SIZE)); */
 	outw(size, ICEMT1724(ice, PLAYBACK_SIZE));
 	outb(size >> 16, ICEMT1724(ice, PLAYBACK_SIZE) + 2);
 	size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
-	// outl(size, ICEMT1724(ice, PLAYBACK_COUNT));
+	/* outl(size, ICEMT1724(ice, PLAYBACK_COUNT)); */
 	outw(size, ICEMT1724(ice, PLAYBACK_COUNT));
 	outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);
 
 	spin_unlock_irq(&ice->reg_lock);
 
-	// printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream));
+	/* printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); */
 	return 0;
 }
 
@@ -771,7 +771,7 @@
 	ptr = inl(ICEMT1724(ice, PLAYBACK_SIZE)) & 0xffffff;
 	ptr = (ptr + 1) << 2;
 	ptr = bytes_to_frames(substream->runtime, ptr);
-	if (! ptr)
+	if (!ptr)
 		;
 	else if (ptr <= substream->runtime->buffer_size)
 		ptr = substream->runtime->buffer_size - ptr;
@@ -815,7 +815,7 @@
 	ptr = inw(ice->profi_port + reg->size);
 	ptr = (ptr + 1) << 2;
 	ptr = bytes_to_frames(substream->runtime, ptr);
-	if (! ptr)
+	if (!ptr)
 		;
 	else if (ptr <= substream->runtime->buffer_size)
 		ptr = substream->runtime->buffer_size - ptr;
@@ -842,8 +842,7 @@
 	.start = VT1724_RDMA0_START,
 };
 
-static const struct snd_pcm_hardware snd_vt1724_playback_pro =
-{
+static const struct snd_pcm_hardware snd_vt1724_playback_pro = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -861,8 +860,7 @@
 	.periods_max =		1024,
 };
 
-static const struct snd_pcm_hardware snd_vt1724_spdif =
-{
+static const struct snd_pcm_hardware snd_vt1724_spdif = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -883,8 +881,7 @@
 	.periods_max =		1024,
 };
 
-static const struct snd_pcm_hardware snd_vt1724_2ch_stereo =
-{
+static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
@@ -942,7 +939,7 @@
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
-	int chs;
+	int chs, num_indeps;
 
 	runtime->private_data = (void *)&vt1724_playback_pro_reg;
 	ice->playback_pro_substream = substream;
@@ -952,7 +949,8 @@
 	set_rate_constraints(ice, substream);
 	mutex_lock(&ice->open_mutex);
 	/* calculate the currently available channels */
-	for (chs = 0; chs < 3; chs++) {
+	num_indeps = ice->num_total_dacs / 2 - 1;
+	for (chs = 0; chs < num_indeps; chs++) {
 		if (ice->pcm_reserved[chs])
 			break;
 	}
@@ -1029,7 +1027,7 @@
 	.pointer =	snd_vt1724_pcm_pointer,
 };
 
-static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 * ice, int device)
+static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
 {
 	struct snd_pcm *pcm;
 	int err;
@@ -1114,7 +1112,7 @@
 static int snd_vt1724_playback_spdif_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
-	if (! ice->force_pdma4)
+	if (!ice->force_pdma4)
 		update_spdif_rate(ice, substream->runtime->rate);
 	return snd_vt1724_pcm_prepare(substream);
 }
@@ -1214,7 +1212,7 @@
 };
 
 
-static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device)
+static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
 {
 	char *name;
 	struct snd_pcm *pcm;
@@ -1233,7 +1231,7 @@
 		ice->has_spdif = 1;
 	} else
 		capt = 0;
-	if (! play && ! capt)
+	if (!play && !capt)
 		return 0; /* no spdif device */
 
 	if (ice->force_pdma4 || ice->force_rdma1)
@@ -1348,7 +1346,7 @@
 };
 
 
-static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 * ice, int device)
+static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
 {
 	struct snd_pcm *pcm;
 	int play;
@@ -1383,11 +1381,11 @@
  *  Mixer section
  */
 
-static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 * ice)
+static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
 {
 	int err;
 
-	if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
+	if (!(ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
 		struct snd_ac97_bus *pbus;
 		struct snd_ac97_template ac97;
 		static struct snd_ac97_bus_ops ops = {
@@ -1400,11 +1398,13 @@
 		mdelay(5); /* FIXME */
 		outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
 
-		if ((err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus)) < 0)
+		err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus);
+		if (err < 0)
 			return err;
 		memset(&ac97, 0, sizeof(ac97));
 		ac97.private_data = ice;
-		if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
+		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
+		if (err < 0)
 			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
 		else
 			return 0;
@@ -1425,7 +1425,7 @@
 		((unsigned int)ice->eeprom.data[idx + 2] << 16);
 }
 
-static void snd_vt1724_proc_read(struct snd_info_entry *entry, 
+static void snd_vt1724_proc_read(struct snd_info_entry *entry,
 				 struct snd_info_buffer *buffer)
 {
 	struct snd_ice1712 *ice = entry->private_data;
@@ -1467,11 +1467,11 @@
 			    idx, inb(ice->profi_port+idx));
 }
 
-static void __devinit snd_vt1724_proc_init(struct snd_ice1712 * ice)
+static void __devinit snd_vt1724_proc_init(struct snd_ice1712 *ice)
 {
 	struct snd_info_entry *entry;
 
-	if (! snd_card_proc_new(ice->card, "ice1724", &entry))
+	if (!snd_card_proc_new(ice->card, "ice1724", &entry))
 		snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);
 }
 
@@ -1491,7 +1491,7 @@
 				 struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	
+
 	memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
 	return 0;
 }
@@ -1606,13 +1606,13 @@
 	if (val != old)
 		update_spdif_bits(ice, val);
 	spin_unlock_irq(&ice->reg_lock);
-	return (val != old);
+	return val != old;
 }
 
 static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
 {
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
 	.info =		snd_vt1724_spdif_info,
 	.get =		snd_vt1724_spdif_default_get,
 	.put =		snd_vt1724_spdif_default_put
@@ -1645,7 +1645,7 @@
 {
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
 	.info =		snd_vt1724_spdif_info,
 	.get =		snd_vt1724_spdif_maskc_get,
 };
@@ -1654,7 +1654,7 @@
 {
 	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
 	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
-	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
 	.info =		snd_vt1724_spdif_info,
 	.get =		snd_vt1724_spdif_maskp_get,
 };
@@ -1691,8 +1691,8 @@
 {
 	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
 	/* FIXME: the following conflict with IEC958 Playback Route */
-	// .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
-	.name =         SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
+	/* .name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), */
+	.name =         SNDRV_CTL_NAME_IEC958("Output ", NONE, SWITCH),
 	.info =		snd_vt1724_spdif_sw_info,
 	.get =		snd_vt1724_spdif_sw_get,
 	.put =		snd_vt1724_spdif_sw_put
@@ -1712,7 +1712,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int shift = kcontrol->private_value & 0xff;
 	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
-	
+
 	snd_ice1712_save_gpio_status(ice);
 	ucontrol->value.integer.value[0] =
 		(snd_ice1712_gpio_read(ice) & (1 << shift) ? 1 : 0) ^ invert;
@@ -1767,7 +1767,7 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned int i, rate;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	if (ice->is_spdif_master(ice)) {
 		ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
@@ -1923,7 +1923,7 @@
 		"H/W In 0", "H/W In 1", /* 1-2 */
 		"IEC958 In L", "IEC958 In R", /* 3-4 */
 	};
-	
+
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items = 5;
@@ -1953,7 +1953,7 @@
 
 	val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
 	val >>= shift;
-	val &= 7;	//we now have 3 bits per output
+	val &= 7; /* we now have 3 bits per output */
 	eitem = xlate[val];
 	if (eitem == 255) {
 		snd_BUG();
@@ -2032,7 +2032,7 @@
 
 static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
+	.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
 	.info = snd_vt1724_pro_route_info,
 	.get = snd_vt1724_pro_route_spdif_get,
 	.put = snd_vt1724_pro_route_spdif_put,
@@ -2055,7 +2055,7 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx;
-	
+
 	spin_lock_irq(&ice->reg_lock);
 	for (idx = 0; idx < 22; idx++) {
 		outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX));
@@ -2082,7 +2082,7 @@
 
 static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
 	snd_vt1724_revo_cards,
-	snd_vt1724_amp_cards, 
+	snd_vt1724_amp_cards,
 	snd_vt1724_aureon_cards,
 	snd_vt1720_mobo_cards,
 	snd_vt1720_pontis_cards,
@@ -2120,7 +2120,7 @@
 	wait_i2c_busy(ice);
 	val = inb(ICEREG1724(ice, I2C_DATA));
 	mutex_unlock(&ice->i2c_mutex);
-	//printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
+	/* printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val); */
 	return val;
 }
 
@@ -2129,7 +2129,7 @@
 {
 	mutex_lock(&ice->i2c_mutex);
 	wait_i2c_busy(ice);
-	//printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
+	/* printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data); */
 	outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
 	outb(data, ICEREG1724(ice, I2C_DATA));
 	outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
@@ -2144,13 +2144,13 @@
 	unsigned int i, size;
 	struct snd_ice1712_card_info * const *tbl, *c;
 
-	if (! modelname || ! *modelname) {
+	if (!modelname || !*modelname) {
 		ice->eeprom.subvendor = 0;
 		if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) != 0)
 			ice->eeprom.subvendor =
 				(snd_vt1724_read_i2c(ice, dev, 0x00) << 0) |
-				(snd_vt1724_read_i2c(ice, dev, 0x01) << 8) | 
-				(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) | 
+				(snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |
+				(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |
 				(snd_vt1724_read_i2c(ice, dev, 0x03) << 24);
 		if (ice->eeprom.subvendor == 0 ||
 		    ice->eeprom.subvendor == (unsigned int)-1) {
@@ -2173,13 +2173,13 @@
 	for (tbl = card_tables; *tbl; tbl++) {
 		for (c = *tbl; c->subvendor; c++) {
 			if (modelname && c->model &&
-			    ! strcmp(modelname, c->model)) {
+			    !strcmp(modelname, c->model)) {
 				printk(KERN_INFO "ice1724: Using board model %s\n",
 				       c->name);
 				ice->eeprom.subvendor = c->subvendor;
 			} else if (c->subvendor != ice->eeprom.subvendor)
 				continue;
-			if (! c->eeprom_size || ! c->eeprom_data)
+			if (!c->eeprom_size || !c->eeprom_data)
 				goto found;
 			/* if the EEPROM is given by the driver, use it */
 			snd_printdd("using the defined eeprom..\n");
@@ -2250,7 +2250,8 @@
 	int err;
 	struct snd_kcontrol *kctl;
 
-	snd_assert(ice->pcm != NULL, return -EIO);
+	if (snd_BUG_ON(!ice->pcm))
+		return -EIO;
 
 	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
 	if (err < 0)
@@ -2320,13 +2321,13 @@
 
 static int snd_vt1724_free(struct snd_ice1712 *ice)
 {
-	if (! ice->port)
+	if (!ice->port)
 		goto __hw_end;
 	/* mask all interrupts */
 	outb(0xff, ICEMT1724(ice, DMA_INT_MASK));
 	outb(0xff, ICEREG1724(ice, IRQMASK));
 	/* --- */
-      __hw_end:
+__hw_end:
 	if (ice->irq >= 0)
 		free_irq(ice->irq, ice);
 	pci_release_regions(ice->pci);
@@ -2346,7 +2347,7 @@
 static int __devinit snd_vt1724_create(struct snd_card *card,
 				       struct pci_dev *pci,
 				       const char *modelname,
-				       struct snd_ice1712 ** r_ice1712)
+				       struct snd_ice1712 **r_ice1712)
 {
 	struct snd_ice1712 *ice;
 	int err;
@@ -2357,8 +2358,9 @@
 
 	*r_ice1712 = NULL;
 
-        /* enable PCI device */
-	if ((err = pci_enable_device(pci)) < 0)
+	/* enable PCI device */
+	err = pci_enable_device(pci);
+	if (err < 0)
 		return err;
 
 	ice = kzalloc(sizeof(*ice), GFP_KERNEL);
@@ -2382,7 +2384,8 @@
 	snd_vt1724_proc_init(ice);
 	synchronize_irq(pci->irq);
 
-	if ((err = pci_request_regions(pci, "ICE1724")) < 0) {
+	err = pci_request_regions(pci, "ICE1724");
+	if (err < 0) {
 		kfree(ice);
 		pci_disable_device(pci);
 		return err;
@@ -2417,9 +2420,10 @@
 	 */
 	outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
 
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
+	if (err < 0) {
 		snd_vt1724_free(ice);
- 		return err;
+		return err;
 	}
 
 	snd_card_set_dev(card, &pci->dev);
@@ -2457,8 +2461,9 @@
 
 	strcpy(card->driver, "ICE1724");
 	strcpy(card->shortname, "ICEnsemble ICE1724");
-	
-	if ((err = snd_vt1724_create(card, pci, model[dev], &ice)) < 0) {
+
+	err = snd_vt1724_create(card, pci, model[dev], &ice);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
@@ -2470,7 +2475,8 @@
 				if (c->driver) /* specific driver? */
 					strcpy(card->driver, c->driver);
 				if (c->chip_init) {
-					if ((err = c->chip_init(ice)) < 0) {
+					err = c->chip_init(ice);
+					if (err < 0) {
 						snd_card_free(card);
 						return err;
 					}
@@ -2480,15 +2486,15 @@
 		}
 	}
 	c = &no_matched;
- __found:
-       /*
-        * VT1724 has separate DMAs for the analog and the SPDIF streams while
-        * ICE1712 has only one for both (mixed up).
-        *
-        * Confusingly the analog PCM is named "professional" here because it
-        * was called so in ice1712 driver, and vt1724 driver is derived from
-        * ice1712 driver.
-        */
+__found:
+	/*
+	* VT1724 has separate DMAs for the analog and the SPDIF streams while
+	* ICE1712 has only one for both (mixed up).
+	*
+	* Confusingly the analog PCM is named "professional" here because it
+	* was called so in ice1712 driver, and vt1724 driver is derived from
+	* ice1712 driver.
+	*/
 	ice->pro_rate_default = PRO_RATE_DEFAULT;
 	if (!ice->is_spdif_master)
 		ice->is_spdif_master = stdclock_is_spdif_master;
@@ -2503,46 +2509,53 @@
 	if (!ice->hw_rates)
 		set_std_hw_rates(ice);
 
-	if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	
-	if ((err = snd_vt1724_pcm_spdif(ice, pcm_dev++)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	
-	if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) {
+	err = snd_vt1724_pcm_profi(ice, pcm_dev++);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
 
-	if ((err = snd_vt1724_ac97_mixer(ice)) < 0) {
+	err = snd_vt1724_pcm_spdif(ice, pcm_dev++);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
 
-	if ((err = snd_vt1724_build_controls(ice)) < 0) {
+	err = snd_vt1724_pcm_indep(ice, pcm_dev++);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	err = snd_vt1724_ac97_mixer(ice);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	err = snd_vt1724_build_controls(ice);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
 
 	if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
-		if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) {
+		err = snd_vt1724_spdif_build_controls(ice);
+		if (err < 0) {
 			snd_card_free(card);
 			return err;
 		}
 	}
 
 	if (c->build_controls) {
-		if ((err = c->build_controls(ice)) < 0) {
+		err = c->build_controls(ice);
+		if (err < 0) {
 			snd_card_free(card);
 			return err;
 		}
 	}
 
-	if (! c->no_mpu401) {
+	if (!c->no_mpu401) {
 		if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
 			struct snd_rawmidi *rmidi;
 
@@ -2574,7 +2587,8 @@
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
 		card->shortname, ice->port, ice->irq);
 
-	if ((err = snd_card_register(card)) < 0) {
+	err = snd_card_register(card);
+	if (err < 0) {
 		snd_card_free(card);
 		return err;
 	}
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index b4e0c16..c51659b 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -21,7 +21,7 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
- */      
+ */
 
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -34,9 +34,10 @@
 #include "ice1712.h"
 #include "envy24ht.h"
 #include "juli.h"
+
 struct juli_spec {
 	struct ak4114 *ak4114;
-	unsigned int analog: 1;
+	unsigned int analog:1;
 };
 
 /*
@@ -160,14 +161,17 @@
 	return 0;
 }
 
-static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val)
+static void juli_ak4114_write(void *private_data, unsigned char reg,
+				unsigned char val)
 {
-	snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg, val);
+	snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR,
+				reg, val);
 }
-        
+
 static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
 {
-	return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg);
+	return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data,
+					AK4114_ADDR, reg);
 }
 
 /*
@@ -175,7 +179,7 @@
  * to the external rate
  */
 static void juli_spdif_in_open(struct snd_ice1712 *ice,
-			       struct snd_pcm_substream *substream)
+				struct snd_pcm_substream *substream)
 {
 	struct juli_spec *spec = ice->spec;
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -208,7 +212,8 @@
 {
 	struct snd_ice1712 *ice = ak->private_data[0];
 	 
-	snd_assert(chip == 0, return);
+	if (snd_BUG_ON(chip))
+		return;
 	snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data);
 }
 
@@ -571,10 +576,12 @@
 static int __devinit juli_init(struct snd_ice1712 *ice)
 {
 	static const unsigned char ak4114_init_vals[] = {
-		/* AK4117_REG_PWRDN */	AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
+		/* AK4117_REG_PWRDN */	AK4114_RST | AK4114_PWN |
+					AK4114_OCKS0 | AK4114_OCKS1,
 		/* AK4114_REQ_FORMAT */	AK4114_DIF_I24I2S,
 		/* AK4114_REG_IO0 */	AK4114_TX1E,
-		/* AK4114_REG_IO1 */	AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
+		/* AK4114_REG_IO1 */	AK4114_EFH_1024 | AK4114_DIT |
+					AK4114_IPS(1),
 		/* AK4114_REG_INT0_MASK */ 0,
 		/* AK4114_REG_INT1_MASK */ 0
 	};
@@ -604,12 +611,14 @@
 	spec->ak4114->check_flags = 0;
 
 #if 0
-        /* it seems that the analog doughter board detection does not work
-           reliably, so force the analog flag; it should be very rare
-           to use Juli@ without the analog doughter board */
+/*
+ * it seems that the analog doughter board detection does not work reliably, so
+ * force the analog flag; it should be very rare (if ever) to come at Juli@
+ * used without the analog daughter board
+ */
 	spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
 #else
-        spec->analog = 1;
+	spec->analog = 1;
 #endif
 
 	if (spec->analog) {
@@ -617,14 +626,16 @@
 		ice->num_total_dacs = 2;
 		ice->num_total_adcs = 2;
 
-		ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
-		if (! ak)
+		ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+		ak = ice->akm;
+		if (!ak)
 			return -ENOMEM;
 		ice->akm_codecs = 1;
-		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice)) < 0)
+		err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice);
+		if (err < 0)
 			return err;
 	}
-	
+
 	/* juli is clocked by Xilinx array */
 	ice->hw_rates = &juli_rates_info;
 	ice->is_spdif_master = juli_is_spdif_master;
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index 5a158b7..de29be8 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -22,15 +22,24 @@
  */
 
 /* PHASE 22 overview:
- *   Audio controller: VIA Envy24HT-S (slightly trimmed down version of Envy24HT)
+ *   Audio controller: VIA Envy24HT-S (slightly trimmed down Envy24HT, 4in/4out)
  *   Analog chip: AK4524 (partially via Philip's 74HCT125)
- *   Digital receiver: CS8414-CS (not supported in this release)
+ *   Digital receiver: CS8414-CS (supported in this release)
+ *		PHASE 22 revision 2.0 and Terrasoniq/Musonik TS22PCI have CS8416
+ *		(support status unknown, please test and report)
  *
  *   Envy connects to AK4524
  *	- CS directly from GPIO 10
  *	- CCLK via 74HCT125's gate #4 from GPIO 4
  *	- CDTI via 74HCT125's gate #2 from GPIO 5
- *		CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
+ *		CDTI may be completely blocked by 74HCT125's gate #1
+ *		controlled by GPIO 3
+ */
+
+/* PHASE 28 overview:
+ *   Audio controller: VIA Envy24HT (full untrimmed version, 4in/8out)
+ *   Analog chip: WM8770 (8 channel 192k DAC, 2 channel 96k ADC)
+ *   Digital receiver: CS8414-CS (supported in this release)
  */
 
 #include <asm/io.h>
@@ -77,18 +86,18 @@
  * Computed as 20 * Log10(255 / x)
  */
 static const unsigned char wm_vol[256] = {
-	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
-	23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
-	17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
-	13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
-	11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
-	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
-	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-	5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
-	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0
+	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24,
+	24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18,
+	17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14,
+	14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11,
+	11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9,
+	9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
 #define WM_VOL_MAX	(sizeof(wm_vol) - 1)
@@ -117,26 +126,31 @@
 	struct snd_akm4xxx *ak;
 	int err;
 
-	// Configure DAC/ADC description for generic part of ice1724
+	/* Configure DAC/ADC description for generic part of ice1724 */
 	switch (ice->eeprom.subvendor) {
 	case VT1724_SUBDEVICE_PHASE22:
+	case VT1724_SUBDEVICE_TS22:
 		ice->num_total_dacs = 2;
 		ice->num_total_adcs = 2;
-		ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
+		ice->vt1720 = 1; /* Envy24HT-S have 16 bit wide GPIO */
 		break;
 	default:
 		snd_BUG();
 		return -EINVAL;
 	}
 
-	// Initialize analog chips
-	ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
-	if (! ak)
+	/* Initialize analog chips */
+	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+	ak = ice->akm;
+	if (!ak)
 		return -ENOMEM;
 	ice->akm_codecs = 1;
 	switch (ice->eeprom.subvendor) {
 	case VT1724_SUBDEVICE_PHASE22:
-		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
+	case VT1724_SUBDEVICE_TS22:
+		err = snd_ice1712_akm4xxx_init(ak, &akm_phase22,
+						&akm_phase22_priv, ice);
+		if (err < 0)
 			return err;
 		break;
 	}
@@ -150,6 +164,7 @@
 
 	switch (ice->eeprom.subvendor) {
 	case VT1724_SUBDEVICE_PHASE22:
+	case VT1724_SUBDEVICE_TS22:
 		err = snd_ice1712_akm4xxx_build_controls(ice);
 		if (err < 0)
 			return err;
@@ -158,9 +173,10 @@
 }
 
 static unsigned char phase22_eeprom[] __devinitdata = {
-	[ICE_EEP2_SYSCONF]     = 0x00,	/* 1xADC, 1xDACs */
+	[ICE_EEP2_SYSCONF]     = 0x28,  /* clock 512, mpu 401,
+					spdif-in/1xADC, 1xDACs */
 	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
-	[ICE_EEP2_I2S]         = 0xf8,	/* vol, 96k, 24bit */
+	[ICE_EEP2_I2S]         = 0xf0,	/* vol, 96k, 24bit */
 	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */
 	[ICE_EEP2_GPIO_DIR]    = 0xff,
 	[ICE_EEP2_GPIO_DIR1]   = 0xff,
@@ -174,7 +190,8 @@
 };
 
 static unsigned char phase28_eeprom[] __devinitdata = {
-	[ICE_EEP2_SYSCONF]     = 0x0b,	/* clock 512, spdif-in/ADC, 4DACs */
+	[ICE_EEP2_SYSCONF]     = 0x2b,  /* clock 512, mpu401,
+					spdif-in/1xADC, 4xDACs */
 	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
 	[ICE_EEP2_I2S]         = 0xfc,	/* vol, 96k, 24bit, 192k */
 	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */
@@ -192,15 +209,16 @@
 /*
  * write data in the SPI mode
  */
-static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits)
+static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs,
+				unsigned int data, int bits)
 {
 	unsigned int tmp;
 	int i;
 
 	tmp = snd_ice1712_gpio_read(ice);
 
-	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
-					 PHASE28_WM_CS));
+	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|
+					PHASE28_SPI_CLK|PHASE28_WM_CS));
 	tmp |= PHASE28_WM_RW;
 	tmp &= ~cs;
 	snd_ice1712_gpio_write(ice, tmp);
@@ -259,14 +277,16 @@
 	ice->akm[0].images[reg + 1] = val;
 }
 
-static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
+static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index,
+			unsigned short vol, unsigned short master)
 {
 	unsigned char nvol;
 
 	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
 		nvol = 0;
 	else
-		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
+		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) *
+			(master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
 
 	wm_put(ice, index, nvol);
 	wm_put_nocache(ice, index, 0x180 | nvol);
@@ -277,17 +297,20 @@
  */
 #define wm_pcm_mute_info	snd_ctl_boolean_mono_info
 
-static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 
 	mutex_lock(&ice->gpio_mutex);
-	ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
+	ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ?
+						0 : 1;
 	mutex_unlock(&ice->gpio_mutex);
 	return 0;
 }
 
-static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned short nval, oval;
@@ -296,7 +319,8 @@
 	snd_ice1712_save_gpio_status(ice);
 	oval = wm_get(ice, WM_MUTE);
 	nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
-	if ((change = (nval != oval)))
+	change = (nval != oval);
+	if (change)
 		wm_put(ice, WM_MUTE, nval);
 	snd_ice1712_restore_gpio_status(ice);
 
@@ -306,7 +330,8 @@
 /*
  * Master volume attenuation mixer control
  */
-static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int wm_master_vol_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 2;
@@ -315,17 +340,20 @@
 	return 0;
 }
 
-static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_master_vol_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
 	int i;
-	for (i=0; i<2; i++)
-		ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE;
+	for (i = 0; i < 2; i++)
+		ucontrol->value.integer.value[i] = spec->master[i] &
+							~WM_VOL_MUTE;
 	return 0;
 }
 
-static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -355,38 +383,38 @@
 {
 	static const unsigned short wm_inits_phase28[] = {
 		/* These come first to reduce init pop noise */
-		0x1b, 0x044,		/* ADC Mux (AC'97 source) */
-		0x1c, 0x00B,		/* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
-		0x1d, 0x009,		/* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
+		0x1b, 0x044,	/* ADC Mux (AC'97 source) */
+		0x1c, 0x00B,	/* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
+		0x1d, 0x009,	/* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
 
-		0x18, 0x000,		/* All power-up */
+		0x18, 0x000,	/* All power-up */
 
-		0x16, 0x122,		/* I2S, normal polarity, 24bit */
-		0x17, 0x022,		/* 256fs, slave mode */
-		0x00, 0,		/* DAC1 analog mute */
-		0x01, 0,		/* DAC2 analog mute */
-		0x02, 0,		/* DAC3 analog mute */
-		0x03, 0,		/* DAC4 analog mute */
-		0x04, 0,		/* DAC5 analog mute */
-		0x05, 0,		/* DAC6 analog mute */
-		0x06, 0,		/* DAC7 analog mute */
-		0x07, 0,		/* DAC8 analog mute */
-		0x08, 0x100,		/* master analog mute */
-		0x09, 0xff,		/* DAC1 digital full */
-		0x0a, 0xff,		/* DAC2 digital full */
-		0x0b, 0xff,		/* DAC3 digital full */
-		0x0c, 0xff,		/* DAC4 digital full */
-		0x0d, 0xff,		/* DAC5 digital full */
-		0x0e, 0xff,		/* DAC6 digital full */
-		0x0f, 0xff,		/* DAC7 digital full */
-		0x10, 0xff,		/* DAC8 digital full */
-		0x11, 0x1ff,		/* master digital full */
-		0x12, 0x000,		/* phase normal */
-		0x13, 0x090,		/* unmute DAC L/R */
-		0x14, 0x000,		/* all unmute */
-		0x15, 0x000,		/* no deemphasis, no ZFLG */
-		0x19, 0x000,		/* -12dB ADC/L */
-		0x1a, 0x000,		/* -12dB ADC/R */
+		0x16, 0x122,	/* I2S, normal polarity, 24bit */
+		0x17, 0x022,	/* 256fs, slave mode */
+		0x00, 0,	/* DAC1 analog mute */
+		0x01, 0,	/* DAC2 analog mute */
+		0x02, 0,	/* DAC3 analog mute */
+		0x03, 0,	/* DAC4 analog mute */
+		0x04, 0,	/* DAC5 analog mute */
+		0x05, 0,	/* DAC6 analog mute */
+		0x06, 0,	/* DAC7 analog mute */
+		0x07, 0,	/* DAC8 analog mute */
+		0x08, 0x100,	/* master analog mute */
+		0x09, 0xff,	/* DAC1 digital full */
+		0x0a, 0xff,	/* DAC2 digital full */
+		0x0b, 0xff,	/* DAC3 digital full */
+		0x0c, 0xff,	/* DAC4 digital full */
+		0x0d, 0xff,	/* DAC5 digital full */
+		0x0e, 0xff,	/* DAC6 digital full */
+		0x0f, 0xff,	/* DAC7 digital full */
+		0x10, 0xff,	/* DAC8 digital full */
+		0x11, 0x1ff,	/* master digital full */
+		0x12, 0x000,	/* phase normal */
+		0x13, 0x090,	/* unmute DAC L/R */
+		0x14, 0x000,	/* all unmute */
+		0x15, 0x000,	/* no deemphasis, no ZFLG */
+		0x19, 0x000,	/* -12dB ADC/L */
+		0x1a, 0x000,	/* -12dB ADC/R */
 		(unsigned short)-1
 	};
 
@@ -404,17 +432,19 @@
 		return -ENOMEM;
 	ice->spec = spec;
 
-	// Initialize analog chips
-	ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+	/* Initialize analog chips */
+	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
+	ak = ice->akm;
 	if (!ak)
 		return -ENOMEM;
 	ice->akm_codecs = 1;
 
-	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
+	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for time being */
 
 	/* reset the wm codec as the SPI mode */
 	snd_ice1712_save_gpio_status(ice);
-	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
+	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|
+					PHASE28_HP_SEL));
 
 	tmp = snd_ice1712_gpio_read(ice);
 	tmp &= ~PHASE28_WM_RESET;
@@ -446,7 +476,8 @@
 /*
  * DAC volume attenuation mixer control
  */
-static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int wm_vol_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
 {
 	int voices = kcontrol->private_value >> 8;
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -456,7 +487,8 @@
 	return 0;
 }
 
-static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_vol_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -470,7 +502,8 @@
 	return 0;
 }
 
-static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_vol_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -501,7 +534,8 @@
 /*
  * WM8770 mute control
  */
-static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) {
+static int wm_mute_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo) {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	uinfo->count = kcontrol->private_value >> 8;
 	uinfo->value.integer.min = 0;
@@ -509,7 +543,8 @@
 	return 0;
 }
 
-static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_mute_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -524,7 +559,8 @@
 	return 0;
 }
 
-static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_mute_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -539,9 +575,10 @@
 		if (ucontrol->value.integer.value[i] != val) {
 			spec->vol[ofs + i] &= ~WM_VOL_MUTE;
 			spec->vol[ofs + i] |=
-				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+				ucontrol->value.integer.value[i] ? 0 :
+				WM_VOL_MUTE;
 			wm_set_vol(ice, ofs + i, spec->vol[ofs + i],
-				   spec->master[i]);
+					spec->master[i]);
 			change = 1;
 		}
 	}
@@ -555,7 +592,8 @@
  */
 #define wm_master_mute_info		snd_ctl_boolean_stereo_info
 
-static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_master_mute_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -567,7 +605,8 @@
 	return 0;
 }
 
-static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_master_mute_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	struct phase28_spec *spec = ice->spec;
@@ -580,11 +619,12 @@
 			int dac;
 			spec->master[i] &= ~WM_VOL_MUTE;
 			spec->master[i] |=
-				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+				ucontrol->value.integer.value[i] ? 0 :
+				WM_VOL_MUTE;
 			for (dac = 0; dac < ice->num_total_dacs; dac += 2)
 				wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
-					   spec->vol[dac + i],
-					   spec->master[i]);
+						spec->vol[dac + i],
+						spec->master[i]);
 			change = 1;
 		}
 	}
@@ -597,7 +637,8 @@
 #define PCM_0dB 0xff
 #define PCM_RES 128	/* -64dB */
 #define PCM_MIN (PCM_0dB - PCM_RES)
-static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
@@ -606,7 +647,8 @@
 	return 0;
 }
 
-static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned short val;
@@ -619,7 +661,8 @@
 	return 0;
 }
 
-static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned short ovol, nvol;
@@ -633,7 +676,8 @@
 	ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
 	if (ovol != nvol) {
 		wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
-		wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
+		/* update */
+		wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100);
 		change = 1;
 	}
 	snd_ice1712_restore_gpio_status(ice);
@@ -645,18 +689,22 @@
  */
 #define phase28_deemp_info	snd_ctl_boolean_mono_info
 
-static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_deemp_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) ==
+						0xf;
 	return 0;
 }
 
-static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_deemp_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int temp, temp2;
-	temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
+	temp = wm_get(ice, WM_DAC_CTRL2);
+	temp2 = temp;
 	if (ucontrol->value.integer.value[0])
 		temp |= 0xf;
 	else
@@ -671,7 +719,8 @@
 /*
  * ADC Oversampling
  */
-static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo)
+static int phase28_oversampling_info(struct snd_kcontrol *k,
+					struct snd_ctl_elem_info *uinfo)
 {
 	static char *texts[2] = { "128x", "64x"	};
 
@@ -680,25 +729,31 @@
 	uinfo->value.enumerated.items = 2;
 
 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items -
+						1;
+	strcpy(uinfo->value.enumerated.name,
+		texts[uinfo->value.enumerated.item]);
 
-        return 0;
-}
-
-static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
 	return 0;
 }
 
-static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int phase28_oversampling_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) ==
+						0x8;
+	return 0;
+}
+
+static int phase28_oversampling_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
 {
 	int temp, temp2;
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 
-	temp2 = temp = wm_get(ice, WM_MASTER);
+	temp = wm_get(ice, WM_MASTER);
+	temp2 = temp;
 
 	if (ucontrol->value.enumerated.item[0])
 		temp |= 0x8;
@@ -871,13 +926,16 @@
 
 	counts = ARRAY_SIZE(phase28_dac_controls);
 	for (i = 0; i < counts; i++) {
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
+		err = snd_ctl_add(ice->card,
+					snd_ctl_new1(&phase28_dac_controls[i],
+							ice));
 		if (err < 0)
 			return err;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
+		err = snd_ctl_add(ice->card,
+					snd_ctl_new1(&wm_controls[i], ice));
 		if (err < 0)
 			return err;
 	}
@@ -904,5 +962,14 @@
 		.eeprom_size = sizeof(phase28_eeprom),
 		.eeprom_data = phase28_eeprom,
 	},
+	{
+		.subvendor = VT1724_SUBDEVICE_TS22,
+		.name = "Terrasoniq TS22 PCI",
+		.model = "TS22",
+		.chip_init = phase22_init,
+		.build_controls = phase22_add_controls,
+		.eeprom_size = sizeof(phase22_eeprom),
+		.eeprom_data = phase22_eeprom,
+	},
 	{ } /* terminator */
 };
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
index 13e841b..7fc22d9 100644
--- a/sound/pci/ice1712/phase.h
+++ b/sound/pci/ice1712/phase.h
@@ -22,13 +22,15 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
- */      
+ */
 
-#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
-                          "{Terratec,Phase 28},"
+#define PHASE_DEVICE_DESC	"{Terratec,Phase 22},"\
+				"{Terratec,Phase 28},"\
+				"{Terrasoniq,TS22},"
 
 #define VT1724_SUBDEVICE_PHASE22	0x3b155011
 #define VT1724_SUBDEVICE_PHASE28	0x3b154911
+#define VT1724_SUBDEVICE_TS22		0x3b157b11
 
 /* entry point */
 extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 203cdc1bf..6bc3f91 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -43,7 +43,8 @@
 /* WM8776 registers */
 #define WM_HP_ATTEN_L		0x00	/* headphone left attenuation */
 #define WM_HP_ATTEN_R		0x01	/* headphone left attenuation */
-#define WM_HP_MASTER		0x02	/* headphone master (both channels), override LLR */
+#define WM_HP_MASTER		0x02	/* headphone master (both channels) */
+					/* override LLR */
 #define WM_DAC_ATTEN_L		0x03	/* digital left attenuation */
 #define WM_DAC_ATTEN_R		0x04
 #define WM_DAC_MASTER		0x05
@@ -740,7 +741,7 @@
 		WM_DAC_ATTEN_L,	0x0100,	/* DAC 0dB */
 		WM_DAC_ATTEN_R,	0x0000,	/* DAC 0dB */
 		WM_DAC_ATTEN_R,	0x0100,	/* DAC 0dB */
-		// WM_DAC_MASTER,	0x0100,	/* DAC master muted */
+		/* WM_DAC_MASTER,	0x0100, */	/* DAC master muted */
 		WM_PHASE_SWAP,	0x0000,	/* phase normal */
 		WM_DAC_CTRL2,	0x0000,	/* no deemphasis, no ZFLG */
 		WM_ADC_ATTEN_L,	0x0000,	/* ADC muted */
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index 4d26314..b508bb3 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -1,7 +1,7 @@
 /*
  *   ALSA driver for ICEnsemble ICE1712 (Envy24)
  *
- *   Lowlevel functions for M-Audio Revolution 7.1
+ *   Lowlevel functions for M-Audio Audiophile 192, Revolution 7.1 and 5.1
  *
  *	Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
  *
@@ -48,7 +48,7 @@
 }
 
 /*
- * change the rate of envy24HT, AK4355 and AK4381
+ * change the rate of Envy24HT, AK4355 and AK4381
  */
 static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
 {
@@ -83,8 +83,8 @@
 	tmp = snd_akm4xxx_get(ak, 0, reg);
 	tmp &= ~(0x03 << shift);
 	tmp |= dfs << shift;
-	// snd_akm4xxx_write(ak, 0, reg, tmp);
-	snd_akm4xxx_set(ak, 0, reg, tmp); /* the value is written in reset(0) */
+	/* snd_akm4xxx_write(ak, 0, reg, tmp); */
+	snd_akm4xxx_set(ak, 0, reg, tmp); /* value is written in reset(0) */
 	snd_akm4xxx_reset(ak, 0);
 }
 
@@ -216,6 +216,7 @@
 	AK_DAC("PCM Center Playback Volume", 1),
 	AK_DAC("PCM LFE Playback Volume", 1),
 	AK_DAC("PCM Rear Playback Volume", 2),
+	AK_DAC("PCM Headphone Volume", 2),
 };
 
 static const char *revo51_adc_input_names[] = {
@@ -279,7 +280,7 @@
 
 static struct snd_akm4xxx akm_revo51 __devinitdata = {
 	.type = SND_AK4358,
-	.num_dacs = 6,
+	.num_dacs = 8,
 	.ops = {
 		.set_rate_val = revo_set_rate_val
 	},
@@ -508,7 +509,7 @@
 		ice->gpio.i2s_mclk_changed = revo_i2s_mclk_changed;
 		break;
 	case VT1724_SUBDEVICE_REVOLUTION51:
-		ice->num_total_dacs = 6;
+		ice->num_total_dacs = 8;
 		ice->num_total_adcs = 2;
 		break;
 	case VT1724_SUBDEVICE_AUDIOPHILE192:
@@ -524,16 +525,20 @@
 	ak = ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL);
 	if (! ak)
 		return -ENOMEM;
-	ice->akm_codecs = 2;
 	switch (ice->eeprom.subvendor) {
 	case VT1724_SUBDEVICE_REVOLUTION71:
 		ice->akm_codecs = 2;
-		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front, &akm_revo_front_priv, ice)) < 0)
+		err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front,
+						&akm_revo_front_priv, ice);
+		if (err < 0)
 			return err;
-		if ((err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, &akm_revo_surround_priv, ice)) < 0)
+		err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo_surround,
+						&akm_revo_surround_priv, ice);
+		if (err < 0)
 			return err;
 		/* unmute all codecs */
-		snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE);
+		snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
+						VT1724_REVO_MUTE);
 		break;
 	case VT1724_SUBDEVICE_REVOLUTION51:
 		ice->akm_codecs = 2;
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index a08d17c..5af9e84 100644
--- a/sound/pci/ice1712/wtm.c
+++ b/sound/pci/ice1712/wtm.c
@@ -1,12 +1,12 @@
 /*
  *	ALSA driver for ICEnsemble VT1724 (Envy24HT)
- *	
+ *
  *	Lowlevel functions for Ego Sys Waveterminal 192M
  *
  *		Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
  *		Some functions are taken from the Prodigy192 driver
  *		source
- *		
+ *
  *	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
@@ -20,12 +20,12 @@
  *	You should have received a copy of the GNU General Public License
  *	along with this program; if not, write to the Free Software
  *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *	
- */	
+ *
+ */
 
 
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
@@ -39,9 +39,9 @@
 
 
 /*
- *	2*ADC 6*DAC no1 ringbuffer r/w on i2c bus 
+ *	2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
  */
-static inline void stac9460_put(struct snd_ice1712 *ice, int reg, 
+static inline void stac9460_put(struct snd_ice1712 *ice, int reg,
 						unsigned char val)
 {
 	snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val);
@@ -73,7 +73,7 @@
 #define stac9460_dac_mute_info		snd_ctl_boolean_mono_info
 
 static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char val;
@@ -88,14 +88,14 @@
 	}
 	if (id < 6)
 		val = stac9460_get(ice, idx);
-	else 
-		val = stac9460_2_get(ice,idx - 6);
+	else
+		val = stac9460_2_get(ice, idx - 6);
 	ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
 	return 0;
 }
 
 static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char new, old;
@@ -105,8 +105,8 @@
 	if (kcontrol->private_value) {
 		idx = STAC946X_MASTER_VOLUME;
 		old = stac9460_get(ice, idx);
-		new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) |
-		       					(old & ~0x80);
+		new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
+							(old & ~0x80);
 		change = (new != old);
 		if (change) {
 			stac9460_put(ice, idx, new);
@@ -117,16 +117,16 @@
 		idx = id + STAC946X_LF_VOLUME;
 		if (id < 6)
 			old = stac9460_get(ice, idx);
-		else 
+		else
 			old = stac9460_2_get(ice, idx - 6);
-		new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) |
+		new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |
 							(old & ~0x80);
 		change = (new != old);
 		if (change) {
 			if (id < 6)
-			       	stac9460_put(ice, idx, new);
+				stac9460_put(ice, idx, new);
 			else
-			       	stac9460_2_put(ice, idx - 6, new);
+				stac9460_2_put(ice, idx - 6, new);
 		}
 	}
 	return change;
@@ -136,7 +136,7 @@
  * 	DAC volume attenuation mixer control
  */
 static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_info *uinfo)
+				struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
@@ -146,7 +146,7 @@
 }
 
 static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx, id;
@@ -161,14 +161,14 @@
 	}
 	if (id < 6)
 		vol = stac9460_get(ice, idx) & 0x7f;
-	else 
+	else
 		vol = stac9460_2_get(ice, idx - 6) & 0x7f;
 	ucontrol->value.integer.value[0] = 0x7f - vol;
 	return 0;
 }
 
 static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx, id;
@@ -182,8 +182,8 @@
 		ovol = 0x7f - (tmp & 0x7f);
 		change = (ovol != nvol);
 		if (change) {
-			 stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
-			 stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+			stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+			stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
 		}
 	} else {
 		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
@@ -191,17 +191,17 @@
 		nvol = ucontrol->value.integer.value[0] & 0x7f;
 		if (id < 6)
 			tmp = stac9460_get(ice, idx);
-		else 
+		else
 			tmp = stac9460_2_get(ice, idx - 6);
 		ovol = 0x7f - (tmp & 0x7f);
 		change = (ovol != nvol);
 		if (change) {
 			if (id < 6)
 				stac9460_put(ice, idx, (0x7f - nvol) |
-					       		(tmp & 0x80));
-			else 
+							(tmp & 0x80));
+			else
 				stac9460_2_put(ice, idx-6, (0x7f - nvol) |
-					       			(tmp & 0x80));
+							(tmp & 0x80));
 		}
 	}
 	return change;
@@ -213,12 +213,12 @@
 #define stac9460_adc_mute_info		snd_ctl_boolean_stereo_info
 
 static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char val;
 	int i, id;
-	
+
 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	if (id == 0) {
 		for (i = 0; i < 2; ++i) {
@@ -235,20 +235,20 @@
 }
 
 static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char new, old;
 	int i, reg, id;
 	int change;
-	
+
 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	if (id == 0) {
 		for (i = 0; i < 2; ++i) {
 			reg = STAC946X_MIC_L_VOLUME + i;
 			old = stac9460_get(ice, reg);
 			new = (~ucontrol->value.integer.value[i]<<7&0x80) |
-			       					(old&~0x80);
+								(old&~0x80);
 			change = (new != old);
 			if (change)
 				stac9460_put(ice, reg, new);
@@ -258,7 +258,7 @@
 			reg = STAC946X_MIC_L_VOLUME + i;
 			old = stac9460_2_get(ice, reg);
 			new = (~ucontrol->value.integer.value[i]<<7&0x80) |
-			       					(old&~0x80);
+								(old&~0x80);
 			change = (new != old);
 			if (change)
 				stac9460_2_put(ice, reg, new);
@@ -271,7 +271,7 @@
  *ADC gain mixer control
  */
 static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_info *uinfo)
+				struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 2;
@@ -281,12 +281,12 @@
 }
 
 static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,
-	       			struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int i, reg, id;
 	unsigned char vol;
-	
+
 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	if (id == 0) {
 		for (i = 0; i < 2; ++i) {
@@ -305,13 +305,13 @@
 }
 
 static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
-	       		struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int i, reg, id;
 	unsigned char ovol, nvol;
 	int change;
-	
+
 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	if (id == 0) {
 		for (i = 0; i < 2; ++i) {
@@ -321,7 +321,7 @@
 			change = ((ovol & 0x0f) != nvol);
 			if (change)
 				stac9460_put(ice, reg, (0x0f - nvol) |
-					       		(ovol & ~0x0f));
+							(ovol & ~0x0f));
 		}
 	} else {
 		for (i = 0; i < 2; ++i) {
@@ -331,7 +331,7 @@
 			change = ((ovol & 0x0f) != nvol);
 			if (change)
 				stac9460_2_put(ice, reg, (0x0f - nvol) |
-					       		(ovol & ~0x0f));
+							(ovol & ~0x0f));
 		}
 	}
 	return change;
@@ -344,23 +344,23 @@
 #define stac9460_mic_sw_info		snd_ctl_boolean_mono_info
 
 static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
-	       		struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char val;
 	int id;
-		
+
 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	if (id == 0)
-	       	val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
+		val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
 	else
-	       	val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
+		val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
 	ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
 	return 0;
 }
 
 static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
-	       		struct snd_ctl_elem_value *ucontrol)
+				struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned char new, old;
@@ -368,16 +368,16 @@
 
 	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	if (id == 0)
-	       	old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
+		old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
 	else
-	       	old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
-	new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
+		old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
+	new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80);
 	change = (new != old);
 	if (change) {
 		if (id == 0)
-		       	stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
+			stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);
 		else
-		       	stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
+			stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);
 	}
 	return change;
 }
@@ -443,7 +443,7 @@
 		.get = stac9460_adc_vol_get,
 		.put = stac9460_adc_vol_put,
 
-	}	
+	}
 };
 
 
@@ -470,7 +470,7 @@
 		(unsigned short)-1
 	};
 	unsigned short *p;
-		
+
 	/*WTM 192M*/
 	ice->num_total_dacs = 8;
 	ice->num_total_adcs = 4;
diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h
index 03a394e..423c1a2 100644
--- a/sound/pci/ice1712/wtm.h
+++ b/sound/pci/ice1712/wtm.h
@@ -10,8 +10,8 @@
  */
 
 #define	AK4114_ADDR		0x20	/*S/PDIF receiver*/
-#define STAC9460_I2C_ADDR	0x54	/* ADC*2 | DAC*6 */	
-#define STAC9460_2_I2C_ADDR	0x56	/* ADC|DAC *2 */	
+#define STAC9460_I2C_ADDR	0x54	/* ADC*2 | DAC*6 */
+#define STAC9460_2_I2C_ADDR	0x56	/* ADC|DAC *2 */
 
 
 extern struct snd_ice1712_card_info snd_vt1724_wtm_cards[];
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 048d99e..c88d1ea 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -59,6 +59,12 @@
 		"{SiS,SI7012},"
 		"{NVidia,nForce Audio},"
 		"{NVidia,nForce2 Audio},"
+		"{NVidia,nForce3 Audio},"
+		"{NVidia,MCP04},"
+		"{NVidia,MCP501},"
+		"{NVidia,CK804},"
+		"{NVidia,CK8},"
+		"{NVidia,CK8S},"
 		"{AMD,AMD768},"
 		"{AMD,AMD8111},"
 	        "{ALI,M5455}}");
@@ -77,7 +83,7 @@
 module_param(id, charp, 0444);
 MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
 module_param(ac97_clock, int, 0444);
-MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = whitelist + auto-detect, 1 = force autodetect).");
 module_param(ac97_quirk, charp, 0444);
 MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
 module_param(buggy_semaphore, bool, 0444);
@@ -1957,6 +1963,12 @@
 	},
 	{
 		.subvendor = 0x10cf,
+		.subdevice = 0x127d,
+		.name = "Fujitsu Lifebook P7010",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
+		.subvendor = 0x10cf,
 		.subdevice = 0x127e,
 		.name = "Fujitsu Lifebook C1211D",
 		.type = AC97_TUNE_HP_ONLY
@@ -2132,8 +2144,8 @@
 				snd_intel8x0_codec_read_test(chip, codecs);
 				chip->ac97_sdin[codecs] =
 					igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
-				snd_assert(chip->ac97_sdin[codecs] < 3,
-					   chip->ac97_sdin[codecs] = 0);
+				if (snd_BUG_ON(chip->ac97_sdin[codecs] >= 3))
+					chip->ac97_sdin[codecs] = 0;
 			} else
 				chip->ac97_sdin[codecs] = i;
 			codecs++;
@@ -2686,6 +2698,28 @@
 	snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
 }
 
+static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = {
+	SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+	SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
+	SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
+	SND_PCI_QUIRK(0x1043, 0x80f3, "AD1985", 48000),
+	{ }	/* terminator */
+};
+
+static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip)
+{
+	struct pci_dev *pci = chip->pci;
+	const struct snd_pci_quirk *wl;
+
+	wl = snd_pci_quirk_lookup(pci, intel8x0_clock_list);
+	if (!wl)
+		return 0;
+	printk(KERN_INFO "intel8x0: white list rate for %04x:%04x is %i\n",
+	       pci->subsystem_vendor, pci->subsystem_device, wl->value);
+	chip->ac97_bus->clock = wl->value;
+	return 1;
+}
+
 #ifdef CONFIG_PROC_FS
 static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
 				   struct snd_info_buffer *buffer)
@@ -3081,8 +3115,14 @@
 		 "%s with %s at irq %i", card->shortname,
 		 snd_ac97_get_short_name(chip->ac97[0]), chip->irq);
 
-	if (! ac97_clock)
-		intel8x0_measure_ac97_clock(chip);
+	if (ac97_clock == 0 || ac97_clock == 1) {
+		if (ac97_clock == 0) {
+			if (intel8x0_in_clock_list(chip) == 0)
+				intel8x0_measure_ac97_clock(chip);
+		} else {
+			intel8x0_measure_ac97_clock(chip);
+		}
+	}
 
 	if ((err = snd_card_register(card)) < 0) {
 		snd_card_free(card);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index faf674e..93449e4 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -306,7 +306,8 @@
 	static unsigned int codec_bit[3] = {
 		ICH_PCR, ICH_SCR, ICH_TCR
 	};
-	snd_assert(codec < 3, return ICH_PCR);
+	if (snd_BUG_ON(codec >= 3))
+		return ICH_PCR;
 	return codec_bit[codec];
 }
 
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 4a44c0f..5f8006b 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1281,7 +1281,8 @@
 
 	K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_silence pos=%d offset=%d size=%d count=%d\n",
 				   pos, offset, size, count);
-	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+	if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
+		return -EINVAL;
 
 	for (i=0; i < count; i++) {
 #if K1212_DEBUG_LEVEL > 0
@@ -1306,7 +1307,8 @@
 
 	K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n",
 				   pos, offset, size);
-	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+	if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
+		return -EINVAL;
 
 	for (i=0; i < count; i++) {
 #if K1212_DEBUG_LEVEL > 0
@@ -1336,7 +1338,8 @@
 	K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n",
 				   pos, offset, size, count);
 
-	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL);
+	if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
+		return -EINVAL;
 
 	for (i=0; i < count; i++) {
 #if K1212_DEBUG_LEVEL > 0
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 0037be7..9ff3f9e 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -1175,7 +1175,8 @@
 	struct m3_dma *s = subs->runtime->private_data;
 	int err = -EINVAL;
 
-	snd_assert(s != NULL, return -ENXIO);
+	if (snd_BUG_ON(!s))
+		return -ENXIO;
 
 	spin_lock(&chip->reg_lock);
 	switch (cmd) {
@@ -1487,7 +1488,8 @@
 	struct snd_pcm_runtime *runtime = subs->runtime;
 	struct m3_dma *s = runtime->private_data;
 
-	snd_assert(s != NULL, return -ENXIO);
+	if (snd_BUG_ON(!s))
+		return -ENXIO;
 
 	if (runtime->format != SNDRV_PCM_FORMAT_U8 &&
 	    runtime->format != SNDRV_PCM_FORMAT_S16_LE)
@@ -1546,7 +1548,9 @@
 	struct snd_m3 *chip = snd_pcm_substream_chip(subs);
 	unsigned int ptr;
 	struct m3_dma *s = subs->runtime->private_data;
-	snd_assert(s != NULL, return 0);
+
+	if (snd_BUG_ON(!s))
+		return 0;
 
 	spin_lock(&chip->reg_lock);
 	ptr = snd_m3_get_pointer(chip, s, subs);
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 3dd0c79..2d0dce6 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -708,7 +708,7 @@
 		pcm_number = MIXART_PCM_ANALOG;
 		runtime->hw = snd_mixart_analog_caps;
 	} else {
-		snd_assert ( pcm == chip->pcm_dig ); 
+		snd_BUG_ON(pcm != chip->pcm_dig);
 		pcm_number = MIXART_PCM_DIGITAL;
 		runtime->hw = snd_mixart_digital_caps;
 	}
@@ -783,7 +783,7 @@
 		pcm_number = MIXART_PCM_ANALOG;
 		runtime->hw = snd_mixart_analog_caps;
 	} else {
-		snd_assert ( pcm == chip->pcm_dig ); 
+		snd_BUG_ON(pcm != chip->pcm_dig);
 		pcm_number = MIXART_PCM_DIGITAL;
 		runtime->hw = snd_mixart_digital_caps;
 	}
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index 785085e..b9a06c2 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -56,8 +56,10 @@
 	if (tailptr == headptr)
 		return 0; /* no message posted */
 
-	snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */
-	snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */
+	if (tailptr < MSG_OUTBOUND_POST_STACK)
+		return 0; /* error */
+	if (tailptr >= MSG_OUTBOUND_POST_STACK + MSG_BOUND_STACK_SIZE)
+		return 0; /* error */
 
 	*msg_frame = readl_be(MIXART_MEM(mgr, tailptr));
 
@@ -149,7 +151,8 @@
 	u32 msg_frame_address;
 	int err, i;
 
-	snd_assert(msg->size % 4 == 0, return -EINVAL);
+	if (snd_BUG_ON(msg->size % 4))
+		return -EINVAL;
 
 	err = 0;
 
@@ -289,9 +292,12 @@
 	wait_queue_t wait;
 	long timeout;
 
-	snd_assert(notif_event != 0, return -EINVAL);
-	snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL);
-	snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL);
+	if (snd_BUG_ON(!notif_event))
+		return -EINVAL;
+	if (snd_BUG_ON((notif_event & MSG_TYPE_MASK) != MSG_TYPE_NOTIFY))
+		return -EINVAL;
+	if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK))
+		return -EINVAL;
 
 	mutex_lock(&mgr->msg_mutex);
 
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index f986031..3782b52 100644
--- a/sound/pci/mixart/mixart_hwdep.c
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -288,7 +288,9 @@
 		return -EINVAL;
 	}
 
-	snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2),  return -EINVAL); /* min 2 phys io per card (analog in + analog out) */
+	/* min 2 phys io per card (analog in + analog out) */
+	if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)
+		return -EINVAL;
 
 	for(k=0; k<mgr->num_cards; k++) {
 		mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
@@ -363,8 +365,10 @@
 		}
 
 		/* check xilinx validity */
-		snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
-		snd_assert(dsp->size % 4 == 0, return -EINVAL);
+		if (((u32*)(dsp->data))[0] == 0xffffffff)
+			return -EINVAL;
+		if (dsp->size % 4)
+			return -EINVAL;
 
 		/* set xilinx status to copying */
 		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
@@ -462,8 +466,10 @@
 		}
  
 		/* check daughterboard xilinx validity */
-		snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
-		snd_assert(dsp->size % 4 == 0, return -EINVAL);
+		if (((u32*)(dsp->data))[0] == 0xffffffff)
+			return -EINVAL;
+		if (dsp->size % 4)
+			return -EINVAL;
 
 		/* inform mixart about the size of the file */
 		writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
@@ -480,7 +486,8 @@
 
 		/* get the address where to write the file */
 		val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
-		snd_assert(val != 0, return -EINVAL);
+		if (!val)
+			return -EINVAL;
 
 		/* copy daughterboard xilinx code */
 		memcpy_toio(  MIXART_MEM( mgr, val),  dsp->data,  dsp->size);
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 6fdda1f..3ba6174 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -837,7 +837,7 @@
 		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */
 		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */
 	} else {
-		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+		snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
 		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
 		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */
 	}
@@ -863,7 +863,7 @@
 		else		/* analog capture */
 			stored_volume = chip->digital_capture_volume[0];
 	} else {
-		snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+		snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
 		if (is_aes)	/* AES playback */
 			stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];
 		else		/* analog playback */
@@ -909,7 +909,7 @@
 {
 	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
-	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+	snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
 	mutex_lock(&chip->mgr->mixer_mutex);
 	if(kcontrol->private_value & MIXART_VOL_AES_MASK)	/* AES playback */
 		idx += MIXART_PLAYBACK_STREAMS;
@@ -926,7 +926,7 @@
 	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
 	int i, j;
-	snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); 
+	snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);
 	mutex_lock(&chip->mgr->mixer_mutex);
 	j = idx;
 	if (is_aes)
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 06d13e7..50c9f8a 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -562,7 +562,8 @@
 	struct nm256_stream *s = substream->runtime->private_data;
 	int err = 0;
 
-	snd_assert(s != NULL, return -ENXIO);
+	if (snd_BUG_ON(!s))
+		return -ENXIO;
 
 	spin_lock(&chip->reg_lock);
 	switch (cmd) {
@@ -599,7 +600,8 @@
 	struct nm256_stream *s = substream->runtime->private_data;
 	int err = 0;
 
-	snd_assert(s != NULL, return -ENXIO);
+	if (snd_BUG_ON(!s))
+		return -ENXIO;
 
 	spin_lock(&chip->reg_lock);
 	switch (cmd) {
@@ -635,7 +637,8 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct nm256_stream *s = runtime->private_data;
 
-	snd_assert(s, return -ENXIO);
+	if (snd_BUG_ON(!s))
+		return -ENXIO;
 	s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);
 	s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);
 	s->periods = substream->runtime->periods;
@@ -660,7 +663,8 @@
 	struct nm256_stream *s = substream->runtime->private_data;
 	unsigned long curp;
 
-	snd_assert(s, return 0);
+	if (snd_BUG_ON(!s))
+		return 0;
 	curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;
 	curp %= s->dma_size;
 	return bytes_to_frames(substream->runtime, curp);
@@ -673,7 +677,8 @@
 	struct nm256_stream *s = substream->runtime->private_data;
 	unsigned long curp;
 
-	snd_assert(s != NULL, return 0);
+	if (snd_BUG_ON(!s))
+		return 0;
 	curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;
 	curp %= s->dma_size;	
 	return bytes_to_frames(substream->runtime, curp);
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index dad393a..1ab833f 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -94,6 +94,11 @@
 {
 }
 
+static void hifier_resume(struct oxygen *chip)
+{
+	hifier_registers_init(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
 			       struct snd_pcm_hw_params *params)
 {
@@ -150,16 +155,16 @@
 	.init = hifier_init,
 	.control_filter = hifier_control_filter,
 	.cleanup = hifier_cleanup,
-	.resume = hifier_registers_init,
+	.resume = hifier_resume,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_cs5340_params,
 	.update_dac_volume = update_ak4396_volume,
 	.update_dac_mute = update_ak4396_mute,
 	.dac_tlv = ak4396_db_scale,
 	.model_data_size = sizeof(struct hifier_data),
-	.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-		       PLAYBACK_1_TO_SPDIF |
-		       CAPTURE_0_FROM_I2S_1,
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_1,
 	.dac_channels = 2,
 	.dac_volume_min = 0,
 	.dac_volume_max = 255,
@@ -180,7 +185,7 @@
 		++dev;
 		return -ENOENT;
 	}
-	err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier);
+	err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier, 0);
 	if (err >= 0)
 		++dev;
 	return err;
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index c5829d3..b60f621 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -58,17 +58,22 @@
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "enable card");
 
+enum {
+	MODEL_CMEDIA_REF,	/* C-Media's reference design */
+	MODEL_MERIDIAN,		/* AuzenTech X-Meridian */
+};
+
 static struct pci_device_id oxygen_ids[] __devinitdata = {
-	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216) },
-	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218) },
-	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219) },
-	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001) },
-	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010) },
-	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
-	{ OXYGEN_PCI_SUBID(0x147a, 0xa017) },
-	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910) },
-	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 },
-	{ OXYGEN_PCI_SUBID(0x7284, 0x9761) },
+	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
+	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
+	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, oxygen_ids);
@@ -199,6 +204,11 @@
 	wm8785_registers_init(chip);
 }
 
+static void meridian_resume(struct oxygen *chip)
+{
+	ak4396_registers_init(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 {
@@ -281,11 +291,28 @@
 
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
+static int generic_probe(struct oxygen *chip, unsigned long driver_data)
+{
+	if (driver_data == MODEL_MERIDIAN) {
+		chip->model.init = meridian_init;
+		chip->model.resume = meridian_resume;
+		chip->model.set_adc_params = set_ak5385_params;
+		chip->model.device_config = PLAYBACK_0_TO_I2S |
+					    PLAYBACK_1_TO_SPDIF |
+					    CAPTURE_0_FROM_I2S_2 |
+					    CAPTURE_1_FROM_SPDIF;
+		chip->model.misc_flags = OXYGEN_MISC_MIDI;
+		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
+	}
+	return 0;
+}
+
 static const struct oxygen_model model_generic = {
 	.shortname = "C-Media CMI8788",
 	.longname = "C-Media Oxygen HD Audio",
 	.chip = "CMI8788",
 	.owner = THIS_MODULE,
+	.probe = generic_probe,
 	.init = generic_init,
 	.cleanup = generic_cleanup,
 	.resume = generic_resume,
@@ -295,12 +322,12 @@
 	.update_dac_mute = update_ak4396_mute,
 	.dac_tlv = ak4396_db_scale,
 	.model_data_size = sizeof(struct generic_data),
-	.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-		       PLAYBACK_1_TO_SPDIF |
-		       PLAYBACK_2_TO_AC97_1 |
-		       CAPTURE_0_FROM_I2S_1 |
-		       CAPTURE_1_FROM_SPDIF |
-		       CAPTURE_2_FROM_AC97_1,
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 PLAYBACK_2_TO_AC97_1 |
+			 CAPTURE_0_FROM_I2S_1 |
+			 CAPTURE_1_FROM_SPDIF |
+			 CAPTURE_2_FROM_AC97_1,
 	.dac_channels = 8,
 	.dac_volume_min = 0,
 	.dac_volume_max = 255,
@@ -309,41 +336,11 @@
 	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
-static const struct oxygen_model model_meridian = {
-	.shortname = "C-Media CMI8788",
-	.longname = "C-Media Oxygen HD Audio",
-	.chip = "CMI8788",
-	.owner = THIS_MODULE,
-	.init = meridian_init,
-	.cleanup = generic_cleanup,
-	.resume = ak4396_registers_init,
-	.set_dac_params = set_ak4396_params,
-	.set_adc_params = set_ak5385_params,
-	.update_dac_volume = update_ak4396_volume,
-	.update_dac_mute = update_ak4396_mute,
-	.dac_tlv = ak4396_db_scale,
-	.model_data_size = sizeof(struct generic_data),
-	.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-		       PLAYBACK_1_TO_SPDIF |
-		       PLAYBACK_2_TO_AC97_1 |
-		       CAPTURE_0_FROM_I2S_2 |
-		       CAPTURE_1_FROM_SPDIF |
-		       CAPTURE_2_FROM_AC97_1,
-	.dac_channels = 8,
-	.dac_volume_min = 0,
-	.dac_volume_max = 255,
-	.misc_flags = OXYGEN_MISC_MIDI,
-	.function_flags = OXYGEN_FUNCTION_SPI |
-			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
 
 static int __devinit generic_oxygen_probe(struct pci_dev *pci,
 					  const struct pci_device_id *pci_id)
 {
 	static int dev;
-	int is_meridian;
 	int err;
 
 	if (dev >= SNDRV_CARDS)
@@ -352,9 +349,8 @@
 		++dev;
 		return -ENOENT;
 	}
-	is_meridian = pci_id->driver_data;
 	err = oxygen_pci_probe(pci, index[dev], id[dev],
-			       is_meridian ? &model_meridian : &model_generic);
+			       &model_generic, pci_id->driver_data);
 	if (err >= 0)
 		++dev;
 	return err;
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 74a6448..19107c6 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -19,14 +19,19 @@
 #define OXYGEN_IO_SIZE	0x100
 
 /* model-specific configuration of outputs/inputs */
-#define PLAYBACK_0_TO_I2S	0x001
-#define PLAYBACK_1_TO_SPDIF	0x004
-#define PLAYBACK_2_TO_AC97_1	0x008
-#define CAPTURE_0_FROM_I2S_1	0x010
-#define CAPTURE_0_FROM_I2S_2	0x020
-#define CAPTURE_1_FROM_SPDIF	0x080
-#define CAPTURE_2_FROM_I2S_2	0x100
-#define CAPTURE_2_FROM_AC97_1	0x200
+#define PLAYBACK_0_TO_I2S	0x0001
+     /* PLAYBACK_0_TO_AC97_0		not implemented */
+#define PLAYBACK_1_TO_SPDIF	0x0004
+#define PLAYBACK_2_TO_AC97_1	0x0008
+#define CAPTURE_0_FROM_I2S_1	0x0010
+#define CAPTURE_0_FROM_I2S_2	0x0020
+     /* CAPTURE_0_FROM_AC97_0		not implemented */
+#define CAPTURE_1_FROM_SPDIF	0x0080
+#define CAPTURE_2_FROM_I2S_2	0x0100
+#define CAPTURE_2_FROM_AC97_1	0x0200
+     /* CAPTURE_3_FROM_I2S_3		not implemented */
+#define MIDI_OUTPUT		0x0800
+#define MIDI_INPUT		0x1000
 
 enum {
 	CONTROL_SPDIF_PCM,
@@ -51,7 +56,43 @@
 struct snd_pcm_hw_params;
 struct snd_kcontrol_new;
 struct snd_rawmidi;
-struct oxygen_model;
+struct oxygen;
+
+struct oxygen_model {
+	const char *shortname;
+	const char *longname;
+	const char *chip;
+	struct module *owner;
+	int (*probe)(struct oxygen *chip, unsigned long driver_data);
+	void (*init)(struct oxygen *chip);
+	int (*control_filter)(struct snd_kcontrol_new *template);
+	int (*mixer_init)(struct oxygen *chip);
+	void (*cleanup)(struct oxygen *chip);
+	void (*suspend)(struct oxygen *chip);
+	void (*resume)(struct oxygen *chip);
+	void (*pcm_hardware_filter)(unsigned int channel,
+				    struct snd_pcm_hardware *hardware);
+	void (*set_dac_params)(struct oxygen *chip,
+			       struct snd_pcm_hw_params *params);
+	void (*set_adc_params)(struct oxygen *chip,
+			       struct snd_pcm_hw_params *params);
+	void (*update_dac_volume)(struct oxygen *chip);
+	void (*update_dac_mute)(struct oxygen *chip);
+	void (*gpio_changed)(struct oxygen *chip);
+	void (*uart_input)(struct oxygen *chip);
+	void (*ac97_switch)(struct oxygen *chip,
+			    unsigned int reg, unsigned int mute);
+	const unsigned int *dac_tlv;
+	size_t model_data_size;
+	unsigned int device_config;
+	u8 dac_channels;
+	u8 dac_volume_min;
+	u8 dac_volume_max;
+	u8 misc_flags;
+	u8 function_flags;
+	u16 dac_i2s_format;
+	u16 adc_i2s_format;
+};
 
 struct oxygen {
 	unsigned long addr;
@@ -61,7 +102,6 @@
 	struct pci_dev *pci;
 	struct snd_rawmidi *midi;
 	int irq;
-	const struct oxygen_model *model;
 	void *model_data;
 	unsigned int interrupt_mask;
 	u8 dac_volume[8];
@@ -86,46 +126,16 @@
 		__le32 _32[OXYGEN_IO_SIZE / 4];
 	} saved_registers;
 	u16 saved_ac97_registers[2][0x40];
-};
-
-struct oxygen_model {
-	const char *shortname;
-	const char *longname;
-	const char *chip;
-	struct module *owner;
-	void (*init)(struct oxygen *chip);
-	int (*control_filter)(struct snd_kcontrol_new *template);
-	int (*mixer_init)(struct oxygen *chip);
-	void (*cleanup)(struct oxygen *chip);
-	void (*suspend)(struct oxygen *chip);
-	void (*resume)(struct oxygen *chip);
-	void (*pcm_hardware_filter)(unsigned int channel,
-				    struct snd_pcm_hardware *hardware);
-	void (*set_dac_params)(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params);
-	void (*set_adc_params)(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params);
-	void (*update_dac_volume)(struct oxygen *chip);
-	void (*update_dac_mute)(struct oxygen *chip);
-	void (*gpio_changed)(struct oxygen *chip);
-	void (*ac97_switch)(struct oxygen *chip,
-			    unsigned int reg, unsigned int mute);
-	const unsigned int *dac_tlv;
-	size_t model_data_size;
-	unsigned int pcm_dev_cfg;
-	u8 dac_channels;
-	u8 dac_volume_min;
-	u8 dac_volume_max;
-	u8 misc_flags;
-	u8 function_flags;
-	u16 dac_i2s_format;
-	u16 adc_i2s_format;
+	unsigned int uart_input_count;
+	u8 uart_input[32];
+	struct oxygen_model model;
 };
 
 /* oxygen_lib.c */
 
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
-		     const struct oxygen_model *model);
+		     const struct oxygen_model *model,
+		     unsigned long driver_data);
 void oxygen_pci_remove(struct pci_dev *pci);
 #ifdef CONFIG_PM
 int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
@@ -167,6 +177,9 @@
 void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
 void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
 
+void oxygen_reset_uart(struct oxygen *chip);
+void oxygen_write_uart(struct oxygen *chip, u8 data);
+
 static inline void oxygen_set_bits8(struct oxygen *chip,
 				    unsigned int reg, u8 value)
 {
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 83f135f..3126c4b 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -20,6 +20,7 @@
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <sound/core.h>
+#include <sound/mpu401.h>
 #include <asm/io.h>
 #include "oxygen.h"
 
@@ -232,3 +233,24 @@
 		      device | OXYGEN_2WIRE_DIR_WRITE);
 }
 EXPORT_SYMBOL(oxygen_write_i2c);
+
+static void _write_uart(struct oxygen *chip, unsigned int port, u8 data)
+{
+	if (oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_TX_FULL)
+		msleep(1);
+	oxygen_write8(chip, OXYGEN_MPU401 + port, data);
+}
+
+void oxygen_reset_uart(struct oxygen *chip)
+{
+	_write_uart(chip, 1, MPU401_RESET);
+	msleep(1); /* wait for ACK */
+	_write_uart(chip, 1, MPU401_ENTER_UART);
+}
+EXPORT_SYMBOL(oxygen_reset_uart);
+
+void oxygen_write_uart(struct oxygen *chip, u8 data)
+{
+	_write_uart(chip, 0, data);
+}
+EXPORT_SYMBOL(oxygen_write_uart);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 22f3785..84f481d 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -35,6 +35,30 @@
 MODULE_LICENSE("GPL v2");
 
 
+static inline int oxygen_uart_input_ready(struct oxygen *chip)
+{
+	return !(oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_RX_EMPTY);
+}
+
+static void oxygen_read_uart(struct oxygen *chip)
+{
+	if (unlikely(!oxygen_uart_input_ready(chip))) {
+		/* no data, but read it anyway to clear the interrupt */
+		oxygen_read8(chip, OXYGEN_MPU401);
+		return;
+	}
+	do {
+		u8 data = oxygen_read8(chip, OXYGEN_MPU401);
+		if (data == MPU401_ACK)
+			continue;
+		if (chip->uart_input_count >= ARRAY_SIZE(chip->uart_input))
+			chip->uart_input_count = 0;
+		chip->uart_input[chip->uart_input_count++] = data;
+	} while (oxygen_uart_input_ready(chip));
+	if (chip->model.uart_input)
+		chip->model.uart_input(chip);
+}
+
 static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
 {
 	struct oxygen *chip = dev_id;
@@ -87,8 +111,12 @@
 	if (status & OXYGEN_INT_GPIO)
 		schedule_work(&chip->gpio_work);
 
-	if ((status & OXYGEN_INT_MIDI) && chip->midi)
-		snd_mpu401_uart_interrupt(0, chip->midi->private_data);
+	if (status & OXYGEN_INT_MIDI) {
+		if (chip->midi)
+			snd_mpu401_uart_interrupt(0, chip->midi->private_data);
+		else
+			oxygen_read_uart(chip);
+	}
 
 	if (status & OXYGEN_INT_AC97)
 		wake_up(&chip->ac97_waitqueue);
@@ -161,8 +189,8 @@
 {
 	struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
 
-	if (chip->model->gpio_changed)
-		chip->model->gpio_changed(chip);
+	if (chip->model.gpio_changed)
+		chip->model.gpio_changed(chip);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -221,7 +249,7 @@
 
 	chip->dac_routing = 1;
 	for (i = 0; i < 8; ++i)
-		chip->dac_volume[i] = chip->model->dac_volume_min;
+		chip->dac_volume[i] = chip->model.dac_volume_min;
 	chip->dac_mute = 1;
 	chip->spdif_playback_enable = 1;
 	chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
@@ -243,7 +271,7 @@
 
 	oxygen_write8_masked(chip, OXYGEN_FUNCTION,
 			     OXYGEN_FUNCTION_RESET_CODEC |
-			     chip->model->function_flags,
+			     chip->model.function_flags,
 			     OXYGEN_FUNCTION_RESET_CODEC |
 			     OXYGEN_FUNCTION_2WIRE_SPI_MASK |
 			     OXYGEN_FUNCTION_ENABLE_SPI_4_5);
@@ -255,7 +283,7 @@
 		      OXYGEN_DMA_MULTICH_BURST_8);
 	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
 	oxygen_write8_masked(chip, OXYGEN_MISC,
-			     chip->model->misc_flags,
+			     chip->model.misc_flags,
 			     OXYGEN_MISC_WRITE_PCI_SUBID |
 			     OXYGEN_MISC_REC_C_FROM_SPDIF |
 			     OXYGEN_MISC_REC_B_FROM_AC97 |
@@ -270,21 +298,21 @@
 		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
 	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
 	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
-		       OXYGEN_RATE_48000 | chip->model->dac_i2s_format |
+		       OXYGEN_RATE_48000 | chip->model.dac_i2s_format |
 		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
 		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
-	if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+	if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
 		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
-			       OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+			       OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
 			       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
 			       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
 	else
 		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
 			       OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
-	if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 |
-					CAPTURE_2_FROM_I2S_2))
+	if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
+					 CAPTURE_2_FROM_I2S_2))
 		oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
-			       OXYGEN_RATE_48000 | chip->model->adc_i2s_format |
+			       OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
 			       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
 			       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
 	else
@@ -295,7 +323,7 @@
 	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
 			    OXYGEN_SPDIF_OUT_ENABLE |
 			    OXYGEN_SPDIF_LOOPBACK);
-	if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+	if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
 		oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
 				      OXYGEN_SPDIF_SENSE_MASK |
 				      OXYGEN_SPDIF_LOCK_MASK |
@@ -417,14 +445,15 @@
 	if (chip->irq >= 0)
 		free_irq(chip->irq, chip);
 	flush_scheduled_work();
-	chip->model->cleanup(chip);
+	chip->model.cleanup(chip);
 	mutex_destroy(&chip->mutex);
 	pci_release_regions(chip->pci);
 	pci_disable_device(chip->pci);
 }
 
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
-		     const struct oxygen_model *model)
+		     const struct oxygen_model *model,
+		     unsigned long driver_data)
 {
 	struct snd_card *card;
 	struct oxygen *chip;
@@ -439,7 +468,7 @@
 	chip->card = card;
 	chip->pci = pci;
 	chip->irq = -1;
-	chip->model = model;
+	chip->model = *model;
 	chip->model_data = chip + 1;
 	spin_lock_init(&chip->reg_lock);
 	mutex_init(&chip->mutex);
@@ -470,23 +499,28 @@
 	snd_card_set_dev(card, &pci->dev);
 	card->private_free = oxygen_card_free;
 
+	if (chip->model.probe) {
+		err = chip->model.probe(chip, driver_data);
+		if (err < 0)
+			goto err_card;
+	}
 	oxygen_init(chip);
-	model->init(chip);
+	chip->model.init(chip);
 
 	err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
-			  model->chip, chip);
+			  chip->model.chip, chip);
 	if (err < 0) {
 		snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
 		goto err_card;
 	}
 	chip->irq = pci->irq;
 
-	strcpy(card->driver, model->chip);
-	strcpy(card->shortname, model->shortname);
+	strcpy(card->driver, chip->model.chip);
+	strcpy(card->shortname, chip->model.shortname);
 	sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
-		model->longname, chip->revision, chip->addr, chip->irq);
-	strcpy(card->mixername, model->chip);
-	snd_component_add(card, model->chip);
+		chip->model.longname, chip->revision, chip->addr, chip->irq);
+	strcpy(card->mixername, chip->model.chip);
+	snd_component_add(card, chip->model.chip);
 
 	err = oxygen_pcm_init(chip);
 	if (err < 0)
@@ -496,10 +530,15 @@
 	if (err < 0)
 		goto err_card;
 
-	if (model->misc_flags & OXYGEN_MISC_MIDI) {
+	if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) {
+		unsigned int info_flags = MPU401_INFO_INTEGRATED;
+		if (chip->model.device_config & MIDI_OUTPUT)
+			info_flags |= MPU401_INFO_OUTPUT;
+		if (chip->model.device_config & MIDI_INPUT)
+			info_flags |= MPU401_INFO_INPUT;
 		err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
 					  chip->addr + OXYGEN_MPU401,
-					  MPU401_INFO_INTEGRATED, 0, 0,
+					  info_flags, 0, 0,
 					  &chip->midi);
 		if (err < 0)
 			goto err_card;
@@ -508,7 +547,7 @@
 	oxygen_proc_init(chip);
 
 	spin_lock_irq(&chip->reg_lock);
-	if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF)
+	if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
 		chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
 	if (chip->has_ac97_0 | chip->has_ac97_1)
 		chip->interrupt_mask |= OXYGEN_INT_AC97;
@@ -552,8 +591,8 @@
 		if (chip->streams[i])
 			snd_pcm_suspend(chip->streams[i]);
 
-	if (chip->model->suspend)
-		chip->model->suspend(chip);
+	if (chip->model.suspend)
+		chip->model.suspend(chip);
 
 	spin_lock_irq(&chip->reg_lock);
 	saved_interrupt_mask = chip->interrupt_mask;
@@ -624,8 +663,8 @@
 	if (chip->has_ac97_1)
 		oxygen_restore_ac97(chip, 1);
 
-	if (chip->model->resume)
-		chip->model->resume(chip);
+	if (chip->model.resume)
+		chip->model.resume(chip);
 
 	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
 
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 05eb899..304da16 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -31,9 +31,9 @@
 	struct oxygen *chip = ctl->private_data;
 
 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	info->count = chip->model->dac_channels;
-	info->value.integer.min = chip->model->dac_volume_min;
-	info->value.integer.max = chip->model->dac_volume_max;
+	info->count = chip->model.dac_channels;
+	info->value.integer.min = chip->model.dac_volume_min;
+	info->value.integer.max = chip->model.dac_volume_max;
 	return 0;
 }
 
@@ -44,7 +44,7 @@
 	unsigned int i;
 
 	mutex_lock(&chip->mutex);
-	for (i = 0; i < chip->model->dac_channels; ++i)
+	for (i = 0; i < chip->model.dac_channels; ++i)
 		value->value.integer.value[i] = chip->dac_volume[i];
 	mutex_unlock(&chip->mutex);
 	return 0;
@@ -59,13 +59,13 @@
 
 	changed = 0;
 	mutex_lock(&chip->mutex);
-	for (i = 0; i < chip->model->dac_channels; ++i)
+	for (i = 0; i < chip->model.dac_channels; ++i)
 		if (value->value.integer.value[i] != chip->dac_volume[i]) {
 			chip->dac_volume[i] = value->value.integer.value[i];
 			changed = 1;
 		}
 	if (changed)
-		chip->model->update_dac_volume(chip);
+		chip->model.update_dac_volume(chip);
 	mutex_unlock(&chip->mutex);
 	return changed;
 }
@@ -91,7 +91,7 @@
 	changed = !value->value.integer.value[0] != chip->dac_mute;
 	if (changed) {
 		chip->dac_mute = !value->value.integer.value[0];
-		chip->model->update_dac_mute(chip);
+		chip->model.update_dac_mute(chip);
 	}
 	mutex_unlock(&chip->mutex);
 	return changed;
@@ -103,7 +103,7 @@
 		"Front", "Front+Surround", "Front+Surround+Back"
 	};
 	struct oxygen *chip = ctl->private_data;
-	unsigned int count = 2 + (chip->model->dac_channels == 8);
+	unsigned int count = 2 + (chip->model.dac_channels == 8);
 
 	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	info->count = 1;
@@ -172,7 +172,7 @@
 static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 {
 	struct oxygen *chip = ctl->private_data;
-	unsigned int count = 2 + (chip->model->dac_channels == 8);
+	unsigned int count = 2 + (chip->model.dac_channels == 8);
 	int changed;
 
 	mutex_lock(&chip->mutex);
@@ -211,13 +211,13 @@
 	case OXYGEN_RATE_64000:
 		return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT;
 	case OXYGEN_RATE_88200:
-		return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT;
+		return IEC958_AES3_CON_FS_88200 << OXYGEN_SPDIF_CS_RATE_SHIFT;
 	case OXYGEN_RATE_96000:
-		return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT;
+		return IEC958_AES3_CON_FS_96000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
 	case OXYGEN_RATE_176400:
-		return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT;
+		return IEC958_AES3_CON_FS_176400 << OXYGEN_SPDIF_CS_RATE_SHIFT;
 	case OXYGEN_RATE_192000:
-		return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT;
+		return IEC958_AES3_CON_FS_192000 << OXYGEN_SPDIF_CS_RATE_SHIFT;
 	}
 }
 
@@ -521,8 +521,8 @@
 	value = oxygen_read_ac97(chip, 0, priv_idx);
 	if (!(value & 0x8000)) {
 		oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000);
-		if (chip->model->ac97_switch)
-			chip->model->ac97_switch(chip, priv_idx, 0x8000);
+		if (chip->model.ac97_switch)
+			chip->model.ac97_switch(chip, priv_idx, 0x8000);
 		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
 			       &chip->controls[control]->id);
 	}
@@ -549,8 +549,8 @@
 	change = newreg != oldreg;
 	if (change) {
 		oxygen_write_ac97(chip, codec, index, newreg);
-		if (codec == 0 && chip->model->ac97_switch)
-			chip->model->ac97_switch(chip, index, newreg & 0x8000);
+		if (codec == 0 && chip->model.ac97_switch)
+			chip->model.ac97_switch(chip, index, newreg & 0x8000);
 		if (index == AC97_LINE) {
 			oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
 						 newreg & 0x8000 ?
@@ -939,16 +939,16 @@
 
 	for (i = 0; i < count; ++i) {
 		template = controls[i];
-		if (chip->model->control_filter) {
-			err = chip->model->control_filter(&template);
+		if (chip->model.control_filter) {
+			err = chip->model.control_filter(&template);
 			if (err < 0)
 				return err;
 			if (err == 1)
 				continue;
 		}
 		if (!strcmp(template.name, "Master Playback Volume") &&
-		    chip->model->dac_tlv) {
-			template.tlv.p = chip->model->dac_tlv;
+		    chip->model.dac_tlv) {
+			template.tlv.p = chip->model.dac_tlv;
 			template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 		}
 		ctl = snd_ctl_new1(&template, chip);
@@ -974,14 +974,14 @@
 	err = add_controls(chip, controls, ARRAY_SIZE(controls));
 	if (err < 0)
 		return err;
-	if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) {
+	if (chip->model.device_config & CAPTURE_1_FROM_SPDIF) {
 		err = add_controls(chip, spdif_input_controls,
 				   ARRAY_SIZE(spdif_input_controls));
 		if (err < 0)
 			return err;
 	}
 	for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) {
-		if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev))
+		if (!(chip->model.device_config & monitor_controls[i].pcm_dev))
 			continue;
 		err = add_controls(chip, monitor_controls[i].controls,
 				   ARRAY_SIZE(monitor_controls[i].controls));
@@ -1000,5 +1000,5 @@
 		if (err < 0)
 			return err;
 	}
-	return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0;
+	return chip->model.mixer_init ? chip->model.mixer_init(chip) : 0;
 }
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index c4ad65a..c262049 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -129,7 +129,7 @@
 
 	runtime->private_data = (void *)(uintptr_t)channel;
 	if (channel == PCM_B && chip->has_ac97_1 &&
-	    (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1))
+	    (chip->model.device_config & CAPTURE_2_FROM_AC97_1))
 		runtime->hw = oxygen_ac97_hardware;
 	else
 		runtime->hw = *oxygen_hardware[channel];
@@ -140,11 +140,11 @@
 		runtime->hw.rate_min = 44100;
 		break;
 	case PCM_MULTICH:
-		runtime->hw.channels_max = chip->model->dac_channels;
+		runtime->hw.channels_max = chip->model.dac_channels;
 		break;
 	}
-	if (chip->model->pcm_hardware_filter)
-		chip->model->pcm_hardware_filter(channel, &runtime->hw);
+	if (chip->model.pcm_hardware_filter)
+		chip->model.pcm_hardware_filter(channel, &runtime->hw);
 	err = snd_pcm_hw_constraint_step(runtime, 0,
 					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
 	if (err < 0)
@@ -355,7 +355,7 @@
 	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
 			      oxygen_rate(hw_params) |
 			      oxygen_i2s_mclk(hw_params) |
-			      chip->model->adc_i2s_format |
+			      chip->model.adc_i2s_format |
 			      oxygen_i2s_bits(hw_params),
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
@@ -364,7 +364,7 @@
 	spin_unlock_irq(&chip->reg_lock);
 
 	mutex_lock(&chip->mutex);
-	chip->model->set_adc_params(chip, hw_params);
+	chip->model.set_adc_params(chip, hw_params);
 	mutex_unlock(&chip->mutex);
 	return 0;
 }
@@ -381,7 +381,7 @@
 		return err;
 
 	is_ac97 = chip->has_ac97_1 &&
-		(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+		(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
 
 	spin_lock_irq(&chip->reg_lock);
 	oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
@@ -391,7 +391,7 @@
 		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
 				      oxygen_rate(hw_params) |
 				      oxygen_i2s_mclk(hw_params) |
-				      chip->model->adc_i2s_format |
+				      chip->model.adc_i2s_format |
 				      oxygen_i2s_bits(hw_params),
 				      OXYGEN_I2S_RATE_MASK |
 				      OXYGEN_I2S_FORMAT_MASK |
@@ -401,7 +401,7 @@
 
 	if (!is_ac97) {
 		mutex_lock(&chip->mutex);
-		chip->model->set_adc_params(chip, hw_params);
+		chip->model.set_adc_params(chip, hw_params);
 		mutex_unlock(&chip->mutex);
 	}
 	return 0;
@@ -468,7 +468,7 @@
 			     OXYGEN_MULTICH_FORMAT_MASK);
 	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
 			      oxygen_rate(hw_params) |
-			      chip->model->dac_i2s_format |
+			      chip->model.dac_i2s_format |
 			      oxygen_i2s_bits(hw_params),
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
@@ -478,7 +478,7 @@
 	spin_unlock_irq(&chip->reg_lock);
 
 	mutex_lock(&chip->mutex);
-	chip->model->set_dac_params(chip, hw_params);
+	chip->model.set_dac_params(chip, hw_params);
 	mutex_unlock(&chip->mutex);
 	return 0;
 }
@@ -657,25 +657,26 @@
 	int outs, ins;
 	int err;
 
-	outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S);
-	ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 |
-					     CAPTURE_0_FROM_I2S_2));
+	outs = !!(chip->model.device_config & PLAYBACK_0_TO_I2S);
+	ins = !!(chip->model.device_config & (CAPTURE_0_FROM_I2S_1 |
+					      CAPTURE_0_FROM_I2S_2));
 	if (outs | ins) {
-		err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
+		err = snd_pcm_new(chip->card, "Multichannel",
+				  0, outs, ins, &pcm);
 		if (err < 0)
 			return err;
 		if (outs)
 			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
 					&oxygen_multich_ops);
-		if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+		if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
 			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
 					&oxygen_rec_a_ops);
-		else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2)
+		else if (chip->model.device_config & CAPTURE_0_FROM_I2S_2)
 			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
 					&oxygen_rec_b_ops);
 		pcm->private_data = chip;
 		pcm->private_free = oxygen_pcm_free;
-		strcpy(pcm->name, "Analog");
+		strcpy(pcm->name, "Multichannel");
 		if (outs)
 			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
 						      SNDRV_DMA_TYPE_DEV,
@@ -690,8 +691,8 @@
 						      BUFFER_BYTES_MAX);
 	}
 
-	outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
-	ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF);
+	outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF);
+	ins = !!(chip->model.device_config & CAPTURE_1_FROM_SPDIF);
 	if (outs | ins) {
 		err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
 		if (err < 0)
@@ -712,11 +713,11 @@
 	}
 
 	if (chip->has_ac97_1) {
-		outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1);
-		ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+		outs = !!(chip->model.device_config & PLAYBACK_2_TO_AC97_1);
+		ins = !!(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
 	} else {
 		outs = 0;
-		ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2);
+		ins = !!(chip->model.device_config & CAPTURE_2_FROM_I2S_2);
 	}
 	if (outs | ins) {
 		err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 01d7b75..98c6a8c 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -62,14 +62,66 @@
  * AD0 <- 0
  */
 
+/*
+ * Xonar HDAV1.3 (Deluxe)
+ * ----------------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1796 (front)
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * TXD -> HDMI controller
+ * RXD <- HDMI controller
+ *
+ * PCM1796 front: AD1,0 <- 0,0
+ *
+ * no daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 1
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ *
+ * I²C <-> PCM1796 (surround)
+ *     <-> PCM1796 (center/LFE)
+ *     <-> PCM1796 (back)
+ *
+ * PCM1796 surround:   AD1,0 <- 0,1
+ * PCM1796 center/LFE: AD1,0 <- 1,0
+ * PCM1796 back:       AD1,0 <- 1,1
+ *
+ * unknown daughterboard
+ * ---------------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 1
+ *
+ * I²C <-> CS4362A (surround, center/LFE, back)
+ *
+ * CS4362A: AD0 <- 0
+ */
+
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <sound/ac97_codec.h>
+#include <sound/asoundef.h>
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <sound/tlv.h>
 #include "oxygen.h"
 #include "cm9780.h"
@@ -98,12 +150,15 @@
 	MODEL_D2X,
 	MODEL_D1,
 	MODEL_DX,
+	MODEL_HDAV,	/* without daughterboard */
+	MODEL_HDAV_H6,	/* with H6 daughterboard */
 };
 
 static struct pci_device_id xonar_ids[] __devinitdata = {
 	{ OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
 	{ }
 };
@@ -124,11 +179,18 @@
 #define GPIO_DX_FRONT_PANEL	0x0002
 #define GPIO_DX_INPUT_ROUTE	0x0100
 
+#define GPIO_HDAV_DB_MASK	0x0030
+#define GPIO_HDAV_DB_H6		0x0000
+#define GPIO_HDAV_DB_XX		0x0020
+
+#define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ADx=i, /W=0 */
 #define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */
 #define I2C_DEVICE_CS4362A	0x30	/* 001100, AD0=0, /W=0 */
 
 struct xonar_data {
+	unsigned int model;
 	unsigned int anti_pop_delay;
+	unsigned int dacs;
 	u16 output_enable_bit;
 	u8 ext_power_reg;
 	u8 ext_power_int_reg;
@@ -137,10 +199,13 @@
 	u8 pcm1796_oversampling;
 	u8 cs4398_fm;
 	u8 cs4362a_fm;
+	u8 hdmi_params[5];
 };
 
-static void pcm1796_write(struct oxygen *chip, unsigned int codec,
-			  u8 reg, u8 value)
+static void xonar_gpio_changed(struct oxygen *chip);
+
+static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec,
+				     u8 reg, u8 value)
 {
 	/* maps ALSA channel pair number to SPI output */
 	static const u8 codec_map[4] = {
@@ -154,6 +219,22 @@
 			 (reg << 8) | value);
 }
 
+static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec,
+				     u8 reg, u8 value)
+{
+	oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value);
+}
+
+static void pcm1796_write(struct oxygen *chip, unsigned int codec,
+			  u8 reg, u8 value)
+{
+	if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
+	    OXYGEN_FUNCTION_SPI)
+		pcm1796_write_spi(chip, codec, reg, value);
+	else
+		pcm1796_write_i2c(chip, codec, reg, value);
+}
+
 static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
 {
 	oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
@@ -164,6 +245,24 @@
 	oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
 }
 
+static void hdmi_write_command(struct oxygen *chip, u8 command,
+			       unsigned int count, const u8 *params)
+{
+	unsigned int i;
+	u8 checksum;
+
+	oxygen_write_uart(chip, 0xfb);
+	oxygen_write_uart(chip, 0xef);
+	oxygen_write_uart(chip, command);
+	oxygen_write_uart(chip, count);
+	for (i = 0; i < count; ++i)
+		oxygen_write_uart(chip, params[i]);
+	checksum = 0xfb + 0xef + command + count;
+	for (i = 0; i < count; ++i)
+		checksum += params[i];
+	oxygen_write_uart(chip, checksum);
+}
+
 static void xonar_enable_output(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -180,6 +279,7 @@
 		oxygen_set_bits8(chip, data->ext_power_int_reg,
 				 data->ext_power_bit);
 		chip->interrupt_mask |= OXYGEN_INT_GPIO;
+		chip->model.gpio_changed = xonar_gpio_changed;
 		data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
 				     & data->ext_power_bit);
 	}
@@ -193,9 +293,10 @@
 
 static void update_pcm1796_volume(struct oxygen *chip)
 {
+	struct xonar_data *data = chip->model_data;
 	unsigned int i;
 
-	for (i = 0; i < 4; ++i) {
+	for (i = 0; i < data->dacs; ++i) {
 		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
 		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
 	}
@@ -203,13 +304,14 @@
 
 static void update_pcm1796_mute(struct oxygen *chip)
 {
+	struct xonar_data *data = chip->model_data;
 	unsigned int i;
 	u8 value;
 
 	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
 	if (chip->dac_mute)
 		value |= PCM1796_MUTE;
-	for (i = 0; i < 4; ++i)
+	for (i = 0; i < data->dacs; ++i)
 		pcm1796_write(chip, i, 18, value);
 }
 
@@ -218,7 +320,7 @@
 	struct xonar_data *data = chip->model_data;
 	unsigned int i;
 
-	for (i = 0; i < 4; ++i) {
+	for (i = 0; i < data->dacs; ++i) {
 		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
 		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
 		pcm1796_write(chip, i, 21, 0);
@@ -234,6 +336,13 @@
 	data->anti_pop_delay = 300;
 	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
 	data->pcm1796_oversampling = PCM1796_OS_64;
+	if (data->model == MODEL_D2X) {
+		data->ext_power_reg = OXYGEN_GPIO_DATA;
+		data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+		data->ext_power_bit = GPIO_D2X_EXT_POWER;
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+				    GPIO_D2X_EXT_POWER);
+	}
 
 	pcm1796_init(chip);
 
@@ -246,17 +355,6 @@
 	snd_component_add(chip->card, "CS5381");
 }
 
-static void xonar_d2x_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->ext_power_reg = OXYGEN_GPIO_DATA;
-	data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
-	data->ext_power_bit = GPIO_D2X_EXT_POWER;
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
-	xonar_d2_init(chip);
-}
-
 static void update_cs4362a_volumes(struct oxygen *chip)
 {
 	u8 mute;
@@ -324,6 +422,11 @@
 	data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
 	data->cs4362a_fm = CS4362A_FM_SINGLE |
 		CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+	if (data->model == MODEL_DX) {
+		data->ext_power_reg = OXYGEN_GPI_DATA;
+		data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+		data->ext_power_bit = GPI_DX_EXT_POWER;
+	}
 
 	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 		       OXYGEN_2WIRE_LENGTH_8 |
@@ -344,30 +447,86 @@
 	snd_component_add(chip->card, "CS5361");
 }
 
-static void xonar_dx_init(struct oxygen *chip)
+static void xonar_hdav_init(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
+	u8 param;
 
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+
+	data->anti_pop_delay = 100;
+	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
 	data->ext_power_reg = OXYGEN_GPI_DATA;
 	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
 	data->ext_power_bit = GPI_DX_EXT_POWER;
-	xonar_d1_init(chip);
+	data->pcm1796_oversampling = PCM1796_OS_64;
+
+	pcm1796_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE);
+
+	oxygen_reset_uart(chip);
+	param = 0;
+	hdmi_write_command(chip, 0x61, 1, &param);
+	param = 1;
+	hdmi_write_command(chip, 0x74, 1, &param);
+	data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
+	data->hdmi_params[4] = 1;
+	hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
+
+	xonar_common_init(chip);
+
+	snd_component_add(chip->card, "PCM1796");
+	snd_component_add(chip->card, "CS5381");
 }
 
-static void xonar_cleanup(struct oxygen *chip)
+static void xonar_disable_output(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
 
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
 }
 
+static void xonar_d2_cleanup(struct oxygen *chip)
+{
+	xonar_disable_output(chip);
+}
+
 static void xonar_d1_cleanup(struct oxygen *chip)
 {
-	xonar_cleanup(chip);
+	xonar_disable_output(chip);
 	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
 	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
 }
 
+static void xonar_hdav_cleanup(struct oxygen *chip)
+{
+	u8 param = 0;
+
+	hdmi_write_command(chip, 0x74, 1, &param);
+	xonar_disable_output(chip);
+}
+
+static void xonar_d2_suspend(struct oxygen *chip)
+{
+	xonar_d2_cleanup(chip);
+}
+
+static void xonar_d1_suspend(struct oxygen *chip)
+{
+	xonar_d1_cleanup(chip);
+}
+
+static void xonar_hdav_suspend(struct oxygen *chip)
+{
+	xonar_hdav_cleanup(chip);
+	msleep(2);
+}
+
 static void xonar_d2_resume(struct oxygen *chip)
 {
 	pcm1796_init(chip);
@@ -380,6 +539,33 @@
 	xonar_enable_output(chip);
 }
 
+static void xonar_hdav_resume(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+	u8 param;
+
+	oxygen_reset_uart(chip);
+	param = 0;
+	hdmi_write_command(chip, 0x61, 1, &param);
+	param = 1;
+	hdmi_write_command(chip, 0x74, 1, &param);
+	hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
+	pcm1796_init(chip);
+	xonar_enable_output(chip);
+}
+
+static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
+					   struct snd_pcm_hardware *hardware)
+{
+	if (channel == PCM_MULTICH) {
+		hardware->rates = SNDRV_PCM_RATE_44100 |
+				  SNDRV_PCM_RATE_48000 |
+				  SNDRV_PCM_RATE_96000 |
+				  SNDRV_PCM_RATE_192000;
+		hardware->rate_min = 44100;
+	}
+}
+
 static void set_pcm1796_params(struct oxygen *chip,
 			       struct snd_pcm_hw_params *params)
 {
@@ -388,7 +574,7 @@
 
 	data->pcm1796_oversampling =
 		params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
-	for (i = 0; i < 4; ++i)
+	for (i = 0; i < data->dacs; ++i)
 		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
 }
 
@@ -430,6 +616,42 @@
 	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
 }
 
+static void set_hdmi_params(struct oxygen *chip,
+			    struct snd_pcm_hw_params *params)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->hdmi_params[0] = 0; /* 1 = non-audio */
+	switch (params_rate(params)) {
+	case 44100:
+		data->hdmi_params[1] = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
+		break;
+	default: /* 96000 */
+		data->hdmi_params[1] = IEC958_AES3_CON_FS_96000;
+		break;
+	case 192000:
+		data->hdmi_params[1] = IEC958_AES3_CON_FS_192000;
+		break;
+	}
+	data->hdmi_params[2] = params_channels(params) / 2 - 1;
+	if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+		data->hdmi_params[3] = 0;
+	else
+		data->hdmi_params[3] = 0xc0;
+	data->hdmi_params[4] = 1; /* ? */
+	hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
+}
+
+static void set_hdav_params(struct oxygen *chip,
+			    struct snd_pcm_hw_params *params)
+{
+	set_pcm1796_params(chip, params);
+	set_hdmi_params(chip, params);
+}
+
 static void xonar_gpio_changed(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -449,29 +671,43 @@
 	}
 }
 
-static int alt_switch_get(struct snd_kcontrol *ctl,
-			  struct snd_ctl_elem_value *value)
+static void xonar_hdav_uart_input(struct oxygen *chip)
+{
+	if (chip->uart_input_count >= 2 &&
+	    chip->uart_input[chip->uart_input_count - 2] == 'O' &&
+	    chip->uart_input[chip->uart_input_count - 1] == 'K') {
+		printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:");
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+				     chip->uart_input, chip->uart_input_count);
+		chip->uart_input_count = 0;
+	}
+}
+
+static int gpio_bit_switch_get(struct snd_kcontrol *ctl,
+			       struct snd_ctl_elem_value *value)
 {
 	struct oxygen *chip = ctl->private_data;
+	u16 bit = ctl->private_value;
 
 	value->value.integer.value[0] =
-		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT);
+		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
 	return 0;
 }
 
-static int alt_switch_put(struct snd_kcontrol *ctl,
-			  struct snd_ctl_elem_value *value)
+static int gpio_bit_switch_put(struct snd_kcontrol *ctl,
+			       struct snd_ctl_elem_value *value)
 {
 	struct oxygen *chip = ctl->private_data;
+	u16 bit = ctl->private_value;
 	u16 old_bits, new_bits;
 	int changed;
 
 	spin_lock_irq(&chip->reg_lock);
 	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
 	if (value->value.integer.value[0])
-		new_bits = old_bits | GPIO_D2_ALT;
+		new_bits = old_bits | bit;
 	else
-		new_bits = old_bits & ~GPIO_D2_ALT;
+		new_bits = old_bits & ~bit;
 	changed = new_bits != old_bits;
 	if (changed)
 		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
@@ -483,47 +719,22 @@
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Analog Loopback Switch",
 	.info = snd_ctl_boolean_mono_info,
-	.get = alt_switch_get,
-	.put = alt_switch_put,
+	.get = gpio_bit_switch_get,
+	.put = gpio_bit_switch_put,
+	.private_value = GPIO_D2_ALT,
 };
 
-static int front_panel_get(struct snd_kcontrol *ctl,
-			   struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-
-	value->value.integer.value[0] =
-		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL);
-	return 0;
-}
-
-static int front_panel_put(struct snd_kcontrol *ctl,
-			   struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	u16 old_reg, new_reg;
-
-	spin_lock_irq(&chip->reg_lock);
-	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
-	if (value->value.integer.value[0])
-		new_reg = old_reg | GPIO_DX_FRONT_PANEL;
-	else
-		new_reg = old_reg & ~GPIO_DX_FRONT_PANEL;
-	oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
-	spin_unlock_irq(&chip->reg_lock);
-	return old_reg != new_reg;
-}
-
 static const struct snd_kcontrol_new front_panel_switch = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Front Panel Switch",
 	.info = snd_ctl_boolean_mono_info,
-	.get = front_panel_get,
-	.put = front_panel_put,
+	.get = gpio_bit_switch_get,
+	.put = gpio_bit_switch_put,
+	.private_value = GPIO_DX_FRONT_PANEL,
 };
 
-static void xonar_d1_ac97_switch(struct oxygen *chip,
-				 unsigned int reg, unsigned int mute)
+static void xonar_line_mic_ac97_switch(struct oxygen *chip,
+				       unsigned int reg, unsigned int mute)
 {
 	if (reg == AC97_LINE) {
 		spin_lock_irq(&chip->reg_lock);
@@ -552,7 +763,7 @@
 	return 0;
 }
 
-static int xonar_mixer_init(struct oxygen *chip)
+static int xonar_d2_mixer_init(struct oxygen *chip)
 {
 	return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
 }
@@ -562,130 +773,147 @@
 	return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
 }
 
-static const struct oxygen_model xonar_models[] = {
-	[MODEL_D2] = {
-		.shortname = "Xonar D2",
-		.longname = "Asus Virtuoso 200",
-		.chip = "AV200",
-		.owner = THIS_MODULE,
-		.init = xonar_d2_init,
-		.control_filter = xonar_d2_control_filter,
-		.mixer_init = xonar_mixer_init,
-		.cleanup = xonar_cleanup,
-		.suspend = xonar_cleanup,
-		.resume = xonar_d2_resume,
-		.set_dac_params = set_pcm1796_params,
-		.set_adc_params = set_cs53x1_params,
-		.update_dac_volume = update_pcm1796_volume,
-		.update_dac_mute = update_pcm1796_mute,
-		.dac_tlv = pcm1796_db_scale,
-		.model_data_size = sizeof(struct xonar_data),
-		.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-			       PLAYBACK_1_TO_SPDIF |
-			       CAPTURE_0_FROM_I2S_2 |
-			       CAPTURE_1_FROM_SPDIF,
-		.dac_channels = 8,
-		.dac_volume_min = 0x0f,
-		.dac_volume_max = 0xff,
-		.misc_flags = OXYGEN_MISC_MIDI,
-		.function_flags = OXYGEN_FUNCTION_SPI |
-				  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
-		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	},
-	[MODEL_D2X] = {
-		.shortname = "Xonar D2X",
-		.longname = "Asus Virtuoso 200",
-		.chip = "AV200",
-		.owner = THIS_MODULE,
-		.init = xonar_d2x_init,
-		.control_filter = xonar_d2_control_filter,
-		.mixer_init = xonar_mixer_init,
-		.cleanup = xonar_cleanup,
-		.suspend = xonar_cleanup,
-		.resume = xonar_d2_resume,
-		.set_dac_params = set_pcm1796_params,
-		.set_adc_params = set_cs53x1_params,
-		.update_dac_volume = update_pcm1796_volume,
-		.update_dac_mute = update_pcm1796_mute,
-		.gpio_changed = xonar_gpio_changed,
-		.dac_tlv = pcm1796_db_scale,
-		.model_data_size = sizeof(struct xonar_data),
-		.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-			       PLAYBACK_1_TO_SPDIF |
-			       CAPTURE_0_FROM_I2S_2 |
-			       CAPTURE_1_FROM_SPDIF,
-		.dac_channels = 8,
-		.dac_volume_min = 0x0f,
-		.dac_volume_max = 0xff,
-		.misc_flags = OXYGEN_MISC_MIDI,
-		.function_flags = OXYGEN_FUNCTION_SPI |
-				  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
-		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	},
-	[MODEL_D1] = {
-		.shortname = "Xonar D1",
-		.longname = "Asus Virtuoso 100",
-		.chip = "AV200",
-		.owner = THIS_MODULE,
-		.init = xonar_d1_init,
-		.control_filter = xonar_d1_control_filter,
-		.mixer_init = xonar_d1_mixer_init,
-		.cleanup = xonar_d1_cleanup,
-		.suspend = xonar_d1_cleanup,
-		.resume = xonar_d1_resume,
-		.set_dac_params = set_cs43xx_params,
-		.set_adc_params = set_cs53x1_params,
-		.update_dac_volume = update_cs43xx_volume,
-		.update_dac_mute = update_cs43xx_mute,
-		.ac97_switch = xonar_d1_ac97_switch,
-		.dac_tlv = cs4362a_db_scale,
-		.model_data_size = sizeof(struct xonar_data),
-		.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-			       PLAYBACK_1_TO_SPDIF |
-			       CAPTURE_0_FROM_I2S_2,
-		.dac_channels = 8,
-		.dac_volume_min = 0,
-		.dac_volume_max = 127,
-		.function_flags = OXYGEN_FUNCTION_2WIRE,
-		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	},
-	[MODEL_DX] = {
-		.shortname = "Xonar DX",
-		.longname = "Asus Virtuoso 100",
-		.chip = "AV200",
-		.owner = THIS_MODULE,
-		.init = xonar_dx_init,
-		.control_filter = xonar_d1_control_filter,
-		.mixer_init = xonar_d1_mixer_init,
-		.cleanup = xonar_d1_cleanup,
-		.suspend = xonar_d1_cleanup,
-		.resume = xonar_d1_resume,
-		.set_dac_params = set_cs43xx_params,
-		.set_adc_params = set_cs53x1_params,
-		.update_dac_volume = update_cs43xx_volume,
-		.update_dac_mute = update_cs43xx_mute,
-		.gpio_changed = xonar_gpio_changed,
-		.ac97_switch = xonar_d1_ac97_switch,
-		.dac_tlv = cs4362a_db_scale,
-		.model_data_size = sizeof(struct xonar_data),
-		.pcm_dev_cfg = PLAYBACK_0_TO_I2S |
-			       PLAYBACK_1_TO_SPDIF |
-			       CAPTURE_0_FROM_I2S_2,
-		.dac_channels = 8,
-		.dac_volume_min = 0,
-		.dac_volume_max = 127,
-		.function_flags = OXYGEN_FUNCTION_2WIRE,
-		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	},
+static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data)
+{
+	static const char *const names[] = {
+		[MODEL_D1]	= "Xonar D1",
+		[MODEL_DX]	= "Xonar DX",
+		[MODEL_D2]	= "Xonar D2",
+		[MODEL_D2X]	= "Xonar D2X",
+		[MODEL_HDAV]	= "Xonar HDAV1.3",
+		[MODEL_HDAV_H6]	= "Xonar HDAV1.3+H6",
+	};
+	static const u8 dacs[] = {
+		[MODEL_D1]	= 2,
+		[MODEL_DX]	= 2,
+		[MODEL_D2]	= 4,
+		[MODEL_D2X]	= 4,
+		[MODEL_HDAV]	= 1,
+		[MODEL_HDAV_H6]	= 4,
+	};
+	struct xonar_data *data = chip->model_data;
+
+	data->model = driver_data;
+	if (data->model == MODEL_HDAV) {
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+				    GPIO_HDAV_DB_MASK);
+		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+			GPIO_HDAV_DB_MASK) {
+		case GPIO_HDAV_DB_H6:
+			data->model = MODEL_HDAV_H6;
+			break;
+		case GPIO_HDAV_DB_XX:
+			snd_printk(KERN_ERR "unknown daughterboard\n");
+			return -ENODEV;
+		}
+	}
+
+	data->dacs = dacs[data->model];
+	chip->model.shortname = names[data->model];
+	return 0;
+}
+
+static const struct oxygen_model model_xonar_d2 = {
+	.longname = "Asus Virtuoso 200",
+	.chip = "AV200",
+	.owner = THIS_MODULE,
+	.probe = xonar_model_probe,
+	.init = xonar_d2_init,
+	.control_filter = xonar_d2_control_filter,
+	.mixer_init = xonar_d2_mixer_init,
+	.cleanup = xonar_d2_cleanup,
+	.suspend = xonar_d2_suspend,
+	.resume = xonar_d2_resume,
+	.set_dac_params = set_pcm1796_params,
+	.set_adc_params = set_cs53x1_params,
+	.update_dac_volume = update_pcm1796_volume,
+	.update_dac_mute = update_pcm1796_mute,
+	.dac_tlv = pcm1796_db_scale,
+	.model_data_size = sizeof(struct xonar_data),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2 |
+			 CAPTURE_1_FROM_SPDIF |
+			 MIDI_OUTPUT |
+			 MIDI_INPUT,
+	.dac_channels = 8,
+	.dac_volume_min = 0x0f,
+	.dac_volume_max = 0xff,
+	.misc_flags = OXYGEN_MISC_MIDI,
+	.function_flags = OXYGEN_FUNCTION_SPI |
+			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_d1 = {
+	.longname = "Asus Virtuoso 100",
+	.chip = "AV200",
+	.owner = THIS_MODULE,
+	.probe = xonar_model_probe,
+	.init = xonar_d1_init,
+	.control_filter = xonar_d1_control_filter,
+	.mixer_init = xonar_d1_mixer_init,
+	.cleanup = xonar_d1_cleanup,
+	.suspend = xonar_d1_suspend,
+	.resume = xonar_d1_resume,
+	.set_dac_params = set_cs43xx_params,
+	.set_adc_params = set_cs53x1_params,
+	.update_dac_volume = update_cs43xx_volume,
+	.update_dac_mute = update_cs43xx_mute,
+	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dac_tlv = cs4362a_db_scale,
+	.model_data_size = sizeof(struct xonar_data),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2,
+	.dac_channels = 8,
+	.dac_volume_min = 0,
+	.dac_volume_max = 127,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_hdav = {
+	.longname = "Asus Virtuoso 200",
+	.chip = "AV200",
+	.owner = THIS_MODULE,
+	.probe = xonar_model_probe,
+	.init = xonar_hdav_init,
+	.cleanup = xonar_hdav_cleanup,
+	.suspend = xonar_hdav_suspend,
+	.resume = xonar_hdav_resume,
+	.pcm_hardware_filter = xonar_hdav_pcm_hardware_filter,
+	.set_dac_params = set_hdav_params,
+	.set_adc_params = set_cs53x1_params,
+	.update_dac_volume = update_pcm1796_volume,
+	.update_dac_mute = update_pcm1796_mute,
+	.uart_input = xonar_hdav_uart_input,
+	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dac_tlv = pcm1796_db_scale,
+	.model_data_size = sizeof(struct xonar_data),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2,
+	.dac_channels = 8,
+	.dac_volume_min = 0x0f,
+	.dac_volume_max = 0xff,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
 static int __devinit xonar_probe(struct pci_dev *pci,
 				 const struct pci_device_id *pci_id)
 {
+	static const struct oxygen_model *const models[] = {
+		[MODEL_D1]	= &model_xonar_d1,
+		[MODEL_DX]	= &model_xonar_d1,
+		[MODEL_D2]	= &model_xonar_d2,
+		[MODEL_D2X]	= &model_xonar_d2,
+		[MODEL_HDAV]	= &model_xonar_hdav,
+	};
 	static int dev;
 	int err;
 
@@ -695,8 +923,10 @@
 		++dev;
 		return -ENOENT;
 	}
+	BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));
 	err = oxygen_pci_probe(pci, index[dev], id[dev],
-			       &xonar_models[pci_id->driver_data]);
+			       models[pci_id->driver_data],
+			       pci_id->driver_data);
 	if (err >= 0)
 		++dev;
 	return err;
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 2c7e253..0e06c6c 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -464,7 +464,8 @@
 	pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
 	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
 
-	snd_assert(subs->runtime->dma_bytes < 0x200000);	/* max buffer size is 2 MByte */
+	/* max buffer size is 2 MByte */
+	snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
 	rmh.cmd[1] = subs->runtime->dma_bytes * 8;		/* size in bits */
 	rmh.cmd[2] = subs->runtime->dma_addr >> 24;		/* most significant byte */
 	rmh.cmd[2] |= 1<<19;					/* this is a circular buffer */
@@ -1228,7 +1229,8 @@
 		return -ENOMEM;
 	}
 
-	snd_assert(pci_id->driver_data < PCI_ID_LAST, return -ENODEV);
+	if (snd_BUG_ON(pci_id->driver_data >= PCI_ID_LAST))
+		return -ENODEV;
 	card_name = pcxhr_board_params[pci_id->driver_data].board_name;
 	mgr->playback_chips = pcxhr_board_params[pci_id->driver_data].playback_chips;
 	mgr->capture_chips  = pcxhr_board_params[pci_id->driver_data].capture_chips;
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 000e6fe..7143259 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -319,16 +319,20 @@
 	const unsigned char *data;
 	unsigned char dummy;
 	/* check the length of boot image */
-	snd_assert(dsp->size > 0, return -EINVAL);
-	snd_assert(dsp->size % 3 == 0, return -EINVAL);
-	snd_assert(dsp->data, return -EINVAL);
+	if (dsp->size <= 0)
+		return -EINVAL;
+	if (dsp->size % 3)
+		return -EINVAL;
+	if (snd_BUG_ON(!dsp->data))
+		return -EINVAL;
 	/* transfert data buffer from PC to DSP */
 	for (i = 0; i < dsp->size; i += 3) {
 		data = dsp->data + i;
 		if (i == 0) {
 			/* test data header consistency */
 			len = (unsigned int)((data[0]<<16) + (data[1]<<8) + data[2]);
-			snd_assert((len==0) || (dsp->size == (len+2)*3), return -EINVAL);
+			if (len && dsp->size != (len + 2) * 3)
+				return -EINVAL;
 		}
 		/* wait DSP ready for new transfer */
 		err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
@@ -389,7 +393,8 @@
 	unsigned char dummy;
 
 	/* send the hostport address to the DSP (only the upper 24 bit !) */
-	snd_assert((physaddr & 0xff) == 0, return -EINVAL);
+	if (snd_BUG_ON(physaddr & 0xff))
+		return -EINVAL;
 	PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX1, (physaddr >> 8));
 
 	err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_BOOT, 0);
@@ -570,7 +575,8 @@
 	u32 data;
 	unsigned char reg;
 
-	snd_assert(rmh->cmd_len<PCXHR_SIZE_MAX_CMD, return -EINVAL);
+	if (snd_BUG_ON(rmh->cmd_len >= PCXHR_SIZE_MAX_CMD))
+		return -EINVAL;
 	err = pcxhr_send_it_dsp(mgr, PCXHR_IT_MESSAGE, 1);
 	if (err) {
 		snd_printk(KERN_ERR "pcxhr_send_message : ED_DSP_CRASHED\n");
@@ -677,7 +683,8 @@
  */
 void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd)
 {
-	snd_assert(cmd < CMD_LAST_INDEX, return);
+	if (snd_BUG_ON(cmd >= CMD_LAST_INDEX))
+		return;
 	rmh->cmd[0] = pcxhr_dsp_cmds[cmd].opcode;
 	rmh->cmd_len = 1;
 	rmh->stat_len = pcxhr_dsp_cmds[cmd].st_length;
@@ -690,17 +697,17 @@
 			       unsigned int param1, unsigned int param2,
 			       unsigned int param3)
 {
-	snd_assert(param1 <= MASK_FIRST_FIELD);
+	snd_BUG_ON(param1 > MASK_FIRST_FIELD);
 	if (capture)
 		rmh->cmd[0] |= 0x800;		/* COMMAND_RECORD_MASK */
 	if (param1)
 		rmh->cmd[0] |= (param1 << FIELD_SIZE);
 	if (param2) {
-		snd_assert(param2 <= MASK_FIRST_FIELD);
+		snd_BUG_ON(param2 > MASK_FIRST_FIELD);
 		rmh->cmd[0] |= param2;
 	}
 	if(param3) {
-		snd_assert(param3 <= MASK_DSP_WORD);
+		snd_BUG_ON(param3 > MASK_DSP_WORD);
 		rmh->cmd[1] = param3;
 		rmh->cmd_len = 2;
 	}
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index d2f0432..96640d9 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -65,15 +65,18 @@
 	if (err)
 		return err;
 	/* test 8 or 12 phys out */
-	snd_assert((rmh.stat[0] & MASK_FIRST_FIELD) == mgr->playback_chips*2,
-		   return -EINVAL);
+	if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2)
+		return -EINVAL;
 	/* test 8 or 2 phys in */
-	snd_assert(((rmh.stat[0] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD) ==
-		   mgr->capture_chips * 2, return -EINVAL);
+	if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) !=
+	    mgr->capture_chips * 2)
+		return -EINVAL;
 	/* test max nb substream per board */
-	snd_assert((rmh.stat[1] & 0x5F) >= card_streams, return -EINVAL);
+	if ((rmh.stat[1] & 0x5F) < card_streams)
+		return -EINVAL;
 	/* test max nb substream per pipe */
-	snd_assert(((rmh.stat[1]>>7)&0x5F) >= PCXHR_PLAYBACK_STREAMS, return -EINVAL);
+	if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS)
+		return -EINVAL;
 
 	pcxhr_init_rmh(&rmh, CMD_VERSION);
 	/* firmware num for DSP */
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 6a35962..e9f0706 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -865,7 +865,8 @@
 	struct riptideport *hwport;
 	struct cmdport *cmdport = NULL;
 
-	snd_assert(cif, return -EINVAL);
+	if (snd_BUG_ON(!cif))
+		return -EINVAL;
 
 	hwport = cif->hwport;
 	if (cif->errcnt > MAX_ERROR_COUNT) {
@@ -1482,7 +1483,6 @@
 {
 	struct snd_riptide *chip = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 	struct pcmhw *data = get_pcmhwdev(substream);
 	struct cmdif *cif = chip->cif;
 	unsigned char *lbuspath = NULL;
@@ -1490,7 +1490,8 @@
 	int err = 0;
 	snd_pcm_format_t format;
 
-	snd_assert(cif && data, return -EINVAL);
+	if (snd_BUG_ON(!cif || !data))
+		return -EINVAL;
 
 	snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id,
 		    runtime->channels, runtime->format, runtime->rate);
@@ -1513,9 +1514,9 @@
 			lbuspath = data->paths.stereo;
 		break;
 	}
-	snd_printdd("use sgdlist at 0x%p and buffer at 0x%p\n",
-		    data->sgdlist.area, sgbuf);
-	if (data->sgdlist.area && sgbuf) {
+	snd_printdd("use sgdlist at 0x%p\n",
+		    data->sgdlist.area);
+	if (data->sgdlist.area) {
 		unsigned int i, j, size, pages, f, pt, period;
 		struct sgd *c, *p = NULL;
 
@@ -1533,6 +1534,7 @@
 		pt = 0;
 		j = 0;
 		for (i = 0; i < pages; i++) {
+			unsigned int ofs, addr;
 			c = &data->sgdbuf[i];
 			if (p)
 				p->dwNextLink = cpu_to_le32(data->sgdlist.addr +
@@ -1540,8 +1542,9 @@
 							     sizeof(struct
 								    sgd)));
 			c->dwNextLink = cpu_to_le32(data->sgdlist.addr);
-			c->dwSegPtrPhys =
-			    cpu_to_le32(sgbuf->table[j].addr + pt);
+			ofs = j << PAGE_SHIFT;
+			addr = snd_pcm_sgbuf_get_addr(substream, ofs) + pt;
+			c->dwSegPtrPhys = cpu_to_le32(addr);
 			pt = (pt + f) % PAGE_SIZE;
 			if (pt == 0)
 				j++;
@@ -1772,7 +1775,8 @@
 	union cmdret rptr = CMDRET_ZERO;
 	int i = 0;
 
-	snd_assert(cif, return);
+	if (snd_BUG_ON(!cif))
+		return;
 
 	snd_printdd("Write AC97 reg 0x%x 0x%x\n", reg, val);
 	do {
@@ -1790,7 +1794,8 @@
 	struct cmdif *cif = chip->cif;
 	union cmdret rptr = CMDRET_ZERO;
 
-	snd_assert(cif, return 0);
+	if (snd_BUG_ON(!cif))
+		return 0;
 
 	if (SEND_RACR(cif, reg, &rptr) != 0)
 		SEND_RACR(cif, reg, &rptr);
@@ -1804,7 +1809,8 @@
 	unsigned int device_id;
 	int err;
 
-	snd_assert(chip, return -EINVAL);
+	if (snd_BUG_ON(!chip))
+		return -EINVAL;
 
 	cif = chip->cif;
 	if (!cif) {
@@ -1836,7 +1842,8 @@
 {
 	struct cmdif *cif;
 
-	snd_assert(chip, return 0);
+	if (!chip)
+		return 0;
 
 	if ((cif = chip->cif)) {
 		SET_GRESET(cif->hwport);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 4d6fbb3..d723543 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -1036,7 +1036,7 @@
 	n = DDS_NUMERATOR;
 	div64_32(&n, rate, &r);
 	/* n should be less than 2^32 for being written to FREQ register */
-	snd_assert((n >> 32) == 0);
+	snd_BUG_ON(n >> 32);
 	/* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS
 	   value to write it after a reset */
 	hdsp->dds_value = n;
@@ -3043,7 +3043,7 @@
 	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
 
 	offset = ucontrol->id.index - 1;
-	snd_assert(offset >= 0);
+	snd_BUG_ON(offset < 0);
 
 	switch (hdsp->io_type) {
 	case Digiface:
@@ -3767,7 +3767,8 @@
 {
 	int mapped_channel;
 
-        snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL);
+        if (snd_BUG_ON(channel < 0 || channel >= hdsp->max_channels))
+		return NULL;
         
 	if ((mapped_channel = hdsp->channel_map[channel]) < 0)
 		return NULL;
@@ -3784,10 +3785,12 @@
 	struct hdsp *hdsp = snd_pcm_substream_chip(substream);
 	char *channel_buf;
 
-	snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+	if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4))
+		return -EINVAL;
 
 	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	if (copy_from_user(channel_buf + pos * 4, src, count * 4))
 		return -EFAULT;
 	return count;
@@ -3799,10 +3802,12 @@
 	struct hdsp *hdsp = snd_pcm_substream_chip(substream);
 	char *channel_buf;
 
-	snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+	if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4))
+		return -EINVAL;
 
 	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
 		return -EFAULT;
 	return count;
@@ -3815,7 +3820,8 @@
 	char *channel_buf;
 
 	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	memset(channel_buf + pos * 4, 0, count * 4);
 	return count;
 }
@@ -3927,7 +3933,8 @@
 	struct hdsp *hdsp = snd_pcm_substream_chip(substream);
 	int mapped_channel;
 
-	snd_assert(info->channel < hdsp->max_channels, return -EINVAL);
+	if (snd_BUG_ON(info->channel >= hdsp->max_channels))
+		return -EINVAL;
 
 	if ((mapped_channel = hdsp->channel_map[info->channel]) < 0)
 		return -EINVAL;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index ab423bc..98762f9 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -535,7 +535,8 @@
 static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm);
 static int hdspm_autosync_ref(struct hdspm * hdspm);
 static int snd_hdspm_set_defaults(struct hdspm * hdspm);
-static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf,
+static void hdspm_set_sgbuf(struct hdspm * hdspm,
+			    struct snd_pcm_substream *substream,
 			     unsigned int reg, int channels);
 
 static inline int HDSPM_bit2freq(int n)
@@ -845,7 +846,7 @@
 	n = 110100480000000ULL;    /* Value checked for AES32 and MADI */
 	div64_32(&n, rate, &r);
 	/* n should be less than 2^32 for being written to FREQ register */
-	snd_assert((n >> 32) == 0);
+	snd_BUG_ON(n >> 32);
 	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
 }
 
@@ -2617,8 +2618,8 @@
 
 	channel = ucontrol->id.index - 1;
 
-	snd_assert(channel >= 0
-		   || channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+		return -EINVAL;
 
 	mapped_channel = hdspm->channel_map[channel];
 	if (mapped_channel < 0)
@@ -2652,8 +2653,8 @@
 
 	channel = ucontrol->id.index - 1;
 
-	snd_assert(channel >= 0
-		   || channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+		return -EINVAL;
 
 	mapped_channel = hdspm->channel_map[channel];
 	if (mapped_channel < 0)
@@ -3496,8 +3497,8 @@
 {
 	int mapped_channel;
 
-	snd_assert(channel >= 0
-		   || channel < HDSPM_MAX_CHANNELS, return NULL);
+	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS))
+		return NULL;
 
 	mapped_channel = hdspm->channel_map[channel];
 	if (mapped_channel < 0)
@@ -3520,14 +3521,15 @@
 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
 	char *channel_buf;
 
-	snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4,
-		   return -EINVAL);
+	if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
+		return -EINVAL;
 
 	channel_buf =
 		hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
 					      channel);
 
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 
 	return copy_from_user(channel_buf + pos * 4, src, count * 4);
 }
@@ -3539,13 +3541,14 @@
 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
 	char *channel_buf;
 
-	snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4,
-		   return -EINVAL);
+	if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4))
+		return -EINVAL;
 
 	channel_buf =
 		hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
 					      channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	return copy_to_user(dst, channel_buf + pos * 4, count * 4);
 }
 
@@ -3559,7 +3562,8 @@
 	channel_buf =
 		hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
 					      channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	memset(channel_buf + pos * 4, 0, count * 4);
 	return 0;
 }
@@ -3601,8 +3605,6 @@
 	int i;
 	pid_t this_pid;
 	pid_t other_pid;
-	struct snd_sg_buf *sgbuf;
-
 
 	spin_lock_irq(&hdspm->lock);
 
@@ -3670,11 +3672,9 @@
 	if (err < 0)
 		return err;
 
-	sgbuf = snd_pcm_substream_sgbuf(substream);
-
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
-		hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferOut,
+		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,
 				params_channels(params));
 
 		for (i = 0; i < params_channels(params); ++i)
@@ -3685,7 +3685,7 @@
 		snd_printdd("Allocated sample buffer for playback at %p\n",
 				hdspm->playback_buffer);
 	} else {
-		hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn,
+		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,
 				params_channels(params));
 
 		for (i = 0; i < params_channels(params); ++i)
@@ -3700,7 +3700,7 @@
 	   snd_printdd("Allocated sample buffer for %s at 0x%08X\n",
 	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
 	   "playback" : "capture",
-	   snd_pcm_sgbuf_get_addr(sgbuf, 0));
+	   snd_pcm_sgbuf_get_addr(substream, 0));
 	 */
 	/*
 	snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n",
@@ -3744,7 +3744,8 @@
 	struct hdspm *hdspm = snd_pcm_substream_chip(substream);
 	int mapped_channel;
 
-	snd_assert(info->channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+	if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS))
+		return -EINVAL;
 
 	mapped_channel = hdspm->channel_map[info->channel];
 	if (mapped_channel < 0)
@@ -4249,13 +4250,14 @@
 	return 0;
 }
 
-static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf,
+static void hdspm_set_sgbuf(struct hdspm * hdspm,
+			    struct snd_pcm_substream *substream,
 			     unsigned int reg, int channels)
 {
 	int i;
 	for (i = 0; i < (channels * 16); i++)
 		hdspm_write(hdspm, reg + 4 * i,
-			    snd_pcm_sgbuf_get_addr(sgbuf, (size_t) 4096 * i));
+			    snd_pcm_sgbuf_get_addr(substream, 4096 * i));
 }
 
 /* ------------- ALSA Devices ---------------------------- */
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index a123f0e..2570907 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -595,8 +595,6 @@
 	} else {
 		int mapped_channel;
 
-		snd_assert(channel == RME9652_NCHANNELS, return);
-
 		mapped_channel = rme9652->channel_map[channel];
 
 		if (enable) {
@@ -1893,7 +1891,8 @@
 {
 	int mapped_channel;
 
-        snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL);
+	if (snd_BUG_ON(channel < 0 || channel >= RME9652_NCHANNELS))
+		return NULL;
         
 	if ((mapped_channel = rme9652->channel_map[channel]) < 0) {
 		return NULL;
@@ -1914,12 +1913,14 @@
 	struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
 	char *channel_buf;
 
-	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+	if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4))
+		return -EINVAL;
 
 	channel_buf = rme9652_channel_buffer_location (rme9652,
 						       substream->pstr->stream,
 						       channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	if (copy_from_user(channel_buf + pos * 4, src, count * 4))
 		return -EFAULT;
 	return count;
@@ -1931,12 +1932,14 @@
 	struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
 	char *channel_buf;
 
-	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL);
+	if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4))
+		return -EINVAL;
 
 	channel_buf = rme9652_channel_buffer_location (rme9652,
 						       substream->pstr->stream,
 						       channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	if (copy_to_user(dst, channel_buf + pos * 4, count * 4))
 		return -EFAULT;
 	return count;
@@ -1951,7 +1954,8 @@
 	channel_buf = rme9652_channel_buffer_location (rme9652,
 						       substream->pstr->stream,
 						       channel);
-	snd_assert(channel_buf != NULL, return -EIO);
+	if (snd_BUG_ON(!channel_buf))
+		return -EIO;
 	memset(channel_buf + pos * 4, 0, count * 4);
 	return count;
 }
@@ -2053,7 +2057,8 @@
 	struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);
 	int chn;
 
-	snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL);
+	if (snd_BUG_ON(info->channel >= RME9652_NCHANNELS))
+		return -EINVAL;
 
 	if ((chn = rme9652->channel_map[info->channel]) < 0) {
 		return -EINVAL;
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 0d3d305..cd408b8 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -534,8 +534,8 @@
 			params->rate_den = 1;
 		} else {
 			snd_sonicvibes_pll(rate, &r, &m, &n);
-			snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL);
-			snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL);
+			snd_BUG_ON(SV_REFFREQUENCY % 16);
+			snd_BUG_ON(SV_ADCMULT % 512);
 			params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r;
 			params->rate_den = (SV_ADCMULT/512) * (m+2);
 		}
@@ -849,7 +849,8 @@
 
 	if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)
 		return err;
-	snd_assert(pcm != NULL, return -EINVAL);
+	if (snd_BUG_ON(!pcm))
+		return -EINVAL;
 
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops);
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops);
@@ -1089,7 +1090,8 @@
 	unsigned int idx;
 	int err;
 
-	snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!sonic || !sonic->card))
+		return -EINVAL;
 	card = sonic->card;
 	strcpy(card->mixername, "S3 SonicVibes");
 
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index a69b420..c612b43 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -2931,7 +2931,8 @@
 {
 	struct snd_trident_pcm_mixer *tmix;
 
-	snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL);
+	if (snd_BUG_ON(!trident || !voice || !substream))
+		return -EINVAL;
 	tmix = &trident->pcm_mixer[substream->number];
 	tmix->voice = voice;
 	tmix->vol = T4D_DEFAULT_PCM_VOL;
@@ -2946,7 +2947,8 @@
 {
 	struct snd_trident_pcm_mixer *tmix;
 
-	snd_assert(trident != NULL && substream != NULL, return -EINVAL);
+	if (snd_BUG_ON(!trident || !substream))
+		return -EINVAL;
 	tmix = &trident->pcm_mixer[substream->number];
 	tmix->voice = NULL;
 	snd_trident_notify_pcm_change(trident, tmix, substream->number, 0);
@@ -3131,7 +3133,8 @@
 {
 	struct snd_trident *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 	return inb(TRID_REG(chip, GAMEPORT_LEGACY));
 }
 
@@ -3139,7 +3142,8 @@
 {
 	struct snd_trident *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return);
+	if (snd_BUG_ON(!chip))
+		return;
 	outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));
 }
 
@@ -3148,7 +3152,8 @@
 	struct snd_trident *chip = gameport_get_port_data(gameport);
 	int i;
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 
 	*buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf;
 
@@ -3164,7 +3169,8 @@
 {
 	struct snd_trident *chip = gameport_get_port_data(gameport);
 
-	snd_assert(chip, return 0);
+	if (snd_BUG_ON(!chip))
+		return 0;
 
 	switch (mode) {
 		case GAMEPORT_MODE_COOKED:
@@ -3891,8 +3897,8 @@
 {
 	unsigned int i, val, mask[2] = { 0, 0 };
 
-	snd_assert(v_min <= 63, return);
-	snd_assert(v_max <= 63, return);
+	if (snd_BUG_ON(v_min > 63 || v_max > 63))
+		return;
 	for (i = v_min; i <= v_max; i++)
 		mask[i >> 5] |= 1 << (i & 0x1f);
 	if (mask[0]) {
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index 3fd7f1b..f9779e2 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -194,11 +194,14 @@
 	struct snd_util_memblk *blk;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int idx, page;
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 
-	snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+	if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+		       runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
+					SNDRV_TRIDENT_PAGE_SIZE))
+		return NULL;
 	hdr = trident->tlb.memhdr;
-	snd_assert(hdr != NULL, return NULL);
+	if (snd_BUG_ON(!hdr))
+		return NULL;
 
 	
 
@@ -208,18 +211,14 @@
 		mutex_unlock(&hdr->block_mutex);
 		return NULL;
 	}
-	if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) {
-		snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk));
-		__snd_util_mem_free(hdr, blk);
-		mutex_unlock(&hdr->block_mutex);
-		return NULL;
-	}
 			   
 	/* set TLB entries */
 	idx = 0;
 	for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) {
-		dma_addr_t addr = sgbuf->table[idx].addr;
-		unsigned long ptr = (unsigned long)sgbuf->table[idx].buf;
+		unsigned long ofs = idx << PAGE_SHIFT;
+		dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+		unsigned long ptr = (unsigned long)
+			snd_pcm_sgbuf_get_ptr(substream, ofs);
 		if (! is_valid_page(addr)) {
 			__snd_util_mem_free(hdr, blk);
 			mutex_unlock(&hdr->block_mutex);
@@ -245,9 +244,13 @@
 	dma_addr_t addr;
 	unsigned long ptr;
 
-	snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL);
+	if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+		       runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
+					SNDRV_TRIDENT_PAGE_SIZE))
+		return NULL;
 	hdr = trident->tlb.memhdr;
-	snd_assert(hdr != NULL, return NULL);
+	if (snd_BUG_ON(!hdr))
+		return NULL;
 
 	mutex_lock(&hdr->block_mutex);
 	blk = search_empty(hdr, runtime->dma_bytes);
@@ -279,8 +282,8 @@
 snd_trident_alloc_pages(struct snd_trident *trident,
 			struct snd_pcm_substream *substream)
 {
-	snd_assert(trident != NULL, return NULL);
-	snd_assert(substream != NULL, return NULL);
+	if (snd_BUG_ON(!trident || !substream))
+		return NULL;
 	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG)
 		return snd_trident_alloc_sg_pages(trident, substream);
 	else
@@ -297,8 +300,8 @@
 	struct snd_util_memhdr *hdr;
 	int page;
 
-	snd_assert(trident != NULL, return -EINVAL);
-	snd_assert(blk != NULL, return -EINVAL);
+	if (snd_BUG_ON(!trident || !blk))
+		return -EINVAL;
 
 	hdr = trident->tlb.memhdr;
 	mutex_lock(&hdr->block_mutex);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 6781be9..1aafe95 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -313,6 +313,7 @@
 } ;
 
 #define VIA_TABLE_SIZE	255
+#define VIA_MAX_BUFSIZE	(1<<24)
 
 struct viadev {
 	unsigned int reg_offset;
@@ -420,7 +421,6 @@
 {
 	unsigned int i, idx, ofs, rest;
 	struct via82xx *chip = snd_pcm_substream_chip(substream);
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 
 	if (dev->table.area == NULL) {
 		/* the start of each lists must be aligned to 8 bytes,
@@ -449,15 +449,15 @@
 		do {
 			unsigned int r;
 			unsigned int flag;
+			unsigned int addr;
 
 			if (idx >= VIA_TABLE_SIZE) {
 				snd_printk(KERN_ERR "via82xx: too much table size!\n");
 				return -EINVAL;
 			}
-			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
-			r = PAGE_SIZE - (ofs % PAGE_SIZE);
-			if (rest < r)
-				r = rest;
+			addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
+			r = snd_pcm_sgbuf_get_chunk_size(substream, ofs, rest);
 			rest -= r;
 			if (! rest) {
 				if (i == periods - 1)
@@ -824,7 +824,8 @@
 	struct viadev *viadev = substream->runtime->private_data;
 	unsigned int idx, ptr, count, res;
 
-	snd_assert(viadev->tbl_entries, return 0);
+	if (snd_BUG_ON(!viadev->tbl_entries))
+		return 0;
 	if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
 		return 0;
 
@@ -855,7 +856,8 @@
 	unsigned int idx, count, res;
 	int status;
 	
-	snd_assert(viadev->tbl_entries, return 0);
+	if (snd_BUG_ON(!viadev->tbl_entries))
+		return 0;
 
 	spin_lock(&chip->reg_lock);
 	count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT));
@@ -1037,7 +1039,7 @@
 	else
 		rbits = (0x100000 / 48000) * runtime->rate +
 			((0x100000 % 48000) * runtime->rate) / 48000;
-	snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
+	snd_BUG_ON(rbits & ~0xfffff);
 	snd_via82xx_channel_reset(chip, viadev);
 	snd_via82xx_set_table_ptr(chip, viadev);
 	outb(chip->playback_volume[viadev->reg_offset / 0x10][0],
@@ -1144,9 +1146,9 @@
 	.rate_max =		48000,
 	.channels_min =		1,
 	.channels_max =		2,
-	.buffer_bytes_max =	128 * 1024,
+	.buffer_bytes_max =	VIA_MAX_BUFSIZE,
 	.period_bytes_min =	32,
-	.period_bytes_max =	128 * 1024,
+	.period_bytes_max =	VIA_MAX_BUFSIZE / 2,
 	.periods_min =		2,
 	.periods_max =		VIA_TABLE_SIZE / 2,
 	.fifo_size =		0,
@@ -1398,10 +1400,9 @@
 	/* capture */
 	init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
 
-	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
-							 snd_dma_pci_data(chip->pci),
-							 64*1024, 128*1024)) < 0)
-		return err;
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, VIA_MAX_BUFSIZE);
 
 	/* PCM #1:  multi-channel playback and 2nd capture */
 	err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
@@ -1417,11 +1418,9 @@
 	/* set up capture */
 	init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 7, 1);
 
-	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
-						         snd_dma_pci_data(chip->pci),
-							 64*1024, 128*1024)) < 0)
-		return err;
-
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, VIA_MAX_BUFSIZE);
 	return 0;
 }
 
@@ -1453,10 +1452,9 @@
 	/* capture */
 	init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1);
 
-	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
-							 snd_dma_pci_data(chip->pci),
-							 64*1024, 128*1024)) < 0)
-		return err;
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, VIA_MAX_BUFSIZE);
 
 	/* SPDIF supported? */
 	if (! ac97_can_spdif(chip->ac97))
@@ -1473,11 +1471,9 @@
 	/* set up playback */
 	init_viadev(chip, chip->playback_devno, 0x30, 3, 0);
 
-	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
-							 snd_dma_pci_data(chip->pci),
-							 64*1024, 128*1024)) < 0)
-		return err;
-
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, VIA_MAX_BUFSIZE);
 	return 0;
 }
 
@@ -1505,11 +1501,9 @@
 	init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0);
 	init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1);
 
-	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
-							 snd_dma_pci_data(chip->pci),
-							 64*1024, 128*1024)) < 0)
-		return err;
-
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+					      snd_dma_pci_data(chip->pci),
+					      64*1024, VIA_MAX_BUFSIZE);
 	return 0;
 }
 
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 31f64ee..5bd79d2 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -281,7 +281,6 @@
 {
 	unsigned int i, idx, ofs, rest;
 	struct via82xx_modem *chip = snd_pcm_substream_chip(substream);
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 
 	if (dev->table.area == NULL) {
 		/* the start of each lists must be aligned to 8 bytes,
@@ -310,12 +309,14 @@
 		do {
 			unsigned int r;
 			unsigned int flag;
+			unsigned int addr;
 
 			if (idx >= VIA_TABLE_SIZE) {
 				snd_printk(KERN_ERR "via82xx: too much table size!\n");
 				return -EINVAL;
 			}
-			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+			addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);
 			r = PAGE_SIZE - (ofs % PAGE_SIZE);
 			if (rest < r)
 				r = rest;
@@ -612,7 +613,8 @@
 	struct viadev *viadev = substream->runtime->private_data;
 	unsigned int idx, ptr, count, res;
 
-	snd_assert(viadev->tbl_entries, return 0);
+	if (snd_BUG_ON(!viadev->tbl_entries))
+		return 0;
 	if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
 		return 0;
 
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 631f3a6..7e87f39 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -253,7 +253,8 @@
 	int offset = pipe->hw_ptr;
 	u32 *addr = (u32 *)(runtime->dma_area + offset);
 
-	snd_assert(count % 4 == 0, return);
+	if (snd_BUG_ON(count % 4))
+		return;
 
 	vx2_setup_pseudo_dma(chip, 1);
 
@@ -291,7 +292,8 @@
 	u32 *addr = (u32 *)(runtime->dma_area + offset);
 	unsigned long port = vx2_reg_addr(chip, VX_DMA);
 
-	snd_assert(count % 4 == 0, return);
+	if (snd_BUG_ON(count % 4))
+		return;
 
 	vx2_setup_pseudo_dma(chip, 0);
 	/* Transfer using pseudo-dma.
@@ -675,7 +677,8 @@
 	   a look up table, as there is no linear matching between the driver codec values
 	   and the real dBu value
 	*/
-	snd_assert(data < sizeof(vx2_akm_gains_lut), return);
+	if (snd_BUG_ON(data >= sizeof(vx2_akm_gains_lut)))
+		return;
 
 	switch (reg) {
 	case XX_CODEC_LEVEL_LEFT_REGISTER:
@@ -823,7 +826,8 @@
 		preamp++;	/* raise pre ampli + 18dB */
 		miclevel -= (18 * 2);   /* lower level 18 dB (*2 because of 0.5 dB steps !) */
         }
-	snd_assert(preamp < 4, return);
+	if (snd_BUG_ON(preamp >= 4))
+		return;
 
 	/* set pre-amp level */
 	chip->regSELMIC &= ~MICRO_SELECT_PREAMPLI_MASK;
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 92d49aa..90d0d62 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -259,8 +259,10 @@
 	unsigned long flags;
 	int result;
 	
-	snd_assert(rvoice != NULL, return -EINVAL);
-	snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);
+	if (snd_BUG_ON(!rvoice))
+		return -EINVAL;
+	if (snd_BUG_ON(pair && type != YMFPCI_PCM))
+		return -EINVAL;
 	
 	spin_lock_irqsave(&chip->voice_lock, flags);
 	for (;;) {
@@ -278,7 +280,8 @@
 {
 	unsigned long flags;
 	
-	snd_assert(pvoice != NULL, return -EINVAL);
+	if (snd_BUG_ON(!pvoice))
+		return -EINVAL;
 	snd_ymfpci_hw_stop(chip);
 	spin_lock_irqsave(&chip->voice_lock, flags);
 	if (pvoice->number == chip->src441_used) {
@@ -494,7 +497,8 @@
 	u8 use_left, use_right;
 	unsigned long flags;
 
-	snd_assert(voice != NULL, return);
+	if (snd_BUG_ON(!voice))
+		return;
 	if (runtime->channels == 1) {
 		use_left = 1;
 		use_right = 1;
@@ -1813,7 +1817,8 @@
 	}
 
 	/* add S/PDIF control */
-	snd_assert(chip->pcm_spdif != NULL, return -EIO);
+	if (snd_BUG_ON(!chip->pcm_spdif))
+		return -ENXIO;
 	if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)
 		return err;
 	kctl->id.device = chip->pcm_spdif->device;
@@ -2133,7 +2138,8 @@
 	chip->work_base = ptr;
 	chip->work_base_addr = ptr_addr;
 	
-	snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, );
+	snd_BUG_ON(ptr + chip->work_size !=
+		   chip->work_ptr.area + chip->work_ptr.bytes);
 
 	snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);
 	snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr);
@@ -2168,7 +2174,8 @@
 {
 	u16 ctrl;
 
-	snd_assert(chip != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip))
+		return -EINVAL;
 
 	if (chip->res_reg_area) {	/* don't touch busy hardware */
 		snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
index 99bf2a6..989e04a 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -408,7 +408,8 @@
 	int offset = pipe->hw_ptr;
 	unsigned short *addr = (unsigned short *)(runtime->dma_area + offset);
 
-	snd_assert(count % 2 == 0, return);
+	if (snd_BUG_ON(count % 2))
+		return;
 	vx_setup_pseudo_dma(chip, 0);
 	if (offset + count > pipe->buffer_bytes) {
 		int length = pipe->buffer_bytes - offset;
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 106c482..7bd33e6 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -319,7 +319,8 @@
 static void awacs_amp_free(struct snd_pmac *chip)
 {
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return);
+	if (!amp)
+		return;
 	kfree(amp);
 	chip->mixer_data = NULL;
 	chip->mixer_free = NULL;
@@ -345,8 +346,7 @@
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	int index = kcontrol->private_value;
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return -EINVAL);
-	snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
 	ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);
 	ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);
 	return 0;
@@ -359,8 +359,6 @@
 	int index = kcontrol->private_value;
 	int vol[2];
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return -EINVAL);
-	snd_assert(index >= 0 && index <= 1, return -EINVAL);
 
 	vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))
 		| (amp->amp_vol[index][0] & 32);
@@ -375,8 +373,7 @@
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	int index = kcontrol->private_value;
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return -EINVAL);
-	snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
 	ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)
 					? 0 : 1;
 	ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32)
@@ -391,8 +388,6 @@
 	int index = kcontrol->private_value;
 	int vol[2];
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return -EINVAL);
-	snd_assert(index >= 0 && index <= 1, return -EINVAL);
 
 	vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)
 		| (amp->amp_vol[index][0] & 31);
@@ -417,8 +412,7 @@
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	int index = kcontrol->private_value;
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return -EINVAL);
-	snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
 	ucontrol->value.integer.value[0] = amp->amp_tone[index];
 	return 0;
 }
@@ -430,8 +424,7 @@
 	int index = kcontrol->private_value;
 	struct awacs_amp *amp = chip->mixer_data;
 	unsigned int val;
-	snd_assert(amp, return -EINVAL);
-	snd_assert(index >= 0 && index <= 1, return -EINVAL);
+
 	val = ucontrol->value.integer.value[0];
 	if (val > 14)
 		return -EINVAL;
@@ -458,7 +451,7 @@
 {
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	struct awacs_amp *amp = chip->mixer_data;
-	snd_assert(amp, return -EINVAL);
+
 	ucontrol->value.integer.value[0] = amp->amp_master;
 	return 0;
 }
@@ -469,7 +462,7 @@
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	struct awacs_amp *amp = chip->mixer_data;
 	unsigned int val;
-	snd_assert(amp, return -EINVAL);
+
 	val = ucontrol->value.integer.value[0];
 	if (val > 99)
 		return -EINVAL;
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index baa2a72..89f5c32 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -185,7 +185,8 @@
 			     struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
-	snd_assert(chip->beep, return -ENXIO);
+	if (snd_BUG_ON(!chip->beep))
+		return -ENXIO;
 	ucontrol->value.integer.value[0] = chip->beep->volume;
 	return 0;
 }
@@ -195,7 +196,8 @@
 {
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	unsigned int oval, nval;
-	snd_assert(chip->beep, return -ENXIO);
+	if (snd_BUG_ON(!chip->beep))
+		return -ENXIO;
 	oval = chip->beep->volume;
 	nval = ucontrol->value.integer.value[0];
 	if (nval > 100)
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 009df8d..f746e15 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -263,7 +263,7 @@
 {
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	struct pmac_tumbler *mix = chip->mixer_data;
-	snd_assert(mix, return -ENODEV);
+
 	ucontrol->value.integer.value[0] = mix->master_vol[0];
 	ucontrol->value.integer.value[1] = mix->master_vol[1];
 	return 0;
@@ -277,7 +277,6 @@
 	unsigned int vol[2];
 	int change;
 
-	snd_assert(mix, return -ENODEV);
 	vol[0] = ucontrol->value.integer.value[0];
 	vol[1] = ucontrol->value.integer.value[1];
 	if (vol[0] >= ARRAY_SIZE(master_volume_table) ||
@@ -299,7 +298,7 @@
 {
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	struct pmac_tumbler *mix = chip->mixer_data;
-	snd_assert(mix, return -ENODEV);
+
 	ucontrol->value.integer.value[0] = mix->master_switch[0];
 	ucontrol->value.integer.value[1] = mix->master_switch[1];
 	return 0;
@@ -312,7 +311,6 @@
 	struct pmac_tumbler *mix = chip->mixer_data;
 	int change;
 
-	snd_assert(mix, return -ENODEV);
 	change = mix->master_switch[0] != ucontrol->value.integer.value[0] ||
 		mix->master_switch[1] != ucontrol->value.integer.value[1];
 	if (change) {
@@ -807,7 +805,6 @@
 	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 	struct pmac_tumbler *mix = chip->mixer_data;
 
-	snd_assert(mix, return -ENODEV);
 	ucontrol->value.enumerated.item[0] = mix->capture_source;
 	return 0;
 }
@@ -819,7 +816,6 @@
 	struct pmac_tumbler *mix = chip->mixer_data;
 	int change;
 
-	snd_assert(mix, return -ENODEV);
 	change = ucontrol->value.enumerated.item[0] != mix->capture_source;
 	if (change) {
 		mix->capture_source = !!ucontrol->value.enumerated.item[0];
@@ -978,7 +974,8 @@
 		return;
 
 	mix = chip->mixer_data;
-	snd_assert(mix, return);
+	if (snd_BUG_ON(!mix))
+		return;
 
 	headphone = tumbler_detect_headphone(chip);
 	lineout = tumbler_detect_lineout(chip);
@@ -1033,7 +1030,8 @@
 	if (chip->auto_mute) {
 		struct pmac_tumbler *mix;
 		mix = chip->mixer_data;
-		snd_assert(mix, return);
+		if (snd_BUG_ON(!mix))
+			return;
 		mix->auto_mute_notify = do_notify;
 		schedule_work(&device_change);
 	}
@@ -1227,8 +1225,6 @@
 {
 	struct pmac_tumbler *mix = chip->mixer_data;
 
-	snd_assert(mix, return);
-
 	mix->acs &= ~1;
 	mix->master_switch[0] = mix->save_master_switch[0];
 	mix->master_switch[1] = mix->save_master_switch[1];
@@ -1275,7 +1271,6 @@
 {
 	int irq;
 	struct pmac_tumbler *mix = chip->mixer_data;
-	snd_assert(mix, return -EINVAL);
 
 	if (tumbler_find_device("audio-hw-reset",
 				"platform-do-hw-reset",
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 54df8ba..7c920f3 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -106,7 +106,8 @@
 {
 	int i;
 	unsigned long flags;
-	snd_assert(length % 4 == 0, return);
+	if (snd_BUG_ON(length % 4))
+		return;
 	for (i = 0; i < length; i++) {
 		if (!(i % 8))
 			spu_write_wait();
@@ -589,7 +590,7 @@
 	return 0;
 }
 
-static int snd_aica_remove(struct platform_device *devptr)
+static int __devexit snd_aica_remove(struct platform_device *devptr)
 {
 	struct snd_card_aica *dreamcastcard;
 	dreamcastcard = platform_get_drvdata(devptr);
@@ -601,7 +602,7 @@
 	return 0;
 }
 
-static int __init snd_aica_probe(struct platform_device *devptr)
+static int __devinit snd_aica_probe(struct platform_device *devptr)
 {
 	int err;
 	struct snd_card_aica *dreamcastcard;
@@ -650,7 +651,7 @@
 
 static struct platform_driver snd_aica_driver = {
 	.probe = snd_aica_probe,
-	.remove = snd_aica_remove,
+	.remove = __devexit_p(snd_aica_remove),
 	.driver = {
 		   .name = SND_AICA_DRIVER},
 };
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index f743530..4dfda66 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -5,6 +5,7 @@
 menuconfig SND_SOC
 	tristate "ALSA for SoC audio support"
 	select SND_PCM
+	select AC97_BUS if SND_SOC_AC97_BUS
 	---help---
 
 	  If you want ASoC support, you should say Y here and also to the
@@ -31,6 +32,7 @@
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/davinci/Kconfig"
 source "sound/soc/omap/Kconfig"
+source "sound/soc/blackfin/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 933a66d..d849349 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -2,4 +2,4 @@
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
-obj-$(CONFIG_SND_SOC)	+= omap/ au1x/
+obj-$(CONFIG_SND_SOC)	+= omap/ au1x/ blackfin/
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c
index 3f32621..98a2d58 100644
--- a/sound/soc/at32/playpaq_wm8510.c
+++ b/sound/soc/at32/playpaq_wm8510.c
@@ -377,6 +377,7 @@
 
 
 static struct wm8510_setup_data playpaq_wm8510_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1a,
 };
 
@@ -405,7 +406,6 @@
 	ssc = ssc_request(0);
 	if (IS_ERR(ssc)) {
 		ret = PTR_ERR(ssc);
-		ssc = NULL;
 		goto err_ssc;
 	}
 	ssc_p->ssc = ssc;
@@ -476,10 +476,7 @@
 		_gclk0 = NULL;
 	}
 err_gclk0:
-	if (ssc != NULL) {
-		ssc_free(ssc);
-		ssc = NULL;
-	}
+	ssc_free(ssc);
 err_ssc:
 	return ret;
 }
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index 5d44515..a5b1a79 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -408,7 +408,7 @@
 		dma_params->pdc_xfer_size = 4;
 		break;
 	default:
-		printk(KERN_WARNING "at91-ssc: unsupported PCM format");
+		printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");
 		return -EINVAL;
 	}
 
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
index b81d6b2..684781e 100644
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -243,6 +243,7 @@
 };
 
 static struct wm8731_setup_data eti_b1_wm8731_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1a,
 };
 
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
new file mode 100644
index 0000000..f98331d
--- /dev/null
+++ b/sound/soc/blackfin/Kconfig
@@ -0,0 +1,85 @@
+config SND_BF5XX_I2S
+	tristate "SoC I2S Audio for the ADI BF5xx chip"
+	depends on BLACKFIN && SND_SOC
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the Blackfin SPORT (synchronous serial ports) interface in I2S
+	  mode (supports single stereo In/Out).
+	  You will also need to select the audio interfaces to support below.
+
+config SND_BF5XX_SOC_SSM2602
+	tristate "SoC SSM2602 Audio support for BF52x ezkit"
+	depends on SND_BF5XX_I2S
+	select SND_BF5XX_SOC_I2S
+	select SND_SOC_SSM2602
+	select I2C
+	select I2C_BLACKFIN_TWI
+	help
+	  Say Y if you want to add support for SoC audio on BF527-EZKIT.
+
+config SND_BF5XX_AC97
+	tristate "SoC AC97 Audio for the ADI BF5xx chip"
+	depends on BLACKFIN && SND_SOC
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the Blackfin SPORT (synchronous serial ports) interface in slot 16
+	  mode (pseudo AC97 interface).
+	  You will also need to select the audio interfaces to support below.
+
+	  Note:
+	  AC97 codecs which do not implment the slot-16 mode will not function
+	  properly with this driver. This driver is known to work with the
+	  Analog Devices line of AC97 codecs.
+
+config SND_MMAP_SUPPORT
+	bool "Enable MMAP Support"
+	depends on SND_BF5XX_AC97
+	default y
+	help
+	  Say y if you want AC97 driver to support mmap mode.
+	  We introduce an intermediate buffer to simulate mmap.
+
+config SND_BF5XX_SOC_SPORT
+	tristate
+	
+config SND_BF5XX_SOC_I2S
+	tristate
+	select SND_BF5XX_SOC_SPORT
+
+config SND_BF5XX_SOC_AC97
+	tristate
+	select AC97_BUS
+	select SND_SOC_AC97_BUS
+	select SND_BF5XX_SOC_SPORT
+
+config SND_BF5XX_SOC_AD1980
+	tristate "SoC AD1980/1 Audio support for BF5xx"
+	depends on SND_BF5XX_AC97
+	select SND_BF5XX_SOC_AC97
+	select SND_SOC_AD1980
+	help
+	  Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+
+config SND_BF5XX_SPORT_NUM
+	int "Set a SPORT for Sound chip"
+	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
+	range 0 3 if BF54x
+	range 0 1 if (BF53x || BF561)
+	default 0
+	help
+	  Set the correct SPORT for sound chip.
+
+config SND_BF5XX_HAVE_COLD_RESET
+	bool "BOARD has COLD Reset GPIO"
+	depends on SND_BF5XX_AC97
+	default y if BFIN548_EZKIT
+	default n if !BFIN548_EZKIT
+	
+config SND_BF5XX_RESET_GPIO_NUM
+	int "Set a GPIO for cold reset"
+	depends on SND_BF5XX_HAVE_COLD_RESET
+	range 0 159
+	default 19 if BFIN548_EZKIT
+	default 5 if BFIN537_STAMP
+	help
+	  Set the correct GPIO for RESET the sound chip.
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
new file mode 100644
index 0000000..9ea8bd9
--- /dev/null
+++ b/sound/soc/blackfin/Makefile
@@ -0,0 +1,20 @@
+# Blackfin Platform Support
+snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
+snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
+snd-soc-bf5xx-sport-objs := bf5xx-sport.o
+snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
+snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
+
+obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
+obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
+obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
+obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
+
+# Blackfin Machine Support
+snd-ad1980-objs := bf5xx-ad1980.o
+snd-ssm2602-objs := bf5xx-ssm2602.o
+
+
+obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
+obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
new file mode 100644
index 0000000..51f4907
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -0,0 +1,429 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-ac97-pcm.c
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  DMA Driver for AC97 sound chip
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "bf5xx-ac97-pcm.h"
+#include "bf5xx-ac97.h"
+#include "bf5xx-sport.h"
+
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
+	 snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		bf5xx_pcm_to_ac97(
+			(struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
+			(__u32 *)runtime->dma_area + sport->tx_pos, count);
+		sport->tx_pos += runtime->period_size;
+		if (sport->tx_pos >= runtime->buffer_size)
+			sport->tx_pos %= runtime->buffer_size;
+	} else {
+		bf5xx_ac97_to_pcm(
+			(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
+			(__u32 *)runtime->dma_area + sport->rx_pos, count);
+		sport->rx_pos += runtime->period_size;
+		if (sport->rx_pos >= runtime->buffer_size)
+			sport->rx_pos %= runtime->buffer_size;
+	}
+}
+#endif
+
+static void bf5xx_dma_irq(void *data)
+{
+	struct snd_pcm_substream *pcm = data;
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	bf5xx_mmap_copy(pcm, runtime->period_size);
+#endif
+	snd_pcm_period_elapsed(pcm);
+}
+
+/* The memory size for pure pcm data is 128*1024 = 0x20000 bytes.
+ * The total rx/tx buffer is for ac97 frame to hold all pcm data
+ * is  0x20000 * sizeof(struct ac97_frame) / 4.
+ */
+#ifdef CONFIG_SND_MMAP_SUPPORT
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_MMAP_VALID |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER,
+#else
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_BLOCK_TRANSFER,
+#endif
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 0x10000,
+	.periods_min		= 1,
+	.periods_max		= PAGE_SIZE/32,
+	.buffer_bytes_max	= 0x20000, /* 128 kbytes */
+	.fifo_size		= 16,
+};
+
+static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max
+			* sizeof(struct ac97_frame) / 4;
+
+	snd_pcm_lib_malloc_pages(substream, size);
+
+	return 0;
+}
+
+static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+
+	/* An intermediate buffer is introduced for implementing mmap for
+	 * SPORT working in TMD mode(include AC97).
+	 */
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max
+			* sizeof(struct ac97_frame) / 4;
+	/*clean up intermediate buffer*/
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		memset(sport->tx_dma_buf, 0, size);
+		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
+			runtime->period_size * sizeof(struct ac97_frame));
+	} else {
+		memset(sport->rx_dma_buf, 0, size);
+		sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
+			runtime->period_size * sizeof(struct ac97_frame));
+	}
+#else
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_tx_dma(sport, runtime->dma_area, runtime->periods,
+			runtime->period_size * sizeof(struct ac97_frame));
+	} else {
+		sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_rx_dma(sport, runtime->dma_area, runtime->periods,
+			runtime->period_size * sizeof(struct ac97_frame));
+	}
+#endif
+	return 0;
+}
+
+static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	int ret = 0;
+
+	pr_debug("%s enter\n", __func__);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			sport_tx_start(sport);
+		else
+			sport_rx_start(sport);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+			sport->tx_pos = 0;
+#endif
+			sport_tx_stop(sport);
+		} else {
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+			sport->rx_pos = 0;
+#endif
+			sport_rx_stop(sport);
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	unsigned int curr;
+
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		curr = sport->tx_pos;
+	else
+		curr = sport->rx_pos;
+#else
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame);
+	else
+		curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame);
+
+#endif
+	return curr;
+}
+
+static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	if (sport_handle != NULL)
+		runtime->private_data = sport_handle;
+	else {
+		pr_err("sport_handle is NULL\n");
+		return -1;
+	}
+	return 0;
+
+ out:
+	return ret;
+}
+
+#ifdef CONFIG_SND_MMAP_SUPPORT
+static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
+	struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	size_t size = vma->vm_end - vma->vm_start;
+	vma->vm_start = (unsigned long)runtime->dma_area;
+	vma->vm_end = vma->vm_start + size;
+	vma->vm_flags |=  VM_SHARED;
+	return 0 ;
+}
+#else
+static	int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
+		    snd_pcm_uframes_t pos,
+		    void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	pr_debug("%s copy pos:0x%lx count:0x%lx\n",
+			substream->stream ? "Capture" : "Playback", pos, count);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		bf5xx_pcm_to_ac97(
+				(struct ac97_frame *)runtime->dma_area + pos,
+				buf, count);
+	else
+		bf5xx_ac97_to_pcm(
+				(struct ac97_frame *)runtime->dma_area + pos,
+				buf, count);
+	return 0;
+}
+#endif
+
+struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
+	.open		= bf5xx_pcm_open,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= bf5xx_pcm_hw_params,
+	.hw_free	= bf5xx_pcm_hw_free,
+	.prepare	= bf5xx_pcm_prepare,
+	.trigger	= bf5xx_pcm_trigger,
+	.pointer	= bf5xx_pcm_pointer,
+#ifdef CONFIG_SND_MMAP_SUPPORT
+	.mmap		= bf5xx_pcm_mmap,
+#else
+	.copy		= bf5xx_pcm_copy,
+#endif
+};
+
+static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max
+			* sizeof(struct ac97_frame) / 4;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+	if (!buf->area) {
+		pr_err("Failed to allocate dma memory\n");
+		pr_err("Please increase uncached DMA memory region\n");
+		return -ENOMEM;
+	}
+	buf->bytes = size;
+
+	pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
+			buf->area, buf->bytes);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		sport_handle->tx_buf = buf->area;
+	else
+		sport_handle->rx_buf = buf->area;
+
+/*
+ * Need to allocate local buffer when enable
+ * MMAP for SPORT working in TMD mode (include AC97).
+ */
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (!sport_handle->tx_dma_buf) {
+			sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
+				size, &sport_handle->tx_dma_phy, GFP_KERNEL);
+			if (!sport_handle->tx_dma_buf) {
+				pr_err("Failed to allocate memory for tx dma \
+					buf - Please increase uncached DMA \
+					memory region\n");
+				return -ENOMEM;
+			} else
+				memset(sport_handle->tx_dma_buf, 0, size);
+		} else
+			memset(sport_handle->tx_dma_buf, 0, size);
+	} else {
+		if (!sport_handle->rx_dma_buf) {
+			sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
+				size, &sport_handle->rx_dma_phy, GFP_KERNEL);
+			if (!sport_handle->rx_dma_buf) {
+				pr_err("Failed to allocate memory for rx dma \
+					buf - Please increase uncached DMA \
+					memory region\n");
+				return -ENOMEM;
+			} else
+				memset(sport_handle->rx_dma_buf, 0, size);
+		} else
+			memset(sport_handle->rx_dma_buf, 0, size);
+	}
+#endif
+	return 0;
+}
+
+static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
+		sizeof(struct ac97_frame) / 4;
+#endif
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(NULL, buf->bytes, buf->area, 0);
+		buf->area = NULL;
+#if defined(CONFIG_SND_MMAP_SUPPORT)
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (sport_handle->tx_dma_buf)
+			dma_free_coherent(NULL, size, \
+				sport_handle->tx_dma_buf, 0);
+		sport_handle->tx_dma_buf = NULL;
+	} else {
+
+		if (sport_handle->rx_dma_buf)
+			dma_free_coherent(NULL, size, \
+				sport_handle->rx_dma_buf, 0);
+		sport_handle->rx_dma_buf = NULL;
+	}
+#endif
+	}
+	if (sport_handle)
+		sport_done(sport_handle);
+}
+
+static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
+
+int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	pr_debug("%s enter\n", __func__);
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &bf5xx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+	if (dai->playback.channels_min) {
+		ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+ out:
+	return ret;
+}
+
+struct snd_soc_platform bf5xx_ac97_soc_platform = {
+	.name		= "bf5xx-audio",
+	.pcm_ops 	= &bf5xx_pcm_ac97_ops,
+	.pcm_new	= bf5xx_pcm_ac97_new,
+	.pcm_free	= bf5xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h
new file mode 100644
index 0000000..350125a
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.h
@@ -0,0 +1,29 @@
+/*
+ * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin
+ *
+ * Copyright 2007 Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_AC97_PCM_H
+#define _BF5XX_AC97_PCM_H
+
+struct bf5xx_pcm_dma_params {
+	char *name;			/* stream identifier */
+};
+
+struct bf5xx_gpio {
+	u32 sys;
+	u32 rx;
+	u32 tx;
+	u32 clk;
+	u32 frm;
+};
+
+/* platform data */
+extern struct snd_soc_platform bf5xx_ac97_soc_platform;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
new file mode 100644
index 0000000..c782e31
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -0,0 +1,407 @@
+/*
+ * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip.
+ *
+ * Author:	Roy Huang
+ * Created:	11th. June 2007
+ * Copyright:	Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/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/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/portmux.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include "bf5xx-sport.h"
+#include "bf5xx-ac97.h"
+
+#if defined(CONFIG_BF54x)
+#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \
+		P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
+
+#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \
+		P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
+
+#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \
+		P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0}
+
+#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \
+		P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0}
+#else
+#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
+		 P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
+
+#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
+		 P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
+#endif
+
+static int *cmd_count;
+static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
+
+#if defined(CONFIG_BF54x)
+static struct sport_param sport_params[4] = {
+	{
+		.dma_rx_chan	= CH_SPORT0_RX,
+		.dma_tx_chan	= CH_SPORT0_TX,
+		.err_irq	= IRQ_SPORT0_ERR,
+		.regs		= (struct sport_register *)SPORT0_TCR1,
+	},
+	{
+		.dma_rx_chan	= CH_SPORT1_RX,
+		.dma_tx_chan	= CH_SPORT1_TX,
+		.err_irq	= IRQ_SPORT1_ERR,
+		.regs		= (struct sport_register *)SPORT1_TCR1,
+	},
+	{
+		.dma_rx_chan	= CH_SPORT2_RX,
+		.dma_tx_chan	= CH_SPORT2_TX,
+		.err_irq	= IRQ_SPORT2_ERR,
+		.regs		= (struct sport_register *)SPORT2_TCR1,
+	},
+	{
+		.dma_rx_chan	= CH_SPORT3_RX,
+		.dma_tx_chan	= CH_SPORT3_TX,
+		.err_irq	= IRQ_SPORT3_ERR,
+		.regs		= (struct sport_register *)SPORT3_TCR1,
+	}
+};
+#else
+static struct sport_param sport_params[2] = {
+	{
+		.dma_rx_chan	= CH_SPORT0_RX,
+		.dma_tx_chan	= CH_SPORT0_TX,
+		.err_irq	= IRQ_SPORT0_ERROR,
+		.regs		= (struct sport_register *)SPORT0_TCR1,
+	},
+	{
+		.dma_rx_chan	= CH_SPORT1_RX,
+		.dma_tx_chan	= CH_SPORT1_TX,
+		.err_irq	= IRQ_SPORT1_ERROR,
+		.regs		= (struct sport_register *)SPORT1_TCR1,
+	}
+};
+#endif
+
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
+		size_t count)
+{
+	while (count--) {
+		dst->ac97_tag = TAG_VALID | TAG_PCM;
+		(dst++)->ac97_pcm = *src++;
+	}
+}
+EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
+
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+		size_t count)
+{
+	while (count--)
+		*(dst++) = (src++)->ac97_pcm;
+}
+EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
+
+static unsigned int sport_tx_curr_frag(struct sport_device *sport)
+{
+	return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
+			sport->tx_fragsize;
+}
+
+static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
+{
+	struct sport_device *sport = sport_handle;
+	int nextfrag = sport_tx_curr_frag(sport);
+	struct ac97_frame *nextwrite;
+
+	sport_incfrag(sport, &nextfrag, 1);
+	sport_incfrag(sport, &nextfrag, 1);
+
+	nextwrite = (struct ac97_frame *)(sport->tx_buf + \
+			nextfrag * sport->tx_fragsize);
+	pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
+		sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
+	nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
+	nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
+	nextwrite[cmd_count[nextfrag]].ac97_data = data;
+	++cmd_count[nextfrag];
+	pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
+			addr >> 8, data, nextfrag);
+}
+
+static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
+	unsigned short reg)
+{
+	struct ac97_frame out_frame[2], in_frame[2];
+
+	pr_debug("%s enter 0x%x\n", __func__, reg);
+
+	/* When dma descriptor is enabled, the register should not be read */
+	if (sport_handle->tx_run || sport_handle->rx_run) {
+		pr_err("Could you send a mail to cliff.cai@analog.com "
+				"to report this?\n");
+		return -EFAULT;
+	}
+
+	memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
+	memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
+	out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
+	out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
+	sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
+			(unsigned char *)&in_frame,
+			2 * sizeof(struct ac97_frame));
+	return in_frame[1].ac97_data;
+}
+
+void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+	unsigned short val)
+{
+	pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
+
+	if (sport_handle->tx_run) {
+		enqueue_cmd(ac97, (reg << 8), val); /* write */
+		enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
+	} else {
+		struct ac97_frame frame;
+		memset(&frame, 0, sizeof(struct ac97_frame));
+		frame.ac97_tag = TAG_VALID | TAG_CMD;
+		frame.ac97_addr = (reg << 8);
+		frame.ac97_data = val;
+		sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
+				NULL, sizeof(struct ac97_frame));
+	}
+}
+
+static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
+ (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
+
+#define CONCAT(a, b, c) a ## b ## c
+#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
+
+	u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
+	u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
+
+	pr_debug("%s enter\n", __func__);
+
+	peripheral_free(per);
+	gpio_request(gpio, "bf5xx-ac97");
+	gpio_direction_output(gpio, 1);
+	udelay(2);
+	gpio_set_value(gpio, 0);
+	udelay(1);
+	gpio_free(gpio);
+	peripheral_request(per, "soc-audio");
+#else
+	pr_info("%s: Not implemented\n", __func__);
+#endif
+}
+
+static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+	pr_debug("%s enter\n", __func__);
+
+	/* It is specified for bf548-ezkit */
+	gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
+	/* Keep reset pin low for 1 ms */
+	mdelay(1);
+	gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
+	/* Wait for bit clock recover */
+	mdelay(1);
+#else
+	pr_info("%s: Not implemented\n", __func__);
+#endif
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read	= bf5xx_ac97_read,
+	.write	= bf5xx_ac97_write,
+	.warm_reset	= bf5xx_ac97_warm_reset,
+	.reset	= bf5xx_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+#ifdef CONFIG_PM
+static int bf5xx_ac97_suspend(struct platform_device *pdev,
+	struct snd_soc_dai *dai)
+{
+	struct sport_device *sport =
+		(struct sport_device *)dai->private_data;
+
+	pr_debug("%s : sport %d\n", __func__, dai->id);
+	if (!dai->active)
+		return 0;
+	if (dai->capture.active)
+		sport_rx_stop(sport);
+	if (dai->playback.active)
+		sport_tx_stop(sport);
+	return 0;
+}
+
+static int bf5xx_ac97_resume(struct platform_device *pdev,
+	struct snd_soc_dai *dai)
+{
+	int ret;
+	struct sport_device *sport =
+		(struct sport_device *)dai->private_data;
+
+	pr_debug("%s : sport %d\n", __func__, dai->id);
+	if (!dai->active)
+		return 0;
+
+	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		return -EBUSY;
+	}
+
+	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		return -EBUSY;
+	}
+
+	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		return -EBUSY;
+	}
+
+	if (dai->capture.active)
+		sport_rx_start(sport);
+	if (dai->playback.active)
+		sport_tx_start(sport);
+	return 0;
+}
+
+#else
+#define bf5xx_ac97_suspend	NULL
+#define bf5xx_ac97_resume	NULL
+#endif
+
+static int bf5xx_ac97_probe(struct platform_device *pdev,
+			    struct snd_soc_dai *dai)
+{
+	int ret;
+#if defined(CONFIG_BF54x)
+	u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
+				 PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
+#else
+	u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
+#endif
+	cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
+	if (cmd_count == NULL)
+		return -ENOMEM;
+
+	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+		pr_err("Requesting Peripherals failed\n");
+		return -EFAULT;
+		}
+
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+	/* Request PB3 as reset pin */
+	if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
+		pr_err("Failed to request GPIO_%d for reset\n",
+				CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+		peripheral_free_list(&sport_req[sport_num][0]);
+		return -1;
+	}
+	gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
+#endif
+	sport_handle = sport_init(&sport_params[sport_num], 2, \
+			sizeof(struct ac97_frame), NULL);
+	if (!sport_handle) {
+		peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+		return -ENODEV;
+	}
+	/*SPORT works in TDM mode to simulate AC97 transfers*/
+	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		kfree(sport_handle);
+		peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+		return -EBUSY;
+	}
+
+	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		kfree(sport_handle);
+		peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+		return -EBUSY;
+	}
+
+	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		kfree(sport_handle);
+		peripheral_free_list(&sport_req[sport_num][0]);
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+		return -EBUSY;
+	}
+	return 0;
+}
+
+static void bf5xx_ac97_remove(struct platform_device *pdev,
+			      struct snd_soc_dai *dai)
+{
+	free_page((unsigned long)cmd_count);
+	cmd_count = NULL;
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+}
+
+struct snd_soc_dai bfin_ac97_dai = {
+	.name = "bf5xx-ac97",
+	.id = 0,
+	.type = SND_SOC_DAI_AC97,
+	.probe = bf5xx_ac97_probe,
+	.remove = bf5xx_ac97_remove,
+	.suspend = bf5xx_ac97_suspend,
+	.resume = bf5xx_ac97_resume,
+	.playback = {
+		.stream_name = "AC97 Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
+	.capture = {
+		.stream_name = "AC97 Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+EXPORT_SYMBOL_GPL(bfin_ac97_dai);
+
+MODULE_AUTHOR("Roy Huang");
+MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h
new file mode 100644
index 0000000..3f77cc5
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.h
@@ -0,0 +1,36 @@
+/*
+ * linux/sound/arm/bf5xx-ac97.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_AC97_H
+#define _BF5XX_AC97_H
+
+extern struct snd_ac97_bus_ops bf5xx_ac97_ops;
+extern struct snd_ac97 *ac97;
+/* Frame format in memory, only support stereo currently */
+struct ac97_frame {
+	u16 ac97_tag;		/* slot 0 */
+	u16 ac97_addr;		/* slot 1 */
+	u16 ac97_data;		/* slot 2 */
+	u32 ac97_pcm;		/* slot 3 and 4: left and right pcm data */
+} __attribute__ ((packed));
+
+#define TAG_VALID		0x8000
+#define TAG_CMD			0x6000
+#define TAG_PCM_LEFT		0x1000
+#define TAG_PCM_RIGHT		0x0800
+#define TAG_PCM			(TAG_PCM_LEFT | TAG_PCM_RIGHT)
+
+extern struct snd_soc_dai bfin_ac97_dai;
+
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
+		size_t count);
+
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+		size_t count);
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
new file mode 100644
index 0000000..124425d
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -0,0 +1,113 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-ad1980.c
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  Board driver for AD1980/1 audio codec
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <linux/gpio.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad1980.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-ac97-pcm.h"
+#include "bf5xx-ac97.h"
+
+static struct snd_soc_machine bf5xx_board;
+
+static int bf5xx_board_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	pr_debug("%s enter\n", __func__);
+	cpu_dai->private_data = sport_handle;
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_board_ops = {
+	.startup = bf5xx_board_startup,
+};
+
+static struct snd_soc_dai_link bf5xx_board_dai = {
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai = &bfin_ac97_dai,
+	.codec_dai = &ad1980_dai,
+	.ops = &bf5xx_board_ops,
+};
+
+static struct snd_soc_machine bf5xx_board = {
+	.name = "bf5xx-board",
+	.dai_link = &bf5xx_board_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_board_snd_devdata = {
+	.machine = &bf5xx_board,
+	.platform = &bf5xx_ac97_soc_platform,
+	.codec_dev = &soc_codec_dev_ad1980,
+};
+
+static struct platform_device *bf5xx_board_snd_device;
+
+static int __init bf5xx_board_init(void)
+{
+	int ret;
+
+	bf5xx_board_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_board_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata);
+	bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev;
+	ret = platform_device_add(bf5xx_board_snd_device);
+
+	if (ret)
+		platform_device_put(bf5xx_board_snd_device);
+
+	return ret;
+}
+
+static void __exit bf5xx_board_exit(void)
+{
+	platform_device_unregister(bf5xx_board_snd_device);
+}
+
+module_init(bf5xx_board_init);
+module_exit(bf5xx_board_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
new file mode 100644
index 0000000..61fccf9
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -0,0 +1,288 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-i2s-pcm.c
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  DMA driver for i2s codec
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+#include "bf5xx-sport.h"
+
+static void bf5xx_dma_irq(void *data)
+{
+	struct snd_pcm_substream *pcm = data;
+	snd_pcm_period_elapsed(pcm);
+}
+
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_MMAP_VALID |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 0x10000,
+	.periods_min		= 1,
+	.periods_max		= PAGE_SIZE/32,
+	.buffer_bytes_max	= 0x20000, /* 128 kbytes */
+	.fifo_size		= 16,
+};
+
+static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+	snd_pcm_lib_malloc_pages(substream, size);
+
+	return 0;
+}
+
+static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	int period_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+	pr_debug("%s enter\n", __func__);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_tx_dma(sport, runtime->dma_area,
+			runtime->periods, period_bytes);
+	} else {
+		sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_rx_dma(sport, runtime->dma_area,
+			runtime->periods, period_bytes);
+	}
+
+	return 0;
+}
+
+static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	int ret = 0;
+
+	pr_debug("%s enter\n", __func__);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			sport_tx_start(sport);
+		else
+			sport_rx_start(sport);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			sport_tx_stop(sport);
+		else
+			sport_rx_stop(sport);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	unsigned int diff;
+	snd_pcm_uframes_t frames;
+	pr_debug("%s enter\n", __func__);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		diff = sport_curr_offset_tx(sport);
+		frames = bytes_to_frames(substream->runtime, diff);
+	} else {
+		diff = sport_curr_offset_rx(sport);
+		frames = bytes_to_frames(substream->runtime, diff);
+	}
+	return frames;
+}
+
+static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+
+	ret = snd_pcm_hw_constraint_integer(runtime, \
+			SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	if (sport_handle != NULL)
+		runtime->private_data = sport_handle;
+	else {
+		pr_err("sport_handle is NULL\n");
+		return -1;
+	}
+	return 0;
+
+ out:
+	return ret;
+}
+
+static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
+	struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	size_t size = vma->vm_end - vma->vm_start;
+	vma->vm_start = (unsigned long)runtime->dma_area;
+	vma->vm_end = vma->vm_start + size;
+	vma->vm_flags |=  VM_SHARED;
+
+	return 0 ;
+}
+
+struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
+	.open		= bf5xx_pcm_open,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= bf5xx_pcm_hw_params,
+	.hw_free	= bf5xx_pcm_hw_free,
+	.prepare	= bf5xx_pcm_prepare,
+	.trigger	= bf5xx_pcm_trigger,
+	.pointer	= bf5xx_pcm_pointer,
+	.mmap		= bf5xx_pcm_mmap,
+};
+
+static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+	if (!buf->area) {
+		pr_err("Failed to allocate dma memory \
+			Please increase uncached DMA memory region\n");
+		return -ENOMEM;
+	}
+	buf->bytes = size;
+
+	pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
+		buf->area, buf->bytes);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		sport_handle->tx_buf = buf->area;
+	else
+		sport_handle->rx_buf = buf->area;
+
+	return 0;
+}
+
+static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(NULL, buf->bytes, buf->area, 0);
+		buf->area = NULL;
+	}
+	if (sport_handle)
+		sport_done(sport_handle);
+}
+
+static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
+
+int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	pr_debug("%s enter\n", __func__);
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &bf5xx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+	if (dai->playback.channels_min) {
+		ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+ out:
+	return ret;
+}
+
+struct snd_soc_platform bf5xx_i2s_soc_platform = {
+	.name		= "bf5xx-audio",
+	.pcm_ops 	= &bf5xx_pcm_i2s_ops,
+	.pcm_new	= bf5xx_pcm_i2s_new,
+	.pcm_free	= bf5xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
+
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h
new file mode 100644
index 0000000..4d4609a
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h
@@ -0,0 +1,29 @@
+/*
+ * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin
+ *
+ * Copyright 2007 Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_I2S_PCM_H
+#define _BF5XX_I2S_PCM_H
+
+struct bf5xx_pcm_dma_params {
+	char *name;			/* stream identifier */
+};
+
+struct bf5xx_gpio {
+	u32 sys;
+	u32 rx;
+	u32 tx;
+	u32 clk;
+	u32 frm;
+};
+
+/* platform data */
+extern struct snd_soc_platform bf5xx_i2s_soc_platform;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
new file mode 100644
index 0000000..43a4092
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -0,0 +1,292 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-i2s.c
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  Blackfin I2S CPU DAI driver
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/portmux.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s.h"
+
+struct bf5xx_i2s_port {
+	u16 tcr1;
+	u16 rcr1;
+	u16 tcr2;
+	u16 rcr2;
+	int counter;
+};
+
+static struct bf5xx_i2s_port bf5xx_i2s;
+static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
+
+static struct sport_param sport_params[2] = {
+	{
+		.dma_rx_chan	= CH_SPORT0_RX,
+		.dma_tx_chan	= CH_SPORT0_TX,
+		.err_irq	= IRQ_SPORT0_ERROR,
+		.regs		= (struct sport_register *)SPORT0_TCR1,
+	},
+	{
+		.dma_rx_chan	= CH_SPORT1_RX,
+		.dma_tx_chan	= CH_SPORT1_TX,
+		.err_irq	= IRQ_SPORT1_ERROR,
+		.regs		= (struct sport_register *)SPORT1_TCR1,
+	}
+};
+
+static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+		unsigned int fmt)
+{
+	int ret = 0;
+
+	/* interface format:support I2S,slave mode */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ret = -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ret = -EINVAL;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		ret = -EINVAL;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		ret = -EINVAL;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
+{
+	pr_debug("%s enter\n", __func__);
+
+	/*this counter is used for counting how many pcm streams are opened*/
+	bf5xx_i2s.counter++;
+	return 0;
+}
+
+static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	int ret = 0;
+
+	bf5xx_i2s.tcr2 &= ~0x1f;
+	bf5xx_i2s.rcr2 &= ~0x1f;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bf5xx_i2s.tcr2 |= 15;
+		bf5xx_i2s.rcr2 |= 15;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bf5xx_i2s.tcr2 |= 23;
+		bf5xx_i2s.rcr2 |= 23;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bf5xx_i2s.tcr2 |= 31;
+		bf5xx_i2s.rcr2 |= 31;
+		break;
+	}
+
+	if (bf5xx_i2s.counter == 1) {
+		/*
+		 * TX and RX are not independent,they are enabled at the
+		 * same time, even if only one side is running. So, we
+		 * need to configure both of them at the time when the first
+		 * stream is opened.
+		 *
+		 * CPU DAI format:I2S, slave mode.
+		 */
+		ret = sport_config_rx(sport_handle, RFSR | RCKFE,
+				      RSFSE|bf5xx_i2s.rcr2, 0, 0);
+		if (ret) {
+			pr_err("SPORT is busy!\n");
+			return -EBUSY;
+		}
+
+		ret = sport_config_tx(sport_handle, TFSR | TCKFE,
+				      TSFSE|bf5xx_i2s.tcr2, 0, 0);
+		if (ret) {
+			pr_err("SPORT is busy!\n");
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+	pr_debug("%s enter\n", __func__);
+	bf5xx_i2s.counter--;
+}
+
+static int bf5xx_i2s_probe(struct platform_device *pdev,
+			   struct snd_soc_dai *dai)
+{
+	u16 sport_req[][7] = {
+		{ P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
+		  P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0},
+		{ P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
+		  P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0},
+	};
+
+	pr_debug("%s enter\n", __func__);
+	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+		pr_err("Requesting Peripherals failed\n");
+		return -EFAULT;
+	}
+
+	/* request DMA for SPORT */
+	sport_handle = sport_init(&sport_params[sport_num], 4, \
+			2 * sizeof(u32), NULL);
+	if (!sport_handle) {
+		peripheral_free_list(&sport_req[sport_num][0]);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bf5xx_i2s_suspend(struct platform_device *dev,
+			     struct snd_soc_dai *dai)
+{
+	struct sport_device *sport =
+		(struct sport_device *)dai->private_data;
+
+	pr_debug("%s : sport %d\n", __func__, dai->id);
+	if (!dai->active)
+		return 0;
+	if (dai->capture.active)
+		sport_rx_stop(sport);
+	if (dai->playback.active)
+		sport_tx_stop(sport);
+	return 0;
+}
+
+static int bf5xx_i2s_resume(struct platform_device *pdev,
+			    struct snd_soc_dai *dai)
+{
+	int ret;
+	struct sport_device *sport =
+		(struct sport_device *)dai->private_data;
+
+	pr_debug("%s : sport %d\n", __func__, dai->id);
+	if (!dai->active)
+		return 0;
+
+	ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		return -EBUSY;
+	}
+
+	ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		return -EBUSY;
+	}
+
+	if (dai->capture.active)
+		sport_rx_start(sport);
+	if (dai->playback.active)
+		sport_tx_start(sport);
+	return 0;
+}
+
+#else
+#define bf5xx_i2s_suspend	NULL
+#define bf5xx_i2s_resume	NULL
+#endif
+
+#define BF5XX_I2S_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 | \
+		SNDRV_PCM_RATE_96000)
+
+#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+	SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai bf5xx_i2s_dai = {
+	.name = "bf5xx-i2s",
+	.id = 0,
+	.type = SND_SOC_DAI_I2S,
+	.probe = bf5xx_i2s_probe,
+	.suspend = bf5xx_i2s_suspend,
+	.resume = bf5xx_i2s_resume,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = BF5XX_I2S_RATES,
+		.formats = BF5XX_I2S_FORMATS,},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = BF5XX_I2S_RATES,
+		.formats = BF5XX_I2S_FORMATS,},
+	.ops = {
+		.startup   = bf5xx_i2s_startup,
+		.shutdown  = bf5xx_i2s_shutdown,
+		.hw_params = bf5xx_i2s_hw_params,},
+	.dai_ops = {
+		.set_fmt = bf5xx_i2s_set_dai_fmt,
+	},
+};
+EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-i2s.h b/sound/soc/blackfin/bf5xx-i2s.h
new file mode 100644
index 0000000..7107d1a
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.h
@@ -0,0 +1,14 @@
+/*
+ * linux/sound/arm/bf5xx-i2s.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_I2S_H
+#define _BF5XX_I2S_H
+
+extern struct snd_soc_dai bf5xx_i2s_dai;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
new file mode 100644
index 0000000..3b99e48
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -0,0 +1,1032 @@
+/*
+ * File:         bf5xx_sport.c
+ * Based on:
+ * Author:       Roy Huang <roy.huang@analog.com>
+ *
+ * Created:      Tue Sep 21 10:52:42 CEST 2004
+ * Description:
+ *               Blackfin SPORT Driver
+ *
+ *               Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
+#include <linux/bug.h>
+#include <asm/portmux.h>
+#include <asm/dma.h>
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+
+#include "bf5xx-sport.h"
+/* delay between frame sync pulse and first data bit in multichannel mode */
+#define FRAME_DELAY (1<<12)
+
+struct sport_device *sport_handle;
+EXPORT_SYMBOL(sport_handle);
+/* note: multichannel is in units of 8 channels,
+ * tdm_count is # channels NOT / 8 ! */
+int sport_set_multichannel(struct sport_device *sport,
+		int tdm_count, u32 mask, int packed)
+{
+	pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__,
+			tdm_count, mask, packed);
+
+	if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
+		return -EBUSY;
+
+	if (tdm_count & 0x7)
+		return -EINVAL;
+
+	if (tdm_count > 32)
+		return -EINVAL; /* Only support less than 32 channels now */
+
+	if (tdm_count) {
+		sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12;
+		sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
+				(packed ? (MCDTXPE|MCDRXPE) : 0);
+
+		sport->regs->mtcs0 = mask;
+		sport->regs->mrcs0 = mask;
+		sport->regs->mtcs1 = 0;
+		sport->regs->mrcs1 = 0;
+		sport->regs->mtcs2 = 0;
+		sport->regs->mrcs2 = 0;
+		sport->regs->mtcs3 = 0;
+		sport->regs->mrcs3 = 0;
+	} else {
+		sport->regs->mcmc1 = 0;
+		sport->regs->mcmc2 = 0;
+
+		sport->regs->mtcs0 = 0;
+		sport->regs->mrcs0 = 0;
+	}
+
+	sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0;
+	sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0;
+
+	SSYNC();
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_set_multichannel);
+
+int sport_config_rx(struct sport_device *sport, unsigned int rcr1,
+		unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv)
+{
+	if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
+		return -EBUSY;
+
+	sport->regs->rcr1 = rcr1;
+	sport->regs->rcr2 = rcr2;
+	sport->regs->rclkdiv = clkdiv;
+	sport->regs->rfsdiv = fsdiv;
+
+	SSYNC();
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_config_rx);
+
+int sport_config_tx(struct sport_device *sport, unsigned int tcr1,
+		unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv)
+{
+	if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
+		return -EBUSY;
+
+	sport->regs->tcr1 = tcr1;
+	sport->regs->tcr2 = tcr2;
+	sport->regs->tclkdiv = clkdiv;
+	sport->regs->tfsdiv = fsdiv;
+
+	SSYNC();
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_config_tx);
+
+static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
+		size_t fragsize, unsigned int cfg,
+		unsigned int x_count, unsigned int ycount, size_t wdsize)
+{
+
+	int i;
+
+	for (i = 0; i < fragcount; ++i) {
+		desc[i].next_desc_addr  = (unsigned long)&(desc[i + 1]);
+		desc[i].start_addr = (unsigned long)buf + i*fragsize;
+		desc[i].cfg = cfg;
+		desc[i].x_count = x_count;
+		desc[i].x_modify = wdsize;
+		desc[i].y_count = ycount;
+		desc[i].y_modify = wdsize;
+	}
+
+	/* make circular */
+	desc[fragcount-1].next_desc_addr = (unsigned long)desc;
+
+	pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p,"
+		"next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
+		&(desc[0]), desc[0].next_desc_addr,
+		&(desc[1]), desc[1].next_desc_addr,
+		desc[0].x_count, desc[0].y_count,
+		desc[0].start_addr, desc[0].cfg);
+}
+
+static int sport_start(struct sport_device *sport)
+{
+	enable_dma(sport->dma_rx_chan);
+	enable_dma(sport->dma_tx_chan);
+	sport->regs->rcr1 |= RSPEN;
+	sport->regs->tcr1 |= TSPEN;
+	SSYNC();
+
+	return 0;
+}
+
+static int sport_stop(struct sport_device *sport)
+{
+	sport->regs->tcr1 &= ~TSPEN;
+	sport->regs->rcr1 &= ~RSPEN;
+	SSYNC();
+
+	disable_dma(sport->dma_rx_chan);
+	disable_dma(sport->dma_tx_chan);
+	return 0;
+}
+
+static inline int sport_hook_rx_dummy(struct sport_device *sport)
+{
+	struct dmasg *desc, temp_desc;
+	unsigned long flags;
+
+	BUG_ON(sport->dummy_rx_desc == NULL);
+	BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc);
+
+	/* Maybe the dummy buffer descriptor ring is damaged */
+	sport->dummy_rx_desc->next_desc_addr = \
+			(unsigned long)(sport->dummy_rx_desc+1);
+
+	local_irq_save(flags);
+	desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan);
+	/* Copy the descriptor which will be damaged to backup */
+	temp_desc = *desc;
+	desc->x_count = 0xa;
+	desc->y_count = 0;
+	desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc);
+	local_irq_restore(flags);
+	/* Waiting for dummy buffer descriptor is already hooked*/
+	while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
+			sizeof(struct dmasg)) !=
+			(unsigned long)sport->dummy_rx_desc)
+		;
+	sport->curr_rx_desc = sport->dummy_rx_desc;
+	/* Restore the damaged descriptor */
+	*desc = temp_desc;
+
+	return 0;
+}
+
+static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
+{
+	if (dummy) {
+		sport->dummy_rx_desc->next_desc_addr = \
+				(unsigned long) sport->dummy_rx_desc;
+		sport->curr_rx_desc = sport->dummy_rx_desc;
+	} else
+		sport->curr_rx_desc = sport->dma_rx_desc;
+
+	set_dma_next_desc_addr(sport->dma_rx_chan, \
+			(unsigned long)(sport->curr_rx_desc));
+	set_dma_x_count(sport->dma_rx_chan, 0);
+	set_dma_x_modify(sport->dma_rx_chan, 0);
+	set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \
+				WDSIZE_32 | WNR));
+	set_dma_curr_addr(sport->dma_rx_chan, sport->curr_rx_desc->start_addr);
+	SSYNC();
+
+	return 0;
+}
+
+static inline int sport_tx_dma_start(struct sport_device *sport, int dummy)
+{
+	if (dummy) {
+		sport->dummy_tx_desc->next_desc_addr = \
+				(unsigned long) sport->dummy_tx_desc;
+		sport->curr_tx_desc = sport->dummy_tx_desc;
+	} else
+		sport->curr_tx_desc = sport->dma_tx_desc;
+
+	set_dma_next_desc_addr(sport->dma_tx_chan, \
+			(unsigned long)(sport->curr_tx_desc));
+	set_dma_x_count(sport->dma_tx_chan, 0);
+	set_dma_x_modify(sport->dma_tx_chan, 0);
+	set_dma_config(sport->dma_tx_chan,
+			(DMAFLOW_LARGE | NDSIZE_9 | WDSIZE_32));
+	set_dma_curr_addr(sport->dma_tx_chan, sport->curr_tx_desc->start_addr);
+	SSYNC();
+
+	return 0;
+}
+
+int sport_rx_start(struct sport_device *sport)
+{
+	unsigned long flags;
+	pr_debug("%s enter\n", __func__);
+	if (sport->rx_run)
+		return -EBUSY;
+	if (sport->tx_run) {
+		/* tx is running, rx is not running */
+		BUG_ON(sport->dma_rx_desc == NULL);
+		BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc);
+		local_irq_save(flags);
+		while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
+			sizeof(struct dmasg)) !=
+			(unsigned long)sport->dummy_rx_desc)
+			;
+		sport->dummy_rx_desc->next_desc_addr =
+				(unsigned long)(sport->dma_rx_desc);
+		local_irq_restore(flags);
+		sport->curr_rx_desc = sport->dma_rx_desc;
+	} else {
+		sport_tx_dma_start(sport, 1);
+		sport_rx_dma_start(sport, 0);
+		sport_start(sport);
+	}
+
+	sport->rx_run = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_rx_start);
+
+int sport_rx_stop(struct sport_device *sport)
+{
+	pr_debug("%s enter\n", __func__);
+
+	if (!sport->rx_run)
+		return 0;
+	if (sport->tx_run) {
+		/* TX dma is still running, hook the dummy buffer */
+		sport_hook_rx_dummy(sport);
+	} else {
+		/* Both rx and tx dma will be stopped */
+		sport_stop(sport);
+		sport->curr_rx_desc = NULL;
+		sport->curr_tx_desc = NULL;
+	}
+
+	sport->rx_run = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_rx_stop);
+
+static inline int sport_hook_tx_dummy(struct sport_device *sport)
+{
+	struct dmasg *desc, temp_desc;
+	unsigned long flags;
+
+	BUG_ON(sport->dummy_tx_desc == NULL);
+	BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc);
+
+	sport->dummy_tx_desc->next_desc_addr = \
+			(unsigned long)(sport->dummy_tx_desc+1);
+
+	/* Shorten the time on last normal descriptor */
+	local_irq_save(flags);
+	desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan);
+	/* Store the descriptor which will be damaged */
+	temp_desc = *desc;
+	desc->x_count = 0xa;
+	desc->y_count = 0;
+	desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc);
+	local_irq_restore(flags);
+	/* Waiting for dummy buffer descriptor is already hooked*/
+	while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \
+			sizeof(struct dmasg)) != \
+			(unsigned long)sport->dummy_tx_desc)
+		;
+	sport->curr_tx_desc = sport->dummy_tx_desc;
+	/* Restore the damaged descriptor */
+	*desc = temp_desc;
+
+	return 0;
+}
+
+int sport_tx_start(struct sport_device *sport)
+{
+	unsigned flags;
+	pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__,
+			sport->tx_run, sport->rx_run);
+	if (sport->tx_run)
+		return -EBUSY;
+	if (sport->rx_run) {
+		BUG_ON(sport->dma_tx_desc == NULL);
+		BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc);
+		/* Hook the normal buffer descriptor */
+		local_irq_save(flags);
+		while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) -
+			sizeof(struct dmasg)) !=
+			(unsigned long)sport->dummy_tx_desc)
+			;
+		sport->dummy_tx_desc->next_desc_addr =
+				(unsigned long)(sport->dma_tx_desc);
+		local_irq_restore(flags);
+		sport->curr_tx_desc = sport->dma_tx_desc;
+	} else {
+
+		sport_tx_dma_start(sport, 0);
+		/* Let rx dma run the dummy buffer */
+		sport_rx_dma_start(sport, 1);
+		sport_start(sport);
+	}
+	sport->tx_run = 1;
+	return 0;
+}
+EXPORT_SYMBOL(sport_tx_start);
+
+int sport_tx_stop(struct sport_device *sport)
+{
+	if (!sport->tx_run)
+		return 0;
+	if (sport->rx_run) {
+		/* RX is still running, hook the dummy buffer */
+		sport_hook_tx_dummy(sport);
+	} else {
+		/* Both rx and tx dma stopped */
+		sport_stop(sport);
+		sport->curr_rx_desc = NULL;
+		sport->curr_tx_desc = NULL;
+	}
+
+	sport->tx_run = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_tx_stop);
+
+static inline int compute_wdsize(size_t wdsize)
+{
+	switch (wdsize) {
+	case 1:
+		return WDSIZE_8;
+	case 2:
+		return WDSIZE_16;
+	case 4:
+	default:
+		return WDSIZE_32;
+	}
+}
+
+int sport_config_rx_dma(struct sport_device *sport, void *buf,
+		int fragcount, size_t fragsize)
+{
+	unsigned int x_count;
+	unsigned int y_count;
+	unsigned int cfg;
+	dma_addr_t addr;
+
+	pr_debug("%s buf:%p, frag:%d, fragsize:0x%lx\n", __func__, \
+			buf, fragcount, fragsize);
+
+	x_count = fragsize / sport->wdsize;
+	y_count = 0;
+
+	/* for fragments larger than 64k words we use 2d dma,
+	 * denote fragecount as two numbers' mutliply and both of them
+	 * are less than 64k.*/
+	if (x_count >= 0x10000) {
+		int i, count = x_count;
+
+		for (i = 16; i > 0; i--) {
+			x_count = 1 << i;
+			if ((count & (x_count - 1)) == 0) {
+				y_count = count >> i;
+				if (y_count < 0x10000)
+					break;
+			}
+		}
+		if (i == 0)
+			return -EINVAL;
+	}
+	pr_debug("%s(x_count:0x%x, y_count:0x%x)\n", __func__,
+			x_count, y_count);
+
+	if (sport->dma_rx_desc)
+		dma_free_coherent(NULL, sport->rx_desc_bytes,
+					sport->dma_rx_desc, 0);
+
+	/* Allocate a new descritor ring as current one. */
+	sport->dma_rx_desc = dma_alloc_coherent(NULL, \
+			fragcount * sizeof(struct dmasg), &addr, 0);
+	sport->rx_desc_bytes = fragcount * sizeof(struct dmasg);
+
+	if (!sport->dma_rx_desc) {
+		pr_err("Failed to allocate memory for rx desc\n");
+		return -ENOMEM;
+	}
+
+	sport->rx_buf = buf;
+	sport->rx_fragsize = fragsize;
+	sport->rx_frags = fragcount;
+
+	cfg     = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | WNR | \
+		  (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
+
+	if (y_count != 0)
+		cfg |= DMA2D;
+
+	setup_desc(sport->dma_rx_desc, buf, fragcount, fragsize,
+			cfg|DMAEN, x_count, y_count, sport->wdsize);
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_config_rx_dma);
+
+int sport_config_tx_dma(struct sport_device *sport, void *buf, \
+		int fragcount, size_t fragsize)
+{
+	unsigned int x_count;
+	unsigned int y_count;
+	unsigned int cfg;
+	dma_addr_t addr;
+
+	pr_debug("%s buf:%p, fragcount:%d, fragsize:0x%lx\n",
+			__func__, buf, fragcount, fragsize);
+
+	x_count = fragsize/sport->wdsize;
+	y_count = 0;
+
+	/* for fragments larger than 64k words we use 2d dma,
+	 * denote fragecount as two numbers' mutliply and both of them
+	 * are less than 64k.*/
+	if (x_count >= 0x10000) {
+		int i, count = x_count;
+
+		for (i = 16; i > 0; i--) {
+			x_count = 1 << i;
+			if ((count & (x_count - 1)) == 0) {
+				y_count = count >> i;
+				if (y_count < 0x10000)
+					break;
+			}
+		}
+		if (i == 0)
+			return -EINVAL;
+	}
+	pr_debug("%s x_count:0x%x, y_count:0x%x\n", __func__,
+			x_count, y_count);
+
+
+	if (sport->dma_tx_desc) {
+		dma_free_coherent(NULL, sport->tx_desc_bytes, \
+				sport->dma_tx_desc, 0);
+	}
+
+	sport->dma_tx_desc = dma_alloc_coherent(NULL, \
+			fragcount * sizeof(struct dmasg), &addr, 0);
+	sport->tx_desc_bytes = fragcount * sizeof(struct dmasg);
+	if (!sport->dma_tx_desc) {
+		pr_err("Failed to allocate memory for tx desc\n");
+		return -ENOMEM;
+	}
+
+	sport->tx_buf = buf;
+	sport->tx_fragsize = fragsize;
+	sport->tx_frags = fragcount;
+	cfg     = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | \
+		  (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
+
+	if (y_count != 0)
+		cfg |= DMA2D;
+
+	setup_desc(sport->dma_tx_desc, buf, fragcount, fragsize,
+			cfg|DMAEN, x_count, y_count, sport->wdsize);
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_config_tx_dma);
+
+/* setup dummy dma descriptor ring, which don't generate interrupts,
+ * the x_modify is set to 0 */
+static int sport_config_rx_dummy(struct sport_device *sport)
+{
+	struct dmasg *desc;
+	unsigned config;
+
+	pr_debug("%s entered\n", __func__);
+#if L1_DATA_A_LENGTH != 0
+	desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
+#else
+	{
+		dma_addr_t addr;
+		desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
+	}
+#endif
+	if (desc == NULL) {
+		pr_err("Failed to allocate memory for dummy rx desc\n");
+		return -ENOMEM;
+	}
+	memset(desc, 0, 2 * sizeof(*desc));
+	sport->dummy_rx_desc = desc;
+	desc->start_addr = (unsigned long)sport->dummy_buf;
+	config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize)
+		 | WNR | DMAEN;
+	desc->cfg = config;
+	desc->x_count = sport->dummy_count/sport->wdsize;
+	desc->x_modify = sport->wdsize;
+	desc->y_count = 0;
+	desc->y_modify = 0;
+	memcpy(desc+1, desc, sizeof(*desc));
+	desc->next_desc_addr = (unsigned long)(desc+1);
+	desc[1].next_desc_addr = (unsigned long)desc;
+	return 0;
+}
+
+static int sport_config_tx_dummy(struct sport_device *sport)
+{
+	struct dmasg *desc;
+	unsigned int config;
+
+	pr_debug("%s entered\n", __func__);
+
+#if L1_DATA_A_LENGTH != 0
+	desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
+#else
+	{
+		dma_addr_t addr;
+		desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
+	}
+#endif
+	if (!desc) {
+		pr_err("Failed to allocate memory for dummy tx desc\n");
+		return -ENOMEM;
+	}
+	memset(desc, 0, 2 * sizeof(*desc));
+	sport->dummy_tx_desc = desc;
+	desc->start_addr = (unsigned long)sport->dummy_buf + \
+		sport->dummy_count;
+	config = DMAFLOW_LARGE | NDSIZE_9 |
+		 compute_wdsize(sport->wdsize) | DMAEN;
+	desc->cfg = config;
+	desc->x_count = sport->dummy_count/sport->wdsize;
+	desc->x_modify = sport->wdsize;
+	desc->y_count = 0;
+	desc->y_modify = 0;
+	memcpy(desc+1, desc, sizeof(*desc));
+	desc->next_desc_addr = (unsigned long)(desc+1);
+	desc[1].next_desc_addr = (unsigned long)desc;
+	return 0;
+}
+
+unsigned long sport_curr_offset_rx(struct sport_device *sport)
+{
+	unsigned long curr = get_dma_curr_addr(sport->dma_rx_chan);
+
+	return (unsigned char *)curr - sport->rx_buf;
+}
+EXPORT_SYMBOL(sport_curr_offset_rx);
+
+unsigned long sport_curr_offset_tx(struct sport_device *sport)
+{
+	unsigned long curr = get_dma_curr_addr(sport->dma_tx_chan);
+
+	return (unsigned char *)curr - sport->tx_buf;
+}
+EXPORT_SYMBOL(sport_curr_offset_tx);
+
+void sport_incfrag(struct sport_device *sport, int *frag, int tx)
+{
+	++(*frag);
+	if (tx == 1 && *frag == sport->tx_frags)
+		*frag = 0;
+
+	if (tx == 0 && *frag == sport->rx_frags)
+		*frag = 0;
+}
+EXPORT_SYMBOL(sport_incfrag);
+
+void sport_decfrag(struct sport_device *sport, int *frag, int tx)
+{
+	--(*frag);
+	if (tx == 1 && *frag == 0)
+		*frag = sport->tx_frags;
+
+	if (tx == 0 && *frag == 0)
+		*frag = sport->rx_frags;
+}
+EXPORT_SYMBOL(sport_decfrag);
+
+static int sport_check_status(struct sport_device *sport,
+		unsigned int *sport_stat,
+		unsigned int *rx_stat,
+		unsigned int *tx_stat)
+{
+	int status = 0;
+
+	if (sport_stat) {
+		SSYNC();
+		status = sport->regs->stat;
+		if (status & (TOVF|TUVF|ROVF|RUVF))
+			sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
+		SSYNC();
+		*sport_stat = status;
+	}
+
+	if (rx_stat) {
+		SSYNC();
+		status = get_dma_curr_irqstat(sport->dma_rx_chan);
+		if (status & (DMA_DONE|DMA_ERR))
+			clear_dma_irqstat(sport->dma_rx_chan);
+		SSYNC();
+		*rx_stat = status;
+	}
+
+	if (tx_stat) {
+		SSYNC();
+		status = get_dma_curr_irqstat(sport->dma_tx_chan);
+		if (status & (DMA_DONE|DMA_ERR))
+			clear_dma_irqstat(sport->dma_tx_chan);
+		SSYNC();
+		*tx_stat = status;
+	}
+
+	return 0;
+}
+
+int  sport_dump_stat(struct sport_device *sport, char *buf, size_t len)
+{
+	int ret;
+
+	ret = snprintf(buf, len,
+			"sts: 0x%04x\n"
+			"rx dma %d sts: 0x%04x tx dma %d sts: 0x%04x\n",
+			sport->regs->stat,
+			sport->dma_rx_chan,
+			get_dma_curr_irqstat(sport->dma_rx_chan),
+			sport->dma_tx_chan,
+			get_dma_curr_irqstat(sport->dma_tx_chan));
+	buf += ret;
+	len -= ret;
+
+	ret += snprintf(buf, len,
+			"curr_rx_desc:0x%p, curr_tx_desc:0x%p\n"
+			"dma_rx_desc:0x%p, dma_tx_desc:0x%p\n"
+			"dummy_rx_desc:0x%p, dummy_tx_desc:0x%p\n",
+			sport->curr_rx_desc, sport->curr_tx_desc,
+			sport->dma_rx_desc, sport->dma_tx_desc,
+			sport->dummy_rx_desc, sport->dummy_tx_desc);
+
+	return ret;
+}
+
+static irqreturn_t rx_handler(int irq, void *dev_id)
+{
+	unsigned int rx_stat;
+	struct sport_device *sport = dev_id;
+
+	pr_debug("%s enter\n", __func__);
+	sport_check_status(sport, NULL, &rx_stat, NULL);
+	if (!(rx_stat & DMA_DONE))
+		pr_err("rx dma is already stopped\n");
+
+	if (sport->rx_callback) {
+		sport->rx_callback(sport->rx_data);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static irqreturn_t tx_handler(int irq, void *dev_id)
+{
+	unsigned int tx_stat;
+	struct sport_device *sport = dev_id;
+	pr_debug("%s enter\n", __func__);
+	sport_check_status(sport, NULL, NULL, &tx_stat);
+	if (!(tx_stat & DMA_DONE)) {
+		pr_err("tx dma is already stopped\n");
+		return IRQ_HANDLED;
+	}
+	if (sport->tx_callback) {
+		sport->tx_callback(sport->tx_data);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static irqreturn_t err_handler(int irq, void *dev_id)
+{
+	unsigned int status = 0;
+	struct sport_device *sport = dev_id;
+
+	pr_debug("%s\n", __func__);
+	if (sport_check_status(sport, &status, NULL, NULL)) {
+		pr_err("error checking status ??");
+		return IRQ_NONE;
+	}
+
+	if (status & (TOVF|TUVF|ROVF|RUVF)) {
+		pr_info("sport status error:%s%s%s%s\n",
+				status & TOVF ? " TOVF" : "",
+				status & TUVF ? " TUVF" : "",
+				status & ROVF ? " ROVF" : "",
+				status & RUVF ? " RUVF" : "");
+		if (status & TOVF || status & TUVF) {
+			disable_dma(sport->dma_tx_chan);
+			if (sport->tx_run)
+				sport_tx_dma_start(sport, 0);
+			else
+				sport_tx_dma_start(sport, 1);
+			enable_dma(sport->dma_tx_chan);
+		} else {
+			disable_dma(sport->dma_rx_chan);
+			if (sport->rx_run)
+				sport_rx_dma_start(sport, 0);
+			else
+				sport_rx_dma_start(sport, 1);
+			enable_dma(sport->dma_rx_chan);
+		}
+	}
+	status = sport->regs->stat;
+	if (status & (TOVF|TUVF|ROVF|RUVF))
+		sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
+	SSYNC();
+
+	if (sport->err_callback)
+		sport->err_callback(sport->err_data);
+
+	return IRQ_HANDLED;
+}
+
+int sport_set_rx_callback(struct sport_device *sport,
+		       void (*rx_callback)(void *), void *rx_data)
+{
+	BUG_ON(rx_callback == NULL);
+	sport->rx_callback = rx_callback;
+	sport->rx_data = rx_data;
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_set_rx_callback);
+
+int sport_set_tx_callback(struct sport_device *sport,
+		void (*tx_callback)(void *), void *tx_data)
+{
+	BUG_ON(tx_callback == NULL);
+	sport->tx_callback = tx_callback;
+	sport->tx_data = tx_data;
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_set_tx_callback);
+
+int sport_set_err_callback(struct sport_device *sport,
+		void (*err_callback)(void *), void *err_data)
+{
+	BUG_ON(err_callback == NULL);
+	sport->err_callback = err_callback;
+	sport->err_data = err_data;
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_set_err_callback);
+
+struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
+		unsigned dummy_count, void *private_data)
+{
+	int ret;
+	struct sport_device *sport;
+	pr_debug("%s enter\n", __func__);
+	BUG_ON(param == NULL);
+	BUG_ON(wdsize == 0 || dummy_count == 0);
+	sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
+	if (!sport) {
+		pr_err("Failed to allocate for sport device\n");
+		return NULL;
+	}
+
+	memset(sport, 0, sizeof(struct sport_device));
+	sport->dma_rx_chan = param->dma_rx_chan;
+	sport->dma_tx_chan = param->dma_tx_chan;
+	sport->err_irq = param->err_irq;
+	sport->regs = param->regs;
+	sport->private_data = private_data;
+
+	if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
+		pr_err("Failed to request RX dma %d\n", \
+				sport->dma_rx_chan);
+		goto __init_err1;
+	}
+	if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
+		pr_err("Failed to request RX irq %d\n", \
+				sport->dma_rx_chan);
+		goto __init_err2;
+	}
+
+	if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
+		pr_err("Failed to request TX dma %d\n", \
+				sport->dma_tx_chan);
+		goto __init_err2;
+	}
+
+	if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
+		pr_err("Failed to request TX irq %d\n", \
+				sport->dma_tx_chan);
+		goto __init_err3;
+	}
+
+	if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
+			sport) < 0) {
+		pr_err("Failed to request err irq:%d\n", \
+				sport->err_irq);
+		goto __init_err3;
+	}
+
+	pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
+			sport->dma_rx_chan, sport->dma_tx_chan,
+			sport->err_irq, sport->regs);
+
+	sport->wdsize = wdsize;
+	sport->dummy_count = dummy_count;
+
+#if L1_DATA_A_LENGTH != 0
+	sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2);
+#else
+	sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL);
+#endif
+	if (sport->dummy_buf == NULL) {
+		pr_err("Failed to allocate dummy buffer\n");
+		goto __error;
+	}
+
+	memset(sport->dummy_buf, 0, dummy_count * 2);
+	ret = sport_config_rx_dummy(sport);
+	if (ret) {
+		pr_err("Failed to config rx dummy ring\n");
+		goto __error;
+	}
+	ret = sport_config_tx_dummy(sport);
+	if (ret) {
+		pr_err("Failed to config tx dummy ring\n");
+		goto __error;
+	}
+
+	return sport;
+__error:
+	free_irq(sport->err_irq, sport);
+__init_err3:
+	free_dma(sport->dma_tx_chan);
+__init_err2:
+	free_dma(sport->dma_rx_chan);
+__init_err1:
+	kfree(sport);
+	return NULL;
+}
+EXPORT_SYMBOL(sport_init);
+
+void sport_done(struct sport_device *sport)
+{
+	if (sport == NULL)
+		return;
+
+	sport_stop(sport);
+	if (sport->dma_rx_desc)
+		dma_free_coherent(NULL, sport->rx_desc_bytes,
+			sport->dma_rx_desc, 0);
+	if (sport->dma_tx_desc)
+		dma_free_coherent(NULL, sport->tx_desc_bytes,
+			sport->dma_tx_desc, 0);
+
+#if L1_DATA_A_LENGTH != 0
+	l1_data_sram_free(sport->dummy_rx_desc);
+	l1_data_sram_free(sport->dummy_tx_desc);
+	l1_data_sram_free(sport->dummy_buf);
+#else
+	dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+		sport->dummy_rx_desc, 0);
+	dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+		sport->dummy_tx_desc, 0);
+	kfree(sport->dummy_buf);
+#endif
+	free_dma(sport->dma_rx_chan);
+	free_dma(sport->dma_tx_chan);
+	free_irq(sport->err_irq, sport);
+
+	kfree(sport);
+		sport = NULL;
+}
+EXPORT_SYMBOL(sport_done);
+/*
+* It is only used to send several bytes when dma is not enabled
+ * sport controller is configured but not enabled.
+ * Multichannel cannot works with pio mode */
+/* Used by ac97 to write and read codec register */
+int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
+		u8 *in_data, int len)
+{
+	unsigned short dma_config;
+	unsigned short status;
+	unsigned long flags;
+	unsigned long wait = 0;
+
+	pr_debug("%s enter, out_data:%p, in_data:%p len:%d\n", \
+			__func__, out_data, in_data, len);
+	pr_debug("tcr1:0x%04x, tcr2:0x%04x, tclkdiv:0x%04x, tfsdiv:0x%04x\n"
+			"mcmc1:0x%04x, mcmc2:0x%04x\n",
+			sport->regs->tcr1, sport->regs->tcr2,
+			sport->regs->tclkdiv, sport->regs->tfsdiv,
+			sport->regs->mcmc1, sport->regs->mcmc2);
+	flush_dcache_range((unsigned)out_data, (unsigned)(out_data + len));
+
+	/* Enable tx dma */
+	dma_config = (RESTART | WDSIZE_16 | DI_EN);
+	set_dma_start_addr(sport->dma_tx_chan, (unsigned long)out_data);
+	set_dma_x_count(sport->dma_tx_chan, len/2);
+	set_dma_x_modify(sport->dma_tx_chan, 2);
+	set_dma_config(sport->dma_tx_chan, dma_config);
+	enable_dma(sport->dma_tx_chan);
+
+	if (in_data != NULL) {
+		invalidate_dcache_range((unsigned)in_data, \
+				(unsigned)(in_data + len));
+		/* Enable rx dma */
+		dma_config = (RESTART | WDSIZE_16 | WNR | DI_EN);
+		set_dma_start_addr(sport->dma_rx_chan, (unsigned long)in_data);
+		set_dma_x_count(sport->dma_rx_chan, len/2);
+		set_dma_x_modify(sport->dma_rx_chan, 2);
+		set_dma_config(sport->dma_rx_chan, dma_config);
+		enable_dma(sport->dma_rx_chan);
+	}
+
+	local_irq_save(flags);
+	sport->regs->tcr1 |= TSPEN;
+	sport->regs->rcr1 |= RSPEN;
+	SSYNC();
+
+	status = get_dma_curr_irqstat(sport->dma_tx_chan);
+	while (status & DMA_RUN) {
+		udelay(1);
+		status = get_dma_curr_irqstat(sport->dma_tx_chan);
+		pr_debug("DMA status:0x%04x\n", status);
+		if (wait++ > 100)
+			goto __over;
+	}
+	status = sport->regs->stat;
+	wait = 0;
+
+	while (!(status & TXHRE)) {
+		pr_debug("sport status:0x%04x\n", status);
+		udelay(1);
+		status = *(unsigned short *)&sport->regs->stat;
+		if (wait++ > 1000)
+			goto __over;
+	}
+	/* Wait for the last byte sent out */
+	udelay(20);
+	pr_debug("sport status:0x%04x\n", status);
+
+__over:
+	sport->regs->tcr1 &= ~TSPEN;
+	sport->regs->rcr1 &= ~RSPEN;
+	SSYNC();
+	disable_dma(sport->dma_tx_chan);
+	/* Clear the status */
+	clear_dma_irqstat(sport->dma_tx_chan);
+	if (in_data != NULL) {
+		disable_dma(sport->dma_rx_chan);
+		clear_dma_irqstat(sport->dma_rx_chan);
+	}
+	SSYNC();
+	local_irq_restore(flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_send_and_recv);
+
+MODULE_AUTHOR("Roy Huang");
+MODULE_DESCRIPTION("SPORT driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
new file mode 100644
index 0000000..4c16345
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -0,0 +1,192 @@
+/*
+ * File:         bf5xx_ac97_sport.h
+ * Based on:
+ * Author:       Roy Huang <roy.huang@analog.com>
+ *
+ * Created:
+ * Description:
+ *
+ *               Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef __BF5XX_SPORT_H__
+#define __BF5XX_SPORT_H__
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <asm/dma.h>
+
+struct sport_register {
+	u16 tcr1;	u16 reserved0;
+	u16 tcr2;	u16 reserved1;
+	u16 tclkdiv;	u16 reserved2;
+	u16 tfsdiv;	u16 reserved3;
+	u32 tx;
+	u32 reserved_l0;
+	u32 rx;
+	u32 reserved_l1;
+	u16 rcr1;	u16 reserved4;
+	u16 rcr2;	u16 reserved5;
+	u16 rclkdiv;	u16 reserved6;
+	u16 rfsdiv;	u16 reserved7;
+	u16 stat;	u16 reserved8;
+	u16 chnl;	u16 reserved9;
+	u16 mcmc1;	u16 reserved10;
+	u16 mcmc2;	u16 reserved11;
+	u32 mtcs0;
+	u32 mtcs1;
+	u32 mtcs2;
+	u32 mtcs3;
+	u32 mrcs0;
+	u32 mrcs1;
+	u32 mrcs2;
+	u32 mrcs3;
+};
+
+#define DESC_ELEMENT_COUNT 9
+
+struct sport_device {
+	int dma_rx_chan;
+	int dma_tx_chan;
+	int err_irq;
+	struct sport_register *regs;
+
+	unsigned char *rx_buf;
+	unsigned char *tx_buf;
+	unsigned int rx_fragsize;
+	unsigned int tx_fragsize;
+	unsigned int rx_frags;
+	unsigned int tx_frags;
+	unsigned int wdsize;
+
+	/* for dummy dma transfer */
+	void *dummy_buf;
+	unsigned int dummy_count;
+
+	/* DMA descriptor ring head of current audio stream*/
+	struct dmasg *dma_rx_desc;
+	struct dmasg *dma_tx_desc;
+	unsigned int rx_desc_bytes;
+	unsigned int tx_desc_bytes;
+
+	unsigned int rx_run:1; /* rx is running */
+	unsigned int tx_run:1; /* tx is running */
+
+	struct dmasg *dummy_rx_desc;
+	struct dmasg *dummy_tx_desc;
+
+	struct dmasg *curr_rx_desc;
+	struct dmasg *curr_tx_desc;
+
+	int rx_curr_frag;
+	int tx_curr_frag;
+
+	unsigned int rcr1;
+	unsigned int rcr2;
+	int rx_tdm_count;
+
+	unsigned int tcr1;
+	unsigned int tcr2;
+	int tx_tdm_count;
+
+	void (*rx_callback)(void *data);
+	void *rx_data;
+	void (*tx_callback)(void *data);
+	void *tx_data;
+	void (*err_callback)(void *data);
+	void *err_data;
+	unsigned char *tx_dma_buf;
+	unsigned char *rx_dma_buf;
+#ifdef CONFIG_SND_MMAP_SUPPORT
+	dma_addr_t tx_dma_phy;
+	dma_addr_t rx_dma_phy;
+	int tx_pos;/*pcm sample count*/
+	int rx_pos;
+	unsigned int tx_buffer_size;
+	unsigned int rx_buffer_size;
+#endif
+	void *private_data;
+};
+
+extern struct sport_device *sport_handle;
+
+struct sport_param {
+	int dma_rx_chan;
+	int dma_tx_chan;
+	int err_irq;
+	struct sport_register *regs;
+};
+
+struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
+		unsigned dummy_count, void *private_data);
+
+void sport_done(struct sport_device *sport);
+
+/* first use these ...*/
+
+/* note: multichannel is in units of 8 channels, tdm_count is number of channels
+ *  NOT / 8 ! all channels are enabled by default */
+int sport_set_multichannel(struct sport_device *sport, int tdm_count,
+		u32 mask, int packed);
+
+int sport_config_rx(struct sport_device *sport,
+		unsigned int rcr1, unsigned int rcr2,
+		unsigned int clkdiv, unsigned int fsdiv);
+
+int sport_config_tx(struct sport_device *sport,
+		unsigned int tcr1, unsigned int tcr2,
+		unsigned int clkdiv, unsigned int fsdiv);
+
+/* ... then these: */
+
+/* buffer size (in bytes) == fragcount * fragsize_bytes */
+
+/* this is not a very general api, it sets the dma to 2d autobuffer mode */
+
+int sport_config_rx_dma(struct sport_device *sport, void *buf,
+		int fragcount, size_t fragsize_bytes);
+
+int sport_config_tx_dma(struct sport_device *sport, void *buf,
+		int fragcount, size_t fragsize_bytes);
+
+int sport_tx_start(struct sport_device *sport);
+int sport_tx_stop(struct sport_device *sport);
+int sport_rx_start(struct sport_device *sport);
+int sport_rx_stop(struct sport_device *sport);
+
+/* for use in interrupt handler */
+unsigned long sport_curr_offset_rx(struct sport_device *sport);
+unsigned long sport_curr_offset_tx(struct sport_device *sport);
+
+void sport_incfrag(struct sport_device *sport, int *frag, int tx);
+void sport_decfrag(struct sport_device *sport, int *frag, int tx);
+
+int sport_set_rx_callback(struct sport_device *sport,
+		       void (*rx_callback)(void *), void *rx_data);
+int sport_set_tx_callback(struct sport_device *sport,
+		       void (*tx_callback)(void *), void *tx_data);
+int sport_set_err_callback(struct sport_device *sport,
+		       void (*err_callback)(void *), void *err_data);
+
+int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
+		u8 *in_data, int len);
+#endif /* BF53X_SPORT_H */
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
new file mode 100644
index 0000000..e15f67f
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -0,0 +1,186 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-ssm2602.c
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  board driver for SSM2602 sound chip
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <linux/gpio.h>
+#include "../codecs/ssm2602.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+static struct snd_soc_machine bf5xx_ssm2602;
+
+static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	pr_debug("%s enter\n", __func__);
+	cpu_dai->private_data = sport_handle;
+	return 0;
+}
+
+static int bf5xx_ssm2602_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_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int clk = 0;
+	int ret = 0;
+
+	pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
+		params_format(params));
+	/*
+	 * If you are using a crystal source which frequency is not 12MHz
+	 * then modify the below case statement with frequency of the crystal.
+	 *
+	 * If you are using the SPORT to generate clocking then this is
+	 * where to do it.
+	 */
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 48000:
+	case 96000:
+	case 11025:
+	case 22050:
+	case 44100:
+		clk = 12000000;
+		break;
+	}
+
+	/*
+	 * CODEC is master for BCLK and LRC in this configuration.
+	 */
+
+	/* 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;
+
+	ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_ssm2602_ops = {
+	.startup = bf5xx_ssm2602_startup,
+	.hw_params = bf5xx_ssm2602_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
+	.name = "ssm2602",
+	.stream_name = "SSM2602",
+	.cpu_dai = &bf5xx_i2s_dai,
+	.codec_dai = &ssm2602_dai,
+	.ops = &bf5xx_ssm2602_ops,
+};
+
+/*
+ * SSM2602 2 wire address is determined by CSB
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+
+static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
+	.i2c_bus = 0,
+	.i2c_address = 0x1b,
+};
+
+static struct snd_soc_machine bf5xx_ssm2602 = {
+	.name = "bf5xx_ssm2602",
+	.dai_link = &bf5xx_ssm2602_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
+	.machine = &bf5xx_ssm2602,
+	.platform = &bf5xx_i2s_soc_platform,
+	.codec_dev = &soc_codec_dev_ssm2602,
+	.codec_data = &bf5xx_ssm2602_setup,
+};
+
+static struct platform_device *bf52x_ssm2602_snd_device;
+
+static int __init bf5xx_ssm2602_init(void)
+{
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf52x_ssm2602_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bf52x_ssm2602_snd_device,
+				&bf5xx_ssm2602_snd_devdata);
+	bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
+	ret = platform_device_add(bf52x_ssm2602_snd_device);
+
+	if (ret)
+		platform_device_put(bf52x_ssm2602_snd_device);
+
+	return ret;
+}
+
+static void __exit bf5xx_ssm2602_exit(void)
+{
+	pr_debug("%s enter\n", __func__);
+	platform_device_unregister(bf52x_ssm2602_snd_device);
+}
+
+module_init(bf5xx_ssm2602_init);
+module_exit(bf5xx_ssm2602_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC SSM2602 BF527-EZKIT");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 1db04a2..e0b9869 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,34 +1,42 @@
+config SND_SOC_ALL_CODECS
+	tristate "Build all ASoC CODEC drivers"
+	depends on I2C
+	select SPI
+	select SPI_MASTER
+	select SND_SOC_AK4535
+	select SND_SOC_CS4270
+	select SND_SOC_SSM2602
+	select SND_SOC_TLV320AIC26
+	select SND_SOC_TLV320AIC3X
+	select SND_SOC_UDA1380
+	select SND_SOC_WM8510
+	select SND_SOC_WM8580
+	select SND_SOC_WM8731
+	select SND_SOC_WM8750
+	select SND_SOC_WM8753
+	select SND_SOC_WM8900
+	select SND_SOC_WM8903
+	select SND_SOC_WM8971
+	select SND_SOC_WM8990
+        help
+          Normally ASoC codec drivers are only built if a machine driver which
+          uses them is also built since they are only usable with a machine
+          driver.  Selecting this option will allow these drivers to be built
+          without an explicit machine driver for test and development purposes.
+
+          If unsure select "N".
+
+
 config SND_SOC_AC97_CODEC
 	tristate
 	select SND_AC97_CODEC
 
+config SND_SOC_AD1980
+	tristate
+
 config SND_SOC_AK4535
 	tristate
 
-config SND_SOC_UDA1380
-        tristate
-
-config SND_SOC_WM8510
-	tristate
-
-config SND_SOC_WM8731
-	tristate
-
-config SND_SOC_WM8750
-	tristate
-
-config SND_SOC_WM8753
-	tristate
-
-config SND_SOC_WM8990
-	tristate
-
-config SND_SOC_WM9712
-	tristate
-
-config SND_SOC_WM9713
-	tristate
-
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
 	tristate
@@ -47,6 +55,49 @@
 	bool
 	depends on SND_SOC_CS4270
 
+config SND_SOC_SSM2602
+	tristate
+
+config SND_SOC_TLV320AIC26
+	tristate "TI TLV320AIC26 Codec support"
+	depends on SND_SOC && SPI
+
 config SND_SOC_TLV320AIC3X
 	tristate
 	depends on I2C
+
+config SND_SOC_UDA1380
+        tristate
+
+config SND_SOC_WM8510
+	tristate
+
+config SND_SOC_WM8580
+	tristate
+
+config SND_SOC_WM8731
+	tristate
+
+config SND_SOC_WM8750
+	tristate
+
+config SND_SOC_WM8753
+	tristate
+
+config SND_SOC_WM8900
+	tristate
+
+config SND_SOC_WM8903
+	tristate
+
+config SND_SOC_WM8971
+	tristate
+
+config SND_SOC_WM8990
+	tristate
+
+config SND_SOC_WM9712
+	tristate
+
+config SND_SOC_WM9713
+	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d7b97ab..f977978 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,25 +1,39 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-ad1980-objs := ad1980.o
 snd-soc-ak4535-objs := ak4535.o
+snd-soc-cs4270-objs := cs4270.o
+snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-tlv320aic26-objs := tlv320aic26.o
+snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 snd-soc-uda1380-objs := uda1380.o
 snd-soc-wm8510-objs := wm8510.o
+snd-soc-wm8580-objs := wm8580.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8900-objs := wm8900.o
+snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8971-objs := wm8971.o
 snd-soc-wm8990-objs := wm8990.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
-snd-soc-cs4270-objs := cs4270.o
-snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AD1980)	+= snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
+obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
+obj-$(CONFIG_SND_SOC_WM8580)	+= snd-soc-wm8580.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8900)	+= snd-soc-wm8900.o
+obj-$(CONFIG_SND_SOC_WM8903)	+= snd-soc-wm8903.o
+obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o
 obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o
 obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o
-obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
-obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
new file mode 100644
index 0000000..4e09c1f
--- /dev/null
+++ b/sound/soc/codecs/ad1980.c
@@ -0,0 +1,309 @@
+/*
+ * ad1980.c  --  ALSA Soc AD1980 codec support
+ *
+ * Copyright:	Analog Device Inc.
+ * Author:	Roy Huang <roy.huang@analog.com>
+ * 		Cliff Cai <cliff.cai@analog.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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "ad1980.h"
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+	unsigned int reg);
+static int ac97_write(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int val);
+
+/*
+ * AD1980 register cache
+ */
+static const u16 ad1980_reg[] = {
+	0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6  */
+	0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e  */
+	0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
+	0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
+	0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
+	0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
+	0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
+	0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
+	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+	0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
+	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
+	0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
+	0x0000, 0x0000, 0x4144, 0x5370  /* 78 - 7e */
+};
+
+static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
+		"Stereo Mix", "Mono Mix", "Phone"};
+
+static const struct soc_enum ad1980_cap_src =
+	SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 7, ad1980_rec_sel);
+
+static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = {
+SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
+
+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+
+SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
+SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
+
+SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0),
+SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+
+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1),
+SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1),
+
+SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1),
+SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
+
+SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0),
+SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
+
+SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
+
+SOC_ENUM("Capture Source", ad1980_cap_src),
+
+SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
+};
+
+/* add non dapm controls */
+static int ad1980_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) {
+		err = snd_ctl_add(codec->card, snd_soc_cnew(
+				&ad1980_snd_ac97_controls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static unsigned int ac97_read(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+
+	switch (reg) {
+	case AC97_RESET:
+	case AC97_INT_PAGING:
+	case AC97_POWERDOWN:
+	case AC97_EXTENDED_STATUS:
+	case AC97_VENDOR_ID1:
+	case AC97_VENDOR_ID2:
+		return soc_ac97_ops.read(codec->ac97, reg);
+	default:
+		reg = reg >> 1;
+
+		if (reg >= (ARRAY_SIZE(ad1980_reg)))
+			return -EINVAL;
+
+		return cache[reg];
+	}
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int val)
+{
+	u16 *cache = codec->reg_cache;
+
+	soc_ac97_ops.write(codec->ac97, reg, val);
+	reg = reg >> 1;
+	if (reg < (ARRAY_SIZE(ad1980_reg)))
+		cache[reg] = val;
+
+	return 0;
+}
+
+struct snd_soc_dai ad1980_dai = {
+	.name = "AC97",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+EXPORT_SYMBOL_GPL(ad1980_dai);
+
+static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+{
+	u16 retry_cnt = 0;
+
+retry:
+	if (try_warm && soc_ac97_ops.warm_reset) {
+		soc_ac97_ops.warm_reset(codec->ac97);
+		if (ac97_read(codec, AC97_RESET) == 0x0090)
+			return 1;
+	}
+
+	soc_ac97_ops.reset(codec->ac97);
+	/* Set bit 16slot in register 74h, then every slot will has only 16
+	 * bits. This command is sent out in 20bit mode, in which case the
+	 * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
+	ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+
+	if (ac97_read(codec, AC97_RESET)  != 0x0090)
+		goto err;
+	return 0;
+
+err:
+	while (retry_cnt++ < 10)
+		goto retry;
+
+	printk(KERN_ERR "AD1980 AC97 reset failed\n");
+	return -EIO;
+}
+
+static int ad1980_soc_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+	u16 vendor_id2;
+
+	printk(KERN_INFO "AD1980 SoC Audio Codec\n");
+
+	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (socdev->codec == NULL)
+		return -ENOMEM;
+	codec = socdev->codec;
+	mutex_init(&codec->mutex);
+
+	codec->reg_cache =
+		kzalloc(sizeof(u16) * ARRAY_SIZE(ad1980_reg), GFP_KERNEL);
+	if (codec->reg_cache == NULL) {
+		ret = -ENOMEM;
+		goto cache_err;
+	}
+	memcpy(codec->reg_cache, ad1980_reg, sizeof(u16) * \
+			ARRAY_SIZE(ad1980_reg));
+	codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ad1980_reg);
+	codec->reg_cache_step = 2;
+	codec->name = "AD1980";
+	codec->owner = THIS_MODULE;
+	codec->dai = &ad1980_dai;
+	codec->num_dai = 1;
+	codec->write = ac97_write;
+	codec->read = ac97_read;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	if (ret < 0) {
+		printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+		goto codec_err;
+	}
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0)
+		goto pcm_err;
+
+
+	ret = ad1980_reset(codec, 0);
+	if (ret < 0) {
+		printk(KERN_ERR "AC97 link error\n");
+		goto reset_err;
+	}
+
+	/* Read out vendor ID to make sure it is ad1980 */
+	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
+		goto reset_err;
+
+	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+
+	if (vendor_id2 != 0x5370) {
+		if (vendor_id2 != 0x5374)
+			goto reset_err;
+		else
+			printk(KERN_WARNING "ad1980: "
+				"Found AD1981 - only 2/2 IN/OUT Channels "
+				"supported\n");
+	}
+
+	ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */
+	ac97_write(codec, AC97_PCM, 0x0000);	/* unmute PCM out volume */
+	ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */
+
+	ad1980_add_controls(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "ad1980: failed to register card\n");
+		goto reset_err;
+	}
+
+	return 0;
+
+reset_err:
+	snd_soc_free_pcms(socdev);
+
+pcm_err:
+	snd_soc_free_ac97_codec(codec);
+
+codec_err:
+	kfree(codec->reg_cache);
+
+cache_err:
+	kfree(socdev->codec);
+	socdev->codec = NULL;
+	return ret;
+}
+
+static int ad1980_soc_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec == NULL)
+		return 0;
+
+	snd_soc_dapm_free(socdev);
+	snd_soc_free_pcms(socdev);
+	snd_soc_free_ac97_codec(codec);
+	kfree(codec->reg_cache);
+	kfree(codec);
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad1980 = {
+	.probe = 	ad1980_soc_probe,
+	.remove = 	ad1980_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1980);
+
+MODULE_DESCRIPTION("ASoC ad1980 driver");
+MODULE_AUTHOR("Roy Huang, Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
new file mode 100644
index 0000000..db6c850
--- /dev/null
+++ b/sound/soc/codecs/ad1980.h
@@ -0,0 +1,23 @@
+/*
+ * ad1980.h  --  ad1980 Soc Audio driver
+ */
+
+#ifndef _AD1980_H
+#define _AD1980_H
+/* Bit definition of Power-Down Control/Status Register */
+#define ADC		0x0001
+#define DAC		0x0002
+#define ANL		0x0004
+#define REF		0x0008
+#define PR0		0x0100
+#define PR1		0x0200
+#define PR2		0x0400
+#define PR3		0x0800
+#define PR4		0x1000
+#define PR5		0x2000
+#define PR6		0x4000
+
+extern struct snd_soc_dai ad1980_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1980;
+
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 7da9f46..088cf99 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -535,87 +535,85 @@
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 
-#define I2C_DRIVERID_AK4535 0xfefe /* liam -  need a proper id */
-
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver ak4535_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
-   around */
-static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int ak4535_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = ak4535_socdev;
-	struct ak4535_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = ak4535_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		printk(KERN_ERR "failed to initialise AK4535\n");
-		goto err;
-	}
-	return ret;
 
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int ak4535_i2c_detach(struct i2c_client *client)
+static int ak4535_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int ak4535_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, ak4535_codec_probe);
-}
+static const struct i2c_device_id ak4535_i2c_id[] = {
+	{ "ak4535", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver ak4535_i2c_driver = {
 	.driver = {
 		.name = "AK4535 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_AK4535,
-	.attach_adapter = ak4535_i2c_attach,
-	.detach_client =  ak4535_i2c_detach,
-	.command =        NULL,
+	.probe =    ak4535_i2c_probe,
+	.remove =   ak4535_i2c_remove,
+	.id_table = ak4535_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "AK4535",
-	.driver = &ak4535_i2c_driver,
-};
+static int ak4535_add_i2c_device(struct platform_device *pdev,
+				 const struct ak4535_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&ak4535_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "ak4535", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&ak4535_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
 static int ak4535_probe(struct platform_device *pdev)
@@ -624,7 +622,7 @@
 	struct ak4535_setup_data *setup;
 	struct snd_soc_codec *codec;
 	struct ak4535_priv *ak4535;
-	int ret = 0;
+	int ret;
 
 	printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
 
@@ -646,17 +644,14 @@
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
 	ak4535_socdev = socdev;
+	ret = -ENODEV;
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
 		codec->hw_read = (hw_read_t)i2c_master_recv;
-		ret = i2c_add_driver(&ak4535_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = ak4535_add_i2c_device(pdev, setup);
 	}
-#else
-	/* Add other interfaces here */
 #endif
 
 	if (ret != 0) {
@@ -678,6 +673,7 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&ak4535_i2c_driver);
 #endif
 	kfree(codec->private_data);
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
index e9fe30e..c7a5870 100644
--- a/sound/soc/codecs/ak4535.h
+++ b/sound/soc/codecs/ak4535.h
@@ -37,6 +37,7 @@
 #define AK4535_CACHEREGNUM 	0x10
 
 struct ak4535_setup_data {
+	int            i2c_bus;
 	unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
new file mode 100644
index 0000000..940ce1c
--- /dev/null
+++ b/sound/soc/codecs/ssm2602.c
@@ -0,0 +1,776 @@
+/*
+ * File:         sound/soc/codecs/ssm2602.c
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ * Description:  Driver for ssm2602 sound chip
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ssm2602.h"
+
+#define AUDIO_NAME "ssm2602"
+#define SSM2602_VERSION "0.1"
+
+struct snd_soc_codec_device soc_codec_dev_ssm2602;
+
+/* codec private data */
+struct ssm2602_priv {
+	unsigned int sysclk;
+	struct snd_pcm_substream *master_substream;
+	struct snd_pcm_substream *slave_substream;
+};
+
+/*
+ * ssm2602 register cache
+ * We can't read the ssm2602 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
+	0x0017, 0x0017, 0x0079, 0x0079,
+	0x0000, 0x0000, 0x0000, 0x000a,
+	0x0000, 0x0000
+};
+
+/*
+ * read ssm2602 register cache
+ */
+static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == SSM2602_RESET)
+		return 0;
+	if (reg >= SSM2602_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write ssm2602 register cache
+ */
+static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= SSM2602_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the ssm2602 register space
+ */
+static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D9 ssm2602 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	ssm2602_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define ssm2602_reset(c)	ssm2602_write(c, SSM2602_RESET, 0)
+
+/*Appending several "None"s just for OSS mixer use*/
+static const char *ssm2602_input_select[] = {
+	"Line", "Mic", "None", "None", "None",
+	"None", "None", "None",
+};
+
+static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum ssm2602_enum[] = {
+	SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select),
+	SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
+};
+
+static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+
+SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
+	0, 127, 0),
+SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
+	7, 1, 0),
+
+SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
+
+SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
+
+SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
+
+SOC_ENUM("Capture Source", ssm2602_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
+};
+
+/* add non dapm controls */
+static int ssm2602_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+			snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Output Mixer */
+static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ssm2602_input_mux_controls =
+SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
+
+static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
+	&ssm2602_output_mixer_controls[0],
+	ARRAY_SIZE(ssm2602_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
+SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const struct snd_soc_dapm_route audio_conn[] = {
+	/* output mixer */
+	{"Output Mixer", "Line Bypass Switch", "Line Input"},
+	{"Output Mixer", "HiFi Playback Switch", "DAC"},
+	{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
+
+	/* outputs */
+	{"RHPOUT", NULL, "Output Mixer"},
+	{"ROUT", NULL, "Output Mixer"},
+	{"LHPOUT", NULL, "Output Mixer"},
+	{"LOUT", NULL, "Output Mixer"},
+
+	/* input mux */
+	{"Input Mux", "Line", "Line Input"},
+	{"Input Mux", "Mic", "Mic Bias"},
+	{"ADC", NULL, "Input Mux"},
+
+	/* inputs */
+	{"Line Input", NULL, "LLINEIN"},
+	{"Line Input", NULL, "RLINEIN"},
+	{"Mic Bias", NULL, "MICIN"},
+};
+
+static int ssm2602_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, ssm2602_dapm_widgets,
+				  ARRAY_SIZE(ssm2602_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+struct _coeff_div {
+	u32 mclk;
+	u32 rate;
+	u16 fs;
+	u8 sr:4;
+	u8 bosr:1;
+	u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+	/* 48k */
+	{12288000, 48000, 256, 0x0, 0x0, 0x0},
+	{18432000, 48000, 384, 0x0, 0x1, 0x0},
+	{12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+	/* 32k */
+	{12288000, 32000, 384, 0x6, 0x0, 0x0},
+	{18432000, 32000, 576, 0x6, 0x1, 0x0},
+	{12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+	/* 8k */
+	{12288000, 8000, 1536, 0x3, 0x0, 0x0},
+	{18432000, 8000, 2304, 0x3, 0x1, 0x0},
+	{11289600, 8000, 1408, 0xb, 0x0, 0x0},
+	{16934400, 8000, 2112, 0xb, 0x1, 0x0},
+	{12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+	/* 96k */
+	{12288000, 96000, 128, 0x7, 0x0, 0x0},
+	{18432000, 96000, 192, 0x7, 0x1, 0x0},
+	{12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+	/* 44.1k */
+	{11289600, 44100, 256, 0x8, 0x0, 0x0},
+	{16934400, 44100, 384, 0x8, 0x1, 0x0},
+	{12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+	/* 88.2k */
+	{11289600, 88200, 128, 0xf, 0x0, 0x0},
+	{16934400, 88200, 192, 0xf, 0x1, 0x0},
+	{12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return i;
+}
+
+static int ssm2602_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	u16 srate;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ssm2602_priv *ssm2602 = codec->private_data;
+	u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
+	int i = get_coeff(ssm2602->sysclk, params_rate(params));
+
+	/*no match is found*/
+	if (i == ARRAY_SIZE(coeff_div))
+		return -EINVAL;
+
+	srate = (coeff_div[i].sr << 2) |
+		(coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+	ssm2602_write(codec, SSM2602_ACTIVE, 0);
+	ssm2602_write(codec, SSM2602_SRATE, srate);
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0004;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0008;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x000c;
+		break;
+	}
+	ssm2602_write(codec, SSM2602_IFACE, iface);
+	ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+	return 0;
+}
+
+static int ssm2602_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ssm2602_priv *ssm2602 = codec->private_data;
+	struct snd_pcm_runtime *master_runtime;
+
+	/* The DAI has shared clocks so if we already have a playback or
+	 * capture going then constrain this substream to match it.
+	 */
+	if (ssm2602->master_substream) {
+		master_runtime = ssm2602->master_substream->runtime;
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_RATE,
+					     master_runtime->rate,
+					     master_runtime->rate);
+
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+					     master_runtime->sample_bits,
+					     master_runtime->sample_bits);
+
+		ssm2602->slave_substream = substream;
+	} else
+		ssm2602->master_substream = substream;
+
+	return 0;
+}
+
+static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	/* set active */
+	ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+
+	return 0;
+}
+
+static void ssm2602_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	/* deactivate */
+	if (!codec->active)
+		ssm2602_write(codec, SSM2602_ACTIVE, 0);
+}
+
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
+	if (mute)
+		ssm2602_write(codec, SSM2602_APDIGI,
+				mute_reg | APDIGI_ENABLE_DAC_MUTE);
+	else
+		ssm2602_write(codec, SSM2602_APDIGI, mute_reg);
+	return 0;
+}
+
+static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ssm2602_priv *ssm2602 = codec->private_data;
+	switch (freq) {
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 16934400:
+	case 18432000:
+		ssm2602->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface |= 0x0040;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0013;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x0090;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0080;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0010;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set iface */
+	ssm2602_write(codec, SSM2602_IFACE, iface);
+	return 0;
+}
+
+static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* vref/mid, osc on, dac unmute */
+		ssm2602_write(codec, SSM2602_PWR, reg);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* everything off except vref/vmid, */
+		ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* everything off, dac mute, inactive */
+		ssm2602_write(codec, SSM2602_ACTIVE, 0);
+		ssm2602_write(codec, SSM2602_PWR, 0xffff);
+		break;
+
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+		SNDRV_PCM_RATE_96000)
+
+struct snd_soc_dai ssm2602_dai = {
+	.name = "SSM2602",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SSM2602_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SSM2602_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE,},
+	.ops = {
+		.startup = ssm2602_startup,
+		.prepare = ssm2602_pcm_prepare,
+		.hw_params = ssm2602_hw_params,
+		.shutdown = ssm2602_shutdown,
+	},
+	.dai_ops = {
+		.digital_mute = ssm2602_mute,
+		.set_sysclk = ssm2602_set_dai_sysclk,
+		.set_fmt = ssm2602_set_dai_fmt,
+	}
+};
+EXPORT_SYMBOL_GPL(ssm2602_dai);
+
+static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int ssm2602_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	ssm2602_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the ssm2602 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ssm2602_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int reg, ret = 0;
+
+	codec->name = "SSM2602";
+	codec->owner = THIS_MODULE;
+	codec->read = ssm2602_read_reg_cache;
+	codec->write = ssm2602_write;
+	codec->set_bias_level = ssm2602_set_bias_level;
+	codec->dai = &ssm2602_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = sizeof(ssm2602_reg);
+	codec->reg_cache = kmemdup(ssm2602_reg, sizeof(ssm2602_reg),
+					GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	ssm2602_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		pr_err("ssm2602: failed to create pcms\n");
+		goto pcm_err;
+	}
+	/*power on device*/
+	ssm2602_write(codec, SSM2602_ACTIVE, 0);
+	/* set the update bits */
+	reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL);
+	ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
+	reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL);
+	ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
+	reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V);
+	ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
+	reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
+	ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+	/*select Line in as default input*/
+	ssm2602_write(codec, SSM2602_APANA,
+			APANA_ENABLE_MIC_BOOST2 | APANA_SELECT_DAC |
+			APANA_ENABLE_MIC_BOOST);
+	ssm2602_write(codec, SSM2602_PWR, 0);
+
+	ssm2602_add_controls(codec);
+	ssm2602_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		pr_err("ssm2602: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *ssm2602_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/*
+ * ssm2602 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+static int ssm2602_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct snd_soc_device *socdev = ssm2602_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = ssm2602_init(socdev);
+	if (ret < 0)
+		pr_err("failed to initialise SSM2602\n");
+
+	return ret;
+}
+
+static int ssm2602_i2c_remove(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	kfree(codec->reg_cache);
+	return 0;
+}
+
+static const struct i2c_device_id ssm2602_i2c_id[] = {
+	{ "ssm2602", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
+/* corgi i2c codec control layer */
+static struct i2c_driver ssm2602_i2c_driver = {
+	.driver = {
+		.name = "SSM2602 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = ssm2602_i2c_probe,
+	.remove = ssm2602_i2c_remove,
+	.id_table = ssm2602_i2c_id,
+};
+
+static int ssm2602_add_i2c_device(struct platform_device *pdev,
+				  const struct ssm2602_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&ssm2602_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "ssm2602", I2C_NAME_SIZE);
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+		setup->i2c_bus);
+		goto err_driver;
+	}
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+		(unsigned int)info.addr);
+		goto err_driver;
+	}
+	return 0;
+err_driver:
+	i2c_del_driver(&ssm2602_i2c_driver);
+	return -ENODEV;
+}
+#endif
+
+static int ssm2602_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct ssm2602_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct ssm2602_priv *ssm2602;
+	int ret = 0;
+
+	pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
+	if (ssm2602 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = ssm2602;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ssm2602_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = ssm2602_add_i2c_device(pdev, setup);
+	}
+#else
+	/* other interfaces */
+#endif
+	return ret;
+}
+
+/* remove everything here */
+static int ssm2602_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
+	i2c_del_driver(&ssm2602_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
+	.probe = 	ssm2602_probe,
+	.remove = 	ssm2602_remove,
+	.suspend = 	ssm2602_suspend,
+	.resume =	ssm2602_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
+
+MODULE_DESCRIPTION("ASoC ssm2602 driver");
+MODULE_AUTHOR("Cliff Cai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
new file mode 100644
index 0000000..f344e6d
--- /dev/null
+++ b/sound/soc/codecs/ssm2602.h
@@ -0,0 +1,130 @@
+/*
+ * File:         sound/soc/codecs/ssm2602.h
+ * Author:       Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created:      Tue June 06 2008
+ *
+ * Modified:
+ *               Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SSM2602_H
+#define _SSM2602_H
+
+/* SSM2602 Codec Register definitions */
+
+#define SSM2602_LINVOL   0x00
+#define SSM2602_RINVOL   0x01
+#define SSM2602_LOUT1V   0x02
+#define SSM2602_ROUT1V   0x03
+#define SSM2602_APANA    0x04
+#define SSM2602_APDIGI   0x05
+#define SSM2602_PWR      0x06
+#define SSM2602_IFACE    0x07
+#define SSM2602_SRATE    0x08
+#define SSM2602_ACTIVE   0x09
+#define SSM2602_RESET	 0x0f
+
+/*SSM2602 Codec Register Field definitions
+ *(Mask value to extract the corresponding Register field)
+ */
+
+/*Left ADC Volume Control (SSM2602_REG_LEFT_ADC_VOL)*/
+#define     LINVOL_LIN_VOL                0x01F   /* Left Channel PGA Volume control                      */
+#define     LINVOL_LIN_ENABLE_MUTE        0x080   /* Left Channel Input Mute                              */
+#define     LINVOL_LRIN_BOTH              0x100   /* Left Channel Line Input Volume update                */
+
+/*Right ADC Volume Control (SSM2602_REG_RIGHT_ADC_VOL)*/
+#define     RINVOL_RIN_VOL                0x01F   /* Right Channel PGA Volume control                     */
+#define     RINVOL_RIN_ENABLE_MUTE        0x080   /* Right Channel Input Mute                             */
+#define     RINVOL_RLIN_BOTH              0x100   /* Right Channel Line Input Volume update               */
+
+/*Left DAC Volume Control (SSM2602_REG_LEFT_DAC_VOL)*/
+#define     LOUT1V_LHP_VOL                0x07F   /* Left Channel Headphone volume control                */
+#define     LOUT1V_ENABLE_LZC             0x080   /* Left Channel Zero cross detect enable                */
+#define     LOUT1V_LRHP_BOTH              0x100   /* Left Channel Headphone volume update                 */
+
+/*Right DAC Volume Control (SSM2602_REG_RIGHT_DAC_VOL)*/
+#define     ROUT1V_RHP_VOL                0x07F   /* Right Channel Headphone volume control               */
+#define     ROUT1V_ENABLE_RZC             0x080   /* Right Channel Zero cross detect enable               */
+#define     ROUT1V_RLHP_BOTH              0x100   /* Right Channel Headphone volume update                */
+
+/*Analogue Audio Path Control (SSM2602_REG_ANALOGUE_PATH)*/
+#define     APANA_ENABLE_MIC_BOOST       0x001   /* Primary Microphone Amplifier gain booster control    */
+#define     APANA_ENABLE_MIC_MUTE        0x002   /* Microphone Mute Control                              */
+#define     APANA_ADC_IN_SELECT          0x004   /* Microphone/Line IN select to ADC (1=MIC, 0=Line In)  */
+#define     APANA_ENABLE_BYPASS          0x008   /* Line input bypass to line output                     */
+#define     APANA_SELECT_DAC             0x010   /* Select DAC (1=Select DAC, 0=Don't Select DAC)        */
+#define     APANA_ENABLE_SIDETONE        0x020   /* Enable/Disable Side Tone                             */
+#define     APANA_SIDETONE_ATTN          0x0C0   /* Side Tone Attenuation                                */
+#define     APANA_ENABLE_MIC_BOOST2      0x100   /* Secondary Microphone Amplifier gain booster control  */
+
+/*Digital Audio Path Control (SSM2602_REG_DIGITAL_PATH)*/
+#define     APDIGI_ENABLE_ADC_HPF         0x001   /* Enable/Disable ADC Highpass Filter                   */
+#define     APDIGI_DE_EMPHASIS            0x006   /* De-Emphasis Control                                  */
+#define     APDIGI_ENABLE_DAC_MUTE        0x008   /* DAC Mute Control                                     */
+#define     APDIGI_STORE_OFFSET           0x010   /* Store/Clear DC offset when HPF is disabled           */
+
+/*Power Down Control (SSM2602_REG_POWER)
+ *(1=Enable PowerDown, 0=Disable PowerDown)
+ */
+#define     PWR_LINE_IN_PDN            0x001   /* Line Input Power Down                                */
+#define     PWR_MIC_PDN                0x002   /* Microphone Input & Bias Power Down                   */
+#define     PWR_ADC_PDN                0x004   /* ADC Power Down                                       */
+#define     PWR_DAC_PDN                0x008   /* DAC Power Down                                       */
+#define     PWR_OUT_PDN                0x010   /* Outputs Power Down                                   */
+#define     PWR_OSC_PDN                0x020   /* Oscillator Power Down                                */
+#define     PWR_CLK_OUT_PDN            0x040   /* CLKOUT Power Down                                    */
+#define     PWR_POWER_OFF              0x080   /* POWEROFF Mode                                        */
+
+/*Digital Audio Interface Format (SSM2602_REG_DIGITAL_IFACE)*/
+#define     IFACE_IFACE_FORMAT           0x003   /* Digital Audio input format control                   */
+#define     IFACE_AUDIO_DATA_LEN         0x00C   /* Audio Data word length control                       */
+#define     IFACE_DAC_LR_POLARITY        0x010   /* Polarity Control for clocks in RJ,LJ and I2S modes   */
+#define     IFACE_DAC_LR_SWAP            0x020   /* Swap DAC data control                                */
+#define     IFACE_ENABLE_MASTER          0x040   /* Enable/Disable Master Mode                           */
+#define     IFACE_BCLK_INVERT            0x080   /* Bit Clock Inversion control                          */
+
+/*Sampling Control (SSM2602_REG_SAMPLING_CTRL)*/
+#define     SRATE_ENABLE_USB_MODE        0x001   /* Enable/Disable USB Mode                              */
+#define     SRATE_BOS_RATE               0x002   /* Base Over-Sampling rate                              */
+#define     SRATE_SAMPLE_RATE            0x03C   /* Clock setting condition (Sampling rate control)      */
+#define     SRATE_CORECLK_DIV2           0x040   /* Core Clock divider select                            */
+#define     SRATE_CLKOUT_DIV2            0x080   /* Clock Out divider select                             */
+
+/*Active Control (SSM2602_REG_ACTIVE_CTRL)*/
+#define     ACTIVE_ACTIVATE_CODEC         0x001   /* Activate Codec Digital Audio Interface               */
+
+/*********************************************************************/
+
+#define SSM2602_CACHEREGNUM 	10
+
+#define SSM2602_SYSCLK	0
+#define SSM2602_DAI		0
+
+struct ssm2602_setup_data {
+	int i2c_bus;
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ssm2602_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ssm2602;
+
+#endif
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
new file mode 100644
index 0000000..bed8a9e
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -0,0 +1,520 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * ALSA SoC CODEC driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-of-simple.h>
+#include <sound/initval.h>
+
+#include "tlv320aic26.h"
+
+MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver");
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+/* AIC26 driver private data */
+struct aic26 {
+	struct spi_device *spi;
+	struct snd_soc_codec codec;
+	u16 reg_cache[AIC26_NUM_REGS];	/* shadow registers */
+	int master;
+	int datfm;
+	int mclk;
+
+	/* Keyclick parameters */
+	int keyclick_amplitude;
+	int keyclick_freq;
+	int keyclick_len;
+};
+
+/* ---------------------------------------------------------------------
+ * Register access routines
+ */
+static unsigned int aic26_reg_read(struct snd_soc_codec *codec,
+				   unsigned int reg)
+{
+	struct aic26 *aic26 = codec->private_data;
+	u16 *cache = codec->reg_cache;
+	u16 cmd, value;
+	u8 buffer[2];
+	int rc;
+
+	if (reg >= AIC26_NUM_REGS) {
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+
+	/* Do SPI transfer; first 16bits are command; remaining is
+	 * register contents */
+	cmd = AIC26_READ_COMMAND_WORD(reg);
+	buffer[0] = (cmd >> 8) & 0xff;
+	buffer[1] = cmd & 0xff;
+	rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2);
+	if (rc) {
+		dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
+		return -EIO;
+	}
+	value = (buffer[0] << 8) | buffer[1];
+
+	/* Update the cache before returning with the value */
+	cache[reg] = value;
+	return value;
+}
+
+static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec,
+					 unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+
+	if (reg >= AIC26_NUM_REGS) {
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+
+	return cache[reg];
+}
+
+static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
+			   unsigned int value)
+{
+	struct aic26 *aic26 = codec->private_data;
+	u16 *cache = codec->reg_cache;
+	u16 cmd;
+	u8 buffer[4];
+	int rc;
+
+	if (reg >= AIC26_NUM_REGS) {
+		WARN_ON_ONCE(1);
+		return -EINVAL;
+	}
+
+	/* Do SPI transfer; first 16bits are command; remaining is data
+	 * to write into register */
+	cmd = AIC26_WRITE_COMMAND_WORD(reg);
+	buffer[0] = (cmd >> 8) & 0xff;
+	buffer[1] = cmd & 0xff;
+	buffer[2] = value >> 8;
+	buffer[3] = value;
+	rc = spi_write(aic26->spi, buffer, 4);
+	if (rc) {
+		dev_err(&aic26->spi->dev, "AIC26 reg read error\n");
+		return -EIO;
+	}
+
+	/* update cache before returning */
+	cache[reg] = value;
+	return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Operations
+ */
+static int aic26_hw_params(struct snd_pcm_substream *substream,
+			   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct aic26 *aic26 = codec->private_data;
+	int fsref, divisor, wlen, pval, jval, dval, qval;
+	u16 reg;
+
+	dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n",
+		substream, params);
+	dev_dbg(&aic26->spi->dev, "rate=%i format=%i\n", params_rate(params),
+		params_format(params));
+
+	switch (params_rate(params)) {
+	case 8000:  fsref = 48000; divisor = AIC26_DIV_6; break;
+	case 11025: fsref = 44100; divisor = AIC26_DIV_4; break;
+	case 12000: fsref = 48000; divisor = AIC26_DIV_4; break;
+	case 16000: fsref = 48000; divisor = AIC26_DIV_3; break;
+	case 22050: fsref = 44100; divisor = AIC26_DIV_2; break;
+	case 24000: fsref = 48000; divisor = AIC26_DIV_2; break;
+	case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break;
+	case 44100: fsref = 44100; divisor = AIC26_DIV_1; break;
+	case 48000: fsref = 48000; divisor = AIC26_DIV_1; break;
+	default:
+		dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL;
+	}
+
+	/* select data word length */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:     wlen = AIC26_WLEN_16; break;
+	case SNDRV_PCM_FORMAT_S16_BE: wlen = AIC26_WLEN_16; break;
+	case SNDRV_PCM_FORMAT_S24_BE: wlen = AIC26_WLEN_24; break;
+	case SNDRV_PCM_FORMAT_S32_BE: wlen = AIC26_WLEN_32; break;
+	default:
+		dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+	}
+
+	/* Configure PLL */
+	pval = 1;
+	jval = (fsref == 44100) ? 7 : 8;
+	dval = (fsref == 44100) ? 5264 : 1920;
+	qval = 0;
+	reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
+	aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
+	reg = dval << 2;
+	aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
+
+	/* Audio Control 3 (master mode, fsref rate) */
+	reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
+	reg &= ~0xf800;
+	if (aic26->master)
+		reg |= 0x0800;
+	if (fsref == 48000)
+		reg |= 0x2000;
+	aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+	/* Audio Control 1 (FSref divisor) */
+	reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
+	reg &= ~0x0fff;
+	reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
+	aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
+
+	return 0;
+}
+
+/**
+ * aic26_mute - Mute control to reduce noise when changing audio format
+ */
+static int aic26_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct aic26 *aic26 = codec->private_data;
+	u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN);
+
+	dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n",
+		dai, mute);
+
+	if (mute)
+		reg |= 0x8080;
+	else
+		reg &= ~0x8080;
+	aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
+
+	return 0;
+}
+
+static int aic26_set_sysclk(struct snd_soc_dai *codec_dai,
+			    int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct aic26 *aic26 = codec->private_data;
+
+	dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i,"
+		" freq=%i, dir=%i)\n",
+		codec_dai, clk_id, freq, dir);
+
+	/* MCLK needs to fall between 2MHz and 50 MHz */
+	if ((freq < 2000000) || (freq > 50000000))
+		return -EINVAL;
+
+	aic26->mclk = freq;
+	return 0;
+}
+
+static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct aic26 *aic26 = codec->private_data;
+
+	dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n",
+		codec_dai, fmt);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break;
+	case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break;
+	default:
+		dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:     aic26->datfm = AIC26_DATFM_I2S; break;
+	case SND_SOC_DAIFMT_DSP_A:   aic26->datfm = AIC26_DATFM_DSP; break;
+	case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break;
+	case SND_SOC_DAIFMT_LEFT_J:  aic26->datfm = AIC26_DATFM_LEFTJ; break;
+	default:
+		dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * Digital Audio Interface Definition
+ */
+#define AIC26_RATES	(SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |\
+			 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+			 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+			 SNDRV_PCM_RATE_48000)
+#define AIC26_FORMATS	(SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_BE |\
+			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+
+struct snd_soc_dai aic26_dai = {
+	.name = "tlv320aic26",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = AIC26_RATES,
+		.formats = AIC26_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = AIC26_RATES,
+		.formats = AIC26_FORMATS,
+	},
+	.ops = {
+		.hw_params = aic26_hw_params,
+	},
+	.dai_ops = {
+		.digital_mute = aic26_mute,
+		.set_sysclk = aic26_set_sysclk,
+		.set_fmt = aic26_set_fmt,
+	},
+};
+EXPORT_SYMBOL_GPL(aic26_dai);
+
+/* ---------------------------------------------------------------------
+ * ALSA controls
+ */
+static const char *aic26_capture_src_text[] = {"Mic", "Aux"};
+static const struct soc_enum aic26_capture_src_enum =
+	SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12, 2, aic26_capture_src_text);
+
+static const struct snd_kcontrol_new aic26_snd_controls[] = {
+	/* Output */
+	SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1),
+	SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1),
+	SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0),
+	SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1),
+	SOC_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0),
+	SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0),
+	SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0),
+	SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0),
+	SOC_ENUM("Capture Source", aic26_capture_src_enum),
+};
+
+/* ---------------------------------------------------------------------
+ * SoC CODEC portion of driver: probe and release routines
+ */
+static int aic26_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct snd_kcontrol *kcontrol;
+	struct aic26 *aic26;
+	int i, ret, err;
+
+	dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n");
+	dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
+	dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data);
+
+	/* Fetch the relevant aic26 private data here (it's already been
+	 * stored in the .codec pointer) */
+	aic26 = socdev->codec_data;
+	if (aic26 == NULL) {
+		dev_err(&pdev->dev, "aic26: missing codec pointer\n");
+		return -ENODEV;
+	}
+	codec = &aic26->codec;
+	socdev->codec = codec;
+
+	dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
+		&pdev->dev, socdev->dev);
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "aic26: failed to create pcms\n");
+		return -ENODEV;
+	}
+
+	/* register controls */
+	dev_dbg(&pdev->dev, "Registering controls\n");
+	for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) {
+		kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL);
+		err = snd_ctl_add(codec->card, kcontrol);
+		WARN_ON(err < 0);
+	}
+
+	/* CODEC is setup, we can register the card now */
+	dev_dbg(&pdev->dev, "Registering card\n");
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "aic26: failed to register card\n");
+		goto card_err;
+	}
+	return 0;
+
+ card_err:
+	snd_soc_free_pcms(socdev);
+	return ret;
+}
+
+static int aic26_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	snd_soc_free_pcms(socdev);
+	return 0;
+}
+
+struct snd_soc_codec_device aic26_soc_codec_dev = {
+	.probe = aic26_probe,
+	.remove = aic26_remove,
+};
+EXPORT_SYMBOL_GPL(aic26_soc_codec_dev);
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: sysfs files for debugging
+ */
+
+static ssize_t aic26_keyclick_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct aic26 *aic26 = dev_get_drvdata(dev);
+	int val, amp, freq, len;
+
+	val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+	amp = (val >> 12) & 0x7;
+	freq = (125 << ((val >> 8) & 0x7)) >> 1;
+	len = 2 * (1 + ((val >> 4) & 0xf));
+
+	return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len);
+}
+
+/* Any write to the keyclick attribute will trigger the keyclick event */
+static ssize_t aic26_keyclick_set(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct aic26 *aic26 = dev_get_drvdata(dev);
+	int val;
+
+	val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+	val |= 0x8000;
+	aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
+
+	return count;
+}
+
+static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
+
+/* ---------------------------------------------------------------------
+ * SPI device portion of driver: probe and release routines and SPI
+ * 				 driver registration.
+ */
+static int aic26_spi_probe(struct spi_device *spi)
+{
+	struct aic26 *aic26;
+	int rc, i, reg;
+
+	dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
+
+	/* Allocate driver data */
+	aic26 = kzalloc(sizeof *aic26, GFP_KERNEL);
+	if (!aic26)
+		return -ENOMEM;
+
+	/* Initialize the driver data */
+	aic26->spi = spi;
+	dev_set_drvdata(&spi->dev, aic26);
+
+	/* Setup what we can in the codec structure so that the register
+	 * access functions will work as expected.  More will be filled
+	 * out when it is probed by the SoC CODEC part of this driver */
+	aic26->codec.private_data = aic26;
+	aic26->codec.name = "aic26";
+	aic26->codec.owner = THIS_MODULE;
+	aic26->codec.dai = &aic26_dai;
+	aic26->codec.num_dai = 1;
+	aic26->codec.read = aic26_reg_read;
+	aic26->codec.write = aic26_reg_write;
+	aic26->master = 1;
+	mutex_init(&aic26->codec.mutex);
+	INIT_LIST_HEAD(&aic26->codec.dapm_widgets);
+	INIT_LIST_HEAD(&aic26->codec.dapm_paths);
+	aic26->codec.reg_cache_size = AIC26_NUM_REGS;
+	aic26->codec.reg_cache = aic26->reg_cache;
+
+	/* Reset the codec to power on defaults */
+	aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
+
+	/* Power up CODEC */
+	aic26_reg_write(&aic26->codec, AIC26_REG_POWER_CTRL, 0);
+
+	/* Audio Control 3 (master mode, fsref rate) */
+	reg = aic26_reg_read(&aic26->codec, AIC26_REG_AUDIO_CTRL3);
+	reg &= ~0xf800;
+	reg |= 0x0800; /* set master mode */
+	aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL3, reg);
+
+	/* Fill register cache */
+	for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++)
+		aic26_reg_read(&aic26->codec, i);
+
+	/* Register the sysfs files for debugging */
+	/* Create SysFS files */
+	rc = device_create_file(&spi->dev, &dev_attr_keyclick);
+	if (rc)
+		dev_info(&spi->dev, "error creating sysfs files\n");
+
+#if defined(CONFIG_SND_SOC_OF_SIMPLE)
+	/* Tell the of_soc helper about this codec */
+	of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai,
+				  spi->dev.archdata.of_node);
+#endif
+
+	dev_dbg(&spi->dev, "SPI device initialized\n");
+	return 0;
+}
+
+static int aic26_spi_remove(struct spi_device *spi)
+{
+	struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
+
+	kfree(aic26);
+
+	return 0;
+}
+
+static struct spi_driver aic26_spi = {
+	.driver = {
+		.name = "tlv320aic26",
+		.owner = THIS_MODULE,
+	},
+	.probe = aic26_spi_probe,
+	.remove = aic26_spi_remove,
+};
+
+static int __init aic26_init(void)
+{
+	return spi_register_driver(&aic26_spi);
+}
+module_init(aic26_init);
+
+static void __exit aic26_exit(void)
+{
+	spi_unregister_driver(&aic26_spi);
+}
+module_exit(aic26_exit);
diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h
new file mode 100644
index 0000000..786ba16
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic26.h
@@ -0,0 +1,96 @@
+/*
+ * Texas Instruments TLV320AIC26 low power audio CODEC
+ * register definitions
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#ifndef _TLV320AIC16_H_
+#define _TLV320AIC16_H_
+
+/* AIC26 Registers */
+#define AIC26_READ_COMMAND_WORD(addr)	((1 << 15) | (addr << 5))
+#define AIC26_WRITE_COMMAND_WORD(addr)	((0 << 15) | (addr << 5))
+#define AIC26_PAGE_ADDR(page, offset)	((page << 6) | offset)
+#define AIC26_NUM_REGS			AIC26_PAGE_ADDR(3, 0)
+
+/* Page 0: Auxillary data registers */
+#define AIC26_REG_BAT1			AIC26_PAGE_ADDR(0, 0x05)
+#define AIC26_REG_BAT2			AIC26_PAGE_ADDR(0, 0x06)
+#define AIC26_REG_AUX			AIC26_PAGE_ADDR(0, 0x07)
+#define AIC26_REG_TEMP1			AIC26_PAGE_ADDR(0, 0x09)
+#define AIC26_REG_TEMP2			AIC26_PAGE_ADDR(0, 0x0A)
+
+/* Page 1: Auxillary control registers */
+#define AIC26_REG_AUX_ADC		AIC26_PAGE_ADDR(1, 0x00)
+#define AIC26_REG_STATUS		AIC26_PAGE_ADDR(1, 0x01)
+#define AIC26_REG_REFERENCE		AIC26_PAGE_ADDR(1, 0x03)
+#define AIC26_REG_RESET			AIC26_PAGE_ADDR(1, 0x04)
+
+/* Page 2: Audio control registers */
+#define AIC26_REG_AUDIO_CTRL1		AIC26_PAGE_ADDR(2, 0x00)
+#define AIC26_REG_ADC_GAIN		AIC26_PAGE_ADDR(2, 0x01)
+#define AIC26_REG_DAC_GAIN		AIC26_PAGE_ADDR(2, 0x02)
+#define AIC26_REG_SIDETONE		AIC26_PAGE_ADDR(2, 0x03)
+#define AIC26_REG_AUDIO_CTRL2		AIC26_PAGE_ADDR(2, 0x04)
+#define AIC26_REG_POWER_CTRL		AIC26_PAGE_ADDR(2, 0x05)
+#define AIC26_REG_AUDIO_CTRL3		AIC26_PAGE_ADDR(2, 0x06)
+
+#define AIC26_REG_FILTER_COEFF_L_N0	AIC26_PAGE_ADDR(2, 0x07)
+#define AIC26_REG_FILTER_COEFF_L_N1	AIC26_PAGE_ADDR(2, 0x08)
+#define AIC26_REG_FILTER_COEFF_L_N2	AIC26_PAGE_ADDR(2, 0x09)
+#define AIC26_REG_FILTER_COEFF_L_N3	AIC26_PAGE_ADDR(2, 0x0A)
+#define AIC26_REG_FILTER_COEFF_L_N4	AIC26_PAGE_ADDR(2, 0x0B)
+#define AIC26_REG_FILTER_COEFF_L_N5	AIC26_PAGE_ADDR(2, 0x0C)
+#define AIC26_REG_FILTER_COEFF_L_D1	AIC26_PAGE_ADDR(2, 0x0D)
+#define AIC26_REG_FILTER_COEFF_L_D2	AIC26_PAGE_ADDR(2, 0x0E)
+#define AIC26_REG_FILTER_COEFF_L_D4	AIC26_PAGE_ADDR(2, 0x0F)
+#define AIC26_REG_FILTER_COEFF_L_D5	AIC26_PAGE_ADDR(2, 0x10)
+#define AIC26_REG_FILTER_COEFF_R_N0	AIC26_PAGE_ADDR(2, 0x11)
+#define AIC26_REG_FILTER_COEFF_R_N1	AIC26_PAGE_ADDR(2, 0x12)
+#define AIC26_REG_FILTER_COEFF_R_N2	AIC26_PAGE_ADDR(2, 0x13)
+#define AIC26_REG_FILTER_COEFF_R_N3	AIC26_PAGE_ADDR(2, 0x14)
+#define AIC26_REG_FILTER_COEFF_R_N4	AIC26_PAGE_ADDR(2, 0x15)
+#define AIC26_REG_FILTER_COEFF_R_N5	AIC26_PAGE_ADDR(2, 0x16)
+#define AIC26_REG_FILTER_COEFF_R_D1	AIC26_PAGE_ADDR(2, 0x17)
+#define AIC26_REG_FILTER_COEFF_R_D2	AIC26_PAGE_ADDR(2, 0x18)
+#define AIC26_REG_FILTER_COEFF_R_D4	AIC26_PAGE_ADDR(2, 0x19)
+#define AIC26_REG_FILTER_COEFF_R_D5	AIC26_PAGE_ADDR(2, 0x1A)
+
+#define AIC26_REG_PLL_PROG1		AIC26_PAGE_ADDR(2, 0x1B)
+#define AIC26_REG_PLL_PROG2		AIC26_PAGE_ADDR(2, 0x1C)
+#define AIC26_REG_AUDIO_CTRL4		AIC26_PAGE_ADDR(2, 0x1D)
+#define AIC26_REG_AUDIO_CTRL5		AIC26_PAGE_ADDR(2, 0x1E)
+
+/* fsref dividers; used in register 'Audio Control 1' */
+enum aic26_divisors {
+	AIC26_DIV_1	= 0,
+	AIC26_DIV_1_5	= 1,
+	AIC26_DIV_2	= 2,
+	AIC26_DIV_3	= 3,
+	AIC26_DIV_4	= 4,
+	AIC26_DIV_5	= 5,
+	AIC26_DIV_5_5	= 6,
+	AIC26_DIV_6	= 7,
+};
+
+/* Digital data format */
+enum aic26_datfm {
+	AIC26_DATFM_I2S		= 0 << 8,
+	AIC26_DATFM_DSP		= 1 << 8,
+	AIC26_DATFM_RIGHTJ	= 2 << 8, /* right justified */
+	AIC26_DATFM_LEFTJ	= 3 << 8, /* left justified */
+};
+
+/* Sample word length in bits; used in register 'Audio Control 1' */
+enum aic26_wlen {
+	AIC26_WLEN_16	= 0 << 10,
+	AIC26_WLEN_20	= 1 << 10,
+	AIC26_WLEN_24	= 2 << 10,
+	AIC26_WLEN_32	= 3 << 10,
+};
+
+extern struct snd_soc_dai aic26_dai;
+extern struct snd_soc_codec_device aic26_soc_codec_dev;
+
+#endif /* _TLV320AIC16_H_ */
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5f9abb1..566a427 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1,7 +1,7 @@
 /*
  * ALSA SoC TLV320AIC3X codec driver
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * Based on sound/soc/codecs/wm8753.c by Liam Girdwood
@@ -1172,71 +1172,39 @@
  * AIC3X 2 wire address can be up to 4 devices with device addresses
  * 0x18, 0x19, 0x1A, 0x1B
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver aic3x_i2c_driver;
-static struct i2c_client client_template;
 
 /*
  * If the i2c layer weren't so broken, we could pass this kind of data
  * around
  */
-static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int aic3x_i2c_probe(struct i2c_client *i2c,
+			   const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = aic3x_socdev;
-	struct aic3x_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n",
-		       addr);
-		goto err;
-	}
-
 	ret = aic3x_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		printk(KERN_ERR "aic3x: failed to initialise AIC3X\n");
-		goto err;
-	}
-	return ret;
-
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int aic3x_i2c_detach(struct i2c_client *client)
+static int aic3x_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int aic3x_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, aic3x_codec_probe);
-}
+static const struct i2c_device_id aic3x_i2c_id[] = {
+	{ "tlv320aic3x", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
 
 /* machine i2c codec control layer */
 static struct i2c_driver aic3x_i2c_driver = {
@@ -1244,13 +1212,9 @@
 		.name = "aic3x I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.attach_adapter = aic3x_i2c_attach,
-	.detach_client = aic3x_i2c_detach,
-};
-
-static struct i2c_client client_template = {
-	.name = "AIC3X",
-	.driver = &aic3x_i2c_driver,
+	.probe = aic3x_i2c_probe,
+	.remove = aic3x_i2c_remove,
+	.id_table = aic3x_i2c_id,
 };
 
 static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
@@ -1258,6 +1222,46 @@
 	value[0] = i2c_smbus_read_byte_data(client, value[0]);
 	return (len == 1);
 }
+
+static int aic3x_add_i2c_device(struct platform_device *pdev,
+				 const struct aic3x_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&aic3x_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&aic3x_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
 static int aic3x_probe(struct platform_device *pdev)
@@ -1290,12 +1294,9 @@
 	aic3x_socdev = socdev;
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t) i2c_master_send;
 		codec->hw_read = (hw_read_t) aic3x_i2c_read;
-		ret = i2c_add_driver(&aic3x_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = aic3x_add_i2c_device(pdev, setup);
 	}
 #else
 	/* Add other interfaces here */
@@ -1320,6 +1321,7 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&aic3x_i2c_driver);
 #endif
 	kfree(codec->private_data);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index d76c079..00a195a 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -1,7 +1,7 @@
 /*
  * ALSA SoC TLV320AIC3X codec driver
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -224,6 +224,7 @@
 int aic3x_headset_detected(struct snd_soc_codec *codec);
 
 struct aic3x_setup_data {
+	int i2c_bus;
 	unsigned short i2c_address;
 	unsigned int gpio_func[2];
 };
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 807318f..d206d7f 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -701,87 +701,86 @@
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 
-#define I2C_DRIVERID_UDA1380 0xfefe /* liam -  need a proper id */
-
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver uda1380_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
-   around */
-
-static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int uda1380_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = uda1380_socdev;
 	struct uda1380_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		pr_err("uda1380: failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = uda1380_init(socdev, setup->dac_clk);
-	if (ret < 0) {
+	if (ret < 0)
 		pr_err("uda1380: failed to initialise UDA1380\n");
-		goto err;
-	}
-	return ret;
 
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int uda1380_i2c_detach(struct i2c_client *client)
+static int uda1380_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int uda1380_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, uda1380_codec_probe);
-}
+static const struct i2c_device_id uda1380_i2c_id[] = {
+	{ "uda1380", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
 
 static struct i2c_driver uda1380_i2c_driver = {
 	.driver = {
 		.name =  "UDA1380 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_UDA1380,
-	.attach_adapter = uda1380_i2c_attach,
-	.detach_client =  uda1380_i2c_detach,
-	.command =        NULL,
+	.probe =    uda1380_i2c_probe,
+	.remove =   uda1380_i2c_remove,
+	.id_table = uda1380_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "UDA1380",
-	.driver = &uda1380_i2c_driver,
-};
+static int uda1380_add_i2c_device(struct platform_device *pdev,
+				  const struct uda1380_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&uda1380_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "uda1380", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&uda1380_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
 static int uda1380_probe(struct platform_device *pdev)
@@ -789,7 +788,7 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct uda1380_setup_data *setup;
 	struct snd_soc_codec *codec;
-	int ret = 0;
+	int ret;
 
 	pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
 
@@ -804,16 +803,13 @@
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
 	uda1380_socdev = socdev;
+	ret = -ENODEV;
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&uda1380_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = uda1380_add_i2c_device(pdev, setup);
 	}
-#else
-	/* Add other interfaces here */
 #endif
 
 	if (ret != 0)
@@ -833,6 +829,7 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&uda1380_i2c_driver);
 #endif
 	kfree(codec);
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
index 50c603e..c55c17a 100644
--- a/sound/soc/codecs/uda1380.h
+++ b/sound/soc/codecs/uda1380.h
@@ -73,6 +73,7 @@
 #define R23_AGC_EN	0x0001
 
 struct uda1380_setup_data {
+	int            i2c_bus;
 	unsigned short i2c_address;
 	int            dac_clk;
 #define UDA1380_DAC_CLK_SYSCLK 0
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 3d998e6..9a37c8d 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -199,7 +199,7 @@
 };
 
 static const struct snd_kcontrol_new wm8510_boost_controls[] = {
-SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA,  6, 1, 0),
+SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA,  6, 1, 1),
 SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
 SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
 };
@@ -665,88 +665,86 @@
 /*
  * WM8510 2 wire address is 0x1a
  */
-#define I2C_DRIVERID_WM8510 0xfefe /* liam -  need a proper id */
 
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8510_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
-   around */
-
-static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8510_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = wm8510_socdev;
-	struct wm8510_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		pr_err("failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = wm8510_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		pr_err("failed to initialise WM8510\n");
-		goto err;
-	}
-	return ret;
 
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int wm8510_i2c_detach(struct i2c_client *client)
+static int wm8510_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int wm8510_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8510_codec_probe);
-}
+static const struct i2c_device_id wm8510_i2c_id[] = {
+	{ "wm8510", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8510_i2c_driver = {
 	.driver = {
 		.name = "WM8510 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_WM8510,
-	.attach_adapter = wm8510_i2c_attach,
-	.detach_client =  wm8510_i2c_detach,
-	.command =        NULL,
+	.probe =    wm8510_i2c_probe,
+	.remove =   wm8510_i2c_remove,
+	.id_table = wm8510_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "WM8510",
-	.driver = &wm8510_i2c_driver,
-};
+static int wm8510_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8510_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8510_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8510", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8510_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
 static int wm8510_probe(struct platform_device *pdev)
@@ -771,11 +769,8 @@
 	wm8510_socdev = socdev;
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8510_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = wm8510_add_i2c_device(pdev, setup);
 	}
 #else
 	/* Add other interfaces here */
@@ -798,6 +793,7 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&wm8510_i2c_driver);
 #endif
 	kfree(codec);
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
index f5d2e42..c536839 100644
--- a/sound/soc/codecs/wm8510.h
+++ b/sound/soc/codecs/wm8510.h
@@ -94,6 +94,7 @@
 #define WM8510_MCLKDIV_12	(7 << 5)
 
 struct wm8510_setup_data {
+	int i2c_bus;
 	unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
new file mode 100644
index 0000000..df1ffbe
--- /dev/null
+++ b/sound/soc/codecs/wm8580.c
@@ -0,0 +1,1055 @@
+/*
+ * wm8580.c  --  WM8580 ALSA Soc Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ *  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.
+ *
+ * Notes:
+ *  The WM8580 is a multichannel codec with S/PDIF support, featuring six
+ *  DAC channels and two ADC channels.
+ *
+ *  Currently only the primary audio interface is supported - S/PDIF and
+ *  the secondary audio interfaces are not.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+
+#include "wm8580.h"
+
+#define AUDIO_NAME "wm8580"
+#define WM8580_VERSION "0.1"
+
+struct pll_state {
+	unsigned int in;
+	unsigned int out;
+};
+
+/* codec private data */
+struct wm8580_priv {
+	struct pll_state a;
+	struct pll_state b;
+};
+
+/* WM8580 register space */
+#define WM8580_PLLA1                         0x00
+#define WM8580_PLLA2                         0x01
+#define WM8580_PLLA3                         0x02
+#define WM8580_PLLA4                         0x03
+#define WM8580_PLLB1                         0x04
+#define WM8580_PLLB2                         0x05
+#define WM8580_PLLB3                         0x06
+#define WM8580_PLLB4                         0x07
+#define WM8580_CLKSEL                        0x08
+#define WM8580_PAIF1                         0x09
+#define WM8580_PAIF2                         0x0A
+#define WM8580_SAIF1                         0x0B
+#define WM8580_PAIF3                         0x0C
+#define WM8580_PAIF4                         0x0D
+#define WM8580_SAIF2                         0x0E
+#define WM8580_DAC_CONTROL1                  0x0F
+#define WM8580_DAC_CONTROL2                  0x10
+#define WM8580_DAC_CONTROL3                  0x11
+#define WM8580_DAC_CONTROL4                  0x12
+#define WM8580_DAC_CONTROL5                  0x13
+#define WM8580_DIGITAL_ATTENUATION_DACL1     0x14
+#define WM8580_DIGITAL_ATTENUATION_DACR1     0x15
+#define WM8580_DIGITAL_ATTENUATION_DACL2     0x16
+#define WM8580_DIGITAL_ATTENUATION_DACR2     0x17
+#define WM8580_DIGITAL_ATTENUATION_DACL3     0x18
+#define WM8580_DIGITAL_ATTENUATION_DACR3     0x19
+#define WM8580_MASTER_DIGITAL_ATTENUATION    0x1C
+#define WM8580_ADC_CONTROL1                  0x1D
+#define WM8580_SPDTXCHAN0                    0x1E
+#define WM8580_SPDTXCHAN1                    0x1F
+#define WM8580_SPDTXCHAN2                    0x20
+#define WM8580_SPDTXCHAN3                    0x21
+#define WM8580_SPDTXCHAN4                    0x22
+#define WM8580_SPDTXCHAN5                    0x23
+#define WM8580_SPDMODE                       0x24
+#define WM8580_INTMASK                       0x25
+#define WM8580_GPO1                          0x26
+#define WM8580_GPO2                          0x27
+#define WM8580_GPO3                          0x28
+#define WM8580_GPO4                          0x29
+#define WM8580_GPO5                          0x2A
+#define WM8580_INTSTAT                       0x2B
+#define WM8580_SPDRXCHAN1                    0x2C
+#define WM8580_SPDRXCHAN2                    0x2D
+#define WM8580_SPDRXCHAN3                    0x2E
+#define WM8580_SPDRXCHAN4                    0x2F
+#define WM8580_SPDRXCHAN5                    0x30
+#define WM8580_SPDSTAT                       0x31
+#define WM8580_PWRDN1                        0x32
+#define WM8580_PWRDN2                        0x33
+#define WM8580_READBACK                      0x34
+#define WM8580_RESET                         0x35
+
+/* PLLB4 (register 7h) */
+#define WM8580_PLLB4_MCLKOUTSRC_MASK   0x60
+#define WM8580_PLLB4_MCLKOUTSRC_PLLA   0x20
+#define WM8580_PLLB4_MCLKOUTSRC_PLLB   0x40
+#define WM8580_PLLB4_MCLKOUTSRC_OSC    0x60
+
+#define WM8580_PLLB4_CLKOUTSRC_MASK    0x180
+#define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080
+#define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100
+#define WM8580_PLLB4_CLKOUTSRC_OSCCLK  0x180
+
+/* CLKSEL (register 8h) */
+#define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03
+#define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01
+#define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02
+
+/* AIF control 1 (registers 9h-bh) */
+#define WM8580_AIF_RATE_MASK       0x7
+#define WM8580_AIF_RATE_128        0x0
+#define WM8580_AIF_RATE_192        0x1
+#define WM8580_AIF_RATE_256        0x2
+#define WM8580_AIF_RATE_384        0x3
+#define WM8580_AIF_RATE_512        0x4
+#define WM8580_AIF_RATE_768        0x5
+#define WM8580_AIF_RATE_1152       0x6
+
+#define WM8580_AIF_BCLKSEL_MASK   0x18
+#define WM8580_AIF_BCLKSEL_64     0x00
+#define WM8580_AIF_BCLKSEL_128    0x08
+#define WM8580_AIF_BCLKSEL_256    0x10
+#define WM8580_AIF_BCLKSEL_SYSCLK 0x18
+
+#define WM8580_AIF_MS             0x20
+
+#define WM8580_AIF_CLKSRC_MASK    0xc0
+#define WM8580_AIF_CLKSRC_PLLA    0x40
+#define WM8580_AIF_CLKSRC_PLLB    0x40
+#define WM8580_AIF_CLKSRC_MCLK    0xc0
+
+/* AIF control 2 (registers ch-eh) */
+#define WM8580_AIF_FMT_MASK    0x03
+#define WM8580_AIF_FMT_RIGHTJ  0x00
+#define WM8580_AIF_FMT_LEFTJ   0x01
+#define WM8580_AIF_FMT_I2S     0x02
+#define WM8580_AIF_FMT_DSP     0x03
+
+#define WM8580_AIF_LENGTH_MASK   0x0c
+#define WM8580_AIF_LENGTH_16     0x00
+#define WM8580_AIF_LENGTH_20     0x04
+#define WM8580_AIF_LENGTH_24     0x08
+#define WM8580_AIF_LENGTH_32     0x0c
+
+#define WM8580_AIF_LRP         0x10
+#define WM8580_AIF_BCP         0x20
+
+/* Powerdown Register 1 (register 32h) */
+#define WM8580_PWRDN1_PWDN     0x001
+#define WM8580_PWRDN1_ALLDACPD 0x040
+
+/* Powerdown Register 2 (register 33h) */
+#define WM8580_PWRDN2_OSSCPD   0x001
+#define WM8580_PWRDN2_PLLAPD   0x002
+#define WM8580_PWRDN2_PLLBPD   0x004
+#define WM8580_PWRDN2_SPDIFPD  0x008
+#define WM8580_PWRDN2_SPDIFTXD 0x010
+#define WM8580_PWRDN2_SPDIFRXD 0x020
+
+#define WM8580_DAC_CONTROL5_MUTEALL 0x10
+
+/*
+ * wm8580 register cache
+ * We can't read the WM8580 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8580_reg[] = {
+	0x0121, 0x017e, 0x007d, 0x0014, /*R3*/
+	0x0121, 0x017e, 0x007d, 0x0194, /*R7*/
+	0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/
+	0x0182, 0x0082, 0x000a, 0x0024, /*R15*/
+	0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/
+	0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/
+	0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/
+	0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/
+	0x0000, 0x0000, 0x0031, 0x000b, /*R35*/
+	0x0039, 0x0000, 0x0010, 0x0032, /*R39*/
+	0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/
+	0x0000, 0x0000, 0x0000, 0x0000, /*R47*/
+	0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/
+	0x0000, 0x0000 /*R53*/
+};
+
+/*
+ * read wm8580 register cache
+ */
+static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
+	return cache[reg];
+}
+
+/*
+ * write wm8580 register cache
+ */
+static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8580 register space
+ */
+static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
+
+	/* Registers are 9 bits wide */
+	value &= 0x1ff;
+
+	switch (reg) {
+	case WM8580_RESET:
+		/* Uncached */
+		break;
+	default:
+		if (value == wm8580_read_reg_cache(codec, reg))
+			return 0;
+	}
+
+	/* data is
+	 *   D15..D9 WM8580 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8580_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static inline unsigned int wm8580_read(struct snd_soc_codec *codec,
+				       unsigned int reg)
+{
+	switch (reg) {
+	default:
+		return wm8580_read_reg_cache(codec, reg);
+	}
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+
+static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int reg2 = (kcontrol->private_value >> 24) & 0xff;
+	int ret;
+	u16 val;
+
+	/* Clear the register cache so we write without VU set */
+	wm8580_write_reg_cache(codec, reg, 0);
+	wm8580_write_reg_cache(codec, reg2, 0);
+
+	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	/* Now write again with the volume update bit set */
+	val = wm8580_read_reg_cache(codec, reg);
+	wm8580_write(codec, reg, val | 0x0100);
+
+	val = wm8580_read_reg_cache(codec, reg2);
+	wm8580_write(codec, reg2, val | 0x0100);
+
+	return 0;
+}
+
+#define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		SNDRV_CTL_ELEM_ACCESS_READWRITE,  \
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_2r, \
+	.get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \
+	.private_value = (reg_left) | ((shift) << 8)  |		\
+		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+
+static const struct snd_kcontrol_new wm8580_snd_controls[] = {
+SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume",
+			    WM8580_DIGITAL_ATTENUATION_DACL1,
+			    WM8580_DIGITAL_ATTENUATION_DACR1,
+			    0, 0xff, 0, dac_tlv),
+SOC_WM8580_OUT_DOUBLE_R_TLV("DAC2 Playback Volume",
+			    WM8580_DIGITAL_ATTENUATION_DACL2,
+			    WM8580_DIGITAL_ATTENUATION_DACR2,
+			    0, 0xff, 0, dac_tlv),
+SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume",
+			    WM8580_DIGITAL_ATTENUATION_DACL3,
+			    WM8580_DIGITAL_ATTENUATION_DACR3,
+			    0, 0xff, 0, dac_tlv),
+
+SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0),
+SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0),
+SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0),
+
+SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4,  0, 1, 1, 0),
+SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4,  2, 3, 1, 0),
+SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4,  4, 5, 1, 0),
+
+SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0),
+SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 0),
+SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 0),
+SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 0),
+
+SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0),
+SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
+};
+
+/* Add non-DAPM controls */
+static int wm8580_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&wm8580_snd_controls[i],
+					       codec, NULL));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
+SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
+SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1),
+
+SND_SOC_DAPM_OUTPUT("VOUT1L"),
+SND_SOC_DAPM_OUTPUT("VOUT1R"),
+SND_SOC_DAPM_OUTPUT("VOUT2L"),
+SND_SOC_DAPM_OUTPUT("VOUT2R"),
+SND_SOC_DAPM_OUTPUT("VOUT3L"),
+SND_SOC_DAPM_OUTPUT("VOUT3R"),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1),
+
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{ "VOUT1L", NULL, "DAC1" },
+	{ "VOUT1R", NULL, "DAC1" },
+
+	{ "VOUT2L", NULL, "DAC2" },
+	{ "VOUT2R", NULL, "DAC2" },
+
+	{ "VOUT3L", NULL, "DAC3" },
+	{ "VOUT3R", NULL, "DAC3" },
+
+	{ "ADC", NULL, "AINL" },
+	{ "ADC", NULL, "AINR" },
+};
+
+static int wm8580_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets,
+				  ARRAY_SIZE(wm8580_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+	u32 prescale:1;
+	u32 postscale:1;
+	u32 freqmode:2;
+	u32 n:4;
+	u32 k:24;
+};
+
+/* The size in bits of the pll divide */
+#define FIXED_PLL_SIZE (1 << 22)
+
+/* PLL rate to output rate divisions */
+static struct {
+	unsigned int div;
+	unsigned int freqmode;
+	unsigned int postscale;
+} post_table[] = {
+	{  2,  0, 0 },
+	{  4,  0, 1 },
+	{  4,  1, 0 },
+	{  8,  1, 1 },
+	{  8,  2, 0 },
+	{ 16,  2, 1 },
+	{ 12,  3, 0 },
+	{ 24,  3, 1 }
+};
+
+static int pll_factors(struct _pll_div *pll_div, unsigned int target,
+		       unsigned int source)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod;
+	int i;
+
+	pr_debug("wm8580: PLL %dHz->%dHz\n", source, target);
+
+	/* Scale the output frequency up; the PLL should run in the
+	 * region of 90-100MHz.
+	 */
+	for (i = 0; i < ARRAY_SIZE(post_table); i++) {
+		if (target * post_table[i].div >=  90000000 &&
+		    target * post_table[i].div <= 100000000) {
+			pll_div->freqmode = post_table[i].freqmode;
+			pll_div->postscale = post_table[i].postscale;
+			target *= post_table[i].div;
+			break;
+		}
+	}
+
+	if (i == ARRAY_SIZE(post_table)) {
+		printk(KERN_ERR "wm8580: Unable to scale output frequency "
+		       "%u\n", target);
+		return -EINVAL;
+	}
+
+	Ndiv = target / source;
+
+	if (Ndiv < 5) {
+		source /= 2;
+		pll_div->prescale = 1;
+		Ndiv = target / source;
+	} else
+		pll_div->prescale = 0;
+
+	if ((Ndiv < 5) || (Ndiv > 13)) {
+		printk(KERN_ERR
+			"WM8580 N=%d outside supported range\n", Ndiv);
+		return -EINVAL;
+	}
+
+	pll_div->n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	pll_div->k = K;
+
+	pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n",
+		 pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode,
+		 pll_div->postscale);
+
+	return 0;
+}
+
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	int offset;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8580_priv *wm8580 = codec->private_data;
+	struct pll_state *state;
+	struct _pll_div pll_div;
+	unsigned int reg;
+	unsigned int pwr_mask;
+	int ret;
+
+	/* GCC isn't able to work out the ifs below for initialising/using
+	 * pll_div so suppress warnings.
+	 */
+	memset(&pll_div, 0, sizeof(pll_div));
+
+	switch (pll_id) {
+	case WM8580_PLLA:
+		state = &wm8580->a;
+		offset = 0;
+		pwr_mask = WM8580_PWRDN2_PLLAPD;
+		break;
+	case WM8580_PLLB:
+		state = &wm8580->b;
+		offset = 4;
+		pwr_mask = WM8580_PWRDN2_PLLBPD;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	if (freq_in && freq_out) {
+		ret = pll_factors(&pll_div, freq_out, freq_in);
+		if (ret != 0)
+			return ret;
+	}
+
+	state->in = freq_in;
+	state->out = freq_out;
+
+	/* Always disable the PLL - it is not safe to leave it running
+	 * while reprogramming it.
+	 */
+	reg = wm8580_read(codec, WM8580_PWRDN2);
+	wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask);
+
+	if (!freq_in || !freq_out)
+		return 0;
+
+	wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+	wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
+	wm8580_write(codec, WM8580_PLLA3 + offset,
+		     (pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
+
+	reg = wm8580_read(codec, WM8580_PLLA4 + offset);
+	reg &= ~0x3f;
+	reg |= pll_div.prescale | pll_div.postscale << 1 |
+		pll_div.freqmode << 4;
+
+	wm8580_write(codec, WM8580_PLLA4 + offset, reg);
+
+	/* All done, turn it on */
+	reg = wm8580_read(codec, WM8580_PWRDN2);
+	wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8580_paif_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_dai_link *dai = rtd->dai;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id);
+
+	paifb &= ~WM8580_AIF_LENGTH_MASK;
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		paifb |= WM8580_AIF_LENGTH_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		paifb |= WM8580_AIF_LENGTH_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		paifb |= WM8580_AIF_LENGTH_24;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
+	return 0;
+}
+
+static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
+				      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int aifa;
+	unsigned int aifb;
+	int can_invert_lrclk;
+
+	aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id);
+	aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id);
+
+	aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		aifa &= ~WM8580_AIF_MS;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aifa |= WM8580_AIF_MS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		can_invert_lrclk = 1;
+		aifb |= WM8580_AIF_FMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		can_invert_lrclk = 1;
+		aifb |= WM8580_AIF_FMT_RIGHTJ;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		can_invert_lrclk = 1;
+		aifb |= WM8580_AIF_FMT_LEFTJ;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		can_invert_lrclk = 0;
+		aifb |= WM8580_AIF_FMT_DSP;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		can_invert_lrclk = 0;
+		aifb |= WM8580_AIF_FMT_DSP;
+		aifb |= WM8580_AIF_LRP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF:
+		if (!can_invert_lrclk)
+			return -EINVAL;
+		aifb |= WM8580_AIF_BCP;
+		aifb |= WM8580_AIF_LRP;
+		break;
+
+	case SND_SOC_DAIFMT_IB_NF:
+		aifb |= WM8580_AIF_BCP;
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		if (!can_invert_lrclk)
+			return -EINVAL;
+		aifb |= WM8580_AIF_LRP;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
+	wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
+
+	return 0;
+}
+
+static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+				 int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int reg;
+
+	switch (div_id) {
+	case WM8580_MCLK:
+		reg = wm8580_read(codec, WM8580_PLLB4);
+		reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
+
+		switch (div) {
+		case WM8580_CLKSRC_MCLK:
+			/* Input */
+			break;
+
+		case WM8580_CLKSRC_PLLA:
+			reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA;
+			break;
+		case WM8580_CLKSRC_PLLB:
+			reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB;
+			break;
+
+		case WM8580_CLKSRC_OSC:
+			reg |= WM8580_PLLB4_MCLKOUTSRC_OSC;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		wm8580_write(codec, WM8580_PLLB4, reg);
+		break;
+
+	case WM8580_DAC_CLKSEL:
+		reg = wm8580_read(codec, WM8580_CLKSEL);
+		reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK;
+
+		switch (div) {
+		case WM8580_CLKSRC_MCLK:
+			break;
+
+		case WM8580_CLKSRC_PLLA:
+			reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA;
+			break;
+
+		case WM8580_CLKSRC_PLLB:
+			reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		wm8580_write(codec, WM8580_CLKSEL, reg);
+		break;
+
+	case WM8580_CLKOUTSRC:
+		reg = wm8580_read(codec, WM8580_PLLB4);
+		reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
+
+		switch (div) {
+		case WM8580_CLKSRC_NONE:
+			break;
+
+		case WM8580_CLKSRC_PLLA:
+			reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK;
+			break;
+
+		case WM8580_CLKSRC_PLLB:
+			reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK;
+			break;
+
+		case WM8580_CLKSRC_OSC:
+			reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		wm8580_write(codec, WM8580_PLLB4, reg);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int reg;
+
+	reg = wm8580_read(codec, WM8580_DAC_CONTROL5);
+
+	if (mute)
+		reg |= WM8580_DAC_CONTROL5_MUTEALL;
+	else
+		reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
+
+	wm8580_write(codec, WM8580_DAC_CONTROL5, reg);
+
+	return 0;
+}
+
+static int wm8580_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 reg;
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		break;
+	case SND_SOC_BIAS_OFF:
+		reg = wm8580_read(codec, WM8580_PWRDN1);
+		wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8580_dai[] = {
+	{
+		.name = "WM8580 PAIFRX",
+		.id = 0,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 6,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = WM8580_FORMATS,
+		},
+		.ops = {
+			 .hw_params = wm8580_paif_hw_params,
+		 },
+		.dai_ops = {
+			 .set_fmt = wm8580_set_paif_dai_fmt,
+			 .set_clkdiv = wm8580_set_dai_clkdiv,
+			 .set_pll = wm8580_set_dai_pll,
+			 .digital_mute = wm8580_digital_mute,
+		 },
+	},
+	{
+		.name = "WM8580 PAIFTX",
+		.id = 1,
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = WM8580_FORMATS,
+		},
+		.ops = {
+			 .hw_params = wm8580_paif_hw_params,
+		 },
+		.dai_ops = {
+			 .set_fmt = wm8580_set_paif_dai_fmt,
+			 .set_clkdiv = wm8580_set_dai_clkdiv,
+			 .set_pll = wm8580_set_dai_pll,
+		 },
+	},
+};
+EXPORT_SYMBOL_GPL(wm8580_dai);
+
+/*
+ * initialise the WM8580 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8580_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "WM8580";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8580_read_reg_cache;
+	codec->write = wm8580_write;
+	codec->set_bias_level = wm8580_set_bias_level;
+	codec->dai = wm8580_dai;
+	codec->num_dai = ARRAY_SIZE(wm8580_dai);
+	codec->reg_cache_size = ARRAY_SIZE(wm8580_reg);
+	codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg),
+				   GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* Get the codec into a known state */
+	wm8580_write(codec, WM8580_RESET, 0);
+
+	/* Power up and get individual control of the DACs */
+	wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) &
+		     ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD));
+
+	/* Make VMID high impedence */
+	wm8580_write(codec, WM8580_ADC_CONTROL1,
+		     wm8580_read(codec,  WM8580_ADC_CONTROL1) & ~0x100);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,
+			       SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8580: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	wm8580_add_controls(codec);
+	wm8580_add_widgets(codec);
+
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8580: failed to register card\n");
+		goto card_err;
+	}
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+static struct snd_soc_device *wm8580_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8580 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8580_i2c_driver;
+static struct i2c_client client_template;
+
+static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct snd_soc_device *socdev = wm8580_socdev;
+	struct wm8580_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c;
+	int ret;
+
+	if (addr != setup->i2c_address)
+		return -ENODEV;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr);
+		goto err;
+	}
+
+	ret = wm8580_init(socdev);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "failed to initialise WM8580\n");
+		goto err;
+	}
+
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int wm8580_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	i2c_detach_client(client);
+	kfree(codec->reg_cache);
+	kfree(client);
+	return 0;
+}
+
+static int wm8580_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8580_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8580_i2c_driver = {
+	.driver = {
+		.name = "WM8580 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.attach_adapter = wm8580_i2c_attach,
+	.detach_client =  wm8580_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8580",
+	.driver = &wm8580_i2c_driver,
+};
+#endif
+
+static int wm8580_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8580_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct wm8580_priv *wm8580;
+	int ret = 0;
+
+	pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
+	if (wm8580 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = wm8580;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	wm8580_socdev = socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		normal_i2c[0] = setup->i2c_address;
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = i2c_add_driver(&wm8580_i2c_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add i2c driver");
+	}
+#else
+		/* Add other interfaces here */
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int wm8580_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8580_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8580 = {
+	.probe = 	wm8580_probe,
+	.remove = 	wm8580_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
+
+MODULE_DESCRIPTION("ASoC WM8580 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h
new file mode 100644
index 0000000..589ddab
--- /dev/null
+++ b/sound/soc/codecs/wm8580.h
@@ -0,0 +1,42 @@
+/*
+ * wm8580.h  --  audio driver for WM8580
+ *
+ * Copyright 2008 Samsung Electronics.
+ * Author: Ryu Euiyoul
+ *         ryu.real@gmail.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.
+ *
+ */
+
+#ifndef _WM8580_H
+#define _WM8580_H
+
+#define WM8580_PLLA  1
+#define WM8580_PLLB  2
+
+#define WM8580_MCLK       1
+#define WM8580_DAC_CLKSEL 2
+#define WM8580_CLKOUTSRC  3
+
+#define WM8580_CLKSRC_MCLK 1
+#define WM8580_CLKSRC_PLLA 2
+#define WM8580_CLKSRC_PLLB 3
+#define WM8580_CLKSRC_OSC  4
+#define WM8580_CLKSRC_NONE 5
+
+struct wm8580_setup_data {
+	unsigned short i2c_address;
+};
+
+#define WM8580_DAI_PAIFRX 0
+#define WM8580_DAI_PAIFTX 1
+
+extern struct snd_soc_dai wm8580_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_wm8580;
+
+#endif
+
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 9402fca..7b64d9a 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -570,88 +571,144 @@
  *    low  = 0x1a
  *    high = 0x1b
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
 
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8731_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
-   around */
-
-static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8731_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = wm8731_socdev;
-	struct wm8731_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		pr_err("failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = wm8731_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		pr_err("failed to initialise WM8731\n");
-		goto err;
-	}
-	return ret;
 
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int wm8731_i2c_detach(struct i2c_client *client)
+static int wm8731_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int wm8731_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8731_codec_probe);
-}
+static const struct i2c_device_id wm8731_i2c_id[] = {
+	{ "wm8731", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8731_i2c_driver = {
 	.driver = {
 		.name = "WM8731 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_WM8731,
-	.attach_adapter = wm8731_i2c_attach,
-	.detach_client =  wm8731_i2c_detach,
-	.command =        NULL,
+	.probe =    wm8731_i2c_probe,
+	.remove =   wm8731_i2c_remove,
+	.id_table = wm8731_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "WM8731",
-	.driver = &wm8731_i2c_driver,
-};
+static int wm8731_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8731_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8731_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8731_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8731_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_device *socdev = wm8731_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	codec->control_data = spi;
+
+	ret = wm8731_init(socdev);
+	if (ret < 0)
+		dev_err(&spi->dev, "failed to initialise WM8731\n");
+
+	return ret;
+}
+
+static int __devexit wm8731_spi_remove(struct spi_device *spi)
+{
+	return 0;
+}
+
+static struct spi_driver wm8731_spi_driver = {
+	.driver = {
+		.name	= "wm8731",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8731_spi_probe,
+	.remove		= __devexit_p(wm8731_spi_remove),
+};
+
+static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[2];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[0];
+	msg[1] = data[1];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
 static int wm8731_probe(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -680,16 +737,21 @@
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
 	wm8731_socdev = socdev;
+	ret = -ENODEV;
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8731_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = wm8731_add_i2c_device(pdev, setup);
 	}
-#else
-	/* Add other interfaces here */
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	if (setup->spi) {
+		codec->hw_write = (hw_write_t)wm8731_spi_write;
+		ret = spi_register_driver(&wm8731_spi_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add spi driver");
+	}
 #endif
 
 	if (ret != 0) {
@@ -711,8 +773,12 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&wm8731_i2c_driver);
 #endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&wm8731_spi_driver);
+#endif
 	kfree(codec->private_data);
 	kfree(codec);
 
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 99f2e3c..95190e9 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -35,6 +35,8 @@
 #define WM8731_DAI		0
 
 struct wm8731_setup_data {
+	int            spi;
+	int            i2c_bus;
 	unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index dd1f554..4892e39 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -841,88 +842,147 @@
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 
 /*
- * WM8731 2 wire address is determined by GPIO5
+ * WM8750 2 wire address is determined by GPIO5
  * state during powerup.
  *    low  = 0x1a
  *    high = 0x1b
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
 
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8750_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8750_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = wm8750_socdev;
-	struct wm8750_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		pr_err("failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = wm8750_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		pr_err("failed to initialise WM8750\n");
-		goto err;
-	}
-	return ret;
 
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int wm8750_i2c_detach(struct i2c_client *client)
+static int wm8750_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int wm8750_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8750_codec_probe);
-}
+static const struct i2c_device_id wm8750_i2c_id[] = {
+	{ "wm8750", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8750_i2c_driver = {
 	.driver = {
 		.name = "WM8750 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_WM8750,
-	.attach_adapter = wm8750_i2c_attach,
-	.detach_client =  wm8750_i2c_detach,
-	.command =        NULL,
+	.probe =    wm8750_i2c_probe,
+	.remove =   wm8750_i2c_remove,
+	.id_table = wm8750_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "WM8750",
-	.driver = &wm8750_i2c_driver,
+static int wm8750_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8750_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8750_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8750", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8750_i2c_driver);
+	return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8750_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_device *socdev = wm8750_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	codec->control_data = spi;
+
+	ret = wm8750_init(socdev);
+	if (ret < 0)
+		dev_err(&spi->dev, "failed to initialise WM8750\n");
+
+	return ret;
+}
+
+static int __devexit wm8750_spi_remove(struct spi_device *spi)
+{
+	return 0;
+}
+
+static struct spi_driver wm8750_spi_driver = {
+	.driver = {
+		.name	= "wm8750",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8750_spi_probe,
+	.remove		= __devexit_p(wm8750_spi_remove),
 };
+
+static int wm8750_spi_write(struct spi_device *spi, const char *data, int len)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[2];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[0];
+	msg[1] = data[1];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
 #endif
 
 static int wm8750_probe(struct platform_device *pdev)
@@ -931,7 +991,7 @@
 	struct wm8750_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec;
 	struct wm8750_priv *wm8750;
-	int ret = 0;
+	int ret;
 
 	pr_info("WM8750 Audio Codec %s", WM8750_VERSION);
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -952,16 +1012,21 @@
 	wm8750_socdev = socdev;
 	INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);
 
+	ret = -ENODEV;
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8750_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = wm8750_add_i2c_device(pdev, setup);
 	}
-#else
-		/* Add other interfaces here */
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	if (setup->spi) {
+		codec->hw_write = (hw_write_t)wm8750_spi_write;
+		ret = spi_register_driver(&wm8750_spi_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add spi driver");
+	}
 #endif
 
 	if (ret != 0) {
@@ -1002,8 +1067,12 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&wm8750_i2c_driver);
 #endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&wm8750_spi_driver);
+#endif
 	kfree(codec->private_data);
 	kfree(codec);
 
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
index 8ef30e6..1dc100e 100644
--- a/sound/soc/codecs/wm8750.h
+++ b/sound/soc/codecs/wm8750.h
@@ -58,6 +58,8 @@
 #define WM8750_SYSCLK	0
 
 struct wm8750_setup_data {
+	int spi;
+	int i2c_bus;
 	unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index e873414..8c4df44 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1637,84 +1637,86 @@
  *    low  = 0x1a
  *    high = 0x1b
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
 
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8753_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8753_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = wm8753_socdev;
-	struct wm8753_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (!i2c)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		pr_err("failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = wm8753_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		pr_err("failed to initialise WM8753\n");
-		goto err;
-	}
 
 	return ret;
-
-err:
-	kfree(i2c);
-	return ret;
 }
 
-static int wm8753_i2c_detach(struct i2c_client *client)
+static int wm8753_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int wm8753_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8753_codec_probe);
-}
+static const struct i2c_device_id wm8753_i2c_id[] = {
+	{ "wm8753", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8753_i2c_driver = {
 	.driver = {
 		.name = "WM8753 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_WM8753,
-	.attach_adapter = wm8753_i2c_attach,
-	.detach_client =  wm8753_i2c_detach,
-	.command =        NULL,
+	.probe =    wm8753_i2c_probe,
+	.remove =   wm8753_i2c_remove,
+	.id_table = wm8753_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "WM8753",
-	.driver = &wm8753_i2c_driver,
-};
+static int wm8753_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8753_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8753_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8753", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8753_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
 static int wm8753_probe(struct platform_device *pdev)
@@ -1748,11 +1750,8 @@
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8753_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = wm8753_add_i2c_device(pdev, setup);
 	}
 #else
 		/* Add other interfaces here */
@@ -1796,6 +1795,7 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&wm8753_i2c_driver);
 #endif
 	kfree(codec->private_data);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 44f5f1f..7defde0 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -79,6 +79,7 @@
 #define WM8753_ADCTL2		0x3f
 
 struct wm8753_setup_data {
+	int i2c_bus;
 	unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
new file mode 100644
index 0000000..0b8c6d3
--- /dev/null
+++ b/sound/soc/codecs/wm8900.c
@@ -0,0 +1,1542 @@
+/*
+ * wm8900.c  --  WM8900 ALSA Soc Audio driver
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * TODO:
+ *  - Tristating.
+ *  - TDM.
+ *  - Jack detect.
+ *  - FLL source configuration, currently only MCLK is supported.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8900.h"
+
+/* WM8900 register space */
+#define WM8900_REG_RESET	0x0
+#define WM8900_REG_ID		0x0
+#define WM8900_REG_POWER1	0x1
+#define WM8900_REG_POWER2	0x2
+#define WM8900_REG_POWER3	0x3
+#define WM8900_REG_AUDIO1	0x4
+#define WM8900_REG_AUDIO2	0x5
+#define WM8900_REG_CLOCKING1    0x6
+#define WM8900_REG_CLOCKING2    0x7
+#define WM8900_REG_AUDIO3       0x8
+#define WM8900_REG_AUDIO4       0x9
+#define WM8900_REG_DACCTRL      0xa
+#define WM8900_REG_LDAC_DV      0xb
+#define WM8900_REG_RDAC_DV      0xc
+#define WM8900_REG_SIDETONE     0xd
+#define WM8900_REG_ADCCTRL      0xe
+#define WM8900_REG_LADC_DV	0xf
+#define WM8900_REG_RADC_DV      0x10
+#define WM8900_REG_GPIO         0x12
+#define WM8900_REG_INCTL	0x15
+#define WM8900_REG_LINVOL	0x16
+#define WM8900_REG_RINVOL	0x17
+#define WM8900_REG_INBOOSTMIX1  0x18
+#define WM8900_REG_INBOOSTMIX2  0x19
+#define WM8900_REG_ADCPATH	0x1a
+#define WM8900_REG_AUXBOOST	0x1b
+#define WM8900_REG_ADDCTL       0x1e
+#define WM8900_REG_FLLCTL1      0x24
+#define WM8900_REG_FLLCTL2      0x25
+#define WM8900_REG_FLLCTL3      0x26
+#define WM8900_REG_FLLCTL4      0x27
+#define WM8900_REG_FLLCTL5      0x28
+#define WM8900_REG_FLLCTL6      0x29
+#define WM8900_REG_LOUTMIXCTL1  0x2c
+#define WM8900_REG_ROUTMIXCTL1  0x2d
+#define WM8900_REG_BYPASS1	0x2e
+#define WM8900_REG_BYPASS2	0x2f
+#define WM8900_REG_AUXOUT_CTL   0x30
+#define WM8900_REG_LOUT1CTL     0x33
+#define WM8900_REG_ROUT1CTL     0x34
+#define WM8900_REG_LOUT2CTL	0x35
+#define WM8900_REG_ROUT2CTL	0x36
+#define WM8900_REG_HPCTL1	0x3a
+#define WM8900_REG_OUTBIASCTL   0x73
+
+#define WM8900_MAXREG		0x80
+
+#define WM8900_REG_ADDCTL_OUT1_DIS    0x80
+#define WM8900_REG_ADDCTL_OUT2_DIS    0x40
+#define WM8900_REG_ADDCTL_VMID_DIS    0x20
+#define WM8900_REG_ADDCTL_BIAS_SRC    0x10
+#define WM8900_REG_ADDCTL_VMID_SOFTST 0x04
+#define WM8900_REG_ADDCTL_TEMP_SD     0x02
+
+#define WM8900_REG_GPIO_TEMP_ENA   0x2
+
+#define WM8900_REG_POWER1_STARTUP_BIAS_ENA 0x0100
+#define WM8900_REG_POWER1_BIAS_ENA         0x0008
+#define WM8900_REG_POWER1_VMID_BUF_ENA     0x0004
+#define WM8900_REG_POWER1_FLL_ENA          0x0040
+
+#define WM8900_REG_POWER2_SYSCLK_ENA  0x8000
+#define WM8900_REG_POWER2_ADCL_ENA    0x0002
+#define WM8900_REG_POWER2_ADCR_ENA    0x0001
+
+#define WM8900_REG_POWER3_DACL_ENA    0x0002
+#define WM8900_REG_POWER3_DACR_ENA    0x0001
+
+#define WM8900_REG_AUDIO1_AIF_FMT_MASK 0x0018
+#define WM8900_REG_AUDIO1_LRCLK_INV    0x0080
+#define WM8900_REG_AUDIO1_BCLK_INV     0x0100
+
+#define WM8900_REG_CLOCKING1_BCLK_DIR   0x1
+#define WM8900_REG_CLOCKING1_MCLK_SRC   0x100
+#define WM8900_REG_CLOCKING1_BCLK_MASK  (~0x01e)
+#define WM8900_REG_CLOCKING1_OPCLK_MASK (~0x7000)
+
+#define WM8900_REG_CLOCKING2_ADC_CLKDIV 0xe0
+#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
+
+#define WM8900_REG_DACCTRL_MUTE          0x004
+#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
+
+#define WM8900_REG_AUDIO3_ADCLRC_DIR    0x0800
+
+#define WM8900_REG_AUDIO4_DACLRC_DIR    0x0800
+
+#define WM8900_REG_FLLCTL1_OSC_ENA    0x100
+
+#define WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF 0x100
+
+#define WM8900_REG_HPCTL1_HP_IPSTAGE_ENA 0x80
+#define WM8900_REG_HPCTL1_HP_OPSTAGE_ENA 0x40
+#define WM8900_REG_HPCTL1_HP_CLAMP_IP    0x20
+#define WM8900_REG_HPCTL1_HP_CLAMP_OP    0x10
+#define WM8900_REG_HPCTL1_HP_SHORT       0x08
+#define WM8900_REG_HPCTL1_HP_SHORT2      0x04
+
+#define WM8900_LRC_MASK 0xfc00
+
+struct snd_soc_codec_device soc_codec_dev_wm8900;
+
+struct wm8900_priv {
+	u32 fll_in; /* FLL input frequency */
+	u32 fll_out; /* FLL output frequency */
+};
+
+/*
+ * wm8900 register cache.  We can't read the entire register space and we
+ * have slow control buses so we cache the registers.
+ */
+static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
+	0x8900, 0x0000,
+	0xc000, 0x0000,
+	0x4050, 0x4000,
+	0x0008, 0x0000,
+	0x0040, 0x0040,
+	0x1004, 0x00c0,
+	0x00c0, 0x0000,
+	0x0100, 0x00c0,
+	0x00c0, 0x0000,
+	0xb001, 0x0000,
+	0x0000, 0x0044,
+	0x004c, 0x004c,
+	0x0044, 0x0044,
+	0x0000, 0x0044,
+	0x0000, 0x0000,
+	0x0002, 0x0000,
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+	0x0008, 0x0000,
+	0x0000, 0x0008,
+	0x0097, 0x0100,
+	0x0000, 0x0000,
+	0x0050, 0x0050,
+	0x0055, 0x0055,
+	0x0055, 0x0000,
+	0x0000, 0x0079,
+	0x0079, 0x0079,
+	0x0079, 0x0000,
+	/* Remaining registers all zero */
+};
+
+/*
+ * read wm8900 register cache
+ */
+static inline unsigned int wm8900_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+
+	BUG_ON(reg >= WM8900_MAXREG);
+
+	if (reg == WM8900_REG_ID)
+		return 0;
+
+	return cache[reg];
+}
+
+/*
+ * write wm8900 register cache
+ */
+static inline void wm8900_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+
+	BUG_ON(reg >= WM8900_MAXREG);
+
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8900 register space
+ */
+static int wm8900_write(struct snd_soc_codec *codec, unsigned int reg,
+			unsigned int value)
+{
+	u8 data[3];
+
+	if (value == wm8900_read_reg_cache(codec, reg))
+		return 0;
+
+	/* data is
+	 *   D15..D9 WM8900 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = reg;
+	data[1] = value >> 8;
+	data[2] = value & 0x00ff;
+
+	wm8900_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 3) == 3)
+		return 0;
+	else
+		return -EIO;
+}
+
+/*
+ * Read from the wm8900.
+ */
+static unsigned int wm8900_chip_read(struct snd_soc_codec *codec, u8 reg)
+{
+	struct i2c_msg xfer[2];
+	u16 data;
+	int ret;
+	struct i2c_client *client = codec->control_data;
+
+	BUG_ON(reg != WM8900_REG_ID && reg != WM8900_REG_POWER1);
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 2;
+	xfer[1].buf = (u8 *)&data;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret != 2) {
+		printk(KERN_CRIT "i2c_transfer returned %d\n", ret);
+		return 0;
+	}
+
+	return (data >> 8) | ((data & 0xff) << 8);
+}
+
+/*
+ * Read from the WM8900 register space.  Most registers can't be read
+ * and are therefore supplied from cache.
+ */
+static unsigned int wm8900_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+	switch (reg) {
+	case WM8900_REG_ID:
+		return wm8900_chip_read(codec, reg);
+	default:
+		return wm8900_read_reg_cache(codec, reg);
+	}
+}
+
+static void wm8900_reset(struct snd_soc_codec *codec)
+{
+	wm8900_write(codec, WM8900_REG_RESET, 0);
+
+	memcpy(codec->reg_cache, wm8900_reg_defaults,
+	       sizeof(codec->reg_cache));
+}
+
+static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
+			   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u16 hpctl1 = wm8900_read(codec, WM8900_REG_HPCTL1);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Clamp headphone outputs */
+		hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
+			WM8900_REG_HPCTL1_HP_CLAMP_OP;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		break;
+
+	case SND_SOC_DAPM_POST_PMU:
+		/* Enable the input stage */
+		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP;
+		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT |
+			WM8900_REG_HPCTL1_HP_SHORT2 |
+			WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+		msleep(400);
+
+		/* Enable the output stage */
+		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
+		hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+		/* Remove the shorts */
+		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		/* Short the output */
+		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+		/* Disable the output stage */
+		hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+
+		/* Clamp the outputs and power down input */
+		hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
+			WM8900_REG_HPCTL1_HP_CLAMP_OP;
+		hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
+		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		/* Disable everything */
+		wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+		break;
+
+	default:
+		BUG();
+	}
+
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_boost_tlv, -1200, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1);
+
+static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3600, 300, 0);
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+
+static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" };
+
+static const struct soc_enum mic_bias_level =
+SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt);
+
+static const char *dac_mute_rate_txt[] = { "Fast", "Slow" };
+
+static const struct soc_enum dac_mute_rate =
+SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 7, 2, dac_mute_rate_txt);
+
+static const char *dac_deemphasis_txt[] = {
+	"Disabled", "32kHz", "44.1kHz", "48kHz"
+};
+
+static const struct soc_enum dac_deemphasis =
+SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 4, 4, dac_deemphasis_txt);
+
+static const char *adc_hpf_cut_txt[] = {
+	"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"
+};
+
+static const struct soc_enum adc_hpf_cut =
+SOC_ENUM_SINGLE(WM8900_REG_ADCCTRL, 5, 4, adc_hpf_cut_txt);
+
+static const char *lr_txt[] = {
+	"Left", "Right"
+};
+
+static const struct soc_enum aifl_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 15, 2, lr_txt);
+
+static const struct soc_enum aifr_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 14, 2, lr_txt);
+
+static const struct soc_enum dacl_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 15, 2, lr_txt);
+
+static const struct soc_enum dacr_src =
+SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 14, 2, lr_txt);
+
+static const char *sidetone_txt[] = {
+	"Disabled", "Left ADC", "Right ADC"
+};
+
+static const struct soc_enum dacl_sidetone =
+SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 2, 3, sidetone_txt);
+
+static const struct soc_enum dacr_sidetone =
+SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 0, 3, sidetone_txt);
+
+static const struct snd_kcontrol_new wm8900_snd_controls[] = {
+SOC_ENUM("Mic Bias Level", mic_bias_level),
+
+SOC_SINGLE_TLV("Left Input PGA Volume", WM8900_REG_LINVOL, 0, 31, 0,
+	       in_pga_tlv),
+SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1),
+SOC_SINGLE("Left Input PGA ZC Switch", WM8900_REG_LINVOL, 7, 1, 0),
+
+SOC_SINGLE_TLV("Right Input PGA Volume", WM8900_REG_RINVOL, 0, 31, 0,
+	       in_pga_tlv),
+SOC_SINGLE("Right Input PGA Switch", WM8900_REG_RINVOL, 6, 1, 1),
+SOC_SINGLE("Right Input PGA ZC Switch", WM8900_REG_RINVOL, 7, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1),
+SOC_ENUM("DAC Mute Rate", dac_mute_rate),
+SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemphasis),
+SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0),
+SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
+	   12, 1, 0),
+
+SOC_SINGLE("ADC HPF Switch", WM8900_REG_ADCCTRL, 8, 1, 0),
+SOC_ENUM("ADC HPF Cut-Off", adc_hpf_cut),
+SOC_DOUBLE("ADC Invert Switch", WM8900_REG_ADCCTRL, 1, 0, 1, 0),
+SOC_SINGLE_TLV("Left ADC Sidetone Volume", WM8900_REG_SIDETONE, 9, 12, 0,
+	       adc_svol_tlv),
+SOC_SINGLE_TLV("Right ADC Sidetone Volume", WM8900_REG_SIDETONE, 5, 12, 0,
+	       adc_svol_tlv),
+SOC_ENUM("Left Digital Audio Source", aifl_src),
+SOC_ENUM("Right Digital Audio Source", aifr_src),
+
+SOC_SINGLE_TLV("DAC Input Boost Volume", WM8900_REG_AUDIO2, 10, 4, 0,
+	       dac_boost_tlv),
+SOC_ENUM("Left DAC Source", dacl_src),
+SOC_ENUM("Right DAC Source", dacr_src),
+SOC_ENUM("Left DAC Sidetone", dacl_sidetone),
+SOC_ENUM("Right DAC Sidetone", dacr_sidetone),
+SOC_DOUBLE("DAC Invert Switch", WM8900_REG_DACCTRL, 1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume",
+		 WM8900_REG_LDAC_DV, WM8900_REG_RDAC_DV,
+		 1, 96, 0, dac_tlv),
+SOC_DOUBLE_R_TLV("Digital Capture Volume",
+		 WM8900_REG_LADC_DV, WM8900_REG_RADC_DV, 1, 119, 0, adc_tlv),
+
+SOC_SINGLE_TLV("LINPUT3 Bypass Volume", WM8900_REG_LOUTMIXCTL1, 4, 7, 0,
+	       out_mix_tlv),
+SOC_SINGLE_TLV("RINPUT3 Bypass Volume", WM8900_REG_ROUTMIXCTL1, 4, 7, 0,
+	       out_mix_tlv),
+SOC_SINGLE_TLV("Left AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 4, 7, 0,
+	       out_mix_tlv),
+SOC_SINGLE_TLV("Right AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 0, 7, 0,
+	       out_mix_tlv),
+
+SOC_SINGLE_TLV("LeftIn to RightOut Mixer Volume", WM8900_REG_BYPASS1, 0, 7, 0,
+	       out_mix_tlv),
+SOC_SINGLE_TLV("LeftIn to LeftOut Mixer Volume", WM8900_REG_BYPASS1, 4, 7, 0,
+	       out_mix_tlv),
+SOC_SINGLE_TLV("RightIn to LeftOut Mixer Volume", WM8900_REG_BYPASS2, 0, 7, 0,
+	       out_mix_tlv),
+SOC_SINGLE_TLV("RightIn to RightOut Mixer Volume", WM8900_REG_BYPASS2, 4, 7, 0,
+	       out_mix_tlv),
+
+SOC_SINGLE_TLV("IN2L Boost Volume", WM8900_REG_INBOOSTMIX1, 0, 3, 0,
+	       in_boost_tlv),
+SOC_SINGLE_TLV("IN3L Boost Volume", WM8900_REG_INBOOSTMIX1, 4, 3, 0,
+	       in_boost_tlv),
+SOC_SINGLE_TLV("IN2R Boost Volume", WM8900_REG_INBOOSTMIX2, 0, 3, 0,
+	       in_boost_tlv),
+SOC_SINGLE_TLV("IN3R Boost Volume", WM8900_REG_INBOOSTMIX2, 4, 3, 0,
+	       in_boost_tlv),
+SOC_SINGLE_TLV("Left AUX Boost Volume", WM8900_REG_AUXBOOST, 4, 3, 0,
+	       in_boost_tlv),
+SOC_SINGLE_TLV("Right AUX Boost Volume", WM8900_REG_AUXBOOST, 0, 3, 0,
+	       in_boost_tlv),
+
+SOC_DOUBLE_R_TLV("LINEOUT1 Volume", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+	       0, 63, 0, out_pga_tlv),
+SOC_DOUBLE_R("LINEOUT1 Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+	     6, 1, 1),
+SOC_DOUBLE_R("LINEOUT1 ZC Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL,
+	     7, 1, 0),
+
+SOC_DOUBLE_R_TLV("LINEOUT2 Volume",
+		 WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL,
+		 0, 63, 0, out_pga_tlv),
+SOC_DOUBLE_R("LINEOUT2 Switch",
+	     WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 6, 1, 1),
+SOC_DOUBLE_R("LINEOUT2 ZC Switch",
+	     WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 7, 1, 0),
+SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1,
+	   0, 1, 1),
+
+};
+
+/* add non dapm controls */
+static int wm8900_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&wm8900_snd_controls[i],
+					       codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new wm8900_dapm_loutput2_control =
+SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0);
+
+static const struct snd_kcontrol_new wm8900_dapm_routput2_control =
+SOC_DAPM_SINGLE("LINEOUT2R Switch", WM8900_REG_POWER3, 5, 1, 0);
+
+static const struct snd_kcontrol_new wm8900_loutmix_controls[] = {
+SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0),
+SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0),
+SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_routmix_controls[] = {
+SOC_DAPM_SINGLE("RINPUT3 Bypass Switch", WM8900_REG_ROUTMIXCTL1, 7, 1, 0),
+SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 3, 1, 0),
+SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 3, 1, 0),
+SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 7, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8900_REG_ROUTMIXCTL1, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_linmix_controls[] = {
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INBOOSTMIX1, 2, 1, 1),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INBOOSTMIX1, 6, 1, 1),
+SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 6, 1, 1),
+SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_rinmix_controls[] = {
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INBOOSTMIX2, 2, 1, 1),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INBOOSTMIX2, 6, 1, 1),
+SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 2, 1, 1),
+SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_linpga_controls[] = {
+SOC_DAPM_SINGLE("LINPUT1 Switch", WM8900_REG_INCTL, 6, 1, 0),
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INCTL, 5, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INCTL, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8900_rinpga_controls[] = {
+SOC_DAPM_SINGLE("RINPUT1 Switch", WM8900_REG_INCTL, 2, 1, 0),
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0),
+};
+
+static const char *wm9700_lp_mux[] = { "Disabled", "Enabled" };
+
+static const struct soc_enum wm8900_lineout2_lp_mux =
+SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm9700_lp_mux);
+
+static const struct snd_kcontrol_new wm8900_lineout2_lp =
+SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux);
+
+static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
+
+/* Externally visible pins */
+SND_SOC_DAPM_OUTPUT("LINEOUT1L"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1R"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2L"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2R"),
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_VMID("VMID"),
+
+/* Input */
+SND_SOC_DAPM_MIXER("Left Input PGA", WM8900_REG_POWER2, 3, 0,
+		   wm8900_linpga_controls,
+		   ARRAY_SIZE(wm8900_linpga_controls)),
+SND_SOC_DAPM_MIXER("Right Input PGA", WM8900_REG_POWER2, 2, 0,
+		   wm8900_rinpga_controls,
+		   ARRAY_SIZE(wm8900_rinpga_controls)),
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0,
+		   wm8900_linmix_controls,
+		   ARRAY_SIZE(wm8900_linmix_controls)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", WM8900_REG_POWER2, 4, 0,
+		   wm8900_rinmix_controls,
+		   ARRAY_SIZE(wm8900_rinmix_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8900_REG_POWER1, 4, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8900_REG_POWER2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8900_REG_POWER2, 0, 0),
+
+/* Output */
+SND_SOC_DAPM_DAC("DACL", "Left HiFi Playback", WM8900_REG_POWER3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", "Right HiFi Playback", WM8900_REG_POWER3, 0, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone Amplifier", WM8900_REG_POWER3, 7, 0, NULL, 0,
+		   wm8900_hp_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT1R PGA", WM8900_REG_POWER2, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("LINEOUT2 LP", SND_SOC_NOPM, 0, 0, &wm8900_lineout2_lp),
+SND_SOC_DAPM_PGA("LINEOUT2L PGA", WM8900_REG_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2R PGA", WM8900_REG_POWER3, 5, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,
+		   wm8900_loutmix_controls,
+		   ARRAY_SIZE(wm8900_loutmix_controls)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8900_REG_POWER3, 2, 0,
+		   wm8900_routmix_controls,
+		   ARRAY_SIZE(wm8900_routmix_controls)),
+};
+
+/* Target, Path, Source */
+static const struct snd_soc_dapm_route audio_map[] = {
+/* Inputs */
+{"Left Input PGA", "LINPUT1 Switch", "LINPUT1"},
+{"Left Input PGA", "LINPUT2 Switch", "LINPUT2"},
+{"Left Input PGA", "LINPUT3 Switch", "LINPUT3"},
+
+{"Right Input PGA", "RINPUT1 Switch", "RINPUT1"},
+{"Right Input PGA", "RINPUT2 Switch", "RINPUT2"},
+{"Right Input PGA", "RINPUT3 Switch", "RINPUT3"},
+
+{"Left Input Mixer", "LINPUT2 Switch", "LINPUT2"},
+{"Left Input Mixer", "LINPUT3 Switch", "LINPUT3"},
+{"Left Input Mixer", "AUX Switch", "AUX"},
+{"Left Input Mixer", "Input PGA Switch", "Left Input PGA"},
+
+{"Right Input Mixer", "RINPUT2 Switch", "RINPUT2"},
+{"Right Input Mixer", "RINPUT3 Switch", "RINPUT3"},
+{"Right Input Mixer", "AUX Switch", "AUX"},
+{"Right Input Mixer", "Input PGA Switch", "Right Input PGA"},
+
+{"ADCL", NULL, "Left Input Mixer"},
+{"ADCR", NULL, "Right Input Mixer"},
+
+/* Outputs */
+{"LINEOUT1L", NULL, "LINEOUT1L PGA"},
+{"LINEOUT1L PGA", NULL, "Left Output Mixer"},
+{"LINEOUT1R", NULL, "LINEOUT1R PGA"},
+{"LINEOUT1R PGA", NULL, "Right Output Mixer"},
+
+{"LINEOUT2L PGA", NULL, "Left Output Mixer"},
+{"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"},
+{"LINEOUT2 LP", "Enabled", "Left Output Mixer"},
+{"LINEOUT2L", NULL, "LINEOUT2 LP"},
+
+{"LINEOUT2R PGA", NULL, "Right Output Mixer"},
+{"LINEOUT2 LP", "Disabled", "LINEOUT2R PGA"},
+{"LINEOUT2 LP", "Enabled", "Right Output Mixer"},
+{"LINEOUT2R", NULL, "LINEOUT2 LP"},
+
+{"Left Output Mixer", "LINPUT3 Bypass Switch", "LINPUT3"},
+{"Left Output Mixer", "AUX Bypass Switch", "AUX"},
+{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
+{"Left Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"},
+{"Left Output Mixer", "DACL Switch", "DACL"},
+
+{"Right Output Mixer", "RINPUT3 Bypass Switch", "RINPUT3"},
+{"Right Output Mixer", "AUX Bypass Switch", "AUX"},
+{"Right Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
+{"Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"},
+{"Right Output Mixer", "DACR Switch", "DACR"},
+
+/* Note that the headphone output stage needs to be connected
+ * externally to LINEOUT2 via DC blocking capacitors.  Other
+ * configurations are not supported.
+ *
+ * Note also that left and right headphone paths are treated as a
+ * mono path.
+ */
+{"Headphone Amplifier", NULL, "LINEOUT2 LP"},
+{"Headphone Amplifier", NULL, "LINEOUT2 LP"},
+{"HP_L", NULL, "Headphone Amplifier"},
+{"HP_R", NULL, "Headphone Amplifier"},
+};
+
+static int wm8900_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets,
+				  ARRAY_SIZE(wm8900_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+
+	return 0;
+}
+
+static int wm8900_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 reg;
+
+	reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		reg |= 0x20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		reg |= 0x40;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		reg |= 0x60;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8900_write(codec, WM8900_REG_AUDIO1, reg);
+
+	return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+	u16 fll_ratio;
+	u16 fllclk_div;
+	u16 fll_slow_lock_ref;
+	u16 n;
+	u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+		       unsigned int Fout)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod, target;
+	unsigned int div;
+
+	BUG_ON(!Fout);
+
+	/* The FLL must run at 90-100MHz which is then scaled down to
+	 * the output value by FLLCLK_DIV. */
+	target = Fout;
+	div = 1;
+	while (target < 90000000) {
+		div *= 2;
+		target *= 2;
+	}
+
+	if (target > 100000000)
+		printk(KERN_WARNING "wm8900: FLL rate %d out of range, Fref=%d"
+		       " Fout=%d\n", target, Fref, Fout);
+	if (div > 32) {
+		printk(KERN_ERR "wm8900: Invalid FLL division rate %u, "
+		       "Fref=%d, Fout=%d, target=%d\n",
+		       div, Fref, Fout, target);
+		return -EINVAL;
+	}
+
+	fll_div->fllclk_div = div >> 2;
+
+	if (Fref < 48000)
+		fll_div->fll_slow_lock_ref = 1;
+	else
+		fll_div->fll_slow_lock_ref = 0;
+
+	Ndiv = target / Fref;
+
+	if (Fref < 1000000)
+		fll_div->fll_ratio = 8;
+	else
+		fll_div->fll_ratio = 1;
+
+	fll_div->n = Ndiv / fll_div->fll_ratio;
+	Nmod = (target / fll_div->fll_ratio) % Fref;
+
+	/* Calculate fractional part - scale up so we can round. */
+	Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, Fref);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	fll_div->k = K / 10;
+
+	BUG_ON(target != Fout * (fll_div->fllclk_div << 2));
+	BUG_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n);
+
+	return 0;
+}
+
+static int wm8900_set_fll(struct snd_soc_codec *codec,
+	int fll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct wm8900_priv *wm8900 = codec->private_data;
+	struct _fll_div fll_div;
+	unsigned int reg;
+
+	if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out)
+		return 0;
+
+	/* The digital side should be disabled during any change. */
+	reg = wm8900_read(codec, WM8900_REG_POWER1);
+	wm8900_write(codec, WM8900_REG_POWER1,
+		     reg & (~WM8900_REG_POWER1_FLL_ENA));
+
+	/* Disable the FLL? */
+	if (!freq_in || !freq_out) {
+		reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+		wm8900_write(codec, WM8900_REG_CLOCKING1,
+			     reg & (~WM8900_REG_CLOCKING1_MCLK_SRC));
+
+		reg = wm8900_read(codec, WM8900_REG_FLLCTL1);
+		wm8900_write(codec, WM8900_REG_FLLCTL1,
+			     reg & (~WM8900_REG_FLLCTL1_OSC_ENA));
+
+		wm8900->fll_in = freq_in;
+		wm8900->fll_out = freq_out;
+
+		return 0;
+	}
+
+	if (fll_factors(&fll_div, freq_in, freq_out) != 0)
+		goto reenable;
+
+	wm8900->fll_in = freq_in;
+	wm8900->fll_out = freq_out;
+
+	/* The osclilator *MUST* be enabled before we enable the
+	 * digital circuit. */
+	wm8900_write(codec, WM8900_REG_FLLCTL1,
+		     fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA);
+
+	wm8900_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
+	wm8900_write(codec, WM8900_REG_FLLCTL5,
+		     (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f));
+
+	if (fll_div.k) {
+		wm8900_write(codec, WM8900_REG_FLLCTL2,
+			     (fll_div.k >> 8) | 0x100);
+		wm8900_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
+	} else
+		wm8900_write(codec, WM8900_REG_FLLCTL2, 0);
+
+	if (fll_div.fll_slow_lock_ref)
+		wm8900_write(codec, WM8900_REG_FLLCTL6,
+			     WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF);
+	else
+		wm8900_write(codec, WM8900_REG_FLLCTL6, 0);
+
+	reg = wm8900_read(codec, WM8900_REG_POWER1);
+	wm8900_write(codec, WM8900_REG_POWER1,
+		     reg | WM8900_REG_POWER1_FLL_ENA);
+
+reenable:
+	reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+	wm8900_write(codec, WM8900_REG_CLOCKING1,
+		     reg | WM8900_REG_CLOCKING1_MCLK_SRC);
+
+	return 0;
+}
+
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
+}
+
+static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+				 int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int reg;
+
+	switch (div_id) {
+	case WM8900_BCLK_DIV:
+		reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+		wm8900_write(codec, WM8900_REG_CLOCKING1,
+			     div | (reg & WM8900_REG_CLOCKING1_BCLK_MASK));
+		break;
+	case WM8900_OPCLK_DIV:
+		reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
+		wm8900_write(codec, WM8900_REG_CLOCKING1,
+			     div | (reg & WM8900_REG_CLOCKING1_OPCLK_MASK));
+		break;
+	case WM8900_DAC_LRCLK:
+		reg = wm8900_read(codec, WM8900_REG_AUDIO4);
+		wm8900_write(codec, WM8900_REG_AUDIO4,
+			     div | (reg & WM8900_LRC_MASK));
+		break;
+	case WM8900_ADC_LRCLK:
+		reg = wm8900_read(codec, WM8900_REG_AUDIO3);
+		wm8900_write(codec, WM8900_REG_AUDIO3,
+			     div | (reg & WM8900_LRC_MASK));
+		break;
+	case WM8900_DAC_CLKDIV:
+		reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
+		wm8900_write(codec, WM8900_REG_CLOCKING2,
+			     div | (reg & WM8900_REG_CLOCKING2_DAC_CLKDIV));
+		break;
+	case WM8900_ADC_CLKDIV:
+		reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
+		wm8900_write(codec, WM8900_REG_CLOCKING2,
+			     div | (reg & WM8900_REG_CLOCKING2_ADC_CLKDIV));
+		break;
+	case WM8900_LRCLK_MODE:
+		reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+		wm8900_write(codec, WM8900_REG_DACCTRL,
+			     div | (reg & WM8900_REG_DACCTRL_AIF_LRCLKRATE));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int clocking1, aif1, aif3, aif4;
+
+	clocking1 = wm8900_read(codec, WM8900_REG_CLOCKING1);
+	aif1 = wm8900_read(codec, WM8900_REG_AUDIO1);
+	aif3 = wm8900_read(codec, WM8900_REG_AUDIO3);
+	aif4 = wm8900_read(codec, WM8900_REG_AUDIO4);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR;
+		aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
+		aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR;
+		aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR;
+		aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
+		aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR;
+		aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR;
+		aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR;
+		aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK;
+		aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK;
+		aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+		aif1 |= 0x10;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK;
+		aif1 |= 0x8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+			aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+			aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8900_REG_AUDIO1_BCLK_INV;
+			aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV;
+			aif1 |= WM8900_REG_AUDIO1_LRCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8900_write(codec, WM8900_REG_CLOCKING1, clocking1);
+	wm8900_write(codec, WM8900_REG_AUDIO1, aif1);
+	wm8900_write(codec, WM8900_REG_AUDIO3, aif3);
+	wm8900_write(codec, WM8900_REG_AUDIO4, aif4);
+
+	return 0;
+}
+
+static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+
+	if (mute)
+		reg |= WM8900_REG_DACCTRL_MUTE;
+	else
+		reg &= ~WM8900_REG_DACCTRL_MUTE;
+
+	wm8900_write(codec, WM8900_REG_DACCTRL, reg);
+
+	return 0;
+}
+
+#define WM8900_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)
+
+#define WM8900_PCM_FORMATS \
+	(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
+	 SNDRV_PCM_FORMAT_S24_LE)
+
+struct snd_soc_dai wm8900_dai = {
+	.name = "WM8900 HiFi",
+	.playback = {
+		.stream_name = "HiFi Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8900_RATES,
+		.formats = WM8900_PCM_FORMATS,
+	},
+	.capture = {
+		.stream_name = "HiFi Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8900_RATES,
+		.formats = WM8900_PCM_FORMATS,
+	 },
+	.ops = {
+		.hw_params = wm8900_hw_params,
+	 },
+	.dai_ops = {
+		 .set_clkdiv = wm8900_set_dai_clkdiv,
+		 .set_pll = wm8900_set_dai_pll,
+		 .set_fmt = wm8900_set_dai_fmt,
+		 .digital_mute = wm8900_digital_mute,
+	 },
+};
+EXPORT_SYMBOL_GPL(wm8900_dai);
+
+static int wm8900_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 reg;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* Enable thermal shutdown */
+		reg = wm8900_read(codec, WM8900_REG_GPIO);
+		wm8900_write(codec, WM8900_REG_GPIO,
+			     reg | WM8900_REG_GPIO_TEMP_ENA);
+		reg = wm8900_read(codec, WM8900_REG_ADDCTL);
+		wm8900_write(codec, WM8900_REG_ADDCTL,
+			     reg | WM8900_REG_ADDCTL_TEMP_SD);
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		/* Charge capacitors if initial power up */
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* STARTUP_BIAS_ENA on */
+			wm8900_write(codec, WM8900_REG_POWER1,
+				     WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+
+			/* Startup bias mode */
+			wm8900_write(codec, WM8900_REG_ADDCTL,
+				     WM8900_REG_ADDCTL_BIAS_SRC |
+				     WM8900_REG_ADDCTL_VMID_SOFTST);
+
+			/* VMID 2x50k */
+			wm8900_write(codec, WM8900_REG_POWER1,
+				     WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1);
+
+			/* Allow capacitors to charge */
+			schedule_timeout_interruptible(msecs_to_jiffies(400));
+
+			/* Enable bias */
+			wm8900_write(codec, WM8900_REG_POWER1,
+				     WM8900_REG_POWER1_STARTUP_BIAS_ENA |
+				     WM8900_REG_POWER1_BIAS_ENA | 0x1);
+
+			wm8900_write(codec, WM8900_REG_ADDCTL, 0);
+
+			wm8900_write(codec, WM8900_REG_POWER1,
+				     WM8900_REG_POWER1_BIAS_ENA | 0x1);
+		}
+
+		reg = wm8900_read(codec, WM8900_REG_POWER1);
+		wm8900_write(codec, WM8900_REG_POWER1,
+			     (reg & WM8900_REG_POWER1_FLL_ENA) |
+			     WM8900_REG_POWER1_BIAS_ENA | 0x1);
+		wm8900_write(codec, WM8900_REG_POWER2,
+			     WM8900_REG_POWER2_SYSCLK_ENA);
+		wm8900_write(codec, WM8900_REG_POWER3, 0);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Startup bias enable */
+		reg = wm8900_read(codec, WM8900_REG_POWER1);
+		wm8900_write(codec, WM8900_REG_POWER1,
+			     reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+		wm8900_write(codec, WM8900_REG_ADDCTL,
+			     WM8900_REG_ADDCTL_BIAS_SRC |
+			     WM8900_REG_ADDCTL_VMID_SOFTST);
+
+		/* Discharge caps */
+		wm8900_write(codec, WM8900_REG_POWER1,
+			     WM8900_REG_POWER1_STARTUP_BIAS_ENA);
+		schedule_timeout_interruptible(msecs_to_jiffies(500));
+
+		/* Remove clamp */
+		wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+
+		/* Power down */
+		wm8900_write(codec, WM8900_REG_ADDCTL, 0);
+		wm8900_write(codec, WM8900_REG_POWER1, 0);
+		wm8900_write(codec, WM8900_REG_POWER2, 0);
+		wm8900_write(codec, WM8900_REG_POWER3, 0);
+
+		/* Need to let things settle before stopping the clock
+		 * to ensure that restart works, see "Stopping the
+		 * master clock" in the datasheet. */
+		schedule_timeout_interruptible(msecs_to_jiffies(1));
+		wm8900_write(codec, WM8900_REG_POWER2,
+			     WM8900_REG_POWER2_SYSCLK_ENA);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+static int wm8900_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8900_priv *wm8900 = codec->private_data;
+	int fll_out = wm8900->fll_out;
+	int fll_in  = wm8900->fll_in;
+	int ret;
+
+	/* Stop the FLL in an orderly fashion */
+	ret = wm8900_set_fll(codec, 0, 0, 0);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to stop FLL\n");
+		return ret;
+	}
+
+	wm8900->fll_out = fll_out;
+	wm8900->fll_in = fll_in;
+
+	wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8900_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8900_priv *wm8900 = codec->private_data;
+	u16 *cache;
+	int i, ret;
+
+	cache = kmemdup(codec->reg_cache, sizeof(wm8900_reg_defaults),
+			GFP_KERNEL);
+
+	wm8900_reset(codec);
+	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Restart the FLL? */
+	if (wm8900->fll_out) {
+		int fll_out = wm8900->fll_out;
+		int fll_in  = wm8900->fll_in;
+
+		wm8900->fll_in = 0;
+		wm8900->fll_out = 0;
+
+		ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
+		if (ret != 0) {
+			dev_err(&pdev->dev, "Failed to restart FLL\n");
+			return ret;
+		}
+	}
+
+	if (cache) {
+		for (i = 0; i < WM8900_MAXREG; i++)
+			wm8900_write(codec, i, cache[i]);
+		kfree(cache);
+	} else
+		dev_err(&pdev->dev, "Unable to allocate register cache\n");
+
+	return 0;
+}
+
+/*
+ * initialise the WM8900 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8900_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+	unsigned int reg;
+	struct i2c_client *i2c_client = socdev->codec->control_data;
+
+	codec->name = "WM8900";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8900_read;
+	codec->write = wm8900_write;
+	codec->dai = &wm8900_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8900_MAXREG;
+	codec->reg_cache = kmemdup(wm8900_reg_defaults,
+				   sizeof(wm8900_reg_defaults), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	reg = wm8900_read(codec, WM8900_REG_ID);
+	if (reg != 0x8900) {
+		dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n",
+			reg);
+		return -ENODEV;
+	}
+
+	codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+	if (codec->private_data == NULL) {
+		ret = -ENOMEM;
+		goto priv_err;
+	}
+
+	/* Read back from the chip */
+	reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
+	reg = (reg >> 12) & 0xf;
+	dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg);
+
+	wm8900_reset(codec);
+
+	/* Latch the volume update bits */
+	wm8900_write(codec, WM8900_REG_LINVOL,
+		     wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
+	wm8900_write(codec, WM8900_REG_RINVOL,
+		     wm8900_read(codec, WM8900_REG_RINVOL) | 0x100);
+	wm8900_write(codec, WM8900_REG_LOUT1CTL,
+		     wm8900_read(codec, WM8900_REG_LOUT1CTL) | 0x100);
+	wm8900_write(codec, WM8900_REG_ROUT1CTL,
+		     wm8900_read(codec, WM8900_REG_ROUT1CTL) | 0x100);
+	wm8900_write(codec, WM8900_REG_LOUT2CTL,
+		     wm8900_read(codec, WM8900_REG_LOUT2CTL) | 0x100);
+	wm8900_write(codec, WM8900_REG_ROUT2CTL,
+		     wm8900_read(codec, WM8900_REG_ROUT2CTL) | 0x100);
+	wm8900_write(codec, WM8900_REG_LDAC_DV,
+		     wm8900_read(codec, WM8900_REG_LDAC_DV) | 0x100);
+	wm8900_write(codec, WM8900_REG_RDAC_DV,
+		     wm8900_read(codec, WM8900_REG_RDAC_DV) | 0x100);
+	wm8900_write(codec, WM8900_REG_LADC_DV,
+		     wm8900_read(codec, WM8900_REG_LADC_DV) | 0x100);
+	wm8900_write(codec, WM8900_REG_RADC_DV,
+		     wm8900_read(codec, WM8900_REG_RADC_DV) | 0x100);
+
+	/* Set the DAC and mixer output bias */
+	wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
+
+	/* Register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
+		goto pcm_err;
+	}
+
+	/* Turn the chip on */
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	wm8900_add_controls(codec);
+	wm8900_add_widgets(codec);
+
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Failed to register card\n");
+		goto card_err;
+	}
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+priv_err:
+	kfree(codec->private_data);
+	return ret;
+}
+
+static struct snd_soc_device *wm8900_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8900_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct snd_soc_device *socdev = wm8900_socdev;
+	struct wm8900_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c;
+	int ret;
+
+	if (addr != setup->i2c_address)
+		return -ENODEV;
+
+	dev_err(&adap->dev, "Probe on %x\n", addr);
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		dev_err(&adap->dev,
+			"failed to attach codec at addr %x\n", addr);
+		goto err;
+	}
+
+	ret = wm8900_init(socdev);
+	if (ret < 0) {
+		dev_err(&adap->dev, "failed to initialise WM8900\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int wm8900_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	i2c_detach_client(client);
+	kfree(codec->reg_cache);
+	kfree(client);
+	return 0;
+}
+
+static int wm8900_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8900_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8900_i2c_driver = {
+	.driver = {
+		.name = "WM8900 I2C codec",
+		.owner = THIS_MODULE,
+	},
+	.attach_adapter = wm8900_i2c_attach,
+	.detach_client =  wm8900_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8900",
+	.driver = &wm8900_i2c_driver,
+};
+#endif
+
+static int wm8900_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8900_setup_data *setup;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "WM8900 Audio Codec\n");
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	socdev->codec = codec;
+
+	codec->set_bias_level = wm8900_set_bias_level;
+
+	wm8900_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		normal_i2c[0] = setup->i2c_address;
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = i2c_add_driver(&wm8900_i2c_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add i2c driver");
+	}
+#else
+#error Non-I2C interfaces not yet supported
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int wm8900_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8900_i2c_driver);
+#endif
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8900 = {
+	.probe = 	wm8900_probe,
+	.remove = 	wm8900_remove,
+	.suspend = 	wm8900_suspend,
+	.resume =	wm8900_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
+
+MODULE_DESCRIPTION("ASoC WM8900 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h
new file mode 100644
index 0000000..ba450d9
--- /dev/null
+++ b/sound/soc/codecs/wm8900.h
@@ -0,0 +1,64 @@
+/*
+ * wm8900.h  --  WM890 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8900_H
+#define _WM8900_H
+
+#define WM8900_FLL 1
+
+#define WM8900_BCLK_DIV   1
+#define WM8900_ADC_CLKDIV 2
+#define WM8900_DAC_CLKDIV 3
+#define WM8900_ADC_LRCLK  4
+#define WM8900_DAC_LRCLK  5
+#define WM8900_OPCLK_DIV  6
+#define WM8900_LRCLK_MODE 7
+
+#define WM8900_BCLK_DIV_1   0x00
+#define WM8900_BCLK_DIV_1_5 0x02
+#define WM8900_BCLK_DIV_2   0x04
+#define WM8900_BCLK_DIV_3   0x06
+#define WM8900_BCLK_DIV_4   0x08
+#define WM8900_BCLK_DIV_5_5 0x0a
+#define WM8900_BCLK_DIV_6   0x0c
+#define WM8900_BCLK_DIV_8   0x0e
+#define WM8900_BCLK_DIV_11  0x10
+#define WM8900_BCLK_DIV_12  0x12
+#define WM8900_BCLK_DIV_16  0x14
+#define WM8900_BCLK_DIV_22  0x16
+#define WM8900_BCLK_DIV_24  0x18
+#define WM8900_BCLK_DIV_32  0x1a
+#define WM8900_BCLK_DIV_44  0x1c
+#define WM8900_BCLK_DIV_48  0x1e
+
+#define WM8900_ADC_CLKDIV_1   0x00
+#define WM8900_ADC_CLKDIV_1_5 0x20
+#define WM8900_ADC_CLKDIV_2   0x40
+#define WM8900_ADC_CLKDIV_3   0x60
+#define WM8900_ADC_CLKDIV_4   0x80
+#define WM8900_ADC_CLKDIV_5_5 0xa0
+#define WM8900_ADC_CLKDIV_6   0xc0
+
+#define WM8900_DAC_CLKDIV_1   0x00
+#define WM8900_DAC_CLKDIV_1_5 0x04
+#define WM8900_DAC_CLKDIV_2   0x08
+#define WM8900_DAC_CLKDIV_3   0x0c
+#define WM8900_DAC_CLKDIV_4   0x10
+#define WM8900_DAC_CLKDIV_5_5 0x14
+#define WM8900_DAC_CLKDIV_6   0x18
+
+#define WM8900_
+
+struct wm8900_setup_data {
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8900_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8900;
+
+#endif
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
new file mode 100644
index 0000000..a3f54ec
--- /dev/null
+++ b/sound/soc/codecs/wm8903.c
@@ -0,0 +1,1813 @@
+/*
+ * wm8903.c  --  WM8903 ALSA SoC Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * TODO:
+ *  - TDM mode configuration.
+ *  - Mic detect.
+ *  - Digital microphone support.
+ *  - Interrupt support (mic detect and sequencer).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8903.h"
+
+struct wm8903_priv {
+	int sysclk;
+
+	/* Reference counts */
+	int charge_pump_users;
+	int class_w_users;
+	int playback_active;
+	int capture_active;
+
+	struct snd_pcm_substream *master_substream;
+	struct snd_pcm_substream *slave_substream;
+};
+
+/* Register defaults at reset */
+static u16 wm8903_reg_defaults[] = {
+	0x8903,     /* R0   - SW Reset and ID */
+	0x0000,     /* R1   - Revision Number */
+	0x0000,     /* R2 */
+	0x0000,     /* R3 */
+	0x0018,     /* R4   - Bias Control 0 */
+	0x0000,     /* R5   - VMID Control 0 */
+	0x0000,     /* R6   - Mic Bias Control 0 */
+	0x0000,     /* R7 */
+	0x0001,     /* R8   - Analogue DAC 0 */
+	0x0000,     /* R9 */
+	0x0001,     /* R10  - Analogue ADC 0 */
+	0x0000,     /* R11 */
+	0x0000,     /* R12  - Power Management 0 */
+	0x0000,     /* R13  - Power Management 1 */
+	0x0000,     /* R14  - Power Management 2 */
+	0x0000,     /* R15  - Power Management 3 */
+	0x0000,     /* R16  - Power Management 4 */
+	0x0000,     /* R17  - Power Management 5 */
+	0x0000,     /* R18  - Power Management 6 */
+	0x0000,     /* R19 */
+	0x0400,     /* R20  - Clock Rates 0 */
+	0x0D07,     /* R21  - Clock Rates 1 */
+	0x0000,     /* R22  - Clock Rates 2 */
+	0x0000,     /* R23 */
+	0x0050,     /* R24  - Audio Interface 0 */
+	0x0242,     /* R25  - Audio Interface 1 */
+	0x0008,     /* R26  - Audio Interface 2 */
+	0x0022,     /* R27  - Audio Interface 3 */
+	0x0000,     /* R28 */
+	0x0000,     /* R29 */
+	0x00C0,     /* R30  - DAC Digital Volume Left */
+	0x00C0,     /* R31  - DAC Digital Volume Right */
+	0x0000,     /* R32  - DAC Digital 0 */
+	0x0000,     /* R33  - DAC Digital 1 */
+	0x0000,     /* R34 */
+	0x0000,     /* R35 */
+	0x00C0,     /* R36  - ADC Digital Volume Left */
+	0x00C0,     /* R37  - ADC Digital Volume Right */
+	0x0000,     /* R38  - ADC Digital 0 */
+	0x0073,     /* R39  - Digital Microphone 0 */
+	0x09BF,     /* R40  - DRC 0 */
+	0x3241,     /* R41  - DRC 1 */
+	0x0020,     /* R42  - DRC 2 */
+	0x0000,     /* R43  - DRC 3 */
+	0x0085,     /* R44  - Analogue Left Input 0 */
+	0x0085,     /* R45  - Analogue Right Input 0 */
+	0x0044,     /* R46  - Analogue Left Input 1 */
+	0x0044,     /* R47  - Analogue Right Input 1 */
+	0x0000,     /* R48 */
+	0x0000,     /* R49 */
+	0x0008,     /* R50  - Analogue Left Mix 0 */
+	0x0004,     /* R51  - Analogue Right Mix 0 */
+	0x0000,     /* R52  - Analogue Spk Mix Left 0 */
+	0x0000,     /* R53  - Analogue Spk Mix Left 1 */
+	0x0000,     /* R54  - Analogue Spk Mix Right 0 */
+	0x0000,     /* R55  - Analogue Spk Mix Right 1 */
+	0x0000,     /* R56 */
+	0x002D,     /* R57  - Analogue OUT1 Left */
+	0x002D,     /* R58  - Analogue OUT1 Right */
+	0x0039,     /* R59  - Analogue OUT2 Left */
+	0x0039,     /* R60  - Analogue OUT2 Right */
+	0x0100,     /* R61 */
+	0x0139,     /* R62  - Analogue OUT3 Left */
+	0x0139,     /* R63  - Analogue OUT3 Right */
+	0x0000,     /* R64 */
+	0x0000,     /* R65  - Analogue SPK Output Control 0 */
+	0x0000,     /* R66 */
+	0x0010,     /* R67  - DC Servo 0 */
+	0x0100,     /* R68 */
+	0x00A4,     /* R69  - DC Servo 2 */
+	0x0807,     /* R70 */
+	0x0000,     /* R71 */
+	0x0000,     /* R72 */
+	0x0000,     /* R73 */
+	0x0000,     /* R74 */
+	0x0000,     /* R75 */
+	0x0000,     /* R76 */
+	0x0000,     /* R77 */
+	0x0000,     /* R78 */
+	0x000E,     /* R79 */
+	0x0000,     /* R80 */
+	0x0000,     /* R81 */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0006,     /* R87 */
+	0x0000,     /* R88 */
+	0x0000,     /* R89 */
+	0x0000,     /* R90  - Analogue HP 0 */
+	0x0060,     /* R91 */
+	0x0000,     /* R92 */
+	0x0000,     /* R93 */
+	0x0000,     /* R94  - Analogue Lineout 0 */
+	0x0060,     /* R95 */
+	0x0000,     /* R96 */
+	0x0000,     /* R97 */
+	0x0000,     /* R98  - Charge Pump 0 */
+	0x1F25,     /* R99 */
+	0x2B19,     /* R100 */
+	0x01C0,     /* R101 */
+	0x01EF,     /* R102 */
+	0x2B00,     /* R103 */
+	0x0000,     /* R104 - Class W 0 */
+	0x01C0,     /* R105 */
+	0x1C10,     /* R106 */
+	0x0000,     /* R107 */
+	0x0000,     /* R108 - Write Sequencer 0 */
+	0x0000,     /* R109 - Write Sequencer 1 */
+	0x0000,     /* R110 - Write Sequencer 2 */
+	0x0000,     /* R111 - Write Sequencer 3 */
+	0x0000,     /* R112 - Write Sequencer 4 */
+	0x0000,     /* R113 */
+	0x0000,     /* R114 - Control Interface */
+	0x0000,     /* R115 */
+	0x00A8,     /* R116 - GPIO Control 1 */
+	0x00A8,     /* R117 - GPIO Control 2 */
+	0x00A8,     /* R118 - GPIO Control 3 */
+	0x0220,     /* R119 - GPIO Control 4 */
+	0x01A0,     /* R120 - GPIO Control 5 */
+	0x0000,     /* R121 - Interrupt Status 1 */
+	0xFFFF,     /* R122 - Interrupt Status 1 Mask */
+	0x0000,     /* R123 - Interrupt Polarity 1 */
+	0x0000,     /* R124 */
+	0x0003,     /* R125 */
+	0x0000,     /* R126 - Interrupt Control */
+	0x0000,     /* R127 */
+	0x0005,     /* R128 */
+	0x0000,     /* R129 - Control Interface Test 1 */
+	0x0000,     /* R130 */
+	0x0000,     /* R131 */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 */
+	0x0000,     /* R134 */
+	0x03FF,     /* R135 */
+	0x0007,     /* R136 */
+	0x0040,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0000,     /* R140 */
+	0x0000,     /* R141 */
+	0x0000,     /* R142 */
+	0x0000,     /* R143 */
+	0x0000,     /* R144 */
+	0x0000,     /* R145 */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x4000,     /* R148 */
+	0x6810,     /* R149 - Charge Pump Test 1 */
+	0x0004,     /* R150 */
+	0x0000,     /* R151 */
+	0x0000,     /* R152 */
+	0x0000,     /* R153 */
+	0x0000,     /* R154 */
+	0x0000,     /* R155 */
+	0x0000,     /* R156 */
+	0x0000,     /* R157 */
+	0x0000,     /* R158 */
+	0x0000,     /* R159 */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 */
+	0x0028,     /* R164 - Clock Rate Test 4 */
+	0x0004,     /* R165 */
+	0x0000,     /* R166 */
+	0x0060,     /* R167 */
+	0x0000,     /* R168 */
+	0x0000,     /* R169 */
+	0x0000,     /* R170 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Analogue Output Bias 0 */
+};
+
+static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
+						 unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+
+	BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
+
+	return cache[reg];
+}
+
+static unsigned int wm8903_hw_read(struct snd_soc_codec *codec, u8 reg)
+{
+	struct i2c_msg xfer[2];
+	u16 data;
+	int ret;
+	struct i2c_client *client = codec->control_data;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 2;
+	xfer[1].buf = (u8 *)&data;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret != 2) {
+		pr_err("i2c_transfer returned %d\n", ret);
+		return 0;
+	}
+
+	return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static unsigned int wm8903_read(struct snd_soc_codec *codec,
+				unsigned int reg)
+{
+	switch (reg) {
+	case WM8903_SW_RESET_AND_ID:
+	case WM8903_REVISION_NUMBER:
+	case WM8903_INTERRUPT_STATUS_1:
+	case WM8903_WRITE_SEQUENCER_4:
+		return wm8903_hw_read(codec, reg);
+
+	default:
+		return wm8903_read_reg_cache(codec, reg);
+	}
+}
+
+static void wm8903_write_reg_cache(struct snd_soc_codec *codec,
+				   u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+
+	BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
+
+	switch (reg) {
+	case WM8903_SW_RESET_AND_ID:
+	case WM8903_REVISION_NUMBER:
+		break;
+
+	default:
+		cache[reg] = value;
+		break;
+	}
+}
+
+static int wm8903_write(struct snd_soc_codec *codec, unsigned int reg,
+			unsigned int value)
+{
+	u8 data[3];
+
+	wm8903_write_reg_cache(codec, reg, value);
+
+	/* Data format is 1 byte of address followed by 2 bytes of data */
+	data[0] = reg;
+	data[1] = (value >> 8) & 0xff;
+	data[2] = value & 0xff;
+
+	if (codec->hw_write(codec->control_data, data, 3) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
+{
+	u16 reg[5];
+	struct i2c_client *i2c = codec->control_data;
+
+	BUG_ON(start > 48);
+
+	/* Enable the sequencer */
+	reg[0] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_0);
+	reg[0] |= WM8903_WSEQ_ENA;
+	wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
+
+	dev_dbg(&i2c->dev, "Starting sequence at %d\n", start);
+
+	wm8903_write(codec, WM8903_WRITE_SEQUENCER_3,
+		     start | WM8903_WSEQ_START);
+
+	/* Wait for it to complete.  If we have the interrupt wired up then
+	 * we could block waiting for an interrupt, though polling may still
+	 * be desirable for diagnostic purposes.
+	 */
+	do {
+		msleep(10);
+
+		reg[4] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_4);
+	} while (reg[4] & WM8903_WSEQ_BUSY);
+
+	dev_dbg(&i2c->dev, "Sequence complete\n");
+
+	/* Disable the sequencer again */
+	wm8903_write(codec, WM8903_WRITE_SEQUENCER_0,
+		     reg[0] & ~WM8903_WSEQ_ENA);
+
+	return 0;
+}
+
+static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
+{
+	int i;
+
+	/* There really ought to be something better we can do here :/ */
+	for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
+		cache[i] = wm8903_hw_read(codec, i);
+}
+
+static void wm8903_reset(struct snd_soc_codec *codec)
+{
+	wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+}
+
+#define WM8903_OUTPUT_SHORT 0x8
+#define WM8903_OUTPUT_OUT   0x4
+#define WM8903_OUTPUT_INT   0x2
+#define WM8903_OUTPUT_IN    0x1
+
+/*
+ * Event for headphone and line out amplifier power changes.  Special
+ * power up/down sequences are required in order to maximise pop/click
+ * performance.
+ */
+static int wm8903_output_event(struct snd_soc_dapm_widget *w,
+			       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm8903_priv *wm8903 = codec->private_data;
+	struct i2c_client *i2c = codec->control_data;
+	u16 val;
+	u16 reg;
+	int shift;
+	u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0);
+
+	switch (w->reg) {
+	case WM8903_POWER_MANAGEMENT_2:
+		reg = WM8903_ANALOGUE_HP_0;
+		break;
+	case WM8903_POWER_MANAGEMENT_3:
+		reg = WM8903_ANALOGUE_LINEOUT_0;
+		break;
+	default:
+		BUG();
+	}
+
+	switch (w->shift) {
+	case 0:
+		shift = 0;
+		break;
+	case 1:
+		shift = 4;
+		break;
+	default:
+		BUG();
+	}
+
+	if (event & SND_SOC_DAPM_PRE_PMU) {
+		val = wm8903_read(codec, reg);
+
+		/* Short the output */
+		val &= ~(WM8903_OUTPUT_SHORT << shift);
+		wm8903_write(codec, reg, val);
+
+		wm8903->charge_pump_users++;
+
+		dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
+			wm8903->charge_pump_users);
+
+		if (wm8903->charge_pump_users == 1) {
+			dev_dbg(&i2c->dev, "Enabling charge pump\n");
+			wm8903_write(codec, WM8903_CHARGE_PUMP_0,
+				     cp_reg | WM8903_CP_ENA);
+			mdelay(4);
+		}
+	}
+
+	if (event & SND_SOC_DAPM_POST_PMU) {
+		val = wm8903_read(codec, reg);
+
+		val |= (WM8903_OUTPUT_IN << shift);
+		wm8903_write(codec, reg, val);
+
+		val |= (WM8903_OUTPUT_INT << shift);
+		wm8903_write(codec, reg, val);
+
+		/* Turn on the output ENA_OUTP */
+		val |= (WM8903_OUTPUT_OUT << shift);
+		wm8903_write(codec, reg, val);
+
+		/* Remove the short */
+		val |= (WM8903_OUTPUT_SHORT << shift);
+		wm8903_write(codec, reg, val);
+	}
+
+	if (event & SND_SOC_DAPM_PRE_PMD) {
+		val = wm8903_read(codec, reg);
+
+		/* Short the output */
+		val &= ~(WM8903_OUTPUT_SHORT << shift);
+		wm8903_write(codec, reg, val);
+
+		/* Then disable the intermediate and output stages */
+		val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
+			  WM8903_OUTPUT_IN) << shift);
+		wm8903_write(codec, reg, val);
+	}
+
+	if (event & SND_SOC_DAPM_POST_PMD) {
+		wm8903->charge_pump_users--;
+
+		dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
+			wm8903->charge_pump_users);
+
+		if (wm8903->charge_pump_users == 0) {
+			dev_dbg(&i2c->dev, "Disabling charge pump\n");
+			wm8903_write(codec, WM8903_CHARGE_PUMP_0,
+				     cp_reg & ~WM8903_CP_ENA);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * When used with DAC outputs only the WM8903 charge pump supports
+ * operation in class W mode, providing very low power consumption
+ * when used with digital sources.  Enable and disable this mode
+ * automatically depending on the mixer configuration.
+ *
+ * All the relevant controls are simple switches.
+ */
+static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_codec *codec = widget->codec;
+	struct wm8903_priv *wm8903 = codec->private_data;
+	struct i2c_client *i2c = codec->control_data;
+	u16 reg;
+	int ret;
+
+	reg = wm8903_read(codec, WM8903_CLASS_W_0);
+
+	/* Turn it off if we're about to enable bypass */
+	if (ucontrol->value.integer.value[0]) {
+		if (wm8903->class_w_users == 0) {
+			dev_dbg(&i2c->dev, "Disabling Class W\n");
+			wm8903_write(codec, WM8903_CLASS_W_0, reg &
+				     ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V));
+		}
+		wm8903->class_w_users++;
+	}
+
+	/* Implement the change */
+	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+	/* If we've just disabled the last bypass path turn Class W on */
+	if (!ucontrol->value.integer.value[0]) {
+		if (wm8903->class_w_users == 1) {
+			dev_dbg(&i2c->dev, "Enabling Class W\n");
+			wm8903_write(codec, WM8903_CLASS_W_0, reg |
+				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
+		}
+		wm8903->class_w_users--;
+	}
+
+	dev_dbg(&i2c->dev, "Bypass use count now %d\n",
+		wm8903->class_w_users);
+
+	return ret;
+}
+
+#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \
+	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+/* ALSA can only do steps of .01dB */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0);
+
+static const char *drc_slope_text[] = {
+	"1", "1/2", "1/4", "1/8", "1/16", "0"
+};
+
+static const struct soc_enum drc_slope_r0 =
+	SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text);
+
+static const struct soc_enum drc_slope_r1 =
+	SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text);
+
+static const char *drc_attack_text[] = {
+	"instantaneous",
+	"363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms",
+	"46.4ms", "92.8ms", "185.6ms"
+};
+
+static const struct soc_enum drc_attack =
+	SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+	"186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s",
+	"23.87s", "47.56s"
+};
+
+static const struct soc_enum drc_decay =
+	SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text);
+
+static const char *drc_ff_delay_text[] = {
+	"5 samples", "9 samples"
+};
+
+static const struct soc_enum drc_ff_delay =
+	SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text);
+
+static const char *drc_qr_decay_text[] = {
+	"0.725ms", "1.45ms", "5.8ms"
+};
+
+static const struct soc_enum drc_qr_decay =
+	SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text);
+
+static const char *drc_smoothing_text[] = {
+	"Low", "Medium", "High"
+};
+
+static const struct soc_enum drc_smoothing =
+	SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text);
+
+static const char *soft_mute_text[] = {
+	"Fast (fs/2)", "Slow (fs/32)"
+};
+
+static const struct soc_enum soft_mute =
+	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text);
+
+static const char *mute_mode_text[] = {
+	"Hard", "Soft"
+};
+
+static const struct soc_enum mute_mode =
+	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text);
+
+static const char *dac_deemphasis_text[] = {
+	"Disabled", "32kHz", "44.1kHz", "48kHz"
+};
+
+static const struct soc_enum dac_deemphasis =
+	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text);
+
+static const char *companding_text[] = {
+	"ulaw", "alaw"
+};
+
+static const struct soc_enum dac_companding =
+	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text);
+
+static const struct soc_enum adc_companding =
+	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text);
+
+static const char *input_mode_text[] = {
+	"Single-Ended", "Differential Line", "Differential Mic"
+};
+
+static const struct soc_enum linput_mode_enum =
+	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text);
+
+static const struct soc_enum rinput_mode_enum =
+	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text);
+
+static const char *linput_mux_text[] = {
+	"IN1L", "IN2L", "IN3L"
+};
+
+static const struct soc_enum linput_enum =
+	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text);
+
+static const struct soc_enum linput_inv_enum =
+	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text);
+
+static const char *rinput_mux_text[] = {
+	"IN1R", "IN2R", "IN3R"
+};
+
+static const struct soc_enum rinput_enum =
+	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text);
+
+static const struct soc_enum rinput_inv_enum =
+	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text);
+
+
+static const struct snd_kcontrol_new wm8903_snd_controls[] = {
+
+/* Input PGAs - No TLV since the scale depends on PGA mode */
+SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0,
+	   7, 1, 0),
+SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0,
+	   0, 31, 0),
+SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1,
+	   6, 1, 0),
+
+SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0,
+	   7, 1, 0),
+SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0,
+	   0, 31, 0),
+SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1,
+	   6, 1, 0),
+
+/* ADCs */
+SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0),
+SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0),
+SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1),
+SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8903_DRC_3, 5, 124, 1,
+	       drc_tlv_thresh),
+SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp),
+SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min),
+SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff_delay),
+SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0),
+SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0),
+SOC_SINGLE_TLV("DRC QR Threashold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max),
+SOC_ENUM("DRC QR Decay Rate", drc_qr_decay),
+SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0),
+SOC_ENUM("DRC Smoothing Threashold", drc_smoothing),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
+
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
+		 WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+SOC_ENUM("ADC Companding Mode", adc_companding),
+SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
+
+/* DAC */
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
+		 WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
+SOC_ENUM("DAC Soft Mute Rate", soft_mute),
+SOC_ENUM("DAC Mute Mode", mute_mode),
+SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
+SOC_ENUM("DAC De-emphasis", dac_deemphasis),
+SOC_SINGLE("DAC Sloping Stopband Filter Switch",
+	   WM8903_DAC_DIGITAL_1, 11, 1, 0),
+SOC_ENUM("DAC Companding Mode", dac_companding),
+SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
+
+/* Headphones */
+SOC_DOUBLE_R("Headphone Switch",
+	     WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+	     8, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch",
+	     WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+	     6, 1, 0),
+SOC_DOUBLE_R_TLV("Headphone Volume",
+		 WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT,
+		 0, 63, 0, out_tlv),
+
+/* Line out */
+SOC_DOUBLE_R("Line Out Switch",
+	     WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+	     8, 1, 1),
+SOC_DOUBLE_R("Line Out ZC Switch",
+	     WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+	     6, 1, 0),
+SOC_DOUBLE_R_TLV("Line Out Volume",
+		 WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT,
+		 0, 63, 0, out_tlv),
+
+/* Speaker */
+SOC_DOUBLE_R("Speaker Switch",
+	     WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch",
+	     WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+		 WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT,
+		 0, 63, 0, out_tlv),
+};
+
+static int wm8903_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&wm8903_snd_controls[i],
+					       codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new linput_mode_mux =
+	SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum);
+
+static const struct snd_kcontrol_new rinput_mode_mux =
+	SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum);
+
+static const struct snd_kcontrol_new linput_mux =
+	SOC_DAPM_ENUM("Left Input Mux", linput_enum);
+
+static const struct snd_kcontrol_new linput_inv_mux =
+	SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum);
+
+static const struct snd_kcontrol_new rinput_mux =
+	SOC_DAPM_ENUM("Right Input Mux", rinput_enum);
+
+static const struct snd_kcontrol_new rinput_inv_mux =
+	SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum);
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
+SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
+SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
+		1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0),
+SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
+		1, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
+		1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8903_MIC_BIAS_CONTROL_0, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux),
+SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0,
+		 &linput_inv_mux),
+SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux),
+
+SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux),
+SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0,
+		 &rinput_inv_mux),
+SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
+
+SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0,
+		   left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0,
+		   right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0,
+		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
+		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
+		   1, 0, NULL, 0, wm8903_output_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
+		   0, 0, NULL, 0, wm8903_output_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
+		   NULL, 0, wm8903_output_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
+		   NULL, 0, wm8903_output_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
+		 NULL, 0),
+
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+	{ "Left Input Mux", "IN1L", "IN1L" },
+	{ "Left Input Mux", "IN2L", "IN2L" },
+	{ "Left Input Mux", "IN3L", "IN3L" },
+
+	{ "Left Input Inverting Mux", "IN1L", "IN1L" },
+	{ "Left Input Inverting Mux", "IN2L", "IN2L" },
+	{ "Left Input Inverting Mux", "IN3L", "IN3L" },
+
+	{ "Right Input Mux", "IN1R", "IN1R" },
+	{ "Right Input Mux", "IN2R", "IN2R" },
+	{ "Right Input Mux", "IN3R", "IN3R" },
+
+	{ "Right Input Inverting Mux", "IN1R", "IN1R" },
+	{ "Right Input Inverting Mux", "IN2R", "IN2R" },
+	{ "Right Input Inverting Mux", "IN3R", "IN3R" },
+
+	{ "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" },
+	{ "Left Input Mode Mux", "Differential Line",
+	  "Left Input Mux" },
+	{ "Left Input Mode Mux", "Differential Line",
+	  "Left Input Inverting Mux" },
+	{ "Left Input Mode Mux", "Differential Mic",
+	  "Left Input Mux" },
+	{ "Left Input Mode Mux", "Differential Mic",
+	  "Left Input Inverting Mux" },
+
+	{ "Right Input Mode Mux", "Single-Ended",
+	  "Right Input Inverting Mux" },
+	{ "Right Input Mode Mux", "Differential Line",
+	  "Right Input Mux" },
+	{ "Right Input Mode Mux", "Differential Line",
+	  "Right Input Inverting Mux" },
+	{ "Right Input Mode Mux", "Differential Mic",
+	  "Right Input Mux" },
+	{ "Right Input Mode Mux", "Differential Mic",
+	  "Right Input Inverting Mux" },
+
+	{ "Left Input PGA", NULL, "Left Input Mode Mux" },
+	{ "Right Input PGA", NULL, "Right Input Mode Mux" },
+
+	{ "ADCL", NULL, "Left Input PGA" },
+	{ "ADCR", NULL, "Right Input PGA" },
+
+	{ "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" },
+	{ "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" },
+	{ "Left Output Mixer", "DACL Switch", "DACL" },
+	{ "Left Output Mixer", "DACR Switch", "DACR" },
+
+	{ "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" },
+	{ "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" },
+	{ "Right Output Mixer", "DACL Switch", "DACL" },
+	{ "Right Output Mixer", "DACR Switch", "DACR" },
+
+	{ "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" },
+	{ "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" },
+	{ "Left Speaker Mixer", "DACL Switch", "DACL" },
+	{ "Left Speaker Mixer", "DACR Switch", "DACR" },
+
+	{ "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" },
+	{ "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" },
+	{ "Right Speaker Mixer", "DACL Switch", "DACL" },
+	{ "Right Speaker Mixer", "DACR Switch", "DACR" },
+
+	{ "Left Line Output PGA", NULL, "Left Output Mixer" },
+	{ "Right Line Output PGA", NULL, "Right Output Mixer" },
+
+	{ "Left Headphone Output PGA", NULL, "Left Output Mixer" },
+	{ "Right Headphone Output PGA", NULL, "Right Output Mixer" },
+
+	{ "Left Speaker PGA", NULL, "Left Speaker Mixer" },
+	{ "Right Speaker PGA", NULL, "Right Speaker Mixer" },
+
+	{ "HPOUTL", NULL, "Left Headphone Output PGA" },
+	{ "HPOUTR", NULL, "Right Headphone Output PGA" },
+
+	{ "LINEOUTL", NULL, "Left Line Output PGA" },
+	{ "LINEOUTR", NULL, "Right Line Output PGA" },
+
+	{ "LOP", NULL, "Left Speaker PGA" },
+	{ "LON", NULL, "Left Speaker PGA" },
+
+	{ "ROP", NULL, "Right Speaker PGA" },
+	{ "RON", NULL, "Right Speaker PGA" },
+};
+
+static int wm8903_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets,
+				  ARRAY_SIZE(wm8903_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+
+	return 0;
+}
+
+static int wm8903_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct i2c_client *i2c = codec->control_data;
+	u16 reg, reg2;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+		reg &= ~(WM8903_VMID_RES_MASK);
+		reg |= WM8903_VMID_RES_50K;
+		wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			wm8903_run_sequence(codec, 0);
+			wm8903_sync_reg_cache(codec, codec->reg_cache);
+
+			/* Enable low impedence charge pump output */
+			reg = wm8903_read(codec,
+					  WM8903_CONTROL_INTERFACE_TEST_1);
+			wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+				     reg | WM8903_TEST_KEY);
+			reg2 = wm8903_read(codec, WM8903_CHARGE_PUMP_TEST_1);
+			wm8903_write(codec, WM8903_CHARGE_PUMP_TEST_1,
+				     reg2 | WM8903_CP_SW_KELVIN_MODE_MASK);
+			wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+				     reg);
+
+			/* By default no bypass paths are enabled so
+			 * enable Class W support.
+			 */
+			dev_dbg(&i2c->dev, "Enabling Class W\n");
+			wm8903_write(codec, WM8903_CLASS_W_0, reg |
+				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
+		}
+
+		reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+		reg &= ~(WM8903_VMID_RES_MASK);
+		reg |= WM8903_VMID_RES_250K;
+		wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		wm8903_run_sequence(codec, 32);
+		break;
+	}
+
+	codec->bias_level = level;
+
+	return 0;
+}
+
+static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8903_priv *wm8903 = codec->private_data;
+
+	wm8903->sysclk = freq;
+
+	return 0;
+}
+
+static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
+
+	aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK |
+		  WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		aif1 |= WM8903_LRCLK_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		aif1 |= WM8903_BCLK_DIR;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		aif1 |= 0x3;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		aif1 |= 0x3 | WM8903_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif1 |= 0x2;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		aif1 |= 0x1;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8903_AIF_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8903_AIF_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif1 |= WM8903_AIF_LRCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+
+	return 0;
+}
+
+static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	reg = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+
+	if (mute)
+		reg |= WM8903_DAC_MUTE;
+	else
+		reg &= ~WM8903_DAC_MUTE;
+
+	wm8903_write(codec, WM8903_DAC_DIGITAL_1, reg);
+
+	return 0;
+}
+
+/* Lookup table for CLK_SYS/fs ratio.  256fs or more is recommended
+ * for optimal performance so we list the lower rates first and match
+ * on the last match we find. */
+static struct {
+	int div;
+	int rate;
+	int mode;
+	int mclk_div;
+} clk_sys_ratios[] = {
+	{   64, 0x0, 0x0, 1 },
+	{   68, 0x0, 0x1, 1 },
+	{  125, 0x0, 0x2, 1 },
+	{  128, 0x1, 0x0, 1 },
+	{  136, 0x1, 0x1, 1 },
+	{  192, 0x2, 0x0, 1 },
+	{  204, 0x2, 0x1, 1 },
+
+	{   64, 0x0, 0x0, 2 },
+	{   68, 0x0, 0x1, 2 },
+	{  125, 0x0, 0x2, 2 },
+	{  128, 0x1, 0x0, 2 },
+	{  136, 0x1, 0x1, 2 },
+	{  192, 0x2, 0x0, 2 },
+	{  204, 0x2, 0x1, 2 },
+
+	{  250, 0x2, 0x2, 1 },
+	{  256, 0x3, 0x0, 1 },
+	{  272, 0x3, 0x1, 1 },
+	{  384, 0x4, 0x0, 1 },
+	{  408, 0x4, 0x1, 1 },
+	{  375, 0x4, 0x2, 1 },
+	{  512, 0x5, 0x0, 1 },
+	{  544, 0x5, 0x1, 1 },
+	{  500, 0x5, 0x2, 1 },
+	{  768, 0x6, 0x0, 1 },
+	{  816, 0x6, 0x1, 1 },
+	{  750, 0x6, 0x2, 1 },
+	{ 1024, 0x7, 0x0, 1 },
+	{ 1088, 0x7, 0x1, 1 },
+	{ 1000, 0x7, 0x2, 1 },
+	{ 1408, 0x8, 0x0, 1 },
+	{ 1496, 0x8, 0x1, 1 },
+	{ 1536, 0x9, 0x0, 1 },
+	{ 1632, 0x9, 0x1, 1 },
+	{ 1500, 0x9, 0x2, 1 },
+
+	{  250, 0x2, 0x2, 2 },
+	{  256, 0x3, 0x0, 2 },
+	{  272, 0x3, 0x1, 2 },
+	{  384, 0x4, 0x0, 2 },
+	{  408, 0x4, 0x1, 2 },
+	{  375, 0x4, 0x2, 2 },
+	{  512, 0x5, 0x0, 2 },
+	{  544, 0x5, 0x1, 2 },
+	{  500, 0x5, 0x2, 2 },
+	{  768, 0x6, 0x0, 2 },
+	{  816, 0x6, 0x1, 2 },
+	{  750, 0x6, 0x2, 2 },
+	{ 1024, 0x7, 0x0, 2 },
+	{ 1088, 0x7, 0x1, 2 },
+	{ 1000, 0x7, 0x2, 2 },
+	{ 1408, 0x8, 0x0, 2 },
+	{ 1496, 0x8, 0x1, 2 },
+	{ 1536, 0x9, 0x0, 2 },
+	{ 1632, 0x9, 0x1, 2 },
+	{ 1500, 0x9, 0x2, 2 },
+};
+
+/* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */
+static struct {
+	int ratio;
+	int div;
+} bclk_divs[] = {
+	{  10,  0 },
+	{  15,  1 },
+	{  20,  2 },
+	{  30,  3 },
+	{  40,  4 },
+	{  50,  5 },
+	{  55,  6 },
+	{  60,  7 },
+	{  80,  8 },
+	{ 100,  9 },
+	{ 110, 10 },
+	{ 120, 11 },
+	{ 160, 12 },
+	{ 200, 13 },
+	{ 220, 14 },
+	{ 240, 15 },
+	{ 250, 16 },
+	{ 300, 17 },
+	{ 320, 18 },
+	{ 440, 19 },
+	{ 480, 20 },
+};
+
+/* Sample rates for DSP */
+static struct {
+	int rate;
+	int value;
+} sample_rates[] = {
+	{  8000,  0 },
+	{ 11025,  1 },
+	{ 12000,  2 },
+	{ 16000,  3 },
+	{ 22050,  4 },
+	{ 24000,  5 },
+	{ 32000,  6 },
+	{ 44100,  7 },
+	{ 48000,  8 },
+	{ 88200,  9 },
+	{ 96000, 10 },
+	{ 0,      0 },
+};
+
+static int wm8903_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8903_priv *wm8903 = codec->private_data;
+	struct i2c_client *i2c = codec->control_data;
+	struct snd_pcm_runtime *master_runtime;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		wm8903->playback_active++;
+	else
+		wm8903->capture_active++;
+
+	/* The DAI has shared clocks so if we already have a playback or
+	 * capture going then constrain this substream to match it.
+	 */
+	if (wm8903->master_substream) {
+		master_runtime = wm8903->master_substream->runtime;
+
+		dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
+			master_runtime->sample_bits,
+			master_runtime->rate);
+
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_RATE,
+					     master_runtime->rate,
+					     master_runtime->rate);
+
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+					     master_runtime->sample_bits,
+					     master_runtime->sample_bits);
+
+		wm8903->slave_substream = substream;
+	} else
+		wm8903->master_substream = substream;
+
+	return 0;
+}
+
+static void wm8903_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8903_priv *wm8903 = codec->private_data;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		wm8903->playback_active--;
+	else
+		wm8903->capture_active--;
+
+	if (wm8903->master_substream == substream)
+		wm8903->master_substream = wm8903->slave_substream;
+
+	wm8903->slave_substream = NULL;
+}
+
+static int wm8903_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8903_priv *wm8903 = codec->private_data;
+	struct i2c_client *i2c = codec->control_data;
+	int fs = params_rate(params);
+	int bclk;
+	int bclk_div;
+	int i;
+	int dsp_config;
+	int clk_config;
+	int best_val;
+	int cur_val;
+	int clk_sys;
+
+	u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
+	u16 aif2 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_2);
+	u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3);
+	u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0);
+	u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1);
+
+	if (substream == wm8903->slave_substream) {
+		dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+		return 0;
+	}
+
+	/* Configure sample rate logic for DSP - choose nearest rate */
+	dsp_config = 0;
+	best_val = abs(sample_rates[dsp_config].rate - fs);
+	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+		cur_val = abs(sample_rates[i].rate - fs);
+		if (cur_val <= best_val) {
+			dsp_config = i;
+			best_val = cur_val;
+		}
+	}
+
+	/* Constraints should stop us hitting this but let's make sure */
+	if (wm8903->capture_active)
+		switch (sample_rates[dsp_config].rate) {
+		case 88200:
+		case 96000:
+			dev_err(&i2c->dev, "%dHz unsupported by ADC\n",
+				fs);
+			return -EINVAL;
+
+		default:
+			break;
+		}
+
+	dev_dbg(&i2c->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate);
+	clock1 &= ~WM8903_SAMPLE_RATE_MASK;
+	clock1 |= sample_rates[dsp_config].value;
+
+	aif1 &= ~WM8903_AIF_WL_MASK;
+	bclk = 2 * fs;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bclk *= 16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		bclk *= 20;
+		aif1 |= 0x4;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bclk *= 24;
+		aif1 |= 0x8;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bclk *= 32;
+		aif1 |= 0xc;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	dev_dbg(&i2c->dev, "MCLK = %dHz, target sample rate = %dHz\n",
+		wm8903->sysclk, fs);
+
+	/* We may not have an MCLK which allows us to generate exactly
+	 * the clock we want, particularly with USB derived inputs, so
+	 * approximate.
+	 */
+	clk_config = 0;
+	best_val = abs((wm8903->sysclk /
+			(clk_sys_ratios[0].mclk_div *
+			 clk_sys_ratios[0].div)) - fs);
+	for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) {
+		cur_val = abs((wm8903->sysclk /
+			       (clk_sys_ratios[i].mclk_div *
+				clk_sys_ratios[i].div)) - fs);
+
+		if (cur_val <= best_val) {
+			clk_config = i;
+			best_val = cur_val;
+		}
+	}
+
+	if (clk_sys_ratios[clk_config].mclk_div == 2) {
+		clock0 |= WM8903_MCLKDIV2;
+		clk_sys = wm8903->sysclk / 2;
+	} else {
+		clock0 &= ~WM8903_MCLKDIV2;
+		clk_sys = wm8903->sysclk;
+	}
+
+	clock1 &= ~(WM8903_CLK_SYS_RATE_MASK |
+		    WM8903_CLK_SYS_MODE_MASK);
+	clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT;
+	clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT;
+
+	dev_dbg(&i2c->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n",
+		clk_sys_ratios[clk_config].rate,
+		clk_sys_ratios[clk_config].mode,
+		clk_sys_ratios[clk_config].div);
+
+	dev_dbg(&i2c->dev, "Actual CLK_SYS = %dHz\n", clk_sys);
+
+	/* We may not get quite the right frequency if using
+	 * approximate clocks so look for the closest match that is
+	 * higher than the target (we need to ensure that there enough
+	 * BCLKs to clock out the samples).
+	 */
+	bclk_div = 0;
+	best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk;
+	i = 1;
+	while (i < ARRAY_SIZE(bclk_divs)) {
+		cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk;
+		if (cur_val < 0) /* BCLK table is sorted */
+			break;
+		bclk_div = i;
+		best_val = cur_val;
+		i++;
+	}
+
+	aif2 &= ~WM8903_BCLK_DIV_MASK;
+	aif3 &= ~WM8903_LRCLK_RATE_MASK;
+
+	dev_dbg(&i2c->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n",
+		bclk_divs[bclk_div].ratio / 10, bclk,
+		(clk_sys * 10) / bclk_divs[bclk_div].ratio);
+
+	aif2 |= bclk_divs[bclk_div].div;
+	aif3 |= bclk / fs;
+
+	wm8903_write(codec, WM8903_CLOCK_RATES_0, clock0);
+	wm8903_write(codec, WM8903_CLOCK_RATES_1, clock1);
+	wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+	wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
+	wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+
+	return 0;
+}
+
+#define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\
+			       SNDRV_PCM_RATE_11025 |	\
+			       SNDRV_PCM_RATE_16000 |	\
+			       SNDRV_PCM_RATE_22050 |	\
+			       SNDRV_PCM_RATE_32000 |	\
+			       SNDRV_PCM_RATE_44100 |	\
+			       SNDRV_PCM_RATE_48000 |	\
+			       SNDRV_PCM_RATE_88200 |	\
+			       SNDRV_PCM_RATE_96000)
+
+#define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+			      SNDRV_PCM_RATE_11025 |	\
+			      SNDRV_PCM_RATE_16000 |	\
+			      SNDRV_PCM_RATE_22050 |	\
+			      SNDRV_PCM_RATE_32000 |	\
+			      SNDRV_PCM_RATE_44100 |	\
+			      SNDRV_PCM_RATE_48000)
+
+#define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+			SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8903_dai = {
+	.name = "WM8903",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8903_PLAYBACK_RATES,
+		.formats = WM8903_FORMATS,
+	},
+	.capture = {
+		 .stream_name = "Capture",
+		 .channels_min = 2,
+		 .channels_max = 2,
+		 .rates = WM8903_CAPTURE_RATES,
+		 .formats = WM8903_FORMATS,
+	 },
+	.ops = {
+		 .startup = wm8903_startup,
+		 .shutdown = wm8903_shutdown,
+		 .hw_params = wm8903_hw_params,
+	},
+	.dai_ops = {
+		 .digital_mute = wm8903_digital_mute,
+		 .set_fmt = wm8903_set_dai_fmt,
+		 .set_sysclk = wm8903_set_dai_sysclk
+	}
+};
+EXPORT_SYMBOL_GPL(wm8903_dai);
+
+static int wm8903_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8903_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c = codec->control_data;
+	int i;
+	u16 *reg_cache = codec->reg_cache;
+	u16 *tmp_cache = kmemdup(codec->reg_cache, sizeof(wm8903_reg_defaults),
+				 GFP_KERNEL);
+
+	/* Bring the codec back up to standby first to minimise pop/clicks */
+	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8903_set_bias_level(codec, codec->suspend_bias_level);
+
+	/* Sync back everything else */
+	if (tmp_cache) {
+		for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
+			if (tmp_cache[i] != reg_cache[i])
+				wm8903_write(codec, i, tmp_cache[i]);
+	} else {
+		dev_err(&i2c->dev, "Failed to allocate temporary cache\n");
+	}
+
+	return 0;
+}
+
+/*
+ * initialise the WM8903 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8903_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c = codec->control_data;
+	int ret = 0;
+	u16 val;
+
+	val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+	if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
+		dev_err(&i2c->dev,
+			"Device with ID register %x is not a WM8903\n", val);
+		return -ENODEV;
+	}
+
+	codec->name = "WM8903";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8903_read;
+	codec->write = wm8903_write;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8903_set_bias_level;
+	codec->dai = &wm8903_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults);
+	codec->reg_cache = kmemdup(wm8903_reg_defaults,
+				   sizeof(wm8903_reg_defaults),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL) {
+		dev_err(&i2c->dev, "Failed to allocate register cache\n");
+		return -ENOMEM;
+	}
+
+	val = wm8903_read(codec, WM8903_REVISION_NUMBER);
+	dev_info(&i2c->dev, "WM8903 revision %d\n",
+		 val & WM8903_CHIP_REV_MASK);
+
+	wm8903_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* SYSCLK is required for pretty much anything */
+	wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA);
+
+	/* power on device */
+	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Latch volume update bits */
+	val = wm8903_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
+	val |= WM8903_ADCVU;
+	wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
+	wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
+
+	val = wm8903_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
+	val |= WM8903_DACVU;
+	wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
+	wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
+
+	val = wm8903_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
+	val |= WM8903_HPOUTVU;
+	wm8903_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
+	wm8903_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
+
+	val = wm8903_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
+	val |= WM8903_LINEOUTVU;
+	wm8903_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
+	wm8903_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
+
+	val = wm8903_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
+	val |= WM8903_SPKVU;
+	wm8903_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
+	wm8903_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
+
+	/* Enable DAC soft mute by default */
+	val = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+	val |= WM8903_DAC_MUTEMODE;
+	wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
+
+	wm8903_add_controls(codec);
+	wm8903_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "wm8903: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *wm8903_socdev;
+
+static int wm8903_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct snd_soc_device *socdev = wm8903_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = wm8903_init(socdev);
+	if (ret < 0)
+		dev_err(&i2c->dev, "Device initialisation failed\n");
+
+	return ret;
+}
+
+static int wm8903_i2c_remove(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	kfree(codec->reg_cache);
+	return 0;
+}
+
+/* i2c codec control layer */
+static const struct i2c_device_id wm8903_i2c_id[] = {
+       { "wm8903", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
+
+static struct i2c_driver wm8903_i2c_driver = {
+	.driver = {
+		.name = "WM8903",
+		.owner = THIS_MODULE,
+	},
+	.probe    = wm8903_i2c_probe,
+	.remove   = wm8903_i2c_remove,
+	.id_table = wm8903_i2c_id,
+};
+
+static int wm8903_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8903_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct wm8903_priv *wm8903;
+	struct i2c_board_info board_info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *i2c_client;
+	int ret = 0;
+
+	setup = socdev->codec_data;
+
+	if (!setup->i2c_address) {
+		dev_err(&pdev->dev, "No codec address provided\n");
+		return -ENODEV;
+	}
+
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
+	if (wm8903 == NULL) {
+		ret = -ENOMEM;
+		goto err_codec;
+	}
+
+	codec->private_data = wm8903;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	wm8903_socdev = socdev;
+
+	codec->hw_write = (hw_write_t)i2c_master_send;
+	ret = i2c_add_driver(&wm8903_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		goto err_priv;
+	} else {
+		memset(&board_info, 0, sizeof(board_info));
+		strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE);
+		board_info.addr = setup->i2c_address;
+
+		adapter = i2c_get_adapter(setup->i2c_bus);
+		if (!adapter) {
+			dev_err(&pdev->dev, "Can't get I2C bus %d\n",
+				setup->i2c_bus);
+			ret = -ENODEV;
+			goto err_adapter;
+		}
+
+		i2c_client = i2c_new_device(adapter, &board_info);
+		i2c_put_adapter(adapter);
+		if (i2c_client == NULL) {
+			dev_err(&pdev->dev,
+				"I2C driver registration failed\n");
+			ret = -ENODEV;
+			goto err_adapter;
+		}
+	}
+
+	return ret;
+
+err_adapter:
+	i2c_del_driver(&wm8903_i2c_driver);
+err_priv:
+	kfree(codec->private_data);
+err_codec:
+	kfree(codec);
+	return ret;
+}
+
+/* power down chip */
+static int wm8903_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+	i2c_unregister_device(socdev->codec->control_data);
+	i2c_del_driver(&wm8903_i2c_driver);
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8903 = {
+	.probe = 	wm8903_probe,
+	.remove = 	wm8903_remove,
+	.suspend = 	wm8903_suspend,
+	.resume =	wm8903_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
+
+MODULE_DESCRIPTION("ASoC WM8903 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
new file mode 100644
index 0000000..cec622f
--- /dev/null
+++ b/sound/soc/codecs/wm8903.h
@@ -0,0 +1,1463 @@
+/*
+ * wm8903.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.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.
+ */
+
+#ifndef _WM8903_H
+#define _WM8903_H
+
+#include <linux/i2c.h>
+
+extern struct snd_soc_dai wm8903_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8903;
+
+struct wm8903_setup_data {
+	int i2c_bus;
+	int i2c_address;
+};
+
+#define WM8903_MCLK_DIV_2 1
+#define WM8903_CLK_SYS    2
+#define WM8903_BCLK       3
+#define WM8903_LRCLK      4
+
+/*
+ * Register values.
+ */
+#define WM8903_SW_RESET_AND_ID                  0x00
+#define WM8903_REVISION_NUMBER                  0x01
+#define WM8903_BIAS_CONTROL_0                   0x04
+#define WM8903_VMID_CONTROL_0                   0x05
+#define WM8903_MIC_BIAS_CONTROL_0               0x06
+#define WM8903_ANALOGUE_DAC_0                   0x08
+#define WM8903_ANALOGUE_ADC_0                   0x0A
+#define WM8903_POWER_MANAGEMENT_0               0x0C
+#define WM8903_POWER_MANAGEMENT_1               0x0D
+#define WM8903_POWER_MANAGEMENT_2               0x0E
+#define WM8903_POWER_MANAGEMENT_3               0x0F
+#define WM8903_POWER_MANAGEMENT_4               0x10
+#define WM8903_POWER_MANAGEMENT_5               0x11
+#define WM8903_POWER_MANAGEMENT_6               0x12
+#define WM8903_CLOCK_RATES_0                    0x14
+#define WM8903_CLOCK_RATES_1                    0x15
+#define WM8903_CLOCK_RATES_2                    0x16
+#define WM8903_AUDIO_INTERFACE_0                0x18
+#define WM8903_AUDIO_INTERFACE_1                0x19
+#define WM8903_AUDIO_INTERFACE_2                0x1A
+#define WM8903_AUDIO_INTERFACE_3                0x1B
+#define WM8903_DAC_DIGITAL_VOLUME_LEFT          0x1E
+#define WM8903_DAC_DIGITAL_VOLUME_RIGHT         0x1F
+#define WM8903_DAC_DIGITAL_0                    0x20
+#define WM8903_DAC_DIGITAL_1                    0x21
+#define WM8903_ADC_DIGITAL_VOLUME_LEFT          0x24
+#define WM8903_ADC_DIGITAL_VOLUME_RIGHT         0x25
+#define WM8903_ADC_DIGITAL_0                    0x26
+#define WM8903_DIGITAL_MICROPHONE_0             0x27
+#define WM8903_DRC_0                            0x28
+#define WM8903_DRC_1                            0x29
+#define WM8903_DRC_2                            0x2A
+#define WM8903_DRC_3                            0x2B
+#define WM8903_ANALOGUE_LEFT_INPUT_0            0x2C
+#define WM8903_ANALOGUE_RIGHT_INPUT_0           0x2D
+#define WM8903_ANALOGUE_LEFT_INPUT_1            0x2E
+#define WM8903_ANALOGUE_RIGHT_INPUT_1           0x2F
+#define WM8903_ANALOGUE_LEFT_MIX_0              0x32
+#define WM8903_ANALOGUE_RIGHT_MIX_0             0x33
+#define WM8903_ANALOGUE_SPK_MIX_LEFT_0          0x34
+#define WM8903_ANALOGUE_SPK_MIX_LEFT_1          0x35
+#define WM8903_ANALOGUE_SPK_MIX_RIGHT_0         0x36
+#define WM8903_ANALOGUE_SPK_MIX_RIGHT_1         0x37
+#define WM8903_ANALOGUE_OUT1_LEFT               0x39
+#define WM8903_ANALOGUE_OUT1_RIGHT              0x3A
+#define WM8903_ANALOGUE_OUT2_LEFT               0x3B
+#define WM8903_ANALOGUE_OUT2_RIGHT              0x3C
+#define WM8903_ANALOGUE_OUT3_LEFT               0x3E
+#define WM8903_ANALOGUE_OUT3_RIGHT              0x3F
+#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0    0x41
+#define WM8903_DC_SERVO_0                       0x43
+#define WM8903_DC_SERVO_2                       0x45
+#define WM8903_ANALOGUE_HP_0                    0x5A
+#define WM8903_ANALOGUE_LINEOUT_0               0x5E
+#define WM8903_CHARGE_PUMP_0                    0x62
+#define WM8903_CLASS_W_0                        0x68
+#define WM8903_WRITE_SEQUENCER_0                0x6C
+#define WM8903_WRITE_SEQUENCER_1                0x6D
+#define WM8903_WRITE_SEQUENCER_2                0x6E
+#define WM8903_WRITE_SEQUENCER_3                0x6F
+#define WM8903_WRITE_SEQUENCER_4                0x70
+#define WM8903_CONTROL_INTERFACE                0x72
+#define WM8903_GPIO_CONTROL_1                   0x74
+#define WM8903_GPIO_CONTROL_2                   0x75
+#define WM8903_GPIO_CONTROL_3                   0x76
+#define WM8903_GPIO_CONTROL_4                   0x77
+#define WM8903_GPIO_CONTROL_5                   0x78
+#define WM8903_INTERRUPT_STATUS_1               0x79
+#define WM8903_INTERRUPT_STATUS_1_MASK          0x7A
+#define WM8903_INTERRUPT_POLARITY_1             0x7B
+#define WM8903_INTERRUPT_CONTROL                0x7E
+#define WM8903_CONTROL_INTERFACE_TEST_1         0x81
+#define WM8903_CHARGE_PUMP_TEST_1               0x95
+#define WM8903_CLOCK_RATE_TEST_4                0xA4
+#define WM8903_ANALOGUE_OUTPUT_BIAS_0           0xAC
+
+#define WM8903_REGISTER_COUNT                   75
+#define WM8903_MAX_REGISTER                     0xAC
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - SW Reset and ID
+ */
+#define WM8903_SW_RESET_DEV_ID1_MASK            0xFFFF  /* SW_RESET_DEV_ID1 - [15:0] */
+#define WM8903_SW_RESET_DEV_ID1_SHIFT                0  /* SW_RESET_DEV_ID1 - [15:0] */
+#define WM8903_SW_RESET_DEV_ID1_WIDTH               16  /* SW_RESET_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Revision Number
+ */
+#define WM8903_CHIP_REV_MASK                    0x000F  /* CHIP_REV - [3:0] */
+#define WM8903_CHIP_REV_SHIFT                        0  /* CHIP_REV - [3:0] */
+#define WM8903_CHIP_REV_WIDTH                        4  /* CHIP_REV - [3:0] */
+
+/*
+ * R4 (0x04) - Bias Control 0
+ */
+#define WM8903_POBCTRL                          0x0010  /* POBCTRL */
+#define WM8903_POBCTRL_MASK                     0x0010  /* POBCTRL */
+#define WM8903_POBCTRL_SHIFT                         4  /* POBCTRL */
+#define WM8903_POBCTRL_WIDTH                         1  /* POBCTRL */
+#define WM8903_ISEL_MASK                        0x000C  /* ISEL - [3:2] */
+#define WM8903_ISEL_SHIFT                            2  /* ISEL - [3:2] */
+#define WM8903_ISEL_WIDTH                            2  /* ISEL - [3:2] */
+#define WM8903_STARTUP_BIAS_ENA                 0x0002  /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_MASK            0x0002  /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_SHIFT                1  /* STARTUP_BIAS_ENA */
+#define WM8903_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+#define WM8903_BIAS_ENA                         0x0001  /* BIAS_ENA */
+#define WM8903_BIAS_ENA_MASK                    0x0001  /* BIAS_ENA */
+#define WM8903_BIAS_ENA_SHIFT                        0  /* BIAS_ENA */
+#define WM8903_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+
+/*
+ * R5 (0x05) - VMID Control 0
+ */
+#define WM8903_VMID_TIE_ENA                     0x0080  /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_MASK                0x0080  /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_SHIFT                    7  /* VMID_TIE_ENA */
+#define WM8903_VMID_TIE_ENA_WIDTH                    1  /* VMID_TIE_ENA */
+#define WM8903_BUFIO_ENA                        0x0040  /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_MASK                   0x0040  /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_SHIFT                       6  /* BUFIO_ENA */
+#define WM8903_BUFIO_ENA_WIDTH                       1  /* BUFIO_ENA */
+#define WM8903_VMID_IO_ENA                      0x0020  /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_MASK                 0x0020  /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_SHIFT                     5  /* VMID_IO_ENA */
+#define WM8903_VMID_IO_ENA_WIDTH                     1  /* VMID_IO_ENA */
+#define WM8903_VMID_SOFT_MASK                   0x0018  /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_SOFT_SHIFT                       3  /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_SOFT_WIDTH                       2  /* VMID_SOFT - [4:3] */
+#define WM8903_VMID_RES_MASK                    0x0006  /* VMID_RES - [2:1] */
+#define WM8903_VMID_RES_SHIFT                        1  /* VMID_RES - [2:1] */
+#define WM8903_VMID_RES_WIDTH                        2  /* VMID_RES - [2:1] */
+#define WM8903_VMID_BUF_ENA                     0x0001  /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_MASK                0x0001  /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_SHIFT                    0  /* VMID_BUF_ENA */
+#define WM8903_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+
+#define WM8903_VMID_RES_50K                          2
+#define WM8903_VMID_RES_250K                         3
+#define WM8903_VMID_RES_5K                           4
+
+/*
+ * R6 (0x06) - Mic Bias Control 0
+ */
+#define WM8903_MICDET_HYST_ENA                  0x0080  /* MICDET_HYST_ENA */
+#define WM8903_MICDET_HYST_ENA_MASK             0x0080  /* MICDET_HYST_ENA */
+#define WM8903_MICDET_HYST_ENA_SHIFT                 7  /* MICDET_HYST_ENA */
+#define WM8903_MICDET_HYST_ENA_WIDTH                 1  /* MICDET_HYST_ENA */
+#define WM8903_MICDET_THR_MASK                  0x0070  /* MICDET_THR - [6:4] */
+#define WM8903_MICDET_THR_SHIFT                      4  /* MICDET_THR - [6:4] */
+#define WM8903_MICDET_THR_WIDTH                      3  /* MICDET_THR - [6:4] */
+#define WM8903_MICSHORT_THR_MASK                0x000C  /* MICSHORT_THR - [3:2] */
+#define WM8903_MICSHORT_THR_SHIFT                    2  /* MICSHORT_THR - [3:2] */
+#define WM8903_MICSHORT_THR_WIDTH                    2  /* MICSHORT_THR - [3:2] */
+#define WM8903_MICDET_ENA                       0x0002  /* MICDET_ENA */
+#define WM8903_MICDET_ENA_MASK                  0x0002  /* MICDET_ENA */
+#define WM8903_MICDET_ENA_SHIFT                      1  /* MICDET_ENA */
+#define WM8903_MICDET_ENA_WIDTH                      1  /* MICDET_ENA */
+#define WM8903_MICBIAS_ENA                      0x0001  /* MICBIAS_ENA */
+#define WM8903_MICBIAS_ENA_MASK                 0x0001  /* MICBIAS_ENA */
+#define WM8903_MICBIAS_ENA_SHIFT                     0  /* MICBIAS_ENA */
+#define WM8903_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */
+
+/*
+ * R8 (0x08) - Analogue DAC 0
+ */
+#define WM8903_DACBIAS_SEL_MASK                 0x0018  /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACBIAS_SEL_SHIFT                     3  /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACBIAS_SEL_WIDTH                     2  /* DACBIAS_SEL - [4:3] */
+#define WM8903_DACVMID_BIAS_SEL_MASK            0x0006  /* DACVMID_BIAS_SEL - [2:1] */
+#define WM8903_DACVMID_BIAS_SEL_SHIFT                1  /* DACVMID_BIAS_SEL - [2:1] */
+#define WM8903_DACVMID_BIAS_SEL_WIDTH                2  /* DACVMID_BIAS_SEL - [2:1] */
+
+/*
+ * R10 (0x0A) - Analogue ADC 0
+ */
+#define WM8903_ADC_OSR128                       0x0001  /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_MASK                  0x0001  /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_SHIFT                      0  /* ADC_OSR128 */
+#define WM8903_ADC_OSR128_WIDTH                      1  /* ADC_OSR128 */
+
+/*
+ * R12 (0x0C) - Power Management 0
+ */
+#define WM8903_INL_ENA                          0x0002  /* INL_ENA */
+#define WM8903_INL_ENA_MASK                     0x0002  /* INL_ENA */
+#define WM8903_INL_ENA_SHIFT                         1  /* INL_ENA */
+#define WM8903_INL_ENA_WIDTH                         1  /* INL_ENA */
+#define WM8903_INR_ENA                          0x0001  /* INR_ENA */
+#define WM8903_INR_ENA_MASK                     0x0001  /* INR_ENA */
+#define WM8903_INR_ENA_SHIFT                         0  /* INR_ENA */
+#define WM8903_INR_ENA_WIDTH                         1  /* INR_ENA */
+
+/*
+ * R13 (0x0D) - Power Management 1
+ */
+#define WM8903_MIXOUTL_ENA                      0x0002  /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_MASK                 0x0002  /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_SHIFT                     1  /* MIXOUTL_ENA */
+#define WM8903_MIXOUTL_ENA_WIDTH                     1  /* MIXOUTL_ENA */
+#define WM8903_MIXOUTR_ENA                      0x0001  /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_MASK                 0x0001  /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_SHIFT                     0  /* MIXOUTR_ENA */
+#define WM8903_MIXOUTR_ENA_WIDTH                     1  /* MIXOUTR_ENA */
+
+/*
+ * R14 (0x0E) - Power Management 2
+ */
+#define WM8903_HPL_PGA_ENA                      0x0002  /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_MASK                 0x0002  /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_SHIFT                     1  /* HPL_PGA_ENA */
+#define WM8903_HPL_PGA_ENA_WIDTH                     1  /* HPL_PGA_ENA */
+#define WM8903_HPR_PGA_ENA                      0x0001  /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_MASK                 0x0001  /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_SHIFT                     0  /* HPR_PGA_ENA */
+#define WM8903_HPR_PGA_ENA_WIDTH                     1  /* HPR_PGA_ENA */
+
+/*
+ * R15 (0x0F) - Power Management 3
+ */
+#define WM8903_LINEOUTL_PGA_ENA                 0x0002  /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_MASK            0x0002  /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_SHIFT                1  /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTL_PGA_ENA_WIDTH                1  /* LINEOUTL_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA                 0x0001  /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_MASK            0x0001  /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_SHIFT                0  /* LINEOUTR_PGA_ENA */
+#define WM8903_LINEOUTR_PGA_ENA_WIDTH                1  /* LINEOUTR_PGA_ENA */
+
+/*
+ * R16 (0x10) - Power Management 4
+ */
+#define WM8903_MIXSPKL_ENA                      0x0002  /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_MASK                 0x0002  /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_SHIFT                     1  /* MIXSPKL_ENA */
+#define WM8903_MIXSPKL_ENA_WIDTH                     1  /* MIXSPKL_ENA */
+#define WM8903_MIXSPKR_ENA                      0x0001  /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_MASK                 0x0001  /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_SHIFT                     0  /* MIXSPKR_ENA */
+#define WM8903_MIXSPKR_ENA_WIDTH                     1  /* MIXSPKR_ENA */
+
+/*
+ * R17 (0x11) - Power Management 5
+ */
+#define WM8903_SPKL_ENA                         0x0002  /* SPKL_ENA */
+#define WM8903_SPKL_ENA_MASK                    0x0002  /* SPKL_ENA */
+#define WM8903_SPKL_ENA_SHIFT                        1  /* SPKL_ENA */
+#define WM8903_SPKL_ENA_WIDTH                        1  /* SPKL_ENA */
+#define WM8903_SPKR_ENA                         0x0001  /* SPKR_ENA */
+#define WM8903_SPKR_ENA_MASK                    0x0001  /* SPKR_ENA */
+#define WM8903_SPKR_ENA_SHIFT                        0  /* SPKR_ENA */
+#define WM8903_SPKR_ENA_WIDTH                        1  /* SPKR_ENA */
+
+/*
+ * R18 (0x12) - Power Management 6
+ */
+#define WM8903_DACL_ENA                         0x0008  /* DACL_ENA */
+#define WM8903_DACL_ENA_MASK                    0x0008  /* DACL_ENA */
+#define WM8903_DACL_ENA_SHIFT                        3  /* DACL_ENA */
+#define WM8903_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8903_DACR_ENA                         0x0004  /* DACR_ENA */
+#define WM8903_DACR_ENA_MASK                    0x0004  /* DACR_ENA */
+#define WM8903_DACR_ENA_SHIFT                        2  /* DACR_ENA */
+#define WM8903_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+#define WM8903_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8903_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8903_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8903_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8903_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8903_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8903_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8903_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R20 (0x14) - Clock Rates 0
+ */
+#define WM8903_MCLKDIV2                         0x0001  /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_MASK                    0x0001  /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_SHIFT                        0  /* MCLKDIV2 */
+#define WM8903_MCLKDIV2_WIDTH                        1  /* MCLKDIV2 */
+
+/*
+ * R21 (0x15) - Clock Rates 1
+ */
+#define WM8903_CLK_SYS_RATE_MASK                0x3C00  /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_RATE_SHIFT                   10  /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [13:10] */
+#define WM8903_CLK_SYS_MODE_MASK                0x0300  /* CLK_SYS_MODE - [9:8] */
+#define WM8903_CLK_SYS_MODE_SHIFT                    8  /* CLK_SYS_MODE - [9:8] */
+#define WM8903_CLK_SYS_MODE_WIDTH                    2  /* CLK_SYS_MODE - [9:8] */
+#define WM8903_SAMPLE_RATE_MASK                 0x000F  /* SAMPLE_RATE - [3:0] */
+#define WM8903_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [3:0] */
+#define WM8903_SAMPLE_RATE_WIDTH                     4  /* SAMPLE_RATE - [3:0] */
+
+/*
+ * R22 (0x16) - Clock Rates 2
+ */
+#define WM8903_CLK_SYS_ENA                      0x0004  /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_MASK                 0x0004  /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_SHIFT                     2  /* CLK_SYS_ENA */
+#define WM8903_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+#define WM8903_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_MASK                 0x0002  /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_SHIFT                     1  /* CLK_DSP_ENA */
+#define WM8903_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM8903_TO_ENA                           0x0001  /* TO_ENA */
+#define WM8903_TO_ENA_MASK                      0x0001  /* TO_ENA */
+#define WM8903_TO_ENA_SHIFT                          0  /* TO_ENA */
+#define WM8903_TO_ENA_WIDTH                          1  /* TO_ENA */
+
+/*
+ * R24 (0x18) - Audio Interface 0
+ */
+#define WM8903_DACL_DATINV                      0x1000  /* DACL_DATINV */
+#define WM8903_DACL_DATINV_MASK                 0x1000  /* DACL_DATINV */
+#define WM8903_DACL_DATINV_SHIFT                    12  /* DACL_DATINV */
+#define WM8903_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8903_DACR_DATINV                      0x0800  /* DACR_DATINV */
+#define WM8903_DACR_DATINV_MASK                 0x0800  /* DACR_DATINV */
+#define WM8903_DACR_DATINV_SHIFT                    11  /* DACR_DATINV */
+#define WM8903_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+#define WM8903_DAC_BOOST_MASK                   0x0600  /* DAC_BOOST - [10:9] */
+#define WM8903_DAC_BOOST_SHIFT                       9  /* DAC_BOOST - [10:9] */
+#define WM8903_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [10:9] */
+#define WM8903_LOOPBACK                         0x0100  /* LOOPBACK */
+#define WM8903_LOOPBACK_MASK                    0x0100  /* LOOPBACK */
+#define WM8903_LOOPBACK_SHIFT                        8  /* LOOPBACK */
+#define WM8903_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+#define WM8903_AIFADCL_SRC                      0x0080  /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_MASK                 0x0080  /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_SHIFT                     7  /* AIFADCL_SRC */
+#define WM8903_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8903_AIFADCR_SRC                      0x0040  /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_MASK                 0x0040  /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_SHIFT                     6  /* AIFADCR_SRC */
+#define WM8903_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8903_AIFDACL_SRC                      0x0020  /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_MASK                 0x0020  /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_SHIFT                     5  /* AIFDACL_SRC */
+#define WM8903_AIFDACL_SRC_WIDTH                     1  /* AIFDACL_SRC */
+#define WM8903_AIFDACR_SRC                      0x0010  /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_MASK                 0x0010  /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_SHIFT                     4  /* AIFDACR_SRC */
+#define WM8903_AIFDACR_SRC_WIDTH                     1  /* AIFDACR_SRC */
+#define WM8903_ADC_COMP                         0x0008  /* ADC_COMP */
+#define WM8903_ADC_COMP_MASK                    0x0008  /* ADC_COMP */
+#define WM8903_ADC_COMP_SHIFT                        3  /* ADC_COMP */
+#define WM8903_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8903_ADC_COMPMODE                     0x0004  /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_MASK                0x0004  /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_SHIFT                    2  /* ADC_COMPMODE */
+#define WM8903_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8903_DAC_COMP                         0x0002  /* DAC_COMP */
+#define WM8903_DAC_COMP_MASK                    0x0002  /* DAC_COMP */
+#define WM8903_DAC_COMP_SHIFT                        1  /* DAC_COMP */
+#define WM8903_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8903_DAC_COMPMODE                     0x0001  /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_MASK                0x0001  /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_SHIFT                    0  /* DAC_COMPMODE */
+#define WM8903_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+
+/*
+ * R25 (0x19) - Audio Interface 1
+ */
+#define WM8903_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8903_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8903_AIFADC_TDM                       0x0800  /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_MASK                  0x0800  /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_SHIFT                     11  /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8903_AIFADC_TDM_CHAN                  0x0400  /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_MASK             0x0400  /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_SHIFT                10  /* AIFADC_TDM_CHAN */
+#define WM8903_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8903_LRCLK_DIR                        0x0200  /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_MASK                   0x0200  /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_SHIFT                       9  /* LRCLK_DIR */
+#define WM8903_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM8903_AIF_BCLK_INV                     0x0080  /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_MASK                0x0080  /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_SHIFT                    7  /* AIF_BCLK_INV */
+#define WM8903_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8903_BCLK_DIR                         0x0040  /* BCLK_DIR */
+#define WM8903_BCLK_DIR_MASK                    0x0040  /* BCLK_DIR */
+#define WM8903_BCLK_DIR_SHIFT                        6  /* BCLK_DIR */
+#define WM8903_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM8903_AIF_LRCLK_INV                    0x0010  /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_MASK               0x0010  /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_SHIFT                   4  /* AIF_LRCLK_INV */
+#define WM8903_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8903_AIF_WL_MASK                      0x000C  /* AIF_WL - [3:2] */
+#define WM8903_AIF_WL_SHIFT                          2  /* AIF_WL - [3:2] */
+#define WM8903_AIF_WL_WIDTH                          2  /* AIF_WL - [3:2] */
+#define WM8903_AIF_FMT_MASK                     0x0003  /* AIF_FMT - [1:0] */
+#define WM8903_AIF_FMT_SHIFT                         0  /* AIF_FMT - [1:0] */
+#define WM8903_AIF_FMT_WIDTH                         2  /* AIF_FMT - [1:0] */
+
+/*
+ * R26 (0x1A) - Audio Interface 2
+ */
+#define WM8903_BCLK_DIV_MASK                    0x001F  /* BCLK_DIV - [4:0] */
+#define WM8903_BCLK_DIV_SHIFT                        0  /* BCLK_DIV - [4:0] */
+#define WM8903_BCLK_DIV_WIDTH                        5  /* BCLK_DIV - [4:0] */
+
+/*
+ * R27 (0x1B) - Audio Interface 3
+ */
+#define WM8903_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM8903_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM8903_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R30 (0x1E) - DAC Digital Volume Left
+ */
+#define WM8903_DACVU                            0x0100  /* DACVU */
+#define WM8903_DACVU_MASK                       0x0100  /* DACVU */
+#define WM8903_DACVU_SHIFT                           8  /* DACVU */
+#define WM8903_DACVU_WIDTH                           1  /* DACVU */
+#define WM8903_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8903_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8903_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital Volume Right
+ */
+#define WM8903_DACVU                            0x0100  /* DACVU */
+#define WM8903_DACVU_MASK                       0x0100  /* DACVU */
+#define WM8903_DACVU_SHIFT                           8  /* DACVU */
+#define WM8903_DACVU_WIDTH                           1  /* DACVU */
+#define WM8903_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8903_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8903_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R32 (0x20) - DAC Digital 0
+ */
+#define WM8903_ADCL_DAC_SVOL_MASK               0x0F00  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCL_DAC_SVOL_SHIFT                   8  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8903_ADCR_DAC_SVOL_MASK               0x00F0  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADCR_DAC_SVOL_SHIFT                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8903_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8903_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8903_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8903_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R33 (0x21) - DAC Digital 1
+ */
+#define WM8903_DAC_MONO                         0x1000  /* DAC_MONO */
+#define WM8903_DAC_MONO_MASK                    0x1000  /* DAC_MONO */
+#define WM8903_DAC_MONO_SHIFT                       12  /* DAC_MONO */
+#define WM8903_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8903_DAC_SB_FILT                      0x0800  /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_MASK                 0x0800  /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_SHIFT                    11  /* DAC_SB_FILT */
+#define WM8903_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8903_DAC_MUTERATE                     0x0400  /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_MASK                0x0400  /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_SHIFT                   10  /* DAC_MUTERATE */
+#define WM8903_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8903_DAC_MUTEMODE                     0x0200  /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_MASK                0x0200  /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_SHIFT                    9  /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTEMODE_WIDTH                    1  /* DAC_MUTEMODE */
+#define WM8903_DAC_MUTE                         0x0008  /* DAC_MUTE */
+#define WM8903_DAC_MUTE_MASK                    0x0008  /* DAC_MUTE */
+#define WM8903_DAC_MUTE_SHIFT                        3  /* DAC_MUTE */
+#define WM8903_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8903_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8903_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8903_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R36 (0x24) - ADC Digital Volume Left
+ */
+#define WM8903_ADCVU                            0x0100  /* ADCVU */
+#define WM8903_ADCVU_MASK                       0x0100  /* ADCVU */
+#define WM8903_ADCVU_SHIFT                           8  /* ADCVU */
+#define WM8903_ADCVU_WIDTH                           1  /* ADCVU */
+#define WM8903_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8903_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8903_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R37 (0x25) - ADC Digital Volume Right
+ */
+#define WM8903_ADCVU                            0x0100  /* ADCVU */
+#define WM8903_ADCVU_MASK                       0x0100  /* ADCVU */
+#define WM8903_ADCVU_SHIFT                           8  /* ADCVU */
+#define WM8903_ADCVU_WIDTH                           1  /* ADCVU */
+#define WM8903_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8903_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8903_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R38 (0x26) - ADC Digital 0
+ */
+#define WM8903_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8903_ADC_HPF_ENA                      0x0010  /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_MASK                 0x0010  /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_SHIFT                     4  /* ADC_HPF_ENA */
+#define WM8903_ADC_HPF_ENA_WIDTH                     1  /* ADC_HPF_ENA */
+#define WM8903_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8903_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8903_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8903_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R39 (0x27) - Digital Microphone 0
+ */
+#define WM8903_DIGMIC_MODE_SEL                  0x0100  /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_MASK             0x0100  /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_SHIFT                 8  /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_MODE_SEL_WIDTH                 1  /* DIGMIC_MODE_SEL */
+#define WM8903_DIGMIC_CLK_SEL_L_MASK            0x00C0  /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_L_SHIFT                6  /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_L_WIDTH                2  /* DIGMIC_CLK_SEL_L - [7:6] */
+#define WM8903_DIGMIC_CLK_SEL_R_MASK            0x0030  /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_R_SHIFT                4  /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_R_WIDTH                2  /* DIGMIC_CLK_SEL_R - [5:4] */
+#define WM8903_DIGMIC_CLK_SEL_RT_MASK           0x000C  /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_RT_SHIFT               2  /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_RT_WIDTH               2  /* DIGMIC_CLK_SEL_RT - [3:2] */
+#define WM8903_DIGMIC_CLK_SEL_MASK              0x0003  /* DIGMIC_CLK_SEL - [1:0] */
+#define WM8903_DIGMIC_CLK_SEL_SHIFT                  0  /* DIGMIC_CLK_SEL - [1:0] */
+#define WM8903_DIGMIC_CLK_SEL_WIDTH                  2  /* DIGMIC_CLK_SEL - [1:0] */
+
+/*
+ * R40 (0x28) - DRC 0
+ */
+#define WM8903_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM8903_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM8903_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM8903_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM8903_DRC_THRESH_HYST_MASK             0x1800  /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_THRESH_HYST_SHIFT                11  /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_THRESH_HYST_WIDTH                 2  /* DRC_THRESH_HYST - [12:11] */
+#define WM8903_DRC_STARTUP_GAIN_MASK            0x07C0  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_STARTUP_GAIN_SHIFT                6  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8903_DRC_FF_DELAY                     0x0020  /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_MASK                0x0020  /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_SHIFT                    5  /* DRC_FF_DELAY */
+#define WM8903_DRC_FF_DELAY_WIDTH                    1  /* DRC_FF_DELAY */
+#define WM8903_DRC_SMOOTH_ENA                   0x0008  /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_MASK              0x0008  /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_SHIFT                  3  /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_SMOOTH_ENA_WIDTH                  1  /* DRC_SMOOTH_ENA */
+#define WM8903_DRC_QR_ENA                       0x0004  /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_MASK                  0x0004  /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_SHIFT                      2  /* DRC_QR_ENA */
+#define WM8903_DRC_QR_ENA_WIDTH                      1  /* DRC_QR_ENA */
+#define WM8903_DRC_ANTICLIP_ENA                 0x0002  /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_MASK            0x0002  /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_SHIFT                1  /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_ANTICLIP_ENA_WIDTH                1  /* DRC_ANTICLIP_ENA */
+#define WM8903_DRC_HYST_ENA                     0x0001  /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_MASK                0x0001  /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_SHIFT                    0  /* DRC_HYST_ENA */
+#define WM8903_DRC_HYST_ENA_WIDTH                    1  /* DRC_HYST_ENA */
+
+/*
+ * R41 (0x29) - DRC 1
+ */
+#define WM8903_DRC_ATTACK_RATE_MASK             0xF000  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_ATTACK_RATE_SHIFT                12  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_ATTACK_RATE_WIDTH                 4  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8903_DRC_DECAY_RATE_MASK              0x0F00  /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_DECAY_RATE_SHIFT                  8  /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_DECAY_RATE_WIDTH                  4  /* DRC_DECAY_RATE - [11:8] */
+#define WM8903_DRC_THRESH_QR_MASK               0x00C0  /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_THRESH_QR_SHIFT                   6  /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_THRESH_QR_WIDTH                   2  /* DRC_THRESH_QR - [7:6] */
+#define WM8903_DRC_RATE_QR_MASK                 0x0030  /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_RATE_QR_SHIFT                     4  /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_RATE_QR_WIDTH                     2  /* DRC_RATE_QR - [5:4] */
+#define WM8903_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8903_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM8903_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM8903_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R42 (0x2A) - DRC 2
+ */
+#define WM8903_DRC_R0_SLOPE_COMP_MASK           0x0038  /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R0_SLOPE_COMP_SHIFT               3  /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R0_SLOPE_COMP_WIDTH               3  /* DRC_R0_SLOPE_COMP - [5:3] */
+#define WM8903_DRC_R1_SLOPE_COMP_MASK           0x0007  /* DRC_R1_SLOPE_COMP - [2:0] */
+#define WM8903_DRC_R1_SLOPE_COMP_SHIFT               0  /* DRC_R1_SLOPE_COMP - [2:0] */
+#define WM8903_DRC_R1_SLOPE_COMP_WIDTH               3  /* DRC_R1_SLOPE_COMP - [2:0] */
+
+/*
+ * R43 (0x2B) - DRC 3
+ */
+#define WM8903_DRC_THRESH_COMP_MASK             0x07E0  /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_THRESH_COMP_SHIFT                 5  /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_THRESH_COMP_WIDTH                 6  /* DRC_THRESH_COMP - [10:5] */
+#define WM8903_DRC_AMP_COMP_MASK                0x001F  /* DRC_AMP_COMP - [4:0] */
+#define WM8903_DRC_AMP_COMP_SHIFT                    0  /* DRC_AMP_COMP - [4:0] */
+#define WM8903_DRC_AMP_COMP_WIDTH                    5  /* DRC_AMP_COMP - [4:0] */
+
+/*
+ * R44 (0x2C) - Analogue Left Input 0
+ */
+#define WM8903_LINMUTE                          0x0080  /* LINMUTE */
+#define WM8903_LINMUTE_MASK                     0x0080  /* LINMUTE */
+#define WM8903_LINMUTE_SHIFT                         7  /* LINMUTE */
+#define WM8903_LINMUTE_WIDTH                         1  /* LINMUTE */
+#define WM8903_LIN_VOL_MASK                     0x001F  /* LIN_VOL - [4:0] */
+#define WM8903_LIN_VOL_SHIFT                         0  /* LIN_VOL - [4:0] */
+#define WM8903_LIN_VOL_WIDTH                         5  /* LIN_VOL - [4:0] */
+
+/*
+ * R45 (0x2D) - Analogue Right Input 0
+ */
+#define WM8903_RINMUTE                          0x0080  /* RINMUTE */
+#define WM8903_RINMUTE_MASK                     0x0080  /* RINMUTE */
+#define WM8903_RINMUTE_SHIFT                         7  /* RINMUTE */
+#define WM8903_RINMUTE_WIDTH                         1  /* RINMUTE */
+#define WM8903_RIN_VOL_MASK                     0x001F  /* RIN_VOL - [4:0] */
+#define WM8903_RIN_VOL_SHIFT                         0  /* RIN_VOL - [4:0] */
+#define WM8903_RIN_VOL_WIDTH                         5  /* RIN_VOL - [4:0] */
+
+/*
+ * R46 (0x2E) - Analogue Left Input 1
+ */
+#define WM8903_INL_CM_ENA                       0x0040  /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_MASK                  0x0040  /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_SHIFT                      6  /* INL_CM_ENA */
+#define WM8903_INL_CM_ENA_WIDTH                      1  /* INL_CM_ENA */
+#define WM8903_L_IP_SEL_N_MASK                  0x0030  /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_N_SHIFT                      4  /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_N_WIDTH                      2  /* L_IP_SEL_N - [5:4] */
+#define WM8903_L_IP_SEL_P_MASK                  0x000C  /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_IP_SEL_P_SHIFT                      2  /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_IP_SEL_P_WIDTH                      2  /* L_IP_SEL_P - [3:2] */
+#define WM8903_L_MODE_MASK                      0x0003  /* L_MODE - [1:0] */
+#define WM8903_L_MODE_SHIFT                          0  /* L_MODE - [1:0] */
+#define WM8903_L_MODE_WIDTH                          2  /* L_MODE - [1:0] */
+
+/*
+ * R47 (0x2F) - Analogue Right Input 1
+ */
+#define WM8903_INR_CM_ENA                       0x0040  /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_MASK                  0x0040  /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_SHIFT                      6  /* INR_CM_ENA */
+#define WM8903_INR_CM_ENA_WIDTH                      1  /* INR_CM_ENA */
+#define WM8903_R_IP_SEL_N_MASK                  0x0030  /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_N_SHIFT                      4  /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_N_WIDTH                      2  /* R_IP_SEL_N - [5:4] */
+#define WM8903_R_IP_SEL_P_MASK                  0x000C  /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_IP_SEL_P_SHIFT                      2  /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_IP_SEL_P_WIDTH                      2  /* R_IP_SEL_P - [3:2] */
+#define WM8903_R_MODE_MASK                      0x0003  /* R_MODE - [1:0] */
+#define WM8903_R_MODE_SHIFT                          0  /* R_MODE - [1:0] */
+#define WM8903_R_MODE_WIDTH                          2  /* R_MODE - [1:0] */
+
+/*
+ * R50 (0x32) - Analogue Left Mix 0
+ */
+#define WM8903_DACL_TO_MIXOUTL                  0x0008  /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_MASK             0x0008  /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_SHIFT                 3  /* DACL_TO_MIXOUTL */
+#define WM8903_DACL_TO_MIXOUTL_WIDTH                 1  /* DACL_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL                  0x0004  /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_MASK             0x0004  /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_SHIFT                 2  /* DACR_TO_MIXOUTL */
+#define WM8903_DACR_TO_MIXOUTL_WIDTH                 1  /* DACR_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL               0x0002  /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_MASK          0x0002  /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_SHIFT              1  /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSL_TO_MIXOUTL_WIDTH              1  /* BYPASSL_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL               0x0001  /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_MASK          0x0001  /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_SHIFT              0  /* BYPASSR_TO_MIXOUTL */
+#define WM8903_BYPASSR_TO_MIXOUTL_WIDTH              1  /* BYPASSR_TO_MIXOUTL */
+
+/*
+ * R51 (0x33) - Analogue Right Mix 0
+ */
+#define WM8903_DACL_TO_MIXOUTR                  0x0008  /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_MASK             0x0008  /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_SHIFT                 3  /* DACL_TO_MIXOUTR */
+#define WM8903_DACL_TO_MIXOUTR_WIDTH                 1  /* DACL_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR                  0x0004  /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_MASK             0x0004  /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_SHIFT                 2  /* DACR_TO_MIXOUTR */
+#define WM8903_DACR_TO_MIXOUTR_WIDTH                 1  /* DACR_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR               0x0002  /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_MASK          0x0002  /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_SHIFT              1  /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSL_TO_MIXOUTR_WIDTH              1  /* BYPASSL_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR               0x0001  /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_MASK          0x0001  /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_SHIFT              0  /* BYPASSR_TO_MIXOUTR */
+#define WM8903_BYPASSR_TO_MIXOUTR_WIDTH              1  /* BYPASSR_TO_MIXOUTR */
+
+/*
+ * R52 (0x34) - Analogue Spk Mix Left 0
+ */
+#define WM8903_DACL_TO_MIXSPKL                  0x0008  /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_MASK             0x0008  /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_SHIFT                 3  /* DACL_TO_MIXSPKL */
+#define WM8903_DACL_TO_MIXSPKL_WIDTH                 1  /* DACL_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL                  0x0004  /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_MASK             0x0004  /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_SHIFT                 2  /* DACR_TO_MIXSPKL */
+#define WM8903_DACR_TO_MIXSPKL_WIDTH                 1  /* DACR_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL               0x0002  /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_MASK          0x0002  /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_SHIFT              1  /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSL_TO_MIXSPKL_WIDTH              1  /* BYPASSL_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL               0x0001  /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_MASK          0x0001  /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_SHIFT              0  /* BYPASSR_TO_MIXSPKL */
+#define WM8903_BYPASSR_TO_MIXSPKL_WIDTH              1  /* BYPASSR_TO_MIXSPKL */
+
+/*
+ * R53 (0x35) - Analogue Spk Mix Left 1
+ */
+#define WM8903_DACL_MIXSPKL_VOL                 0x0008  /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_MASK            0x0008  /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_SHIFT                3  /* DACL_MIXSPKL_VOL */
+#define WM8903_DACL_MIXSPKL_VOL_WIDTH                1  /* DACL_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL                 0x0004  /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_MASK            0x0004  /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_SHIFT                2  /* DACR_MIXSPKL_VOL */
+#define WM8903_DACR_MIXSPKL_VOL_WIDTH                1  /* DACR_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL              0x0002  /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_MASK         0x0002  /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_SHIFT             1  /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSL_MIXSPKL_VOL_WIDTH             1  /* BYPASSL_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL              0x0001  /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_MASK         0x0001  /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_SHIFT             0  /* BYPASSR_MIXSPKL_VOL */
+#define WM8903_BYPASSR_MIXSPKL_VOL_WIDTH             1  /* BYPASSR_MIXSPKL_VOL */
+
+/*
+ * R54 (0x36) - Analogue Spk Mix Right 0
+ */
+#define WM8903_DACL_TO_MIXSPKR                  0x0008  /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_MASK             0x0008  /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_SHIFT                 3  /* DACL_TO_MIXSPKR */
+#define WM8903_DACL_TO_MIXSPKR_WIDTH                 1  /* DACL_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR                  0x0004  /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_MASK             0x0004  /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_SHIFT                 2  /* DACR_TO_MIXSPKR */
+#define WM8903_DACR_TO_MIXSPKR_WIDTH                 1  /* DACR_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR               0x0002  /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_MASK          0x0002  /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_SHIFT              1  /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSL_TO_MIXSPKR_WIDTH              1  /* BYPASSL_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR               0x0001  /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_MASK          0x0001  /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_SHIFT              0  /* BYPASSR_TO_MIXSPKR */
+#define WM8903_BYPASSR_TO_MIXSPKR_WIDTH              1  /* BYPASSR_TO_MIXSPKR */
+
+/*
+ * R55 (0x37) - Analogue Spk Mix Right 1
+ */
+#define WM8903_DACL_MIXSPKR_VOL                 0x0008  /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_MASK            0x0008  /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_SHIFT                3  /* DACL_MIXSPKR_VOL */
+#define WM8903_DACL_MIXSPKR_VOL_WIDTH                1  /* DACL_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL                 0x0004  /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_MASK            0x0004  /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_SHIFT                2  /* DACR_MIXSPKR_VOL */
+#define WM8903_DACR_MIXSPKR_VOL_WIDTH                1  /* DACR_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL              0x0002  /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_MASK         0x0002  /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_SHIFT             1  /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSL_MIXSPKR_VOL_WIDTH             1  /* BYPASSL_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL              0x0001  /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_MASK         0x0001  /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_SHIFT             0  /* BYPASSR_MIXSPKR_VOL */
+#define WM8903_BYPASSR_MIXSPKR_VOL_WIDTH             1  /* BYPASSR_MIXSPKR_VOL */
+
+/*
+ * R57 (0x39) - Analogue OUT1 Left
+ */
+#define WM8903_HPL_MUTE                         0x0100  /* HPL_MUTE */
+#define WM8903_HPL_MUTE_MASK                    0x0100  /* HPL_MUTE */
+#define WM8903_HPL_MUTE_SHIFT                        8  /* HPL_MUTE */
+#define WM8903_HPL_MUTE_WIDTH                        1  /* HPL_MUTE */
+#define WM8903_HPOUTVU                          0x0080  /* HPOUTVU */
+#define WM8903_HPOUTVU_MASK                     0x0080  /* HPOUTVU */
+#define WM8903_HPOUTVU_SHIFT                         7  /* HPOUTVU */
+#define WM8903_HPOUTVU_WIDTH                         1  /* HPOUTVU */
+#define WM8903_HPOUTLZC                         0x0040  /* HPOUTLZC */
+#define WM8903_HPOUTLZC_MASK                    0x0040  /* HPOUTLZC */
+#define WM8903_HPOUTLZC_SHIFT                        6  /* HPOUTLZC */
+#define WM8903_HPOUTLZC_WIDTH                        1  /* HPOUTLZC */
+#define WM8903_HPOUTL_VOL_MASK                  0x003F  /* HPOUTL_VOL - [5:0] */
+#define WM8903_HPOUTL_VOL_SHIFT                      0  /* HPOUTL_VOL - [5:0] */
+#define WM8903_HPOUTL_VOL_WIDTH                      6  /* HPOUTL_VOL - [5:0] */
+
+/*
+ * R58 (0x3A) - Analogue OUT1 Right
+ */
+#define WM8903_HPR_MUTE                         0x0100  /* HPR_MUTE */
+#define WM8903_HPR_MUTE_MASK                    0x0100  /* HPR_MUTE */
+#define WM8903_HPR_MUTE_SHIFT                        8  /* HPR_MUTE */
+#define WM8903_HPR_MUTE_WIDTH                        1  /* HPR_MUTE */
+#define WM8903_HPOUTVU                          0x0080  /* HPOUTVU */
+#define WM8903_HPOUTVU_MASK                     0x0080  /* HPOUTVU */
+#define WM8903_HPOUTVU_SHIFT                         7  /* HPOUTVU */
+#define WM8903_HPOUTVU_WIDTH                         1  /* HPOUTVU */
+#define WM8903_HPOUTRZC                         0x0040  /* HPOUTRZC */
+#define WM8903_HPOUTRZC_MASK                    0x0040  /* HPOUTRZC */
+#define WM8903_HPOUTRZC_SHIFT                        6  /* HPOUTRZC */
+#define WM8903_HPOUTRZC_WIDTH                        1  /* HPOUTRZC */
+#define WM8903_HPOUTR_VOL_MASK                  0x003F  /* HPOUTR_VOL - [5:0] */
+#define WM8903_HPOUTR_VOL_SHIFT                      0  /* HPOUTR_VOL - [5:0] */
+#define WM8903_HPOUTR_VOL_WIDTH                      6  /* HPOUTR_VOL - [5:0] */
+
+/*
+ * R59 (0x3B) - Analogue OUT2 Left
+ */
+#define WM8903_LINEOUTL_MUTE                    0x0100  /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_MASK               0x0100  /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_SHIFT                   8  /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTL_MUTE_WIDTH                   1  /* LINEOUTL_MUTE */
+#define WM8903_LINEOUTVU                        0x0080  /* LINEOUTVU */
+#define WM8903_LINEOUTVU_MASK                   0x0080  /* LINEOUTVU */
+#define WM8903_LINEOUTVU_SHIFT                       7  /* LINEOUTVU */
+#define WM8903_LINEOUTVU_WIDTH                       1  /* LINEOUTVU */
+#define WM8903_LINEOUTLZC                       0x0040  /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_MASK                  0x0040  /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_SHIFT                      6  /* LINEOUTLZC */
+#define WM8903_LINEOUTLZC_WIDTH                      1  /* LINEOUTLZC */
+#define WM8903_LINEOUTL_VOL_MASK                0x003F  /* LINEOUTL_VOL - [5:0] */
+#define WM8903_LINEOUTL_VOL_SHIFT                    0  /* LINEOUTL_VOL - [5:0] */
+#define WM8903_LINEOUTL_VOL_WIDTH                    6  /* LINEOUTL_VOL - [5:0] */
+
+/*
+ * R60 (0x3C) - Analogue OUT2 Right
+ */
+#define WM8903_LINEOUTR_MUTE                    0x0100  /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_MASK               0x0100  /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_SHIFT                   8  /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTR_MUTE_WIDTH                   1  /* LINEOUTR_MUTE */
+#define WM8903_LINEOUTVU                        0x0080  /* LINEOUTVU */
+#define WM8903_LINEOUTVU_MASK                   0x0080  /* LINEOUTVU */
+#define WM8903_LINEOUTVU_SHIFT                       7  /* LINEOUTVU */
+#define WM8903_LINEOUTVU_WIDTH                       1  /* LINEOUTVU */
+#define WM8903_LINEOUTRZC                       0x0040  /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_MASK                  0x0040  /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_SHIFT                      6  /* LINEOUTRZC */
+#define WM8903_LINEOUTRZC_WIDTH                      1  /* LINEOUTRZC */
+#define WM8903_LINEOUTR_VOL_MASK                0x003F  /* LINEOUTR_VOL - [5:0] */
+#define WM8903_LINEOUTR_VOL_SHIFT                    0  /* LINEOUTR_VOL - [5:0] */
+#define WM8903_LINEOUTR_VOL_WIDTH                    6  /* LINEOUTR_VOL - [5:0] */
+
+/*
+ * R62 (0x3E) - Analogue OUT3 Left
+ */
+#define WM8903_SPKL_MUTE                        0x0100  /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_MASK                   0x0100  /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_SHIFT                       8  /* SPKL_MUTE */
+#define WM8903_SPKL_MUTE_WIDTH                       1  /* SPKL_MUTE */
+#define WM8903_SPKVU                            0x0080  /* SPKVU */
+#define WM8903_SPKVU_MASK                       0x0080  /* SPKVU */
+#define WM8903_SPKVU_SHIFT                           7  /* SPKVU */
+#define WM8903_SPKVU_WIDTH                           1  /* SPKVU */
+#define WM8903_SPKLZC                           0x0040  /* SPKLZC */
+#define WM8903_SPKLZC_MASK                      0x0040  /* SPKLZC */
+#define WM8903_SPKLZC_SHIFT                          6  /* SPKLZC */
+#define WM8903_SPKLZC_WIDTH                          1  /* SPKLZC */
+#define WM8903_SPKL_VOL_MASK                    0x003F  /* SPKL_VOL - [5:0] */
+#define WM8903_SPKL_VOL_SHIFT                        0  /* SPKL_VOL - [5:0] */
+#define WM8903_SPKL_VOL_WIDTH                        6  /* SPKL_VOL - [5:0] */
+
+/*
+ * R63 (0x3F) - Analogue OUT3 Right
+ */
+#define WM8903_SPKR_MUTE                        0x0100  /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_MASK                   0x0100  /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_SHIFT                       8  /* SPKR_MUTE */
+#define WM8903_SPKR_MUTE_WIDTH                       1  /* SPKR_MUTE */
+#define WM8903_SPKVU                            0x0080  /* SPKVU */
+#define WM8903_SPKVU_MASK                       0x0080  /* SPKVU */
+#define WM8903_SPKVU_SHIFT                           7  /* SPKVU */
+#define WM8903_SPKVU_WIDTH                           1  /* SPKVU */
+#define WM8903_SPKRZC                           0x0040  /* SPKRZC */
+#define WM8903_SPKRZC_MASK                      0x0040  /* SPKRZC */
+#define WM8903_SPKRZC_SHIFT                          6  /* SPKRZC */
+#define WM8903_SPKRZC_WIDTH                          1  /* SPKRZC */
+#define WM8903_SPKR_VOL_MASK                    0x003F  /* SPKR_VOL - [5:0] */
+#define WM8903_SPKR_VOL_SHIFT                        0  /* SPKR_VOL - [5:0] */
+#define WM8903_SPKR_VOL_WIDTH                        6  /* SPKR_VOL - [5:0] */
+
+/*
+ * R65 (0x41) - Analogue SPK Output Control 0
+ */
+#define WM8903_SPK_DISCHARGE                    0x0002  /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_MASK               0x0002  /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_SHIFT                   1  /* SPK_DISCHARGE */
+#define WM8903_SPK_DISCHARGE_WIDTH                   1  /* SPK_DISCHARGE */
+#define WM8903_VROI                             0x0001  /* VROI */
+#define WM8903_VROI_MASK                        0x0001  /* VROI */
+#define WM8903_VROI_SHIFT                            0  /* VROI */
+#define WM8903_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R67 (0x43) - DC Servo 0
+ */
+#define WM8903_DCS_MASTER_ENA                   0x0010  /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_MASK              0x0010  /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_SHIFT                  4  /* DCS_MASTER_ENA */
+#define WM8903_DCS_MASTER_ENA_WIDTH                  1  /* DCS_MASTER_ENA */
+#define WM8903_DCS_ENA_MASK                     0x000F  /* DCS_ENA - [3:0] */
+#define WM8903_DCS_ENA_SHIFT                         0  /* DCS_ENA - [3:0] */
+#define WM8903_DCS_ENA_WIDTH                         4  /* DCS_ENA - [3:0] */
+
+/*
+ * R69 (0x45) - DC Servo 2
+ */
+#define WM8903_DCS_MODE_MASK                    0x0003  /* DCS_MODE - [1:0] */
+#define WM8903_DCS_MODE_SHIFT                        0  /* DCS_MODE - [1:0] */
+#define WM8903_DCS_MODE_WIDTH                        2  /* DCS_MODE - [1:0] */
+
+/*
+ * R90 (0x5A) - Analogue HP 0
+ */
+#define WM8903_HPL_RMV_SHORT                    0x0080  /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_MASK               0x0080  /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_SHIFT                   7  /* HPL_RMV_SHORT */
+#define WM8903_HPL_RMV_SHORT_WIDTH                   1  /* HPL_RMV_SHORT */
+#define WM8903_HPL_ENA_OUTP                     0x0040  /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_MASK                0x0040  /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_SHIFT                    6  /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_OUTP_WIDTH                    1  /* HPL_ENA_OUTP */
+#define WM8903_HPL_ENA_DLY                      0x0020  /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_MASK                 0x0020  /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_SHIFT                     5  /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA_DLY_WIDTH                     1  /* HPL_ENA_DLY */
+#define WM8903_HPL_ENA                          0x0010  /* HPL_ENA */
+#define WM8903_HPL_ENA_MASK                     0x0010  /* HPL_ENA */
+#define WM8903_HPL_ENA_SHIFT                         4  /* HPL_ENA */
+#define WM8903_HPL_ENA_WIDTH                         1  /* HPL_ENA */
+#define WM8903_HPR_RMV_SHORT                    0x0008  /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_MASK               0x0008  /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_SHIFT                   3  /* HPR_RMV_SHORT */
+#define WM8903_HPR_RMV_SHORT_WIDTH                   1  /* HPR_RMV_SHORT */
+#define WM8903_HPR_ENA_OUTP                     0x0004  /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_MASK                0x0004  /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_SHIFT                    2  /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_OUTP_WIDTH                    1  /* HPR_ENA_OUTP */
+#define WM8903_HPR_ENA_DLY                      0x0002  /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_MASK                 0x0002  /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_SHIFT                     1  /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA_DLY_WIDTH                     1  /* HPR_ENA_DLY */
+#define WM8903_HPR_ENA                          0x0001  /* HPR_ENA */
+#define WM8903_HPR_ENA_MASK                     0x0001  /* HPR_ENA */
+#define WM8903_HPR_ENA_SHIFT                         0  /* HPR_ENA */
+#define WM8903_HPR_ENA_WIDTH                         1  /* HPR_ENA */
+
+/*
+ * R94 (0x5E) - Analogue Lineout 0
+ */
+#define WM8903_LINEOUTL_RMV_SHORT               0x0080  /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_MASK          0x0080  /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_SHIFT              7  /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_RMV_SHORT_WIDTH              1  /* LINEOUTL_RMV_SHORT */
+#define WM8903_LINEOUTL_ENA_OUTP                0x0040  /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_MASK           0x0040  /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_SHIFT               6  /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_OUTP_WIDTH               1  /* LINEOUTL_ENA_OUTP */
+#define WM8903_LINEOUTL_ENA_DLY                 0x0020  /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_MASK            0x0020  /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_SHIFT                5  /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA_DLY_WIDTH                1  /* LINEOUTL_ENA_DLY */
+#define WM8903_LINEOUTL_ENA                     0x0010  /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_MASK                0x0010  /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_SHIFT                    4  /* LINEOUTL_ENA */
+#define WM8903_LINEOUTL_ENA_WIDTH                    1  /* LINEOUTL_ENA */
+#define WM8903_LINEOUTR_RMV_SHORT               0x0008  /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_MASK          0x0008  /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_SHIFT              3  /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_RMV_SHORT_WIDTH              1  /* LINEOUTR_RMV_SHORT */
+#define WM8903_LINEOUTR_ENA_OUTP                0x0004  /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_MASK           0x0004  /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_SHIFT               2  /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_OUTP_WIDTH               1  /* LINEOUTR_ENA_OUTP */
+#define WM8903_LINEOUTR_ENA_DLY                 0x0002  /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_MASK            0x0002  /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_SHIFT                1  /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA_DLY_WIDTH                1  /* LINEOUTR_ENA_DLY */
+#define WM8903_LINEOUTR_ENA                     0x0001  /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_MASK                0x0001  /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_SHIFT                    0  /* LINEOUTR_ENA */
+#define WM8903_LINEOUTR_ENA_WIDTH                    1  /* LINEOUTR_ENA */
+
+/*
+ * R98 (0x62) - Charge Pump 0
+ */
+#define WM8903_CP_ENA                           0x0001  /* CP_ENA */
+#define WM8903_CP_ENA_MASK                      0x0001  /* CP_ENA */
+#define WM8903_CP_ENA_SHIFT                          0  /* CP_ENA */
+#define WM8903_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R104 (0x68) - Class W 0
+ */
+#define WM8903_CP_DYN_FREQ                      0x0002  /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_MASK                 0x0002  /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_SHIFT                     1  /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_FREQ_WIDTH                     1  /* CP_DYN_FREQ */
+#define WM8903_CP_DYN_V                         0x0001  /* CP_DYN_V */
+#define WM8903_CP_DYN_V_MASK                    0x0001  /* CP_DYN_V */
+#define WM8903_CP_DYN_V_SHIFT                        0  /* CP_DYN_V */
+#define WM8903_CP_DYN_V_WIDTH                        1  /* CP_DYN_V */
+
+/*
+ * R108 (0x6C) - Write Sequencer 0
+ */
+#define WM8903_WSEQ_ENA                         0x0100  /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_MASK                    0x0100  /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_SHIFT                        8  /* WSEQ_ENA */
+#define WM8903_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8903_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8903_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8903_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R109 (0x6D) - Write Sequencer 1
+ */
+#define WM8903_WSEQ_DATA_WIDTH_MASK             0x7000  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_WIDTH_SHIFT                12  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8903_WSEQ_DATA_START_MASK             0x0F00  /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_DATA_START_SHIFT                 8  /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [11:8] */
+#define WM8903_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8903_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8903_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R110 (0x6E) - Write Sequencer 2
+ */
+#define WM8903_WSEQ_EOS                         0x4000  /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_MASK                    0x4000  /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_SHIFT                       14  /* WSEQ_EOS */
+#define WM8903_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8903_WSEQ_DELAY_MASK                  0x0F00  /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DELAY_SHIFT                      8  /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [11:8] */
+#define WM8903_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8903_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8903_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R111 (0x6F) - Write Sequencer 3
+ */
+#define WM8903_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM8903_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8903_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM8903_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM8903_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM8903_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8903_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8903_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8903_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R112 (0x70) - Write Sequencer 4
+ */
+#define WM8903_WSEQ_CURRENT_INDEX_MASK          0x03F0  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_CURRENT_INDEX_SHIFT              4  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_CURRENT_INDEX_WIDTH              6  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8903_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8903_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R114 (0x72) - Control Interface
+ */
+#define WM8903_MASK_WRITE_ENA                   0x0001  /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_MASK              0x0001  /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_SHIFT                  0  /* MASK_WRITE_ENA */
+#define WM8903_MASK_WRITE_ENA_WIDTH                  1  /* MASK_WRITE_ENA */
+
+/*
+ * R116 (0x74) - GPIO Control 1
+ */
+#define WM8903_GP1_FN_MASK                      0x1F00  /* GP1_FN - [12:8] */
+#define WM8903_GP1_FN_SHIFT                          8  /* GP1_FN - [12:8] */
+#define WM8903_GP1_FN_WIDTH                          5  /* GP1_FN - [12:8] */
+#define WM8903_GP1_DIR                          0x0080  /* GP1_DIR */
+#define WM8903_GP1_DIR_MASK                     0x0080  /* GP1_DIR */
+#define WM8903_GP1_DIR_SHIFT                         7  /* GP1_DIR */
+#define WM8903_GP1_DIR_WIDTH                         1  /* GP1_DIR */
+#define WM8903_GP1_OP_CFG                       0x0040  /* GP1_OP_CFG */
+#define WM8903_GP1_OP_CFG_MASK                  0x0040  /* GP1_OP_CFG */
+#define WM8903_GP1_OP_CFG_SHIFT                      6  /* GP1_OP_CFG */
+#define WM8903_GP1_OP_CFG_WIDTH                      1  /* GP1_OP_CFG */
+#define WM8903_GP1_IP_CFG                       0x0020  /* GP1_IP_CFG */
+#define WM8903_GP1_IP_CFG_MASK                  0x0020  /* GP1_IP_CFG */
+#define WM8903_GP1_IP_CFG_SHIFT                      5  /* GP1_IP_CFG */
+#define WM8903_GP1_IP_CFG_WIDTH                      1  /* GP1_IP_CFG */
+#define WM8903_GP1_LVL                          0x0010  /* GP1_LVL */
+#define WM8903_GP1_LVL_MASK                     0x0010  /* GP1_LVL */
+#define WM8903_GP1_LVL_SHIFT                         4  /* GP1_LVL */
+#define WM8903_GP1_LVL_WIDTH                         1  /* GP1_LVL */
+#define WM8903_GP1_PD                           0x0008  /* GP1_PD */
+#define WM8903_GP1_PD_MASK                      0x0008  /* GP1_PD */
+#define WM8903_GP1_PD_SHIFT                          3  /* GP1_PD */
+#define WM8903_GP1_PD_WIDTH                          1  /* GP1_PD */
+#define WM8903_GP1_PU                           0x0004  /* GP1_PU */
+#define WM8903_GP1_PU_MASK                      0x0004  /* GP1_PU */
+#define WM8903_GP1_PU_SHIFT                          2  /* GP1_PU */
+#define WM8903_GP1_PU_WIDTH                          1  /* GP1_PU */
+#define WM8903_GP1_INTMODE                      0x0002  /* GP1_INTMODE */
+#define WM8903_GP1_INTMODE_MASK                 0x0002  /* GP1_INTMODE */
+#define WM8903_GP1_INTMODE_SHIFT                     1  /* GP1_INTMODE */
+#define WM8903_GP1_INTMODE_WIDTH                     1  /* GP1_INTMODE */
+#define WM8903_GP1_DB                           0x0001  /* GP1_DB */
+#define WM8903_GP1_DB_MASK                      0x0001  /* GP1_DB */
+#define WM8903_GP1_DB_SHIFT                          0  /* GP1_DB */
+#define WM8903_GP1_DB_WIDTH                          1  /* GP1_DB */
+
+/*
+ * R117 (0x75) - GPIO Control 2
+ */
+#define WM8903_GP2_FN_MASK                      0x1F00  /* GP2_FN - [12:8] */
+#define WM8903_GP2_FN_SHIFT                          8  /* GP2_FN - [12:8] */
+#define WM8903_GP2_FN_WIDTH                          5  /* GP2_FN - [12:8] */
+#define WM8903_GP2_DIR                          0x0080  /* GP2_DIR */
+#define WM8903_GP2_DIR_MASK                     0x0080  /* GP2_DIR */
+#define WM8903_GP2_DIR_SHIFT                         7  /* GP2_DIR */
+#define WM8903_GP2_DIR_WIDTH                         1  /* GP2_DIR */
+#define WM8903_GP2_OP_CFG                       0x0040  /* GP2_OP_CFG */
+#define WM8903_GP2_OP_CFG_MASK                  0x0040  /* GP2_OP_CFG */
+#define WM8903_GP2_OP_CFG_SHIFT                      6  /* GP2_OP_CFG */
+#define WM8903_GP2_OP_CFG_WIDTH                      1  /* GP2_OP_CFG */
+#define WM8903_GP2_IP_CFG                       0x0020  /* GP2_IP_CFG */
+#define WM8903_GP2_IP_CFG_MASK                  0x0020  /* GP2_IP_CFG */
+#define WM8903_GP2_IP_CFG_SHIFT                      5  /* GP2_IP_CFG */
+#define WM8903_GP2_IP_CFG_WIDTH                      1  /* GP2_IP_CFG */
+#define WM8903_GP2_LVL                          0x0010  /* GP2_LVL */
+#define WM8903_GP2_LVL_MASK                     0x0010  /* GP2_LVL */
+#define WM8903_GP2_LVL_SHIFT                         4  /* GP2_LVL */
+#define WM8903_GP2_LVL_WIDTH                         1  /* GP2_LVL */
+#define WM8903_GP2_PD                           0x0008  /* GP2_PD */
+#define WM8903_GP2_PD_MASK                      0x0008  /* GP2_PD */
+#define WM8903_GP2_PD_SHIFT                          3  /* GP2_PD */
+#define WM8903_GP2_PD_WIDTH                          1  /* GP2_PD */
+#define WM8903_GP2_PU                           0x0004  /* GP2_PU */
+#define WM8903_GP2_PU_MASK                      0x0004  /* GP2_PU */
+#define WM8903_GP2_PU_SHIFT                          2  /* GP2_PU */
+#define WM8903_GP2_PU_WIDTH                          1  /* GP2_PU */
+#define WM8903_GP2_INTMODE                      0x0002  /* GP2_INTMODE */
+#define WM8903_GP2_INTMODE_MASK                 0x0002  /* GP2_INTMODE */
+#define WM8903_GP2_INTMODE_SHIFT                     1  /* GP2_INTMODE */
+#define WM8903_GP2_INTMODE_WIDTH                     1  /* GP2_INTMODE */
+#define WM8903_GP2_DB                           0x0001  /* GP2_DB */
+#define WM8903_GP2_DB_MASK                      0x0001  /* GP2_DB */
+#define WM8903_GP2_DB_SHIFT                          0  /* GP2_DB */
+#define WM8903_GP2_DB_WIDTH                          1  /* GP2_DB */
+
+/*
+ * R118 (0x76) - GPIO Control 3
+ */
+#define WM8903_GP3_FN_MASK                      0x1F00  /* GP3_FN - [12:8] */
+#define WM8903_GP3_FN_SHIFT                          8  /* GP3_FN - [12:8] */
+#define WM8903_GP3_FN_WIDTH                          5  /* GP3_FN - [12:8] */
+#define WM8903_GP3_DIR                          0x0080  /* GP3_DIR */
+#define WM8903_GP3_DIR_MASK                     0x0080  /* GP3_DIR */
+#define WM8903_GP3_DIR_SHIFT                         7  /* GP3_DIR */
+#define WM8903_GP3_DIR_WIDTH                         1  /* GP3_DIR */
+#define WM8903_GP3_OP_CFG                       0x0040  /* GP3_OP_CFG */
+#define WM8903_GP3_OP_CFG_MASK                  0x0040  /* GP3_OP_CFG */
+#define WM8903_GP3_OP_CFG_SHIFT                      6  /* GP3_OP_CFG */
+#define WM8903_GP3_OP_CFG_WIDTH                      1  /* GP3_OP_CFG */
+#define WM8903_GP3_IP_CFG                       0x0020  /* GP3_IP_CFG */
+#define WM8903_GP3_IP_CFG_MASK                  0x0020  /* GP3_IP_CFG */
+#define WM8903_GP3_IP_CFG_SHIFT                      5  /* GP3_IP_CFG */
+#define WM8903_GP3_IP_CFG_WIDTH                      1  /* GP3_IP_CFG */
+#define WM8903_GP3_LVL                          0x0010  /* GP3_LVL */
+#define WM8903_GP3_LVL_MASK                     0x0010  /* GP3_LVL */
+#define WM8903_GP3_LVL_SHIFT                         4  /* GP3_LVL */
+#define WM8903_GP3_LVL_WIDTH                         1  /* GP3_LVL */
+#define WM8903_GP3_PD                           0x0008  /* GP3_PD */
+#define WM8903_GP3_PD_MASK                      0x0008  /* GP3_PD */
+#define WM8903_GP3_PD_SHIFT                          3  /* GP3_PD */
+#define WM8903_GP3_PD_WIDTH                          1  /* GP3_PD */
+#define WM8903_GP3_PU                           0x0004  /* GP3_PU */
+#define WM8903_GP3_PU_MASK                      0x0004  /* GP3_PU */
+#define WM8903_GP3_PU_SHIFT                          2  /* GP3_PU */
+#define WM8903_GP3_PU_WIDTH                          1  /* GP3_PU */
+#define WM8903_GP3_INTMODE                      0x0002  /* GP3_INTMODE */
+#define WM8903_GP3_INTMODE_MASK                 0x0002  /* GP3_INTMODE */
+#define WM8903_GP3_INTMODE_SHIFT                     1  /* GP3_INTMODE */
+#define WM8903_GP3_INTMODE_WIDTH                     1  /* GP3_INTMODE */
+#define WM8903_GP3_DB                           0x0001  /* GP3_DB */
+#define WM8903_GP3_DB_MASK                      0x0001  /* GP3_DB */
+#define WM8903_GP3_DB_SHIFT                          0  /* GP3_DB */
+#define WM8903_GP3_DB_WIDTH                          1  /* GP3_DB */
+
+/*
+ * R119 (0x77) - GPIO Control 4
+ */
+#define WM8903_GP4_FN_MASK                      0x1F00  /* GP4_FN - [12:8] */
+#define WM8903_GP4_FN_SHIFT                          8  /* GP4_FN - [12:8] */
+#define WM8903_GP4_FN_WIDTH                          5  /* GP4_FN - [12:8] */
+#define WM8903_GP4_DIR                          0x0080  /* GP4_DIR */
+#define WM8903_GP4_DIR_MASK                     0x0080  /* GP4_DIR */
+#define WM8903_GP4_DIR_SHIFT                         7  /* GP4_DIR */
+#define WM8903_GP4_DIR_WIDTH                         1  /* GP4_DIR */
+#define WM8903_GP4_OP_CFG                       0x0040  /* GP4_OP_CFG */
+#define WM8903_GP4_OP_CFG_MASK                  0x0040  /* GP4_OP_CFG */
+#define WM8903_GP4_OP_CFG_SHIFT                      6  /* GP4_OP_CFG */
+#define WM8903_GP4_OP_CFG_WIDTH                      1  /* GP4_OP_CFG */
+#define WM8903_GP4_IP_CFG                       0x0020  /* GP4_IP_CFG */
+#define WM8903_GP4_IP_CFG_MASK                  0x0020  /* GP4_IP_CFG */
+#define WM8903_GP4_IP_CFG_SHIFT                      5  /* GP4_IP_CFG */
+#define WM8903_GP4_IP_CFG_WIDTH                      1  /* GP4_IP_CFG */
+#define WM8903_GP4_LVL                          0x0010  /* GP4_LVL */
+#define WM8903_GP4_LVL_MASK                     0x0010  /* GP4_LVL */
+#define WM8903_GP4_LVL_SHIFT                         4  /* GP4_LVL */
+#define WM8903_GP4_LVL_WIDTH                         1  /* GP4_LVL */
+#define WM8903_GP4_PD                           0x0008  /* GP4_PD */
+#define WM8903_GP4_PD_MASK                      0x0008  /* GP4_PD */
+#define WM8903_GP4_PD_SHIFT                          3  /* GP4_PD */
+#define WM8903_GP4_PD_WIDTH                          1  /* GP4_PD */
+#define WM8903_GP4_PU                           0x0004  /* GP4_PU */
+#define WM8903_GP4_PU_MASK                      0x0004  /* GP4_PU */
+#define WM8903_GP4_PU_SHIFT                          2  /* GP4_PU */
+#define WM8903_GP4_PU_WIDTH                          1  /* GP4_PU */
+#define WM8903_GP4_INTMODE                      0x0002  /* GP4_INTMODE */
+#define WM8903_GP4_INTMODE_MASK                 0x0002  /* GP4_INTMODE */
+#define WM8903_GP4_INTMODE_SHIFT                     1  /* GP4_INTMODE */
+#define WM8903_GP4_INTMODE_WIDTH                     1  /* GP4_INTMODE */
+#define WM8903_GP4_DB                           0x0001  /* GP4_DB */
+#define WM8903_GP4_DB_MASK                      0x0001  /* GP4_DB */
+#define WM8903_GP4_DB_SHIFT                          0  /* GP4_DB */
+#define WM8903_GP4_DB_WIDTH                          1  /* GP4_DB */
+
+/*
+ * R120 (0x78) - GPIO Control 5
+ */
+#define WM8903_GP5_FN_MASK                      0x1F00  /* GP5_FN - [12:8] */
+#define WM8903_GP5_FN_SHIFT                          8  /* GP5_FN - [12:8] */
+#define WM8903_GP5_FN_WIDTH                          5  /* GP5_FN - [12:8] */
+#define WM8903_GP5_DIR                          0x0080  /* GP5_DIR */
+#define WM8903_GP5_DIR_MASK                     0x0080  /* GP5_DIR */
+#define WM8903_GP5_DIR_SHIFT                         7  /* GP5_DIR */
+#define WM8903_GP5_DIR_WIDTH                         1  /* GP5_DIR */
+#define WM8903_GP5_OP_CFG                       0x0040  /* GP5_OP_CFG */
+#define WM8903_GP5_OP_CFG_MASK                  0x0040  /* GP5_OP_CFG */
+#define WM8903_GP5_OP_CFG_SHIFT                      6  /* GP5_OP_CFG */
+#define WM8903_GP5_OP_CFG_WIDTH                      1  /* GP5_OP_CFG */
+#define WM8903_GP5_IP_CFG                       0x0020  /* GP5_IP_CFG */
+#define WM8903_GP5_IP_CFG_MASK                  0x0020  /* GP5_IP_CFG */
+#define WM8903_GP5_IP_CFG_SHIFT                      5  /* GP5_IP_CFG */
+#define WM8903_GP5_IP_CFG_WIDTH                      1  /* GP5_IP_CFG */
+#define WM8903_GP5_LVL                          0x0010  /* GP5_LVL */
+#define WM8903_GP5_LVL_MASK                     0x0010  /* GP5_LVL */
+#define WM8903_GP5_LVL_SHIFT                         4  /* GP5_LVL */
+#define WM8903_GP5_LVL_WIDTH                         1  /* GP5_LVL */
+#define WM8903_GP5_PD                           0x0008  /* GP5_PD */
+#define WM8903_GP5_PD_MASK                      0x0008  /* GP5_PD */
+#define WM8903_GP5_PD_SHIFT                          3  /* GP5_PD */
+#define WM8903_GP5_PD_WIDTH                          1  /* GP5_PD */
+#define WM8903_GP5_PU                           0x0004  /* GP5_PU */
+#define WM8903_GP5_PU_MASK                      0x0004  /* GP5_PU */
+#define WM8903_GP5_PU_SHIFT                          2  /* GP5_PU */
+#define WM8903_GP5_PU_WIDTH                          1  /* GP5_PU */
+#define WM8903_GP5_INTMODE                      0x0002  /* GP5_INTMODE */
+#define WM8903_GP5_INTMODE_MASK                 0x0002  /* GP5_INTMODE */
+#define WM8903_GP5_INTMODE_SHIFT                     1  /* GP5_INTMODE */
+#define WM8903_GP5_INTMODE_WIDTH                     1  /* GP5_INTMODE */
+#define WM8903_GP5_DB                           0x0001  /* GP5_DB */
+#define WM8903_GP5_DB_MASK                      0x0001  /* GP5_DB */
+#define WM8903_GP5_DB_SHIFT                          0  /* GP5_DB */
+#define WM8903_GP5_DB_WIDTH                          1  /* GP5_DB */
+
+/*
+ * R121 (0x79) - Interrupt Status 1
+ */
+#define WM8903_MICSHRT_EINT                     0x8000  /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_MASK                0x8000  /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_SHIFT                   15  /* MICSHRT_EINT */
+#define WM8903_MICSHRT_EINT_WIDTH                    1  /* MICSHRT_EINT */
+#define WM8903_MICDET_EINT                      0x4000  /* MICDET_EINT */
+#define WM8903_MICDET_EINT_MASK                 0x4000  /* MICDET_EINT */
+#define WM8903_MICDET_EINT_SHIFT                    14  /* MICDET_EINT */
+#define WM8903_MICDET_EINT_WIDTH                     1  /* MICDET_EINT */
+#define WM8903_WSEQ_BUSY_EINT                   0x2000  /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_MASK              0x2000  /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_SHIFT                 13  /* WSEQ_BUSY_EINT */
+#define WM8903_WSEQ_BUSY_EINT_WIDTH                  1  /* WSEQ_BUSY_EINT */
+#define WM8903_GP5_EINT                         0x0010  /* GP5_EINT */
+#define WM8903_GP5_EINT_MASK                    0x0010  /* GP5_EINT */
+#define WM8903_GP5_EINT_SHIFT                        4  /* GP5_EINT */
+#define WM8903_GP5_EINT_WIDTH                        1  /* GP5_EINT */
+#define WM8903_GP4_EINT                         0x0008  /* GP4_EINT */
+#define WM8903_GP4_EINT_MASK                    0x0008  /* GP4_EINT */
+#define WM8903_GP4_EINT_SHIFT                        3  /* GP4_EINT */
+#define WM8903_GP4_EINT_WIDTH                        1  /* GP4_EINT */
+#define WM8903_GP3_EINT                         0x0004  /* GP3_EINT */
+#define WM8903_GP3_EINT_MASK                    0x0004  /* GP3_EINT */
+#define WM8903_GP3_EINT_SHIFT                        2  /* GP3_EINT */
+#define WM8903_GP3_EINT_WIDTH                        1  /* GP3_EINT */
+#define WM8903_GP2_EINT                         0x0002  /* GP2_EINT */
+#define WM8903_GP2_EINT_MASK                    0x0002  /* GP2_EINT */
+#define WM8903_GP2_EINT_SHIFT                        1  /* GP2_EINT */
+#define WM8903_GP2_EINT_WIDTH                        1  /* GP2_EINT */
+#define WM8903_GP1_EINT                         0x0001  /* GP1_EINT */
+#define WM8903_GP1_EINT_MASK                    0x0001  /* GP1_EINT */
+#define WM8903_GP1_EINT_SHIFT                        0  /* GP1_EINT */
+#define WM8903_GP1_EINT_WIDTH                        1  /* GP1_EINT */
+
+/*
+ * R122 (0x7A) - Interrupt Status 1 Mask
+ */
+#define WM8903_IM_MICSHRT_EINT                  0x8000  /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_MASK             0x8000  /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_SHIFT                15  /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICSHRT_EINT_WIDTH                 1  /* IM_MICSHRT_EINT */
+#define WM8903_IM_MICDET_EINT                   0x4000  /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_MASK              0x4000  /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_SHIFT                 14  /* IM_MICDET_EINT */
+#define WM8903_IM_MICDET_EINT_WIDTH                  1  /* IM_MICDET_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT                0x2000  /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_MASK           0x2000  /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_SHIFT              13  /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_WSEQ_BUSY_EINT_WIDTH               1  /* IM_WSEQ_BUSY_EINT */
+#define WM8903_IM_GP5_EINT                      0x0010  /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_MASK                 0x0010  /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_SHIFT                     4  /* IM_GP5_EINT */
+#define WM8903_IM_GP5_EINT_WIDTH                     1  /* IM_GP5_EINT */
+#define WM8903_IM_GP4_EINT                      0x0008  /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_MASK                 0x0008  /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_SHIFT                     3  /* IM_GP4_EINT */
+#define WM8903_IM_GP4_EINT_WIDTH                     1  /* IM_GP4_EINT */
+#define WM8903_IM_GP3_EINT                      0x0004  /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_MASK                 0x0004  /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_SHIFT                     2  /* IM_GP3_EINT */
+#define WM8903_IM_GP3_EINT_WIDTH                     1  /* IM_GP3_EINT */
+#define WM8903_IM_GP2_EINT                      0x0002  /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_MASK                 0x0002  /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_SHIFT                     1  /* IM_GP2_EINT */
+#define WM8903_IM_GP2_EINT_WIDTH                     1  /* IM_GP2_EINT */
+#define WM8903_IM_GP1_EINT                      0x0001  /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_MASK                 0x0001  /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_SHIFT                     0  /* IM_GP1_EINT */
+#define WM8903_IM_GP1_EINT_WIDTH                     1  /* IM_GP1_EINT */
+
+/*
+ * R123 (0x7B) - Interrupt Polarity 1
+ */
+#define WM8903_MICSHRT_INV                      0x8000  /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_MASK                 0x8000  /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_SHIFT                    15  /* MICSHRT_INV */
+#define WM8903_MICSHRT_INV_WIDTH                     1  /* MICSHRT_INV */
+#define WM8903_MICDET_INV                       0x4000  /* MICDET_INV */
+#define WM8903_MICDET_INV_MASK                  0x4000  /* MICDET_INV */
+#define WM8903_MICDET_INV_SHIFT                     14  /* MICDET_INV */
+#define WM8903_MICDET_INV_WIDTH                      1  /* MICDET_INV */
+
+/*
+ * R126 (0x7E) - Interrupt Control
+ */
+#define WM8903_IRQ_POL                          0x0001  /* IRQ_POL */
+#define WM8903_IRQ_POL_MASK                     0x0001  /* IRQ_POL */
+#define WM8903_IRQ_POL_SHIFT                         0  /* IRQ_POL */
+#define WM8903_IRQ_POL_WIDTH                         1  /* IRQ_POL */
+
+/*
+ * R129 (0x81) - Control Interface Test 1
+ */
+#define WM8903_USER_KEY                         0x0002  /* USER_KEY */
+#define WM8903_USER_KEY_MASK                    0x0002  /* USER_KEY */
+#define WM8903_USER_KEY_SHIFT                        1  /* USER_KEY */
+#define WM8903_USER_KEY_WIDTH                        1  /* USER_KEY */
+#define WM8903_TEST_KEY                         0x0001  /* TEST_KEY */
+#define WM8903_TEST_KEY_MASK                    0x0001  /* TEST_KEY */
+#define WM8903_TEST_KEY_SHIFT                        0  /* TEST_KEY */
+#define WM8903_TEST_KEY_WIDTH                        1  /* TEST_KEY */
+
+/*
+ * R149 (0x95) - Charge Pump Test 1
+ */
+#define WM8903_CP_SW_KELVIN_MODE_MASK           0x0006  /* CP_SW_KELVIN_MODE - [2:1] */
+#define WM8903_CP_SW_KELVIN_MODE_SHIFT               1  /* CP_SW_KELVIN_MODE - [2:1] */
+#define WM8903_CP_SW_KELVIN_MODE_WIDTH               2  /* CP_SW_KELVIN_MODE - [2:1] */
+
+/*
+ * R164 (0xA4) - Clock Rate Test 4
+ */
+#define WM8903_ADC_DIG_MIC                      0x0200  /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_MASK                 0x0200  /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_SHIFT                     9  /* ADC_DIG_MIC */
+#define WM8903_ADC_DIG_MIC_WIDTH                     1  /* ADC_DIG_MIC */
+
+/*
+ * R172 (0xAC) - Analogue Output Bias 0
+ */
+#define WM8903_PGA_BIAS_MASK                    0x0070  /* PGA_BIAS - [6:4] */
+#define WM8903_PGA_BIAS_SHIFT                        4  /* PGA_BIAS - [6:4] */
+#define WM8903_PGA_BIAS_WIDTH                        3  /* PGA_BIAS - [6:4] */
+
+#endif
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
new file mode 100644
index 0000000..974a4cd
--- /dev/null
+++ b/sound/soc/codecs/wm8971.c
@@ -0,0 +1,942 @@
+/*
+ * wm8971.c  --  WM8971 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8971.h"
+
+#define AUDIO_NAME "wm8971"
+#define WM8971_VERSION "0.9"
+
+#define	WM8971_REG_COUNT		43
+
+static struct workqueue_struct *wm8971_workq = NULL;
+
+/* codec private data */
+struct wm8971_priv {
+	unsigned int sysclk;
+};
+
+/*
+ * wm8971 register cache
+ * We can't read the WM8971 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8971_reg[] = {
+	0x0097, 0x0097, 0x0079, 0x0079,  /*  0 */
+	0x0000, 0x0008, 0x0000, 0x000a,  /*  4 */
+	0x0000, 0x0000, 0x00ff, 0x00ff,  /*  8 */
+	0x000f, 0x000f, 0x0000, 0x0000,  /* 12 */
+	0x0000, 0x007b, 0x0000, 0x0032,  /* 16 */
+	0x0000, 0x00c3, 0x00c3, 0x00c0,  /* 20 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 24 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 28 */
+	0x0000, 0x0000, 0x0050, 0x0050,  /* 32 */
+	0x0050, 0x0050, 0x0050, 0x0050,  /* 36 */
+	0x0079, 0x0079, 0x0079,          /* 40 */
+};
+
+static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg < WM8971_REG_COUNT)
+		return cache[reg];
+
+	return -1;
+}
+
+static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg < WM8971_REG_COUNT)
+		cache[reg] = value;
+}
+
+static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D9 WM8753 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8971_write_reg_cache (codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define wm8971_reset(c)	wm8971_write(c, WM8971_RESET, 0)
+
+/* WM8971 Controls */
+static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
+static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz",
+	"200Hz @ 48kHz" };
+static const char *wm8971_treble[] = { "8kHz", "4kHz" };
+static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
+static const char *wm8971_ng_type[] = { "Constant PGA Gain",
+	"Mute ADC Output" };
+static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)",
+	"Mono (Right)", "Digital Mono"};
+static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
+static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA",
+	"Differential"};
+static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA",
+	"Differential"};
+static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
+static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
+static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert",
+	"L + R Invert"};
+
+static const struct soc_enum wm8971_enum[] = {
+	SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass),	/* 0 */
+	SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
+	SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
+	SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
+	SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type),    /* 4 */
+	SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
+	SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
+	SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase),
+	SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */
+	SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
+	SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
+	SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
+	SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol),    /* 12 */
+	SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
+};
+
+static const struct snd_kcontrol_new wm8971_snd_controls[] = {
+	SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0),
+	SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL,
+		     6, 1, 0),
+	SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1),
+
+	SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V,
+		WM8971_ROUT1V, 7, 1, 0),
+	SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V,
+		WM8971_ROUT2V, 7, 1, 0),
+	SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
+
+	SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0),
+
+	SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1,
+		WM8971_LOUTM2, 4, 7, 1),
+	SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1,
+		WM8971_ROUTM2, 4, 7, 1),
+	SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1,
+		WM8971_MOUTM2, 4, 7, 1),
+
+	SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V,
+		WM8971_ROUT1V, 0, 127, 0),
+	SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V,
+		WM8971_ROUT2V, 0, 127, 0),
+
+	SOC_ENUM("Bass Boost", wm8971_enum[0]),
+	SOC_ENUM("Bass Filter", wm8971_enum[1]),
+	SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
+
+	SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
+	SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
+
+	SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
+
+	SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
+	SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
+
+	SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
+	SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
+	SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
+	SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
+	SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
+	SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
+	SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
+	SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
+	SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
+	SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
+
+	SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
+	SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
+
+	SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
+	SOC_ENUM("Playback Function", wm8971_enum[6]),
+	SOC_ENUM("Playback Phase", wm8971_enum[7]),
+
+	SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
+};
+
+/* add non-DAPM controls */
+static int wm8971_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8971_snd_controls[i],
+					     codec, NULL));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const struct snd_kcontrol_new wm8971_left_line_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[8]);
+
+/* Right Line Mux */
+static const struct snd_kcontrol_new wm8971_right_line_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[9]);
+
+/* Left PGA Mux */
+static const struct snd_kcontrol_new wm8971_left_pga_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[10]);
+
+/* Right PGA Mux */
+static const struct snd_kcontrol_new wm8971_right_pga_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[11]);
+
+/* Mono ADC Mux */
+static const struct snd_kcontrol_new wm8971_monomux_controls =
+SOC_DAPM_ENUM("Route", wm8971_enum[13]);
+
+static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = {
+	SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+		&wm8971_left_mixer_controls[0],
+		ARRAY_SIZE(wm8971_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+		&wm8971_right_mixer_controls[0],
+		ARRAY_SIZE(wm8971_right_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0,
+		&wm8971_mono_mixer_controls[0],
+		ARRAY_SIZE(wm8971_mono_mixer_controls)),
+
+	SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0),
+	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0),
+	SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0),
+
+	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0),
+
+	SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0,
+		&wm8971_left_pga_controls),
+	SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0,
+		&wm8971_right_pga_controls),
+	SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+		&wm8971_left_line_controls),
+	SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+		&wm8971_right_line_controls),
+
+	SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+		&wm8971_monomux_controls),
+	SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+		&wm8971_monomux_controls),
+
+	SND_SOC_DAPM_OUTPUT("LOUT1"),
+	SND_SOC_DAPM_OUTPUT("ROUT1"),
+	SND_SOC_DAPM_OUTPUT("LOUT2"),
+	SND_SOC_DAPM_OUTPUT("ROUT2"),
+	SND_SOC_DAPM_OUTPUT("MONO"),
+
+	SND_SOC_DAPM_INPUT("LINPUT1"),
+	SND_SOC_DAPM_INPUT("RINPUT1"),
+	SND_SOC_DAPM_INPUT("MIC"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* left mixer */
+	{"Left Mixer", "Playback Switch", "Left DAC"},
+	{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Left Mixer", "Right Playback Switch", "Right DAC"},
+	{"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* right mixer */
+	{"Right Mixer", "Left Playback Switch", "Left DAC"},
+	{"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Right Mixer", "Playback Switch", "Right DAC"},
+	{"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* left out 1 */
+	{"Left Out 1", NULL, "Left Mixer"},
+	{"LOUT1", NULL, "Left Out 1"},
+
+	/* left out 2 */
+	{"Left Out 2", NULL, "Left Mixer"},
+	{"LOUT2", NULL, "Left Out 2"},
+
+	/* right out 1 */
+	{"Right Out 1", NULL, "Right Mixer"},
+	{"ROUT1", NULL, "Right Out 1"},
+
+	/* right out 2 */
+	{"Right Out 2", NULL, "Right Mixer"},
+	{"ROUT2", NULL, "Right Out 2"},
+
+	/* mono mixer */
+	{"Mono Mixer", "Left Playback Switch", "Left DAC"},
+	{"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Mono Mixer", "Right Playback Switch", "Right DAC"},
+	{"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* mono out */
+	{"Mono Out", NULL, "Mono Mixer"},
+	{"MONO1", NULL, "Mono Out"},
+
+	/* Left Line Mux */
+	{"Left Line Mux", "Line", "LINPUT1"},
+	{"Left Line Mux", "PGA", "Left PGA Mux"},
+	{"Left Line Mux", "Differential", "Differential Mux"},
+
+	/* Right Line Mux */
+	{"Right Line Mux", "Line", "RINPUT1"},
+	{"Right Line Mux", "Mic", "MIC"},
+	{"Right Line Mux", "PGA", "Right PGA Mux"},
+	{"Right Line Mux", "Differential", "Differential Mux"},
+
+	/* Left PGA Mux */
+	{"Left PGA Mux", "Line", "LINPUT1"},
+	{"Left PGA Mux", "Differential", "Differential Mux"},
+
+	/* Right PGA Mux */
+	{"Right PGA Mux", "Line", "RINPUT1"},
+	{"Right PGA Mux", "Differential", "Differential Mux"},
+
+	/* Differential Mux */
+	{"Differential Mux", "Line", "LINPUT1"},
+	{"Differential Mux", "Line", "RINPUT1"},
+
+	/* Left ADC Mux */
+	{"Left ADC Mux", "Stereo", "Left PGA Mux"},
+	{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+	{"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+	/* Right ADC Mux */
+	{"Right ADC Mux", "Stereo", "Right PGA Mux"},
+	{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+	{"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+	/* ADC */
+	{"Left ADC", NULL, "Left ADC Mux"},
+	{"Right ADC", NULL, "Right ADC Mux"},
+};
+
+static int wm8971_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8971_dapm_widgets,
+				  ARRAY_SIZE(wm8971_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+
+	return 0;
+}
+
+struct _coeff_div {
+	u32 mclk;
+	u32 rate;
+	u16 fs;
+	u8 sr:5;
+	u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+	/* 8k */
+	{12288000, 8000, 1536, 0x6, 0x0},
+	{11289600, 8000, 1408, 0x16, 0x0},
+	{18432000, 8000, 2304, 0x7, 0x0},
+	{16934400, 8000, 2112, 0x17, 0x0},
+	{12000000, 8000, 1500, 0x6, 0x1},
+
+	/* 11.025k */
+	{11289600, 11025, 1024, 0x18, 0x0},
+	{16934400, 11025, 1536, 0x19, 0x0},
+	{12000000, 11025, 1088, 0x19, 0x1},
+
+	/* 16k */
+	{12288000, 16000, 768, 0xa, 0x0},
+	{18432000, 16000, 1152, 0xb, 0x0},
+	{12000000, 16000, 750, 0xa, 0x1},
+
+	/* 22.05k */
+	{11289600, 22050, 512, 0x1a, 0x0},
+	{16934400, 22050, 768, 0x1b, 0x0},
+	{12000000, 22050, 544, 0x1b, 0x1},
+
+	/* 32k */
+	{12288000, 32000, 384, 0xc, 0x0},
+	{18432000, 32000, 576, 0xd, 0x0},
+	{12000000, 32000, 375, 0xa, 0x1},
+
+	/* 44.1k */
+	{11289600, 44100, 256, 0x10, 0x0},
+	{16934400, 44100, 384, 0x11, 0x0},
+	{12000000, 44100, 272, 0x11, 0x1},
+
+	/* 48k */
+	{12288000, 48000, 256, 0x0, 0x0},
+	{18432000, 48000, 384, 0x1, 0x0},
+	{12000000, 48000, 250, 0x0, 0x1},
+
+	/* 88.2k */
+	{11289600, 88200, 128, 0x1e, 0x0},
+	{16934400, 88200, 192, 0x1f, 0x0},
+	{12000000, 88200, 136, 0x1f, 0x1},
+
+	/* 96k */
+	{12288000, 96000, 128, 0xe, 0x0},
+	{18432000, 96000, 192, 0xf, 0x0},
+	{12000000, 96000, 125, 0xe, 0x1},
+};
+
+static int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return -EINVAL;
+}
+
+static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8971_priv *wm8971 = codec->private_data;
+
+	switch (freq) {
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 16934400:
+	case 18432000:
+		wm8971->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface = 0x0040;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0013;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x0090;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0080;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0010;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8971_write(codec, WM8971_IFACE, iface);
+	return 0;
+}
+
+static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8971_priv *wm8971 = codec->private_data;
+	u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
+	u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
+	int coeff = get_coeff(wm8971->sysclk, params_rate(params));
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0004;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0008;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x000c;
+		break;
+	}
+
+	/* set iface & srate */
+	wm8971_write(codec, WM8971_IFACE, iface);
+	if (coeff >= 0)
+		wm8971_write(codec, WM8971_SRATE, srate |
+			(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+	return 0;
+}
+
+static int wm8971_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
+
+	if (mute)
+		wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+	else
+		wm8971_write(codec, WM8971_ADCDAC, mute_reg);
+	return 0;
+}
+
+static int wm8971_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* set vmid to 50k and unmute dac */
+		wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* mute dac and set vmid to 500k, enable VREF */
+		wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+		break;
+	case SND_SOC_BIAS_OFF:
+		wm8971_write(codec, WM8971_PWR1, 0x0001);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8971_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 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8971_dai = {
+	.name = "WM8971",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8971_RATES,
+		.formats = WM8971_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8971_RATES,
+		.formats = WM8971_FORMATS,},
+	.ops = {
+		.hw_params = wm8971_pcm_hw_params,
+	},
+	.dai_ops = {
+		.digital_mute = wm8971_mute,
+		.set_fmt = wm8971_set_dai_fmt,
+		.set_sysclk = wm8971_set_dai_sysclk,
+	},
+};
+EXPORT_SYMBOL_GPL(wm8971_dai);
+
+static void wm8971_work(struct work_struct *work)
+{
+	struct snd_soc_codec *codec =
+		container_of(work, struct snd_soc_codec, delayed_work.work);
+	wm8971_set_bias_level(codec, codec->bias_level);
+}
+
+static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8971_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+	u16 reg;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) {
+		if (i + 1 == WM8971_RESET)
+			continue;
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+
+	wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* charge wm8971 caps */
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+		reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+		wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+		codec->bias_level = SND_SOC_BIAS_ON;
+		queue_delayed_work(wm8971_workq, &codec->delayed_work,
+			msecs_to_jiffies(1000));
+	}
+
+	return 0;
+}
+
+static int wm8971_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int reg, ret = 0;
+
+	codec->name = "WM8971";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8971_read_reg_cache;
+	codec->write = wm8971_write;
+	codec->set_bias_level = wm8971_set_bias_level;
+	codec->dai = &wm8971_dai;
+	codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
+	codec->num_dai = 1;
+	codec->reg_cache = kmemdup(wm8971_reg, sizeof(wm8971_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	wm8971_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8971: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* charge output caps - set vmid to 5k for quick power up */
+	reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+	wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+	codec->bias_level = SND_SOC_BIAS_STANDBY;
+	queue_delayed_work(wm8971_workq, &codec->delayed_work,
+		msecs_to_jiffies(1000));
+
+	/* set the update bits */
+	reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
+	wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
+	wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
+
+	reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
+	wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
+	wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
+
+	reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
+	wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
+	wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
+
+	reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
+	wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
+	wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
+
+	wm8971_add_controls(codec);
+	wm8971_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8971: failed to register card\n");
+		goto card_err;
+	}
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+static struct snd_soc_device *wm8971_socdev;
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+
+static int wm8971_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct snd_soc_device *socdev = wm8971_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	i2c_set_clientdata(i2c, codec);
+
+	codec->control_data = i2c;
+
+	ret = wm8971_init(socdev);
+	if (ret < 0)
+		pr_err("failed to initialise WM8971\n");
+
+	return ret;
+}
+
+static int wm8971_i2c_remove(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	kfree(codec->reg_cache);
+	return 0;
+}
+
+static const struct i2c_device_id wm8971_i2c_id[] = {
+	{ "wm8971", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
+
+static struct i2c_driver wm8971_i2c_driver = {
+	.driver = {
+		.name = "WM8971 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe    = wm8971_i2c_probe,
+	.remove   = wm8971_i2c_remove,
+	.id_table = wm8971_i2c_id,
+};
+
+static int wm8971_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8971_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8971_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8971", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8971_i2c_driver);
+	return -ENODEV;
+}
+
+#endif
+
+static int wm8971_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8971_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct wm8971_priv *wm8971;
+	int ret = 0;
+
+	pr_info("WM8971 Audio Codec %s", WM8971_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL);
+	if (wm8971 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = wm8971;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	wm8971_socdev = socdev;
+
+	INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
+	wm8971_workq = create_workqueue("wm8971");
+	if (wm8971_workq == NULL) {
+		kfree(codec->private_data);
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = wm8971_add_i2c_device(pdev, setup);
+	}
+#endif
+	/* Add other interfaces here */
+
+	if (ret != 0) {
+		destroy_workqueue(wm8971_workq);
+		kfree(codec->private_data);
+		kfree(codec);
+	}
+
+	return ret;
+}
+
+/* power down chip */
+static int wm8971_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	if (wm8971_workq)
+		destroy_workqueue(wm8971_workq);
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
+	i2c_del_driver(&wm8971_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8971 = {
+	.probe = 	wm8971_probe,
+	.remove = 	wm8971_remove,
+	.suspend = 	wm8971_suspend,
+	.resume =	wm8971_resume,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
+
+MODULE_DESCRIPTION("ASoC WM8971 driver");
+MODULE_AUTHOR("Lab126");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h
new file mode 100644
index 0000000..ef4f08f
--- /dev/null
+++ b/sound/soc/codecs/wm8971.h
@@ -0,0 +1,64 @@
+/*
+ * wm8971.h  --  audio driver for WM8971
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.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.
+ *
+ */
+
+#ifndef _WM8971_H
+#define _WM8971_H
+
+#define WM8971_LINVOL	0x00
+#define WM8971_RINVOL	0x01
+#define WM8971_LOUT1V	0x02
+#define WM8971_ROUT1V	0x03
+#define WM8971_ADCDAC	0x05
+#define WM8971_IFACE	0x07
+#define WM8971_SRATE	0x08
+#define WM8971_LDAC		0x0a
+#define WM8971_RDAC		0x0b
+#define WM8971_BASS		0x0c
+#define WM8971_TREBLE	0x0d
+#define WM8971_RESET	0x0f
+#define WM8971_ALC1		0x11
+#define	WM8971_ALC2		0x12
+#define	WM8971_ALC3		0x13
+#define WM8971_NGATE	0x14
+#define WM8971_LADC		0x15
+#define WM8971_RADC		0x16
+#define	WM8971_ADCTL1	0x17
+#define	WM8971_ADCTL2	0x18
+#define WM8971_PWR1		0x19
+#define WM8971_PWR2		0x1a
+#define	WM8971_ADCTL3	0x1b
+#define WM8971_ADCIN	0x1f
+#define	WM8971_LADCIN	0x20
+#define	WM8971_RADCIN	0x21
+#define WM8971_LOUTM1	0x22
+#define WM8971_LOUTM2	0x23
+#define WM8971_ROUTM1	0x24
+#define WM8971_ROUTM2	0x25
+#define WM8971_MOUTM1	0x26
+#define WM8971_MOUTM2	0x27
+#define WM8971_LOUT2V	0x28
+#define WM8971_ROUT2V	0x29
+#define WM8971_MOUTV	0x2A
+
+#define WM8971_SYSCLK	0
+
+struct wm8971_setup_data {
+	int i2c_bus;
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8971_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8971;
+
+#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index dd995ef..63410d7 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1477,81 +1477,86 @@
  *    low  = 0x34
  *    high = 0x36
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
 
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8990_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8990_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8990_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 	struct snd_soc_device *socdev = wm8990_socdev;
-	struct wm8990_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL)
-		return -ENOMEM;
-
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		pr_err("failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = wm8990_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		pr_err("failed to initialise WM8990\n");
-		goto err;
-	}
-	return ret;
 
-err:
-	kfree(i2c);
 	return ret;
 }
 
-static int wm8990_i2c_detach(struct i2c_client *client)
+static int wm8990_i2c_remove(struct i2c_client *client)
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 }
 
-static int wm8990_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8990_codec_probe);
-}
+static const struct i2c_device_id wm8990_i2c_id[] = {
+	{ "wm8990", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);
 
 static struct i2c_driver wm8990_i2c_driver = {
 	.driver = {
 		.name = "WM8990 I2C Codec",
 		.owner = THIS_MODULE,
 	},
-	.attach_adapter = wm8990_i2c_attach,
-	.detach_client =  wm8990_i2c_detach,
-	.command =        NULL,
+	.probe =    wm8990_i2c_probe,
+	.remove =   wm8990_i2c_remove,
+	.id_table = wm8990_i2c_id,
 };
 
-static struct i2c_client client_template = {
-	.name =   "WM8990",
-	.driver = &wm8990_i2c_driver,
-};
+static int wm8990_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8990_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8990_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8990", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8990_i2c_driver);
+	return -ENODEV;
+}
 #endif
 
 static int wm8990_probe(struct platform_device *pdev)
@@ -1560,7 +1565,7 @@
 	struct wm8990_setup_data *setup;
 	struct snd_soc_codec *codec;
 	struct wm8990_priv *wm8990;
-	int ret = 0;
+	int ret;
 
 	pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
 
@@ -1582,16 +1587,13 @@
 	INIT_LIST_HEAD(&codec->dapm_paths);
 	wm8990_socdev = socdev;
 
+	ret = -ENODEV;
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8990_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = wm8990_add_i2c_device(pdev, setup);
 	}
-#else
-		/* Add other interfaces here */
 #endif
 
 	if (ret != 0) {
@@ -1612,6 +1614,7 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&wm8990_i2c_driver);
 #endif
 	kfree(codec->private_data);
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
index 0a08325..0e192f3 100644
--- a/sound/soc/codecs/wm8990.h
+++ b/sound/soc/codecs/wm8990.h
@@ -827,6 +827,7 @@
 #define WM8990_AINRMUX_PWR_BIT			3
 
 struct wm8990_setup_data {
+	unsigned i2c_bus;
 	unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 38d1fe0..441d058 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -419,8 +419,12 @@
 SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
 SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
-SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1),
-SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1),
+SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
+SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0),
 SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
 SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
 SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
@@ -583,9 +587,13 @@
 
 	/* left ADC */
 	{"Left ADC", NULL, "Left Capture Source"},
+	{"Left Voice ADC", NULL, "Left ADC"},
+	{"Left HiFi ADC", NULL, "Left ADC"},
 
 	/* right ADC */
 	{"Right ADC", NULL, "Right Capture Source"},
+	{"Right Voice ADC", NULL, "Right ADC"},
+	{"Right HiFi ADC", NULL, "Right ADC"},
 
 	/* mic */
 	{"Mic A Pre Amp", NULL, "Mic A Source"},
@@ -949,17 +957,17 @@
 
 static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
 {
-    struct snd_soc_pcm_runtime *rtd = substream->private_data;
-    struct snd_soc_device *socdev = rtd->socdev;
-    struct snd_soc_codec *codec = socdev->codec;
-    u16 status;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 status;
 
-    /* Gracefully shut down the voice interface. */
-    status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
-    ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
-    schedule_timeout_interruptible(msecs_to_jiffies(1));
-    ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
-    ac97_write(codec, AC97_EXTENDED_MID, status);
+	/* Gracefully shut down the voice interface. */
+	status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
+	ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
+	schedule_timeout_interruptible(msecs_to_jiffies(1));
+	ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
+	ac97_write(codec, AC97_EXTENDED_MID, status);
 }
 
 static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 65fdbd8..9e6062c 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -1,7 +1,7 @@
 /*
  * ASoC driver for TI DAVINCI EVM platform
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -136,6 +136,7 @@
 
 /* evm audio private data */
 static struct aic3x_setup_data evm_aic3x_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1b,
 };
 
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 5ebf1ff..abb5fed 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -1,7 +1,7 @@
 /*
  * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -256,7 +256,7 @@
 		mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
 		break;
 	default:
-		printk(KERN_WARNING "davinci-i2s: unsupported PCM format");
+		printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
 		return -EINVAL;
 	}
 
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index c5b0918..241648c 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -1,7 +1,7 @@
 /*
  * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 6a5e56a..76feaa6 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -1,7 +1,7 @@
 /*
  * ALSA PCM interface for the TI DAVINCI processor
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 8d6a45e..62cb4eb 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -1,7 +1,7 @@
 /*
  * ALSA PCM interface for the TI DAVINCI processor
  *
- * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com>
+ * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3368ace..bba9546 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,3 +1,6 @@
+config SND_SOC_OF_SIMPLE
+	tristate
+
 config SND_SOC_MPC8610
 	bool "ALSA SoC support for the MPC8610 SOC"
 	depends on MPC8610_HPCD
@@ -14,3 +17,10 @@
 	default y if MPC8610_HPCD
 	help
 	  Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
+
+config SND_SOC_MPC5200_I2S
+	tristate "Freescale MPC5200 PSC in I2S mode driver"
+	select SND_SOC_OF_SIMPLE
+	depends on SND_SOC && PPC_MPC52xx
+	help
+	  Say Y here to support the MPC5200 PSCs in I2S mode.
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 62f680a..035da4a 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -1,6 +1,11 @@
+# Simple machine driver that extracts configuration from the OF device tree
+obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o
+
 # MPC8610 HPCD Machine Support
 obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
 
 # MPC8610 Platform Support
 obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
 
+obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
+
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
new file mode 100644
index 0000000..8692329
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -0,0 +1,884 @@
+/*
+ * Freescale MPC5200 PSC in I2S mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-of-simple.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <asm/mpc52xx_psc.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
+MODULE_LICENSE("GPL");
+
+/**
+ * PSC_I2S_RATES: sample rates supported by the I2S
+ *
+ * This driver currently only supports the PSC running in I2S slave mode,
+ * which means the codec determines the sample rate.  Therefore, we tell
+ * ALSA that we support all rates and let the codec driver decide what rates
+ * are really supported.
+ */
+#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
+			SNDRV_PCM_RATE_CONTINUOUS)
+
+/**
+ * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
+ */
+#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
+			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
+			 SNDRV_PCM_FMTBIT_S32_BE)
+
+/**
+ * psc_i2s_stream - Data specific to a single stream (playback or capture)
+ * @active:		flag indicating if the stream is active
+ * @psc_i2s:		pointer back to parent psc_i2s data structure
+ * @bcom_task:		bestcomm task structure
+ * @irq:		irq number for bestcomm task
+ * @period_start:	physical address of start of DMA region
+ * @period_end:		physical address of end of DMA region
+ * @period_next_pt:	physical address of next DMA buffer to enqueue
+ * @period_bytes:	size of DMA period in bytes
+ */
+struct psc_i2s_stream {
+	int active;
+	struct psc_i2s *psc_i2s;
+	struct bcom_task *bcom_task;
+	int irq;
+	struct snd_pcm_substream *stream;
+	dma_addr_t period_start;
+	dma_addr_t period_end;
+	dma_addr_t period_next_pt;
+	dma_addr_t period_current_pt;
+	int period_bytes;
+};
+
+/**
+ * psc_i2s - Private driver data
+ * @name: short name for this device ("PSC0", "PSC1", etc)
+ * @psc_regs: pointer to the PSC's registers
+ * @fifo_regs: pointer to the PSC's FIFO registers
+ * @irq: IRQ of this PSC
+ * @dev: struct device pointer
+ * @dai: the CPU DAI for this device
+ * @sicr: Base value used in serial interface control register; mode is ORed
+ *        with this value.
+ * @playback: Playback stream context data
+ * @capture: Capture stream context data
+ */
+struct psc_i2s {
+	char name[32];
+	struct mpc52xx_psc __iomem *psc_regs;
+	struct mpc52xx_psc_fifo __iomem *fifo_regs;
+	unsigned int irq;
+	struct device *dev;
+	struct snd_soc_dai dai;
+	spinlock_t lock;
+	u32 sicr;
+
+	/* per-stream data */
+	struct psc_i2s_stream playback;
+	struct psc_i2s_stream capture;
+
+	/* Statistics */
+	struct {
+		int overrun_count;
+		int underrun_count;
+	} stats;
+};
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
+{
+	struct psc_i2s *psc_i2s = _psc_i2s;
+	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+	u16 isr;
+
+	isr = in_be16(&regs->mpc52xx_psc_isr);
+
+	/* Playback underrun error */
+	if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
+		psc_i2s->stats.underrun_count++;
+
+	/* Capture overrun error */
+	if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
+		psc_i2s->stats.overrun_count++;
+
+	out_8(&regs->command, 4 << 4);	/* reset the error status */
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
+ * @s: pointer to stream private data structure
+ *
+ * Enqueues another audio period buffer into the bestcomm queue.
+ *
+ * Note: The routine must only be called when there is space available in
+ * the queue.  Otherwise the enqueue will fail and the audio ring buffer
+ * will get out of sync
+ */
+static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
+{
+	struct bcom_bd *bd;
+
+	/* Prepare and enqueue the next buffer descriptor */
+	bd = bcom_prepare_next_buffer(s->bcom_task);
+	bd->status = s->period_bytes;
+	bd->data[0] = s->period_next_pt;
+	bcom_submit_next_buffer(s->bcom_task, NULL);
+
+	/* Update for next period */
+	s->period_next_pt += s->period_bytes;
+	if (s->period_next_pt >= s->period_end)
+		s->period_next_pt = s->period_start;
+}
+
+/* Bestcomm DMA irq handler */
+static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
+{
+	struct psc_i2s_stream *s = _psc_i2s_stream;
+
+	/* For each finished period, dequeue the completed period buffer
+	 * and enqueue a new one in it's place. */
+	while (bcom_buffer_done(s->bcom_task)) {
+		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+		s->period_current_pt += s->period_bytes;
+		if (s->period_current_pt >= s->period_end)
+			s->period_current_pt = s->period_start;
+		psc_i2s_bcom_enqueue_next_buffer(s);
+		bcom_enable(s->bcom_task);
+	}
+
+	/* If the stream is active, then also inform the PCM middle layer
+	 * of the period finished event. */
+	if (s->active)
+		snd_pcm_period_elapsed(s->stream);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * psc_i2s_startup: create a new substream
+ *
+ * This is the first function called when a stream is opened.
+ *
+ * If this is the first stream open, then grab the IRQ and program most of
+ * the PSC registers.
+ */
+static int psc_i2s_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	int rc;
+
+	dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
+
+	if (!psc_i2s->playback.active &&
+	    !psc_i2s->capture.active) {
+		/* Setup the IRQs */
+		rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
+				 "psc-i2s-status", psc_i2s);
+		rc |= request_irq(psc_i2s->capture.irq,
+				  &psc_i2s_bcom_irq, IRQF_SHARED,
+				  "psc-i2s-capture", &psc_i2s->capture);
+		rc |= request_irq(psc_i2s->playback.irq,
+				  &psc_i2s_bcom_irq, IRQF_SHARED,
+				  "psc-i2s-playback", &psc_i2s->playback);
+		if (rc) {
+			free_irq(psc_i2s->irq, psc_i2s);
+			free_irq(psc_i2s->capture.irq,
+				 &psc_i2s->capture);
+			free_irq(psc_i2s->playback.irq,
+				 &psc_i2s->playback);
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	u32 mode;
+
+	dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+		" periods=%i buffer_size=%i  buffer_bytes=%i\n",
+		__func__, substream, params_period_size(params),
+		params_period_bytes(params), params_periods(params),
+		params_buffer_size(params), params_buffer_bytes(params));
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		mode = MPC52xx_PSC_SICR_SIM_CODEC_8;
+		break;
+	case SNDRV_PCM_FORMAT_S16_BE:
+		mode = MPC52xx_PSC_SICR_SIM_CODEC_16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_BE:
+		mode = MPC52xx_PSC_SICR_SIM_CODEC_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_BE:
+		mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
+		break;
+	default:
+		dev_dbg(psc_i2s->dev, "invalid format\n");
+		return -EINVAL;
+	}
+	out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+/**
+ * psc_i2s_trigger: start and stop the DMA transfer.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the DMA
+ * transfer of data.
+ */
+static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct psc_i2s_stream *s;
+	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
+	u16 imr;
+	u8 psc_cmd;
+	long flags;
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_i2s->capture;
+	else
+		s = &psc_i2s->playback;
+
+	dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
+		" stream_id=%i\n",
+		substream, cmd, substream->pstr->stream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		s->period_bytes = frames_to_bytes(runtime,
+						  runtime->period_size);
+		s->period_start = virt_to_phys(runtime->dma_area);
+		s->period_end = s->period_start +
+				(s->period_bytes * runtime->periods);
+		s->period_next_pt = s->period_start;
+		s->period_current_pt = s->period_start;
+		s->active = 1;
+
+		/* First; reset everything */
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			out_8(&regs->command, MPC52xx_PSC_RST_RX);
+			out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+		} else {
+			out_8(&regs->command, MPC52xx_PSC_RST_TX);
+			out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+		}
+
+		/* Next, fill up the bestcomm bd queue and enable DMA.
+		 * This will begin filling the PSC's fifo. */
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+			bcom_gen_bd_rx_reset(s->bcom_task);
+		else
+			bcom_gen_bd_tx_reset(s->bcom_task);
+		while (!bcom_queue_full(s->bcom_task))
+			psc_i2s_bcom_enqueue_next_buffer(s);
+		bcom_enable(s->bcom_task);
+
+		/* Due to errata in the i2s mode; need to line up enabling
+		 * the transmitter with a transition on the frame sync
+		 * line */
+
+		spin_lock_irqsave(&psc_i2s->lock, flags);
+		/* first make sure it is low */
+		while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
+			;
+		/* then wait for the transition to high */
+		while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
+			;
+		/* Finally, enable the PSC.
+		 * Receiver must always be enabled; even when we only want
+		 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
+		psc_cmd = MPC52xx_PSC_RX_ENABLE;
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			psc_cmd |= MPC52xx_PSC_TX_ENABLE;
+		out_8(&regs->command, psc_cmd);
+		spin_unlock_irqrestore(&psc_i2s->lock, flags);
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* Turn off the PSC */
+		s->active = 0;
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			if (!psc_i2s->playback.active) {
+				out_8(&regs->command, 2 << 4);	/* reset rx */
+				out_8(&regs->command, 3 << 4);	/* reset tx */
+				out_8(&regs->command, 4 << 4);	/* reset err */
+			}
+		} else {
+			out_8(&regs->command, 3 << 4);	/* reset tx */
+			out_8(&regs->command, 4 << 4);	/* reset err */
+			if (!psc_i2s->capture.active)
+				out_8(&regs->command, 2 << 4);	/* reset rx */
+		}
+
+		bcom_disable(s->bcom_task);
+		while (!bcom_queue_empty(s->bcom_task))
+			bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+		break;
+
+	default:
+		dev_dbg(psc_i2s->dev, "invalid command\n");
+		return -EINVAL;
+	}
+
+	/* Update interrupt enable settings */
+	imr = 0;
+	if (psc_i2s->playback.active)
+		imr |= MPC52xx_PSC_IMR_TXEMP;
+	if (psc_i2s->capture.active)
+		imr |= MPC52xx_PSC_IMR_ORERR;
+	out_be16(&regs->isr_imr.imr, imr);
+
+	return 0;
+}
+
+/**
+ * psc_i2s_shutdown: shutdown the data transfer on a stream
+ *
+ * Shutdown the PSC if there are no other substreams open.
+ */
+static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+
+	dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
+
+	/*
+	 * If this is the last active substream, disable the PSC and release
+	 * the IRQ.
+	 */
+	if (!psc_i2s->playback.active &&
+	    !psc_i2s->capture.active) {
+
+		/* Disable all interrupts and reset the PSC */
+		out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
+		out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */
+		out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */
+		out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
+		out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+
+		/* Release irqs */
+		free_irq(psc_i2s->irq, psc_i2s);
+		free_irq(psc_i2s->capture.irq, &psc_i2s->capture);
+		free_irq(psc_i2s->playback.irq, &psc_i2s->playback);
+	}
+}
+
+/**
+ * psc_i2s_set_sysclk: set the clock frequency and direction
+ *
+ * This function is called by the machine driver to tell us what the clock
+ * frequency and direction are.
+ *
+ * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
+ * and we don't care about the frequency.  Return an error if the direction
+ * is not SND_SOC_CLOCK_IN.
+ *
+ * @clk_id: reserved, should be zero
+ * @freq: the frequency of the given clock ID, currently ignored
+ * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
+ */
+static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+			      int clk_id, unsigned int freq, int dir)
+{
+	struct psc_i2s *psc_i2s = cpu_dai->private_data;
+	dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
+				cpu_dai, dir);
+	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
+}
+
+/**
+ * psc_i2s_set_fmt: set the serial format.
+ *
+ * This function is called by the machine driver to tell us what serial
+ * format to use.
+ *
+ * This driver only supports I2S mode.  Return an error if the format is
+ * not SND_SOC_DAIFMT_I2S.
+ *
+ * @format: one of SND_SOC_DAIFMT_xxx
+ */
+static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
+{
+	struct psc_i2s *psc_i2s = cpu_dai->private_data;
+	dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
+				cpu_dai, format);
+	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
+}
+
+/* ---------------------------------------------------------------------
+ * ALSA SoC Bindings
+ *
+ * - Digital Audio Interface (DAI) template
+ * - create/destroy dai hooks
+ */
+
+/**
+ * psc_i2s_dai_template: template CPU Digital Audio Interface
+ */
+static struct snd_soc_dai psc_i2s_dai_template = {
+	.type = SND_SOC_DAI_I2S,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = PSC_I2S_RATES,
+		.formats = PSC_I2S_FORMATS,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = PSC_I2S_RATES,
+		.formats = PSC_I2S_FORMATS,
+	},
+	.ops = {
+		.startup = psc_i2s_startup,
+		.hw_params = psc_i2s_hw_params,
+		.hw_free = psc_i2s_hw_free,
+		.shutdown = psc_i2s_shutdown,
+		.trigger = psc_i2s_trigger,
+	},
+	.dai_ops = {
+		.set_sysclk = psc_i2s_set_sysclk,
+		.set_fmt = psc_i2s_set_fmt,
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * The PSC I2S 'ASoC platform' driver
+ *
+ * Can be referenced by an 'ASoC machine' driver
+ * This driver only deals with the audio bus; it doesn't have any
+ * interaction with the attached codec
+ */
+
+static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER,
+	.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
+		   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
+	.rate_min = 8000,
+	.rate_max = 48000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.period_bytes_max	= 1024 * 1024,
+	.period_bytes_min	= 32,
+	.periods_min		= 2,
+	.periods_max		= 256,
+	.buffer_bytes_max	= 2 * 1024 * 1024,
+	.fifo_size		= 0,
+};
+
+static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	struct psc_i2s_stream *s;
+
+	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_i2s->capture;
+	else
+		s = &psc_i2s->playback;
+
+	snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
+
+	s->stream = substream;
+	return 0;
+}
+
+static int psc_i2s_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	struct psc_i2s_stream *s;
+
+	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_i2s->capture;
+	else
+		s = &psc_i2s->playback;
+
+	s->stream = NULL;
+	return 0;
+}
+
+static snd_pcm_uframes_t
+psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	struct psc_i2s_stream *s;
+	dma_addr_t count;
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_i2s->capture;
+	else
+		s = &psc_i2s->playback;
+
+	count = s->period_current_pt - s->period_start;
+
+	return bytes_to_frames(substream->runtime, count);
+}
+
+static struct snd_pcm_ops psc_i2s_pcm_ops = {
+	.open		= psc_i2s_pcm_open,
+	.close		= psc_i2s_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.pointer	= psc_i2s_pcm_pointer,
+};
+
+static u64 psc_i2s_pcm_dmamask = 0xffffffff;
+static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+			   struct snd_pcm *pcm)
+{
+	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+	size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
+	int rc = 0;
+
+	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
+		card, dai, pcm);
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &psc_i2s_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (pcm->streams[0].substream) {
+		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+					&pcm->streams[0].substream->dma_buffer);
+		if (rc)
+			goto playback_alloc_err;
+	}
+
+	if (pcm->streams[1].substream) {
+		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
+					&pcm->streams[1].substream->dma_buffer);
+		if (rc)
+			goto capture_alloc_err;
+	}
+
+	return 0;
+
+ capture_alloc_err:
+	if (pcm->streams[0].substream)
+		snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+ playback_alloc_err:
+	dev_err(card->dev, "Cannot allocate buffer(s)\n");
+	return -ENOMEM;
+}
+
+static void psc_i2s_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+	struct snd_pcm_substream *substream;
+	int stream;
+
+	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (substream) {
+			snd_dma_free_pages(&substream->dma_buffer);
+			substream->dma_buffer.area = NULL;
+			substream->dma_buffer.addr = 0;
+		}
+	}
+}
+
+struct snd_soc_platform psc_i2s_pcm_soc_platform = {
+	.name		= "mpc5200-psc-audio",
+	.pcm_ops	= &psc_i2s_pcm_ops,
+	.pcm_new	= &psc_i2s_pcm_new,
+	.pcm_free	= &psc_i2s_pcm_free,
+};
+
+/* ---------------------------------------------------------------------
+ * Sysfs attributes for debugging
+ */
+
+static ssize_t psc_i2s_status_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+
+	return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x "
+			"tfnum=%i tfstat=0x%.4x\n",
+			in_be16(&psc_i2s->psc_regs->sr_csr.status),
+			in_be32(&psc_i2s->psc_regs->sicr),
+			in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
+			in_be16(&psc_i2s->fifo_regs->rfstat),
+			in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
+			in_be16(&psc_i2s->fifo_regs->tfstat));
+}
+
+static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name)
+{
+	if (strcmp(name, "playback_underrun") == 0)
+		return &psc_i2s->stats.underrun_count;
+	if (strcmp(name, "capture_overrun") == 0)
+		return &psc_i2s->stats.overrun_count;
+
+	return NULL;
+}
+
+static ssize_t psc_i2s_stat_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+	int *attrib;
+
+	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
+	if (!attrib)
+		return 0;
+
+	return sprintf(buf, "%i\n", *attrib);
+}
+
+static ssize_t psc_i2s_stat_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
+	int *attrib;
+
+	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
+	if (!attrib)
+		return 0;
+
+	*attrib = simple_strtoul(buf, NULL, 0);
+	return count;
+}
+
+DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
+DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
+DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store);
+
+/* ---------------------------------------------------------------------
+ * OF platform bus binding code:
+ * - Probe/remove operations
+ * - OF device match table
+ */
+static int __devinit psc_i2s_of_probe(struct of_device *op,
+				      const struct of_device_id *match)
+{
+	phys_addr_t fifo;
+	struct psc_i2s *psc_i2s;
+	struct resource res;
+	int size, psc_id, irq, rc;
+	const __be32 *prop;
+	void __iomem *regs;
+
+	dev_dbg(&op->dev, "probing psc i2s device\n");
+
+	/* Get the PSC ID */
+	prop = of_get_property(op->node, "cell-index", &size);
+	if (!prop || size < sizeof *prop)
+		return -ENODEV;
+	psc_id = be32_to_cpu(*prop);
+
+	/* Fetch the registers and IRQ of the PSC */
+	irq = irq_of_parse_and_map(op->node, 0);
+	if (of_address_to_resource(op->node, 0, &res)) {
+		dev_err(&op->dev, "Missing reg property\n");
+		return -ENODEV;
+	}
+	regs = ioremap(res.start, 1 + res.end - res.start);
+	if (!regs) {
+		dev_err(&op->dev, "Could not map registers\n");
+		return -ENODEV;
+	}
+
+	/* Allocate and initialize the driver private data */
+	psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
+	if (!psc_i2s) {
+		iounmap(regs);
+		return -ENOMEM;
+	}
+	spin_lock_init(&psc_i2s->lock);
+	psc_i2s->irq = irq;
+	psc_i2s->psc_regs = regs;
+	psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
+	psc_i2s->dev = &op->dev;
+	psc_i2s->playback.psc_i2s = psc_i2s;
+	psc_i2s->capture.psc_i2s = psc_i2s;
+	snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
+
+	/* Fill out the CPU DAI structure */
+	memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
+	psc_i2s->dai.private_data = psc_i2s;
+	psc_i2s->dai.name = psc_i2s->name;
+	psc_i2s->dai.id = psc_id;
+
+	/* Find the address of the fifo data registers and setup the
+	 * DMA tasks */
+	fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
+	psc_i2s->capture.bcom_task =
+		bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
+	psc_i2s->playback.bcom_task =
+		bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
+	if (!psc_i2s->capture.bcom_task ||
+	    !psc_i2s->playback.bcom_task) {
+		dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
+		iounmap(regs);
+		kfree(psc_i2s);
+		return -ENODEV;
+	}
+
+	/* Disable all interrupts and reset the PSC */
+	out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
+	out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */
+	out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */
+	out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
+	out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+
+	/* Configure the serial interface mode; defaulting to CODEC8 mode */
+	psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
+			MPC52xx_PSC_SICR_CLKPOL;
+	if (of_get_property(op->node, "fsl,cellslave", NULL))
+		psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE |
+				 MPC52xx_PSC_SICR_GENCLK;
+	out_be32(&psc_i2s->psc_regs->sicr,
+		 psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
+
+	/* Check for the codec handle.  If it is not present then we
+	 * are done */
+	if (!of_get_property(op->node, "codec-handle", NULL))
+		return 0;
+
+	/* Set up mode register;
+	 * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
+	 * Second write: register Normal mode for non loopback
+	 */
+	out_8(&psc_i2s->psc_regs->mode, 0);
+	out_8(&psc_i2s->psc_regs->mode, 0);
+
+	/* Set the TX and RX fifo alarm thresholds */
+	out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100);
+	out_8(&psc_i2s->fifo_regs->rfcntl, 0x4);
+	out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100);
+	out_8(&psc_i2s->fifo_regs->tfcntl, 0x7);
+
+	/* Lookup the IRQ numbers */
+	psc_i2s->playback.irq =
+		bcom_get_task_irq(psc_i2s->playback.bcom_task);
+	psc_i2s->capture.irq =
+		bcom_get_task_irq(psc_i2s->capture.bcom_task);
+
+	/* Save what we've done so it can be found again later */
+	dev_set_drvdata(&op->dev, psc_i2s);
+
+	/* Register the SYSFS files */
+	rc = device_create_file(psc_i2s->dev, &dev_attr_status);
+	rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
+	rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
+	if (rc)
+		dev_info(psc_i2s->dev, "error creating sysfs files\n");
+
+	/* Tell the ASoC OF helpers about it */
+	of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
+				     &psc_i2s->dai);
+
+	return 0;
+}
+
+static int __devexit psc_i2s_of_remove(struct of_device *op)
+{
+	struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
+
+	dev_dbg(&op->dev, "psc_i2s_remove()\n");
+
+	bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
+	bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
+
+	iounmap(psc_i2s->psc_regs);
+	iounmap(psc_i2s->fifo_regs);
+	kfree(psc_i2s);
+	dev_set_drvdata(&op->dev, NULL);
+
+	return 0;
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id psc_i2s_match[] __devinitdata = {
+	{ .compatible = "fsl,mpc5200-psc-i2s", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, psc_i2s_match);
+
+static struct of_platform_driver psc_i2s_driver = {
+	.match_table = psc_i2s_match,
+	.probe = psc_i2s_of_probe,
+	.remove = __devexit_p(psc_i2s_of_remove),
+	.driver = {
+		.name = "mpc5200-psc-i2s",
+		.owner = THIS_MODULE,
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup and teardown; simply register the of_platform driver
+ * for the PSC in I2S mode.
+ */
+static int __init psc_i2s_init(void)
+{
+	return of_register_platform_driver(&psc_i2s_driver);
+}
+module_init(psc_i2s_init);
+
+static void __exit psc_i2s_exit(void)
+{
+	of_unregister_platform_driver(&psc_i2s_driver);
+}
+module_exit(psc_i2s_exit);
+
+
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 4bdc9d8..94f89de 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -68,10 +68,6 @@
 	guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[1], 0);
 
-	guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0);
-	guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0);
-	guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0);
-
 	switch (machine_data->ssi_id) {
 	case 0:
 		clrsetbits_be32(&machine_data->guts->pmuxcr,
@@ -230,6 +226,8 @@
 	struct fsl_ssi_info ssi_info;
 	struct fsl_dma_info dma_info;
 	int ret = -ENODEV;
+	unsigned int playback_dma_channel;
+	unsigned int capture_dma_channel;
 
 	machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
 	if (!machine_data)
@@ -381,8 +379,9 @@
 		goto error;
 	}
 
-	/* Find the DMA channels to use.  For now, we always use the first DMA
-	   controller. */
+	/* Find the DMA channels to use.  Both SSIs need to use the same DMA
+	 * controller, so let's use DMA#1.
+	 */
 	for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {
 		iprop = of_get_property(dma_np, "cell-index", NULL);
 		if (iprop && (*iprop == 0)) {
@@ -397,14 +396,19 @@
 	}
 	machine_data->dma_id = *iprop;
 
+	/* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA
+	 * channels 2 and 3.  This is just how the MPC8610 is wired
+	 * internally.
+	 */
+	playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2;
+	capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3;
+
 	/*
-	 * Find the DMA channels to use.  For now, we always use DMA channel 0
-	 * for playback, and DMA channel 1 for capture.
+	 * Find the DMA channels to use.
 	 */
 	while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) {
 		iprop = of_get_property(dma_channel_np, "cell-index", NULL);
-		/* Is it DMA channel 0? */
-		if (iprop && (*iprop == 0)) {
+		if (iprop && (*iprop == playback_dma_channel)) {
 			/* dma_channel[0] and dma_irq[0] are for playback */
 			dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0);
 			dma_info.dma_irq[0] =
@@ -412,7 +416,7 @@
 			machine_data->dma_channel_id[0] = *iprop;
 			continue;
 		}
-		if (iprop && (*iprop == 1)) {
+		if (iprop && (*iprop == capture_dma_channel)) {
 			/* dma_channel[1] and dma_irq[1] are for capture */
 			dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0);
 			dma_info.dma_irq[1] =
diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c
new file mode 100644
index 0000000..0382fda
--- /dev/null
+++ b/sound/soc/fsl/soc-of-simple.c
@@ -0,0 +1,171 @@
+/*
+ * OF helpers for ALSA SoC Layer
+ *
+ * Copyright (C) 2008, Secret Lab Technologies Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-of-simple.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings");
+
+static DEFINE_MUTEX(of_snd_soc_mutex);
+static LIST_HEAD(of_snd_soc_device_list);
+static int of_snd_soc_next_index;
+
+struct of_snd_soc_device {
+	int id;
+	struct list_head list;
+	struct snd_soc_device device;
+	struct snd_soc_machine machine;
+	struct snd_soc_dai_link dai_link;
+	struct platform_device *pdev;
+	struct device_node *platform_node;
+	struct device_node *codec_node;
+};
+
+static struct snd_soc_ops of_snd_soc_ops = {
+};
+
+static struct of_snd_soc_device *
+of_snd_soc_get_device(struct device_node *codec_node)
+{
+	struct of_snd_soc_device *of_soc;
+
+	list_for_each_entry(of_soc, &of_snd_soc_device_list, list) {
+		if (of_soc->codec_node == codec_node)
+			return of_soc;
+	}
+
+	of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL);
+	if (!of_soc)
+		return NULL;
+
+	/* Initialize the structure and add it to the global list */
+	of_soc->codec_node = codec_node;
+	of_soc->id = of_snd_soc_next_index++;
+	of_soc->machine.dai_link = &of_soc->dai_link;
+	of_soc->machine.num_links = 1;
+	of_soc->device.machine = &of_soc->machine;
+	of_soc->dai_link.ops = &of_snd_soc_ops;
+	list_add(&of_soc->list, &of_snd_soc_device_list);
+
+	return of_soc;
+}
+
+static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc)
+{
+	struct platform_device *pdev;
+	int rc;
+
+	/* Only register the device if both the codec and platform have
+	 * been registered */
+	if ((!of_soc->device.codec_data) || (!of_soc->platform_node))
+		return;
+
+	pr_info("platform<-->codec match achieved; registering machine\n");
+
+	pdev = platform_device_alloc("soc-audio", of_soc->id);
+	if (!pdev) {
+		pr_err("of_soc: platform_device_alloc() failed\n");
+		return;
+	}
+
+	pdev->dev.platform_data = of_soc;
+	platform_set_drvdata(pdev, &of_soc->device);
+	of_soc->device.dev = &pdev->dev;
+
+	/* The ASoC device is complete; register it */
+	rc = platform_device_add(pdev);
+	if (rc) {
+		pr_err("of_soc: platform_device_add() failed\n");
+		return;
+	}
+
+}
+
+int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev,
+			      void *codec_data, struct snd_soc_dai *dai,
+			      struct device_node *node)
+{
+	struct of_snd_soc_device *of_soc;
+	int rc = 0;
+
+	pr_info("registering ASoC codec driver: %s\n", node->full_name);
+
+	mutex_lock(&of_snd_soc_mutex);
+	of_soc = of_snd_soc_get_device(node);
+	if (!of_soc) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/* Store the codec data */
+	of_soc->device.codec_data = codec_data;
+	of_soc->device.codec_dev = codec_dev;
+	of_soc->dai_link.name = (char *)node->name;
+	of_soc->dai_link.stream_name = (char *)node->name;
+	of_soc->dai_link.codec_dai = dai;
+
+	/* Now try to register the SoC device */
+	of_snd_soc_register_device(of_soc);
+
+ out:
+	mutex_unlock(&of_snd_soc_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(of_snd_soc_register_codec);
+
+int of_snd_soc_register_platform(struct snd_soc_platform *platform,
+				 struct device_node *node,
+				 struct snd_soc_dai *cpu_dai)
+{
+	struct of_snd_soc_device *of_soc;
+	struct device_node *codec_node;
+	const phandle *handle;
+	int len, rc = 0;
+
+	pr_info("registering ASoC platform driver: %s\n", node->full_name);
+
+	handle = of_get_property(node, "codec-handle", &len);
+	if (!handle || len < sizeof(handle))
+		return -ENODEV;
+	codec_node = of_find_node_by_phandle(*handle);
+	if (!codec_node)
+		return -ENODEV;
+	pr_info("looking for codec: %s\n", codec_node->full_name);
+
+	mutex_lock(&of_snd_soc_mutex);
+	of_soc = of_snd_soc_get_device(codec_node);
+	if (!of_soc) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	of_soc->platform_node = node;
+	of_soc->dai_link.cpu_dai = cpu_dai;
+	of_soc->device.platform = platform;
+	of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
+
+	/* Now try to register the SoC device */
+	of_snd_soc_register_device(of_soc);
+
+ out:
+	mutex_unlock(&of_snd_soc_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(of_snd_soc_register_platform);
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 87d0ed0..d166b6b 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -290,6 +290,7 @@
 
 /* Audio private data */
 static struct aic3x_setup_data n810_aic33_setup = {
+	.i2c_bus = 2,
 	.i2c_address = 0x18,
 	.gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
 	.gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 9212c37..f8c1cdd 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,6 +1,7 @@
 config SND_PXA2XX_SOC
 	tristate "SoC Audio for the Intel PXA2xx chip"
 	depends on ARCH_PXA
+	select SND_PXA2XX_LIB
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the PXA2xx AC97, I2S or SSP interface. You will also need
@@ -13,6 +14,8 @@
 config SND_PXA2XX_SOC_AC97
 	tristate
 	select AC97_BUS
+	select SND_ARM
+	select SND_PXA2XX_LIB_AC97
 	select SND_SOC_AC97_BUS
 
 config SND_PXA2XX_SOC_I2S
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 0a53f72..72b7a51 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -330,6 +330,7 @@
 
 /* corgi audio private data */
 static struct wm8731_setup_data corgi_wm8731_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1b,
 };
 
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index a4697f7..f84f7d8 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -284,6 +284,7 @@
 
 /* poodle audio private data */
 static struct wm8731_setup_data poodle_wm8731_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1b,
 };
 
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index d94a495..a80ae07 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -13,225 +13,30 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
 
 #include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/ac97_codec.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
 
-#include <asm/irq.h>
-#include <linux/mutex.h>
 #include <mach/hardware.h>
 #include <mach/pxa-regs.h>
-#include <mach/pxa2xx-gpio.h>
-#include <mach/audio.h>
 
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
 
-static DEFINE_MUTEX(car_mutex);
-static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
-static volatile long gsr_bits;
-static struct clk *ac97_clk;
-#ifdef CONFIG_PXA27x
-static struct clk *ac97conf_clk;
-#endif
-
-/*
- * Beware PXA27x bugs:
- *
- *   o Slot 12 read from modem space will hang controller.
- *   o CDONE, SDONE interrupt fails after any slot 12 IO.
- *
- * We therefore have an hybrid approach for waiting on SDONE (interrupt or
- * 1 jiffy timeout if interrupt never comes).
- */
-
-static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97,
-	unsigned short reg)
-{
-	unsigned short val = -1;
-	volatile u32 *reg_addr;
-
-	mutex_lock(&car_mutex);
-
-	/* set up primary or secondary codec/modem space */
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
-	reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#else
-	if (reg == AC97_GPIO_STATUS)
-		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
-	else
-		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#endif
-	reg_addr += (reg >> 1);
-
-#ifndef CONFIG_PXA27x
-	if (reg == AC97_GPIO_STATUS) {
-		/* read from controller cache */
-		val = *reg_addr;
-		goto out;
-	}
-#endif
-
-	/* start read access across the ac97 link */
-	GSR = GSR_CDONE | GSR_SDONE;
-	gsr_bits = 0;
-	val = *reg_addr;
-
-	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-	if (!((GSR | gsr_bits) & GSR_SDONE)) {
-		printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n",
-				__func__, reg, GSR | gsr_bits);
-		val = -1;
-		goto out;
-	}
-
-	/* valid data now */
-	GSR = GSR_CDONE | GSR_SDONE;
-	gsr_bits = 0;
-	val = *reg_addr;
-	/* but we've just started another cycle... */
-	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out:	mutex_unlock(&car_mutex);
-	return val;
-}
-
-static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
-	unsigned short val)
-{
-	volatile u32 *reg_addr;
-
-	mutex_lock(&car_mutex);
-
-	/* set up primary or secondary codec/modem space */
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
-	reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#else
-	if (reg == AC97_GPIO_STATUS)
-		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
-	else
-		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
-#endif
-	reg_addr += (reg >> 1);
-
-	GSR = GSR_CDONE | GSR_SDONE;
-	gsr_bits = 0;
-	*reg_addr = val;
-	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1);
-	if (!((GSR | gsr_bits) & GSR_CDONE))
-		printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n",
-				__func__, reg, GSR | gsr_bits);
-
-	mutex_unlock(&car_mutex);
-}
-
 static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
 {
-#ifdef CONFIG_PXA3xx
-	int timeout = 100;
-#endif
-	gsr_bits = 0;
+	pxa2xx_ac97_try_warm_reset(ac97);
 
-#ifdef CONFIG_PXA27x
-	/* warm reset broken on Bulverde,
-	   so manually keep AC97 reset high */
-	pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
-	udelay(10);
-	GCR |= GCR_WARM_RST;
-	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-	udelay(500);
-#elif defined(CONFIG_PXA3xx)
-	/* Can't use interrupts */
-	GCR |= GCR_WARM_RST;
-	while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
-		mdelay(1);
-#else
-	GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
-	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
-	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
-		printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
-				 __func__, gsr_bits);
-
-	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-	GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+	pxa2xx_ac97_finish_reset(ac97);
 }
 
 static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
 {
-#ifdef CONFIG_PXA3xx
-	int timeout = 1000;
+	pxa2xx_ac97_try_cold_reset(ac97);
 
-	/* Hold CLKBPB for 100us */
-	GCR = 0;
-	GCR = GCR_CLKBPB;
-	udelay(100);
-	GCR = 0;
-#endif
-
-	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
-	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
-
-	gsr_bits = 0;
-#ifdef CONFIG_PXA27x
-	/* PXA27x Developers Manual section 13.5.2.2.1 */
-	clk_enable(ac97conf_clk);
-	udelay(5);
-	clk_disable(ac97conf_clk);
-	GCR = GCR_COLD_RST;
-	udelay(50);
-#elif defined(CONFIG_PXA3xx)
-	/* Can't use interrupts on PXA3xx */
-	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-
-	GCR = GCR_WARM_RST | GCR_COLD_RST;
-	while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
-		mdelay(10);
-#else
-	GCR = GCR_COLD_RST;
-	GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
-	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
-#endif
-
-	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
-		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
-				 __func__, gsr_bits);
-
-	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
-	GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
-}
-
-static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
-{
-	long status;
-
-	status = GSR;
-	if (status) {
-		GSR = status;
-		gsr_bits |= status;
-		wake_up(&gsr_wq);
-
-#ifdef CONFIG_PXA27x
-		/* Although we don't use those we still need to clear them
-		   since they tend to spuriously trigger when MMC is used
-		   (hardware bug? go figure)... */
-		MISR = MISR_EOC;
-		PISR = PISR_EOC;
-		MCSR = MCSR_EOC;
-#endif
-
-		return IRQ_HANDLED;
-	}
-
-	return IRQ_NONE;
+	pxa2xx_ac97_finish_reset(ac97);
 }
 
 struct snd_ac97_bus_ops soc_ac97_ops = {
@@ -285,24 +90,13 @@
 static int pxa2xx_ac97_suspend(struct platform_device *pdev,
 	struct snd_soc_dai *dai)
 {
-	GCR |= GCR_ACLINK_OFF;
-	clk_disable(ac97_clk);
-	return 0;
+	return pxa2xx_ac97_hw_suspend();
 }
 
 static int pxa2xx_ac97_resume(struct platform_device *pdev,
 	struct snd_soc_dai *dai)
 {
-	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
-	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
-	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
-	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
-#ifdef CONFIG_PXA27x
-	/* Use GPIO 113 as AC97 Reset on Bulverde */
-	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-#endif
-	clk_enable(ac97_clk);
-	return 0;
+	return pxa2xx_ac97_hw_resume();
 }
 
 #else
@@ -313,61 +107,13 @@
 static int pxa2xx_ac97_probe(struct platform_device *pdev,
 			     struct snd_soc_dai *dai)
 {
-	int ret;
-
-	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
-	if (ret < 0)
-		goto err;
-
-	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
-	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
-	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
-	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
-#ifdef CONFIG_PXA27x
-	/* Use GPIO 113 as AC97 Reset on Bulverde */
-	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
-
-	ac97conf_clk = clk_get(&pdev->dev, "AC97CONFCLK");
-	if (IS_ERR(ac97conf_clk)) {
-		ret = PTR_ERR(ac97conf_clk);
-		ac97conf_clk = NULL;
-		goto err_irq;
-	}
-#endif
-	ac97_clk = clk_get(&pdev->dev, "AC97CLK");
-	if (IS_ERR(ac97_clk)) {
-		ret = PTR_ERR(ac97_clk);
-		ac97_clk = NULL;
-		goto err_irq;
-	}
-	clk_enable(ac97_clk);
-	return 0;
-
- err_irq:
-	GCR |= GCR_ACLINK_OFF;
-#ifdef CONFIG_PXA27x
-	if (ac97conf_clk) {
-		clk_put(ac97conf_clk);
-		ac97conf_clk = NULL;
-	}
-#endif
-	free_irq(IRQ_AC97, NULL);
- err:
-	return ret;
+	return pxa2xx_ac97_hw_probe(pdev);
 }
 
 static void pxa2xx_ac97_remove(struct platform_device *pdev,
 			       struct snd_soc_dai *dai)
 {
-	GCR |= GCR_ACLINK_OFF;
-	free_irq(IRQ_AC97, NULL);
-#ifdef CONFIG_PXA27x
-	clk_put(ac97conf_clk);
-	ac97conf_clk = NULL;
-#endif
-	clk_disable(ac97_clk);
-	clk_put(ac97_clk);
-	ac97_clk = NULL;
+	pxa2xx_ac97_hw_remove(pdev);
 }
 
 static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index c796b18..39d1921 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -21,6 +21,7 @@
 #include <sound/pcm.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
 
 #include <mach/hardware.h>
 #include <mach/pxa-regs.h>
@@ -30,6 +31,15 @@
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-i2s.h"
 
+struct pxa2xx_gpio {
+	u32 sys;
+	u32	rx;
+	u32 tx;
+	u32 clk;
+	u32 frm;
+};
+
+
 struct pxa_i2s_port {
 	u32 sadiv;
 	u32 sacr0;
@@ -65,11 +75,6 @@
 		.frm = GPIO31_SYNC_I2S_MD,
 	},
 	{ /* I2S SoC Master */
-#ifdef CONFIG_PXA27x
-		.sys = GPIO113_I2S_SYSCLK_MD,
-#else
-		.sys = GPIO32_SYSCLK_I2S_MD,
-#endif
 		.rx = GPIO29_SDATA_IN_I2S_MD,
 		.tx = GPIO30_SDATA_OUT_I2S_MD,
 		.clk = GPIO28_BITCLK_OUT_I2S_MD,
@@ -343,6 +348,11 @@
 
 static int __init pxa2xx_i2s_init(void)
 {
+	if (cpu_is_pxa27x())
+		gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD;
+	else
+		gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD;
+
 	clk_i2s = ERR_PTR(-ENOENT);
 	return platform_driver_register(&pxa2xx_i2s_driver);
 }
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 4345f38..afcd892 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -10,64 +10,14 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
 #include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
-
-#include <asm/dma.h>
-#include <mach/hardware.h>
-#include <mach/pxa-regs.h>
-#include <mach/audio.h>
+#include <sound/pxa2xx-lib.h>
 
 #include "pxa2xx-pcm.h"
-
-static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
-	.info			= SNDRV_PCM_INFO_MMAP |
-				  SNDRV_PCM_INFO_MMAP_VALID |
-				  SNDRV_PCM_INFO_INTERLEAVED |
-				  SNDRV_PCM_INFO_PAUSE |
-				  SNDRV_PCM_INFO_RESUME,
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
-					SNDRV_PCM_FMTBIT_S24_LE |
-					SNDRV_PCM_FMTBIT_S32_LE,
-	.period_bytes_min	= 32,
-	.period_bytes_max	= 8192 - 32,
-	.periods_min		= 1,
-	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc),
-	.buffer_bytes_max	= 128 * 1024,
-	.fifo_size		= 32,
-};
-
-struct pxa2xx_runtime_data {
-	int dma_ch;
-	struct pxa2xx_pcm_dma_params *params;
-	pxa_dma_desc *dma_desc_array;
-	dma_addr_t dma_desc_array_phys;
-};
-
-static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
-	struct snd_pcm_substream *substream = dev_id;
-	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-	int dcsr;
-
-	dcsr = DCSR(dma_ch);
-	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
-	if (dcsr & DCSR_ENDINTR) {
-		snd_pcm_period_elapsed(substream);
-	} else {
-		printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
-			prtd->params->name, dma_ch, dcsr);
-	}
-}
+#include "../../arm/pxa2xx-pcm.h"
 
 static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
@@ -76,10 +26,6 @@
 	struct pxa2xx_runtime_data *prtd = runtime->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
-	size_t totsize = params_buffer_bytes(params);
-	size_t period = params_period_bytes(params);
-	pxa_dma_desc *dma_desc;
-	dma_addr_t dma_buff_phys, next_desc_phys;
 	int ret;
 
 	/* return if this is a bufferless transfer e.g.
@@ -106,42 +52,16 @@
 		prtd->dma_ch = ret;
 	}
 
-	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = totsize;
-
-	dma_desc = prtd->dma_desc_array;
-	next_desc_phys = prtd->dma_desc_array_phys;
-	dma_buff_phys = runtime->dma_addr;
-	do {
-		next_desc_phys += sizeof(pxa_dma_desc);
-		dma_desc->ddadr = next_desc_phys;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			dma_desc->dsadr = dma_buff_phys;
-			dma_desc->dtadr = prtd->params->dev_addr;
-		} else {
-			dma_desc->dsadr = prtd->params->dev_addr;
-			dma_desc->dtadr = dma_buff_phys;
-		}
-		if (period > totsize)
-			period = totsize;
-		dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN;
-		dma_desc++;
-		dma_buff_phys += period;
-	} while (totsize -= period);
-	dma_desc[-1].ddadr = prtd->dma_desc_array_phys;
-
-	return 0;
+	return __pxa2xx_pcm_hw_params(substream, params);
 }
 
 static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
 
-	if (prtd && prtd->params)
-		*prtd->params->drcmr = 0;
+	__pxa2xx_pcm_hw_free(substream);
 
 	if (prtd->dma_ch) {
-		snd_pcm_set_runtime_buffer(substream, NULL);
 		pxa_free_dma(prtd->dma_ch);
 		prtd->dma_ch = 0;
 	}
@@ -149,188 +69,21 @@
 	return 0;
 }
 
-static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
-	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
-	DCSR(prtd->dma_ch) &= ~DCSR_RUN;
-	DCSR(prtd->dma_ch) = 0;
-	DCMD(prtd->dma_ch) = 0;
-	*prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
-
-	return 0;
-}
-
-static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-	int ret = 0;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
-		DCSR(prtd->dma_ch) = DCSR_RUN;
-		break;
-
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		DCSR(prtd->dma_ch) &= ~DCSR_RUN;
-		break;
-
-	case SNDRV_PCM_TRIGGER_RESUME:
-		DCSR(prtd->dma_ch) |= DCSR_RUN;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
-		DCSR(prtd->dma_ch) |= DCSR_RUN;
-		break;
-
-	default:
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static snd_pcm_uframes_t
-pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
-	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-			 DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
-	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
-	if (x == runtime->buffer_size)
-		x = 0;
-	return x;
-}
-
-static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct pxa2xx_runtime_data *prtd;
-	int ret;
-
-	snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware);
-
-	/*
-	 * For mysterious reasons (and despite what the manual says)
-	 * playback samples are lost if the DMA count is not a multiple
-	 * of the DMA burst size.  Let's add a rule to enforce that.
-	 */
-	ret = snd_pcm_hw_constraint_step(runtime, 0,
-		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
-	if (ret)
-		goto out;
-
-	ret = snd_pcm_hw_constraint_step(runtime, 0,
-		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
-	if (ret)
-		goto out;
-
-	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		goto out;
-
-	prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL);
-	if (prtd == NULL) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	prtd->dma_desc_array =
-		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-				       &prtd->dma_desc_array_phys, GFP_KERNEL);
-	if (!prtd->dma_desc_array) {
-		ret = -ENOMEM;
-		goto err1;
-	}
-
-	runtime->private_data = prtd;
-	return 0;
-
- err1:
-	kfree(prtd);
- out:
-	return ret;
-}
-
-static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
-	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-			      prtd->dma_desc_array, prtd->dma_desc_array_phys);
-	kfree(prtd);
-	return 0;
-}
-
-static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
-	struct vm_area_struct *vma)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-				     runtime->dma_area,
-				     runtime->dma_addr,
-				     runtime->dma_bytes);
-}
-
 struct snd_pcm_ops pxa2xx_pcm_ops = {
-	.open		= pxa2xx_pcm_open,
-	.close		= pxa2xx_pcm_close,
+	.open		= __pxa2xx_pcm_open,
+	.close		= __pxa2xx_pcm_close,
 	.ioctl		= snd_pcm_lib_ioctl,
 	.hw_params	= pxa2xx_pcm_hw_params,
 	.hw_free	= pxa2xx_pcm_hw_free,
-	.prepare	= pxa2xx_pcm_prepare,
+	.prepare	= __pxa2xx_pcm_prepare,
 	.trigger	= pxa2xx_pcm_trigger,
 	.pointer	= pxa2xx_pcm_pointer,
 	.mmap		= pxa2xx_pcm_mmap,
 };
 
-static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
-	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-	struct snd_dma_buffer *buf = &substream->dma_buffer;
-	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
-	buf->dev.type = SNDRV_DMA_TYPE_DEV;
-	buf->dev.dev = pcm->card->dev;
-	buf->private_data = NULL;
-	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-					   &buf->addr, GFP_KERNEL);
-	if (!buf->area)
-		return -ENOMEM;
-	buf->bytes = size;
-	return 0;
-}
-
-static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
-	struct snd_pcm_substream *substream;
-	struct snd_dma_buffer *buf;
-	int stream;
-
-	for (stream = 0; stream < 2; stream++) {
-		substream = pcm->streams[stream].substream;
-		if (!substream)
-			continue;
-
-		buf = &substream->dma_buffer;
-		if (!buf->area)
-			continue;
-
-		dma_free_writecombine(pcm->card->dev, buf->bytes,
-				      buf->area, buf->addr);
-		buf->area = NULL;
-	}
-}
-
 static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
 
-int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 	struct snd_pcm *pcm)
 {
 	int ret = 0;
@@ -360,7 +113,7 @@
 struct snd_soc_platform pxa2xx_soc_platform = {
 	.name		= "pxa2xx-audio",
 	.pcm_ops 	= &pxa2xx_pcm_ops,
-	.pcm_new	= pxa2xx_pcm_new,
+	.pcm_new	= pxa2xx_soc_pcm_new,
 	.pcm_free	= pxa2xx_pcm_free_dma_buffers,
 };
 EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h
index 54c9c75..60c3b20 100644
--- a/sound/soc/pxa/pxa2xx-pcm.h
+++ b/sound/soc/pxa/pxa2xx-pcm.h
@@ -13,21 +13,6 @@
 #ifndef _PXA2XX_PCM_H
 #define _PXA2XX_PCM_H
 
-struct pxa2xx_pcm_dma_params {
-	char *name;			/* stream identifier */
-	u32 dcmd;			/* DMA descriptor dcmd field */
-	volatile u32 *drcmr;		/* the DMA request channel to use */
-	u32 dev_addr;			/* device physical address for DMA */
-};
-
-struct pxa2xx_gpio {
-	u32 sys;
-	u32	rx;
-	u32 tx;
-	u32 clk;
-	u32 frm;
-};
-
 /* platform data */
 extern struct snd_soc_platform pxa2xx_soc_platform;
 
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 37cb768..3d4738c 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -337,6 +337,7 @@
 
 /* spitz audio private data */
 static struct wm8750_setup_data spitz_wm8750_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1b,
 };
 
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 8089f8e..73a50e9 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -24,6 +24,7 @@
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
 
+#include <asm/mach-types.h>
 #include <asm/hardware/scoop.h>
 #include <mach/regs-clock.h>
 #include <mach/regs-gpio.h>
@@ -586,6 +587,7 @@
 };
 
 static struct wm8753_setup_data neo1973_wm8753_setup = {
+	.i2c_bus = 0,
 	.i2c_address = 0x1a,
 };
 
@@ -596,54 +598,20 @@
 	.codec_data = &neo1973_wm8753_setup,
 };
 
-static struct i2c_client client_template;
-
-static const 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)
+static int lm4857_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
 {
-	int ret;
-
 	DBG("Entered %s\n", __func__);
 
-	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)
-{
-	DBG("Entered %s\n", __func__);
-
-	i2c_detach_client(client);
-	kfree(client);
 	return 0;
 }
 
-static int lm4857_i2c_attach(struct i2c_adapter *adap)
+static int lm4857_i2c_remove(struct i2c_client *client)
 {
 	DBG("Entered %s\n", __func__);
 
-	return i2c_probe(adap, &addr_data, lm4857_amp_probe);
+	return 0;
 }
 
 static u8 lm4857_state;
@@ -681,27 +649,67 @@
 	lm4857_write_regs();
 }
 
-/* corgi i2c codec control layer */
+static const struct i2c_device_id lm4857_i2c_id[] = {
+	{ "neo1973_lm4857", 0 }
+	{ }
+};
+
 static struct i2c_driver lm4857_i2c_driver = {
 	.driver = {
 		.name = "LM4857 I2C Amp",
 		.owner = THIS_MODULE,
 	},
-	.id =             I2C_DRIVERID_LM4857,
 	.suspend =        lm4857_suspend,
 	.resume	=         lm4857_resume,
 	.shutdown =       lm4857_shutdown,
-	.attach_adapter = lm4857_i2c_attach,
-	.detach_client =  lm4857_i2c_detach,
-	.command =        NULL,
-};
-
-static struct i2c_client client_template = {
-	.name =   "LM4857",
-	.driver = &lm4857_i2c_driver,
+	.probe =          lm4857_i2c_probe,
+	.remove =         lm4857_i2c_remove,
+	.id_table =       lm4857_i2c_id,
 };
 
 static struct platform_device *neo1973_snd_device;
+static struct i2c_client *lm4857_client;
+
+static int __init neo1973_add_lm4857_device(struct platform_device *pdev,
+					    int i2c_bus,
+					    unsigned short i2c_address)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&lm4857_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add lm4857 driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = i2c_address;
+	strlcpy(info.type, "neo1973_lm4857", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n", i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add lm4857 device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	lm4857_client = client;
+	return 0;
+
+err_driver:
+	i2c_del_driver(&lm4857_i2c_driver);
+	return -ENODEV;
+}
 
 static int __init neo1973_init(void)
 {
@@ -709,6 +717,12 @@
 
 	DBG("Entered %s\n", __func__);
 
+	if (!machine_is_neo1973_gta01()) {
+		printk(KERN_INFO
+			"Only GTA01 hardware supported by ASoC driver\n");
+		return -ENODEV;
+	}
+
 	neo1973_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!neo1973_snd_device)
 		return -ENOMEM;
@@ -717,12 +731,15 @@
 	neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
 	ret = platform_device_add(neo1973_snd_device);
 
-	if (ret)
+	if (ret) {
 		platform_device_put(neo1973_snd_device);
+		return ret;
+	}
 
-	ret = i2c_add_driver(&lm4857_i2c_driver);
+	ret = neo1973_add_lm4857_device(neo1973_snd_device,
+					neo1973_wm8753_setup, 0x7C);
 	if (ret != 0)
-		printk(KERN_ERR "can't add i2c driver");
+		platform_device_unregister(neo1973_snd_device);
 
 	return ret;
 }
@@ -731,6 +748,7 @@
 {
 	DBG("Entered %s\n", __func__);
 
+	i2c_unregister_device(lm4857_client);
 	i2c_del_driver(&lm4857_i2c_driver);
 	platform_device_unregister(neo1973_snd_device);
 }
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 83f1190..ad38113 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -340,6 +340,12 @@
 	}
 	codec->active--;
 
+	/* Muting the DAC suppresses artifacts caused during digital
+	 * shutdown, for example from stopping clocks.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_soc_dai_digital_mute(codec_dai, 1);
+
 	if (cpu_dai->ops.shutdown)
 		cpu_dai->ops.shutdown(substream);
 
@@ -970,9 +976,29 @@
 		step = codec->reg_cache_step;
 
 	count += sprintf(buf, "%s registers\n", codec->name);
-	for (i = 0; i < codec->reg_cache_size; i += step)
-		count += sprintf(buf + count, "%2x: %4x\n", i,
-			codec->read(codec, i));
+	for (i = 0; i < codec->reg_cache_size; i += step) {
+		count += sprintf(buf + count, "%2x: ", i);
+		if (count >= PAGE_SIZE - 1)
+			break;
+
+		if (codec->display_register)
+			count += codec->display_register(codec, buf + count,
+							 PAGE_SIZE - count, i);
+		else
+			count += snprintf(buf + count, PAGE_SIZE - count,
+					  "%4x", codec->read(codec, i));
+
+		if (count >= PAGE_SIZE - 1)
+			break;
+
+		count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+		if (count >= PAGE_SIZE - 1)
+			break;
+	}
+
+	/* Truncate count; min() would cause a warning */
+	if (count >= PAGE_SIZE)
+		count = PAGE_SIZE - 1;
 
 	return count;
 }
@@ -1296,10 +1322,10 @@
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
-	uinfo->value.enumerated.items = e->mask;
+	uinfo->value.enumerated.items = e->max;
 
-	if (uinfo->value.enumerated.item > e->mask - 1)
-		uinfo->value.enumerated.item = e->mask - 1;
+	if (uinfo->value.enumerated.item > e->max - 1)
+		uinfo->value.enumerated.item = e->max - 1;
 	strcpy(uinfo->value.enumerated.name,
 		e->texts[uinfo->value.enumerated.item]);
 	return 0;
@@ -1322,7 +1348,7 @@
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned short val, bitmask;
 
-	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
 	val = snd_soc_read(codec, e->reg);
 	ucontrol->value.enumerated.item[0]
@@ -1352,14 +1378,14 @@
 	unsigned short val;
 	unsigned short mask, bitmask;
 
-	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
-	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+	if (ucontrol->value.enumerated.item[0] > e->max - 1)
 		return -EINVAL;
 	val = ucontrol->value.enumerated.item[0] << e->shift_l;
 	mask = (bitmask - 1) << e->shift_l;
 	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+		if (ucontrol->value.enumerated.item[1] > e->max - 1)
 			return -EINVAL;
 		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
 		mask |= (bitmask - 1) << e->shift_r;
@@ -1386,10 +1412,10 @@
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = e->mask;
+	uinfo->value.enumerated.items = e->max;
 
-	if (uinfo->value.enumerated.item > e->mask - 1)
-		uinfo->value.enumerated.item = e->mask - 1;
+	if (uinfo->value.enumerated.item > e->max - 1)
+		uinfo->value.enumerated.item = e->max - 1;
 	strcpy(uinfo->value.enumerated.name,
 		e->texts[uinfo->value.enumerated.item]);
 	return 0;
@@ -1434,9 +1460,11 @@
 int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_info *uinfo)
 {
-	int max = (kcontrol->private_value >> 16) & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int max = mc->max;
+	unsigned int shift = mc->min;
+	unsigned int rshift = mc->rshift;
 
 	if (max == 1)
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -1462,13 +1490,15 @@
 int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int rshift = (kcontrol->private_value >> 12) & 0x0f;
-	int max = (kcontrol->private_value >> 16) & 0xff;
-	int mask = (1 << fls(max)) - 1;
-	int invert = (kcontrol->private_value >> 24) & 0x01;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
 
 	ucontrol->value.integer.value[0] =
 		(snd_soc_read(codec, reg) >> shift) & mask;
@@ -1499,13 +1529,15 @@
 int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int rshift = (kcontrol->private_value >> 12) & 0x0f;
-	int max = (kcontrol->private_value >> 16) & 0xff;
-	int mask = (1 << fls(max)) - 1;
-	int invert = (kcontrol->private_value >> 24) & 0x01;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
 	unsigned short val, val2, val_mask;
 
 	val = (ucontrol->value.integer.value[0] & mask);
@@ -1537,7 +1569,9 @@
 int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_info *uinfo)
 {
-	int max = (kcontrol->private_value >> 12) & 0xff;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int max = mc->max;
 
 	if (max == 1)
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -1563,13 +1597,15 @@
 int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int reg2 = (kcontrol->private_value >> 24) & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int max = (kcontrol->private_value >> 12) & 0xff;
-	int mask = (1<<fls(max))-1;
-	int invert = (kcontrol->private_value >> 20) & 0x01;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	int max = mc->max;
+	unsigned int mask = (1<<fls(max))-1;
+	unsigned int invert = mc->invert;
 
 	ucontrol->value.integer.value[0] =
 		(snd_soc_read(codec, reg) >> shift) & mask;
@@ -1598,13 +1634,15 @@
 int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int reg2 = (kcontrol->private_value >> 24) & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int max = (kcontrol->private_value >> 12) & 0xff;
-	int mask = (1 << fls(max)) - 1;
-	int invert = (kcontrol->private_value >> 20) & 0x01;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
 	int err;
 	unsigned short val, val2, val_mask;
 
@@ -1641,8 +1679,10 @@
 int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_info *uinfo)
 {
-	int max = (signed char)((kcontrol->private_value >> 16) & 0xff);
-	int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int max = mc->max;
+	int min = mc->min;
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 2;
@@ -1664,9 +1704,11 @@
 int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	unsigned int reg = mc->reg;
+	int min = mc->min;
 	int val = snd_soc_read(codec, reg);
 
 	ucontrol->value.integer.value[0] =
@@ -1689,9 +1731,11 @@
 int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	unsigned int reg = mc->reg;
+	int min = mc->min;
 	unsigned short val;
 
 	val = (ucontrol->value.integer.value[0]+min) & 0xff;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index f9d100b..9ca9c08 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -38,6 +38,7 @@
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
+#include <linux/debugfs.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -67,7 +68,9 @@
 module_param(dapm_status, int, 0);
 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
 
-static unsigned int pop_time;
+static struct dentry *asoc_debugfs;
+
+static u32 pop_time;
 
 static void pop_wait(void)
 {
@@ -104,10 +107,13 @@
 	case snd_soc_dapm_switch:
 	case snd_soc_dapm_mixer: {
 		int val;
-		int reg = w->kcontrols[i].private_value & 0xff;
-		int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
-		int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
-		int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
+		struct soc_mixer_control *mc = (struct soc_mixer_control *)
+			w->kcontrols[i].private_value;
+		unsigned int reg = mc->reg;
+		unsigned int shift = mc->shift;
+		int max = mc->max;
+		unsigned int mask = (1 << fls(max)) - 1;
+		unsigned int invert = mc->invert;
 
 		val = snd_soc_read(w->codec, reg);
 		val = (val >> shift) & mask;
@@ -122,13 +128,13 @@
 		struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
 		int val, item, bitmask;
 
-		for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
 		val = snd_soc_read(w->codec, e->reg);
 		item = (val >> e->shift_l) & (bitmask - 1);
 
 		p->connect = 0;
-		for (i = 0; i < e->mask; i++) {
+		for (i = 0; i < e->max; i++) {
 			if (!(strcmp(p->name, e->texts[i])) && item == i)
 				p->connect = 1;
 		}
@@ -165,7 +171,7 @@
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	int i;
 
-	for (i = 0; i < e->mask; i++) {
+	for (i = 0; i < e->max; i++) {
 		if (!(strcmp(control_name, e->texts[i]))) {
 			list_add(&path->list, &codec->dapm_paths);
 			list_add(&path->list_sink, &dest->sources);
@@ -247,16 +253,19 @@
 		return 0;
 
 	if (widget->num_kcontrols && k) {
-		int reg = k->private_value & 0xff;
-		int shift = (k->private_value >> 8) & 0x0f;
-		int mask = (k->private_value >> 16) & 0xff;
-		int invert = (k->private_value >> 24) & 0x01;
+		struct soc_mixer_control *mc =
+			(struct soc_mixer_control *)k->private_value;
+		unsigned int reg = mc->reg;
+		unsigned int shift = mc->shift;
+		int max = mc->max;
+		unsigned int mask = (1 << fls(max)) - 1;
+		unsigned int invert = mc->invert;
 
 		if (power) {
 			int i;
 			/* power up has happended, increase volume to last level */
 			if (invert) {
-				for (i = mask; i > widget->saved_value; i--)
+				for (i = max; i > widget->saved_value; i--)
 					snd_soc_update_bits(widget->codec, reg, mask, i);
 			} else {
 				for (i = 0; i < widget->saved_value; i++)
@@ -684,7 +693,7 @@
 /* test and update the power status of a mux widget */
 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
 				 struct snd_kcontrol *kcontrol, int mask,
-				 int val, struct soc_enum* e)
+				 int mux, int val, struct soc_enum *e)
 {
 	struct snd_soc_dapm_path *path;
 	int found = 0;
@@ -700,12 +709,12 @@
 		if (path->kcontrol != kcontrol)
 			continue;
 
-		if (!path->name || ! e->texts[val])
+		if (!path->name || !e->texts[mux])
 			continue;
 
 		found = 1;
 		/* we now need to match the string in the enum to the path */
-		if (!(strcmp(path->name, e->texts[val])))
+		if (!(strcmp(path->name, e->texts[mux])))
 			path->connect = 1; /* new connection */
 		else
 			path->connect = 0; /* old connection must be powered down */
@@ -811,51 +820,35 @@
 
 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
 
-/* pop/click delay times */
-static ssize_t dapm_pop_time_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%d\n", pop_time);
-}
-
-static ssize_t dapm_pop_time_store(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t count)
-
-{
-	unsigned long val;
-
-	if (strict_strtoul(buf, 10, &val) >= 0)
-		pop_time = val;
-	else
-		printk(KERN_ERR "Unable to parse pop_time setting\n");
-
-	return count;
-}
-
-static DEVICE_ATTR(dapm_pop_time, 0744, dapm_pop_time_show,
-		   dapm_pop_time_store);
-
 int snd_soc_dapm_sys_add(struct device *dev)
 {
 	int ret = 0;
 
-	if (dapm_status) {
-		ret = device_create_file(dev, &dev_attr_dapm_widget);
+	if (!dapm_status)
+		return 0;
 
-		if (ret == 0)
-			ret = device_create_file(dev, &dev_attr_dapm_pop_time);
-	}
+	ret = device_create_file(dev, &dev_attr_dapm_widget);
+	if (ret != 0)
+		return ret;
 
-	return ret;
+	asoc_debugfs = debugfs_create_dir("asoc", NULL);
+	if (!IS_ERR(asoc_debugfs))
+		debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs,
+				   &pop_time);
+	else
+		asoc_debugfs = NULL;
+
+	return 0;
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
 {
 	if (dapm_status) {
-		device_remove_file(dev, &dev_attr_dapm_pop_time);
 		device_remove_file(dev, &dev_attr_dapm_widget);
 	}
+
+	if (asoc_debugfs)
+		debugfs_remove_recursive(asoc_debugfs);
 }
 
 /* free all dapm widgets and resources */
@@ -1133,12 +1126,14 @@
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int rshift = (kcontrol->private_value >> 12) & 0x0f;
-	int max = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0x01;
-	int mask = (1 << fls(max)) - 1;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	unsigned int invert = mc->invert;
+	unsigned int mask = (1 << fls(max)) - 1;
 
 	/* return the saved value if we are powered down */
 	if (widget->id == snd_soc_dapm_pga && !widget->power) {
@@ -1176,12 +1171,14 @@
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0x0f;
-	int rshift = (kcontrol->private_value >> 12) & 0x0f;
-	int max = (kcontrol->private_value >> 16) & 0xff;
-	int mask = (1 << fls(max)) - 1;
-	int invert = (kcontrol->private_value >> 24) & 0x01;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
 	unsigned short val, val2, val_mask;
 	int ret;
 
@@ -1248,7 +1245,7 @@
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned short val, bitmask;
 
-	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
 	val = snd_soc_read(widget->codec, e->reg);
 	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
@@ -1278,15 +1275,15 @@
 	unsigned short mask, bitmask;
 	int ret = 0;
 
-	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
-	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+	if (ucontrol->value.enumerated.item[0] > e->max - 1)
 		return -EINVAL;
 	mux = ucontrol->value.enumerated.item[0];
 	val = mux << e->shift_l;
 	mask = (bitmask - 1) << e->shift_l;
 	if (e->shift_l != e->shift_r) {
-		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+		if (ucontrol->value.enumerated.item[1] > e->max - 1)
 			return -EINVAL;
 		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
 		mask |= (bitmask - 1) << e->shift_r;
@@ -1294,7 +1291,7 @@
 
 	mutex_lock(&widget->codec->mutex);
 	widget->value = val;
-	dapm_mux_update_power(widget, kcontrol, mask, mux, e);
+	dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
 	if (widget->event) {
 		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
 			ret = widget->event(widget,
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 1b04259..4ae07e2 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -1,5 +1,61 @@
 /*
- *	Sound core handling. Breaks out sound functions to submodules
+ *	Sound core.  This file is composed of two parts.  sound_class
+ *	which is common to both OSS and ALSA and OSS sound core which
+ *	is used OSS or emulation of it.
+ */
+
+/*
+ * First, the common part.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_SOUND_OSS_CORE
+static int __init init_oss_soundcore(void);
+static void cleanup_oss_soundcore(void);
+#else
+static inline int init_oss_soundcore(void)	{ return 0; }
+static inline void cleanup_oss_soundcore(void)	{ }
+#endif
+
+struct class *sound_class;
+EXPORT_SYMBOL(sound_class);
+
+MODULE_DESCRIPTION("Core sound module");
+MODULE_AUTHOR("Alan Cox");
+MODULE_LICENSE("GPL");
+
+static int __init init_soundcore(void)
+{
+	int rc;
+
+	rc = init_oss_soundcore();
+	if (rc)
+		return rc;
+
+	sound_class = class_create(THIS_MODULE, "sound");
+	if (IS_ERR(sound_class)) {
+		cleanup_oss_soundcore();
+		return PTR_ERR(sound_class);
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_soundcore(void)
+{
+	cleanup_oss_soundcore();
+	class_destroy(sound_class);
+}
+
+module_init(init_soundcore);
+module_exit(cleanup_soundcore);
+
+
+#ifdef CONFIG_SOUND_OSS_CORE
+/*
+ *	OSS sound core handling. Breaks out sound functions to submodules
  *	
  *	Author:		Alan Cox <alan.cox@linux.org>
  *
@@ -34,21 +90,17 @@
  *	locking at some point in 2.3.x.
  */
 
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/smp_lock.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/fs.h>
 #include <linux/sound.h>
 #include <linux/major.h>
 #include <linux/kmod.h>
-#include <linux/device.h>
 
 #define SOUND_STEP 16
 
-
 struct sound_unit
 {
 	int unit_minor;
@@ -64,9 +116,6 @@
 extern int msnd_pinnacle_init(void);
 #endif
 
-struct class *sound_class;
-EXPORT_SYMBOL(sound_class);
-
 /*
  *	Low level list operator. Scan the ordered list, find a hole and
  *	join into it. Called with the lock asserted
@@ -523,31 +572,23 @@
 	return -ENODEV;
 }
 
-MODULE_DESCRIPTION("Core sound module");
-MODULE_AUTHOR("Alan Cox");
-MODULE_LICENSE("GPL");
 MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR);
 
-static void __exit cleanup_soundcore(void)
+static void cleanup_oss_soundcore(void)
 {
 	/* We have nothing to really do here - we know the lists must be
 	   empty */
 	unregister_chrdev(SOUND_MAJOR, "sound");
-	class_destroy(sound_class);
 }
 
-static int __init init_soundcore(void)
+static int __init init_oss_soundcore(void)
 {
 	if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) {
 		printk(KERN_ERR "soundcore: sound device already in use.\n");
 		return -EBUSY;
 	}
-	sound_class = class_create(THIS_MODULE, "sound");
-	if (IS_ERR(sound_class))
-		return PTR_ERR(sound_class);
 
 	return 0;
 }
 
-module_init(init_soundcore);
-module_exit(cleanup_soundcore);
+#endif /* CONFIG_SOUND_OSS_CORE */
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index 0c63e05..49acee0 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -765,7 +765,6 @@
 			       /* playback count */ 1,
 			       /* capture count */  1, &pcm)) < 0)
 		return err;
-	snd_assert(pcm != NULL, return -EINVAL);
 
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops);
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops);
@@ -788,13 +787,6 @@
 
 static int snd_amd7930_info_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
 {
-	int type = kctl->private_value;
-
-	snd_assert(type == VOLUME_MONITOR ||
-		   type == VOLUME_CAPTURE ||
-		   type == VOLUME_PLAYBACK, return -EINVAL);
-	(void) type;
-
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
@@ -809,10 +801,6 @@
 	int type = kctl->private_value;
 	int *swval;
 
-	snd_assert(type == VOLUME_MONITOR ||
-		   type == VOLUME_CAPTURE ||
-		   type == VOLUME_PLAYBACK, return -EINVAL);
-
 	switch (type) {
 	case VOLUME_MONITOR:
 		swval = &amd->mgain;
@@ -838,10 +826,6 @@
 	int type = kctl->private_value;
 	int *swval, change;
 
-	snd_assert(type == VOLUME_MONITOR ||
-		   type == VOLUME_CAPTURE ||
-		   type == VOLUME_PLAYBACK, return -EINVAL);
-
 	switch (type) {
 	case VOLUME_MONITOR:
 		swval = &amd->mgain;
@@ -904,7 +888,8 @@
 	struct snd_card *card;
 	int idx, err;
 
-	snd_assert(amd != NULL && amd->card != NULL, return -EINVAL);
+	if (snd_BUG_ON(!amd || !amd->card))
+		return -EINVAL;
 
 	card = amd->card;
 	strcpy(card->mixername, card->shortname);
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index 1c4797b..791d2fb 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -1560,7 +1560,8 @@
 	struct snd_cs4231 *chip = card->private_data;
 	int err, idx;
 
-	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
+	if (snd_BUG_ON(!chip || !chip->pcm))
+		return -EINVAL;
 
 	strcpy(card->mixername, chip->pcm->name);
 
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index ee2e1b4..c534a2a 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -2223,7 +2223,6 @@
 			       /* playback count */ 1,
 			       /* capture count */  1, &pcm)) < 0)
 		return err;
-	snd_assert(pcm != NULL, return -EINVAL);
 
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);
 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops);
@@ -2263,9 +2262,10 @@
 {
 	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);
 	struct dbri_streaminfo *info;
-	snd_assert(dbri != NULL, return -EINVAL);
+
+	if (snd_BUG_ON(!dbri))
+		return -EINVAL;
 	info = &dbri->stream_info[kcontrol->private_value];
-	snd_assert(info != NULL, return -EINVAL);
 
 	ucontrol->value.integer.value[0] = info->left_gain;
 	ucontrol->value.integer.value[1] = info->right_gain;
@@ -2331,7 +2331,9 @@
 	int shift = (kcontrol->private_value >> 8) & 0xff;
 	int mask = (kcontrol->private_value >> 16) & 0xff;
 	int invert = (kcontrol->private_value >> 24) & 1;
-	snd_assert(dbri != NULL, return -EINVAL);
+
+	if (snd_BUG_ON(!dbri))
+		return -EINVAL;
 
 	if (elem < 4)
 		ucontrol->value.integer.value[0] =
@@ -2356,7 +2358,9 @@
 	int invert = (kcontrol->private_value >> 24) & 1;
 	int changed = 0;
 	unsigned short val;
-	snd_assert(dbri != NULL, return -EINVAL);
+
+	if (snd_BUG_ON(!dbri))
+		return -EINVAL;
 
 	val = (ucontrol->value.integer.value[0] & mask);
 	if (invert == 1)
@@ -2432,7 +2436,8 @@
 	int idx, err;
 	struct snd_dbri *dbri;
 
-	snd_assert(card != NULL && card->private_data != NULL, return -EINVAL);
+	if (snd_BUG_ON(!card || !card->private_data))
+		return -EINVAL;
 	dbri = card->private_data;
 
 	strcpy(card->mixername, card->shortname);
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index c89d2ea..f16a3fc 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -93,10 +93,10 @@
 	int err;
 	struct snd_sf_callback sf_cb;
 
-	snd_assert(emu->hw != NULL, return -EINVAL);
-	snd_assert(emu->max_voices > 0, return -EINVAL);
-	snd_assert(card != NULL, return -EINVAL);
-	snd_assert(name != NULL, return -EINVAL);
+	if (snd_BUG_ON(!emu->hw || emu->max_voices <= 0))
+		return -EINVAL;
+	if (snd_BUG_ON(!card || !name))
+		return -EINVAL;
 
 	emu->card = card;
 	emu->name = kstrdup(name, GFP_KERNEL);
diff --git a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c
index c6917ba..00fc005 100644
--- a/sound/synth/emux/emux_nrpn.c
+++ b/sound/synth/emux/emux_nrpn.c
@@ -289,8 +289,8 @@
 	struct snd_emux_port *port;
 
 	port = p;
-	snd_assert(port != NULL, return);
-	snd_assert(chan != NULL, return);
+	if (snd_BUG_ON(!port || !chan))
+		return;
 
 	if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 &&
 	    chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) {
@@ -379,8 +379,8 @@
 	struct snd_emux *emu;
 
 	port = p;
-	snd_assert(port != NULL, return);
-	snd_assert(chset != NULL, return);
+	if (snd_BUG_ON(!port || !chset))
+		return;
 	emu = port->emu;
 
 	switch (parsed) {
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index f60a98e..5c47b6c 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -114,7 +114,8 @@
 	char tmpname[64];
 
 	emu = closure;
-	snd_assert(arg != NULL && emu != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg || !emu))
+		return -ENXIO;
 
 	mutex_lock(&emu->register_mutex);
 
@@ -183,12 +184,15 @@
 	struct snd_emux *emu;
 	struct snd_emux_port *p;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	p = arg->private_data;
-	snd_assert(p != NULL, return -ENXIO);
+	if (snd_BUG_ON(!p))
+		return -ENXIO;
 
 	emu = p->emu;
-	snd_assert(emu != NULL, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 
 	mutex_lock(&emu->register_mutex);
 	snd_emux_sounds_off_all(p);
@@ -212,12 +216,15 @@
 	struct snd_emux_port *p;
 	int rc;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	p = arg->private_data;
-	snd_assert(p != NULL, return -ENXIO);
+	if (snd_BUG_ON(!p))
+		return -ENXIO;
 
 	emu = p->emu;
-	snd_assert(emu != NULL, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 
 	if (format == GUS_PATCH)
 		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
@@ -252,12 +259,15 @@
 	struct snd_emux_port *p;
 	struct snd_emux *emu;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	p = arg->private_data;
-	snd_assert(p != NULL, return -ENXIO);
+	if (snd_BUG_ON(!p))
+		return -ENXIO;
 
 	emu = p->emu;
-	snd_assert(emu != NULL, return -ENXIO);
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
 
 	switch (cmd) {
 	case SNDCTL_SEQ_RESETSAMPLES:
@@ -282,9 +292,11 @@
 {
 	struct snd_emux_port *p;
 
-	snd_assert(arg != NULL, return -ENXIO);
+	if (snd_BUG_ON(!arg))
+		return -ENXIO;
 	p = arg->private_data;
-	snd_assert(p != NULL, return -ENXIO);
+	if (snd_BUG_ON(!p))
+		return -ENXIO;
 	snd_emux_reset_port(p);
 	return 0;
 }
@@ -302,9 +314,11 @@
 	unsigned char cmd, *data;
 
 	p = private_data;
-	snd_assert(p != NULL, return -EINVAL);
+	if (snd_BUG_ON(!p))
+		return -EINVAL;
 	emu = p->emu;
-	snd_assert(emu != NULL, return -EINVAL);
+	if (snd_BUG_ON(!emu))
+		return -EINVAL;
 	if (ev->type != SNDRV_SEQ_EVENT_OSS)
 		return snd_emux_event_input(ev, direct, private_data, atomic, hop);
 
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
index d176cc0..335aa2c 100644
--- a/sound/synth/emux/emux_seq.c
+++ b/sound/synth/emux/emux_seq.c
@@ -257,7 +257,8 @@
 	struct snd_emux_port *port;
 
 	port = private_data;
-	snd_assert(port != NULL && ev != NULL, return -EINVAL);
+	if (snd_BUG_ON(!port || !ev))
+		return -EINVAL;
 
 	snd_midi_process_event(&emux_ops, ev, &port->chset);
 
@@ -308,9 +309,11 @@
 	struct snd_emux *emu;
 
 	p = private_data;
-	snd_assert(p != NULL, return -EINVAL);
+	if (snd_BUG_ON(!p))
+		return -EINVAL;
 	emu = p->emu;
-	snd_assert(emu != NULL, return -EINVAL);
+	if (snd_BUG_ON(!emu))
+		return -EINVAL;
 
 	mutex_lock(&emu->register_mutex);
 	snd_emux_init_port(p);
@@ -329,9 +332,11 @@
 	struct snd_emux *emu;
 
 	p = private_data;
-	snd_assert(p != NULL, return -EINVAL);
+	if (snd_BUG_ON(!p))
+		return -EINVAL;
 	emu = p->emu;
-	snd_assert(emu != NULL, return -EINVAL);
+	if (snd_BUG_ON(!emu))
+		return -EINVAL;
 
 	mutex_lock(&emu->register_mutex);
 	snd_emux_sounds_off_all(p);
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index b343818..2cc6f6f 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -66,12 +66,12 @@
 	struct snd_emux_port *port;
 
 	port = p;
-	snd_assert(port != NULL && chan != NULL, return);
+	if (snd_BUG_ON(!port || !chan))
+		return;
 
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.get_voice != NULL, return);
-	snd_assert(emu->ops.trigger != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger))
+		return;
 
 	key = note; /* remember the original note */
 	nvoices = get_zone(emu, port, &note, vel, chan, table);
@@ -164,11 +164,12 @@
 	struct snd_emux_port *port;
 
 	port = p;
-	snd_assert(port != NULL && chan != NULL, return);
+	if (snd_BUG_ON(!port || !chan))
+		return;
 
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.release != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.release))
+		return;
 
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	for (ch = 0; ch < emu->max_voices; ch++) {
@@ -242,11 +243,12 @@
 	struct snd_emux_port *port;
 
 	port = p;
-	snd_assert(port != NULL && chan != NULL, return);
+	if (snd_BUG_ON(!port || !chan))
+		return;
 
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.update != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.update))
+		return;
 
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	for (ch = 0; ch < emu->max_voices; ch++) {
@@ -276,8 +278,8 @@
 		return;
 
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.update != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.update))
+		return;
 
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	for (i = 0; i < emu->max_voices; i++) {
@@ -303,8 +305,8 @@
 		return;
 
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.update != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.update))
+		return;
 
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	for (i = 0; i < emu->max_voices; i++) {
@@ -326,7 +328,8 @@
 	struct snd_emux_port *port;
 
 	port = p;
-	snd_assert(port != NULL && chan != NULL, return);
+	if (snd_BUG_ON(!port || !chan))
+		return;
 
 	switch (type) {
 	case MIDI_CTL_MSB_MAIN_VOLUME:
@@ -400,11 +403,12 @@
 	struct snd_emux_port *port;
 
 	port = p;
-	snd_assert(port != NULL && chan != NULL, return);
+	if (snd_BUG_ON(!port || !chan))
+		return;
 
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.terminate != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.terminate))
+		return;
 
 	terminate_note1(emu, note, chan, 1);
 }
@@ -451,10 +455,11 @@
 	struct snd_emux_voice *vp;
 	unsigned long flags;
 
-	snd_assert(port != NULL, return);
+	if (snd_BUG_ON(!port))
+		return;
 	emu = port->emu;
-	snd_assert(emu != NULL, return);
-	snd_assert(emu->ops.terminate != NULL, return);
+	if (snd_BUG_ON(!emu || !emu->ops.terminate))
+		return;
 
 	spin_lock_irqsave(&emu->voice_lock, flags);
 	for (i = 0; i < emu->max_voices; i++) {
diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c
index deabe5f..c85522e 100644
--- a/sound/synth/util_mem.c
+++ b/sound/synth/util_mem.c
@@ -55,7 +55,8 @@
 {
 	struct list_head *p;
 
-	snd_assert(hdr != NULL, return);
+	if (!hdr)
+		return;
 	/* release all blocks */
 	while ((p = hdr->block.next) != &hdr->block) {
 		list_del(p);
@@ -74,8 +75,8 @@
 	unsigned int units, prev_offset;
 	struct list_head *p;
 
-	snd_assert(hdr != NULL, return NULL);
-	snd_assert(size > 0, return NULL);
+	if (snd_BUG_ON(!hdr || size <= 0))
+		return NULL;
 
 	/* word alignment */
 	units = size;
@@ -161,7 +162,8 @@
  */
 int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
 {
-	snd_assert(hdr && blk, return -EINVAL);
+	if (snd_BUG_ON(!hdr || !blk))
+		return -EINVAL;
 
 	mutex_lock(&hdr->block_mutex);
 	__snd_util_mem_free(hdr, blk);
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index ffcdc8f..4f0eac9 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -67,5 +67,17 @@
 	   * Native Instruments Kore Controller 2
 	   * Native Instruments Audio Kontrol 1
 
+config SND_USB_US122L
+	tristate "Tascam US-122L USB driver"
+	depends on X86 && EXPERIMENTAL
+	select SND_HWDEP
+	select SND_RAWMIDI
+	help
+	  Say Y here to include support for Tascam US-122L USB Audio/MIDI
+	  interfaces.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-usb-us122l.
+
 endif	# SND_USB
 
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index aa252ef..abb288b 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -8,5 +8,6 @@
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
 obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o
 
 obj-$(CONFIG_SND) += usx2y/ caiaq/
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index b8cfb7c..bbd70d5 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -71,6 +71,7 @@
 static int nrpacks = 8;		/* max. number of packets per urb */
 static int async_unlink = 1;
 static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/
+static int ignore_ctl_error;
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -88,7 +89,9 @@
 MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
 module_param_array(device_setup, int, NULL, 0444);
 MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
-
+module_param(ignore_ctl_error, bool, 0444);
+MODULE_PARM_DESC(ignore_ctl_error,
+		 "Ignore errors from USB controller for mixer interfaces.");
 
 /*
  * debug the h/w constraints
@@ -481,7 +484,7 @@
 }
 
 /*
- * process after E-Mu 0202/0404 high speed playback sync complete
+ * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
  *
  * These devices return the number of samples per packet instead of the number
  * of samples per microframe.
@@ -841,7 +844,8 @@
 		return -EBADFD;
 
 	for (i = 0; i < subs->nurbs; i++) {
-		snd_assert(subs->dataurb[i].urb, return -EINVAL);
+		if (snd_BUG_ON(!subs->dataurb[i].urb))
+			return -EINVAL;
 		if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
 			snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
 			goto __error;
@@ -849,7 +853,8 @@
 	}
 	if (subs->syncpipe) {
 		for (i = 0; i < SYNC_URBS; i++) {
-			snd_assert(subs->syncurb[i].urb, return -EINVAL);
+			if (snd_BUG_ON(!subs->syncurb[i].urb))
+				return -EINVAL;
 			if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
 				snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
 				goto __error;
@@ -1321,10 +1326,12 @@
 	int err;
 
 	iface = usb_ifnum_to_if(dev, fmt->iface);
-	snd_assert(iface, return -EINVAL);
+	if (WARN_ON(!iface))
+		return -EINVAL;
 	alts = &iface->altsetting[fmt->altset_idx];
 	altsd = get_iface_desc(alts);
-	snd_assert(altsd->bAlternateSetting == fmt->altsetting, return -EINVAL);
+	if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
+		return -EINVAL;
 
 	if (fmt == subs->cur_audiofmt)
 		return 0;
@@ -2257,6 +2264,7 @@
 		switch (as->chip->usb_id) {
 		case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
 		case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+		case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
 			subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
 			break;
 		}
@@ -2989,12 +2997,12 @@
 }
 
 /*
- * Create a stream for an Edirol UA-700/UA-25 interface.  The only way
- * to detect the sample rate is by looking at wMaxPacketSize.
+ * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.  
+ * The only way to detect the sample rate is by looking at wMaxPacketSize.
  */
-static int create_ua700_ua25_quirk(struct snd_usb_audio *chip,
-				   struct usb_interface *iface,
-				   const struct snd_usb_audio_quirk *quirk)
+static int create_uaxx_quirk(struct snd_usb_audio *chip,
+			      struct usb_interface *iface,
+			      const struct snd_usb_audio_quirk *quirk)
 {
 	static const struct audioformat ua_format = {
 		.format = SNDRV_PCM_FORMAT_S24_3LE,
@@ -3009,8 +3017,8 @@
 	struct audioformat *fp;
 	int stream, err;
 
-	/* both PCM and MIDI interfaces have 2 altsettings */
-	if (iface->num_altsetting != 2)
+	/* both PCM and MIDI interfaces have 2 or more altsettings */
+	if (iface->num_altsetting < 2)
 		return -ENXIO;
 	alts = &iface->altsetting[1];
 	altsd = get_iface_desc(alts);
@@ -3024,20 +3032,20 @@
 			.type = QUIRK_MIDI_FIXED_ENDPOINT,
 			.data = &ua700_ep
 		};
-		static const struct snd_usb_midi_endpoint_info ua25_ep = {
+		static const struct snd_usb_midi_endpoint_info uaxx_ep = {
 			.out_cables = 0x0001,
 			.in_cables  = 0x0001
 		};
-		static const struct snd_usb_audio_quirk ua25_quirk = {
+		static const struct snd_usb_audio_quirk uaxx_quirk = {
 			.type = QUIRK_MIDI_FIXED_ENDPOINT,
-			.data = &ua25_ep
+			.data = &uaxx_ep
 		};
 		if (chip->usb_id == USB_ID(0x0582, 0x002b))
 			return snd_usb_create_midi_interface(chip, iface,
 							     &ua700_quirk);
 		else
 			return snd_usb_create_midi_interface(chip, iface,
-							     &ua25_quirk);
+							     &uaxx_quirk);
 	}
 
 	if (altsd->bNumEndpoints != 1)
@@ -3369,9 +3377,9 @@
 		[QUIRK_MIDI_CME] = snd_usb_create_midi_interface,
 		[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
 		[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
-		[QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
 		[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
 		[QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk,
+		[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk
 	};
 
 	if (quirk->type < QUIRK_TYPE_COUNT) {
@@ -3629,7 +3637,7 @@
 	if (err > 0) {
 		/* create normal USB audio interfaces */
 		if (snd_usb_create_streams(chip, ifnum) < 0 ||
-		    snd_usb_create_mixer(chip, ifnum) < 0) {
+		    snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {
 			goto __error;
 		}
 	}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 7cf18c3..36e4f7a 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -156,11 +156,12 @@
 	QUIRK_MIDI_RAW,
 	QUIRK_MIDI_EMAGIC,
 	QUIRK_MIDI_CME,
+	QUIRK_MIDI_US122L,
 	QUIRK_AUDIO_STANDARD_INTERFACE,
 	QUIRK_AUDIO_FIXED_ENDPOINT,
-	QUIRK_AUDIO_EDIROL_UA700_UA25,
 	QUIRK_AUDIO_EDIROL_UA1000,
 	QUIRK_AUDIO_EDIROL_UA101,
+	QUIRK_AUDIO_EDIROL_UAXX,
 
 	QUIRK_TYPE_COUNT
 };
@@ -222,7 +223,8 @@
 		    __u8 request, __u8 requesttype, __u16 value, __u16 index,
 		    void *data, __u16 size, int timeout);
 
-int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif);
+int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
+			 int ignore_error);
 void snd_usb_mixer_disconnect(struct list_head *p);
 
 int snd_usb_create_midi_interface(struct snd_usb_audio *chip, struct usb_interface *iface,
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 6676a177..5962e4b 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -669,6 +669,42 @@
 	.output = snd_usbmidi_raw_output,
 };
 
+static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,
+				     uint8_t *buffer, int buffer_length)
+{
+	if (buffer_length != 9)
+		return;
+	buffer_length = 8;
+	while (buffer_length && buffer[buffer_length - 1] == 0xFD)
+		buffer_length--;
+	if (buffer_length)
+		snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
+}
+
+static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep)
+{
+	int count;
+
+	if (!ep->ports[0].active)
+		return;
+	count = ep->urb->dev->speed == USB_SPEED_HIGH ? 1 : 2;
+	count = snd_rawmidi_transmit(ep->ports[0].substream,
+				     ep->urb->transfer_buffer,
+				     count);
+	if (count < 1) {
+		ep->ports[0].active = 0;
+		return;
+	}
+
+	memset(ep->urb->transfer_buffer + count, 0xFD, 9 - count);
+	ep->urb->transfer_buffer_length = count;
+}
+
+static struct usb_protocol_ops snd_usbmidi_122l_ops = {
+	.input = snd_usbmidi_us122l_input,
+	.output = snd_usbmidi_us122l_output,
+};
+
 /*
  * Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching.
  */
@@ -1076,6 +1112,15 @@
 		}
 		if (ep->in)
 			usb_kill_urb(ep->in->urb);
+		/* free endpoints here; later call can result in Oops */
+		if (ep->out) {
+			snd_usbmidi_out_endpoint_delete(ep->out);
+			ep->out = NULL;
+		}
+		if (ep->in) {
+			snd_usbmidi_in_endpoint_delete(ep->in);
+			ep->in = NULL;
+		}
 	}
 	del_timer_sync(&umidi->error_timer);
 }
@@ -1714,6 +1759,9 @@
 			umidi->usb_protocol_ops =
 				&snd_usbmidi_maudio_broken_running_status_ops;
 		break;
+	case QUIRK_MIDI_US122L:
+		umidi->usb_protocol_ops = &snd_usbmidi_122l_ops;
+		/* fall through */
 	case QUIRK_MIDI_FIXED_ENDPOINT:
 		memcpy(&endpoints[0], quirk->data,
 		       sizeof(struct snd_usb_midi_endpoint_info));
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 89c63d0..a492461 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -59,12 +59,13 @@
 	u8  offset;
 	u8  length;
 	u8  packet_length;
+	u8  min_packet_length; /* minimum accepted length of the URB result */
 	u8  mute_mixer_id;
 	u32 mute_code;
 } rc_configs[] = {
-	{ USB_ID(0x041e, 0x3000), 0, 1, 2,  18, 0x0013 }, /* Extigy       */
-	{ USB_ID(0x041e, 0x3020), 2, 1, 6,  18, 0x0013 }, /* Audigy 2 NX  */
-	{ USB_ID(0x041e, 0x3040), 2, 2, 6,  2,  0x6e91 }, /* Live! 24-bit */
+	{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
+	{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
+	{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
 };
 
 struct usb_mixer_interface {
@@ -1388,7 +1389,8 @@
 	struct usb_mixer_elem_info *cval = kcontrol->private_data;
 	char **itemlist = (char **)kcontrol->private_value;
 
-	snd_assert(itemlist, return -EINVAL);
+	if (snd_BUG_ON(!itemlist))
+		return -EINVAL;
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items = cval->max;
@@ -1781,7 +1783,7 @@
 	const struct rc_config *rc = mixer->rc_cfg;
 	u32 code;
 
-	if (urb->status < 0 || urb->actual_length < rc->packet_length)
+	if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
 		return;
 
 	code = mixer->rc_buffer[rc->offset];
@@ -2012,7 +2014,8 @@
 	}
 }
 
-int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
+int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
+			 int ignore_error)
 {
 	static struct snd_device_ops dev_ops = {
 		.dev_free = snd_usb_mixer_dev_free
@@ -2027,9 +2030,7 @@
 		return -ENOMEM;
 	mixer->chip = chip;
 	mixer->ctrlif = ctrlif;
-#ifdef IGNORE_CTL_ERROR
-	mixer->ignore_ctl_error = 1;
-#endif
+	mixer->ignore_ctl_error = ignore_error;
 	mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL);
 	if (!mixer->id_elems) {
 		kfree(mixer);
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 9ea726c..69689e7 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -62,6 +62,13 @@
 	.idProduct = 0x3f04,
 	.bInterfaceClass = USB_CLASS_AUDIO,
 },
+{
+	/* E-Mu Tracker Pre */
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor = 0x041e,
+	.idProduct = 0x3f0a,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+},
 
 /*
  * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface
@@ -855,15 +862,15 @@
 		.data = (const struct snd_usb_audio_quirk[]) {
 			{
 				.ifnum = 1,
-				.type = QUIRK_AUDIO_EDIROL_UA700_UA25
+				.type = QUIRK_AUDIO_EDIROL_UAXX
 			},
 			{
 				.ifnum = 2,
-				.type = QUIRK_AUDIO_EDIROL_UA700_UA25
+				.type = QUIRK_AUDIO_EDIROL_UAXX
 			},
 			{
 				.ifnum = 3,
-				.type = QUIRK_AUDIO_EDIROL_UA700_UA25
+				.type = QUIRK_AUDIO_EDIROL_UAXX
 			},
 			{
 				.ifnum = -1
@@ -1197,15 +1204,15 @@
 		.data = (const struct snd_usb_audio_quirk[]) {
 			{
 				.ifnum = 0,
-				.type = QUIRK_AUDIO_EDIROL_UA700_UA25
+				.type = QUIRK_AUDIO_EDIROL_UAXX
 			},
 			{
 				.ifnum = 1,
-				.type = QUIRK_AUDIO_EDIROL_UA700_UA25
+				.type = QUIRK_AUDIO_EDIROL_UAXX
 			},
 			{
 				.ifnum = 2,
-				.type = QUIRK_AUDIO_EDIROL_UA700_UA25
+				.type = QUIRK_AUDIO_EDIROL_UAXX
 			},
 			{
 				.ifnum = -1
@@ -1338,6 +1345,36 @@
 		}
 	}
 },
+{
+	/*
+	 * This quirk is for the "Advanced Driver" mode. If off, the UA-4FX
+	 * is standard compliant, but has only 16-bit PCM and no MIDI.
+	 */
+	USB_DEVICE(0x0582, 0x00a3),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "EDIROL",
+		.product_name = "UA-4FX",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_EDIROL_UAXX
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_EDIROL_UAXX
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_AUDIO_EDIROL_UAXX
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
 	/* TODO: add Edirol MD-P1 support */
 {
 	USB_DEVICE(0x582, 0x00a6),
@@ -1383,7 +1420,6 @@
 		}
 	}
 },
-
 {
 	/* Roland SonicCell */
 	USB_DEVICE(0x0582, 0x00c2),
@@ -1415,7 +1451,35 @@
 		}
 	}
 },
-
+{
+	/* BOSS GT-10 */
+	USB_DEVICE(0x0582, 0x00da),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const struct snd_usb_midi_endpoint_info) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
 
 /* Guillemot devices */
 {
diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile
index 9ac22bc..7489330 100644
--- a/sound/usb/usx2y/Makefile
+++ b/sound/usb/usx2y/Makefile
@@ -1,3 +1,5 @@
 snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
+snd-usb-us122l-objs := us122l.o
 
 obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usb-us122l.o
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
new file mode 100644
index 0000000..b441fe2
--- /dev/null
+++ b/sound/usb/usx2y/us122l.c
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sound/core.h>
+#include <sound/hwdep.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#define MODNAME "US122L"
+#include "usb_stream.c"
+#include "../usbaudio.h"
+#include "us122l.h"
+
+MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>");
+MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.5");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-max */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* Id for this card */
+							/* Enable this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");
+
+static int snd_us122l_card_used[SNDRV_CARDS];
+
+
+static int us122l_create_usbmidi(struct snd_card *card)
+{
+	static struct snd_usb_midi_endpoint_info quirk_data = {
+		.out_ep = 4,
+		.in_ep = 3,
+		.out_cables =	0x001,
+		.in_cables =	0x001
+	};
+	static struct snd_usb_audio_quirk quirk = {
+		.vendor_name =	"US122L",
+		.product_name =	NAME_ALLCAPS,
+		.ifnum = 	1,
+		.type = QUIRK_MIDI_US122L,
+		.data = &quirk_data
+	};
+	struct usb_device *dev = US122L(card)->chip.dev;
+	struct usb_interface *iface = usb_ifnum_to_if(dev, 1);
+
+	return snd_usb_create_midi_interface(&US122L(card)->chip,
+					     iface, &quirk);
+}
+
+/*
+ * Wrapper for usb_control_msg().
+ * Allocates a temp buffer to prevent dmaing from/to the stack.
+ */
+static int us122l_ctl_msg(struct usb_device *dev, unsigned int pipe,
+			  __u8 request, __u8 requesttype,
+			  __u16 value, __u16 index, void *data,
+			  __u16 size, int timeout)
+{
+	int err;
+	void *buf = NULL;
+
+	if (size > 0) {
+		buf = kmemdup(data, size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+	}
+	err = usb_control_msg(dev, pipe, request, requesttype,
+			      value, index, buf, size, timeout);
+	if (size > 0) {
+		memcpy(data, buf, size);
+		kfree(buf);
+	}
+	return err;
+}
+
+static void pt_info_set(struct usb_device *dev, u8 v)
+{
+	int ret;
+
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+			      'I',
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      v, 0, NULL, 0, 1000);
+	snd_printdd(KERN_DEBUG "%i\n", ret);
+}
+
+static void usb_stream_hwdep_vm_open(struct vm_area_struct *area)
+{
+	struct us122l *us122l = area->vm_private_data;
+	atomic_inc(&us122l->mmap_count);
+	snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
+}
+
+static int usb_stream_hwdep_vm_fault(struct vm_area_struct *area,
+				     struct vm_fault *vmf)
+{
+	unsigned long offset;
+	struct page *page;
+	void *vaddr;
+	struct us122l *us122l = area->vm_private_data;
+	struct usb_stream *s;
+	int vm_f = VM_FAULT_SIGBUS;
+
+	mutex_lock(&us122l->mutex);
+	s = us122l->sk.s;
+	if (!s)
+		goto out;
+
+	offset = vmf->pgoff << PAGE_SHIFT;
+	if (offset < PAGE_ALIGN(s->read_size))
+		vaddr = (char *)s + offset;
+	else {
+		offset -= PAGE_ALIGN(s->read_size);
+		if (offset >= PAGE_ALIGN(s->write_size))
+			goto out;
+
+		vaddr = us122l->sk.write_page + offset;
+	}
+	page = virt_to_page(vaddr);
+
+	get_page(page);
+	mutex_unlock(&us122l->mutex);
+
+	vmf->page = page;
+	vm_f = 0;
+out:
+	return vm_f;
+}
+
+static void usb_stream_hwdep_vm_close(struct vm_area_struct *area)
+{
+	struct us122l *us122l = area->vm_private_data;
+	atomic_dec(&us122l->mmap_count);
+	snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
+}
+
+static struct vm_operations_struct usb_stream_hwdep_vm_ops = {
+	.open = usb_stream_hwdep_vm_open,
+	.fault = usb_stream_hwdep_vm_fault,
+	.close = usb_stream_hwdep_vm_close,
+};
+
+
+static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	struct us122l	*us122l = hw->private_data;
+	struct usb_interface *iface;
+	snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
+	if (hw->used >= 2)
+		return -EBUSY;
+
+	if (!us122l->first)
+		us122l->first = file;
+	iface = usb_ifnum_to_if(us122l->chip.dev, 1);
+	usb_autopm_get_interface(iface);
+	return 0;
+}
+
+static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct us122l	*us122l = hw->private_data;
+	struct usb_interface *iface = usb_ifnum_to_if(us122l->chip.dev, 1);
+	snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
+	usb_autopm_put_interface(iface);
+	if (us122l->first == file)
+		us122l->first = NULL;
+	mutex_lock(&us122l->mutex);
+	if (us122l->master == file)
+		us122l->master = us122l->slave;
+
+	us122l->slave = NULL;
+	mutex_unlock(&us122l->mutex);
+	return 0;
+}
+
+static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
+				 struct file *filp, struct vm_area_struct *area)
+{
+	unsigned long	size = area->vm_end - area->vm_start;
+	struct us122l	*us122l = hw->private_data;
+	unsigned long offset;
+	struct usb_stream *s;
+	int err = 0;
+	bool read;
+
+	offset = area->vm_pgoff << PAGE_SHIFT;
+	mutex_lock(&us122l->mutex);
+	s = us122l->sk.s;
+	read = offset < s->read_size;
+	if (read && area->vm_flags & VM_WRITE) {
+		err = -EPERM;
+		goto out;
+	}
+	snd_printdd(KERN_DEBUG "%lu %u\n", size,
+		    read ? s->read_size : s->write_size);
+	/* if userspace tries to mmap beyond end of our buffer, fail */
+	if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) {
+		snd_printk(KERN_WARNING "%lu > %u\n", size,
+			   read ? s->read_size : s->write_size);
+		err = -EINVAL;
+		goto out;
+	}
+
+	area->vm_ops = &usb_stream_hwdep_vm_ops;
+	area->vm_flags |= VM_RESERVED;
+	area->vm_private_data = us122l;
+	atomic_inc(&us122l->mmap_count);
+out:
+	mutex_unlock(&us122l->mutex);
+	return err;
+}
+
+static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
+					  struct file *file, poll_table *wait)
+{
+	struct us122l	*us122l = hw->private_data;
+	struct usb_stream *s = us122l->sk.s;
+	unsigned	*polled;
+	unsigned int	mask;
+
+	poll_wait(file, &us122l->sk.sleep, wait);
+
+	switch (s->state) {
+	case usb_stream_ready:
+		if (us122l->first == file)
+			polled = &s->periods_polled;
+		else
+			polled = &us122l->second_periods_polled;
+		if (*polled != s->periods_done) {
+			*polled = s->periods_done;
+			mask = POLLIN | POLLOUT | POLLWRNORM;
+			break;
+		}
+		/* Fall through */
+		mask = 0;
+		break;
+	default:
+		mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
+		break;
+	}
+	return mask;
+}
+
+static void us122l_stop(struct us122l *us122l)
+{
+	struct list_head *p;
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_stop(p);
+
+	usb_stream_stop(&us122l->sk);
+	usb_stream_free(&us122l->sk);
+}
+
+static int us122l_set_sample_rate(struct usb_device *dev, int rate)
+{
+	unsigned int ep = 0x81;
+	unsigned char data[3];
+	int err;
+
+	data[0] = rate;
+	data[1] = rate >> 8;
+	data[2] = rate >> 16;
+	err = us122l_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,
+			     USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+			     SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000);
+	if (err < 0)
+		snd_printk(KERN_ERR "%d: cannot set freq %d to ep 0x%x\n",
+			   dev->devnum, rate, ep);
+	return err;
+}
+
+static bool us122l_start(struct us122l *us122l,
+			 unsigned rate, unsigned period_frames)
+{
+	struct list_head *p;
+	int err;
+	unsigned use_packsize = 0;
+	bool success = false;
+
+	if (us122l->chip.dev->speed == USB_SPEED_HIGH) {
+		/* The us-122l's descriptor defaults to iso max_packsize 78,
+		   which isn't needed for samplerates <= 48000.
+		   Lets save some memory:
+		*/
+		switch (rate) {
+		case 44100:
+			use_packsize = 36;
+			break;
+		case 48000:
+			use_packsize = 42;
+			break;
+		case 88200:
+			use_packsize = 72;
+			break;
+		}
+	}
+	if (!usb_stream_new(&us122l->sk, us122l->chip.dev, 1, 2,
+			    rate, use_packsize, period_frames, 6))
+		goto out;
+
+	err = us122l_set_sample_rate(us122l->chip.dev, rate);
+	if (err < 0) {
+		us122l_stop(us122l);
+		snd_printk(KERN_ERR "us122l_set_sample_rate error \n");
+		goto out;
+	}
+	err = usb_stream_start(&us122l->sk);
+	if (err < 0) {
+		us122l_stop(us122l);
+		snd_printk(KERN_ERR "us122l_start error %i \n", err);
+		goto out;
+	}
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_start(p);
+	success = true;
+out:
+	return success;
+}
+
+static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+				  unsigned cmd, unsigned long arg)
+{
+	struct usb_stream_config *cfg;
+	struct us122l *us122l = hw->private_data;
+	unsigned min_period_frames;
+	int err = 0;
+	bool high_speed;
+
+	if (cmd != SNDRV_USB_STREAM_IOCTL_SET_PARAMS)
+		return -ENOTTY;
+
+	cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	if (copy_from_user(cfg, (void *)arg, sizeof(*cfg))) {
+		err = -EFAULT;
+		goto free;
+	}
+	if (cfg->version != USB_STREAM_INTERFACE_VERSION) {
+		err = -ENXIO;
+		goto free;
+	}
+	high_speed = us122l->chip.dev->speed == USB_SPEED_HIGH;
+	if ((cfg->sample_rate != 44100 && cfg->sample_rate != 48000  &&
+	     (!high_speed ||
+	      (cfg->sample_rate != 88200 && cfg->sample_rate != 96000))) ||
+	    cfg->frame_size != 6 ||
+	    cfg->period_frames > 0x3000) {
+		err = -EINVAL;
+		goto free;
+	}
+	switch (cfg->sample_rate) {
+	case 44100:
+		min_period_frames = 48;
+		break;
+	case 48000:
+		min_period_frames = 52;
+		break;
+	default:
+		min_period_frames = 104;
+		break;
+	}
+	if (!high_speed)
+		min_period_frames <<= 1;
+	if (cfg->period_frames < min_period_frames) {
+		err = -EINVAL;
+		goto free;
+	}
+
+	snd_power_wait(hw->card, SNDRV_CTL_POWER_D0);
+
+	mutex_lock(&us122l->mutex);
+	if (!us122l->master)
+		us122l->master = file;
+	else if (us122l->master != file) {
+		if (memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg))) {
+			err = -EIO;
+			goto unlock;
+		}
+		us122l->slave = file;
+	}
+	if (!us122l->sk.s ||
+	    memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) ||
+	    us122l->sk.s->state == usb_stream_xrun) {
+		us122l_stop(us122l);
+		if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames))
+			err = -EIO;
+		else
+			err = 1;
+	}
+unlock:
+	mutex_unlock(&us122l->mutex);
+free:
+	kfree(cfg);
+	return err;
+}
+
+#define SND_USB_STREAM_ID "USB STREAM"
+static int usb_stream_hwdep_new(struct snd_card *card)
+{
+	int err;
+	struct snd_hwdep *hw;
+	struct usb_device *dev = US122L(card)->chip.dev;
+
+	err = snd_hwdep_new(card, SND_USB_STREAM_ID, 0, &hw);
+	if (err < 0)
+		return err;
+
+	hw->iface = SNDRV_HWDEP_IFACE_USB_STREAM;
+	hw->private_data = US122L(card);
+	hw->ops.open = usb_stream_hwdep_open;
+	hw->ops.release = usb_stream_hwdep_release;
+	hw->ops.ioctl = usb_stream_hwdep_ioctl;
+	hw->ops.ioctl_compat = usb_stream_hwdep_ioctl;
+	hw->ops.mmap = usb_stream_hwdep_mmap;
+	hw->ops.poll = usb_stream_hwdep_poll;
+
+	sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm",
+		dev->bus->busnum, dev->devnum);
+	return 0;
+}
+
+
+static bool us122l_create_card(struct snd_card *card)
+{
+	int err;
+	struct us122l *us122l = US122L(card);
+
+	err = usb_set_interface(us122l->chip.dev, 1, 1);
+	if (err) {
+		snd_printk(KERN_ERR "usb_set_interface error \n");
+		return false;
+	}
+
+	pt_info_set(us122l->chip.dev, 0x11);
+	pt_info_set(us122l->chip.dev, 0x10);
+
+	if (!us122l_start(us122l, 44100, 256))
+		return false;
+
+	err = us122l_create_usbmidi(card);
+	if (err < 0) {
+		snd_printk(KERN_ERR "us122l_create_usbmidi error %i \n", err);
+		us122l_stop(us122l);
+		return false;
+	}
+	err = usb_stream_hwdep_new(card);
+	if (err < 0) {
+/* release the midi resources */
+		struct list_head *p;
+		list_for_each(p, &us122l->chip.midi_list)
+			snd_usbmidi_disconnect(p);
+
+		us122l_stop(us122l);
+		return false;
+	}
+	return true;
+}
+
+static struct snd_card *usx2y_create_card(struct usb_device *device)
+{
+	int		dev;
+	struct snd_card *card;
+	for (dev = 0; dev < SNDRV_CARDS; ++dev)
+		if (enable[dev] && !snd_us122l_card_used[dev])
+			break;
+	if (dev >= SNDRV_CARDS)
+		return NULL;
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+			    sizeof(struct us122l));
+	if (!card)
+		return NULL;
+	snd_us122l_card_used[US122L(card)->chip.index = dev] = 1;
+
+	US122L(card)->chip.dev = device;
+	US122L(card)->chip.card = card;
+	mutex_init(&US122L(card)->mutex);
+	init_waitqueue_head(&US122L(card)->sk.sleep);
+	INIT_LIST_HEAD(&US122L(card)->chip.midi_list);
+	strcpy(card->driver, "USB "NAME_ALLCAPS"");
+	sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
+	sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
+		card->shortname,
+		le16_to_cpu(device->descriptor.idVendor),
+		le16_to_cpu(device->descriptor.idProduct),
+		0,
+		US122L(card)->chip.dev->bus->busnum,
+		US122L(card)->chip.dev->devnum
+		);
+	snd_card_set_dev(card, &device->dev);
+	return card;
+}
+
+static void *us122l_usb_probe(struct usb_interface *intf,
+			      const struct usb_device_id *device_id)
+{
+	struct usb_device *device = interface_to_usbdev(intf);
+	struct snd_card *card = usx2y_create_card(device);
+
+	if (!card)
+		return NULL;
+
+	if (!us122l_create_card(card) ||
+	    snd_card_register(card) < 0) {
+		snd_card_free(card);
+		return NULL;
+	}
+
+	usb_get_dev(device);
+	return card;
+}
+
+static int snd_us122l_probe(struct usb_interface *intf,
+			    const struct usb_device_id *id)
+{
+	struct snd_card *card;
+	snd_printdd(KERN_DEBUG"%p:%i\n",
+		    intf, intf->cur_altsetting->desc.bInterfaceNumber);
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
+		return 0;
+
+	card = us122l_usb_probe(usb_get_intf(intf), id);
+
+	if (card) {
+		usb_set_intfdata(intf, card);
+		return 0;
+	}
+
+	usb_put_intf(intf);
+	return -EIO;
+}
+
+static void snd_us122l_disconnect(struct usb_interface *intf)
+{
+	struct snd_card *card;
+	struct us122l *us122l;
+	struct list_head *p;
+
+	card = usb_get_intfdata(intf);
+	if (!card)
+		return;
+
+	snd_card_disconnect(card);
+
+	us122l = US122L(card);
+	mutex_lock(&us122l->mutex);
+	us122l_stop(us122l);
+	mutex_unlock(&us122l->mutex);
+	us122l->chip.shutdown = 1;
+
+/* release the midi resources */
+	list_for_each(p, &us122l->chip.midi_list) {
+		snd_usbmidi_disconnect(p);
+	}
+
+	usb_put_intf(intf);
+	usb_put_dev(US122L(card)->chip.dev);
+
+	while (atomic_read(&us122l->mmap_count))
+		msleep(500);
+
+	snd_card_free(card);
+}
+
+static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct snd_card *card;
+	struct us122l *us122l;
+	struct list_head *p;
+
+	card = dev_get_drvdata(&intf->dev);
+	if (!card)
+		return 0;
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	us122l = US122L(card);
+	if (!us122l)
+		return 0;
+
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_stop(p);
+
+	mutex_lock(&us122l->mutex);
+	usb_stream_stop(&us122l->sk);
+	mutex_unlock(&us122l->mutex);
+
+	return 0;
+}
+
+static int snd_us122l_resume(struct usb_interface *intf)
+{
+	struct snd_card *card;
+	struct us122l *us122l;
+	struct list_head *p;
+	int err;
+
+	card = dev_get_drvdata(&intf->dev);
+	if (!card)
+		return 0;
+
+	us122l = US122L(card);
+	if (!us122l)
+		return 0;
+
+	mutex_lock(&us122l->mutex);
+	/* needed, doesn't restart without: */
+	err = usb_set_interface(us122l->chip.dev, 1, 1);
+	if (err) {
+		snd_printk(KERN_ERR "usb_set_interface error \n");
+		goto unlock;
+	}
+
+	pt_info_set(us122l->chip.dev, 0x11);
+	pt_info_set(us122l->chip.dev, 0x10);
+
+	err = us122l_set_sample_rate(us122l->chip.dev,
+				     us122l->sk.s->cfg.sample_rate);
+	if (err < 0) {
+		snd_printk(KERN_ERR "us122l_set_sample_rate error \n");
+		goto unlock;
+	}
+	err = usb_stream_start(&us122l->sk);
+	if (err)
+		goto unlock;
+
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_start(p);
+unlock:
+	mutex_unlock(&us122l->mutex);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return err;
+}
+
+static struct usb_device_id snd_us122l_usb_id_table[] = {
+	{
+		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
+		.idVendor =	0x0644,
+		.idProduct =	USB_ID_US122L
+	},
+/*  	{ */		/* US-144 maybe works when @USB1.1. Untested. */
+/* 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE, */
+/* 		.idVendor =	0x0644, */
+/* 		.idProduct =	USB_ID_US144 */
+/* 	}, */
+	{ /* terminator */ }
+};
+
+MODULE_DEVICE_TABLE(usb, snd_us122l_usb_id_table);
+static struct usb_driver snd_us122l_usb_driver = {
+	.name =		"snd-usb-us122l",
+	.probe =	snd_us122l_probe,
+	.disconnect =	snd_us122l_disconnect,
+	.suspend =	snd_us122l_suspend,
+	.resume =	snd_us122l_resume,
+	.reset_resume =	snd_us122l_resume,
+	.id_table =	snd_us122l_usb_id_table,
+	.supports_autosuspend = 1
+};
+
+
+static int __init snd_us122l_module_init(void)
+{
+	return usb_register(&snd_us122l_usb_driver);
+}
+
+static void __exit snd_us122l_module_exit(void)
+{
+	usb_deregister(&snd_us122l_usb_driver);
+}
+
+module_init(snd_us122l_module_init)
+module_exit(snd_us122l_module_exit)
diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h
new file mode 100644
index 0000000..3d10c4b
--- /dev/null
+++ b/sound/usb/usx2y/us122l.h
@@ -0,0 +1,27 @@
+#ifndef US122L_H
+#define US122L_H
+
+
+struct us122l {
+	struct snd_usb_audio 	chip;
+	int			stride;
+	struct usb_stream_kernel sk;
+
+	struct mutex		mutex;
+	struct file		*first;
+	unsigned		second_periods_polled;
+	struct file		*master;
+	struct file		*slave;
+
+	atomic_t		mmap_count;
+};
+
+
+#define US122L(c) ((struct us122l *)(c)->private_data)
+
+#define NAME_ALLCAPS "US-122L"
+
+#define USB_ID_US122L 0x800E
+#define USB_ID_US144 0x800F
+
+#endif
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
new file mode 100644
index 0000000..ff23cc1
--- /dev/null
+++ b/sound/usb/usx2y/usb_stream.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/usb.h>
+
+#include "usb_stream.h"
+
+
+/*                             setup                                  */
+
+static unsigned usb_stream_next_packet_size(struct usb_stream_kernel *sk)
+{
+	struct usb_stream *s = sk->s;
+	sk->out_phase_peeked = (sk->out_phase & 0xffff) + sk->freqn;
+	return (sk->out_phase_peeked >> 16) * s->cfg.frame_size;
+}
+
+static void playback_prep_freqn(struct usb_stream_kernel *sk, struct urb *urb)
+{
+	struct usb_stream *s = sk->s;
+	unsigned l = 0;
+	int pack;
+
+	urb->iso_frame_desc[0].offset = 0;
+	urb->iso_frame_desc[0].length =	usb_stream_next_packet_size(sk);
+	sk->out_phase = sk->out_phase_peeked;
+	urb->transfer_buffer_length = urb->iso_frame_desc[0].length;
+
+	for (pack = 1; pack < sk->n_o_ps; pack++) {
+		l = usb_stream_next_packet_size(sk);
+		if (s->idle_outsize + urb->transfer_buffer_length + l >
+		    s->period_size)
+			goto check;
+
+		sk->out_phase = sk->out_phase_peeked;
+		urb->iso_frame_desc[pack].offset = urb->transfer_buffer_length;
+		urb->iso_frame_desc[pack].length = l;
+		urb->transfer_buffer_length += l;
+	}
+	snd_printdd(KERN_DEBUG "%i\n", urb->transfer_buffer_length);
+
+check:
+	urb->number_of_packets = pack;
+	s->idle_outsize += urb->transfer_buffer_length - s->period_size;
+	snd_printdd(KERN_DEBUG "idle=%i ul=%i ps=%i\n", s->idle_outsize,
+		    urb->transfer_buffer_length, s->period_size);
+}
+
+static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize,
+			   struct urb **urbs, char *transfer,
+			   struct usb_device *dev, int pipe)
+{
+	int u, p;
+	int maxpacket = use_packsize ?
+		use_packsize : usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+	int transfer_length = maxpacket * sk->n_o_ps;
+
+	for (u = 0; u < USB_STREAM_NURBS;
+	     ++u, transfer += transfer_length) {
+		struct urb *urb = urbs[u];
+		struct usb_iso_packet_descriptor *desc;
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->transfer_buffer = transfer;
+		urb->dev = dev;
+		urb->pipe = pipe;
+		urb->number_of_packets = sk->n_o_ps;
+		urb->context = sk;
+		urb->interval = 1;
+		if (usb_pipeout(pipe))
+			continue;
+
+		urb->transfer_buffer_length = transfer_length;
+		desc = urb->iso_frame_desc;
+		desc->offset = 0;
+		desc->length = maxpacket;
+		for (p = 1; p < sk->n_o_ps; ++p) {
+			desc[p].offset = desc[p - 1].offset + maxpacket;
+			desc[p].length = maxpacket;
+		}
+	}
+}
+
+static void init_urbs(struct usb_stream_kernel *sk, unsigned use_packsize,
+		      struct usb_device *dev, int in_pipe, int out_pipe)
+{
+	struct usb_stream	*s = sk->s;
+	char			*indata = (char *)s + sizeof(*s) +
+					sizeof(struct usb_stream_packet) *
+					s->inpackets;
+	int			u;
+
+	for (u = 0; u < USB_STREAM_NURBS; ++u) {
+		sk->inurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL);
+		sk->outurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL);
+	}
+
+	init_pipe_urbs(sk, use_packsize, sk->inurb, indata, dev, in_pipe);
+	init_pipe_urbs(sk, use_packsize, sk->outurb, sk->write_page, dev,
+		       out_pipe);
+}
+
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+static inline unsigned get_usb_full_speed_rate(unsigned rate)
+{
+	return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned rate)
+{
+	return ((rate << 10) + 62) / 125;
+}
+
+void usb_stream_free(struct usb_stream_kernel *sk)
+{
+	struct usb_stream *s;
+	unsigned u;
+
+	for (u = 0; u < USB_STREAM_NURBS; ++u) {
+		usb_free_urb(sk->inurb[u]);
+		sk->inurb[u] = NULL;
+		usb_free_urb(sk->outurb[u]);
+		sk->outurb[u] = NULL;
+	}
+
+	s = sk->s;
+	if (!s)
+		return;
+
+	free_pages((unsigned long)sk->write_page, get_order(s->write_size));
+	sk->write_page = NULL;
+	free_pages((unsigned long)s, get_order(s->read_size));
+	sk->s = NULL;
+}
+
+struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
+				  struct usb_device *dev,
+				  unsigned in_endpoint, unsigned out_endpoint,
+				  unsigned sample_rate, unsigned use_packsize,
+				  unsigned period_frames, unsigned frame_size)
+{
+	int packets, max_packsize;
+	int in_pipe, out_pipe;
+	int read_size = sizeof(struct usb_stream);
+	int write_size;
+	int usb_frames = dev->speed == USB_SPEED_HIGH ? 8000 : 1000;
+	int pg;
+
+	in_pipe = usb_rcvisocpipe(dev, in_endpoint);
+	out_pipe = usb_sndisocpipe(dev, out_endpoint);
+
+	max_packsize = use_packsize ?
+		use_packsize : usb_maxpacket(dev, in_pipe, 0);
+
+	/*
+		t_period = period_frames / sample_rate
+		iso_packs = t_period / t_iso_frame
+			= (period_frames / sample_rate) * (1 / t_iso_frame)
+	*/
+
+	packets = period_frames * usb_frames / sample_rate + 1;
+
+	if (dev->speed == USB_SPEED_HIGH)
+		packets = (packets + 7) & ~7;
+
+	read_size += packets * USB_STREAM_URBDEPTH *
+		(max_packsize + sizeof(struct usb_stream_packet));
+
+	max_packsize = usb_maxpacket(dev, out_pipe, 1);
+	write_size = max_packsize * packets * USB_STREAM_URBDEPTH;
+
+	if (read_size >= 256*PAGE_SIZE || write_size >= 256*PAGE_SIZE) {
+		snd_printk(KERN_WARNING "a size exceeds 128*PAGE_SIZE\n");
+		goto out;
+	}
+
+	pg = get_order(read_size);
+	sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+	if (!sk->s) {
+		snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
+		goto out;
+	}
+	sk->s->cfg.version = USB_STREAM_INTERFACE_VERSION;
+
+	sk->s->read_size = read_size;
+
+	sk->s->cfg.sample_rate = sample_rate;
+	sk->s->cfg.frame_size = frame_size;
+	sk->n_o_ps = packets;
+	sk->s->inpackets = packets * USB_STREAM_URBDEPTH;
+	sk->s->cfg.period_frames = period_frames;
+	sk->s->period_size = frame_size * period_frames;
+
+	sk->s->write_size = write_size;
+	pg = get_order(write_size);
+
+	sk->write_page =
+		(void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+	if (!sk->write_page) {
+		snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
+		usb_stream_free(sk);
+		return NULL;
+	}
+
+	/* calculate the frequency in 16.16 format */
+	if (dev->speed == USB_SPEED_FULL)
+		sk->freqn = get_usb_full_speed_rate(sample_rate);
+	else
+		sk->freqn = get_usb_high_speed_rate(sample_rate);
+
+	init_urbs(sk, use_packsize, dev, in_pipe, out_pipe);
+	sk->s->state = usb_stream_stopped;
+out:
+	return sk->s;
+}
+
+
+/*                             start                                  */
+
+static bool balance_check(struct usb_stream_kernel *sk, struct urb *urb)
+{
+	bool r;
+	if (unlikely(urb->status)) {
+		if (urb->status != -ESHUTDOWN && urb->status != -ENOENT)
+			snd_printk(KERN_WARNING "status=%i\n", urb->status);
+		sk->iso_frame_balance = 0x7FFFFFFF;
+		return false;
+	}
+	r = sk->iso_frame_balance == 0;
+	if (!r)
+		sk->i_urb = urb;
+	return r;
+}
+
+static bool balance_playback(struct usb_stream_kernel *sk, struct urb *urb)
+{
+	sk->iso_frame_balance += urb->number_of_packets;
+	return balance_check(sk, urb);
+}
+
+static bool balance_capture(struct usb_stream_kernel *sk, struct urb *urb)
+{
+	sk->iso_frame_balance -= urb->number_of_packets;
+	return balance_check(sk, urb);
+}
+
+static void subs_set_complete(struct urb **urbs, void (*complete)(struct urb *))
+{
+	int u;
+
+	for (u = 0; u < USB_STREAM_NURBS; u++) {
+		struct urb *urb = urbs[u];
+		urb->complete = complete;
+	}
+}
+
+int usb_stream_prepare_playback(struct usb_stream_kernel *sk, struct urb *inurb)
+{
+	struct usb_stream *s = sk->s;
+	struct urb *io;
+	struct usb_iso_packet_descriptor *id, *od;
+	int p, l = 0;
+
+	io = sk->idle_outurb;
+	od = io->iso_frame_desc;
+	io->transfer_buffer_length = 0;
+
+	for (p = 0; s->sync_packet < 0; ++p, ++s->sync_packet) {
+		struct urb *ii = sk->completed_inurb;
+		id = ii->iso_frame_desc +
+			ii->number_of_packets + s->sync_packet;
+		l = id->actual_length;
+
+		od[p].length = l;
+		od[p].offset = io->transfer_buffer_length;
+		io->transfer_buffer_length += l;
+	}
+
+	for (;
+	     s->sync_packet < inurb->number_of_packets && p < sk->n_o_ps;
+	     ++p, ++s->sync_packet) {
+		l = inurb->iso_frame_desc[s->sync_packet].actual_length;
+
+		if (s->idle_outsize + io->transfer_buffer_length + l >
+		    s->period_size)
+			goto check_ok;
+
+		od[p].length = l;
+		od[p].offset = io->transfer_buffer_length;
+		io->transfer_buffer_length += l;
+	}
+
+check_ok:
+	s->sync_packet -= inurb->number_of_packets;
+	if (s->sync_packet < -2 || s->sync_packet > 0) {
+		snd_printk(KERN_WARNING "invalid sync_packet = %i;"
+			   " p=%i nop=%i %i %x %x %x > %x\n",
+			   s->sync_packet, p, inurb->number_of_packets,
+			   s->idle_outsize + io->transfer_buffer_length + l,
+			   s->idle_outsize, io->transfer_buffer_length,  l,
+			   s->period_size);
+		return -1;
+	}
+	if (io->transfer_buffer_length % s->cfg.frame_size) {
+		snd_printk(KERN_WARNING"invalid outsize = %i\n",
+			   io->transfer_buffer_length);
+		return -1;
+	}
+	s->idle_outsize += io->transfer_buffer_length - s->period_size;
+	io->number_of_packets = p;
+	if (s->idle_outsize > 0) {
+		snd_printk(KERN_WARNING "idle=%i\n", s->idle_outsize);
+		return -1;
+	}
+	return 0;
+}
+
+static void prepare_inurb(int number_of_packets, struct urb *iu)
+{
+	struct usb_iso_packet_descriptor *id;
+	int p;
+
+	iu->number_of_packets = number_of_packets;
+	id = iu->iso_frame_desc;
+	id->offset = 0;
+	for (p = 0; p < iu->number_of_packets - 1; ++p)
+		id[p + 1].offset = id[p].offset + id[p].length;
+
+	iu->transfer_buffer_length =
+		id[0].length * iu->number_of_packets;
+}
+
+static int submit_urbs(struct usb_stream_kernel *sk,
+		       struct urb *inurb, struct urb *outurb)
+{
+	int err;
+	prepare_inurb(sk->idle_outurb->number_of_packets, sk->idle_inurb);
+	err = usb_submit_urb(sk->idle_inurb, GFP_ATOMIC);
+	if (err < 0) {
+		snd_printk(KERN_ERR "%i\n", err);
+		return err;
+	}
+	sk->idle_inurb = sk->completed_inurb;
+	sk->completed_inurb = inurb;
+	err = usb_submit_urb(sk->idle_outurb, GFP_ATOMIC);
+	if (err < 0) {
+		snd_printk(KERN_ERR "%i\n", err);
+		return err;
+	}
+	sk->idle_outurb = sk->completed_outurb;
+	sk->completed_outurb = outurb;
+	return 0;
+}
+
+#ifdef DEBUG_LOOP_BACK
+/*
+  This loop_back() shows how to read/write the period data.
+ */
+static void loop_back(struct usb_stream *s)
+{
+	char *i, *o;
+	int il, ol, l, p;
+	struct urb *iu;
+	struct usb_iso_packet_descriptor *id;
+
+	o = s->playback1st_to;
+	ol = s->playback1st_size;
+	l = 0;
+
+	if (s->insplit_pack >= 0) {
+		iu = sk->idle_inurb;
+		id = iu->iso_frame_desc;
+		p = s->insplit_pack;
+	} else
+		goto second;
+loop:
+	for (; p < iu->number_of_packets && l < s->period_size; ++p) {
+		i = iu->transfer_buffer + id[p].offset;
+		il = id[p].actual_length;
+		if (l + il > s->period_size)
+			il = s->period_size - l;
+		if (il <= ol) {
+			memcpy(o, i, il);
+			o += il;
+			ol -= il;
+		} else {
+			memcpy(o, i, ol);
+			singen_6pack(o, ol);
+			o = s->playback_to;
+			memcpy(o, i + ol, il - ol);
+			o += il - ol;
+			ol = s->period_size - s->playback1st_size;
+		}
+		l += il;
+	}
+	if (iu == sk->completed_inurb) {
+		if (l != s->period_size)
+			printk(KERN_DEBUG"%s:%i %i\n", __func__, __LINE__,
+			       l/(int)s->cfg.frame_size);
+
+		return;
+	}
+second:
+	iu = sk->completed_inurb;
+	id = iu->iso_frame_desc;
+	p = 0;
+	goto loop;
+
+}
+#else
+static void loop_back(struct usb_stream *s)
+{
+}
+#endif
+
+static void stream_idle(struct usb_stream_kernel *sk,
+			struct urb *inurb, struct urb *outurb)
+{
+	struct usb_stream *s = sk->s;
+	int l, p;
+	int insize = s->idle_insize;
+	int urb_size = 0;
+
+	s->inpacket_split = s->next_inpacket_split;
+	s->inpacket_split_at = s->next_inpacket_split_at;
+	s->next_inpacket_split = -1;
+	s->next_inpacket_split_at = 0;
+
+	for (p = 0; p < inurb->number_of_packets; ++p) {
+		struct usb_iso_packet_descriptor *id = inurb->iso_frame_desc;
+		l = id[p].actual_length;
+		if (unlikely(l == 0 || id[p].status)) {
+			snd_printk(KERN_WARNING "underrun, status=%u\n",
+				   id[p].status);
+			goto err_out;
+		}
+		s->inpacket_head++;
+		s->inpacket_head %= s->inpackets;
+		if (s->inpacket_split == -1)
+			s->inpacket_split = s->inpacket_head;
+
+		s->inpacket[s->inpacket_head].offset =
+			id[p].offset + (inurb->transfer_buffer - (void *)s);
+		s->inpacket[s->inpacket_head].length = l;
+		if (insize + l > s->period_size &&
+		    s->next_inpacket_split == -1) {
+			s->next_inpacket_split = s->inpacket_head;
+			s->next_inpacket_split_at = s->period_size - insize;
+		}
+		insize += l;
+		urb_size += l;
+	}
+	s->idle_insize += urb_size - s->period_size;
+	if (s->idle_insize < 0) {
+		snd_printk(KERN_WARNING "%i\n",
+			   (s->idle_insize)/(int)s->cfg.frame_size);
+		goto err_out;
+	}
+	s->insize_done += urb_size;
+
+	l = s->idle_outsize;
+	s->outpacket[0].offset = (sk->idle_outurb->transfer_buffer -
+				  sk->write_page) - l;
+
+	if (usb_stream_prepare_playback(sk, inurb) < 0)
+		goto err_out;
+
+	s->outpacket[0].length = sk->idle_outurb->transfer_buffer_length + l;
+	s->outpacket[1].offset = sk->completed_outurb->transfer_buffer -
+		sk->write_page;
+
+	if (submit_urbs(sk, inurb, outurb) < 0)
+		goto err_out;
+
+	loop_back(s);
+	s->periods_done++;
+	wake_up_all(&sk->sleep);
+	return;
+err_out:
+	s->state = usb_stream_xrun;
+	wake_up_all(&sk->sleep);
+}
+
+static void i_capture_idle(struct urb *urb)
+{
+	struct usb_stream_kernel *sk = urb->context;
+	if (balance_capture(sk, urb))
+		stream_idle(sk, urb, sk->i_urb);
+}
+
+static void i_playback_idle(struct urb *urb)
+{
+	struct usb_stream_kernel *sk = urb->context;
+	if (balance_playback(sk, urb))
+		stream_idle(sk, sk->i_urb, urb);
+}
+
+static void stream_start(struct usb_stream_kernel *sk,
+			 struct urb *inurb, struct urb *outurb)
+{
+	struct usb_stream *s = sk->s;
+	if (s->state >= usb_stream_sync1) {
+		int l, p, max_diff, max_diff_0;
+		int urb_size = 0;
+		unsigned frames_per_packet, min_frames = 0;
+		frames_per_packet = (s->period_size - s->idle_insize);
+		frames_per_packet <<= 8;
+		frames_per_packet /=
+			s->cfg.frame_size * inurb->number_of_packets;
+		frames_per_packet++;
+
+		max_diff_0 = s->cfg.frame_size;
+		if (s->cfg.period_frames >= 256)
+			max_diff_0 <<= 1;
+		if (s->cfg.period_frames >= 1024)
+			max_diff_0 <<= 1;
+		max_diff = max_diff_0;
+		for (p = 0; p < inurb->number_of_packets; ++p) {
+			int diff;
+			l = inurb->iso_frame_desc[p].actual_length;
+			urb_size += l;
+
+			min_frames += frames_per_packet;
+			diff = urb_size -
+				(min_frames >> 8) * s->cfg.frame_size;
+			if (diff < max_diff) {
+				snd_printdd(KERN_DEBUG "%i %i %i %i\n",
+					    s->insize_done,
+					    urb_size / (int)s->cfg.frame_size,
+					    inurb->number_of_packets, diff);
+				max_diff = diff;
+			}
+		}
+		s->idle_insize -= max_diff - max_diff_0;
+		s->idle_insize += urb_size - s->period_size;
+		if (s->idle_insize < 0) {
+			snd_printk("%i %i %i\n",
+				   s->idle_insize, urb_size, s->period_size);
+			return;
+		} else if (s->idle_insize == 0) {
+			s->next_inpacket_split =
+				(s->inpacket_head + 1) % s->inpackets;
+			s->next_inpacket_split_at = 0;
+		} else {
+			unsigned split = s->inpacket_head;
+			l = s->idle_insize;
+			while (l > s->inpacket[split].length) {
+				l -= s->inpacket[split].length;
+				if (split == 0)
+					split = s->inpackets - 1;
+				else
+					split--;
+			}
+			s->next_inpacket_split = split;
+			s->next_inpacket_split_at =
+				s->inpacket[split].length - l;
+		}
+
+		s->insize_done += urb_size;
+
+		if (usb_stream_prepare_playback(sk, inurb) < 0)
+			return;
+
+	} else
+		playback_prep_freqn(sk, sk->idle_outurb);
+
+	if (submit_urbs(sk, inurb, outurb) < 0)
+		return;
+
+	if (s->state == usb_stream_sync1 && s->insize_done > 360000) {
+		/* just guesswork                            ^^^^^^ */
+		s->state = usb_stream_ready;
+		subs_set_complete(sk->inurb, i_capture_idle);
+		subs_set_complete(sk->outurb, i_playback_idle);
+	}
+}
+
+static void i_capture_start(struct urb *urb)
+{
+	struct usb_iso_packet_descriptor *id = urb->iso_frame_desc;
+	struct usb_stream_kernel *sk = urb->context;
+	struct usb_stream *s = sk->s;
+	int p;
+	int empty = 0;
+
+	if (urb->status) {
+		snd_printk(KERN_WARNING "status=%i\n", urb->status);
+		return;
+	}
+
+	for (p = 0; p < urb->number_of_packets; ++p) {
+		int l = id[p].actual_length;
+		if (l < s->cfg.frame_size) {
+			++empty;
+			if (s->state >= usb_stream_sync0) {
+				snd_printk(KERN_WARNING "%i\n", l);
+				return;
+			}
+		}
+		s->inpacket_head++;
+		s->inpacket_head %= s->inpackets;
+		s->inpacket[s->inpacket_head].offset =
+			id[p].offset + (urb->transfer_buffer - (void *)s);
+		s->inpacket[s->inpacket_head].length = l;
+	}
+#ifdef SHOW_EMPTY
+	if (empty) {
+		printk(KERN_DEBUG"%s:%i: %i", __func__, __LINE__,
+		       urb->iso_frame_desc[0].actual_length);
+		for (pack = 1; pack < urb->number_of_packets; ++pack) {
+			int l = urb->iso_frame_desc[pack].actual_length;
+			printk(" %i", l);
+		}
+		printk("\n");
+	}
+#endif
+	if (!empty && s->state < usb_stream_sync1)
+		++s->state;
+
+	if (balance_capture(sk, urb))
+		stream_start(sk, urb, sk->i_urb);
+}
+
+static void i_playback_start(struct urb *urb)
+{
+	struct usb_stream_kernel *sk = urb->context;
+	if (balance_playback(sk, urb))
+		stream_start(sk, sk->i_urb, urb);
+}
+
+int usb_stream_start(struct usb_stream_kernel *sk)
+{
+	struct usb_stream *s = sk->s;
+	int frame = 0, iters = 0;
+	int u, err;
+	int try = 0;
+
+	if (s->state != usb_stream_stopped)
+		return -EAGAIN;
+
+	subs_set_complete(sk->inurb, i_capture_start);
+	subs_set_complete(sk->outurb, i_playback_start);
+	memset(sk->write_page, 0, s->write_size);
+dotry:
+	s->insize_done = 0;
+	s->idle_insize = 0;
+	s->idle_outsize = 0;
+	s->sync_packet = -1;
+	s->inpacket_head = -1;
+	sk->iso_frame_balance = 0;
+	++try;
+	for (u = 0; u < 2; u++) {
+		struct urb *inurb = sk->inurb[u];
+		struct urb *outurb = sk->outurb[u];
+		playback_prep_freqn(sk, outurb);
+		inurb->number_of_packets = outurb->number_of_packets;
+		inurb->transfer_buffer_length =
+			inurb->number_of_packets *
+			inurb->iso_frame_desc[0].length;
+		preempt_disable();
+		if (u == 0) {
+			int now;
+			struct usb_device *dev = inurb->dev;
+			frame = usb_get_current_frame_number(dev);
+			do {
+				now = usb_get_current_frame_number(dev);
+				++iters;
+			} while (now > -1 && now == frame);
+		}
+		err = usb_submit_urb(inurb, GFP_ATOMIC);
+		if (err < 0) {
+			preempt_enable();
+			snd_printk(KERN_ERR"usb_submit_urb(sk->inurb[%i])"
+				   " returned %i\n", u, err);
+			return err;
+		}
+		err = usb_submit_urb(outurb, GFP_ATOMIC);
+		if (err < 0) {
+			preempt_enable();
+			snd_printk(KERN_ERR"usb_submit_urb(sk->outurb[%i])"
+				   " returned %i\n", u, err);
+			return err;
+		}
+		preempt_enable();
+		if (inurb->start_frame != outurb->start_frame) {
+			snd_printd(KERN_DEBUG
+				   "u[%i] start_frames differ in:%u out:%u\n",
+				   u, inurb->start_frame, outurb->start_frame);
+			goto check_retry;
+		}
+	}
+	snd_printdd(KERN_DEBUG "%i %i\n", frame, iters);
+	try = 0;
+check_retry:
+	if (try) {
+		usb_stream_stop(sk);
+		if (try < 5) {
+			msleep(1500);
+			snd_printd(KERN_DEBUG "goto dotry;\n");
+			goto dotry;
+		}
+		snd_printk(KERN_WARNING"couldn't start"
+			   " all urbs on the same start_frame.\n");
+		return -EFAULT;
+	}
+
+	sk->idle_inurb = sk->inurb[USB_STREAM_NURBS - 2];
+	sk->idle_outurb = sk->outurb[USB_STREAM_NURBS - 2];
+	sk->completed_inurb = sk->inurb[USB_STREAM_NURBS - 1];
+	sk->completed_outurb = sk->outurb[USB_STREAM_NURBS - 1];
+
+/* wait, check */
+	{
+		int wait_ms = 3000;
+		while (s->state != usb_stream_ready && wait_ms > 0) {
+			snd_printdd(KERN_DEBUG "%i\n", s->state);
+			msleep(200);
+			wait_ms -= 200;
+		}
+	}
+
+	return s->state == usb_stream_ready ? 0 : -EFAULT;
+}
+
+
+/*                             stop                                   */
+
+void usb_stream_stop(struct usb_stream_kernel *sk)
+{
+	int u;
+	if (!sk->s)
+		return;
+	for (u = 0; u < USB_STREAM_NURBS; ++u) {
+		usb_kill_urb(sk->inurb[u]);
+		usb_kill_urb(sk->outurb[u]);
+	}
+	sk->s->state = usb_stream_stopped;
+	msleep(400);
+}
diff --git a/sound/usb/usx2y/usb_stream.h b/sound/usb/usx2y/usb_stream.h
new file mode 100644
index 0000000..4dd74ab
--- /dev/null
+++ b/sound/usb/usx2y/usb_stream.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define USB_STREAM_INTERFACE_VERSION 2
+
+#define SNDRV_USB_STREAM_IOCTL_SET_PARAMS \
+	_IOW('H', 0x90, struct usb_stream_config)
+
+struct usb_stream_packet {
+	unsigned offset;
+	unsigned length;
+};
+
+
+struct usb_stream_config {
+	unsigned version;
+	unsigned sample_rate;
+	unsigned period_frames;
+	unsigned frame_size;
+};
+
+struct usb_stream {
+	struct usb_stream_config cfg;
+	unsigned read_size;
+	unsigned write_size;
+
+	int period_size;
+
+	unsigned state;
+
+	int idle_insize;
+	int idle_outsize;
+	int sync_packet;
+	unsigned insize_done;
+	unsigned periods_done;
+	unsigned periods_polled;
+
+	struct usb_stream_packet outpacket[2];
+	unsigned		 inpackets;
+	unsigned		 inpacket_head;
+	unsigned		 inpacket_split;
+	unsigned		 inpacket_split_at;
+	unsigned		 next_inpacket_split;
+	unsigned		 next_inpacket_split_at;
+	struct usb_stream_packet inpacket[0];
+};
+
+enum usb_stream_state {
+	usb_stream_invalid,
+	usb_stream_stopped,
+	usb_stream_sync0,
+	usb_stream_sync1,
+	usb_stream_ready,
+	usb_stream_running,
+	usb_stream_xrun,
+};
+
+#if __KERNEL__
+
+#define USB_STREAM_NURBS 4
+#define USB_STREAM_URBDEPTH 4
+
+struct usb_stream_kernel {
+	struct usb_stream *s;
+
+	void *write_page;
+
+	unsigned n_o_ps;
+
+	struct urb *inurb[USB_STREAM_NURBS];
+	struct urb *idle_inurb;
+	struct urb *completed_inurb;
+	struct urb *outurb[USB_STREAM_NURBS];
+	struct urb *idle_outurb;
+	struct urb *completed_outurb;
+	struct urb *i_urb;
+
+	int iso_frame_balance;
+
+	wait_queue_head_t sleep;
+
+	unsigned out_phase;
+	unsigned out_phase_peeked;
+	unsigned freqn;
+};
+
+struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
+				  struct usb_device *dev,
+				  unsigned in_endpoint, unsigned out_endpoint,
+				  unsigned sample_rate, unsigned use_packsize,
+				  unsigned period_frames, unsigned frame_size);
+void usb_stream_free(struct usb_stream_kernel *);
+int usb_stream_start(struct usb_stream_kernel *);
+void usb_stream_stop(struct usb_stream_kernel *);
+
+
+#endif